Search code examples
swiftdigital-signaturepublic-key-encryptionapple-cryptokit

Testing CryptoKit's data validation


I want to verify some downloaded data is unmodified. My expectation is that if I modify the original data, the signature would fail. While this is true in some cases (data2) it's surprisingly not working in others (data3). Using hashes/Digest returns the same results.

import CryptoKit

let rootKey = P256.Signing.PrivateKey()
let publicKey = rootKey.publicKey

let data = Data(bytes: [0xA, 0xB, 0xC, 0xD], count: 4)
let digest = SHA256.hash(data: data)
let signature = try rootKey.signature(for: data)
let hashSignature = try rootKey.signature(for: digest)

// now alter the data
let data2 = Data(bytes: [0x0, 0xB, 0xC, 0xD], count: 4)
let data3 = Data(bytes: [0xA, 0xB, 0xC, 0xE], count: 4)

publicKey.isValidSignature(signature, for: data) // true, as expected
publicKey.isValidSignature(signature, for: data2) // false, as expected
publicKey.isValidSignature(signature, for: data3) // why is THIS true/valid?

publicKey.isValidSignature(hashSignature, for: SHA256.hash(data: data)) // true
publicKey.isValidSignature(hashSignature, for: SHA256.hash(data: data2)) // false
publicKey.isValidSignature(hashSignature, for: SHA256.hash(data: data3)) // true

For simplicity CryptoKit is used. This also fails in (my) CommonCrypto/SecKey... implementation.


Solution

  • Thanks to Sajjon's answer I was able to figure out what I did wrong. It seems it's not a bug but a simple type casting issue:

    let bytes = [UInt8]([0xA, 0xB, 0xC, 0xD])
    var foo = Data(bytes: bytes, count: 4)
    print(hexString(foo)) // 0a0b0c0d
    foo[1] <<= 1
    print(hexString(foo)) // 0a160c0d
    
    let bar = Data(bytes: [0xA, 0xB, 0xC, 0xD], count: 32)
    print(hexString(bar)) // 0a000000000000000b000000000000000c000000000000000d00000000000000
    

    I defined the bytes in my original Data in place, so they were defaulted to Int a.k.a. Int64. Providing a data length of 4 simply omits everything after the first 4 bytes of data.