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.
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.