Hi,
I am trying to perform Diffie Hellman Key exchange between an iPad App and a microcontroller. I have successfully performed DH Key exchange between the microcontroller and PCs running .Net and PCs running Java using BouncyCastle. The particular DH I am using is ECDH curve secP256r1. For some reason I am unable to get the shared secret generated in iOS to match the shared secret generated in my microcontroller.
XCode version is 9.0.1. iOS version in iPad is 10.3 and MacOS is 10.12.6 Sierra.
Code snippet for ECDH is below. The 32 bytes in shared_secret did not match the 32 bytes in my microcontroller. I've checked before and after the SHA256 hash in my microcontroller.
Please help. Thank you.
-Yan
#define DH_PUBLIC_KEY_SIZE 64
Byte m_dh_public_key[DH_PUBLIC_KEY_SIZE+1];
SecKeyRef m_privateKey;
bool m_is_peripheral_key_rcvd = false;
Byte m_dh_peripheral_public_key[DH_PUBLIC_KEY_SIZE+1];
Byte m_dh_shared_secret[DH_PUBLIC_KEY_SIZE/2];
-(Byte *)GenerateDHKeyPair
{
NSData *tag = [@"DH.Keys" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *p_attributes =
@{ (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
(id)kSecAttrKeySizeInBits: @256,
(id)kSecPrivateKeyAttrs:
@{ (id)kSecAttrIsPermanent: @YES,
(id)kSecAttrApplicationTag: tag,
},
};
CFErrorRef error = NULL;
m_privateKey = SecKeyCreateRandomKey((__bridge CFDictionaryRef)p_attributes,
&error);
if (m_privateKey == NULL)
{
// Handle the error
}
else
{
SecKeyRef publicKey = SecKeyCopyPublicKey(m_privateKey);
if (publicKey == NULL)
{
// Handle the error
}
NSData* keyData = (NSData*)CFBridgingRelease( // ARC takes ownership
SecKeyCopyExternalRepresentation(publicKey, &error)
);
memcpy(m_dh_public_key, [keyData bytes], 65);
}
return(m_dh_public_key);
}
- (void)SendPublicKey
{
Byte *p_public_key = [self GenerateDHKeyPair];
m_is_peripheral_key_rcvd = false;
//build host key message
BLEMsgHostPublicKey_t host_key_msg;
host_key_msg.msg_hdr.msg_id = MESSAGE_BASE_EVENT + EVENT_HOST_PUBLIC_KEY;
host_key_msg.msg_hdr.msg_body_sz = sizeof(host_key_msg.host_key);
// first byte of key blob is header - skip it. Only copy key itself (x and y)
memcpy((uint8_t *)host_key_msg.host_key.key, p_public_key + 1, sizeof(host_key_msg.host_key));
// Send Key To external device
[self SendData:(uint8_t *)&host_key_msg wNumBytes:sizeof(host_key_msg)];
}
- (void)HandlePeripheralPublicKey: (uint8_t *)m_rcvd_msg_ptr
{
memcpy(m_dh_peripheral_public_key + 1, m_rcvd_msg_ptr, sizeof(m_dh_peripheral_public_key) - 1);
m_dh_peripheral_public_key[0] = m_dh_public_key[0]; // one byte of header
m_is_peripheral_key_rcvd = true;
NSData *peripheral_pub_key = [NSData dataWithBytes:m_dh_peripheral_public_key length:sizeof(m_dh_peripheral_public_key)];
// transform rcvd key into SecKey format
NSDictionary *p_pub_key_attr =
@{ (id)kSecAttrKeyType: (id)kSecAttrKeyTypeECSECPrimeRandom,
(id)kSecAttrKeyClass: (id)kSecAttrKeyClassPublic,
(id)kSecAttrKeySizeInBits: @256,
};
CFErrorRef error = NULL;
SecKeyRef public_key = SecKeyCreateWithData((__bridge CFDataRef)peripheral_pub_key,
(__bridge CFDictionaryRef)p_pub_key_attr, &error);
NSDictionary *p_dh_options =
@{ (id)kSecKeyKeyExchangeParameterRequestedSize: @32,
};
CFDataRef shared_secret;
if(m_privateKey != NULL)
// generate shared secret
shared_secret = SecKeyCopyKeyExchangeResult(m_privateKey, kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA256, public_key,
(__bridge CFDictionaryRef)p_dh_options, &error);
NSData* keyData = (NSData*)CFBridgingRelease(shared_secret);
memcpy(m_dh_shared_secret, [keyData bytes], 32);
}
I ended up ditching the Security Framework and used a C-based open source ECDH module instead. After that, using the CommonCrypto library to perform SHA hashing and AES encryption was suprisingly easy. As I already have a solution, I will not open an incident. Thank you for the response.