Hello fellow developers,
I'm currently working on an SDK involving the SECP256R1 standard and facing an interesting issue. My goal is to ensure the Swift implementation of SECP256R1 signatures matches that of Rust's FastCrypto implementation.
The Issue:
When running tests to compare signatures generated by Swift and Rust implementations, the signatures do not match. Despite this mismatch, verification tests still succeed. I've tried using both the P256 class from CryptoKit and SecKey from the Security SDK. The Swift code is being written in Xcode 15 Beta 8, Swift 5.9. Code Snippet:
struct SECP256R1PrivateKey {
/// Commented is P256, uncommented is SecKey
// public init(key: Data) throws {
// if let privateKey = try? P256.Signing.PrivateKey(rawRepresentation: key) {
// self.key = privateKey
// } else {
// throw AccountError.invalidData
// }
// }
public init(key: Data) throws {
if let privateKeyP256 = try? P256.Signing.PrivateKey(rawRepresentation: key) {
let attributes: [String: Any] = [
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
kSecAttrKeyType as String: kSecAttrKeyTypeECDSA,
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
kSecAttrKeySizeInBits as String: 256
]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateWithData(privateKeyP256.rawRepresentation as CFData, attributes as CFDictionary, &error) else {
throw error?.takeRetainedValue() as Error? ?? NSError(domain: NSOSStatusErrorDomain, code: Int(errSecParam), userInfo: nil)
}
self.key = privateKey
} else {
throw AccountError.invalidData
}
}
// public func sign(data: Data) throws -> Signature {
// let signature = try self.key.signature(for: data)
// return Signature(
// signature: signature.rawRepresentation,
// publickey: try self.publicKey().key.compressedRepresentation,
// signatureScheme: .SECP256R1
// )
// }
public func sign(data: Data) throws -> Signature {
let dataHash = Data(data.sha256)
var error: Unmanaged<CFError>?
guard let signature = SecKeyCreateSignature(self.key, .ecdsaSignatureMessageX962SHA256, dataHash as NSData, &error) as Data? else {
throw error!.takeRetainedValue() as Error
}
guard let publicKey = SecKeyCopyExternalRepresentation(try self.publicKey().key, &error) as Data? else {
throw AccountError.invalidData
}
return Signature(
signature: signature,
publickey: publicKey,
signatureScheme: .SECP256R1
)
}
}
func testThatTheRustImplementationForSignaturesIsTheSame() throws {
let account = try Account(privateKey: Data(self.validSecp256r1SecretKey), accountType: .secp256r1)
guard let signData = "Hello, world!".data(using: .utf8) else { XCTFail("Unable to encode message"); return; }
let signature = try account.sign(signData)
XCTAssertEqual(
try signature.hex(),
"26d84720652d8bc4ddd1986434a10b3b7b69f0e35a17c6a5987e6d1cba69652f4384a342487642df5e44592d304bea0ceb0fae2e347fa3cec5ce1a8144cfbbb2"
)
}
The Core Question:
How do I implement the R1 signature in Swift so that it matches the signature generated by Rust's FastCrypto?
Any insights, suggestions, or sample code snippets that could guide me in the right direction would be immensely appreciated!
Thank you in advance!