2 Replies
      Latest reply on Jun 25, 2019 5:10 AM by scml
      scml Level 1 Level 1 (0 points)

        I am using the ecdsa python library to generate keys and signatures for messages, but I am unable to verify those signatures in Swift.

         

        I'm trying to simply verify a signature of the text "S".  I've been able to successfully create a public SecKey, but no matter what I do, I cannot seem to get SecKeyVerifySignature to pass.  The payload is hashed using SHA-1, and I've been able to verify the digest in Swift matches the digest in python.  The failing code below should run in xCode.

         

        //  ecdsaVerify
        
        import Foundation
        import CommonCrypto
        
        
        extension Data {
            struct HexEncodingOptions: OptionSet {
                let rawValue: Int
                static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
            }
            
            func hexEncodedString(options: HexEncodingOptions = []) -> String {
                let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
                return map { String(format: format, $0) }.joined()
            }
        }
        
        extension String{
            func sha1() -> String {
                let data = Data(self.utf8)
                var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
                data.withUnsafeBytes {
                    _ = CC_SHA1($0.baseAddress, CC_LONG(data.count), &digest)
                }
                let hexBytes = digest.map { String(format: "%02hhx", $0) }
                return hexBytes.joined()
            }
            
            var hexadecimalStringToData: Data? {
                // Convert a hexstring to data
                var data = Data(capacity: self.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
            }
        }
        
        
        
        //prime256v1, a.k.a kSecECCurveSecp256r1
        // .der with first 26 bits removed
        let EC_PUBLIC_KEY = """
        -----BEGIN BARE KEY-----
        BOptLvuuwPYOaQC6v7hDh36fTqsuwFTCyHVg3uYvfdbPtV5c3urT8HeS/UaGgaO8tMFfpz1sljTnwWTPb8gQcD8=
        -----END BARE KEY-----
        """
        
        // Here for example purposes - to help others generate signatures.
        let PRIVATE_KEY = """
        -----BEGIN EC PRIVATE KEY-----
        MHcCAQEEINxdvHX9q9PGrJBWHcmvXGe3poOc0Qc1/C4HnScp6cP7oAoGCCqGSM49
        AwEHoUQDQgAE6m0u+67A9g5pALq/uEOHfp9Oqy7AVMLIdWDe5i991s+1Xlze6tPw
        d5L9RoaBo7y0wV+nPWyWNOfBZM9vyBBwPw==
        -----END EC PRIVATE KEY-----
        """
        
        class ecdsaVerifier{
            
            var publicKeyData:Data
            var publicKey: SecKey?
            
            init(_ pemPublicKey: String){
                
                let pemPublicKeyNoComments = pemPublicKey.replacingOccurrences(
                    of: #"(?m)^----.*"#,
                    with: "",
                    options: .regularExpression
                    ).trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: "\n", with: "")
                self.publicKeyData = Data(base64Encoded:pemPublicKeyNoComments)!
                var error: Unmanaged<CFError>? = nil
                self.publicKey = SecKeyCreateWithData(self.publicKeyData as NSData, [
                    kSecAttrKeyType: kSecAttrKeyTypeECDSA,
                    kSecAttrKeyClass: kSecAttrKeyClassPublic,
                    kSecAttrKeySizeInBits: 256
                    ] as NSDictionary, &error)
                print(self.publicKey)
                
            }
            
            
            func verifyFromHexString(content: String, hexSignature: String) -> Bool{
                let cleanSignature = hexSignature.trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: " ", with: "")
                print("verify from", cleanSignature, content)
                let cleanSignatureData = cleanSignature.hexadecimalStringToData!
                let digest = content.sha1().hexadecimalStringToData!
                //let digest = content.data(using: .utf8)!
                print("Digest", digest.count, digest.hexEncodedString())
                
                return self.verifyFromDigest(digest, signature: cleanSignatureData)
            }
            func verifyFromDigest(_ digest: Data, signature: Data) -> Bool{
                var error: Unmanaged<CFError>? = nil
                
                guard SecKeyVerifySignature(publicKey!, SecKeyAlgorithm.ecdsaSignatureDigestX962SHA1, digest as CFData, signature as CFData, &error) else {
                    print("error", error)
                    return false
                }
                print("true!")
                return true
            }
            
        }
        
        let verifier = ecdsaVerifier(EC_PUBLIC_KEY)
        
        let signature = """
        30 44 02 20 14 08 69 ac 3a 39 29 78 c2 fd e1 0a
        c2 6d 87 4e 66 57 fd bf 1a 01 b8 4c b2 13 63 fc
        f6 e8 0b 64 02 20 42 88 2e a0 c8 4a ca 04 a1 0a
        59 be e3 c7 50 d6 53 d4 14 d3 a6 f7 e6 07 0d e2
        e5 6b 8f 44 47 52
        """
        
        let content = "S"
        verifier.verifyFromHexString(content: content, hexSignature: signature)
        
        

         

        Any help would be appriciated.

        • Re: SecKeyVerifySignature failing to verify signature
          eskimo Apple Staff Apple Staff (11,625 points)

          Debugging problems like this is a pain, especially with EC signatures [1].

          I had a quick look at your stuff and I can’t see anything obviously broken.  have you tried creating the signature on the Apple side (using SecKeyCreateSignature) and verifying that?  That should work, obviously, but if it fails it might be point to a problem with your verify code.  You can take then take that signature to the Python side to see if that works.

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

          [1] In RSA you can use raw mode to see what’s inside the signature.

          • Re: SecKeyVerifySignature failing to verify signature
            scml Level 1 Level 1 (0 points)

            Problem was on the python side.

             

            `Missed that there was a `sigencode` parameter to ensure the signatures were compatible with openssl.

            https://github.com/warner/python-ecdsa#openssl-compatibility

             

            privkey.sign(msg, hashfunc=hashlib.sha1, sigencode=ecdsa.util.sigencode_der)