import Foundation import Testing import SSHProtocolKit @testable import SecretKit @Suite struct OpenSSHSignatureWriterTests { private let writer = OpenSSHSignatureWriter() @Test func ecdsaMpintStripsUnnecessaryLeadingZeros() throws { let secret = Constants.ecdsa256Secret // r has a leading 0x00 followed by 0x01 (< 0x80): the mpint must not keep the leading zero. let rBytes: [UInt8] = [0x00] + (1...31).map { UInt8($0) } let r = Data(rBytes) // s has two leading 0x00 bytes followed by 0x7f (< 0x80): the mpint must not keep the leading zeros. let sBytes: [UInt8] = [0x00, 0x00, 0x7f] + Array(repeating: UInt8(0x01), count: 29) let s = Data(sBytes) let rawRepresentation = r + s let response = writer.data(secret: secret, signature: rawRepresentation) let (parsedR, parsedS) = try parseEcdsaSignatureMpints(from: response) #expect(parsedR == Data((1...31).map { UInt8($0) })) #expect(parsedS == Data([0x7f] + Array(repeating: UInt8(0x01), count: 29))) } @Test func ecdsaMpintPrefixesZeroWhenHighBitSet() throws { let secret = Constants.ecdsa256Secret // r starts with 0x80 (high bit set): mpint must be prefixed with 0x00. let r = Data([UInt8(0x80)] + Array(repeating: UInt8(0x01), count: 31)) let s = Data([UInt8(0x01)] + Array(repeating: UInt8(0x02), count: 31)) let rawRepresentation = r + s let response = writer.data(secret: secret, signature: rawRepresentation) let (parsedR, parsedS) = try parseEcdsaSignatureMpints(from: response) #expect(parsedR == Data([0x00, 0x80] + Array(repeating: UInt8(0x01), count: 31))) #expect(parsedS == Data([0x01] + Array(repeating: UInt8(0x02), count: 31))) } } private extension OpenSSHSignatureWriterTests { enum Constants { static let ecdsa256Secret = TestSecret( id: Data(), name: "Test Key (ECDSA 256)", publicKey: Data(repeating: 0x01, count: 65), attributes: Attributes( keyType: KeyType(algorithm: .ecdsa, size: 256), authentication: .notRequired, publicKeyAttribution: "test@example.com" ) ) } enum ParseError: Error { case eof case invalidAlgorithm } func parseEcdsaSignatureMpints(from openSSHSignedData: Data) throws -> (r: Data, s: Data) { let reader = OpenSSHReader(data: openSSHSignedData) // Prefix _ = try reader.readNextBytes(as: UInt32.self) let algorithm = try reader.readNextChunkAsString() guard algorithm == "ecdsa-sha2-nistp256" else { throw ParseError.invalidAlgorithm } let sigReader = try reader.readNextChunkAsSubReader() let r = try sigReader.readNextChunk() let s = try sigReader.readNextChunk() return (r, s) } }