|
|
|
@ -1,12 +1,11 @@
|
|
|
|
|
#import "EvpKeyAgreement.h"
|
|
|
|
|
#import "Constraints.h"
|
|
|
|
|
#import "NumberUtil.h"
|
|
|
|
|
#import "Util.h"
|
|
|
|
|
#import <openssl/bn.h>
|
|
|
|
|
#import <openssl/dh.h>
|
|
|
|
|
#import <openssl/ec.h>
|
|
|
|
|
#import <openssl/pem.h>
|
|
|
|
|
|
|
|
|
|
#define checkEvpSucess(expr, desc) checkSecurityOperation((expr) == 1, desc)
|
|
|
|
|
#define checkEvpOperationResult(expr) checkSecurityOperation((expr) == 1, @"An elliptic curve operation didn't succeed.")
|
|
|
|
|
#define checkEvpNotNull(expr, desc) checkSecurityOperation((expr) != NULL, desc)
|
|
|
|
|
|
|
|
|
|
#define EC25_COORDINATE_LENGTH 32
|
|
|
|
@ -64,8 +63,7 @@ enum KeyAgreementType {
|
|
|
|
|
ctx = EVP_PKEY_CTX_new_id(keyAgreementType, NULL);
|
|
|
|
|
checkEvpNotNull(ctx , @"pctx_new_id");
|
|
|
|
|
|
|
|
|
|
int ret = EVP_PKEY_paramgen_init(ctx);
|
|
|
|
|
checkEvpSucess(ret, @"paramgen_init");
|
|
|
|
|
checkEvpOperationResult(EVP_PKEY_paramgen_init(ctx));
|
|
|
|
|
|
|
|
|
|
return ctx;
|
|
|
|
|
}
|
|
|
|
@ -79,15 +77,15 @@ enum KeyAgreementType {
|
|
|
|
|
|
|
|
|
|
@try{
|
|
|
|
|
checkEvpNotNull(dh, @"dh_new");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dh->p= [self generateBignumberFor:modulus];
|
|
|
|
|
dh->g= [self generateBignumberFor:generator];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((dh->p == NULL) || (dh->g == NULL))
|
|
|
|
|
{
|
|
|
|
|
[self reportError:@"DH Parameters uninitialized"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[self createNewEvpKeyFreePreviousIfNessesary:¶ms];
|
|
|
|
|
EVP_PKEY_set1_DH(params, dh);
|
|
|
|
|
|
|
|
|
@ -100,56 +98,47 @@ enum KeyAgreementType {
|
|
|
|
|
-(void) generateEc25Parameters {
|
|
|
|
|
EVP_PKEY_CTX* pctx = [self createParameterContext];
|
|
|
|
|
|
|
|
|
|
int ret;
|
|
|
|
|
ret = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NAMED_ELLIPTIC_CURVE);
|
|
|
|
|
checkEvpSucess(ret, @"pctx_ec_init");
|
|
|
|
|
checkEvpOperationResult(EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NAMED_ELLIPTIC_CURVE));
|
|
|
|
|
|
|
|
|
|
ret = EVP_PKEY_paramgen(pctx, ¶ms);
|
|
|
|
|
checkEvpSucess(ret,@"pctx_paramgen" );
|
|
|
|
|
checkEvpOperationResult(EVP_PKEY_paramgen(pctx, ¶ms));
|
|
|
|
|
|
|
|
|
|
EVP_PKEY_CTX_free(pctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-(void) generateKeyPair {
|
|
|
|
|
int ret;
|
|
|
|
|
EVP_PKEY_CTX* kctx;
|
|
|
|
|
|
|
|
|
|
checkEvpNotNull(params, @"parameters uninitialized");
|
|
|
|
|
|
|
|
|
|
kctx = EVP_PKEY_CTX_new(params, NULL);
|
|
|
|
|
checkEvpNotNull(kctx, @"key_ctx");
|
|
|
|
|
|
|
|
|
|
ret = EVP_PKEY_keygen_init(kctx);
|
|
|
|
|
checkEvpSucess(ret, @"keygen_init");
|
|
|
|
|
|
|
|
|
|
ret = EVP_PKEY_keygen(kctx, &pkey);
|
|
|
|
|
checkEvpSucess(ret, @"keygen");
|
|
|
|
|
|
|
|
|
|
EVP_PKEY_CTX_free(kctx);
|
|
|
|
|
EVP_PKEY_CTX* kctx = NULL;
|
|
|
|
|
@try {
|
|
|
|
|
kctx = EVP_PKEY_CTX_new(params, NULL);
|
|
|
|
|
checkEvpNotNull(kctx, @"key_ctx");
|
|
|
|
|
|
|
|
|
|
checkEvpOperationResult(EVP_PKEY_keygen_init(kctx));
|
|
|
|
|
|
|
|
|
|
checkEvpOperationResult(EVP_PKEY_keygen(kctx, &pkey));
|
|
|
|
|
} @finally {
|
|
|
|
|
if (kctx != NULL) EVP_PKEY_CTX_free(kctx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-(NSData*) getSharedSecretForRemotePublicKey:(NSData*) publicKey{
|
|
|
|
|
size_t secret_len;
|
|
|
|
|
unsigned char* secret;
|
|
|
|
|
-(NSData*) getSharedSecretForRemotePublicKey:(NSData*)publicKey {
|
|
|
|
|
EVP_PKEY* peerkey = [self deserializePublicKey:publicKey];
|
|
|
|
|
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new(pkey, NULL);
|
|
|
|
|
checkEvpNotNull(ctx, @"ctx_new");
|
|
|
|
|
|
|
|
|
|
EVP_PKEY* peerkey = [self deserializePublicKey:[publicKey bytes] withLength:publicKey.length];
|
|
|
|
|
EVP_PKEY_CTX* ctx;
|
|
|
|
|
checkEvpOperationResult(EVP_PKEY_derive_init(ctx));
|
|
|
|
|
checkEvpOperationResult(EVP_PKEY_derive_set_peer(ctx, peerkey));
|
|
|
|
|
|
|
|
|
|
size_t secret_len;
|
|
|
|
|
checkEvpOperationResult(EVP_PKEY_derive(ctx, NULL, &secret_len));
|
|
|
|
|
|
|
|
|
|
ctx = EVP_PKEY_CTX_new(pkey, NULL);
|
|
|
|
|
checkEvpNotNull(ctx, @"ctx_new");
|
|
|
|
|
unsigned char* secret = OPENSSL_malloc(secret_len);
|
|
|
|
|
checkEvpNotNull(secret, @"OPENSSL_malloc");
|
|
|
|
|
|
|
|
|
|
checkEvpSucess(EVP_PKEY_derive_init(ctx), @"derive_init");
|
|
|
|
|
checkEvpSucess(EVP_PKEY_derive_set_peer(ctx, peerkey), @"set_peer");
|
|
|
|
|
checkEvpSucess(EVP_PKEY_derive(ctx, NULL, &secret_len), @"derive_step1");
|
|
|
|
|
checkEvpOperationResult(EVP_PKEY_derive(ctx, secret, &secret_len));
|
|
|
|
|
|
|
|
|
|
secret = OPENSSL_malloc(secret_len);
|
|
|
|
|
checkEvpNotNull(secret, @"secret_malloc");
|
|
|
|
|
|
|
|
|
|
checkEvpSucess(EVP_PKEY_derive(ctx, secret, &secret_len), @"derive_step2");
|
|
|
|
|
|
|
|
|
|
NSData* secretData = [NSData dataWithBytes:secret length:secret_len];
|
|
|
|
|
|
|
|
|
|
EVP_PKEY_CTX_free(ctx);
|
|
|
|
@ -173,80 +162,124 @@ enum KeyAgreementType {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(NSData*) serializeDhPublicKey:(EVP_PKEY*)evkey {
|
|
|
|
|
|
|
|
|
|
DH* dh;
|
|
|
|
|
unsigned char* buf;
|
|
|
|
|
|
|
|
|
|
dh = EVP_PKEY_get1_DH(evkey);
|
|
|
|
|
|
|
|
|
|
int bufsize = BN_num_bytes(dh->pub_key);
|
|
|
|
|
buf = OPENSSL_malloc(bufsize);
|
|
|
|
|
checkEvpNotNull(buf, @"dh_pubkey_buffer");
|
|
|
|
|
|
|
|
|
|
BN_bn2bin(dh->pub_key, buf);
|
|
|
|
|
NSData* pub_key = [NSData dataWithBytes:buf length:(unsigned int)bufsize];
|
|
|
|
|
|
|
|
|
|
DH_free(dh);
|
|
|
|
|
OPENSSL_free(buf);
|
|
|
|
|
|
|
|
|
|
return pub_key;
|
|
|
|
|
DH* dh = NULL;
|
|
|
|
|
unsigned char* buf = NULL;
|
|
|
|
|
@try {
|
|
|
|
|
dh = EVP_PKEY_get1_DH(evkey);
|
|
|
|
|
checkEvpNotNull(dh, @"EVP_PKEY_get1_DH");
|
|
|
|
|
|
|
|
|
|
int publicKeySize = BN_num_bytes(dh->pub_key);
|
|
|
|
|
NSMutableData* publicKeyBuffer = [NSMutableData dataWithLength:(NSUInteger)publicKeySize];
|
|
|
|
|
|
|
|
|
|
int wroteLength = BN_bn2bin(dh->pub_key, publicKeyBuffer.mutableBytes);
|
|
|
|
|
checkSecurityOperation(wroteLength == (long long)publicKeyBuffer.length, @"BN_bn2bin");
|
|
|
|
|
|
|
|
|
|
return publicKeyBuffer;
|
|
|
|
|
} @finally {
|
|
|
|
|
if (dh != NULL) DH_free(dh);
|
|
|
|
|
if (buf != NULL) OPENSSL_free(buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-(NSData*) serializeEcPublicKey:(EVP_PKEY*)evkey {
|
|
|
|
|
require(evkey != NULL);
|
|
|
|
|
|
|
|
|
|
EC_KEY* ec_key = EVP_PKEY_get1_EC_KEY(evkey);
|
|
|
|
|
const EC_POINT* ec_pub = EC_KEY_get0_public_key(ec_key);
|
|
|
|
|
const EC_GROUP* ec_group = EC_KEY_get0_group(ec_key);
|
|
|
|
|
|
|
|
|
|
NSData* data = [self packEcCoordinatesFromEcPoint:ec_pub withEcGroup:ec_group];
|
|
|
|
|
|
|
|
|
|
EC_KEY_free(ec_key);
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
EC_KEY* ec_key = NULL;
|
|
|
|
|
@try {
|
|
|
|
|
ec_key = EVP_PKEY_get1_EC_KEY(evkey);
|
|
|
|
|
checkEvpNotNull(ec_key, @"EVP_PKEY_get1_EC_KEY");
|
|
|
|
|
|
|
|
|
|
const EC_POINT* ec_pub = EC_KEY_get0_public_key(ec_key);
|
|
|
|
|
checkEvpNotNull(ec_pub, @"EC_KEY_get0_public_key");
|
|
|
|
|
|
|
|
|
|
const EC_GROUP* ec_group = EC_KEY_get0_group(ec_key);
|
|
|
|
|
checkEvpNotNull(ec_group, @"EC_KEY_get0_group");
|
|
|
|
|
|
|
|
|
|
return [self packEcCoordinatesFromEcPoint:ec_pub withEcGroup:ec_group];
|
|
|
|
|
} @finally {
|
|
|
|
|
EC_KEY_free(ec_key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(EVP_PKEY*) deserializePublicKey:(const unsigned char*) buf withLength:(size_t) bufsize {
|
|
|
|
|
-(EVP_PKEY*) deserializePublicKey:(NSData*)buf {
|
|
|
|
|
switch (keyAgreementType) {
|
|
|
|
|
case KeyAgreementType_DH:
|
|
|
|
|
return [self deserializeDhPublicKey:buf withLength:bufsize];
|
|
|
|
|
return [self deserializeDhPublicKey:buf];
|
|
|
|
|
case KeyAgreementType_ECDH:
|
|
|
|
|
return [self deserializeEcPublicKey:buf withLength:bufsize];
|
|
|
|
|
return [self deserializeEcPublicKey:buf];
|
|
|
|
|
default:
|
|
|
|
|
[self reportError:@"Undefined KeyType"];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(EVP_PKEY*) deserializeDhPublicKey:(const unsigned char*) buf withLength:(size_t) bufsize {
|
|
|
|
|
|
|
|
|
|
EVP_PKEY* evpk = EVP_PKEY_new();
|
|
|
|
|
DH* dh = DH_new();
|
|
|
|
|
|
|
|
|
|
BIGNUM* bn = BN_new();
|
|
|
|
|
BN_bin2bn(buf, [NumberUtil assertConvertNSUIntegerToInt:bufsize], bn);
|
|
|
|
|
dh->pub_key = bn;
|
|
|
|
|
|
|
|
|
|
EVP_PKEY_assign_DH(evpk, dh);
|
|
|
|
|
|
|
|
|
|
return evpk;
|
|
|
|
|
-(EVP_PKEY*) deserializeDhPublicKey:(NSData*)buf {
|
|
|
|
|
EVP_PKEY* evpk = NULL;
|
|
|
|
|
DH* dh = NULL;
|
|
|
|
|
BIGNUM* bn = NULL;
|
|
|
|
|
@try {
|
|
|
|
|
evpk = EVP_PKEY_new();
|
|
|
|
|
checkEvpNotNull(evpk, @"EVP_PKEY_new");
|
|
|
|
|
|
|
|
|
|
dh = DH_new();
|
|
|
|
|
checkEvpNotNull(dh, @"DH_new");
|
|
|
|
|
|
|
|
|
|
bn = BN_bin2bn(buf.bytes, [NumberUtil assertConvertNSUIntegerToInt:buf.length], NULL);
|
|
|
|
|
checkEvpNotNull(bn, @"BN_bin2bn");
|
|
|
|
|
|
|
|
|
|
dh->pub_key = bn;
|
|
|
|
|
checkEvpOperationResult(EVP_PKEY_assign_DH(evpk, dh));
|
|
|
|
|
|
|
|
|
|
// Return without cleaning up the result
|
|
|
|
|
EVP_PKEY* result = evpk;
|
|
|
|
|
evpk = NULL;
|
|
|
|
|
dh = NULL;
|
|
|
|
|
bn = NULL;
|
|
|
|
|
return result;
|
|
|
|
|
} @finally {
|
|
|
|
|
if (evpk != NULL) EVP_PKEY_free(evpk);
|
|
|
|
|
if (dh != NULL) DH_free(dh);
|
|
|
|
|
if (bn != NULL) BN_free(bn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-(EVP_PKEY*) deserializeEcPublicKey:(const unsigned char*) buf withLength:(size_t) bufsize {
|
|
|
|
|
EC_KEY* eck = EC_KEY_new_by_curve_name(NAMED_ELLIPTIC_CURVE);
|
|
|
|
|
const EC_GROUP* ecg = EC_KEY_get0_group(eck);
|
|
|
|
|
|
|
|
|
|
EC_POINT* ecp = EC_POINT_new(ecg);
|
|
|
|
|
[self unpackEcCoordinatesFromBuffer:buf ofSize:bufsize toEcPoint:ecp withEcGroup:ecg];
|
|
|
|
|
|
|
|
|
|
EVP_PKEY* evpk = EVP_PKEY_new();
|
|
|
|
|
EC_KEY_set_public_key(eck, ecp);
|
|
|
|
|
EVP_PKEY_assign_EC_KEY(evpk, eck);
|
|
|
|
|
|
|
|
|
|
EC_POINT_free(ecp);
|
|
|
|
|
|
|
|
|
|
return evpk;
|
|
|
|
|
|
|
|
|
|
-(EVP_PKEY*) deserializeEcPublicKey:(NSData*)buf {
|
|
|
|
|
EC_KEY* key = NULL;
|
|
|
|
|
EC_POINT* publicKeyPoint = NULL;
|
|
|
|
|
EVP_PKEY* publicKey = NULL;
|
|
|
|
|
@try {
|
|
|
|
|
key = EC_KEY_new_by_curve_name(NAMED_ELLIPTIC_CURVE);
|
|
|
|
|
checkSecurityOperation(key != NULL, @"EC_KEY_new_by_curve_name");
|
|
|
|
|
|
|
|
|
|
const EC_GROUP* group = EC_KEY_get0_group(key);
|
|
|
|
|
checkSecurityOperation(group != NULL, @"EC_KEY_get0_group");
|
|
|
|
|
|
|
|
|
|
publicKeyPoint = EC_POINT_new(group);
|
|
|
|
|
checkSecurityOperation(publicKeyPoint != NULL, @"EC_POINT_new");
|
|
|
|
|
|
|
|
|
|
[self unpackEcCoordinatesFromBuffer:buf
|
|
|
|
|
toEcPoint:publicKeyPoint
|
|
|
|
|
withEcGroup:group];
|
|
|
|
|
|
|
|
|
|
publicKey = EVP_PKEY_new();
|
|
|
|
|
checkSecurityOperation(publicKey != NULL, @"EVP_PKEY_new");
|
|
|
|
|
|
|
|
|
|
checkEvpOperationResult(EC_KEY_set_public_key(key, publicKeyPoint));
|
|
|
|
|
|
|
|
|
|
checkEvpOperationResult(EVP_PKEY_assign_EC_KEY(publicKey, key));
|
|
|
|
|
|
|
|
|
|
// Return without cleaning up the result
|
|
|
|
|
EVP_PKEY* result = publicKey;
|
|
|
|
|
publicKey = NULL;
|
|
|
|
|
key = NULL;
|
|
|
|
|
publicKeyPoint = NULL;
|
|
|
|
|
return result;
|
|
|
|
|
} @finally {
|
|
|
|
|
if (key != NULL) EC_KEY_free(key);
|
|
|
|
|
if (publicKeyPoint != NULL) EC_POINT_free(publicKeyPoint);
|
|
|
|
|
if (publicKey != NULL) EVP_PKEY_free(publicKey);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(NSData*) packEcCoordinatesFromEcPoint:(const EC_POINT*) ec_point withEcGroup:(const EC_GROUP*) ec_group {
|
|
|
|
@ -256,16 +289,16 @@ enum KeyAgreementType {
|
|
|
|
|
x = BN_new();
|
|
|
|
|
y = BN_new();
|
|
|
|
|
checkSecurityOperation(x != NULL && y != NULL, @"BN_new");
|
|
|
|
|
|
|
|
|
|
checkSecurityOperation(1 == EC_POINT_get_affine_coordinates_GFp(ec_group, ec_point, x, y, NULL), @"EC_POINT_get_affine_coordinates_GFp");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
checkEvpOperationResult(EC_POINT_get_affine_coordinates_GFp(ec_group, ec_point, x, y, NULL));
|
|
|
|
|
|
|
|
|
|
int len_x = BN_num_bytes(x);
|
|
|
|
|
int len_y = BN_num_bytes(y);
|
|
|
|
|
checkSecurityOperation(len_x >= 0 && len_x <= EC25_COORDINATE_LENGTH, @"BN_num_bytes(x)");
|
|
|
|
|
checkSecurityOperation(len_y >= 0 && len_y <= EC25_COORDINATE_LENGTH, @"BN_num_bytes(y)");
|
|
|
|
|
int unused_x = EC25_COORDINATE_LENGTH - len_x;
|
|
|
|
|
int unused_y = EC25_COORDINATE_LENGTH - len_y;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NSMutableData* data = [NSMutableData dataWithLength:EC25_COORDINATE_LENGTH*2];
|
|
|
|
|
|
|
|
|
|
// We offset the writes to keep things constant sized.
|
|
|
|
@ -281,23 +314,25 @@ enum KeyAgreementType {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
-(void) unpackEcCoordinatesFromBuffer:(const unsigned char*) buffer
|
|
|
|
|
ofSize:(size_t) bufsize
|
|
|
|
|
toEcPoint:(EC_POINT*) ecp
|
|
|
|
|
withEcGroup:(const EC_GROUP*) ecg {
|
|
|
|
|
|
|
|
|
|
checkOperation(2*EC25_COORDINATE_LENGTH == bufsize);
|
|
|
|
|
|
|
|
|
|
BIGNUM* x = BN_new();
|
|
|
|
|
BIGNUM* y = BN_new();
|
|
|
|
|
-(void) unpackEcCoordinatesFromBuffer:(NSData*)buffer
|
|
|
|
|
toEcPoint:(EC_POINT*)ecp
|
|
|
|
|
withEcGroup:(const EC_GROUP*)ecg {
|
|
|
|
|
|
|
|
|
|
BN_bin2bn(buffer, EC25_COORDINATE_LENGTH, x);
|
|
|
|
|
BN_bin2bn(buffer+EC25_COORDINATE_LENGTH, EC25_COORDINATE_LENGTH, y);
|
|
|
|
|
checkOperation(buffer.length == 2*EC25_COORDINATE_LENGTH);
|
|
|
|
|
|
|
|
|
|
EC_POINT_set_affine_coordinates_GFp(ecg, ecp, x, y, NULL);
|
|
|
|
|
|
|
|
|
|
BN_free(x);
|
|
|
|
|
BN_free(y);
|
|
|
|
|
BIGNUM* x = NULL;
|
|
|
|
|
BIGNUM* y = NULL;
|
|
|
|
|
@try {
|
|
|
|
|
const unsigned char* bytes = buffer.bytes;
|
|
|
|
|
x = BN_bin2bn(bytes, EC25_COORDINATE_LENGTH, NULL);
|
|
|
|
|
y = BN_bin2bn(bytes + EC25_COORDINATE_LENGTH, EC25_COORDINATE_LENGTH, NULL);
|
|
|
|
|
checkSecurityOperation(x != NULL && y != NULL, @"BN_bin2bn");
|
|
|
|
|
|
|
|
|
|
checkEvpOperationResult(EC_POINT_set_affine_coordinates_GFp(ecg, ecp, x, y, NULL));
|
|
|
|
|
} @finally {
|
|
|
|
|
if (x != NULL) BN_free(x);
|
|
|
|
|
if (y != NULL) BN_free(y);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark Helper Functions
|
|
|
|
@ -324,9 +359,4 @@ enum KeyAgreementType {
|
|
|
|
|
[SecurityFailure raise:[NSString stringWithFormat:@"Security related failure: %@ (in %s at line %d)", errorString,__FILE__,__LINE__]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|