测试 CryptoKit 的数据验证

问题描述

我想验证一些下载的数据是否未修改。我的期望是,如果我修改原始数据,签名会失败。虽然这在某些情况下 (data2) 是正确的,但令人惊讶的是在其他情况下 (data3) 不起作用。使用 hashes/Digest 返回相同的结果。

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,count: 4)
let data3 = Data(bytes: [0xA,0xE],count: 4)

publicKey.isValidSignature(signature,for: data) // true,as expected
publicKey.isValidSignature(signature,for: data2) // false,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

为简单起见,使用 CryptoKit。这在(我的)CommonCrypto/SecKey... 实现中也失败了。

解决方法

好的,我发现了问题。您的代码是 100% 正确的。这个问题是由 Swift 中的一些相当有问题的错误引起的 - 我认为。更聪明的 Swift 专家 - 请纠正和启发我!

顺便说一句:我正在使用 Xcode Version 12.2 beta 3 (12B5035g)

数据不平等问题

func testDataInequality() {
    func hexString(_ data: Data) -> String {
        data.map { String(format: "%02hhx",$0) }.joined()
    }
    
    let data = Data(bytes: [0xA,0xB,0xC,0xD],count: 4)
    let data3 = Data(bytes: [0xA,0xE],count: 4)
    XCTAssertEqual(data.count,data3.count)
    XCTAssertEqual(data.count,4)
    XCTAssertNotEqual(data,data3,"Expected 'data': \(hexString(data)) to NOT equal: 'data3':  \(hexString(data3)),but it did.")
}

这个简单的单元测试失败了,但它不应该失败。意思是,datadata3 显然不一样,因为我们看到 [0xA,0xD] != [0xA,0xE]。我们使用 XCTAssertNotEqual 断言它们不应该相等,但是这个断言失败了,这意味着 Swift 相信它们是相等的。

我使用嵌套方法data分别打印了data3hexString的内容,我们在Xcode中运行测试时得到这个错误信息:

XCTAssertNotEqual failed: ("4 bytes") is equal to ("4 bytes") - Expected 'data': 0a000000 to NOT equal: 'data3':  0a000000,but it did.

正如我们所见,datadata3 都错误地取值:0a000000,如 [0xa,0x0,0x0]

更改为对数据使用已弃用的初始化程序:

    @available(swift 4.2)
    @available(swift,deprecated: 5,message: "use `init(_:)` instead")
    public init<S>(bytes elements: S) where S : Sequence,S.Element == UInt8

我们得到了正确的行为,而不是您使用的“正确”(未弃用)bytes:count....

所以改为:

let data = Data(bytes: [0xA,0xD])
let data3 = Data(bytes: [0xA,0xE])

然后重新运行测试,看看它是否有效。

此外,您可以添加以下断言:

XCTAssertEqual(hexString(data),"0a0b0c0d")
XCTAssertEqual(hexString(data3),"0a0b0c0e")

我会确保将此报告为 Swift 错误。

,

多亏了 Sajjon's answer,我才能够弄清楚我做错了什么。这似乎不是一个错误,而是一个简单的类型转换问题:

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

let bar = Data(bytes: [0xA,count: 32)
print(hexString(bar)) // 0a000000000000000b000000000000000c000000000000000d00000000000000

我在原始数据中定义了 bytes,因此它们默认为 Int 又名 Int64。提供 4 的数据长度只会忽略前 4 个数据字节之后的所有内容。