1238384Sjkim/********************************************************************** 2238384Sjkim * gost_keyx.c * 3238384Sjkim * Copyright (c) 2005-2006 Cryptocom LTD * 4238384Sjkim * This file is distributed under the same license as OpenSSL * 5238384Sjkim * * 6238384Sjkim * VK0 34.10-2001 key exchange and GOST R 34.10-2001 * 7238384Sjkim * based PKCS7/SMIME support * 8238384Sjkim * Requires OpenSSL 0.9.9 for compilation * 9238384Sjkim **********************************************************************/ 10238384Sjkim#include <openssl/evp.h> 11238384Sjkim#include <openssl/rand.h> 12238384Sjkim#include <string.h> 13238384Sjkim#include <openssl/objects.h> 14238384Sjkim#include "gost89.h" 15238384Sjkim#include "gosthash.h" 16238384Sjkim#include "e_gost_err.h" 17238384Sjkim#include "gost_keywrap.h" 18238384Sjkim#include "gost_lcl.h" 19238384Sjkim#include "gost2001_keyx.h" 20238384Sjkim 21238384Sjkim/* Implementation of CryptoPro VKO 34.10-2001 algorithm */ 22280297Sjkimstatic int VKO_compute_key(unsigned char *shared_key, size_t shared_key_size, 23280297Sjkim const EC_POINT *pub_key, EC_KEY *priv_key, 24280297Sjkim const unsigned char *ukm) 25280297Sjkim{ 26280297Sjkim unsigned char ukm_be[8], databuf[64], hashbuf[64]; 27280297Sjkim BIGNUM *UKM = NULL, *p = NULL, *order = NULL, *X = NULL, *Y = NULL; 28280297Sjkim const BIGNUM *key = EC_KEY_get0_private_key(priv_key); 29280297Sjkim EC_POINT *pnt = EC_POINT_new(EC_KEY_get0_group(priv_key)); 30280297Sjkim int i; 31280297Sjkim gost_hash_ctx hash_ctx; 32280297Sjkim BN_CTX *ctx = BN_CTX_new(); 33238384Sjkim 34280297Sjkim for (i = 0; i < 8; i++) { 35280297Sjkim ukm_be[7 - i] = ukm[i]; 36280297Sjkim } 37280297Sjkim BN_CTX_start(ctx); 38280297Sjkim UKM = getbnfrombuf(ukm_be, 8); 39280297Sjkim p = BN_CTX_get(ctx); 40280297Sjkim order = BN_CTX_get(ctx); 41280297Sjkim X = BN_CTX_get(ctx); 42280297Sjkim Y = BN_CTX_get(ctx); 43280297Sjkim EC_GROUP_get_order(EC_KEY_get0_group(priv_key), order, ctx); 44280297Sjkim BN_mod_mul(p, key, UKM, order, ctx); 45280297Sjkim EC_POINT_mul(EC_KEY_get0_group(priv_key), pnt, NULL, pub_key, p, ctx); 46280297Sjkim EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(priv_key), 47280297Sjkim pnt, X, Y, ctx); 48280297Sjkim /* 49280297Sjkim * Serialize elliptic curve point same way as we do it when saving key 50280297Sjkim */ 51280297Sjkim store_bignum(Y, databuf, 32); 52280297Sjkim store_bignum(X, databuf + 32, 32); 53280297Sjkim /* And reverse byte order of whole buffer */ 54280297Sjkim for (i = 0; i < 64; i++) { 55280297Sjkim hashbuf[63 - i] = databuf[i]; 56280297Sjkim } 57280297Sjkim init_gost_hash_ctx(&hash_ctx, &GostR3411_94_CryptoProParamSet); 58280297Sjkim start_hash(&hash_ctx); 59280297Sjkim hash_block(&hash_ctx, hashbuf, 64); 60280297Sjkim finish_hash(&hash_ctx, shared_key); 61280297Sjkim done_gost_hash_ctx(&hash_ctx); 62280297Sjkim BN_free(UKM); 63280297Sjkim BN_CTX_end(ctx); 64280297Sjkim BN_CTX_free(ctx); 65280297Sjkim EC_POINT_free(pnt); 66280297Sjkim return 32; 67280297Sjkim} 68238384Sjkim 69238384Sjkim/* 70238384Sjkim * EVP_PKEY_METHOD callback derive. Implements VKO R 34.10-2001 71238384Sjkim * algorithm 72238384Sjkim */ 73280297Sjkimint pkey_gost2001_derive(EVP_PKEY_CTX *ctx, unsigned char *key, 74280297Sjkim size_t *keylen) 75238384Sjkim{ 76280297Sjkim /* 77280297Sjkim * Public key of peer in the ctx field peerkey Our private key in the ctx 78280297Sjkim * pkey ukm is in the algorithm specific context data 79280297Sjkim */ 80280297Sjkim EVP_PKEY *my_key = EVP_PKEY_CTX_get0_pkey(ctx); 81280297Sjkim EVP_PKEY *peer_key = EVP_PKEY_CTX_get0_peerkey(ctx); 82280297Sjkim struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(ctx); 83238384Sjkim 84280297Sjkim if (!data->shared_ukm) { 85280297Sjkim GOSTerr(GOST_F_PKEY_GOST2001_DERIVE, GOST_R_UKM_NOT_SET); 86280297Sjkim return 0; 87280297Sjkim } 88238384Sjkim 89280297Sjkim if (key == NULL) { 90280297Sjkim *keylen = 32; 91280297Sjkim return 32; 92280297Sjkim } 93238384Sjkim 94280297Sjkim *keylen = 95280297Sjkim VKO_compute_key(key, 32, 96280297Sjkim EC_KEY_get0_public_key(EVP_PKEY_get0(peer_key)), 97280297Sjkim (EC_KEY *)EVP_PKEY_get0(my_key), data->shared_ukm); 98280297Sjkim return 1; 99280297Sjkim} 100238384Sjkim 101280297Sjkim/* 102280297Sjkim * EVP_PKEY_METHOD callback encrypt 103280297Sjkim * Implementation of GOST2001 key transport, cryptocom variation 104238384Sjkim */ 105280297Sjkim/* 106280297Sjkim * Generates ephemeral key based on pubk algorithm computes shared key using 107280297Sjkim * VKO and returns filled up GOST_KEY_TRANSPORT structure 108238384Sjkim */ 109238384Sjkim 110280297Sjkim/* 111280297Sjkim * EVP_PKEY_METHOD callback encrypt 112280297Sjkim * Implementation of GOST2001 key transport, cryptopo variation 113238384Sjkim */ 114238384Sjkim 115280297Sjkimint pkey_GOST01cp_encrypt(EVP_PKEY_CTX *pctx, unsigned char *out, 116280297Sjkim size_t *out_len, const unsigned char *key, 117280297Sjkim size_t key_len) 118280297Sjkim{ 119280297Sjkim GOST_KEY_TRANSPORT *gkt = NULL; 120280297Sjkim EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(pctx); 121280297Sjkim struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx); 122280297Sjkim const struct gost_cipher_info *param = get_encryption_params(NULL); 123280297Sjkim unsigned char ukm[8], shared_key[32], crypted_key[44]; 124280297Sjkim int ret = 0; 125280297Sjkim int key_is_ephemeral = 1; 126280297Sjkim gost_ctx cctx; 127280297Sjkim EVP_PKEY *sec_key = EVP_PKEY_CTX_get0_peerkey(pctx); 128280297Sjkim if (data->shared_ukm) { 129280297Sjkim memcpy(ukm, data->shared_ukm, 8); 130280297Sjkim } else if (out) { 131280297Sjkim 132280297Sjkim if (RAND_bytes(ukm, 8) <= 0) { 133280297Sjkim GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT, 134280297Sjkim GOST_R_RANDOM_GENERATOR_FAILURE); 135280297Sjkim return 0; 136280297Sjkim } 137280297Sjkim } 138280297Sjkim /* Check for private key in the peer_key of context */ 139280297Sjkim if (sec_key) { 140280297Sjkim key_is_ephemeral = 0; 141280297Sjkim if (!gost_get0_priv_key(sec_key)) { 142280297Sjkim GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT, 143280297Sjkim GOST_R_NO_PRIVATE_PART_OF_NON_EPHEMERAL_KEYPAIR); 144280297Sjkim goto err; 145280297Sjkim } 146280297Sjkim } else { 147280297Sjkim key_is_ephemeral = 1; 148280297Sjkim if (out) { 149280297Sjkim sec_key = EVP_PKEY_new(); 150306195Sjkim if (sec_key == NULL) 151306195Sjkim goto err; 152280297Sjkim EVP_PKEY_assign(sec_key, EVP_PKEY_base_id(pubk), EC_KEY_new()); 153280297Sjkim EVP_PKEY_copy_parameters(sec_key, pubk); 154280297Sjkim if (!gost2001_keygen(EVP_PKEY_get0(sec_key))) { 155280297Sjkim goto err; 156280297Sjkim } 157280297Sjkim } 158280297Sjkim } 159280297Sjkim if (!get_gost_engine_param(GOST_PARAM_CRYPT_PARAMS) 160280297Sjkim && param == gost_cipher_list) { 161280297Sjkim param = gost_cipher_list + 1; 162280297Sjkim } 163280297Sjkim if (out) { 164280297Sjkim VKO_compute_key(shared_key, 32, 165280297Sjkim EC_KEY_get0_public_key(EVP_PKEY_get0(pubk)), 166280297Sjkim EVP_PKEY_get0(sec_key), ukm); 167280297Sjkim gost_init(&cctx, param->sblock); 168280297Sjkim keyWrapCryptoPro(&cctx, shared_key, ukm, key, crypted_key); 169280297Sjkim } 170280297Sjkim gkt = GOST_KEY_TRANSPORT_new(); 171280297Sjkim if (!gkt) { 172280297Sjkim goto err; 173280297Sjkim } 174280297Sjkim if (!ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv, ukm, 8)) { 175280297Sjkim goto err; 176280297Sjkim } 177280297Sjkim if (!ASN1_OCTET_STRING_set(gkt->key_info->imit, crypted_key + 40, 4)) { 178280297Sjkim goto err; 179280297Sjkim } 180280297Sjkim if (!ASN1_OCTET_STRING_set 181280297Sjkim (gkt->key_info->encrypted_key, crypted_key + 8, 32)) { 182280297Sjkim goto err; 183280297Sjkim } 184280297Sjkim if (key_is_ephemeral) { 185280297Sjkim if (!X509_PUBKEY_set 186280297Sjkim (&gkt->key_agreement_info->ephem_key, out ? sec_key : pubk)) { 187280297Sjkim GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT, 188280297Sjkim GOST_R_CANNOT_PACK_EPHEMERAL_KEY); 189280297Sjkim goto err; 190280297Sjkim } 191280297Sjkim } 192280297Sjkim ASN1_OBJECT_free(gkt->key_agreement_info->cipher); 193280297Sjkim gkt->key_agreement_info->cipher = OBJ_nid2obj(param->nid); 194280297Sjkim if (key_is_ephemeral && sec_key) 195280297Sjkim EVP_PKEY_free(sec_key); 196280297Sjkim if (!key_is_ephemeral) { 197280297Sjkim /* Set control "public key from client certificate used" */ 198280297Sjkim if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL) 199280297Sjkim <= 0) { 200280297Sjkim GOSTerr(GOST_F_PKEY_GOST01CP_ENCRYPT, GOST_R_CTRL_CALL_FAILED); 201280297Sjkim goto err; 202280297Sjkim } 203280297Sjkim } 204280297Sjkim if ((*out_len = i2d_GOST_KEY_TRANSPORT(gkt, out ? &out : NULL)) > 0) 205280297Sjkim ret = 1; 206280297Sjkim GOST_KEY_TRANSPORT_free(gkt); 207280297Sjkim return ret; 208280297Sjkim err: 209280297Sjkim if (key_is_ephemeral && sec_key) 210280297Sjkim EVP_PKEY_free(sec_key); 211280297Sjkim GOST_KEY_TRANSPORT_free(gkt); 212280297Sjkim return -1; 213280297Sjkim} 214280297Sjkim 215280297Sjkim/* 216280297Sjkim * EVP_PKEY_METHOD callback decrypt 217280297Sjkim * Implementation of GOST2001 key transport, cryptopo variation 218238384Sjkim */ 219280297Sjkimint pkey_GOST01cp_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key, 220280297Sjkim size_t *key_len, const unsigned char *in, 221280297Sjkim size_t in_len) 222280297Sjkim{ 223280297Sjkim const unsigned char *p = in; 224280297Sjkim EVP_PKEY *priv = EVP_PKEY_CTX_get0_pkey(pctx); 225280297Sjkim GOST_KEY_TRANSPORT *gkt = NULL; 226280297Sjkim int ret = 0; 227280297Sjkim unsigned char wrappedKey[44]; 228280297Sjkim unsigned char sharedKey[32]; 229280297Sjkim gost_ctx ctx; 230280297Sjkim const struct gost_cipher_info *param = NULL; 231280297Sjkim EVP_PKEY *eph_key = NULL, *peerkey = NULL; 232238384Sjkim 233280297Sjkim if (!key) { 234280297Sjkim *key_len = 32; 235280297Sjkim return 1; 236280297Sjkim } 237280297Sjkim gkt = d2i_GOST_KEY_TRANSPORT(NULL, (const unsigned char **)&p, in_len); 238280297Sjkim if (!gkt) { 239280297Sjkim GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT, 240280297Sjkim GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO); 241280297Sjkim return -1; 242280297Sjkim } 243238384Sjkim 244280297Sjkim /* If key transport structure contains public key, use it */ 245280297Sjkim eph_key = X509_PUBKEY_get(gkt->key_agreement_info->ephem_key); 246280297Sjkim if (eph_key) { 247280297Sjkim if (EVP_PKEY_derive_set_peer(pctx, eph_key) <= 0) { 248280297Sjkim GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT, 249280297Sjkim GOST_R_INCOMPATIBLE_PEER_KEY); 250280297Sjkim goto err; 251280297Sjkim } 252280297Sjkim } else { 253280297Sjkim /* Set control "public key from client certificate used" */ 254280297Sjkim if (EVP_PKEY_CTX_ctrl(pctx, -1, -1, EVP_PKEY_CTRL_PEER_KEY, 3, NULL) 255280297Sjkim <= 0) { 256280297Sjkim GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT, GOST_R_CTRL_CALL_FAILED); 257280297Sjkim goto err; 258280297Sjkim } 259280297Sjkim } 260280297Sjkim peerkey = EVP_PKEY_CTX_get0_peerkey(pctx); 261280297Sjkim if (!peerkey) { 262280297Sjkim GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT, GOST_R_NO_PEER_KEY); 263238384Sjkim goto err; 264238384Sjkim } 265238384Sjkim 266280297Sjkim param = get_encryption_params(gkt->key_agreement_info->cipher); 267280297Sjkim if (!param) { 268280297Sjkim goto err; 269280297Sjkim } 270280297Sjkim 271280297Sjkim gost_init(&ctx, param->sblock); 272280297Sjkim OPENSSL_assert(gkt->key_agreement_info->eph_iv->length == 8); 273280297Sjkim memcpy(wrappedKey, gkt->key_agreement_info->eph_iv->data, 8); 274280297Sjkim OPENSSL_assert(gkt->key_info->encrypted_key->length == 32); 275280297Sjkim memcpy(wrappedKey + 8, gkt->key_info->encrypted_key->data, 32); 276280297Sjkim OPENSSL_assert(gkt->key_info->imit->length == 4); 277280297Sjkim memcpy(wrappedKey + 40, gkt->key_info->imit->data, 4); 278280297Sjkim VKO_compute_key(sharedKey, 32, 279280297Sjkim EC_KEY_get0_public_key(EVP_PKEY_get0(peerkey)), 280280297Sjkim EVP_PKEY_get0(priv), wrappedKey); 281280297Sjkim if (!keyUnwrapCryptoPro(&ctx, sharedKey, wrappedKey, key)) { 282280297Sjkim GOSTerr(GOST_F_PKEY_GOST01CP_DECRYPT, 283280297Sjkim GOST_R_ERROR_COMPUTING_SHARED_KEY); 284280297Sjkim goto err; 285280297Sjkim } 286280297Sjkim 287280297Sjkim ret = 1; 288280297Sjkim err: 289280297Sjkim if (eph_key) 290280297Sjkim EVP_PKEY_free(eph_key); 291280297Sjkim if (gkt) 292280297Sjkim GOST_KEY_TRANSPORT_free(gkt); 293280297Sjkim return ret; 294280297Sjkim} 295