swiftencryptioncryptographysecp256k1hmacsha256

Swift Key derivation. Public key decoding


I need a help with creating P256.Signing.PublicKey from child key that were got with using HMAC-Sha256 hashing (we implemented key derivation). And after that get a SecKey to create signature for messages

I can't understand how to decode it. Child keys in future will be used for keys exchange, signing and encrypting messages.

Keys example where taken from here :

private: eaa31c2e46ca2962227cf21d73a7ef0ce8b31c756897521eb6c7b39796633357
public: 02c9e16154474b3ed5b38218bb0463e008f89ee03e62d22fdcc8014beab25b48fa

So in future other device will send me such public key

02c9e16154474b3ed5b38218bb0463e008f89ee03e62d22fdcc8014beab25b48fa

And I should be able to decode it.

For now i'm able to decode private key in such way

  1. Make data from key by converting it from hex string
extension String {
   var hexData: Data? {
       var data = Data(capacity: count / 2)
       let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
       regex.enumerateMatches(in: self, range: NSRange(startIndex..., in: self)) { match, _, _ in
           let byteString = (self as NSString).substring(with: match!.range)
           let num = UInt8(byteString, radix: 16)!
           data.append(num)
       }
       guard data.count > 0 else { return nil }
       return data
   }
}

It returns me a Data with 32 bytes length

  1. Make the key from the data
let key = try P256.Signing.PrivateKey.init(rawRepresentation: hexData) 
  1. Create SecKey from the private key
let attributes = [
        kSecAttrKeyType: kSecAttrKeyTypeEC,
        kSecAttrKeyClass: kSecAttrKeyClassPrivate,
        kSecAttrKeySizeInBits: 256,
        kSecAttrIsPermanent: false
    ] as [CFString: Any]
var error: Unmanaged<CFError>? = nil
let key = SecKeyCreateWithData(curveKeyData.x963Representation as CFData, attributes as CFDictionary, &error)

But it doesn't work for public key

Code for public key decoding.

// hexData - public key with length 33 bytes
do {
let key = try P256.Signing.PublicKey.init(rawRepresentation: hexData)
let attributes = [
    kSecAttrKeyType: kSecAttrKeyTypeEC,
    kSecAttrKeyClass: kSecAttrKeyClassPublic,
    kSecAttrKeySizeInBits: 256,
    kSecAttrIsPermanent: false
] as [CFString: Any]

let b = SecKeyCreateWithData(key.x963Representation as CFData, attributes as CFDictionary, nil)!
} catch {
    print(error)
}

It fails with error incorrectParameterSize

Which says I'm doing some shit


Solution

  • The provided key material in the test vectors is in the compressed form, which is not supported before iOS 16. As far as I know there's no way to create the key from compressed form before iOS 16, so you're left with one of the below mentioned methods. You should also adjust the way the key is exported on the other party.

    iOS 13 and up

    public init<D>(rawRepresentation: D) throws where D : ContiguousBytes
    public init<Bytes>(compactRepresentation: Bytes) throws where Bytes : ContiguousBytes
    public init<Bytes>(x963Representation: Bytes) throws where Bytes : ContiguousBytes
    

    iOS 14 and up

    public init(pemRepresentation: String) throws
    public init<Bytes>(derRepresentation: Bytes) throws where Bytes : RandomAccessCollection, Bytes.Element == UInt8
    

    If you can support iOS 16 you can use this function

    public init<Bytes>(compressedRepresentation: Bytes) throws where Bytes : ContiguousBytes