I am trying to convert an ephemeralKey
which is a series of bytes to an Elliptic Curve public key and then use it to create a shared key using my private key.
I know how to do this in python (code below). But I cannot find a way to do this in Swift. My Swift code is also copied below but it is not correct. Do you see the problem with my Swift code?
My python code:
from cryptography.hazmat.primitives.asymmetric import ec
privateKey = ec.generate_private_key(ec.SECP256R1())
devicePublicKey = ec.EllipticCurvePublicKey.from_encoded_point(ec.SECP256R1(), ephemeralKey)
sharedKey = privateKey.exchange(ec.ECDH(), devicePublicKey)
My Swift code:
let privateKeyParams: [String: Any] = [
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256
]
var error: Unmanaged<CFError>?
let privateKey = SecKeyCreateRandomKey(privateKeyParams as CFDictionary, &error)
let attributes: [String:Any] =
[
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
]
let devicePublicKey = SecKeyCreateWithData(ephemeralKey as CFData, attributes as CFDictionary, nil)!
let sharedKey = ecdhSecretCalculation(publicKey: devicePublicKey, privateKey: privateKey)
func ecdhSecretCalculation(publicKey: SecKey, privateKey: SecKey) -> Data? {
var error: Unmanaged<CFError>?
let keyPairAttr:[String : Any] = [
kSecAttrKeySizeInBits as String: 256,
SecKeyKeyExchangeParameter.requestedSize.rawValue as String: 32,
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecPrivateKeyAttrs as String: [kSecAttrIsPermanent as String: false],
kSecPublicKeyAttrs as String:[kSecAttrIsPermanent as String: false]
]
let algorithm:SecKeyAlgorithm = .ecdhKeyExchangeStandardX963SHA256
let shared = SecKeyCopyKeyExchangeResult(privateKey, algorithm, publicKey, keyPairAttr as CFDictionary, &error) as Data?
return shared
}
The reason for the different shared secrets is that
ecdhKeyExchangeStandardX963SHA256
is applied in the posted Swift code. Instead,ecdhKeyExchangeStandard
must be applied.Also, bear in mind that
SecKeyCreateWithData()
in the Swift code expects the private key to be the concatenation of the uncompressed public key (0x04|x|y
) and the raw private key:0x04|x|y|private
, while in the Python code only the raw private key is specified (this is only for the sake of completeness, since the posted codes do not show how the private key was imported).If this is taken into account, both codes provide the same shared secret.
Test:
Python:
Swift:
Both codes provide the same shared secret.
If the
ecdhKeyExchangeStandardX963SHA256
algorithm is used in the Swift code, the shared secret is additionally processed with ANSI-X9.63-KDF. The Cryptography library also provides thisX963KDF
:For the reasons why a KDF is generally used afterwards, see e.g. here. Note that the X9.63-KDF is only one possibility, there are others, see e.g. here.