Need help with verifying signature using Elliptical Curve DSA

Hello,
I have a block of bytes that I need to verify the signature. The public key is known AND base64 encoded. The signature is known AND base64 encoded as it is retrieved from an accompanying file. I have visibility into the python script that generates the signature and I know that a SHA256 digest is created from the block of bytes. That digest is then passed into the ECDSA encryptor using the same "well-known" private bytes.

But I am receiving the error:

[F_MANAGER] Unable to verify signature Error Domain=NSOSStatusErrorDomain Code=-67808 "EC signature verification failed, no match" UserInfo={numberOfErrorsDeep=0, NSDescription=EC signature verification failed, no match}

whenever I try to verify the signature. I've enclosed the code. Both verify and verifyRaw are failing.

If anyone has any insight as to what I am doing wrong, that'd be great.

Code Block
   private var algorithm: SecKeyAlgorithm = .ecdsaSignatureDigestX962SHA256
/**
Generates a public key using the 256 bit Elliptic Curve Signature
(a 256 bit EC public key is 65 bytes long and starts with an 04.)
*/
func getPublicKey(keyAsBytes: [UInt8]) -> SecKeyPair {
let keyType = kSecAttrKeyTypeECSECPrimeRandom
let keySize = 256
let privateData = Data(base64Encoded: privateKey)
let privateKeyParams: [String: AnyObject] = [kSecAttrIsPermanent as String: false as AnyObject,
kSecAttrApplicationTag as String: privateData?.bytes as AnyObject]
let publicKeyParams: [String: AnyObject] = [kSecAttrIsPermanent as String: false as AnyObject,
kSecAttrCanVerify as String: true as AnyObject,
kSecAttrApplicationTag as String: keyAsBytes as AnyObject]
let parameters: [String: AnyObject] = [kSecAttrKeyType as String: keyType,
kSecAttrKeySizeInBits as String: keySize as AnyObject,
kSecPublicKeyAttrs as String: publicKeyParams as AnyObject,
kSecPrivateKeyAttrs as String: privateKeyParams as AnyObject]
var publicKey, privateKey: SecKey?
_ = SecKeyGeneratePair(parameters as CFDictionary, &publicKey, &privateKey)
return SecKeyPair(publicKey: publicKey, privateKey: privateKey)
}
/*
https://stackoverflow.com/questions/21022526/ios-verify-digital-signature, states that Android's Signature class
automatically creates a digest(SHA-256) whereas iOS does not
*/
private func verifyRaw(publicKey: SecKey,
updatePackage: Data,
signature: Data) -> Bool {
/// #define CC_SHA256_DIGEST_LENGTH 32
/// Creates an array of unsigned 8 bit integers that contains 32 zeros
var digest = [UInt8](repeating: 0, count:Int(CC_SHA256_DIGEST_LENGTH))
/// CC_SHA256 performs digest calculation and places the result in the caller-supplied buffer for digest (md)
_ = updatePackage.withUnsafeBytes {
CC_SHA256($0.baseAddress, UInt32(updatePackage.count), &digest)
}
let result = SecKeyRawVerify(publicKey,
.PKCS1SHA256,
digest,
digest.count,
signature.bytes,
signature.bytes.count)
return result == errSecSuccess
}
private func verify(publicKey: SecKey,
updatePackage: Data,
signature: CFData,
privateKey: SecKey? = nil) -> Bool {
var result = false
var pbError:Unmanaged<CFError>?
var digest = [UInt8](repeating: 0, count:Int(CC_SHA256_DIGEST_LENGTH))
/// CC_SHA256 performs digest calculation and places the result in the caller-supplied buffer for digest (md)
_ = updatePackage.withUnsafeBytes {
CC_SHA256($0.baseAddress, UInt32(updatePackage.count), &digest)
}
let digestAsData = Data(bytes: digest, count: digest.count)
if SecKeyVerifySignature(publicKey,
algorithm,
digestAsData as CFData,
signature,
&pbError) {
result = true
} else {
Log.error(message: "[F_MANAGER] Unable to verify signature \(pbError!.takeRetainedValue() as Error)")
}
return result
}


The public key is known AND base64 encoded. The signature is known AND
base64 encoded as it is retrieved from an accompanying file.

Please post an example of these and the matching file.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

   private let cloudPublicKey = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE9WXZyQ6CMFSA/VZQ1vLBcbsFtVUT5K3oJbkXarA8evb493el0M1g+H7zm0lFLqq1kfeXloU+RGLETvuNZ7LuqaN41tSqmbhrKnCF9A6PJUncjB5i0/CUAxCDn0M837iX"

The signature in the update.sig file is: MGQCMCLTkQ6w8xY9ofWYElhIQ68I6d/YtQbpS5fqf41tG2OWZaVQPxyWrcTAsmhMv2YmIQIwZYyNakvGSoWZvdHuGTOuhwwrd6wLZN06ovLhd9inTWSFjkYRvgzCgf0n6wl7/lI0

Is this what you were looking for?


Accepted Answer

Is this what you were looking for?

Kinda. I was hoping to get an example of:
  • The original data

  • The private key

  • The signature generated from that data using the private key

  • The public key

Still, what you sent me suggests that you’re getting yourself tied up in knots. Consider:

Code Block
% base64 -D > sig.asn1
MGQCMCLTkQ6w8xY9ofWYElhIQ68I6d/YtQbpS5fqf41tG2OWZaVQPxyWrcTAsmhMv2YmIQIwZYyNakvGSoWZvdHuGTOuhwwrd6wLZN06ovLhd9inTWSFjkYRvgzCgf0n6wl7/lI0
^C
% dumpasn1 -p -a sig.asn1
SEQUENCE {
INTEGER
22 D3 91 0E B0 F3 16 3D A1 F5 98 12 58 48 43 AF
08 E9 DF D8 B5 06 E9 4B 97 EA 7F 8D 6D 1B 63 96
65 A5 50 3F 1C 96 AD C4 C0 B2 68 4C BF 66 26 21
INTEGER
65 8C 8D 6A 4B C6 4A 85 99 BD D1 EE 19 33 AE 87
0C 2B 77 AC 0B 64 DD 3A A2 F2 E1 77 D8 A7 4D 64
85 8E 46 11 BE 0C C2 81 FD 27 EB 09 7B FE 52 34
}


That looks like an X9.62 EC signature, which is good. But consider this:

Code Block
% base64 -D > public.asn1
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE9WXZyQ6CMFSA/VZQ1vLBcbsFtVUT5K3oJbkXarA8evb493el0M1g+H7zm0lFLqq1kfeXloU+RGLETvuNZ7LuqaN41tSqmbhrKnCF9A6PJUncjB5i0/CUAxCDn0M837iX
^D
% xxd public.asn1
00000000: 3076 3010 0607 2a86 48ce 3d02 0106 052b 0v0...*.H.=....+
00000010: 8104 0022 0362 0004 f565 d9c9 0e82 3054 ...".b...e....0T
00000020: 80fd 5650 d6f2 c171 bb05 b555 13e4 ade8 ..VP...q...U....
00000030: 25b9 176a b03c 7af6 f8f7 77a5 d0cd 60f8 %..j.<z...w...`.
00000040: 7ef3 9b49 452e aab5 91f7 9796 853e 4462 ~..IE........>Db
00000050: c44e fb8d 67b2 eea9 a378 d6d4 aa99 b86b .N..g....x.....k
00000060: 2a70 85f4 0e8f 2549 dc8c 1e62 d3f0 9403 *p....%I...b....
00000070: 1083 9f43 3cdf b897 ...C<...
% dumpasn1 -p -a public.asn1
SEQUENCE {
SEQUENCE {
OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
OBJECT IDENTIFIER secp384r1 (1 3 132 0 34)
}
BIT STRING
04 F5 65 D9 C9 0E 82 30 54 80 FD 56 50 D6 F2 C1
71 BB 05 B5 55 13 E4 AD E8 25 B9 17 6A B0 3C 7A
F6 F8 F7 77 A5 D0 CD 60 F8 7E F3 9B 49 45 2E AA
B5 91 F7 97 96 85 3E 44 62 C4 4E FB 8D 67 B2 EE
A9 A3 78 D6 D4 AA 99 B8 6B 2A 70 85 F4 0E 8F 25
49 DC 8C 1E 62 D3 F0 94 03 10 83 9F 43 3C DF B8
97
}


That’s a secp384r1 key and yet you have this:

Code Block
let keyType = kSecAttrKeyTypeECSECPrimeRandom
let keySize = 256


which results in a secp256r1 key.

Also, looking at your getPublicKey(keyAsBytes:) function that doesn’t make any sense to me. You are calling SecKeyGeneratePair to generate a new key pair, but to verify the signature you need to use the public key that matches the private key that created the signature. That means you need a call to import your public key, namely SecKeyCreateWithData.

The next problem you’ll have is that the import call expects raw key bytes and your cloudPublicKey value is a DER-encoded ASN.1 SubjectPublicKeyInfo structure. You’ll need to strip the header of this to get the raw key bytes. The easiest way to do this is using Apple CryptoKit. For example:

Code Block
let publicKeyDER: [UInt8] = [
0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2A, 0x86,
0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x05, 0x2B,
0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00, 0x04,
0xF5, 0x65, 0xD9, 0xC9, 0x0E, 0x82, 0x30, 0x54,
0x80, 0xFD, 0x56, 0x50, 0xD6, 0xF2, 0xC1, 0x71,
0xBB, 0x05, 0xB5, 0x55, 0x13, 0xE4, 0xAD, 0xE8,
0x25, 0xB9, 0x17, 0x6A, 0xB0, 0x3C, 0x7A, 0xF6,
0xF8, 0xF7, 0x77, 0xA5, 0xD0, 0xCD, 0x60, 0xF8,
0x7E, 0xF3, 0x9B, 0x49, 0x45, 0x2E, 0xAA, 0xB5,
0x91, 0xF7, 0x97, 0x96, 0x85, 0x3E, 0x44, 0x62,
0xC4, 0x4E, 0xFB, 0x8D, 0x67, 0xB2, 0xEE, 0xA9,
0xA3, 0x78, 0xD6, 0xD4, 0xAA, 0x99, 0xB8, 0x6B,
0x2A, 0x70, 0x85, 0xF4, 0x0E, 0x8F, 0x25, 0x49,
0xDC, 0x8C, 0x1E, 0x62, 0xD3, 0xF0, 0x94, 0x03,
0x10, 0x83, 0x9F, 0x43, 0x3C, 0xDF, 0xB8, 0x97,
]
let publicKey = try P384.Signing.PublicKey(derRepresentation: publicKeyDER)
let publicKeyX963 = publicKey.x963Representation


You can then feed publicKeyX963 into SecKeyCreateWithData.

Having said that, Apple CryptoKit can also verify signatures, so you could just cut out the middleman and use it for that.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
You saved my bacon! Thank you so very much for your (humble) explanation. I know pretty much 0 about security. And the Apple documentation is disgustingly inadequate (never used to be). Put those 2 situations together and you've got a problem that is seemingly impossible to solve. But you took the time to teach me a quite a bit. I finally got the solution here:

Code Block    @available(iOS 14.0, *)
  private func verifySignature(data: Data, key: Data, signature: Data) -> Bool {
    var result = false
    do {
      let publicKey = try P384.Signing.PublicKey(derRepresentation: key.bytes)
      let hash = SHA256.hash(data: data)
       
      let signing384 = try P384.Signing.ECDSASignature(derRepresentation: signature)
      if publicKey.isValidSignature(signing384, for: hash) {
        result = true
      }
    } catch {
      Log.error(message: "[F_MANAGER] Exception \(error)")
    }
    return result
  }


But without your help I NEVER would have figured it out. Thank you so very much.
Glad to hear that you got this working. And in other positive news, this thread was one of the things that prompted me to (finally!) write this all up properly:
Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Need help with verifying signature using Elliptical Curve DSA
 
 
Q