Checking error codes and cleaning up when errors occur in EvpKeyAgreement

- Added a test to actually exercise the DH agreement path
//FREEBIE
pull/1/head
Craig Gidney 10 years ago committed by Frederic Jacobs
parent 4dd8df8049
commit e9f8881bd4

@ -1017,7 +1017,6 @@
A157071D17F0CD6D007C2BD6 /* ShortAuthenticationStringGeneratorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShortAuthenticationStringGeneratorTest.m; sourceTree = "<group>"; };
A157071F17F0CD6D007C2BD6 /* PregeneratedKeyAgreementParticipantProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PregeneratedKeyAgreementParticipantProtocol.h; sourceTree = "<group>"; };
A157072017F0CD6D007C2BD6 /* PregeneratedKeyAgreementParticipantProtocol.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PregeneratedKeyAgreementParticipantProtocol.m; sourceTree = "<group>"; };
A157072117F0CD6D007C2BD6 /* ZrtpTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZrtpTest.h; sourceTree = "<group>"; };
A157072217F0CD6D007C2BD6 /* ZrtpTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZrtpTest.m; sourceTree = "<group>"; };
A157072517F0CD6D007C2BD6 /* LowLatencyConnectorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LowLatencyConnectorTest.m; sourceTree = "<group>"; };
A157072817F0CD6D007C2BD6 /* NetworkStreamTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NetworkStreamTest.m; sourceTree = "<group>"; };
@ -2271,7 +2270,6 @@
A157071B17F0CD6D007C2BD6 /* MasterSecretTest.m */,
A157071D17F0CD6D007C2BD6 /* ShortAuthenticationStringGeneratorTest.m */,
A157071E17F0CD6D007C2BD6 /* utilities */,
A157072117F0CD6D007C2BD6 /* ZrtpTest.h */,
A157072217F0CD6D007C2BD6 /* ZrtpTest.m */,
E16E5BF818AAF02100B7C403 /* EC25AgreerTest.m */,
);

@ -17,6 +17,7 @@
#define ENVIRONMENT_TESTING_OPTION_LOSE_CONF_ACK_ON_PURPOSE @"LoseConfAck"
#define ENVIRONMENT_TESTING_OPTION_ALLOW_NETWORK_STREAM_TO_NON_SECURE_END_POINTS @"AllowTcpWithoutTls"
#define ENVIRONMENT_LEGACY_OPTION_RTP_PADDING_BIT_IMPLIES_EXTENSION_BIT_AND_TWELVE_EXTRA_ZERO_BYTES_IN_HEADER @"LegacyAndroidInterop_1"
#define TESTING_OPTION_USE_DH_FOR_HANDSHAKE @"DhKeyAgreementOnly"
@class RecentCallManager;
@class ContactsManager;

@ -67,6 +67,11 @@ static unsigned char DH3K_PRIME[]={
}
+(Environment*) unitTestEnvironment:(NSArray*)testingAndLegacyOptions {
NSArray* keyAgreementProtocols = self.supportedKeyAgreementProtocols;
if ([testingAndLegacyOptions containsObject:TESTING_OPTION_USE_DH_FOR_HANDSHAKE]) {
keyAgreementProtocols = @[[Release supportedDH3KKeyAgreementProtocol]];
}
return [Environment environmentWithLogging:[DiscardingLog discardingLog]
andErrorNoter:^(id error, id relatedInfo, bool causedTermination) {}
andServerPort:31337
@ -75,7 +80,7 @@ static unsigned char DH3K_PRIME[]={
andRelayServerHostNameSuffix:@"whispersystems.org"
andCertificate:[Certificate certificateFromResourcePath:@"whisperReal" ofType:@"cer"]
andCurrentRegionCodeForPhoneNumbers:@"US"
andSupportedKeyAgreementProtocols:[self supportedKeyAgreementProtocols]
andSupportedKeyAgreementProtocols:keyAgreementProtocols
andPhoneManager:nil
andRecentCallManager:nil
andTestingAndLegacyOptions:testingAndLegacyOptions

@ -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:&params];
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, &params);
checkEvpSucess(ret,@"pctx_paramgen" );
checkEvpOperationResult(EVP_PKEY_paramgen(pctx, &params));
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

@ -1,17 +0,0 @@
#import <XCTest/XCTest.h>
#import "ZrtpManager.h"
#import "HelloPacket.h"
#import "ConfirmPacket.h"
#import "DhPacket.h"
#import "CommitPacket.h"
#import "HandshakePacket.h"
#import "Util.h"
#import "DH3KKeyAgreementProtocol.h"
#import "PregeneratedKeyAgreementParticipantProtocol.h"
#import "MasterSecret.h"
#import "ZrtpResponder.h"
#import "ZrtpInitiator.h"
@interface ZrtpTest : XCTestCase
@end

@ -1,6 +1,8 @@
#import "ZrtpTest.h"
#import "ZrtpHandshakeResult.h"
#import <XCTest/XCTest.h>
#import "TestUtil.h"
#import "Util.h"
#import "CallController.h"
#import "ZrtpManager.h"
#import "ThreadManager.h"
#import "ZrtpHandshakeResult.h"
#import "DiscardingLog.h"
@ -19,6 +21,9 @@ bool pm(HandshakePacket* p1, HandshakePacket* p2) {
}
#define AssertPacketsMatch(p1, p2) STAssertTrue(pm(p1, p2), @"")
@interface ZrtpTest : XCTestCase
@end
@implementation ZrtpTest
- (void)setUp{
@ -94,4 +99,32 @@ bool pm(HandshakePacket* p1, HandshakePacket* p2) {
[cc2 terminateWithReason:CallTerminationType_HangupLocal withFailureInfo:nil andRelatedInfo:nil];
}
-(void) testDhHandshake {
[Environment setCurrent:testEnvWith(TESTING_OPTION_USE_DH_FOR_HANDSHAKE)];
IpEndPoint* receiver = [IpEndPoint ipEndPointAtAddress:[IpAddress localhost]
onPort:10000 + (in_port_t)arc4random_uniform(20000)];
UdpSocket* u1 = [UdpSocket udpSocketToFirstSenderOnLocalPort:receiver.port];
CallController* cc1 = [CallController callControllerForCallInitiatedLocally:true
withRemoteNumber:testPhoneNumber1
andOptionallySpecifiedContact:nil];
TOCFuture* f1 = [ZrtpManager asyncPerformHandshakeOver:[RtpSocket rtpSocketOverUdp:u1 interopOptions:@[]]
andCallController:cc1];
UdpSocket* u2 = [UdpSocket udpSocketTo:receiver];
CallController* cc2 = [CallController callControllerForCallInitiatedLocally:false
withRemoteNumber:testPhoneNumber2
andOptionallySpecifiedContact:nil];
TOCFuture* f2 = [ZrtpManager asyncPerformHandshakeOver:[RtpSocket rtpSocketOverUdp:u2 interopOptions:@[]]
andCallController:cc2];
testChurnUntil(!f1.isIncomplete && !f2.isIncomplete, 15.0);
test(f1.hasResult);
test(f2.hasResult);
[cc1 terminateWithReason:CallTerminationType_HangupLocal withFailureInfo:nil andRelatedInfo:nil];
[cc2 terminateWithReason:CallTerminationType_HangupLocal withFailureInfo:nil andRelatedInfo:nil];
}
@end

Loading…
Cancel
Save