Post

Replies

Boosts

Views

Activity

Reply to Implementing SECP256R1 Signature Matching with Rust's FastCrypto in Swift
Alright, I solved the issue regarding signature verification. For context, the end point I used utilizes P256 methods that conform with the RFC6979 protocol: https://www.rfc-editor.org/rfc/rfc6979.txt And, it turns out, that the signatures returned from CryptoKit's P256 class do in fact conform to this protocol, with one catch: the signature needs to be normalized before you send it off to the end point. I written up some code here that will help out with normalizing these signatures so that s, in context with r || s for P256 elliptic curves, is in the lower half of the curve; and normalized. Note: The developer is required to install the BigInt package published by attaswift, as the signature values are too high to store in a normal UInt64 variable: extension Data { func hexEncodedString() -> String { return map { String(format: "%02hhx", $0) }.joined() } } func normalizeSignature(_ signatureHex: String) -> String? { let curveOrder = BigInt("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", radix: 16)! // Assuming the signature is evenly split between r and s let rHex = String(signatureHex.prefix(signatureHex.count / 2)) let sHex = String(signatureHex.suffix(signatureHex.count / 2)) // Convert hex to BigInt var s = BigInt(sHex, radix: 16)! // Normalize s if necessary let halfCurveOrder = curveOrder / 2 if s > halfCurveOrder { s = curveOrder - s } // Convert back to hex, ensuring it's zero-padded to the correct length let normalizedSHex = String(s, radix: 16).leftPad(toLength: sHex.count, withPad: "0") return rHex + normalizedSHex } extension String { func leftPad(toLength: Int, withPad: String) -> String { guard toLength > self.count else { return self } let padding = String(repeating: withPad, count: toLength - self.count) return padding + self } } While the signature is still not matching that of Rust's FastCrypto, the signature will pass verification if passed into FastCrypto's verification function: % target/debug/sigs-cli verify --msg 48656c6c6f2c20776f726c6421 --public-key 0227322b3a891a0a280d6bc1fb2cbb23d28f54906fd6407f5f741f6def5762609a --signature 4672703055add7ea9f75a80798f4fb65f9dfab2db5a661a002ead74cde53f4bc50e54e4ed2d88c95211b70903562daa3e4088452b32fce659d911665aac0ed2e --scheme secp256r1 Verify result: Ok(()) In short, in order for the developer to be able to use a CryptoKit P256 signature with Rust's FastCrypto library, they must normalize s first before passing the signature into the Rust application. I will mark this post as the solution.
Topic: Programming Languages SubTopic: Swift Tags:
Nov ’23
Reply to Implementing SECP256R1 Signature Matching with Rust's FastCrypto in Swift
Adding to this as well @eskimo, here's the updated code with the private key used for the test. import Foundation import CryptoKit func main() throws { let validSecp256r1SecretKey: [UInt8] = [ 66, 37, 141, 205, 161, 76, 241, 17, 198, 2, 184, 151, 27, 140, 200, 67, 233, 30, 70, 202, 144, 81, 81, 192, 39, 68, 166, 176, 23, 230, 147, 22 ] let pk = try P256.Signing.PrivateKey(rawRepresentation: validSecp256r1SecretKey) let message = Data("Hello, world!".utf8) let signature = try pk.signature(for: message) let expectedSignature = Data([UInt8]([ 38, 216, 71, 32, 101, 45, 139, 196, 221, 209, 152, 100, 52, 161, 11, 59, 123, 105, 240, 227, 90, 23, 198, 165, 152, 126, 109, 28, 186, 105, 101, 47, 67, 132, 163, 66, 72, 118, 66, 223, 94, 68, 89, 45, 48, 75, 234, 12, 235, 15, 174, 46, 52, 127, 163, 206, 197, 206, 26, 129, 68, 207, 187, 178 ])) if signature.rawRepresentation == expectedSignature { print("Signature matches with Rust.") } else { print("Signature mismatch.\n") print("Expected: \([UInt8](expectedSignature))\n") print("Got: \([UInt8](signature.rawRepresentation))\n") } } try! main() The updated code does still have the issue listed above, for the record.
Topic: Programming Languages SubTopic: Swift Tags:
Nov ’23
Reply to Implementing SECP256R1 Signature Matching with Rust's FastCrypto in Swift
Hi @eskimo, Thank you for your prompt reply. To clarify, at the minimum from my understand so feel free to correct any misinformation if presented, the Rust FastCrypto library use PKCS 1.*, while P256 (and it seems like CryptoKit as well) uses PSS signatures. PKCS 1.* signatures are deterministic, while PSS are non-deterministic. Linked here is what flagged to me that CryptoKit utilized PSS, specifically quoted: The signing algorithm employs randomization to generate a different signature on every call, even for the same data and key. I'm also sharing an updated code example without external dependencies. The test case has the expected signature that is derived from Rust's FastCrypto signature function for P256: import Foundation import CryptoKit func main() throws { let pk = P256.Signing.PrivateKey() let message = Data("Hello, world!".utf8) let signature = try pk.signature(for: message) let expectedSignature = Data([UInt8]([ 38, 216, 71, 32, 101, 45, 139, 196, 221, 209, 152, 100, 52, 161, 11, 59, 123, 105, 240, 227, 90, 23, 198, 165, 152, 126, 109, 28, 186, 105, 101, 47, 67, 132, 163, 66, 72, 118, 66, 223, 94, 68, 89, 45, 48, 75, 234, 12, 235, 15, 174, 46, 52, 127, 163, 206, 197, 206, 26, 129, 68, 207, 187, 178 ])) if signature.rawRepresentation == expectedSignature { print("Signature matches with Rust.") } else { print("Signature mismatch.\n") print("Expected: \([UInt8](expectedSignature))\n") print("Got: \([UInt8](signature.rawRepresentation))\n") } } try! main() Here are a couple print outs of the code for context as well to demonstrate the issue at hand: Signature mismatch. Expected: [38, 216, 71, 32, 101, 45, 139, 196, 221, 209, 152, 100, 52, 161, 11, 59, 123, 105, 240, 227, 90, 23, 198, 165, 152, 126, 109, 28, 186, 105, 101, 47, 67, 132, 163, 66, 72, 118, 66, 223, 94, 68, 89, 45, 48, 75, 234, 12, 235, 15, 174, 46, 52, 127, 163, 206, 197, 206, 26, 129, 68, 207, 187, 178] Got: [3, 132, 62, 17, 173, 125, 251, 71, 167, 101, 152, 113, 63, 63, 214, 167, 87, 183, 132, 190, 49, 4, 45, 6, 101, 6, 195, 190, 245, 44, 129, 236, 173, 119, 187, 71, 223, 121, 127, 225, 93, 123, 0, 176, 157, 231, 65, 186, 46, 110, 243, 147, 56, 87, 216, 10, 30, 5, 229, 49, 234, 20, 61, 59] Signature mismatch. Expected: [38, 216, 71, 32, 101, 45, 139, 196, 221, 209, 152, 100, 52, 161, 11, 59, 123, 105, 240, 227, 90, 23, 198, 165, 152, 126, 109, 28, 186, 105, 101, 47, 67, 132, 163, 66, 72, 118, 66, 223, 94, 68, 89, 45, 48, 75, 234, 12, 235, 15, 174, 46, 52, 127, 163, 206, 197, 206, 26, 129, 68, 207, 187, 178] Got: [245, 245, 34, 3, 104, 239, 95, 95, 193, 202, 39, 178, 83, 180, 173, 34, 118, 21, 146, 96, 226, 168, 188, 71, 179, 20, 228, 181, 224, 203, 176, 206, 146, 247, 227, 180, 72, 249, 112, 21, 23, 113, 67, 81, 176, 170, 94, 210, 120, 128, 174, 115, 78, 187, 16, 27, 116, 81, 107, 139, 84, 105, 44, 185] My main question here is what can be done here to match CryptoKit's P256 implementation with that of Rust's FastCrypto P256 implementation. And if not, what other alternatives can be used to have deterministic signatures with either CryptoKit or SecKey. Kindly, MarcoDotIO
Topic: Programming Languages SubTopic: Swift Tags:
Nov ’23