diff --git a/.changeset/sharp-otters-verify.md b/.changeset/sharp-otters-verify.md new file mode 100644 index 0000000..fbfa9e1 --- /dev/null +++ b/.changeset/sharp-otters-verify.md @@ -0,0 +1,5 @@ +--- +"@germ-network/atprototypes": minor +--- + +`AtprotoTypesVerify` now verifies secp256k1 repo commit signatures, not just P-256 — a from-scratch, verify-only Swift port (field/scalar arithmetic, Jacobian point operations, SEC1 decompression, ECDSA), since most Bluesky accounts sign with this curve and swift-crypto has no k256 support. Checked against Wycheproof's secp256k1 test vectors and a P256K-backed differential oracle in `AtprotoTypesVerifyTests` (test-only dependency; nothing new ships in the product). `AtprotoTypesVerifyMocks`'s fixture signer is now a protocol (`RepoFixtureSigningKey`) instead of concrete P256, so a consumer's tests can build synthetic k256 repos too. diff --git a/.changeset/thirty-crabs-verify.md b/.changeset/thirty-crabs-verify.md new file mode 100644 index 0000000..3e93363 --- /dev/null +++ b/.changeset/thirty-crabs-verify.md @@ -0,0 +1,5 @@ +--- +"@germ-network/atprototypes": minor +--- + +Add `AtprotoTypesVerify` and `AtprotoTypesVerifyMocks`: CAR framing, DAG-CBOR, MST proof walking, CID recomputation, and P-256 repo commit-signature verification, plus fixture-building support for building synthetic signed repos in tests. Additive products — a consumer that never links the new products pays nothing for their existing (e.g. an App Clip target). The existing `AtprotoTypes` target gains one small addition of its own: an `Atproto.Repo` seam (`RecordPath`, `Proof`, `ProofVerifying`, `ProofUnavailable`) cheap enough for a space-constrained consumer to link even when it doesn't want the verifier itself. diff --git a/.github/workflows/ci-apple.yml b/.github/workflows/ci-apple.yml index 5bfbf21..a04efd0 100644 --- a/.github/workflows/ci-apple.yml +++ b/.github/workflows/ci-apple.yml @@ -35,4 +35,9 @@ jobs: steps: - uses: actions/checkout@v5 - name: Test platform ${{ matrix.destination }} - run: set -o pipefail && xcodebuild -skipMacroValidation -scheme AtprotoTypes -destination "${{ matrix.destination }}" test | xcbeautify + # -skipPackagePluginValidation: swift-secp256k1 (the differential-test + # oracle for AtprotoTypesVerify's from-scratch k256, test-only and never + # shipped) carries a build-tool plugin that copies its shared sources. + # xcodebuild refuses unvalidated plugins non-interactively; `swift test` + # doesn't gate them, which is why Linux stayed green while Apple failed. + run: set -o pipefail && xcodebuild -skipMacroValidation -skipPackagePluginValidation -scheme AtprotoTypes -destination "${{ matrix.destination }}" test | xcbeautify diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/AtprotoTypes.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/AtprotoTypes.xcscheme index 40d5c9f..855ce0d 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/AtprotoTypes.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/AtprotoTypes.xcscheme @@ -40,6 +40,16 @@ ReferencedContainer = "container:"> + + + + Proof + } + + ///What a space-constrained consumer injects instead of linking + ///`AtprotoTypesVerify`. It refuses rather than passing the record through + ///unverified: a caller asking for a proof must never receive a value that + ///merely looks like one. + public struct ProofUnavailable: ProofVerifying { + public init() {} + + public func verifyRecordProof( + car: Data, + did: Atproto.DID, + path: RecordPath, + document: Atproto.DIDDocument + ) throws -> Proof { + throw Errors.verificationUnavailable + } + } + + public enum Errors: Error, Equatable, LocalizedError { + case verificationUnavailable + + public var errorDescription: String? { + switch self { + case .verificationUnavailable: + "Repo verification is not available in this build" + } + } + } +} diff --git a/Sources/AtprotoTypesVerify/ByteReader.swift b/Sources/AtprotoTypesVerify/ByteReader.swift new file mode 100644 index 0000000..f4c15b3 --- /dev/null +++ b/Sources/AtprotoTypesVerify/ByteReader.swift @@ -0,0 +1,80 @@ +// +// ByteReader.swift +// AtprotoTypesVerify +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import Foundation + +///Cursor over untrusted bytes. Copies to `[UInt8]` up front so slice index +///arithmetic can't be got wrong — `Data`'s indices don't rebase on slicing, and +///every read here is attacker-influenced. +struct ByteReader { + private let bytes: [UInt8] + private(set) var offset: Int + + init(_ data: Data) { + self.bytes = Array(data) + self.offset = 0 + } + + var isAtEnd: Bool { offset >= bytes.count } + var remaining: Int { bytes.count - offset } + + mutating func readByte() throws -> UInt8 { + guard offset < bytes.count else { + throw Atproto.Repo.ProofError.truncated + } + defer { offset += 1 } + return bytes[offset] + } + + mutating func read(_ count: Int) throws -> [UInt8] { + guard count >= 0, remaining >= count else { + throw Atproto.Repo.ProofError.truncated + } + defer { offset += count } + return Array(bytes[offset..<(offset + count)]) + } + + ///Unsigned LEB128 as multiformats uses it. Rejects both overflow and the + ///non-minimal encodings that would otherwise let the same number be written + ///two ways — which matters because CAR block framing is length-prefixed and + ///a second spelling of a length is a second parse of the same stream. + mutating func readUnsignedVarint() throws -> UInt64 { + var result: UInt64 = 0 + var shift: UInt64 = 0 + + for index in 0..<10 { + let byte = try readByte() + let payload = UInt64(byte & 0x7F) + + guard shift < 64, !(shift == 63 && payload > 1) else { + throw Atproto.Repo.ProofError.varintOverflow + } + result |= payload << shift + + if byte & 0x80 == 0 { + //a trailing continuation-free zero byte adds nothing, so it is + //a second spelling of a shorter varint + guard index == 0 || byte != 0 else { + throw Atproto.Repo.ProofError.varintNotMinimal + } + return result + } + shift += 7 + } + throw Atproto.Repo.ProofError.varintOverflow + } + + ///Reads a length that has to be usable as an `Int` index. + mutating func readLength() throws -> Int { + let value = try readUnsignedVarint() + guard value <= UInt64(Int.max), Int(value) <= remaining else { + throw Atproto.Repo.ProofError.truncated + } + return Int(value) + } +} diff --git a/Sources/AtprotoTypesVerify/CAR/CARv1.swift b/Sources/AtprotoTypesVerify/CAR/CARv1.swift new file mode 100644 index 0000000..af5e230 --- /dev/null +++ b/Sources/AtprotoTypesVerify/CAR/CARv1.swift @@ -0,0 +1,96 @@ +// +// CARv1.swift +// AtprotoTypesVerify +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import Foundation + +///CAR v1: a varint-length-prefixed DAG-CBOR header, then varint-length-prefixed +///blocks each carrying its own CID. +/// +///A record proof is one of these — the commit, the MST nodes along the path to +///the record, and the record block. That is what the JSON +///`com.atproto.repo.getRecord` a JSON-only caller uses throws away. +/// +///The bytes originate from `com.atproto.sync.getRecord` at the DID's PDS, but a +///caller is not expected to make that call directly: a monitor performs it, +///verifies it, and serves the stored CAR from its own `/records/{did}`. Either +///way the check below is identical, which is why nothing here knows about +///transport. +/// +///Every block is checked against its own CID at load, not at lookup — the +///property the rest of the walk rests on, and the one the available Swift CAR +///readers skip. See `docs/dependency-choices.md`. +public struct CARv1: Sendable { + public let roots: [ContentIdentifier] + private let blocks: [Data: Data] + + ///Every block is checked against its own CID as it is read. Doing it here + ///rather than at lookup means a block that does not hash to its address can + ///never be observed by the walk at all, so no later code has to remember to + ///ask. + public init(_ data: Data) throws { + var reader = ByteReader(data) + + let headerLength = try reader.readLength() + let headerBytes = Data(try reader.read(headerLength)) + let header = try DAGCBORDecoder.decode(headerBytes) + + guard let version = header["version"]?.integerValue else { + throw Atproto.Repo.ProofError.badCARHeader + } + guard version == 1 else { + throw Atproto.Repo.ProofError.unsupportedCARVersion(UInt64(clamping: version)) + } + guard let rawRoots = header["roots"]?.arrayValue else { + throw Atproto.Repo.ProofError.badCARHeader + } + + let roots = try rawRoots.map { entry -> ContentIdentifier in + guard let link = entry.linkValue else { + throw Atproto.Repo.ProofError.badCARHeader + } + return link + } + guard !roots.isEmpty else { + throw Atproto.Repo.ProofError.noCARRoots + } + self.roots = roots + + var blocks: [Data: Data] = [:] + while !reader.isAtEnd { + let blockLength = try reader.readLength() + let start = reader.offset + let cid = try ContentIdentifier.read(from: &reader) + let cidLength = reader.offset - start + guard blockLength >= cidLength else { + throw Atproto.Repo.ProofError.truncated + } + let payload = Data(try reader.read(blockLength - cidLength)) + + guard cid.matches(block: payload) else { + throw Atproto.Repo.ProofError.blockCIDMismatch(cid.string) + } + //a duplicate block is harmless: it hashed to the same CID, so it is + //the same bytes + blocks[cid.bytes] = payload + } + self.blocks = blocks + } + + public func block(_ cid: ContentIdentifier) throws -> Data { + guard let payload = blocks[cid.bytes] else { + throw Atproto.Repo.ProofError.missingBlock(cid.string) + } + return payload + } + + public func decoded(_ cid: ContentIdentifier) throws -> DAGCBORValue { + try DAGCBORDecoder.decode(try block(cid)) + } + + public var blockCount: Int { blocks.count } +} diff --git a/Sources/AtprotoTypesVerify/ContentIdentifier.swift b/Sources/AtprotoTypesVerify/ContentIdentifier.swift new file mode 100644 index 0000000..dfa6ffc --- /dev/null +++ b/Sources/AtprotoTypesVerify/ContentIdentifier.swift @@ -0,0 +1,123 @@ +// +// ContentIdentifier.swift +// AtprotoTypesVerify +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import Base32 +import Crypto +import Foundation + +///A CID we can actually compute with. `Atproto.CID` in AtprotoTypes holds the +///bytes opaquely and can render base32, which is all a JSON caller needs; a +///proof needs the parts, because the whole point is to recompute the digest +///from the block and compare rather than take the server's word for the link. +/// +///Narrow on purpose — v1 only, two codecs, sha2-256 only. See +///`docs/dependency-choices.md` for why this isn't a multiformats library. +public struct ContentIdentifier: Sendable, Hashable { + public enum Codec: UInt64, Sendable { + case raw = 0x55 + case dagCBOR = 0x71 + } + + //multicodec sha2-256, and the only digest atproto uses + static let sha2_256: UInt64 = 0x12 + static let digestLength = 32 + + public let codec: Codec + public let digest: [UInt8] + + init(codec: Codec, digest: [UInt8]) throws { + guard digest.count == Self.digestLength else { + throw Atproto.Repo.ProofError.badDigestLength(digest.count) + } + self.codec = codec + self.digest = digest + } + + ///CIDv1 binary: version ‖ codec ‖ multihash, each multiformats-varint + ///prefixed. This is the form that appears in CAR block headers and, behind + ///the identity-multibase byte, inside DAG-CBOR tag 42. + public var bytes: Data { + var out = Self.varint(1) + out += Self.varint(codec.rawValue) + out += Self.varint(Self.sha2_256) + out += Self.varint(UInt64(Self.digestLength)) + out += digest + return Data(out) + } + + ///Base32 lower, `b`-prefixed, matching `Atproto.CID.string`. + public var string: String { + "b" + Base32.encode(bytes, options: .letterCase(.lower), .pad(false)) + } + + ///The bridge back to the opaque, JSON-facing CID type. `Atproto.CID`'s byte + ///initialiser is `package`-scoped, which this target shares. + public var atprotoCID: Atproto.CID { + .init(bytes: bytes) + } + + public static func compute(codec: Codec, block: Data) throws -> ContentIdentifier { + try .init(codec: codec, digest: Array(SHA256.hash(data: block))) + } + + ///The check the whole design rests on: content addressing only means + ///anything if someone actually recomputes the address. + public func matches(block: Data) -> Bool { + Array(SHA256.hash(data: block)) == digest + } + + static func read(from reader: inout ByteReader) throws -> ContentIdentifier { + let version = try reader.readUnsignedVarint() + //CIDv0 is a bare base58 sha256 multihash with no version prefix; atproto + //is CIDv1 only, and silently accepting v0 would mean accepting a + //different codec convention than the one we check against + guard version == 1 else { + throw Atproto.Repo.ProofError.unsupportedCIDVersion(version) + } + + let rawCodec = try reader.readUnsignedVarint() + guard let codec = Codec(rawValue: rawCodec) else { + throw Atproto.Repo.ProofError.unsupportedCodec(rawCodec) + } + + let hash = try reader.readUnsignedVarint() + guard hash == Self.sha2_256 else { + throw Atproto.Repo.ProofError.unsupportedHash(hash) + } + + let length = try reader.readUnsignedVarint() + guard length == UInt64(Self.digestLength) else { + throw Atproto.Repo.ProofError.badDigestLength(Int(clamping: length)) + } + + return try .init(codec: codec, digest: try reader.read(Self.digestLength)) + } + + init(bytes: Data) throws { + var reader = ByteReader(bytes) + self = try Self.read(from: &reader) + guard reader.isAtEnd else { + throw Atproto.Repo.ProofError.trailingBytes + } + } + + ///Exposed package-wide so fixture-building test support in + ///AtprotoTypesVerifyMocks can frame CAR headers and block lengths without a + ///second LEB128 implementation. + package static func varint(_ value: UInt64) -> [UInt8] { + var remaining = value + var out: [UInt8] = [] + repeat { + var byte = UInt8(remaining & 0x7F) + remaining >>= 7 + if remaining != 0 { byte |= 0x80 } + out.append(byte) + } while remaining != 0 + return out + } +} diff --git a/Sources/AtprotoTypesVerify/DAGCBOR/DAGCBORDecoder.swift b/Sources/AtprotoTypesVerify/DAGCBOR/DAGCBORDecoder.swift new file mode 100644 index 0000000..2829c6f --- /dev/null +++ b/Sources/AtprotoTypesVerify/DAGCBOR/DAGCBORDecoder.swift @@ -0,0 +1,233 @@ +// +// DAGCBORDecoder.swift +// AtprotoTypesVerify +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import Foundation + +///Strict DAG-CBOR. Every restriction the codec puts on plain CBOR is enforced +///rather than tolerated: one encoding per value, no indefinite lengths, string +///keys in canonical order, and tag 42 as the only tag. +/// +///Strictness is not tidiness here. A lax reader is a second parser of the same +///bytes, and anywhere two parsers can disagree about what a repo says is +///somewhere a proof can be made to mean two things. +public enum DAGCBORDecoder { + static let maxDepth = 128 + + public static func decode(_ data: Data) throws -> DAGCBORValue { + var reader = ByteReader(data) + let value = try decodeValue(from: &reader, depth: 0) + guard reader.isAtEnd else { + throw Atproto.Repo.ProofError.trailingBytes + } + return value + } + + static func decodeValue( + from reader: inout ByteReader, + depth: Int + ) throws -> DAGCBORValue { + guard depth < maxDepth else { + throw Atproto.Repo.ProofError.nestingTooDeep + } + + let initial = try reader.readByte() + let major = initial >> 5 + let additional = initial & 0x1F + + switch major { + case 0: + let value = try readArgument(additional, from: &reader) + guard value <= UInt64(Int64.max) else { + throw Atproto.Repo.ProofError.integerOutOfRange + } + return .integer(Int64(value)) + + case 1: + let value = try readArgument(additional, from: &reader) + //encodes -1 - value, so anything past Int64.max underflows Int64.min + guard value <= UInt64(Int64.max) else { + throw Atproto.Repo.ProofError.integerOutOfRange + } + return .integer(-1 - Int64(value)) + + case 2: + let length = try readCount(additional, from: &reader) + return .bytes(Data(try reader.read(length))) + + case 3: + let length = try readCount(additional, from: &reader) + let raw = try reader.read(length) + guard let string = String(bytes: raw, encoding: .utf8) else { + throw Atproto.Repo.ProofError.invalidUTF8 + } + return .string(string) + + case 4: + let count = try readCount(additional, from: &reader) + var items: [DAGCBORValue] = [] + items.reserveCapacity(min(count, 256)) + for _ in 0.. DAGCBORValue { + let count = try readCount(additional, from: &reader) + var entries: [(key: String, value: DAGCBORValue)] = [] + entries.reserveCapacity(min(count, 64)) + var previousKey: String? + + for _ in 0.. ContentIdentifier { + let inner = try decodeValue(from: &reader, depth: depth + 1) + guard case .bytes(let raw) = inner, raw.first == 0x00 else { + throw Atproto.Repo.ProofError.badCIDLink + } + return try ContentIdentifier(bytes: raw.dropFirst()) + } + + static func decodeSimple( + _ additional: UInt8, + from reader: inout ByteReader + ) throws -> DAGCBORValue { + switch additional { + case 20: return .bool(false) + case 21: return .bool(true) + case 22: return .null + //DAG-CBOR pins floats to 64-bit, so half and single precision are not + //alternate spellings we accept + case 27: + let raw = try reader.read(8) + var bits: UInt64 = 0 + for byte in raw { bits = (bits << 8) | UInt64(byte) } + return .float(Double(bitPattern: bits)) + default: + throw Atproto.Repo.ProofError.unsupportedSimpleValue(additional) + } + } + + ///Reads the argument for majors 0-6, rejecting any encoding longer than the + ///value needs. + static func readArgument( + _ additional: UInt8, + from reader: inout ByteReader + ) throws -> UInt64 { + switch additional { + case 0...23: + return UInt64(additional) + case 24: + let value = UInt64(try reader.readByte()) + guard value >= 24 else { throw Atproto.Repo.ProofError.nonMinimalLength } + return value + case 25: + let value = try readBigEndian(2, from: &reader) + guard value > 0xFF else { throw Atproto.Repo.ProofError.nonMinimalLength } + return value + case 26: + let value = try readBigEndian(4, from: &reader) + guard value > 0xFFFF else { throw Atproto.Repo.ProofError.nonMinimalLength } + return value + case 27: + let value = try readBigEndian(8, from: &reader) + guard value > 0xFFFF_FFFF else { + throw Atproto.Repo.ProofError.nonMinimalLength + } + return value + case 31: + throw Atproto.Repo.ProofError.indefiniteLength + default: + throw Atproto.Repo.ProofError.reservedAdditionalInfo(additional) + } + } + + ///An argument that has to be usable as a count, and that cannot describe + ///more content than the buffer actually holds — so a huge declared length + ///fails immediately instead of after an allocation. + static func readCount( + _ additional: UInt8, + from reader: inout ByteReader + ) throws -> Int { + let value = try readArgument(additional, from: &reader) + guard value <= UInt64(Int.max), Int(value) <= reader.remaining else { + throw Atproto.Repo.ProofError.truncated + } + return Int(value) + } + + static func readBigEndian( + _ count: Int, + from reader: inout ByteReader + ) throws -> UInt64 { + var value: UInt64 = 0 + for byte in try reader.read(count) { + value = (value << 8) | UInt64(byte) + } + return value + } + + ///RFC 7049 canonical order — shorter keys first, then bytewise — which is + ///what DAG-CBOR kept and what the JS and Go implementations both emit. Note + ///this is *not* RFC 8949's plain bytewise ordering. + static func canonicallyPrecedes(_ lhs: String, _ rhs: String) -> Bool { + let left = Array(lhs.utf8) + let right = Array(rhs.utf8) + if left.count != right.count { + return left.count < right.count + } + return left.lexicographicallyPrecedes(right) + } +} diff --git a/Sources/AtprotoTypesVerify/DAGCBOR/DAGCBOREncoder.swift b/Sources/AtprotoTypesVerify/DAGCBOR/DAGCBOREncoder.swift new file mode 100644 index 0000000..809d4d5 --- /dev/null +++ b/Sources/AtprotoTypesVerify/DAGCBOR/DAGCBOREncoder.swift @@ -0,0 +1,105 @@ +// +// DAGCBOREncoder.swift +// AtprotoTypesVerify +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import Foundation + +///Canonical DAG-CBOR out. +/// +///Verification needs an encoder for exactly one reason: the commit signature +///covers the commit with its `sig` field removed, so the preimage has to be +///rebuilt rather than read. That makes `encode(decode(bytes)) == bytes` for +///canonical input the property the whole signature check leans on — it is +///pinned in the round-trip tests. +public enum DAGCBOREncoder { + public static func encode(_ value: DAGCBORValue) -> Data { + var out = Data() + append(value, to: &out) + return out + } + + static func append(_ value: DAGCBORValue, to out: inout Data) { + switch value { + case .null: + out.append(0xF6) + + case .bool(let flag): + out.append(flag ? 0xF5 : 0xF4) + + case .integer(let number): + if number >= 0 { + appendHeader(major: 0, argument: UInt64(number), to: &out) + } else { + appendHeader(major: 1, argument: UInt64(-1 - number), to: &out) + } + + case .float(let number): + out.append(0xFB) + appendBigEndian(number.bitPattern, width: 8, to: &out) + + case .bytes(let data): + appendHeader(major: 2, argument: UInt64(data.count), to: &out) + out.append(data) + + case .string(let string): + let utf8 = Array(string.utf8) + appendHeader(major: 3, argument: UInt64(utf8.count), to: &out) + out.append(contentsOf: utf8) + + case .array(let items): + appendHeader(major: 4, argument: UInt64(items.count), to: &out) + for item in items { append(item, to: &out) } + + case .map(let entries): + appendHeader(major: 5, argument: UInt64(entries.count), to: &out) + //sorted rather than trusted: decode validates order, but a value + //built in code (or with a key removed) should not be able to emit a + //non-canonical map just because someone assembled it out of order + let ordered = entries.sorted { + DAGCBORDecoder.canonicallyPrecedes($0.key, $1.key) + } + for entry in ordered { + append(.string(entry.key), to: &out) + append(entry.value, to: &out) + } + + case .link(let cid): + appendHeader(major: 6, argument: 42, to: &out) + //identity multibase prefix, then the binary CID + var linkBytes = Data([0x00]) + linkBytes.append(cid.bytes) + appendHeader(major: 2, argument: UInt64(linkBytes.count), to: &out) + out.append(linkBytes) + } + } + + static func appendHeader(major: UInt8, argument: UInt64, to out: inout Data) { + let prefix = major << 5 + switch argument { + case 0...23: + out.append(prefix | UInt8(argument)) + case 24...0xFF: + out.append(prefix | 24) + out.append(UInt8(argument)) + case 0x100...0xFFFF: + out.append(prefix | 25) + appendBigEndian(argument, width: 2, to: &out) + case 0x1_0000...0xFFFF_FFFF: + out.append(prefix | 26) + appendBigEndian(argument, width: 4, to: &out) + default: + out.append(prefix | 27) + appendBigEndian(argument, width: 8, to: &out) + } + } + + static func appendBigEndian(_ value: UInt64, width: Int, to out: inout Data) { + for shift in stride(from: (width - 1) * 8, through: 0, by: -8) { + out.append(UInt8((value >> UInt64(shift)) & 0xFF)) + } + } +} diff --git a/Sources/AtprotoTypesVerify/DAGCBOR/DAGCBORValue.swift b/Sources/AtprotoTypesVerify/DAGCBOR/DAGCBORValue.swift new file mode 100644 index 0000000..a1aa285 --- /dev/null +++ b/Sources/AtprotoTypesVerify/DAGCBOR/DAGCBORValue.swift @@ -0,0 +1,119 @@ +// +// DAGCBORValue.swift +// AtprotoTypesVerify +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import Foundation + +///The IPLD data model, as much of it as DAG-CBOR admits. +/// +///A generic value rather than `Codable` models of the commit and MST node, +///because verifying a commit signature means re-encoding the commit *minus its +///`sig`* and hashing that. Any field we didn't model would vanish in the +///round-trip and the preimage would be wrong — so the decode has to be +///lossless over the whole node, not just the parts we happen to read. +/// +///This is also why a CBOR library doesn't substitute, strict or not — the ones +///worth considering are `Codable`-only. See `docs/dependency-choices.md`. +public indirect enum DAGCBORValue: Sendable, Hashable { + case null + case bool(Bool) + case integer(Int64) + case float(Double) + case bytes(Data) + case string(String) + case array([DAGCBORValue]) + ///Ordered, because DAG-CBOR fixes the key order and re-encoding has to + ///reproduce it. Decoding validates the order, so this is already canonical. + case map([(key: String, value: DAGCBORValue)]) + case link(ContentIdentifier) + + public static func == (lhs: DAGCBORValue, rhs: DAGCBORValue) -> Bool { + switch (lhs, rhs) { + case (.null, .null): true + case (.bool(let a), .bool(let b)): a == b + case (.integer(let a), .integer(let b)): a == b + case (.float(let a), .float(let b)): a.bitPattern == b.bitPattern + case (.bytes(let a), .bytes(let b)): a == b + case (.string(let a), .string(let b)): a == b + case (.array(let a), .array(let b)): a == b + case (.link(let a), .link(let b)): a == b + case (.map(let a), .map(let b)): + a.count == b.count + && zip(a, b).allSatisfy { $0.key == $1.key && $0.value == $1.value } + default: false + } + } + + public func hash(into hasher: inout Hasher) { + switch self { + case .null: hasher.combine(0) + case .bool(let value): hasher.combine(value) + case .integer(let value): hasher.combine(value) + case .float(let value): hasher.combine(value.bitPattern) + case .bytes(let value): hasher.combine(value) + case .string(let value): hasher.combine(value) + case .array(let value): hasher.combine(value) + case .link(let value): hasher.combine(value) + case .map(let entries): + for entry in entries { + hasher.combine(entry.key) + hasher.combine(entry.value) + } + } + } +} + +extension DAGCBORValue { + public subscript(key: String) -> DAGCBORValue? { + guard case .map(let entries) = self else { return nil } + return entries.first { $0.key == key }?.value + } + + public var stringValue: String? { + guard case .string(let value) = self else { return nil } + return value + } + + public var integerValue: Int64? { + guard case .integer(let value) = self else { return nil } + return value + } + + public var bytesValue: Data? { + guard case .bytes(let value) = self else { return nil } + return value + } + + public var arrayValue: [DAGCBORValue]? { + guard case .array(let value) = self else { return nil } + return value + } + + public var linkValue: ContentIdentifier? { + guard case .link(let value) = self else { return nil } + return value + } + + ///For the nullable link fields the repo format uses — `prev` on a commit, + ///`l` and `t` on an MST node. A missing key and an explicit null are the + ///same absence; anything else present is malformed rather than absent, + ///which is why this throws instead of returning nil. + public static func optionalLink( + _ value: DAGCBORValue? + ) throws -> ContentIdentifier? { + switch value { + case .none, .some(.null): nil + case .some(.link(let cid)): cid + default: throw Atproto.Repo.ProofError.mstNodeMalformed + } + } + + public func removing(key: String) -> DAGCBORValue { + guard case .map(let entries) = self else { return self } + return .map(entries.filter { $0.key != key }) + } +} diff --git a/Sources/AtprotoTypesVerify/MST/MerkleSearchTree.swift b/Sources/AtprotoTypesVerify/MST/MerkleSearchTree.swift new file mode 100644 index 0000000..a58a654 --- /dev/null +++ b/Sources/AtprotoTypesVerify/MST/MerkleSearchTree.swift @@ -0,0 +1,105 @@ +// +// MerkleSearchTree.swift +// AtprotoTypesVerify +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import Foundation + +///The MST walk: from a signed commit's `data` root down to one key. +/// +///A node is `{ e: [entries], l: left-subtree }`, and an entry is +///`{ p: shared-prefix-length, k: key-suffix, v: value, t: right-subtree }`. +///Keys are `collection/rkey`, stored bytewise-ascending with the shared prefix +///of the preceding key elided — so reconstructing a key means carrying the +///previous one, and a node that lies about `p` is the first thing to reject. +/// +///The semantics are atproto's, not a generic Merkle tree's, so no general +///library applies. See `docs/dependency-choices.md`. +public enum MerkleSearchTree { + enum Step { + case found(ContentIdentifier) + case descend(ContentIdentifier?) + } + + ///Returns the value CID the tree proves for `key`, or throws. There is no + ///"probably" — either the walk lands on the key or the record is not in the + ///repo at that path. + public static func find( + key: String, + root: ContentIdentifier, + in car: CARv1 + ) throws -> ContentIdentifier { + let target = Array(key.utf8) + var current = root + //a proof is a DAG the server chose; nothing stops it pointing a node at + //an ancestor, and an unbounded walk would then never return + var visited: Set = [] + + while true { + guard visited.insert(current.bytes).inserted else { + throw Atproto.Repo.ProofError.mstCycle + } + + switch try step(node: try car.decoded(current), target: target) { + case .found(let value): + return value + case .descend(let next): + guard let next else { + throw Atproto.Repo.ProofError.recordNotInTree + } + current = next + } + } + } + + static func step(node: DAGCBORValue, target: [UInt8]) throws -> Step { + guard let entries = node["e"]?.arrayValue else { + throw Atproto.Repo.ProofError.mstNodeMalformed + } + + //the subtree covering keys below the entry currently being considered: + //the node's own `l` before any entry, then each entry's `t` after it + var subtree = try DAGCBORValue.optionalLink(node["l"]) + var previousKey: [UInt8] = [] + var isFirst = true + + for entry in entries { + guard let prefix = entry["p"]?.integerValue, + let suffix = entry["k"]?.bytesValue, + let value = entry["v"]?.linkValue + else { + throw Atproto.Repo.ProofError.mstNodeMalformed + } + guard prefix >= 0, Int(prefix) <= previousKey.count else { + throw Atproto.Repo.ProofError.mstPrefixOutOfRange + } + + var fullKey = Array(previousKey[0.. Proof { + let signingKey = try RepoSigningKey(atprotoKeyIn: document, did: did) + let archive = try CARv1(car) + + //the CAR's first root is the commit the proof is rooted at; every + //block in the archive was checked against its own CID on the way in + guard let commitCID = archive.roots.first else { + throw ProofError.noCARRoots + } + let commitBytes = try archive.block(commitCID) + let commit = try DAGCBORDecoder.decode(commitBytes) + + let rev = try checkCommit(commit, did: did, signingKey: signingKey) + + guard let mstRoot = commit["data"]?.linkValue else { + throw ProofError.commitFieldMissing("data") + } + + let recordCID = try MerkleSearchTree.find( + key: path.mstKey, + root: mstRoot, + in: archive + ) + guard recordCID.codec == .dagCBOR else { + throw ProofError.unsupportedCodec(recordCID.codec.rawValue) + } + + return Proof( + did: did, + path: path, + cid: recordCID.atprotoCID, + block: try archive.block(recordCID), + rev: rev + ) + } + + ///Returns the commit's `rev` once the signature over it holds. + func checkCommit( + _ commit: DAGCBORValue, + did: Atproto.DID, + signingKey: RepoSigningKey + ) throws -> String { + guard case .map = commit else { + throw ProofError.commitNotAnObject + } + + guard let subject = commit["did"]?.stringValue else { + throw ProofError.commitFieldMissing("did") + } + //without this the proof is real but about somebody else's repo + guard subject == did.rawValue else { + throw ProofError.commitDIDMismatch( + expected: did.rawValue, + found: subject + ) + } + + guard let version = commit["version"]?.integerValue else { + throw ProofError.commitFieldMissing("version") + } + guard version == Self.supportedCommitVersion else { + throw ProofError.unsupportedCommitVersion(version) + } + + guard let rev = commit["rev"]?.stringValue else { + throw ProofError.commitFieldMissing("rev") + } + guard let signature = commit["sig"]?.bytesValue else { + throw ProofError.missingSignature + } + + //the signature covers the commit with `sig` removed, so the + //preimage has to be rebuilt — which is the only reason this module + //carries an encoder at all + let preimage = DAGCBOREncoder.encode(commit.removing(key: "sig")) + try signingKey.verify(signature: signature, over: preimage) + + return rev + } + } +} diff --git a/Sources/AtprotoTypesVerify/Signing/RepoSigningKey.swift b/Sources/AtprotoTypesVerify/Signing/RepoSigningKey.swift new file mode 100644 index 0000000..ca59449 --- /dev/null +++ b/Sources/AtprotoTypesVerify/Signing/RepoSigningKey.swift @@ -0,0 +1,195 @@ +// +// RepoSigningKey.swift +// AtprotoTypesVerify +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import BaseX +import Crypto +import Foundation + +///The repo's signing key, as the DID document publishes it: a multibase +///base58btc string wrapping a multicodec prefix and a compressed point. +/// +///Two curves are in use across the network. P-256 is here; secp256k1 is what +///Bluesky's own PDS mints by default, and swift-crypto has no k256 at all, so +///this file carries a from-scratch, verify-only secp256k1 port +///(`Secp256k1.ECDSA`) alongside the P-256 path swift-crypto backs directly. +///See `docs/dependency-choices.md` for why that isn't an imported library. +public struct RepoSigningKey: Sendable { + public enum Curve: Sendable, Equatable { + case p256 + case secp256k1 + + //multicodec, varint-encoded in the multibase payload + static func named(_ code: UInt64) -> Curve? { + switch code { + case 0x1200: .p256 + case 0xe7: .secp256k1 + default: nil + } + } + + public var name: String { + switch self { + case .p256: "p256" + case .secp256k1: "secp256k1" + } + } + } + + public let curve: Curve + ///SEC1 compressed point, 33 bytes. + public let compressedPoint: Data + + ///Picks the `#atproto` verification method out of a DID document. atproto + ///documents may list several; the repo signing key is the one with that + ///fragment, and taking "the first one" instead would let a document with an + ///extra method up front decide what we check against. + public init(atprotoKeyIn document: Atproto.DIDDocument, did: Atproto.DID) throws { + guard + let method = document.verificationMethod.first(where: { + $0.id == "#atproto" || $0.id.hasSuffix("#atproto") + }) + else { + throw Atproto.Repo.ProofError.noAtprotoSigningKey + } + + //a document that names someone else as controller of its signing key is + //not making a claim about this DID's repo. An absent controller falls + //back to the document's own id, per DID convention (no controller means + //self-controlled) — never to a blanket skip, or a document paired with + //the wrong `did` by a caller-side bug would pass whenever the method + //simply omits the field, which is the common case for real documents. + let controllerDID = method.controller.isEmpty ? document.id : method.controller + guard controllerDID == did.rawValue else { + throw Atproto.Repo.ProofError.signingKeyControllerMismatch + } + + try self.init(multibase: method.publicKeyMultibase) + } + + public init(multibase: String) throws { + let trimmed = + multibase.hasPrefix("did:key:") + ? String(multibase.dropFirst("did:key:".count)) + : multibase + + //`z` is multibase base58btc; nothing else appears in atproto documents + guard trimmed.hasPrefix("z") else { + throw Atproto.Repo.ProofError.badMultibaseKey + } + + let decoded: Data + do { + decoded = try BaseX.decode(String(trimmed.dropFirst()), as: .base58BTC) + } catch { + throw Atproto.Repo.ProofError.badMultibaseKey + } + + var reader = ByteReader(decoded) + let code = try reader.readUnsignedVarint() + guard let curve = Curve.named(code) else { + throw Atproto.Repo.ProofError.unsupportedCurve("multicodec 0x\(String(code, radix: 16))") + } + + let pointLength = reader.remaining + let point = Data(try reader.read(pointLength)) + //compressed form only: 0x02 or 0x03 then a 32-byte x + guard point.count == 33, point.first == 0x02 || point.first == 0x03 else { + throw Atproto.Repo.ProofError.badMultibaseKey + } + + self.curve = curve + self.compressedPoint = point + } + + ///Verifies a 64-byte compact `r ‖ s` signature over `message`, hashing with + ///SHA-256 as the repo format specifies. + public func verify(signature: Data, over message: Data) throws { + guard signature.count == 64 else { + throw Atproto.Repo.ProofError.badSignatureLength(signature.count) + } + + switch curve { + case .secp256k1: + //same low-S rule as p256, against this curve's own order — atproto + //requires it network-wide, not just for the curve swift-crypto backs + guard Self.isLowS(Array(signature.suffix(32)), order: Self.secp256k1Order) else { + throw Atproto.Repo.ProofError.nonCanonicalSignature + } + + let digest = SHA256.hash(data: message) + guard + Secp256k1.ECDSA.verify( + signature: signature, digest: Data(digest), compressedPublicKey: compressedPoint) + else { + throw Atproto.Repo.ProofError.signatureDidNotVerify + } + + case .p256: + //atproto requires low-S. swift-crypto will happily verify the + //high-S twin, so rejecting malleated signatures is on us: without + //it two distinct byte strings both "prove" one commit. + guard Self.isLowS(Array(signature.suffix(32)), order: Self.p256Order) else { + throw Atproto.Repo.ProofError.nonCanonicalSignature + } + + let key: P256.Signing.PublicKey + let parsed: P256.Signing.ECDSASignature + do { + key = try P256.Signing.PublicKey( + compressedRepresentation: compressedPoint + ) + parsed = try P256.Signing.ECDSASignature(rawRepresentation: signature) + } catch { + throw Atproto.Repo.ProofError.badMultibaseKey + } + + guard key.isValidSignature(parsed, for: message) else { + throw Atproto.Repo.ProofError.signatureDidNotVerify + } + } + } + + ///Exposed beyond this module (package-wide, not public) so fixture-building + ///test support in AtprotoTypesVerifyMocks — which needs to fold a signature + ///to its low- or high-S twin — shares the same threshold rather than + ///risking a second, driftable copy of it. + package static let p256Order: [UInt8] = [ + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, + 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51, + ] + + ///Derived from `Secp256k1.Scalar.order` rather than transcribed as a + ///second hex constant — one order to keep in sync with the curve + ///parameters, not two. + package static let secp256k1Order: [UInt8] = Secp256k1.Limbs256.bigEndianBytes( + Secp256k1.Scalar.order) + + ///`s <= n/2`. The half-order is derived from the order rather than written + ///out, so there is one constant to check against the curve parameters + ///instead of two. + package static func isLowS(_ s: [UInt8], order: [UInt8]) -> Bool { + let half = halved(order) + guard s.count == half.count else { return false } + for (lhs, rhs) in zip(s, half) where lhs != rhs { + return lhs < rhs + } + return true + } + + static func halved(_ bytes: [UInt8]) -> [UInt8] { + var out = [UInt8](repeating: 0, count: bytes.count) + var carry: UInt8 = 0 + for index in bytes.indices { + out[index] = (bytes[index] >> 1) | (carry << 7) + carry = bytes[index] & 1 + } + return out + } +} diff --git a/Sources/AtprotoTypesVerify/Signing/Secp256k1/ECDSA.swift b/Sources/AtprotoTypesVerify/Signing/Secp256k1/ECDSA.swift new file mode 100644 index 0000000..788ec67 --- /dev/null +++ b/Sources/AtprotoTypesVerify/Signing/Secp256k1/ECDSA.swift @@ -0,0 +1,56 @@ +// +// ECDSA.swift +// AtprotoTypesVerify +// +// Created by Mark @ Germ on 8/17/26. +// + +import Foundation + +///Verify-only ECDSA over secp256k1. Deliberately just the math: low-S is an +///atproto policy, not an ECDSA property (a high-S signature is a +///mathematically valid twin of its low-S counterpart), so that rejection +///stays a caller concern — `RepoSigningKey.verify` enforces it the same way +///for both curves, rather than this file half-owning the policy. +extension Secp256k1 { + enum ECDSA { + ///`signature` is 64 bytes, big-endian `r ‖ s`. `digest` is the message + ///hash (SHA-256, per the repo signing format) — this does not hash the + ///message itself. Returns `false` for any malformed component: a zero + ///or out-of-range `r`/`s`, an undecodable public key, or a recovered + ///point at infinity all collapse to a plain refusal rather than a + ///distinguished error, matching how Wycheproof's "other invalid" + ///bucket expects these to be indistinguishable from a wrong signature. + static func verify(signature: Data, digest: Data, compressedPublicKey: Data) -> Bool { + //not reachable from `RepoSigningKey.verify` today (always a + //32-byte SHA-256 output) — guarded anyway, because + //`Scalar(reducingBigEndian:)` folds any other length to zero, + //and z = 0 lets anyone forge a signature over an infinite + //family of (r, s) pairs without the private key at all: pick + //any t, R = t·G, r = R.x mod n, s = r·t⁻¹ mod n. + guard digest.count == 32 else { return false } + guard signature.count == 64 else { return false } + guard let r = Scalar(canonicalBigEndian: Array(signature.prefix(32))), !r.isZero else { + return false + } + guard let s = Scalar(canonicalBigEndian: Array(signature.suffix(32))), !s.isZero else { + return false + } + guard let publicKey = Point(compressed: compressedPublicKey) else { return false } + + let z = Scalar(reducingBigEndian: Array(digest)) + let w = s.inverted + let u1 = z * w + let u2 = r * w + + let sum = Point.generator.multiplied(by: u1) + publicKey.multiplied(by: u2) + guard let x = sum.affine?.x else { return false } + + //R.x is an element of F_p; the comparison against r is mod n, per + //the algorithm — folding through the same `reducingBigEndian` path + //`z` used keeps this one reduction routine instead of a second, + //parallel one. + return Scalar(reducingBigEndian: x.bigEndianBytes) == r + } + } +} diff --git a/Sources/AtprotoTypesVerify/Signing/Secp256k1/Field.swift b/Sources/AtprotoTypesVerify/Signing/Secp256k1/Field.swift new file mode 100644 index 0000000..729e0d5 --- /dev/null +++ b/Sources/AtprotoTypesVerify/Signing/Secp256k1/Field.swift @@ -0,0 +1,185 @@ +// +// Field.swift +// AtprotoTypesVerify +// +// Created by Mark @ Germ on 8/17/26. +// + +import Foundation + +///An element of GF(p) for secp256k1's p = 2^256 - 2^32 - 977. +/// +///**Not constant-time, deliberately.** This is verify-only: every value it +///ever touches — a public key, a signature, a message hash — is public, and +///there is no secret whose timing could leak. That is the property that made +///a from-scratch Swift port acceptable at all, and it is why the +///code here can use straightforward branching arithmetic rather than the +///contortions a signing implementation would need. +/// +///**This file must never grow a signing path.** A private key here would be +///a vulnerability, not a feature — verify-only is the whole point. +extension Secp256k1 { + struct Field: Sendable, Equatable { + ///Always reduced: `0 <= value < p`. + let value: Limbs + + ///Tuples compare structurally but do not conform to `Equatable` as a + ///protocol, so synthesis can't see through `value`'s type — spelled + ///out explicitly instead. + static func == (lhs: Field, rhs: Field) -> Bool { + Limbs256.compare(lhs.value, rhs.value) == 0 + } + + ///p = 2^256 - 2^32 - 977 + static let modulus: Limbs = ( + 0xFFFF_FFFE_FFFF_FC2F, + 0xFFFF_FFFF_FFFF_FFFF, + 0xFFFF_FFFF_FFFF_FFFF, + 0xFFFF_FFFF_FFFF_FFFF + ) + + ///2^256 mod p — what the fast reduction folds the high half by, a + ///consequence of p's specific form (2^256 ≡ 2^32 + 977, mod p). + private static let foldFactor: UInt64 = 0x1_0000_03D1 + + static let zero = Field(unchecked: Limbs256.zero) + static let one = Field(unchecked: Limbs256.one) + + private init(unchecked value: Limbs) { + self.value = value + } + + ///Fails on a non-canonical value. For a public-key coordinate that is a + ///rejection, not something to silently reduce into range. + init?(canonical value: Limbs) { + guard Limbs256.compare(value, Self.modulus) < 0 else { return nil } + self.value = value + } + + init?(bigEndian bytes: [UInt8]) { + guard let limbs = Limbs256.from(bigEndian: bytes) else { return nil } + self.init(canonical: limbs) + } + + var bigEndianBytes: [UInt8] { Limbs256.bigEndianBytes(value) } + var isZero: Bool { Limbs256.isZero(value) } + var isOdd: Bool { value.0 & 1 == 1 } + + // MARK: - Arithmetic + + static func + (lhs: Field, rhs: Field) -> Field { + let (sum, carry) = Limbs256.adding(lhs.value, rhs.value) + //a carry out means the true sum is >= 2^256 > p; one subtraction + //suffices because both inputs are already below p + if carry != 0 || Limbs256.compare(sum, modulus) >= 0 { + return Field(unchecked: Limbs256.subtracting(sum, modulus).0) + } + return Field(unchecked: sum) + } + + static func - (lhs: Field, rhs: Field) -> Field { + let (difference, borrow) = Limbs256.subtracting(lhs.value, rhs.value) + if borrow != 0 { + return Field(unchecked: Limbs256.adding(difference, modulus).0) + } + return Field(unchecked: difference) + } + + static func * (lhs: Field, rhs: Field) -> Field { + reduce(Limbs256.multiplyWide(lhs.value, rhs.value)) + } + + func squared() -> Field { + Self.reduce(Limbs256.squareWide(value)) + } + + var negated: Field { + isZero ? self : Field(unchecked: Limbs256.subtracting(Self.modulus, value).0) + } + + ///Folds a 512-bit product down using 2^256 ≡ 2^32 + 977 (mod p): each + ///pass multiplies the high half by the 33-bit `foldFactor` and adds it + ///back into the low half, which is what the modulus's special form + ///buys — no division anywhere. Each pass's leftover high part is + ///roughly `foldFactor`'s own width (33 bits) narrower than the one + ///before it, so a handful of passes clears it — well inside the loop + ///bound below. + static func reduce(_ wide: [UInt64]) -> Field { + var buffer = wide + + var pass = 0 + while !(buffer[4] == 0 && buffer[5] == 0 && buffer[6] == 0 && buffer[7] == 0) { + pass += 1 + precondition(pass < 8, "field reduction did not converge") + + let high: Limbs = (buffer[4], buffer[5], buffer[6], buffer[7]) + var next = [UInt64](repeating: 0, count: 8) + next[0] = buffer[0] + next[1] = buffer[1] + next[2] = buffer[2] + next[3] = buffer[3] + + //high * foldFactor, added into the low half starting at + //position 0 — foldFactor is 33 bits, so each limb's product + //can ripple up to two positions beyond where it starts + for (index, limb) in [high.0, high.1, high.2, high.3].enumerated() { + let (hi, lo) = limb.multipliedFullWidth(by: foldFactor) + Limbs256.rippleAdd(lo, at: index, into: &next) + Limbs256.rippleAdd(hi, at: index + 1, into: &next) + } + buffer = next + } + + var result: Limbs = (buffer[0], buffer[1], buffer[2], buffer[3]) + while Limbs256.compare(result, modulus) >= 0 { + result = Limbs256.subtracting(result, modulus).0 + } + return Field(unchecked: result) + } + + // MARK: - Exponentiation + + ///Square-and-multiply over every bit, including leading zeros. + ///Wasteful and obviously correct, which is the right trade for code + ///that runs a handful of times per signature check. + static func power(_ base: Field, _ exponent: Limbs) -> Field { + var result = Field.one + for limbIndex in stride(from: 3, through: 0, by: -1) { + let limb = Limbs256[exponent, limbIndex] + for bit in stride(from: 63, through: 0, by: -1) { + result = result.squared() + if (limb >> UInt64(bit)) & 1 == 1 { + result = result * base + } + } + } + return result + } + + ///p - 2 and (p+1)/4, derived from the modulus rather than written out + ///as separate 64-digit hex constants — two independently-transcribed + ///constants is two chances to be quietly wrong in a way only a rare + ///vector would catch. + private static let inverseExponent = Limbs256.subtracting(modulus, (2, 0, 0, 0)).0 + private static let sqrtExponent = Limbs256.shiftedRight( + Limbs256.adding(modulus, Limbs256.one).0, + by: 2 + ) + + ///Fermat's little theorem (p is prime). `zero` has no inverse and + ///callers must not ask; the one caller here (point-to-affine) checks + ///for infinity first. + var inverted: Field { + Self.power(self, Self.inverseExponent) + } + + ///A square root when one exists, else nil. p ≡ 3 (mod 4), so the + ///candidate is `a^((p+1)/4)` — and it is squared back to confirm, + ///because that formula returns a wrong answer rather than failing when + ///`self` is not a quadratic residue. + var squareRoot: Field? { + let candidate = Self.power(self, Self.sqrtExponent) + return candidate.squared() == self ? candidate : nil + } + } +} diff --git a/Sources/AtprotoTypesVerify/Signing/Secp256k1/Limbs256.swift b/Sources/AtprotoTypesVerify/Signing/Secp256k1/Limbs256.swift new file mode 100644 index 0000000..1dd2714 --- /dev/null +++ b/Sources/AtprotoTypesVerify/Signing/Secp256k1/Limbs256.swift @@ -0,0 +1,153 @@ +// +// Limbs256.swift +// AtprotoTypesVerify +// +// Created by Mark @ Germ on 8/17/26. +// + +import Foundation + +///256-bit unsigned integers as four `UInt64` limbs, least significant first — +///not `UInt128`, deliberately: this package floors at iOS 16 / macOS 13, and +///the stdlib's `UInt128` needs iOS 18 / macOS 15. `multipliedFullWidth(by:)` +///gives the same 128-bit intermediate a `UInt128` product would, without the +///floor bump. +enum Secp256k1 {} + +extension Secp256k1 { + typealias Limbs = (UInt64, UInt64, UInt64, UInt64) + + enum Limbs256 { + static func from(bigEndian bytes: [UInt8]) -> Limbs? { + guard bytes.count == 32 else { return nil } + func word(_ start: Int) -> UInt64 { + var value: UInt64 = 0 + for index in start..<(start + 8) { + value = (value << 8) | UInt64(bytes[index]) + } + return value + } + return (word(24), word(16), word(8), word(0)) + } + + static func bigEndianBytes(_ value: Limbs) -> [UInt8] { + var out = [UInt8]() + out.reserveCapacity(32) + for word in [value.3, value.2, value.1, value.0] { + for shift in stride(from: 56, through: 0, by: -8) { + out.append(UInt8(truncatingIfNeeded: word >> UInt64(shift))) + } + } + return out + } + + static subscript(_ value: Limbs, _ index: Int) -> UInt64 { + switch index { + case 0: value.0 + case 1: value.1 + case 2: value.2 + default: value.3 + } + } + + static let zero: Limbs = (0, 0, 0, 0) + static let one: Limbs = (1, 0, 0, 0) + + static func isZero(_ value: Limbs) -> Bool { + value.0 == 0 && value.1 == 0 && value.2 == 0 && value.3 == 0 + } + + ///Not constant-time. Every value compared during verification is + ///public — the whole point of a verify-only port — so there is no + ///secret whose timing could leak, and a straightforward branching + ///comparison is the honest, easy-to-audit choice. + static func compare(_ lhs: Limbs, _ rhs: Limbs) -> Int { + if lhs.3 != rhs.3 { return lhs.3 < rhs.3 ? -1 : 1 } + if lhs.2 != rhs.2 { return lhs.2 < rhs.2 ? -1 : 1 } + if lhs.1 != rhs.1 { return lhs.1 < rhs.1 ? -1 : 1 } + if lhs.0 != rhs.0 { return lhs.0 < rhs.0 ? -1 : 1 } + return 0 + } + + static func adding(_ lhs: Limbs, _ rhs: Limbs) -> (Limbs, carry: UInt64) { + var result = [UInt64](repeating: 0, count: 4) + var carry: UInt64 = 0 + let l = [lhs.0, lhs.1, lhs.2, lhs.3] + let r = [rhs.0, rhs.1, rhs.2, rhs.3] + for index in 0..<4 { + let (sum1, o1) = l[index].addingReportingOverflow(r[index]) + let (sum2, o2) = sum1.addingReportingOverflow(carry) + result[index] = sum2 + carry = (o1 ? 1 : 0) &+ (o2 ? 1 : 0) + } + return ((result[0], result[1], result[2], result[3]), carry) + } + + static func subtracting(_ lhs: Limbs, _ rhs: Limbs) -> (Limbs, borrow: UInt64) { + var result = [UInt64](repeating: 0, count: 4) + var borrow: UInt64 = 0 + let l = [lhs.0, lhs.1, lhs.2, lhs.3] + let r = [rhs.0, rhs.1, rhs.2, rhs.3] + for index in 0..<4 { + let (diff1, u1) = l[index].subtractingReportingOverflow(r[index]) + let (diff2, u2) = diff1.subtractingReportingOverflow(borrow) + result[index] = diff2 + borrow = (u1 ? 1 : 0) &+ (u2 ? 1 : 0) + } + return ((result[0], result[1], result[2], result[3]), borrow) + } + + ///Adds `value` at `position` in an 8-limb accumulator, rippling the + ///carry forward through as many further positions as it takes. This is + ///the one primitive `multiplyWide` and the reduction routines build on; + ///keeping it this literal — rather than folding a product's high word + ///and two borrowed carry flags into one addition by hand — is what + ///makes the accumulation provably lossless instead of merely + ///believed so: a hand-fused add can undercount by exactly one in a + ///narrow edge case (large product, both inputs already near + ///`UInt64.max`), and that is not a bug a quick reading catches. + static func rippleAdd(_ value: UInt64, at position: Int, into accumulator: inout [UInt64]) { + var carry = value + var index = position + while carry != 0 { + precondition(index < accumulator.count, "carry overflowed the accumulator") + let (sum, overflow) = accumulator[index].addingReportingOverflow(carry) + accumulator[index] = sum + carry = overflow ? 1 : 0 + index += 1 + } + } + + ///Full 512-bit product as eight limbs, least significant first. + static func multiplyWide(_ lhs: Limbs, _ rhs: Limbs) -> [UInt64] { + var result = [UInt64](repeating: 0, count: 8) + let l = [lhs.0, lhs.1, lhs.2, lhs.3] + let r = [rhs.0, rhs.1, rhs.2, rhs.3] + for i in 0..<4 { + for j in 0..<4 { + let (hi, lo) = l[i].multipliedFullWidth(by: r[j]) + rippleAdd(lo, at: i + j, into: &result) + rippleAdd(hi, at: i + j + 1, into: &result) + } + } + return result + } + + static func squareWide(_ value: Limbs) -> [UInt64] { + multiplyWide(value, value) + } + + ///Only used for small in-limb shifts (0 < count < 64). + static func shiftedRight(_ value: Limbs, by count: Int) -> Limbs { + precondition(count > 0 && count < 64) + let carryShift = UInt64(64 - count) + let shift = UInt64(count) + return ( + (value.0 >> shift) | (value.1 << carryShift), + (value.1 >> shift) | (value.2 << carryShift), + (value.2 >> shift) | (value.3 << carryShift), + value.3 >> shift + ) + } + } +} diff --git a/Sources/AtprotoTypesVerify/Signing/Secp256k1/Point.swift b/Sources/AtprotoTypesVerify/Signing/Secp256k1/Point.swift new file mode 100644 index 0000000..c5f47df --- /dev/null +++ b/Sources/AtprotoTypesVerify/Signing/Secp256k1/Point.swift @@ -0,0 +1,158 @@ +// +// Point.swift +// AtprotoTypesVerify +// +// Created by Mark @ Germ on 8/17/26. +// + +import Foundation + +///A point on secp256k1: y² = x³ + 7 (a = 0, b = 7), in Jacobian coordinates +///(X, Y, Z) representing the affine point (X/Z², Y/Z³). Jacobian avoids a +///field inversion per point operation — this pays exactly one, in +///`affine()`, at the end of a scalar multiplication. +/// +///Same posture as `Field`/`Scalar`: verify-only, not constant-time — every +///point here (a public key, an intermediate in `u1·G + u2·Q`) is public. +extension Secp256k1 { + ///`Equatable` only compares raw (X, Y, Z) triples — safe against the + ///literal `.infinity` case, since it carries no coordinates, but two + ///different Jacobian triples can represent the same affine point (a + ///fresh scalar multiplication accumulates an arbitrary Z; a value built + ///directly, like `.negated`, may keep its input's Z). Comparing two + ///non-infinity points for equality means comparing `.affine` values, not + ///`==` on the `Point` itself. + enum Point: Sendable, Equatable { + ///The identity of point addition. An explicit case rather than a + ///sentinel coordinate (e.g. `Z == 0` folded into the general formulas) + ///— infinity-handling bugs are the classic failure mode for curve + ///code, and a case the compiler makes you switch on is harder to + ///silently mishandle than a coordinate convention is. + case infinity + case affinePoint(x: Field, y: Field, z: Field) + + static func jacobian(x: Field, y: Field, z: Field) -> Point { + .affinePoint(x: x, y: y, z: z) + } + + ///b = 7 in y² = x³ + 7. + static let b = Field(bigEndian: [UInt8](repeating: 0, count: 31) + [7])! + + ///The generator point, from the standard secp256k1 domain parameters. + static let generator: Point = { + let x = Field( + bigEndian: Array( + hex: "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"))! + let y = Field( + bigEndian: Array( + hex: "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"))! + return .jacobian(x: x, y: y, z: Field.one) + }() + + // MARK: - Doubling and addition + + ///Standard Jacobian doubling for a = 0 curves. + func doubled() -> Point { + guard case .affinePoint(let x1, let y1, let z1) = self else { return .infinity } + if y1.isZero { return .infinity } //a point of order 2, which secp256k1 has none of except this degenerate input + + let a = x1.squared() + let b = y1.squared() + let c = b.squared() + let xPlusB = x1 + b + let d = (xPlusB.squared() - a - c) + (xPlusB.squared() - a - c) + let e = a + a + a + let f = e.squared() + let x3 = f - d - d + let y3 = e * (d - x3) - (c + c + c + c + c + c + c + c) + let z3 = (y1 * z1) + (y1 * z1) + + return .jacobian(x: x3, y: y3, z: z3) + } + + ///Standard Jacobian addition. Dispatches to `doubled()` when the two + ///points coincide — the classic bug in a naive Jacobian add, which + ///produces the wrong answer (or a spurious infinity) for `P + P` + ///because the general formula's `H = U2 - U1` denominator-equivalent + ///vanishes exactly when it must not. + static func + (lhs: Point, rhs: Point) -> Point { + guard case .affinePoint(let x1, let y1, let z1) = lhs else { return rhs } + guard case .affinePoint(let x2, let y2, let z2) = rhs else { return lhs } + + let z1z1 = z1.squared() + let z2z2 = z2.squared() + let u1 = x1 * z2z2 + let u2 = x2 * z1z1 + let s1 = y1 * z2 * z2z2 + let s2 = y2 * z1 * z1z1 + + if u1 == u2 { + //same x: either the same point (double it) or additive + //inverses (the result is infinity) + return s1 == s2 ? lhs.doubled() : .infinity + } + + let h = u2 - u1 + let doubleH = h + h + let i = doubleH.squared() + let j = h * i + let r = (s2 - s1) + (s2 - s1) + let v = u1 * i + let x3 = r.squared() - j - v - v + let y3 = r * (v - x3) - (s1 * j + s1 * j) + let sumZ = z1 + z2 + let z3 = (sumZ.squared() - z1z1 - z2z2) * h + + return .jacobian(x: x3, y: y3, z: z3) + } + + var negated: Point { + guard case .affinePoint(let x, let y, let z) = self else { return .infinity } + return .jacobian(x: x, y: y.negated, z: z) + } + + // MARK: - Scalar multiplication + + ///Double-and-add, most significant bit first. Not constant-time — + ///deliberately, see the file header — which is what makes the + ///straightforward textbook algorithm the right one here. + func multiplied(by scalar: Scalar) -> Point { + var result = Point.infinity + for limbIndex in stride(from: 3, through: 0, by: -1) { + let limb = Limbs256[scalar.value, limbIndex] + for bit in stride(from: 63, through: 0, by: -1) { + result = result.doubled() + if (limb >> UInt64(bit)) & 1 == 1 { + result = result + self + } + } + } + return result + } + + // MARK: - Affine conversion + + ///The one inversion a scalar multiplication pays. `nil` only for + ///infinity, which has no affine representation. + var affine: (x: Field, y: Field)? { + guard case .affinePoint(let x, let y, let z) = self else { return nil } + let zInverted = z.inverted + let zInvertedSquared = zInverted.squared() + return (x * zInvertedSquared, y * zInvertedSquared * zInverted) + } + } +} + +extension Array where Element == UInt8 { + fileprivate init(hex: String) { + var bytes: [UInt8] = [] + var index = hex.startIndex + while index < hex.endIndex, + let next = hex.index(index, offsetBy: 2, limitedBy: hex.endIndex) + { + bytes.append(UInt8(hex[index..= p` and any x + ///that is not on the curve (no square root of `x³ + 7` exists). + init?(compressed: Data) { + guard compressed.count == 33 else { return nil } + let prefix = compressed[compressed.startIndex] + guard prefix == 0x02 || prefix == 0x03 else { return nil } + + guard let x = Secp256k1.Field(bigEndian: Array(compressed.dropFirst())) else { + return nil + } + + let rhs = x.squared() * x + Secp256k1.Point.b + guard let candidate = rhs.squareRoot else { return nil } + + let wantsOdd = prefix == 0x03 + let y = candidate.isOdd == wantsOdd ? candidate : candidate.negated + + self = .jacobian(x: x, y: y, z: Secp256k1.Field.one) + } +} diff --git a/Sources/AtprotoTypesVerify/Signing/Secp256k1/Scalar.swift b/Sources/AtprotoTypesVerify/Signing/Secp256k1/Scalar.swift new file mode 100644 index 0000000..769471c --- /dev/null +++ b/Sources/AtprotoTypesVerify/Signing/Secp256k1/Scalar.swift @@ -0,0 +1,148 @@ +// +// Scalar.swift +// AtprotoTypesVerify +// +// Created by Mark @ Germ on 8/17/26. +// + +import Foundation + +///An element of Z/nZ, n being secp256k1's group order. +/// +///Same posture as `Field`: verify-only, no secrets, so not constant-time. +///See that file's note — it applies here unchanged. +extension Secp256k1 { + struct Scalar: Sendable, Equatable { + ///Always reduced: `0 <= value < n`. + let value: Limbs + + ///n, the order of the generator. + static let order: Limbs = ( + 0xBFD2_5E8C_D036_4141, + 0xBAAE_DCE6_AF48_A03B, + 0xFFFF_FFFF_FFFF_FFFE, + 0xFFFF_FFFF_FFFF_FFFF + ) + + ///2^256 - n, so 2^256 ≡ this (mod n) — n has no reduction shortcut as + ///clean as p's, so folding uses the same technique against this + ///180-bit constant instead of a small one. Computed from `order` + ///rather than written out: a 64-digit hex constant transcribed by + ///hand is a second chance to be quietly wrong, alongside `order` + ///itself, in a way only a rare vector would catch. + private static let foldFactor: Limbs = { + let complement: Limbs = (~order.0, ~order.1, ~order.2, ~order.3) + return Limbs256.adding(complement, Limbs256.one).0 + }() + + static let zero = Scalar(unchecked: Limbs256.zero) + static let one = Scalar(unchecked: Limbs256.one) + + private init(unchecked value: Limbs) { + self.value = value + } + + static func == (lhs: Scalar, rhs: Scalar) -> Bool { + Limbs256.compare(lhs.value, rhs.value) == 0 + } + + ///For signature components, which must already be canonical — an + ///out-of-range `r` or `s` is a malformed signature, not something to + ///reduce into range. + init?(canonical value: Limbs) { + guard Limbs256.compare(value, Self.order) < 0 else { return nil } + self.value = value + } + + init?(canonicalBigEndian bytes: [UInt8]) { + guard let limbs = Limbs256.from(bigEndian: bytes) else { return nil } + self.init(canonical: limbs) + } + + ///For the message digest, which ECDSA *does* reduce mod n. A SHA-256 + ///output can exceed n, and folding it is part of the algorithm rather + ///than a leniency. + init(reducingBigEndian bytes: [UInt8]) { + guard let limbs = Limbs256.from(bigEndian: bytes) else { + self.value = Limbs256.zero + return + } + var reduced = limbs + while Limbs256.compare(reduced, Self.order) >= 0 { + reduced = Limbs256.subtracting(reduced, Self.order).0 + } + self.value = reduced + } + + var isZero: Bool { Limbs256.isZero(value) } + var bigEndianBytes: [UInt8] { Limbs256.bigEndianBytes(value) } + + // MARK: - Arithmetic + + static func * (lhs: Scalar, rhs: Scalar) -> Scalar { + reduce(Limbs256.multiplyWide(lhs.value, rhs.value)) + } + + ///Folds using 2^256 ≡ foldFactor (mod n). Each pass's leftover high + ///part is roughly `foldFactor`'s own width (~129 bits) narrower than + ///the one before it, so this converges in a handful of passes, well + ///inside the loop bound — and the trailing subtraction loop mops up + ///whatever remains below 2^256. + static func reduce(_ wide: [UInt64]) -> Scalar { + var buffer = wide + + var pass = 0 + while !(buffer[4] == 0 && buffer[5] == 0 && buffer[6] == 0 && buffer[7] == 0) { + pass += 1 + precondition(pass < 12, "scalar reduction did not converge") + + let high: Limbs = (buffer[4], buffer[5], buffer[6], buffer[7]) + let product = Limbs256.multiplyWide(high, foldFactor) + + var next = [UInt64](repeating: 0, count: 8) + next[0] = buffer[0] + next[1] = buffer[1] + next[2] = buffer[2] + next[3] = buffer[3] + for (index, limb) in product.enumerated() { + Limbs256.rippleAdd(limb, at: index, into: &next) + } + buffer = next + } + + var result: Limbs = (buffer[0], buffer[1], buffer[2], buffer[3]) + while Limbs256.compare(result, order) >= 0 { + result = Limbs256.subtracting(result, order).0 + } + return Scalar(unchecked: result) + } + + private static let inverseExponent = Limbs256.subtracting(order, (2, 0, 0, 0)).0 + + ///Fermat's little theorem (n is prime). Only ever called on `s` in the + ///ECDSA verify equation, which has already been checked non-zero. + var inverted: Scalar { + var result = Scalar.one + for limbIndex in stride(from: 3, through: 0, by: -1) { + let limb = Limbs256[Self.inverseExponent, limbIndex] + for bit in stride(from: 63, through: 0, by: -1) { + result = result * result + if (limb >> UInt64(bit)) & 1 == 1 { + result = result * self + } + } + } + return result + } + + // MARK: - Malleability + + ///n/2, for the low-S rule — mirrors `RepoSigningKey.isLowS`'s existing + ///shape exactly, against this curve's own order. + static let halfOrder = Limbs256.shiftedRight(order, by: 1) + + var isLowS: Bool { + Limbs256.compare(value, Self.halfOrder) <= 0 + } + } +} diff --git a/Sources/AtprotoTypesVerifyMocks/RepoFixture.swift b/Sources/AtprotoTypesVerifyMocks/RepoFixture.swift new file mode 100644 index 0000000..2729526 --- /dev/null +++ b/Sources/AtprotoTypesVerifyMocks/RepoFixture.swift @@ -0,0 +1,325 @@ +// +// RepoFixture.swift +// AtprotoTypesVerifyMocks +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import AtprotoTypesVerify +import Crypto +import Foundation + +///Builds real repos rather than pinning captured bytes: a signed commit over an +///MST over record blocks, framed as a CAR. Pinned blobs would test that the +///parser still parses one capture; building the structure lets a test say +///"forge this one field" and watch the proof fail for the reason it should. +/// +///A real library target, not test-target-internal code, so any package that +///wants to build a synthetic repo for its own tests can depend on this rather +///than reimplementing it — the same shape as `AtprotoTypesMocks` alongside +///`AtprotoTypes`. It only touches `AtprotoTypesVerify`'s public API. +///A compressed EC public key this package knows how to publish in a DID +///document — just enough to build a multibase string, not to verify or sign. +///Lets `AtprotoTypesVerifyTests` mint a k256 fixture key from raw bytes (via +///P256K, a test-only dependency) without `AtprotoTypesVerifyMocks` itself +///needing to depend on it. +public protocol RepoFixturePublicKey { + ///multicodec, e.g. `0x1200` (p256) or `0xe7` (secp256k1). + var repoFixtureMulticodec: UInt64 { get } + ///SEC1 compressed point, 33 bytes. + var repoFixtureCompressedRepresentation: Data { get } +} + +///A signing key `RepoFixture.commit` can use to produce a repo signature — +///P256's own type conforms below; a k256 conformance backed by P256K lives in +///`AtprotoTypesVerifyTests` only, since that library is a differential-test +///dependency, never a shipped one (`AtprotoTypesVerify` carries its own +///from-scratch, verify-only secp256k1 — no signing path, by design). +public protocol RepoFixtureSigningKey { + associatedtype PublicKey: RepoFixturePublicKey + var publicKey: PublicKey { get } + ///A 64-byte compact `r ‖ s` signature over `message`, already folded to + ///its low-S form — callers that want a malleable twin flip it after via + ///`RepoFixture.highS`, rather than every conformance having to know the + ///atproto policy itself. + func repoFixtureSignature(for message: Data) throws -> Data +} + +extension P256.Signing.PublicKey: RepoFixturePublicKey { + public var repoFixtureMulticodec: UInt64 { 0x1200 } + public var repoFixtureCompressedRepresentation: Data { compressedRepresentation } +} + +extension P256.Signing.PrivateKey: RepoFixtureSigningKey { + public func repoFixtureSignature(for message: Data) throws -> Data { + RepoFixture.lowS(try signature(for: message).rawRepresentation, order: RepoSigningKey.p256Order) + } +} + +///A secp256k1 public key, wrapping only the bytes a multibase string needs. +///Dependency-free: `AtprotoTypesVerifyTests` decodes a P256K public key down +///to its compressed representation and wraps it here, so this package never +///needs to know P256K exists. +public struct Secp256k1PublicKey: RepoFixturePublicKey, Sendable { + public let repoFixtureCompressedRepresentation: Data + + public init(compressedRepresentation: Data) { + self.repoFixtureCompressedRepresentation = compressedRepresentation + } + + public var repoFixtureMulticodec: UInt64 { 0xE7 } +} + +public enum RepoFixture { + public static let did = try! Atproto.DID(string: "did:plc:germverifytestsubject") + public static let attacker = try! Atproto.DID(string: "did:plc:germverifytestforger") + + public struct Block { + public let cid: ContentIdentifier + public let bytes: Data + + public init(cid: ContentIdentifier, bytes: Data) { + self.cid = cid + self.bytes = bytes + } + } + + public static func block(_ value: DAGCBORValue) throws -> Block { + let bytes = DAGCBOREncoder.encode(value) + return Block( + cid: try ContentIdentifier.compute(codec: .dagCBOR, block: bytes), + bytes: bytes + ) + } + + ///A declaration-shaped record. The shape does not matter to the proof — the + ///proof is about where the bytes live — but a realistic one keeps the tests + ///honest about sizes and key ordering. + public static func declaration(currentKey: Data) -> DAGCBORValue { + .map([ + ("$type", .string("com.germnetwork.declaration")), + ("currentKey", .bytes(currentKey)), + ("version", .string("1.0.0")), + ]) + } + + // MARK: - MST + + ///One MST node holding `entries` in ascending key order with the shared + ///prefix of the preceding key elided, exactly as the format specifies. + public static func node( + entries: [(key: String, value: ContentIdentifier)], + left: ContentIdentifier? = nil, + subtrees: [String: ContentIdentifier] = [:] + ) -> DAGCBORValue { + var encoded: [DAGCBORValue] = [] + var previous: [UInt8] = [] + + for entry in entries { + let key = Array(entry.key.utf8) + var shared = 0 + while shared < min(previous.count, key.count), + previous[shared] == key[shared] + { + shared += 1 + } + + encoded.append( + .map([ + ("k", .bytes(Data(key.dropFirst(shared)))), + ("p", .integer(Int64(shared))), + ("t", subtrees[entry.key].map { DAGCBORValue.link($0) } ?? .null), + ("v", .link(entry.value)), + ]) + ) + previous = key + } + + return .map([ + ("e", .array(encoded)), + ("l", left.map { DAGCBORValue.link($0) } ?? .null), + ]) + } + + // MARK: - Commit + + public static func commit( + did: Atproto.DID, + dataRoot: ContentIdentifier, + rev: String = "3lbwqrstuvwxy", + signedBy key: some RepoFixtureSigningKey + ) throws -> DAGCBORValue { + let unsigned = DAGCBORValue.map([ + ("data", .link(dataRoot)), + ("did", .string(did.rawValue)), + ("prev", .null), + ("rev", .string(rev)), + ("version", .integer(3)), + ]) + + let signature = try key.repoFixtureSignature(for: DAGCBOREncoder.encode(unsigned)) + guard case .map(let fields) = unsigned else { fatalError("unreachable") } + + return .map(fields + [(key: "sig", value: .bytes(signature))]) + } + + // MARK: - CAR + + public static func car(root: ContentIdentifier, blocks: [Block]) -> Data { + let header = DAGCBOREncoder.encode( + .map([ + ("roots", .array([.link(root)])), + ("version", .integer(1)), + ]) + ) + + var out = Data() + out.append(contentsOf: ContentIdentifier.varint(UInt64(header.count))) + out.append(header) + + for block in blocks { + var framed = block.cid.bytes + framed.append(block.bytes) + out.append(contentsOf: ContentIdentifier.varint(UInt64(framed.count))) + out.append(framed) + } + return out + } + + // MARK: - Identity + + ///Built by decoding JSON because `VerificationMethod`'s initialiser is + ///package-scoped to AtprotoTypes. No loss — this is the shape a document + ///actually arrives in, so the fixture exercises the real decode. + public static func document( + did: Atproto.DID, + key: some RepoFixturePublicKey, + fragment: String = "#atproto", + controller: String? = nil + ) throws -> Atproto.DIDDocument { + try document( + did: did, + methods: [ + ( + id: did.rawValue + fragment, + controller: controller ?? did.rawValue, + multibase: multibase(key) + ) + ] + ) + } + + public static func document( + did: Atproto.DID, + methods: [(id: String, controller: String, multibase: String)] + ) throws -> Atproto.DIDDocument { + let encoded = methods.map { + """ + {"id":"\($0.id)","type":"Multikey",\ + "controller":"\($0.controller)",\ + "publicKeyMultibase":"\($0.multibase)"} + """ + } + .joined(separator: ",") + + let json = """ + {"@context":[],"id":"\(did.rawValue)","alsoKnownAs":[],\ + "verificationMethod":[\(encoded)],"service":[]} + """ + return try JSONDecoder().decode( + Atproto.DIDDocument.self, + from: Data(json.utf8) + ) + } + + ///The multicodec prefix over the compressed point, multibase base58btc — + ///the form a DID document publishes, for whichever curve `key` names. + public static func multibase(_ key: some RepoFixturePublicKey) -> String { + var payload = Data(ContentIdentifier.varint(key.repoFixtureMulticodec)) + payload.append(key.repoFixtureCompressedRepresentation) + return "z" + BaseXEncoding.base58BTC(payload) + } + + // MARK: - Low-S + + ///swift-crypto does not normalise `s`, and atproto requires the low + ///variant, so fixtures fold it here rather than emitting signatures the + ///verifier is right to reject. + /// + ///No default `order` parameter: the default would have to name + ///`RepoSigningKey.p256Order`, a `package`-scoped constant, from this + ///public API's default-argument expression — which the compiler + ///evaluates at each call site, outside the package. This overload is the + ///substitute. + public static func lowS(_ signature: Data) -> Data { + lowS(signature, order: RepoSigningKey.p256Order) + } + + public static func lowS(_ signature: Data, order: [UInt8]) -> Data { + let r = Array(signature.prefix(32)) + let s = Array(signature.suffix(32)) + guard !RepoSigningKey.isLowS(s, order: order) else { + return signature + } + return Data(r + subtract(order, s)) + } + + ///Flips a signature to its high-S twin, for the malleability test. + public static func highS(_ signature: Data) -> Data { + highS(signature, order: RepoSigningKey.p256Order) + } + + public static func highS(_ signature: Data, order: [UInt8]) -> Data { + let r = Array(signature.prefix(32)) + let s = Array(signature.suffix(32)) + guard RepoSigningKey.isLowS(s, order: order) else { + return signature + } + return Data(r + subtract(order, s)) + } + + public static func subtract(_ lhs: [UInt8], _ rhs: [UInt8]) -> [UInt8] { + var out = [UInt8](repeating: 0, count: lhs.count) + var borrow = 0 + for index in stride(from: lhs.count - 1, through: 0, by: -1) { + let difference = Int(lhs[index]) - Int(rhs[index]) - borrow + if difference < 0 { + out[index] = UInt8(difference + 256) + borrow = 1 + } else { + out[index] = UInt8(difference) + borrow = 0 + } + } + return out + } +} + +///base58btc encode, for building the multibase key strings the fixtures +///publish. `BaseX` decodes in `AtprotoTypesVerify`; encoding here keeps the +///fixture from depending on the same code path it is meant to feed. +public enum BaseXEncoding { + static let alphabet = Array("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") + + public static func base58BTC(_ data: Data) -> String { + var digits: [Int] = [] + for byte in data { + var carry = Int(byte) + for index in digits.indices { + carry += digits[index] << 8 + digits[index] = carry % 58 + carry /= 58 + } + while carry > 0 { + digits.append(carry % 58) + carry /= 58 + } + } + + let leadingZeros = data.prefix { $0 == 0 }.count + return String( + repeating: "1", count: leadingZeros + ) + String(digits.reversed().map { alphabet[$0] }) + } +} diff --git a/Tests/AtprotoTypesVerifyTests/CARTests.swift b/Tests/AtprotoTypesVerifyTests/CARTests.swift new file mode 100644 index 0000000..6a248b4 --- /dev/null +++ b/Tests/AtprotoTypesVerifyTests/CARTests.swift @@ -0,0 +1,104 @@ +// +// CARTests.swift +// AtprotoTypesVerifyTests +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import AtprotoTypesVerifyMocks +import Foundation +import Testing + +@testable import AtprotoTypesVerify + +@Suite("CAR v1") +struct CARTests { + @Test("blocks round-trip through the framing") + func roundTrip() throws { + let first = try RepoFixture.block(.string("first")) + let second = try RepoFixture.block(.integer(42)) + let archive = try CARv1( + RepoFixture.car(root: first.cid, blocks: [first, second]) + ) + + #expect(archive.roots == [first.cid]) + #expect(archive.blockCount == 2) + #expect(try archive.block(first.cid) == first.bytes) + #expect(try archive.decoded(second.cid) == .integer(42)) + } + + ///The check the whole scheme depends on. A CAR is just a bag of bytes with + ///addresses attached, and the addresses are only meaningful because they + ///are recomputed here rather than believed. + @Test("a block that does not hash to its stated CID is rejected on load") + func rejectsMismatchedBlock() throws { + let real = try RepoFixture.block(.string("honest")) + let swapped = RepoFixture.Block( + cid: real.cid, + bytes: DAGCBOREncoder.encode(.string("tampered")) + ) + + #expect(throws: Atproto.Repo.ProofError.blockCIDMismatch(real.cid.string)) { + try CARv1(RepoFixture.car(root: real.cid, blocks: [swapped])) + } + } + + @Test("an unsupported CAR version is rejected") + func rejectsVersion() throws { + let block = try RepoFixture.block(.string("x")) + let header = DAGCBOREncoder.encode( + .map([ + ("roots", .array([.link(block.cid)])), + ("version", .integer(2)), + ]) + ) + var car = Data() + car.append(contentsOf: ContentIdentifier.varint(UInt64(header.count))) + car.append(header) + + #expect(throws: Atproto.Repo.ProofError.unsupportedCARVersion(2)) { + try CARv1(car) + } + } + + @Test("a CAR with no roots is rejected") + func rejectsNoRoots() throws { + let header = DAGCBOREncoder.encode( + .map([ + ("roots", .array([])), + ("version", .integer(1)), + ]) + ) + var car = Data() + car.append(contentsOf: ContentIdentifier.varint(UInt64(header.count))) + car.append(header) + + #expect(throws: Atproto.Repo.ProofError.noCARRoots) { + try CARv1(car) + } + } + + @Test("asking for a block the proof omits is an error, not an empty answer") + func rejectsMissingBlock() throws { + let present = try RepoFixture.block(.string("present")) + let absent = try RepoFixture.block(.string("absent")) + let archive = try CARv1( + RepoFixture.car(root: present.cid, blocks: [present]) + ) + + #expect(throws: Atproto.Repo.ProofError.missingBlock(absent.cid.string)) { + try archive.block(absent.cid) + } + } + + @Test("a truncated block frame is rejected") + func rejectsTruncation() throws { + let block = try RepoFixture.block(.string("whole")) + let car = RepoFixture.car(root: block.cid, blocks: [block]) + + #expect(throws: Atproto.Repo.ProofError.truncated) { + try CARv1(car.dropLast(3)) + } + } +} diff --git a/Tests/AtprotoTypesVerifyTests/ContentIdentifierTests.swift b/Tests/AtprotoTypesVerifyTests/ContentIdentifierTests.swift new file mode 100644 index 0000000..40b884b --- /dev/null +++ b/Tests/AtprotoTypesVerifyTests/ContentIdentifierTests.swift @@ -0,0 +1,119 @@ +// +// ContentIdentifierTests.swift +// AtprotoTypesVerifyTests +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import Foundation +import Testing + +@testable import AtprotoTypesVerify + +@Suite("CID") +struct ContentIdentifierTests { + ///Anchored on the SHA-256 of the empty string, which is a constant anyone + ///can check, rather than on a base32 string copied from our own output. + @Test("a raw CID over empty input has the known digest and binary prefix") + func emptyDigest() throws { + let cid = try ContentIdentifier.compute(codec: .raw, block: Data()) + + #expect( + Data(cid.digest).hexString + == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ) + //CIDv1, raw codec, sha2-256, 32 bytes + #expect(Array(cid.bytes.prefix(4)) == [0x01, 0x55, 0x12, 0x20]) + #expect(cid.bytes.count == 36) + } + + @Test("the binary form round-trips") + func binaryRoundTrip() throws { + let cid = try ContentIdentifier.compute( + codec: .dagCBOR, + block: Data("some block".utf8) + ) + #expect(try ContentIdentifier(bytes: cid.bytes) == cid) + } + + ///The bridge back to AtprotoTypes' opaque CID has to agree with what that + ///type would have parsed, or a proven CID and a fetched one would not + ///compare equal. + @Test("the string form matches what Atproto.CID parses") + func atprotoBridge() throws { + let cid = try ContentIdentifier.compute( + codec: .dagCBOR, + block: Data("some block".utf8) + ) + #expect(cid.string.hasPrefix("bafyrei")) + #expect(cid.atprotoCID.string == cid.string) + #expect(try Atproto.CID(string: cid.string).string == cid.string) + } + + @Test("matches() recomputes rather than trusting the link") + func matchesRecomputes() throws { + let block = Data("payload".utf8) + let cid = try ContentIdentifier.compute(codec: .dagCBOR, block: block) + + #expect(cid.matches(block: block)) + #expect(!cid.matches(block: Data("payloae".utf8))) + } + + @Test("CIDv0 is rejected rather than quietly reinterpreted") + func rejectsV0() { + //a bare sha2-256 multihash, which is what a v0 CID is + var v0 = Data([0x12, 0x20]) + v0.append(Data(repeating: 0xAB, count: 32)) + + #expect(throws: Atproto.Repo.ProofError.unsupportedCIDVersion(0x12)) { + try ContentIdentifier(bytes: v0) + } + } + + @Test("an unsupported multihash is rejected") + func rejectsOtherHash() { + //CIDv1, dag-cbor, blake2b-256 (0xb220) + var cid = Data([0x01, 0x71, 0xA0, 0xE4, 0x02]) + cid.append(Data(repeating: 0x11, count: 32)) + + #expect(throws: (any Error).self) { + try ContentIdentifier(bytes: cid) + } + } + + @Test("trailing bytes after a CID are rejected") + func rejectsTrailing() throws { + let cid = try ContentIdentifier.compute(codec: .dagCBOR, block: Data()) + var padded = cid.bytes + padded.append(0x00) + + #expect(throws: Atproto.Repo.ProofError.trailingBytes) { + try ContentIdentifier(bytes: padded) + } + } + + @Test( + "non-minimal varints are rejected", + arguments: [ + //version 1 written as a two-byte varint + "8100711220", + //codec dag-cbor written as a two-byte varint + "01f1001220", + ] + ) + func rejectsNonMinimalVarint(prefix: String) { + var bytes = Data(hex: prefix) + bytes.append(Data(repeating: 0x00, count: 32)) + + #expect(throws: (any Error).self) { + try ContentIdentifier(bytes: bytes) + } + } +} + +extension Data { + var hexString: String { + map { String(format: "%02x", $0) }.joined() + } +} diff --git a/Tests/AtprotoTypesVerifyTests/DAGCBORTests.swift b/Tests/AtprotoTypesVerifyTests/DAGCBORTests.swift new file mode 100644 index 0000000..d0ce4be --- /dev/null +++ b/Tests/AtprotoTypesVerifyTests/DAGCBORTests.swift @@ -0,0 +1,197 @@ +// +// DAGCBORTests.swift +// AtprotoTypesVerifyTests +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import Foundation +import Testing + +@testable import AtprotoTypesVerify + +@Suite("DAG-CBOR") +struct DAGCBORTests { + ///RFC 8949 Appendix A. Pinned against the spec's own table rather than + ///against our encoder's output, so the two can't drift together. + @Test( + "the RFC 8949 example encodings round-trip", + arguments: [ + (DAGCBORValue.integer(0), "00"), + (.integer(1), "01"), + (.integer(10), "0a"), + (.integer(23), "17"), + (.integer(24), "1818"), + (.integer(100), "1864"), + (.integer(1000), "1903e8"), + (.integer(1_000_000), "1a000f4240"), + (.integer(-1), "20"), + (.integer(-10), "29"), + (.integer(-100), "3863"), + (.integer(-1000), "3903e7"), + (.bool(false), "f4"), + (.bool(true), "f5"), + (.null, "f6"), + (.string(""), "60"), + (.string("a"), "6161"), + (.string("IETF"), "6449455446"), + (.array([]), "80"), + (.array([.integer(1), .integer(2), .integer(3)]), "83010203"), + (.map([]), "a0"), + ] + ) + func rfcVectors(value: DAGCBORValue, hex: String) throws { + let bytes = Data(hex: hex) + #expect(DAGCBOREncoder.encode(value) == bytes) + #expect(try DAGCBORDecoder.decode(bytes) == value) + } + + ///The property the commit signature rests on: rebuilding the preimage from + ///a decoded value has to reproduce the original bytes exactly, or the + ///digest we verify is not the digest that was signed. + @Test("decode then encode reproduces the original bytes") + func roundTrip() throws { + let value = DAGCBORValue.map([ + ("did", .string("did:plc:example")), + ("rev", .string("3lbw")), + ("data", .link(try ContentIdentifier.compute(codec: .dagCBOR, block: Data()))), + ("prev", .null), + ("nested", .array([.integer(-1), .bytes(Data([0, 1, 2])), .bool(true)])), + ("version", .integer(3)), + ]) + + let encoded = DAGCBOREncoder.encode(value) + #expect(DAGCBOREncoder.encode(try DAGCBORDecoder.decode(encoded)) == encoded) + } + + @Test("maps encode in length-first canonical order regardless of input order") + func canonicalOrdering() throws { + //keys deliberately supplied out of order; "b" must precede "aa" + let value = DAGCBORValue.map([ + ("version", .integer(1)), + ("aa", .integer(2)), + ("b", .integer(3)), + ]) + + let decoded = try DAGCBORDecoder.decode(DAGCBOREncoder.encode(value)) + guard case .map(let entries) = decoded else { + Issue.record("expected a map") + return + } + #expect(entries.map(\.key) == ["b", "aa", "version"]) + } + + @Test("a CID link survives the round trip") + func linkRoundTrip() throws { + let cid = try ContentIdentifier.compute( + codec: .dagCBOR, + block: Data("hello".utf8) + ) + let encoded = DAGCBOREncoder.encode(.link(cid)) + //tag 42, then a byte string opening with the identity multibase prefix + #expect(encoded.first == 0xD8) + #expect(encoded[1] == 42) + #expect(try DAGCBORDecoder.decode(encoded) == .link(cid)) + } + + // MARK: - Strictness + + @Test( + "non-DAG-CBOR encodings are rejected", + arguments: [ + //indefinite-length array, byte string, text string, map + ("9f01ff", "indefinite array"), + ("5f42010243030405ff", "indefinite bytes"), + ("7f61616161ff", "indefinite text"), + ("bf616101616202ff", "indefinite map"), + //non-minimal integer: 1 written in a two-byte argument + ("1801", "non-minimal one-byte argument"), + ("190001", "non-minimal two-byte argument"), + //reserved additional info + ("1c", "reserved additional info 28"), + //half and single precision floats + ("f93c00", "float16"), + ("fa47c35000", "float32"), + //an unsupported tag + ("c10a", "tag 1"), + ] + ) + func rejectsNonCanonical(hex: String, label: String) { + #expect(throws: (any Error).self, "\(label) should not decode") { + try DAGCBORDecoder.decode(Data(hex: hex)) + } + } + + @Test("integer map keys are rejected") + func rejectsIntegerKeys() { + //{1: 2} + #expect(throws: Atproto.Repo.ProofError.nonStringMapKey) { + try DAGCBORDecoder.decode(Data(hex: "a10102")) + } + } + + @Test("duplicate map keys are rejected") + func rejectsDuplicateKeys() { + //{"a": 1, "a": 2} + #expect(throws: Atproto.Repo.ProofError.duplicateMapKey("a")) { + try DAGCBORDecoder.decode(Data(hex: "a2616101616102")) + } + } + + @Test("out-of-order map keys are rejected") + func rejectsUnorderedKeys() { + //{"b": 1, "a": 2} — canonical order puts "a" first + #expect(throws: Atproto.Repo.ProofError.unorderedMapKeys("b", "a")) { + try DAGCBORDecoder.decode(Data(hex: "a2616201616102")) + } + //{"aa": 1, "b": 2} — length-first, so the one-byte key sorts first + #expect(throws: Atproto.Repo.ProofError.unorderedMapKeys("aa", "b")) { + try DAGCBORDecoder.decode(Data(hex: "a262616101616202")) + } + } + + @Test("trailing bytes after a complete value are rejected") + func rejectsTrailingBytes() { + #expect(throws: Atproto.Repo.ProofError.trailingBytes) { + try DAGCBORDecoder.decode(Data(hex: "0101")) + } + } + + @Test("a truncated value is rejected rather than silently short") + func rejectsTruncation() { + //declares a four-byte string, supplies two + #expect(throws: Atproto.Repo.ProofError.truncated) { + try DAGCBORDecoder.decode(Data(hex: "646162")) + } + } + + @Test("a tag 42 payload without the identity multibase prefix is rejected") + func rejectsBadLink() { + //tag 42 over a byte string that starts with 0x01 rather than 0x00 + #expect(throws: Atproto.Repo.ProofError.badCIDLink) { + try DAGCBORDecoder.decode(Data(hex: "d82a4401550120")) + } + } + + @Test("invalid UTF-8 in a text string is rejected") + func rejectsInvalidUTF8() { + #expect(throws: Atproto.Repo.ProofError.invalidUTF8) { + try DAGCBORDecoder.decode(Data(hex: "62c328")) + } + } +} + +extension Data { + init(hex: String) { + var bytes: [UInt8] = [] + var index = hex.startIndex + while index < hex.endIndex, + let next = hex.index(index, offsetBy: 2, limitedBy: hex.endIndex) + { + bytes.append(UInt8(hex[index.. ContentIdentifier { + let block = try RepoFixture.block(.string("record-" + name)) + blocks.append(block) + return block.cid + } + + let a = try leaf("a") + let b = try leaf("b") + let c = try leaf("c") + + let left = try RepoFixture.block( + RepoFixture.node(entries: [(key: Self.key("a"), value: a)]) + ) + let right = try RepoFixture.block( + RepoFixture.node(entries: [(key: Self.key("c"), value: c)]) + ) + let root = try RepoFixture.block( + RepoFixture.node( + entries: [(key: Self.key("b"), value: b)], + left: left.cid, + subtrees: [Self.key("b"): right.cid] + ) + ) + blocks.append(contentsOf: [left, right, root]) + + self.archive = try CARv1( + RepoFixture.car(root: root.cid, blocks: blocks) + ) + self.root = root.cid + self.values = [ + Self.key("a"): a, Self.key("b"): b, Self.key("c"): c, + ] + } + + static func key(_ rkey: String) -> String { + MerkleSearchTreeTests.collection + "/" + rkey + } + } + + @Test("every key in the tree is found, at the root and in both subtrees") + func findsEachKey() throws { + let tree = try Tree() + + for (key, expected) in tree.values { + #expect( + try MerkleSearchTree.find(key: key, root: tree.root, in: tree.archive) + == expected, + "looking up \(key)" + ) + } + } + + @Test( + "a key that is not in the tree is refused rather than approximated", + arguments: ["ab", "d", "", "a0"] + ) + func rejectsAbsentKey(rkey: String) throws { + let tree = try Tree() + + #expect(throws: Atproto.Repo.ProofError.recordNotInTree) { + try MerkleSearchTree.find( + key: Tree.key(rkey), + root: tree.root, + in: tree.archive + ) + } + } + + ///Keys are stored with the shared prefix of the preceding key elided, so a + ///node holding several near-identical rkeys is where reconstruction goes + ///wrong if `p` is mishandled. + @Test("prefix-compressed keys reconstruct correctly") + func prefixCompression() throws { + let rkeys = ["3lbwaaaa", "3lbwaaab", "3lbwaabb", "3lbxcccc", "zzz"] + var blocks: [RepoFixture.Block] = [] + var entries: [(key: String, value: ContentIdentifier)] = [] + + for rkey in rkeys { + let block = try RepoFixture.block(.string(rkey)) + blocks.append(block) + entries.append((key: Tree.key(rkey), value: block.cid)) + } + + let node = try RepoFixture.block(RepoFixture.node(entries: entries)) + blocks.append(node) + let archive = try CARv1(RepoFixture.car(root: node.cid, blocks: blocks)) + + //the fixture really is eliding prefixes, or this proves nothing + let decoded = try archive.decoded(node.cid) + let prefixes = try #require(decoded["e"]?.arrayValue).map { + $0["p"]?.integerValue ?? -1 + } + #expect(prefixes.contains { $0 > 0 }) + + for (index, rkey) in rkeys.enumerated() { + #expect( + try MerkleSearchTree.find( + key: Tree.key(rkey), + root: node.cid, + in: archive + ) == entries[index].value, + "looking up \(rkey)" + ) + } + } + + @Test("a node whose keys do not ascend is malformed") + func rejectsUnorderedEntries() throws { + let value = try RepoFixture.block(.string("v")) + //built by hand: two entries with p=0 whose keys descend + let node = try RepoFixture.block( + .map([ + ( + "e", + .array([ + .map([ + ("k", .bytes(Data("b".utf8))), + ("p", .integer(0)), + ("t", .null), + ("v", .link(value.cid)), + ]), + .map([ + ("k", .bytes(Data("a".utf8))), + ("p", .integer(0)), + ("t", .null), + ("v", .link(value.cid)), + ]), + ]) + ), + ("l", .null), + ]) + ) + let archive = try CARv1( + RepoFixture.car(root: node.cid, blocks: [value, node]) + ) + + #expect(throws: Atproto.Repo.ProofError.mstNodeMalformed) { + try MerkleSearchTree.find(key: "c", root: node.cid, in: archive) + } + } + + @Test("an entry claiming a longer shared prefix than exists is rejected") + func rejectsPrefixOutOfRange() throws { + let value = try RepoFixture.block(.string("v")) + let node = try RepoFixture.block( + .map([ + ( + "e", + .array([ + .map([ + ("k", .bytes(Data("a".utf8))), + //nothing precedes this entry, so any prefix is a lie + ("p", .integer(4)), + ("t", .null), + ("v", .link(value.cid)), + ]) + ]) + ), + ("l", .null), + ]) + ) + let archive = try CARv1( + RepoFixture.car(root: node.cid, blocks: [value, node]) + ) + + #expect(throws: Atproto.Repo.ProofError.mstPrefixOutOfRange) { + try MerkleSearchTree.find(key: "a", root: node.cid, in: archive) + } + } + + ///A truncated proof — the server names a subtree it does not supply. This is + ///the realistic shape of an incomplete proof, and it must fail rather than + ///read as "not present". + @Test("a subtree the CAR omits is a missing block, not an absent key") + func rejectsOmittedSubtree() throws { + let value = try RepoFixture.block(.string("v")) + let orphan = try RepoFixture.block(.string("never included")) + let node = try RepoFixture.block( + RepoFixture.node( + entries: [(key: Tree.key("m"), value: value.cid)], + left: orphan.cid + ) + ) + let archive = try CARv1( + RepoFixture.car(root: node.cid, blocks: [value, node]) + ) + + #expect(throws: Atproto.Repo.ProofError.missingBlock(orphan.cid.string)) { + try MerkleSearchTree.find( + key: Tree.key("a"), + root: node.cid, + in: archive + ) + } + } +} diff --git a/Tests/AtprotoTypesVerifyTests/RepoProofVerifierTests.swift b/Tests/AtprotoTypesVerifyTests/RepoProofVerifierTests.swift new file mode 100644 index 0000000..95761f2 --- /dev/null +++ b/Tests/AtprotoTypesVerifyTests/RepoProofVerifierTests.swift @@ -0,0 +1,426 @@ +// +// RepoProofVerifierTests.swift +// AtprotoTypesVerifyTests +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import AtprotoTypesVerifyMocks +import Crypto +import Foundation +import Testing + +@testable import AtprotoTypesVerify + +@Suite("Repo proof") +struct RepoProofVerifierTests { + static let path = Atproto.Repo.RecordPath( + collection: .init(string: "com.germnetwork.declaration"), + rkey: "self" + ) + + ///One repo, assembled part by part so a test can replace exactly one piece + ///and watch the proof fail for that reason and no other. + struct Scenario { + let did: Atproto.DID + let signing: P256.Signing.PrivateKey + let record: DAGCBORValue + + init( + did: Atproto.DID = RepoFixture.did, + signing: P256.Signing.PrivateKey = P256.Signing.PrivateKey(), + anchorKey: Data = Data(repeating: 0xA1, count: 32) + ) { + self.did = did + self.signing = signing + self.record = RepoFixture.declaration(currentKey: anchorKey) + } + + func car( + commitDID: Atproto.DID? = nil, + signedBy: P256.Signing.PrivateKey? = nil, + mutateSignature: ((Data) -> Data)? = nil + ) throws -> Data { + let recordBlock = try RepoFixture.block(record) + let node = try RepoFixture.block( + RepoFixture.node( + entries: [ + ( + key: RepoProofVerifierTests.path.mstKey, + value: recordBlock.cid + ) + ] + ) + ) + var commit = try RepoFixture.commit( + did: commitDID ?? did, + dataRoot: node.cid, + signedBy: signedBy ?? signing + ) + if let mutateSignature, let existing = commit["sig"]?.bytesValue { + commit = commit.removing(key: "sig") + guard case .map(let fields) = commit else { fatalError("unreachable") } + commit = .map( + fields + [(key: "sig", value: .bytes(mutateSignature(existing)))] + ) + } + let commitBlock = try RepoFixture.block(commit) + + return RepoFixture.car( + root: commitBlock.cid, + blocks: [recordBlock, node, commitBlock] + ) + } + + func document( + key: P256.Signing.PublicKey? = nil + ) throws -> Atproto.DIDDocument { + try RepoFixture.document(did: did, key: key ?? signing.publicKey) + } + } + + // MARK: - The path that should work + + @Test("a record in the repo verifies against the DID document's signing key") + func verifiesGenuineRecord() throws { + let scenario = Scenario() + let proof = try Atproto.Repo.Verifier().verifyRecordProof( + car: try scenario.car(), + did: scenario.did, + path: Self.path, + document: try scenario.document() + ) + + #expect(proof.did == scenario.did) + #expect(proof.path == Self.path) + #expect(proof.block == DAGCBOREncoder.encode(scenario.record)) + #expect(proof.rev == "3lbwqrstuvwxy") + //the returned CID is the one recomputed from the block + #expect( + try proof.cid.string + == ContentIdentifier.compute( + codec: .dagCBOR, + block: proof.block + ).string + ) + } + + // MARK: - The forgery the current code accepts + + ///GER-2254's whole point. An internal-consistency check like + ///`verified(for:)` checks a declaration against its own `currentKey`, so an + ///attacker who mints a well-formed declaration with *their* anchor key and + ///binds it to a victim's DID passes that check — the record is internally + ///consistent, it just isn't the victim's. Provenance is what a JSON + ///`getRecord` throws away, and what these two cases restore. + @Test("a declaration signed by the wrong key is refused") + func rejectsForgedSigner() throws { + let victim = Scenario() + let attacker = P256.Signing.PrivateKey() + + //attacker's own declaration, in a repo they signed, claiming the + //victim's DID in the commit + let forged = Scenario( + did: victim.did, + signing: attacker, + anchorKey: Data(repeating: 0xEE, count: 32) + ) + + #expect(throws: Atproto.Repo.ProofError.signatureDidNotVerify) { + try Atproto.Repo.Verifier().verifyRecordProof( + car: try forged.car(), + did: victim.did, + path: Self.path, + //the victim's real DID document is the authority + document: try victim.document() + ) + } + } + + @Test("a genuine repo belonging to someone else is refused") + func rejectsSubstitutedRepo() throws { + let victim = Scenario() + let attacker = Scenario( + did: RepoFixture.attacker, + anchorKey: Data(repeating: 0xEE, count: 32) + ) + + //the attacker's repo is perfectly valid — it is just not the victim's + #expect( + throws: Atproto.Repo.ProofError.commitDIDMismatch( + expected: victim.did.rawValue, + found: RepoFixture.attacker.rawValue + ) + ) { + try Atproto.Repo.Verifier().verifyRecordProof( + car: try attacker.car(), + did: victim.did, + path: Self.path, + document: try victim.document() + ) + } + } + + @Test("swapping the record bytes breaks the content address") + func rejectsTamperedRecord() throws { + let scenario = Scenario() + var car = try scenario.car() + + //flip a byte inside the record block; the CAR's own CID check catches it + let anchorByte = try #require(car.firstRange(of: Data([0xA1, 0xA1, 0xA1]))) + car[anchorByte.lowerBound] = 0xA2 + + #expect(throws: (any Error).self) { + try Atproto.Repo.Verifier().verifyRecordProof( + car: car, + did: scenario.did, + path: Self.path, + document: try scenario.document() + ) + } + } + + @Test("a document naming a different key refuses the proof") + func rejectsWrongDocumentKey() throws { + let scenario = Scenario() + let unrelated = P256.Signing.PrivateKey() + + #expect(throws: Atproto.Repo.ProofError.signatureDidNotVerify) { + try Atproto.Repo.Verifier().verifyRecordProof( + car: try scenario.car(), + did: scenario.did, + path: Self.path, + document: try scenario.document(key: unrelated.publicKey) + ) + } + } + + @Test("asking for a path the repo does not hold is refused") + func rejectsAbsentPath() throws { + let scenario = Scenario() + + #expect(throws: Atproto.Repo.ProofError.recordNotInTree) { + try Atproto.Repo.Verifier().verifyRecordProof( + car: try scenario.car(), + did: scenario.did, + path: .init( + collection: .init(string: "com.germnetwork.declaration"), + rkey: "notself" + ), + document: try scenario.document() + ) + } + } + + // MARK: - Signature discipline + + ///Without a low-S rule the same commit has two valid signatures, so two + ///distinct byte strings both "prove" it and a proof stops being a single + ///thing you can point at. + @Test("the high-S twin of a valid signature is refused") + func rejectsMalleatedSignature() throws { + let scenario = Scenario() + + #expect(throws: Atproto.Repo.ProofError.nonCanonicalSignature) { + try Atproto.Repo.Verifier().verifyRecordProof( + car: try scenario.car(mutateSignature: RepoFixture.highS), + did: scenario.did, + path: Self.path, + document: try scenario.document() + ) + } + } + + @Test("a signature of the wrong length is refused") + func rejectsShortSignature() throws { + let scenario = Scenario() + + #expect(throws: Atproto.Repo.ProofError.badSignatureLength(63)) { + try Atproto.Repo.Verifier().verifyRecordProof( + car: try scenario.car(mutateSignature: { $0.dropLast() }), + did: scenario.did, + path: Self.path, + document: try scenario.document() + ) + } + } + + // MARK: - secp256k1 + + ///Mirrors `Scenario`, kept separate rather than adding a type parameter to + ///it: every other test in this file is p256-only and gains nothing from + ///carrying a curve around, and `Scenario`'s default-argument initialiser + ///(`Key = Key()`) doesn't generalize to an arbitrary `RepoFixtureSigningKey`. + struct Secp256k1Scenario { + let did: Atproto.DID + let signer: Secp256k1TestSigner + let record: DAGCBORValue + + init( + did: Atproto.DID = RepoFixture.did, + signer: Secp256k1TestSigner = Secp256k1TestSigner(), + anchorKey: Data = Data(repeating: 0xA1, count: 32) + ) { + self.did = did + self.signer = signer + self.record = RepoFixture.declaration(currentKey: anchorKey) + } + + func car() throws -> Data { + let recordBlock = try RepoFixture.block(record) + let node = try RepoFixture.block( + RepoFixture.node( + entries: [ + (key: RepoProofVerifierTests.path.mstKey, value: recordBlock.cid) + ] + ) + ) + let commit = try RepoFixture.commit(did: did, dataRoot: node.cid, signedBy: signer) + let commitBlock = try RepoFixture.block(commit) + return RepoFixture.car(root: commitBlock.cid, blocks: [recordBlock, node, commitBlock]) + } + + func document() throws -> Atproto.DIDDocument { + try RepoFixture.document(did: did, key: signer.publicKey) + } + } + + ///Most real Bluesky accounts sign with this curve, which is what made the + ///from-scratch verify-only port (Q-PMR-23) worth doing: a genuine k256 + ///commit signature now verifies rather than failing closed. + @Test("a genuine secp256k1 record verifies against the DID document's signing key") + func verifiesGenuineSecp256k1Record() throws { + let scenario = Secp256k1Scenario() + let proof = try Atproto.Repo.Verifier().verifyRecordProof( + car: try scenario.car(), + did: scenario.did, + path: Self.path, + document: try scenario.document() + ) + + #expect(proof.did == scenario.did) + #expect(proof.block == DAGCBOREncoder.encode(scenario.record)) + } + + @Test("a secp256k1 document naming a different key refuses the proof") + func refusesWrongSecp256k1DocumentKey() throws { + let scenario = Secp256k1Scenario() + let unrelated = Secp256k1TestSigner() + let document = try RepoFixture.document(did: scenario.did, key: unrelated.publicKey) + + #expect(throws: Atproto.Repo.ProofError.signatureDidNotVerify) { + try Atproto.Repo.Verifier().verifyRecordProof( + car: try scenario.car(), + did: scenario.did, + path: Self.path, + document: document + ) + } + } + + // MARK: - Identity plumbing + + @Test("a document with no atproto verification method is refused") + func refusesDocumentWithoutKey() throws { + let scenario = Scenario() + let document = try RepoFixture.document(did: scenario.did, methods: []) + + #expect(throws: Atproto.Repo.ProofError.noAtprotoSigningKey) { + try Atproto.Repo.Verifier().verifyRecordProof( + car: try scenario.car(), + did: scenario.did, + path: Self.path, + document: document + ) + } + } + + ///The gap this closes on the shipped fetch path: today's `resolveMiniDoc` + ///adapter builds a document with `verificationMethod: []`, so every proof + ///would stop here until that key is carried through. + @Test("a document with only a non-atproto method is refused") + func refusesWrongFragment() throws { + let scenario = Scenario() + let document = try RepoFixture.document( + did: scenario.did, + key: scenario.signing.publicKey, + fragment: "#somethingElse" + ) + + #expect(throws: Atproto.Repo.ProofError.noAtprotoSigningKey) { + try Atproto.Repo.Verifier().verifyRecordProof( + car: try scenario.car(), + did: scenario.did, + path: Self.path, + document: document + ) + } + } + + @Test("a signing key controlled by another DID is refused") + func refusesForeignController() throws { + let scenario = Scenario() + let document = try RepoFixture.document( + did: scenario.did, + key: scenario.signing.publicKey, + controller: RepoFixture.attacker.rawValue + ) + + #expect(throws: Atproto.Repo.ProofError.signingKeyControllerMismatch) { + try Atproto.Repo.Verifier().verifyRecordProof( + car: try scenario.car(), + did: scenario.did, + path: Self.path, + document: document + ) + } + } + + ///An empty `controller` means "self-controlled" (the DID document convention + ///for an absent field), not "skip the check" — otherwise a caller-side bug + ///pairing the wrong document with a `did` would pass silently whenever the + ///method simply omits the field, which is the common shape for real + ///documents. The document here is genuinely the attacker's own — correctly + ///self-controlled, with no explicit `controller` string at all — and must + ///still be refused when checked against the victim's `did`. + @Test("an empty controller falls back to the document's own id, not a skip") + func refusesEmptyControllerForWrongDID() throws { + let scenario = Scenario() + let document = try RepoFixture.document( + did: RepoFixture.attacker, + methods: [ + ( + id: RepoFixture.attacker.rawValue + "#atproto", + controller: "", + multibase: RepoFixture.multibase(scenario.signing.publicKey) + ) + ] + ) + + #expect(throws: Atproto.Repo.ProofError.signingKeyControllerMismatch) { + try Atproto.Repo.Verifier().verifyRecordProof( + car: try scenario.car(), + did: scenario.did, + path: Self.path, + document: document + ) + } + } + + // MARK: - The space-constrained conformer + + @Test("the non-verifying conformer refuses rather than passing bytes through") + func nonVerifyingConformerRefuses() throws { + let scenario = Scenario() + + #expect(throws: Atproto.Repo.Errors.verificationUnavailable) { + try Atproto.Repo.ProofUnavailable().verifyRecordProof( + car: try scenario.car(), + did: scenario.did, + path: Self.path, + document: try scenario.document() + ) + } + } +} diff --git a/Tests/AtprotoTypesVerifyTests/Resources/wycheproof/LICENSE b/Tests/AtprotoTypesVerifyTests/Resources/wycheproof/LICENSE new file mode 100644 index 0000000..7a4a3ea --- /dev/null +++ b/Tests/AtprotoTypesVerifyTests/Resources/wycheproof/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/Tests/AtprotoTypesVerifyTests/Resources/wycheproof/ecdsa_secp256k1_sha256_p1363_test.json b/Tests/AtprotoTypesVerifyTests/Resources/wycheproof/ecdsa_secp256k1_sha256_p1363_test.json new file mode 100644 index 0000000..3c59b14 --- /dev/null +++ b/Tests/AtprotoTypesVerifyTests/Resources/wycheproof/ecdsa_secp256k1_sha256_p1363_test.json @@ -0,0 +1,5500 @@ +{ + "algorithm": "ECDSA", + "schema": "ecdsa_p1363_verify_schema_v1.json", + "numberOfTests": 252, + "header": [ + "Test vectors of type EcdsaVerify are meant for the verification", + "of IEEE P1363 encoded ECDSA signatures." + ], + "notes": { + "ArithmeticError": { + "bugType": "EDGE_CASE", + "description": "Some implementations of ECDSA have arithmetic errors that occur when intermediate results have extreme values. This test vector has been constructed to test such occurrences.", + "cves": [ + "CVE-2017-18146" + ] + }, + "EdgeCasePublicKey": { + "bugType": "EDGE_CASE", + "description": "The test vector uses a special case public key. " + }, + "EdgeCaseShamirMultiplication": { + "bugType": "EDGE_CASE", + "description": "Shamir proposed a fast method for computing the sum of two scalar multiplications efficiently. This test vector has been constructed so that an intermediate result is the point at infinity if Shamir's method is used." + }, + "IntegerOverflow": { + "bugType": "CAN_OF_WORMS", + "description": "The test vector contains an r and s that has been modified, so that the original value is restored if the implementation ignores the most significant bits.", + "effect": "Without further analysis it is unclear if the modification can be used to forge signatures." + }, + "InvalidSignature": { + "bugType": "AUTH_BYPASS", + "description": "The signature contains special case values such as r=0 and s=0. Buggy implementations may accept such values, if the implementation does not check boundaries and computes s^(-1) == 0.", + "effect": "Accepting such signatures can have the effect that an adversary can forge signatures without even knowing the message to sign.", + "cves": [ + "CVE-2022-21449", + "CVE-2021-43572", + "CVE-2022-24884" + ] + }, + "ModifiedInteger": { + "bugType": "CAN_OF_WORMS", + "description": "The test vector contains an r and s that has been modified. The goal is to check for arithmetic errors.", + "effect": "Without further analysis it is unclear if the modification can be used to forge signatures." + }, + "ModularInverse": { + "bugType": "EDGE_CASE", + "description": "The test vectors contains a signature where computing the modular inverse of s hits an edge case.", + "effect": "While the signature in this test vector is constructed and similar cases are unlikely to occur, it is important to determine if the underlying arithmetic error can be used to forge signatures.", + "cves": [ + "CVE-2019-0865" + ] + }, + "PointDuplication": { + "bugType": "EDGE_CASE", + "description": "Some implementations of ECDSA do not handle duplication and points at infinity correctly. This is a test vector that has been specially crafted to check for such an omission.", + "cves": [ + "2020-12607", + "CVE-2015-2730" + ] + }, + "RangeCheck": { + "bugType": "CAN_OF_WORMS", + "description": "The test vector contains an r and s that has been modified. By adding or subtracting the order of the group (or other values) the test vector checks whether signature verification verifies the range of r and s.", + "effect": "Without further analysis it is unclear if the modification can be used to forge signatures." + }, + "SignatureSize": { + "bugType": "LEGACY", + "description": "This test vector contains valid values for r and s. But the values are encoded using a smaller number of bytes. The size of an IEEE P1363 encoded signature should always be twice the number of bytes of the size of the order. Some libraries accept signatures with less bytes. To our knowledge no standard (i.e., IEEE P1363 or RFC 7515) requires any explicit checks of the signature size during signature verification." + }, + "SmallRandS": { + "bugType": "EDGE_CASE", + "description": "The test vectors contains a signature where both r and s are small integers. Some libraries cannot verify such signatures.", + "effect": "While the signature in this test vector is constructed and similar cases are unlikely to occur, it is important to determine if the underlying arithmetic error can be used to forge signatures.", + "cves": [ + "2020-13895" + ] + }, + "SpecialCaseHash": { + "bugType": "EDGE_CASE", + "description": "The test vector contains a signature where the hash of the message is a special case, e.g., contains a long run of 0 or 1 bits." + }, + "ValidSignature": { + "bugType": "BASIC", + "description": "The test vector contains a valid signature that was generated pseudorandomly. Such signatures should not fail to verify unless some of the parameters (e.g. curve or hash function) are not supported." + } + }, + "testGroups": [ + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9", + "wx": "00b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6f", + "wy": "00f0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEuDj/ROW8F3vyEYnQdmCC/J2EMiaIf8l2\nA3EQC37iCm/wyddb+6ezGmvKGXRJbutW3jVwcZVdg8Sxutqgshgy6Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 1, + "comment": "signature malleability", + "flags": [ + "ValidSignature" + ], + "msg": "313233343030", + "sig": "813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365900e75ad233fcc908509dbff5922647db37c21f4afd3203ae8dc4ae7794b0f87", + "result": "valid" + }, + { + "tcId": 2, + "comment": "replaced r by r + n", + "flags": [ + "RangeCheck" + ], + "msg": "313233343030", + "sig": "01813ef79ccefa9a56f7ba805f0e478583b90deabca4b05c4574e49b5899b964a6006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result": "invalid" + }, + { + "tcId": 3, + "comment": "replaced r by r + 256 * n", + "flags": [ + "RangeCheck" + ], + "msg": "313233343030", + "sig": "0100813ef79ccefa9a56f7ba805f0e47843fad3bf4853e07f7c98770c99bffc4646500006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result": "invalid" + }, + { + "tcId": 4, + "comment": "replaced r by n - r", + "flags": [ + "ModifiedInteger" + ], + "msg": "313233343030", + "sig": "7ec10863310565a908457fa0f1b87a79bc4fcf10b9e0e4320ac021c106b31ddc6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result": "invalid" + }, + { + "tcId": 5, + "comment": "replaced r by r + 2**256", + "flags": [ + "IntegerOverflow" + ], + "msg": "313233343030", + "sig": "01813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result": "invalid" + }, + { + "tcId": 6, + "comment": "replaced r by r + 2**320", + "flags": [ + "IntegerOverflow" + ], + "msg": "313233343030", + "sig": "010000000000000000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650000000000000000006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result": "invalid" + }, + { + "tcId": 7, + "comment": "replaced s by s + n", + "flags": [ + "RangeCheck" + ], + "msg": "313233343030", + "sig": "016ff18a52dcc0336f7af62400a6dd9b7fc1e197d8aebe203c96c87232272172fb006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result": "invalid" + }, + { + "tcId": 8, + "comment": "replaced s by s + 256 * n", + "flags": [ + "RangeCheck" + ], + "msg": "313233343030", + "sig": "01006ff18a52dcc0336f7af62400a6dd9a3bb60fa1a14815bbc0a954a0758d2c72ba00006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result": "invalid" + }, + { + "tcId": 9, + "comment": "replaced s by s + 2**256", + "flags": [ + "IntegerOverflow" + ], + "msg": "313233343030", + "sig": "016ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result": "invalid" + }, + { + "tcId": 10, + "comment": "replaced s by s + 2**320", + "flags": [ + "IntegerOverflow" + ], + "msg": "313233343030", + "sig": "0100000000000000006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000000000000000006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result": "invalid" + }, + { + "tcId": 11, + "comment": "Signature with special case values r=0 and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 12, + "comment": "Signature with special case values r=0 and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", + "result": "invalid" + }, + { + "tcId": 13, + "comment": "Signature with special case values r=0 and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result": "invalid" + }, + { + "tcId": 14, + "comment": "Signature with special case values r=0 and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result": "invalid" + }, + { + "tcId": 15, + "comment": "Signature with special case values r=0 and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result": "invalid" + }, + { + "tcId": 16, + "comment": "Signature with special case values r=0 and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result": "invalid" + }, + { + "tcId": 17, + "comment": "Signature with special case values r=0 and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result": "invalid" + }, + { + "tcId": 18, + "comment": "Signature with special case values r=1 and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 19, + "comment": "Signature with special case values r=1 and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001", + "result": "invalid" + }, + { + "tcId": 20, + "comment": "Signature with special case values r=1 and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result": "invalid" + }, + { + "tcId": 21, + "comment": "Signature with special case values r=1 and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result": "invalid" + }, + { + "tcId": 22, + "comment": "Signature with special case values r=1 and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result": "invalid" + }, + { + "tcId": 23, + "comment": "Signature with special case values r=1 and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result": "invalid" + }, + { + "tcId": 24, + "comment": "Signature with special case values r=1 and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result": "invalid" + }, + { + "tcId": 25, + "comment": "Signature with special case values r=n and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410000000000000000000000000000000000000000000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 26, + "comment": "Signature with special case values r=n and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410000000000000000000000000000000000000000000000000000000000000001", + "result": "invalid" + }, + { + "tcId": 27, + "comment": "Signature with special case values r=n and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result": "invalid" + }, + { + "tcId": 28, + "comment": "Signature with special case values r=n and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result": "invalid" + }, + { + "tcId": 29, + "comment": "Signature with special case values r=n and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result": "invalid" + }, + { + "tcId": 30, + "comment": "Signature with special case values r=n and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result": "invalid" + }, + { + "tcId": 31, + "comment": "Signature with special case values r=n and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result": "invalid" + }, + { + "tcId": 32, + "comment": "Signature with special case values r=n - 1 and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641400000000000000000000000000000000000000000000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 33, + "comment": "Signature with special case values r=n - 1 and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641400000000000000000000000000000000000000000000000000000000000000001", + "result": "invalid" + }, + { + "tcId": 34, + "comment": "Signature with special case values r=n - 1 and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result": "invalid" + }, + { + "tcId": 35, + "comment": "Signature with special case values r=n - 1 and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result": "invalid" + }, + { + "tcId": 36, + "comment": "Signature with special case values r=n - 1 and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result": "invalid" + }, + { + "tcId": 37, + "comment": "Signature with special case values r=n - 1 and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result": "invalid" + }, + { + "tcId": 38, + "comment": "Signature with special case values r=n - 1 and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result": "invalid" + }, + { + "tcId": 39, + "comment": "Signature with special case values r=n + 1 and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641420000000000000000000000000000000000000000000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 40, + "comment": "Signature with special case values r=n + 1 and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641420000000000000000000000000000000000000000000000000000000000000001", + "result": "invalid" + }, + { + "tcId": 41, + "comment": "Signature with special case values r=n + 1 and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result": "invalid" + }, + { + "tcId": 42, + "comment": "Signature with special case values r=n + 1 and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result": "invalid" + }, + { + "tcId": 43, + "comment": "Signature with special case values r=n + 1 and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result": "invalid" + }, + { + "tcId": 44, + "comment": "Signature with special case values r=n + 1 and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result": "invalid" + }, + { + "tcId": 45, + "comment": "Signature with special case values r=n + 1 and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result": "invalid" + }, + { + "tcId": 46, + "comment": "Signature with special case values r=p and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0000000000000000000000000000000000000000000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 47, + "comment": "Signature with special case values r=p and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0000000000000000000000000000000000000000000000000000000000000001", + "result": "invalid" + }, + { + "tcId": 48, + "comment": "Signature with special case values r=p and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result": "invalid" + }, + { + "tcId": 49, + "comment": "Signature with special case values r=p and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result": "invalid" + }, + { + "tcId": 50, + "comment": "Signature with special case values r=p and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result": "invalid" + }, + { + "tcId": 51, + "comment": "Signature with special case values r=p and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result": "invalid" + }, + { + "tcId": 52, + "comment": "Signature with special case values r=p and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result": "invalid" + }, + { + "tcId": 53, + "comment": "Signature with special case values r=p + 1 and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc300000000000000000000000000000000000000000000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 54, + "comment": "Signature with special case values r=p + 1 and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc300000000000000000000000000000000000000000000000000000000000000001", + "result": "invalid" + }, + { + "tcId": 55, + "comment": "Signature with special case values r=p + 1 and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result": "invalid" + }, + { + "tcId": 56, + "comment": "Signature with special case values r=p + 1 and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result": "invalid" + }, + { + "tcId": 57, + "comment": "Signature with special case values r=p + 1 and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result": "invalid" + }, + { + "tcId": 58, + "comment": "Signature with special case values r=p + 1 and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result": "invalid" + }, + { + "tcId": 59, + "comment": "Signature with special case values r=p + 1 and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result": "invalid" + }, + { + "tcId": 60, + "comment": "Edge case for Shamir multiplication", + "flags": [ + "EdgeCaseShamirMultiplication" + ], + "msg": "3235353835", + "sig": "dd1b7d09a7bd8218961034a39a87fecf5314f00c4d25eb58a07ac85e85eab51635138c401ef8d3493d65c9002fe62b43aee568731b744548358996d9cc427e06", + "result": "valid" + }, + { + "tcId": 61, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "343236343739373234", + "sig": "95c29267d972a043d955224546222bba343fc1d4db0fec262a33ac61305696ae6edfe96713aed56f8a28a6653f57e0b829712e5eddc67f34682b24f0676b2640", + "result": "valid" + }, + { + "tcId": 62, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "37313338363834383931", + "sig": "28f94a894e92024699e345fe66971e3edcd050023386135ab3939d550898fb25cd69c1a42be05a6ee1270c821479251e134c21858d800bda6f4e98b37196238e", + "result": "valid" + }, + { + "tcId": 63, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3130333539333331363638", + "sig": "be26b18f9549f89f411a9b52536b15aa270b84548d0e859a1952a27af1a77ac68f3e2b05632fc33715572af9124681113f2b84325b80154c044a544dc1a8fa12", + "result": "valid" + }, + { + "tcId": 64, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33393439343031323135", + "sig": "b1a4b1478e65cc3eafdf225d1298b43f2da19e4bcff7eacc0a2e98cd4b74b114e8655ce1cfb33ebd30af8ce8e8ae4d6f7b50cd3e22af51bf69e0a2851760d52b", + "result": "valid" + }, + { + "tcId": 65, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31333434323933303739", + "sig": "325332021261f1bd18f2712aa1e2252da23796da8a4b1ff6ea18cafec7e171f240b4f5e287ee61fc3c804186982360891eaa35c75f05a43ecd48b35d984a6648", + "result": "valid" + }, + { + "tcId": 66, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33373036323131373132", + "sig": "a23ad18d8fc66d81af0903890cbd453a554cb04cdc1a8ca7f7f78e5367ed88a0dc1c14d31e3fb158b73c764268c8b55579734a7e2a2c9b5ee5d9d0144ef652eb", + "result": "valid" + }, + { + "tcId": 67, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "333433363838373132", + "sig": "2bdea41cda63a2d14bf47353bd20880a690901de7cd6e3cc6d8ed5ba0cdb1091c31599433036064073835b1e3eba8335a650c8fd786f94fe235ad7d41dc94c7a", + "result": "valid" + }, + { + "tcId": 68, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31333531353330333730", + "sig": "d7cd76ec01c1b1079eba9e2aa2a397243c4758c98a1ba0b7404a340b9b00ced6ca8affe1e626dd192174c2937b15bc48f77b5bdfe01f073a8aeaf7f24dc6c85b", + "result": "valid" + }, + { + "tcId": 69, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "36353533323033313236", + "sig": "a872c744d936db21a10c361dd5c9063355f84902219652f6fc56dc95a7139d96400df7575d9756210e9ccc77162c6b593c7746cfb48ac263c42750b421ef4bb9", + "result": "valid" + }, + { + "tcId": 70, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31353634333436363033", + "sig": "9fa9afe07752da10b36d3afcd0fe44bfc40244d75203599cf8f5047fa3453854af1f583fec4040ae7e68c968d2bb4b494eec3a33edc7c0ccf95f7f75bc2569c7", + "result": "valid" + }, + { + "tcId": 71, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "34343239353339313137", + "sig": "885640384d0d910efb177b46be6c3dc5cac81f0b88c3190bb6b5f99c2641f205738ed9bff116306d9caa0f8fc608be243e0b567779d8dab03e8e19d553f1dc8e", + "result": "valid" + }, + { + "tcId": 72, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3130393533323631333531", + "sig": "2d051f91c5a9d440c5676985710483bc4f1a6c611b10c95a2ff0363d90c2a45892206b19045a41a797cc2f3ac30de9518165e96d5b86341ecb3bcff231b3fd65", + "result": "valid" + }, + { + "tcId": 73, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "35393837333530303431", + "sig": "f3ac2523967482f53d508522712d583f4379cd824101ff635ea0935117baa54f27f10812227397e02cea96fb0e680761636dab2b080d1fc5d11685cbe8500cfe", + "result": "valid" + }, + { + "tcId": 74, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33343633303036383738", + "sig": "96447cf68c3ab7266ed7447de3ac52fed7cc08cbdfea391c18a9b8ab370bc913f0a1878b2c53f16e70fe377a5e9c6e86f18ae480a22bb499f5b32e7109c07385", + "result": "valid" + }, + { + "tcId": 75, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "39383137333230323837", + "sig": "530a0832b691da0b5619a0b11de6877f3c0971baaa68ed122758c29caaf46b7293761bb0a14ccf9f15b4b9ce73c6ec700bd015b8cb1cfac56837f4463f53074e", + "result": "valid" + }, + { + "tcId": 76, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33323232303431303436", + "sig": "9c54c25500bde0b92d72d6ec483dc2482f3654294ca74de796b681255ed58a77988bac394a90ad89ce360984c0c149dcbd2684bb64498ace90bcf6b6af1c170e", + "result": "valid" + }, + { + "tcId": 77, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "36363636333037313034", + "sig": "e7909d41439e2f6af29136c7348ca2641a2b070d5b64f91ea9da7070c7a2618b42d782f132fa1d36c2c88ba27c3d678d80184a5d1eccac7501f0b47e3d205008", + "result": "valid" + }, + { + "tcId": 78, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31303335393531383938", + "sig": "5924873209593135a4c3da7bb381227f8a4b6aa9f34fe5bb7f8fbc131a039ffee0e44ee4bbe370155bf0bbdec265bf9fe31c0746faab446de62e3631eacd111f", + "result": "valid" + }, + { + "tcId": 79, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31383436353937313935", + "sig": "eeb692c9b262969b231c38b5a7f60649e0c875cd64df88f33aa571fa3d29ab0e218b3a1eb06379c2c18cf51b06430786d1c64cd2d24c9b232b23e5bac7989acd", + "result": "valid" + }, + { + "tcId": 80, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33313336303436313839", + "sig": "a40034177f36091c2b653684a0e3eb5d4bff18e4d09f664c2800e7cafda1daf83a3ec29853704e52031c58927a800a968353adc3d973beba9172cbbeab4dd149", + "result": "valid" + }, + { + "tcId": 81, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "32363633373834323534", + "sig": "b5d795cc75cea5c434fa4185180cd6bd21223f3d5a86da6670d71d95680dadbfab1b277ef5ffe134460835e3d1402461ba104cb50b16f397fdc7a9abfefef280", + "result": "valid" + }, + { + "tcId": 82, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31363532313030353234", + "sig": "07dc2478d43c1232a4595608c64426c35510051a631ae6a5a6eb1161e57e42e14a59ea0fdb72d12165cea3bf1ca86ba97517bd188db3dbd21a5a157850021984", + "result": "valid" + }, + { + "tcId": 83, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "35373438303831363936", + "sig": "ddd20c4a05596ca868b558839fce9f6511ddd83d1ccb53f82e5269d559a01552a46e8cb8d626cf6c00ddedc3b5da7e613ac376445ee260743f06f79054c7d42a", + "result": "valid" + }, + { + "tcId": 84, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "36333433393133343638", + "sig": "9cde6e0ede0a003f02fda0a01b59facfe5dec063318f279ce2de7a9b1062f7b72886a5b8c679bdf8224c66f908fd6205492cb70b0068d46ae4f33a4149b12a52", + "result": "valid" + }, + { + "tcId": 85, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31353431313033353938", + "sig": "c5771016d0dd6357143c89f684cd740423502554c0c59aa8c99584f1ff38f609ab4bfa0bb88ab99791b9b3ab9c4b02bd2a57ae8dde50b9064063fcf85315cfe5", + "result": "valid" + }, + { + "tcId": 86, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3130343738353830313238", + "sig": "a24ebc0ec224bd67ae397cbe6fa37b3125adbd34891abe2d7c7356921916dfe634f6eb6374731bbbafc4924fb8b0bdcdda49456d724cdae6178d87014cb53d8c", + "result": "valid" + }, + { + "tcId": 87, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3130353336323835353638", + "sig": "2557d64a7aee2e0931c012e4fea1cd3a2c334edae68cdeb7158caf21b68e5a2480f93244956ffdc568c77d12684f7f004fa92da7e60ae94a1b98c422e23eda34", + "result": "valid" + }, + { + "tcId": 88, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "393533393034313035", + "sig": "c4f2eccbb6a24350c8466450b9d61b207ee359e037b3dcedb42a3f2e6dd6aeb5cd9c394a65d0aa322e391eb76b2a1a687f8620a88adef3a01eb8e4fb05b6477a", + "result": "valid" + }, + { + "tcId": 89, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "393738383438303339", + "sig": "eff04781c9cbcd162d0a25a6e2ebcca43506c523385cb515d49ea38a1b12fcadea5328ce6b36e56ab87acb0dcfea498bcec1bba86a065268f6eff3c41c4b0c9c", + "result": "valid" + }, + { + "tcId": 90, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33363130363732343432", + "sig": "f58b4e3110a64bf1b5db97639ee0e5a9c8dfa49dc59b679891f520fdf0584c87d32701ae777511624c1f8abbf02b248b04e7a9eb27938f524f3e8828ba40164a", + "result": "valid" + }, + { + "tcId": 91, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31303534323430373035", + "sig": "f8abecaa4f0c502de4bf5903d48417f786bf92e8ad72fec0bd7fcb7800c0bbe34c7f9e231076a30b7ae36b0cebe69ccef1cd194f7cce93a5588fd6814f437c0e", + "result": "valid" + }, + { + "tcId": 92, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "35313734343438313937", + "sig": "5d5b38bd37ad498b2227a633268a8cca879a5c7c94a4e416bd0a614d09e606d212b8d664ea9991062ecbb834e58400e25c46007af84f6007d7f1685443269afe", + "result": "valid" + }, + { + "tcId": 93, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31393637353631323531", + "sig": "0c1cd9fe4034f086a2b52d65b9d3834d72aebe7f33dfe8f976da82648177d8e313105782e3d0cfe85c2778dec1a848b27ac0ae071aa6da341a9553a946b41e59", + "result": "valid" + }, + { + "tcId": 94, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33343437323533333433", + "sig": "ae7935fb96ff246b7b5d5662870d1ba587b03d6e1360baf47988b5c02ccc1a5b5f00c323272083782d4a59f2dfd65e49de0693627016900ef7e61428056664b3", + "result": "valid" + }, + { + "tcId": 95, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "333638323634333138", + "sig": "00a134b5c6ccbcefd4c882b945baeb4933444172795fa6796aae149067547098a991b9efa2db276feae1c115c140770901839d87e60e7ec45a2b81cf3b437be6", + "result": "valid" + }, + { + "tcId": 96, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33323631313938363038", + "sig": "2e4721363ad3992c139e5a1c26395d2c2d777824aa24fde075e0d7381171309d8bf083b6bbe71ecff22baed087d5a77eaeaf726bf14ace2c03fd6e37ba6c26f2", + "result": "valid" + }, + { + "tcId": 97, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "39363738373831303934", + "sig": "6852e9d3cd9fe373c2d504877967d365ab1456707b6817a042864694e1960ccff9b4d815ebd4cf77847b37952334d05b2045cb398d4c21ba207922a7a4714d84", + "result": "valid" + }, + { + "tcId": 98, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "34393538383233383233", + "sig": "188a8c5648dc79eace158cf886c62b5468f05fd95f03a7635c5b4c31f09af4c536361a0b571a00c6cd5e686ccbfcfa703c4f97e48938346d0c103fdc76dc5867", + "result": "valid" + }, + { + "tcId": 99, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "383234363337383337", + "sig": "a74f1fb9a8263f62fc4416a5b7d584f4206f3996bb91f6fc8e73b9e92bad0e136815032e8c7d76c3ab06a86f33249ce9940148cb36d1f417c2e992e801afa3fa", + "result": "valid" + }, + { + "tcId": 100, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3131303230383333373736", + "sig": "07244865b72ff37e62e3146f0dc14682badd7197799135f0b00ade7671742bfef27f3ddc7124b1b58579573a835650e7a8bad5eeb96e9da215cd7bf9a2a039ed", + "result": "valid" + }, + { + "tcId": 101, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "313333383731363438", + "sig": "da7fdd05b5badabd619d805c4ee7d9a84f84ddd5cf9c5bf4d4338140d689ef0828f1cf4fa1c3c5862cfa149c0013cf5fe6cf5076cae000511063e7de25bb38e5", + "result": "valid" + }, + { + "tcId": 102, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "333232313434313632", + "sig": "d3027c656f6d4fdfd8ede22093e3c303b0133c340d615e7756f6253aea927238f6510f9f371b31068d68bfeeaa720eb9bbdc8040145fcf88d4e0b58de0777d2a", + "result": "valid" + }, + { + "tcId": 103, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3130363836363535353436", + "sig": "0bf6c0188dc9571cd0e21eecac5fbb19d2434988e9cc10244593ef3a98099f694864a562661f9221ec88e3dd0bc2f6e27ac128c30cc1a80f79ec670a22b042ee", + "result": "valid" + }, + { + "tcId": 104, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3632313535323436", + "sig": "ae459640d5d1179be47a47fa538e16d94ddea5585e7a244804a51742c686443a6c8e30e530a634fae80b3ceb062978b39edbe19777e0a24553b68886181fd897", + "result": "valid" + }, + { + "tcId": 105, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "37303330383138373734", + "sig": "1cf3517ba3bf2ab8b9ead4ebb6e866cb88a1deacb6a785d3b63b483ca02ac495249a798b73606f55f5f1c70de67cb1a0cff95d7dc50b3a617df861bad3c6b1c9", + "result": "valid" + }, + { + "tcId": 106, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "35393234353233373434", + "sig": "e69b5238265ea35d77e4dd172288d8cea19810a10292617d5976519dc5757cb84b03c5bc47e826bdb27328abd38d3056d77476b2130f3df6ec4891af08ba1e29", + "result": "valid" + }, + { + "tcId": 107, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31343935353836363231", + "sig": "5f9d7d7c870d085fc1d49fff69e4a275812800d2cf8973e7325866cb40fa2b6f6d1f5491d9f717a597a15fd540406486d76a44697b3f0d9d6dcef6669f8a0a56", + "result": "valid" + }, + { + "tcId": 108, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "34303035333134343036", + "sig": "0a7d5b1959f71df9f817146ee49bd5c89b431e7993e2fdecab6858957da685ae0f8aad2d254690bdc13f34a4fec44a02fd745a422df05ccbb54635a8b86b9609", + "result": "valid" + }, + { + "tcId": 109, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33303936343537353132", + "sig": "79e88bf576b74bc07ca142395fda28f03d3d5e640b0b4ff0752c6d94cd55340832cea05bd2d706c8f6036a507e2ab7766004f0904e2e5c5862749c0073245d6a", + "result": "valid" + }, + { + "tcId": 110, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "32373834303235363230", + "sig": "9d54e037a00212b377bc8874798b8da080564bbdf7e07591b861285809d0148818b4e557667a82bd95965f0706f81a29243fbdd86968a7ebeb43069db3b18c7f", + "result": "valid" + }, + { + "tcId": 111, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "32363138373837343138", + "sig": "2664f1ffa982fedbcc7cab1b8bc6e2cb420218d2a6077ad08e591ba9feab33bd49f5c7cb515e83872a3d41b4cdb85f242ad9d61a5bfc01debfbb52c6c84ba728", + "result": "valid" + }, + { + "tcId": 112, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31363432363235323632", + "sig": "5827518344844fd6a7de73cbb0a6befdea7b13d2dee4475317f0f18ffc81524bb0a334b1f4b774a5a289f553224d286d239ef8a90929ed2d91423e024eb7fa66", + "result": "valid" + }, + { + "tcId": 113, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "36383234313839343336", + "sig": "97ab19bd139cac319325869218b1bce111875d63fb12098a04b0cd59b6fdd3a3bce26315c5dbc7b8cfc31425a9b89bccea7aa9477d711a4d377f833dcc28f820", + "result": "valid" + }, + { + "tcId": 114, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "343834323435343235", + "sig": "52c683144e44119ae2013749d4964ef67509278f6d38ba869adcfa69970e123d3479910167408f45bda420a626ec9c4ec711c1274be092198b4187c018b562ca", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "uDj_ROW8F3vyEYnQdmCC_J2EMiaIf8l2A3EQC37iCm8", + "y": "8MnXW_unsxpryhl0SW7rVt41cHGVXYPEsbraoLIYMuk", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0407310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc36226a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0", + "wx": "07310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc362", + "wy": "26a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000407310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc36226a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEBzEPkKnq4UmghAL1QZSg97SsQnv42b1s\ndoEHHcR9w2ImptN6xG1h/WAMC/G/+HaJ7RF92msOWTGK4BChl6JsoA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 115, + "comment": "k*G has a large x-coordinate", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "000000000000000000000000000000014551231950b75fc4402da1722fc9baebfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e", + "result": "valid" + }, + { + "tcId": 116, + "comment": "r too large", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2cfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "BzEPkKnq4UmghAL1QZSg97SsQnv42b1sdoEHHcR9w2I", + "y": "JqbTesRtYf1gDAvxv_h2ie0RfdprDlkxiuAQoZeibKA", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04bc97e7585eecad48e16683bc4091708e1a930c683fc47001d4b383594f2c4e22705989cf69daeadd4e4e4b8151ed888dfec20fb01728d89d56b3f38f2ae9c8c5", + "wx": "00bc97e7585eecad48e16683bc4091708e1a930c683fc47001d4b383594f2c4e22", + "wy": "705989cf69daeadd4e4e4b8151ed888dfec20fb01728d89d56b3f38f2ae9c8c5" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004bc97e7585eecad48e16683bc4091708e1a930c683fc47001d4b383594f2c4e22705989cf69daeadd4e4e4b8151ed888dfec20fb01728d89d56b3f38f2ae9c8c5", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEvJfnWF7srUjhZoO8QJFwjhqTDGg/xHAB\n1LODWU8sTiJwWYnPadrq3U5OS4FR7YiN/sIPsBco2J1Ws/OPKunIxQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 117, + "comment": "r,s are large", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413ffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "vJfnWF7srUjhZoO8QJFwjhqTDGg_xHAB1LODWU8sTiI", + "y": "cFmJz2na6t1OTkuBUe2Ijf7CD7AXKNidVrPzjyrpyMU", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0444ad339afbc21e9abf7b602a5ca535ea378135b6d10d81310bdd8293d1df3252b63ff7d0774770f8fe1d1722fa83acd02f434e4fc110a0cc8f6dddd37d56c463", + "wx": "44ad339afbc21e9abf7b602a5ca535ea378135b6d10d81310bdd8293d1df3252", + "wy": "00b63ff7d0774770f8fe1d1722fa83acd02f434e4fc110a0cc8f6dddd37d56c463" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000444ad339afbc21e9abf7b602a5ca535ea378135b6d10d81310bdd8293d1df3252b63ff7d0774770f8fe1d1722fa83acd02f434e4fc110a0cc8f6dddd37d56c463", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERK0zmvvCHpq/e2AqXKU16jeBNbbRDYEx\nC92Ck9HfMlK2P/fQd0dw+P4dFyL6g6zQL0NOT8EQoMyPbd3TfVbEYw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 118, + "comment": "r and s^-1 have a large Hamming weight", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3e9a7582886089c62fb840cf3b83061cd1cff3ae4341808bb5bdee6191174177", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "RK0zmvvCHpq_e2AqXKU16jeBNbbRDYExC92Ck9HfMlI", + "y": "tj_30HdHcPj-HRci-oOs0C9DTk_BEKDMj23d031WxGM", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "041260c2122c9e244e1af5151bede0c3ae23b54d7c596881d3eebad21f37dd878c5c9a0c1a9ade76737a8811bd6a7f9287c978ee396aa89c11e47229d2ccb552f0", + "wx": "1260c2122c9e244e1af5151bede0c3ae23b54d7c596881d3eebad21f37dd878c", + "wy": "5c9a0c1a9ade76737a8811bd6a7f9287c978ee396aa89c11e47229d2ccb552f0" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200041260c2122c9e244e1af5151bede0c3ae23b54d7c596881d3eebad21f37dd878c5c9a0c1a9ade76737a8811bd6a7f9287c978ee396aa89c11e47229d2ccb552f0", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEEmDCEiyeJE4a9RUb7eDDriO1TXxZaIHT\n7rrSHzfdh4xcmgwamt52c3qIEb1qf5KHyXjuOWqonBHkcinSzLVS8A==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 119, + "comment": "r and s^-1 have a large Hamming weight", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc24238e70b431b1a64efdf9032669939d4b77f249503fc6905feb7540dea3e6d2", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "EmDCEiyeJE4a9RUb7eDDriO1TXxZaIHT7rrSHzfdh4w", + "y": "XJoMGprednN6iBG9an-Sh8l47jlqqJwR5HIp0sy1UvA", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "041877045be25d34a1d0600f9d5c00d0645a2a54379b6ceefad2e6bf5c2a3352ce821a532cc1751ee1d36d41c3d6ab4e9b143e44ec46d73478ea6a79a5c0e54159", + "wx": "1877045be25d34a1d0600f9d5c00d0645a2a54379b6ceefad2e6bf5c2a3352ce", + "wy": "00821a532cc1751ee1d36d41c3d6ab4e9b143e44ec46d73478ea6a79a5c0e54159" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200041877045be25d34a1d0600f9d5c00d0645a2a54379b6ceefad2e6bf5c2a3352ce821a532cc1751ee1d36d41c3d6ab4e9b143e44ec46d73478ea6a79a5c0e54159", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEGHcEW+JdNKHQYA+dXADQZFoqVDebbO76\n0ua/XCozUs6CGlMswXUe4dNtQcPWq06bFD5E7EbXNHjqanmlwOVBWQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 120, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001", + "result": "valid" + }, + { + "tcId": 121, + "comment": "incorrect size of signature", + "flags": [ + "SmallRandS", + "ArithmeticError", + "SignatureSize" + ], + "msg": "313233343030", + "sig": "0101", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "GHcEW-JdNKHQYA-dXADQZFoqVDebbO760ua_XCozUs4", + "y": "ghpTLMF1HuHTbUHD1qtOmxQ-ROxG1zR46mp5pcDlQVk", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04455439fcc3d2deeceddeaece60e7bd17304f36ebb602adf5a22e0b8f1db46a50aec38fb2baf221e9a8d1887c7bf6222dd1834634e77263315af6d23609d04f77", + "wx": "455439fcc3d2deeceddeaece60e7bd17304f36ebb602adf5a22e0b8f1db46a50", + "wy": "00aec38fb2baf221e9a8d1887c7bf6222dd1834634e77263315af6d23609d04f77" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004455439fcc3d2deeceddeaece60e7bd17304f36ebb602adf5a22e0b8f1db46a50aec38fb2baf221e9a8d1887c7bf6222dd1834634e77263315af6d23609d04f77", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERVQ5/MPS3uzt3q7OYOe9FzBPNuu2Aq31\noi4Ljx20alCuw4+yuvIh6ajRiHx79iIt0YNGNOdyYzFa9tI2CdBPdw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 122, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", + "result": "valid" + }, + { + "tcId": 123, + "comment": "incorrect size of signature", + "flags": [ + "SmallRandS", + "ArithmeticError", + "SignatureSize" + ], + "msg": "313233343030", + "sig": "0102", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "RVQ5_MPS3uzt3q7OYOe9FzBPNuu2Aq31oi4Ljx20alA", + "y": "rsOPsrryIemo0Yh8e_YiLdGDRjTncmMxWvbSNgnQT3c", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "042e1f466b024c0c3ace2437de09127fed04b706f94b19a21bb1c2acf35cece7180449ae3523d72534e964972cfd3b38af0bddd9619e5af223e4d1a40f34cf9f1d", + "wx": "2e1f466b024c0c3ace2437de09127fed04b706f94b19a21bb1c2acf35cece718", + "wy": "0449ae3523d72534e964972cfd3b38af0bddd9619e5af223e4d1a40f34cf9f1d" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200042e1f466b024c0c3ace2437de09127fed04b706f94b19a21bb1c2acf35cece7180449ae3523d72534e964972cfd3b38af0bddd9619e5af223e4d1a40f34cf9f1d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELh9GawJMDDrOJDfeCRJ/7QS3BvlLGaIb\nscKs81zs5xgESa41I9clNOlklyz9OzivC93ZYZ5a8iPk0aQPNM+fHQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 124, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000003", + "result": "valid" + }, + { + "tcId": 125, + "comment": "incorrect size of signature", + "flags": [ + "SmallRandS", + "ArithmeticError", + "SignatureSize" + ], + "msg": "313233343030", + "sig": "0103", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "Lh9GawJMDDrOJDfeCRJ_7QS3BvlLGaIbscKs81zs5xg", + "y": "BEmuNSPXJTTpZJcs_Ts4rwvd2WGeWvIj5NGkDzTPnx0", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "048e7abdbbd18de7452374c1879a1c3b01d13261e7d4571c3b47a1c76c55a2337326ed897cd517a4f5349db809780f6d2f2b9f6299d8b5a89077f1119a718fd7b3", + "wx": "008e7abdbbd18de7452374c1879a1c3b01d13261e7d4571c3b47a1c76c55a23373", + "wy": "26ed897cd517a4f5349db809780f6d2f2b9f6299d8b5a89077f1119a718fd7b3" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200048e7abdbbd18de7452374c1879a1c3b01d13261e7d4571c3b47a1c76c55a2337326ed897cd517a4f5349db809780f6d2f2b9f6299d8b5a89077f1119a718fd7b3", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjnq9u9GN50UjdMGHmhw7AdEyYefUVxw7\nR6HHbFWiM3Mm7Yl81Rek9TSduAl4D20vK59imdi1qJB38RGacY/Xsw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 126, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001", + "result": "valid" + }, + { + "tcId": 127, + "comment": "incorrect size of signature", + "flags": [ + "SmallRandS", + "ArithmeticError", + "SignatureSize" + ], + "msg": "313233343030", + "sig": "0201", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "jnq9u9GN50UjdMGHmhw7AdEyYefUVxw7R6HHbFWiM3M", + "y": "Ju2JfNUXpPU0nbgJeA9tLyufYpnYtaiQd_ERmnGP17M", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "047b333d4340d3d718dd3e6aff7de7bbf8b72bfd616c8420056052842376b9af1942117c5afeac755d6f376fc6329a7d76051b87123a4a5d0bc4a539380f03de7b", + "wx": "7b333d4340d3d718dd3e6aff7de7bbf8b72bfd616c8420056052842376b9af19", + "wy": "42117c5afeac755d6f376fc6329a7d76051b87123a4a5d0bc4a539380f03de7b" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200047b333d4340d3d718dd3e6aff7de7bbf8b72bfd616c8420056052842376b9af1942117c5afeac755d6f376fc6329a7d76051b87123a4a5d0bc4a539380f03de7b", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEezM9Q0DT1xjdPmr/fee7+Lcr/WFshCAF\nYFKEI3a5rxlCEXxa/qx1XW83b8Yymn12BRuHEjpKXQvEpTk4DwPeew==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 128, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002", + "result": "valid" + }, + { + "tcId": 129, + "comment": "incorrect size of signature", + "flags": [ + "SmallRandS", + "ArithmeticError", + "SignatureSize" + ], + "msg": "313233343030", + "sig": "0202", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "ezM9Q0DT1xjdPmr_fee7-Lcr_WFshCAFYFKEI3a5rxk", + "y": "QhF8Wv6sdV1vN2_GMpp9dgUbhxI6Sl0LxKU5OA8D3ns", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04d30ca4a0ddb6616c851d30ced682c40f83c62758a1f2759988d6763a88f1c0e503a80d5415650d41239784e8e2fb1235e9fe991d112ebb81186cbf0da2de3aff", + "wx": "00d30ca4a0ddb6616c851d30ced682c40f83c62758a1f2759988d6763a88f1c0e5", + "wy": "03a80d5415650d41239784e8e2fb1235e9fe991d112ebb81186cbf0da2de3aff" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004d30ca4a0ddb6616c851d30ced682c40f83c62758a1f2759988d6763a88f1c0e503a80d5415650d41239784e8e2fb1235e9fe991d112ebb81186cbf0da2de3aff", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE0wykoN22YWyFHTDO1oLED4PGJ1ih8nWZ\niNZ2OojxwOUDqA1UFWUNQSOXhOji+xI16f6ZHREuu4EYbL8Not46/w==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 130, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", + "result": "valid" + }, + { + "tcId": 131, + "comment": "incorrect size of signature", + "flags": [ + "SmallRandS", + "ArithmeticError", + "SignatureSize" + ], + "msg": "313233343030", + "sig": "0203", + "result": "invalid" + }, + { + "tcId": 132, + "comment": "r is larger than n", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641430000000000000000000000000000000000000000000000000000000000000003", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "0wykoN22YWyFHTDO1oLED4PGJ1ih8nWZiNZ2OojxwOU", + "y": "A6gNVBVlDUEjl4To4vsSNen-mR0RLruBGGy_DaLeOv8", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0448969b39991297b332a652d3ee6e01e909b39904e71fa2354a7830c7750baf24b4012d1b830d199ccb1fc972b32bfded55f09cd62d257e5e844e27e57a1594ec", + "wx": "48969b39991297b332a652d3ee6e01e909b39904e71fa2354a7830c7750baf24", + "wy": "00b4012d1b830d199ccb1fc972b32bfded55f09cd62d257e5e844e27e57a1594ec" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000448969b39991297b332a652d3ee6e01e909b39904e71fa2354a7830c7750baf24b4012d1b830d199ccb1fc972b32bfded55f09cd62d257e5e844e27e57a1594ec", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAESJabOZkSl7MyplLT7m4B6QmzmQTnH6I1\nSngwx3ULryS0AS0bgw0ZnMsfyXKzK/3tVfCc1i0lfl6ETiflehWU7A==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 133, + "comment": "s is larger than n", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000000000000000000002fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd04917c8", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "SJabOZkSl7MyplLT7m4B6QmzmQTnH6I1Sngwx3ULryQ", + "y": "tAEtG4MNGZzLH8lysyv97VXwnNYtJX5ehE4n5XoVlOw", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0402ef4d6d6cfd5a94f1d7784226e3e2a6c0a436c55839619f38fb4472b5f9ee777eb4acd4eebda5cd72875ffd2a2f26229c2dc6b46500919a432c86739f3ae866", + "wx": "02ef4d6d6cfd5a94f1d7784226e3e2a6c0a436c55839619f38fb4472b5f9ee77", + "wy": "7eb4acd4eebda5cd72875ffd2a2f26229c2dc6b46500919a432c86739f3ae866" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000402ef4d6d6cfd5a94f1d7784226e3e2a6c0a436c55839619f38fb4472b5f9ee777eb4acd4eebda5cd72875ffd2a2f26229c2dc6b46500919a432c86739f3ae866", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAu9NbWz9WpTx13hCJuPipsCkNsVYOWGf\nOPtEcrX57nd+tKzU7r2lzXKHX/0qLyYinC3GtGUAkZpDLIZznzroZg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 134, + "comment": "small r and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000000000000000000101c58b162c58b162c58b162c58b162c58a1b242973853e16db75c8a1a71da4d39d", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "Au9NbWz9WpTx13hCJuPipsCkNsVYOWGfOPtEcrX57nc", + "y": "frSs1O69pc1yh1_9Ki8mIpwtxrRlAJGaQyyGc5866GY", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04464f4ff715729cae5072ca3bd801d3195b67aec65e9b01aad20a2943dcbcb584b1afd29d31a39a11d570aa1597439b3b2d1971bf2f1abf15432d0207b10d1d08", + "wx": "464f4ff715729cae5072ca3bd801d3195b67aec65e9b01aad20a2943dcbcb584", + "wy": "00b1afd29d31a39a11d570aa1597439b3b2d1971bf2f1abf15432d0207b10d1d08" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004464f4ff715729cae5072ca3bd801d3195b67aec65e9b01aad20a2943dcbcb584b1afd29d31a39a11d570aa1597439b3b2d1971bf2f1abf15432d0207b10d1d08", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERk9P9xVynK5Qcso72AHTGVtnrsZemwGq\n0gopQ9y8tYSxr9KdMaOaEdVwqhWXQ5s7LRlxvy8avxVDLQIHsQ0dCA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 135, + "comment": "smallish r and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "000000000000000000000000000000000000000000000000002d9b4d347952ccfcbc5103d0da267477d1791461cf2aa44bf9d43198f79507bd8779d69a13108e", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "Rk9P9xVynK5Qcso72AHTGVtnrsZemwGq0gopQ9y8tYQ", + "y": "sa_SnTGjmhHVcKoVl0ObOy0Zcb8vGr8VQy0CB7ENHQg", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04157f8fddf373eb5f49cfcf10d8b853cf91cbcd7d665c3522ba7dd738ddb79a4cdeadf1a5c448ea3c9f4191a8999abfcc757ac6d64567ef072c47fec613443b8f", + "wx": "157f8fddf373eb5f49cfcf10d8b853cf91cbcd7d665c3522ba7dd738ddb79a4c", + "wy": "00deadf1a5c448ea3c9f4191a8999abfcc757ac6d64567ef072c47fec613443b8f" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004157f8fddf373eb5f49cfcf10d8b853cf91cbcd7d665c3522ba7dd738ddb79a4cdeadf1a5c448ea3c9f4191a8999abfcc757ac6d64567ef072c47fec613443b8f", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEFX+P3fNz619Jz88Q2LhTz5HLzX1mXDUi\nun3XON23mkzerfGlxEjqPJ9BkaiZmr/MdXrG1kVn7wcsR/7GE0Q7jw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 136, + "comment": "100-bit r and small s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "000000000000000000000000000000000000001033e67e37b32b445580bf4efc906f906f906f906f906f906f906f906ed8e426f7b1968c35a204236a579723d2", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "FX-P3fNz619Jz88Q2LhTz5HLzX1mXDUiun3XON23mkw", + "y": "3q3xpcRI6jyfQZGomZq_zHV6xtZFZ-8HLEf-xhNEO48", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "040934a537466c07430e2c48feb990bb19fb78cecc9cee424ea4d130291aa237f0d4f92d23b462804b5b68c52558c01c9996dbf727fccabbeedb9621a400535afa", + "wx": "0934a537466c07430e2c48feb990bb19fb78cecc9cee424ea4d130291aa237f0", + "wy": "00d4f92d23b462804b5b68c52558c01c9996dbf727fccabbeedb9621a400535afa" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200040934a537466c07430e2c48feb990bb19fb78cecc9cee424ea4d130291aa237f0d4f92d23b462804b5b68c52558c01c9996dbf727fccabbeedb9621a400535afa", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECTSlN0ZsB0MOLEj+uZC7Gft4zsyc7kJO\npNEwKRqiN/DU+S0jtGKAS1toxSVYwByZltv3J/zKu+7bliGkAFNa+g==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 137, + "comment": "small r and 100 bit s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000000000000000000101783266e90f43dafe5cd9b3b0be86de22f9de83677d0f50713a468ec72fcf5d57", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "CTSlN0ZsB0MOLEj-uZC7Gft4zsyc7kJOpNEwKRqiN_A", + "y": "1PktI7RigEtbaMUlWMAcmZbb9yf8yrvu25YhpABTWvo", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04d6ef20be66c893f741a9bf90d9b74675d1c2a31296397acb3ef174fd0b300c654a0c95478ca00399162d7f0f2dc89efdc2b28a30fbabe285857295a4b0c4e265", + "wx": "00d6ef20be66c893f741a9bf90d9b74675d1c2a31296397acb3ef174fd0b300c65", + "wy": "4a0c95478ca00399162d7f0f2dc89efdc2b28a30fbabe285857295a4b0c4e265" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004d6ef20be66c893f741a9bf90d9b74675d1c2a31296397acb3ef174fd0b300c654a0c95478ca00399162d7f0f2dc89efdc2b28a30fbabe285857295a4b0c4e265", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE1u8gvmbIk/dBqb+Q2bdGddHCoxKWOXrL\nPvF0/QswDGVKDJVHjKADmRYtfw8tyJ79wrKKMPur4oWFcpWksMTiZQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 138, + "comment": "100-bit r and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "00000000000000000000000000000000000000062522bbd3ecbe7c39e93e7c26783266e90f43dafe5cd9b3b0be86de22f9de83677d0f50713a468ec72fcf5d57", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "1u8gvmbIk_dBqb-Q2bdGddHCoxKWOXrLPvF0_QswDGU", + "y": "SgyVR4ygA5kWLX8PLcie_cKyijD7q-KFhXKVpLDE4mU", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04b7291d1404e0c0c07dab9372189f4bd58d2ceaa8d15ede544d9514545ba9ee0629c9a63d5e308769cc30ec276a410e6464a27eeafd9e599db10f053a4fe4a829", + "wx": "00b7291d1404e0c0c07dab9372189f4bd58d2ceaa8d15ede544d9514545ba9ee06", + "wy": "29c9a63d5e308769cc30ec276a410e6464a27eeafd9e599db10f053a4fe4a829" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004b7291d1404e0c0c07dab9372189f4bd58d2ceaa8d15ede544d9514545ba9ee0629c9a63d5e308769cc30ec276a410e6464a27eeafd9e599db10f053a4fe4a829", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEtykdFATgwMB9q5NyGJ9L1Y0s6qjRXt5U\nTZUUVFup7gYpyaY9XjCHacww7CdqQQ5kZKJ+6v2eWZ2xDwU6T+SoKQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 139, + "comment": "r and s^-1 are close to n", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03640c155555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "tykdFATgwMB9q5NyGJ9L1Y0s6qjRXt5UTZUUVFup7gY", + "y": "KcmmPV4wh2nMMOwnakEOZGSifur9nlmdsQ8FOk_kqCk", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "046e28303305d642ccb923b722ea86b2a0bc8e3735ecb26e849b19c9f76b2fdbb8186e80d64d8cab164f5238f5318461bf89d4d96ee6544c816c7566947774e0f6", + "wx": "6e28303305d642ccb923b722ea86b2a0bc8e3735ecb26e849b19c9f76b2fdbb8", + "wy": "186e80d64d8cab164f5238f5318461bf89d4d96ee6544c816c7566947774e0f6" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200046e28303305d642ccb923b722ea86b2a0bc8e3735ecb26e849b19c9f76b2fdbb8186e80d64d8cab164f5238f5318461bf89d4d96ee6544c816c7566947774e0f6", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEbigwMwXWQsy5I7ci6oayoLyONzXssm6E\nmxnJ92sv27gYboDWTYyrFk9SOPUxhGG/idTZbuZUTIFsdWaUd3Tg9g==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 140, + "comment": "r and s are 64-bit integer", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000000000000009c44febf31c3594d000000000000000000000000000000000000000000000000839ed28247c2b06b", + "result": "valid" + }, + { + "tcId": 141, + "comment": "incorrect size of signature", + "flags": [ + "ArithmeticError", + "SignatureSize" + ], + "msg": "313233343030", + "sig": "9c44febf31c3594d839ed28247c2b06b", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "bigwMwXWQsy5I7ci6oayoLyONzXssm6EmxnJ92sv27g", + "y": "GG6A1k2MqxZPUjj1MYRhv4nU2W7mVEyBbHVmlHd04PY", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04375bda93f6af92fb5f8f4b1b5f0534e3bafab34cb7ad9fb9d0b722e4a5c302a9a00b9f387a5a396097aa2162fc5bbcf4a5263372f681c94da51e9799120990fd", + "wx": "375bda93f6af92fb5f8f4b1b5f0534e3bafab34cb7ad9fb9d0b722e4a5c302a9", + "wy": "00a00b9f387a5a396097aa2162fc5bbcf4a5263372f681c94da51e9799120990fd" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004375bda93f6af92fb5f8f4b1b5f0534e3bafab34cb7ad9fb9d0b722e4a5c302a9a00b9f387a5a396097aa2162fc5bbcf4a5263372f681c94da51e9799120990fd", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEN1vak/avkvtfj0sbXwU047r6s0y3rZ+5\n0Lci5KXDAqmgC584elo5YJeqIWL8W7z0pSYzcvaByU2lHpeZEgmQ/Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 142, + "comment": "r and s are 100-bit integer", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "0000000000000000000000000000000000000009df8b682430beef6f5fd7c7cf000000000000000000000000000000000000000fd0a62e13778f4222a0d61c8a", + "result": "valid" + }, + { + "tcId": 143, + "comment": "incorrect size of signature", + "flags": [ + "ArithmeticError", + "SignatureSize" + ], + "msg": "313233343030", + "sig": "09df8b682430beef6f5fd7c7cf0fd0a62e13778f4222a0d61c8a", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "N1vak_avkvtfj0sbXwU047r6s0y3rZ-50Lci5KXDAqk", + "y": "oAufOHpaOWCXqiFi_Fu89KUmM3L2gclNpR6XmRIJkP0", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04d75b68216babe03ae257e94b4e3bf1c52f44e3df266d1524ff8c5ea69da73197da4bff9ed1c53f44917a67d7b978598e89df359e3d5913eaea24f3ae259abc44", + "wx": "00d75b68216babe03ae257e94b4e3bf1c52f44e3df266d1524ff8c5ea69da73197", + "wy": "00da4bff9ed1c53f44917a67d7b978598e89df359e3d5913eaea24f3ae259abc44" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004d75b68216babe03ae257e94b4e3bf1c52f44e3df266d1524ff8c5ea69da73197da4bff9ed1c53f44917a67d7b978598e89df359e3d5913eaea24f3ae259abc44", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE11toIWur4DriV+lLTjvxxS9E498mbRUk\n/4xepp2nMZfaS/+e0cU/RJF6Z9e5eFmOid81nj1ZE+rqJPOuJZq8RA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 144, + "comment": "r and s are 128-bit integer", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "000000000000000000000000000000008a598e563a89f526c32ebec8de26367a0000000000000000000000000000000084f633e2042630e99dd0f1e16f7a04bf", + "result": "valid" + }, + { + "tcId": 145, + "comment": "incorrect size of signature", + "flags": [ + "ArithmeticError", + "SignatureSize" + ], + "msg": "313233343030", + "sig": "8a598e563a89f526c32ebec8de26367a84f633e2042630e99dd0f1e16f7a04bf", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "11toIWur4DriV-lLTjvxxS9E498mbRUk_4xepp2nMZc", + "y": "2kv_ntHFP0SRemfXuXhZjonfNZ49WRPq6iTzriWavEQ", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0478bcda140aed23d430cb23c3dc0d01f423db134ee94a3a8cb483f2deac2ac653118114f6f33045d4e9ed9107085007bfbddf8f58fe7a1a2445d66a990045476e", + "wx": "78bcda140aed23d430cb23c3dc0d01f423db134ee94a3a8cb483f2deac2ac653", + "wy": "118114f6f33045d4e9ed9107085007bfbddf8f58fe7a1a2445d66a990045476e" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000478bcda140aed23d430cb23c3dc0d01f423db134ee94a3a8cb483f2deac2ac653118114f6f33045d4e9ed9107085007bfbddf8f58fe7a1a2445d66a990045476e", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeLzaFArtI9QwyyPD3A0B9CPbE07pSjqM\ntIPy3qwqxlMRgRT28zBF1OntkQcIUAe/vd+PWP56GiRF1mqZAEVHbg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 146, + "comment": "r and s are 160-bit integer", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "000000000000000000000000aa6eeb5823f7fa31b466bb473797f0d0314c0bdf000000000000000000000000e2977c479e6d25703cebbc6bd561938cc9d1bfb9", + "result": "valid" + }, + { + "tcId": 147, + "comment": "incorrect size of signature", + "flags": [ + "ArithmeticError", + "SignatureSize" + ], + "msg": "313233343030", + "sig": "aa6eeb5823f7fa31b466bb473797f0d0314c0bdfe2977c479e6d25703cebbc6bd561938cc9d1bfb9", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "eLzaFArtI9QwyyPD3A0B9CPbE07pSjqMtIPy3qwqxlM", + "y": "EYEU9vMwRdTp7ZEHCFAHv73fj1j-ehokRdZqmQBFR24", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04bb79f61857f743bfa1b6e7111ce4094377256969e4e15159123d9548acc3be6c1f9d9f8860dcffd3eb36dd6c31ff2e7226c2009c4c94d8d7d2b5686bf7abd677", + "wx": "00bb79f61857f743bfa1b6e7111ce4094377256969e4e15159123d9548acc3be6c", + "wy": "1f9d9f8860dcffd3eb36dd6c31ff2e7226c2009c4c94d8d7d2b5686bf7abd677" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004bb79f61857f743bfa1b6e7111ce4094377256969e4e15159123d9548acc3be6c1f9d9f8860dcffd3eb36dd6c31ff2e7226c2009c4c94d8d7d2b5686bf7abd677", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEu3n2GFf3Q7+htucRHOQJQ3claWnk4VFZ\nEj2VSKzDvmwfnZ+IYNz/0+s23Wwx/y5yJsIAnEyU2NfStWhr96vWdw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 148, + "comment": "s == 1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c10000000000000000000000000000000000000000000000000000000000000001", + "result": "valid" + }, + { + "tcId": 149, + "comment": "s == 0", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c10000000000000000000000000000000000000000000000000000000000000000", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "u3n2GFf3Q7-htucRHOQJQ3claWnk4VFZEj2VSKzDvmw", + "y": "H52fiGDc_9PrNt1sMf8ucibCAJxMlNjX0rVoa_er1nc", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0493591827d9e6713b4e9faea62c72b28dfefa68e0c05160b5d6aae88fd2e36c36073f5545ad5af410af26afff68654cf72d45e493489311203247347a890f4518", + "wx": "0093591827d9e6713b4e9faea62c72b28dfefa68e0c05160b5d6aae88fd2e36c36", + "wy": "073f5545ad5af410af26afff68654cf72d45e493489311203247347a890f4518" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000493591827d9e6713b4e9faea62c72b28dfefa68e0c05160b5d6aae88fd2e36c36073f5545ad5af410af26afff68654cf72d45e493489311203247347a890f4518", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEk1kYJ9nmcTtOn66mLHKyjf76aODAUWC1\n1qroj9LjbDYHP1VFrVr0EK8mr/9oZUz3LUXkk0iTESAyRzR6iQ9FGA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 150, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1419d981c515af8cc82545aac0c85e9e308fbb2eab6acd7ed497e0b4145a18fd9", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "k1kYJ9nmcTtOn66mLHKyjf76aODAUWC11qroj9LjbDY", + "y": "Bz9VRa1a9BCvJq__aGVM9y1F5JNIkxEgMkc0eokPRRg", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0431ed3081aefe001eb6402069ee2ccc1862937b85995144dba9503943587bf0dada01b8cc4df34f5ab3b1a359615208946e5ee35f98ee775b8ccecd86ccc1650f", + "wx": "31ed3081aefe001eb6402069ee2ccc1862937b85995144dba9503943587bf0da", + "wy": "00da01b8cc4df34f5ab3b1a359615208946e5ee35f98ee775b8ccecd86ccc1650f" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000431ed3081aefe001eb6402069ee2ccc1862937b85995144dba9503943587bf0dada01b8cc4df34f5ab3b1a359615208946e5ee35f98ee775b8ccecd86ccc1650f", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMe0wga7+AB62QCBp7izMGGKTe4WZUUTb\nqVA5Q1h78NraAbjMTfNPWrOxo1lhUgiUbl7jX5jud1uMzs2GzMFlDw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 151, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c11b21717ad71d23bbac60a9ad0baf75b063c9fdf52a00ebf99d022172910993c9", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "Me0wga7-AB62QCBp7izMGGKTe4WZUUTbqVA5Q1h78No", + "y": "2gG4zE3zT1qzsaNZYVIIlG5e41-Y7ndbjM7NhszBZQ8", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "047dff66fa98509ff3e2e51045f4390523dccda43a3bc2885e58c248090990eea854c76c2b9adeb6bb571823e07fd7c65c8639cf9d905260064c8e7675ce6d98b4", + "wx": "7dff66fa98509ff3e2e51045f4390523dccda43a3bc2885e58c248090990eea8", + "wy": "54c76c2b9adeb6bb571823e07fd7c65c8639cf9d905260064c8e7675ce6d98b4" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200047dff66fa98509ff3e2e51045f4390523dccda43a3bc2885e58c248090990eea854c76c2b9adeb6bb571823e07fd7c65c8639cf9d905260064c8e7675ce6d98b4", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEff9m+phQn/Pi5RBF9DkFI9zNpDo7wohe\nWMJICQmQ7qhUx2wrmt62u1cYI+B/18ZchjnPnZBSYAZMjnZ1zm2YtA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 152, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c12f588f66018f3dd14db3e28e77996487e32486b521ed8e5a20f06591951777e9", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "ff9m-phQn_Pi5RBF9DkFI9zNpDo7woheWMJICQmQ7qg", + "y": "VMdsK5retrtXGCPgf9fGXIY5z52QUmAGTI52dc5tmLQ", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "044280509aab64edfc0b4a2967e4cbce849cb544e4a77313c8e6ece579fbd7420a2e89fe5cc1927d554e6a3bb14033ea7c922cd75cba2c7415fdab52f20b1860f1", + "wx": "4280509aab64edfc0b4a2967e4cbce849cb544e4a77313c8e6ece579fbd7420a", + "wy": "2e89fe5cc1927d554e6a3bb14033ea7c922cd75cba2c7415fdab52f20b1860f1" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200044280509aab64edfc0b4a2967e4cbce849cb544e4a77313c8e6ece579fbd7420a2e89fe5cc1927d554e6a3bb14033ea7c922cd75cba2c7415fdab52f20b1860f1", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEQoBQmqtk7fwLSiln5MvOhJy1ROSncxPI\n5uzlefvXQgouif5cwZJ9VU5qO7FAM+p8kizXXLosdBX9q1LyCxhg8Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 153, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1091a08870ff4daf9123b30c20e8c4fc8505758dcf4074fcaff2170c9bfcf74f4", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "QoBQmqtk7fwLSiln5MvOhJy1ROSncxPI5uzlefvXQgo", + "y": "Lon-XMGSfVVOajuxQDPqfJIs11y6LHQV_atS8gsYYPE", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "044f8df145194e3c4fc3eea26d43ce75b402d6b17472ddcbb254b8a79b0bf3d9cb2aa20d82844cb266344e71ca78f2ad27a75a09e5bc0fa57e4efd9d465a0888db", + "wx": "4f8df145194e3c4fc3eea26d43ce75b402d6b17472ddcbb254b8a79b0bf3d9cb", + "wy": "2aa20d82844cb266344e71ca78f2ad27a75a09e5bc0fa57e4efd9d465a0888db" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200044f8df145194e3c4fc3eea26d43ce75b402d6b17472ddcbb254b8a79b0bf3d9cb2aa20d82844cb266344e71ca78f2ad27a75a09e5bc0fa57e4efd9d465a0888db", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAET43xRRlOPE/D7qJtQ851tALWsXRy3cuy\nVLinmwvz2csqog2ChEyyZjROccp48q0np1oJ5bwPpX5O/Z1GWgiI2w==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 154, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c17c370dc0ce8c59a8b273cba44a7c1191fc3186dc03cab96b0567312df0d0b250", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "T43xRRlOPE_D7qJtQ851tALWsXRy3cuyVLinmwvz2cs", + "y": "KqINgoRMsmY0TnHKePKtJ6daCeW8D6V-Tv2dRloIiNs", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "049598a57dd67ec3e16b587a338aa3a10a3a3913b41a3af32e3ed3ff01358c6b14122819edf8074bbc521f7d4cdce82fef7a516706affba1d93d9dea9ccae1a207", + "wx": "009598a57dd67ec3e16b587a338aa3a10a3a3913b41a3af32e3ed3ff01358c6b14", + "wy": "122819edf8074bbc521f7d4cdce82fef7a516706affba1d93d9dea9ccae1a207" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200049598a57dd67ec3e16b587a338aa3a10a3a3913b41a3af32e3ed3ff01358c6b14122819edf8074bbc521f7d4cdce82fef7a516706affba1d93d9dea9ccae1a207", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAElZilfdZ+w+FrWHoziqOhCjo5E7QaOvMu\nPtP/ATWMaxQSKBnt+AdLvFIffUzc6C/velFnBq/7odk9neqcyuGiBw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 155, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c170b59a7d1ee77a2f9e0491c2a7cfcd0ed04df4a35192f6132dcc668c79a6160e", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "lZilfdZ-w-FrWHoziqOhCjo5E7QaOvMuPtP_ATWMaxQ", + "y": "EigZ7fgHS7xSH31M3Ogv73pRZwav-6HZPZ3qnMrhogc", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "049171fec3ca20806bc084f12f0760911b60990bd80e5b2a71ca03a048b20f837e634fd17863761b2958d2be4e149f8d3d7abbdc18be03f451ab6c17fa0a1f8330", + "wx": "009171fec3ca20806bc084f12f0760911b60990bd80e5b2a71ca03a048b20f837e", + "wy": "634fd17863761b2958d2be4e149f8d3d7abbdc18be03f451ab6c17fa0a1f8330" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200049171fec3ca20806bc084f12f0760911b60990bd80e5b2a71ca03a048b20f837e634fd17863761b2958d2be4e149f8d3d7abbdc18be03f451ab6c17fa0a1f8330", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEkXH+w8oggGvAhPEvB2CRG2CZC9gOWypx\nygOgSLIPg35jT9F4Y3YbKVjSvk4Un409ervcGL4D9FGrbBf6Ch+DMA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 156, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c12736d76e412246e097148e2bf62915614eb7c428913a58eb5e9cd4674a9423de", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "kXH-w8oggGvAhPEvB2CRG2CZC9gOWypxygOgSLIPg34", + "y": "Y0_ReGN2GylY0r5OFJ-NPXq73Bi-A_RRq2wX-gofgzA", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04777c8930b6e1d271100fe68ce93f163fa37612c5fff67f4a62fc3bafaf3d17a9ed73d86f60a51b5ed91353a3b054edc0aa92c9ebcbd0b75d188fdc882791d68d", + "wx": "777c8930b6e1d271100fe68ce93f163fa37612c5fff67f4a62fc3bafaf3d17a9", + "wy": "00ed73d86f60a51b5ed91353a3b054edc0aa92c9ebcbd0b75d188fdc882791d68d" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004777c8930b6e1d271100fe68ce93f163fa37612c5fff67f4a62fc3bafaf3d17a9ed73d86f60a51b5ed91353a3b054edc0aa92c9ebcbd0b75d188fdc882791d68d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEd3yJMLbh0nEQD+aM6T8WP6N2EsX/9n9K\nYvw7r689F6ntc9hvYKUbXtkTU6OwVO3AqpLJ68vQt10Yj9yIJ5HWjQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 157, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c14a1e12831fbe93627b02d6e7f24bccdd6ef4b2d0f46739eaf3b1eaf0ca117770", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "d3yJMLbh0nEQD-aM6T8WP6N2EsX_9n9KYvw7r689F6k", + "y": "7XPYb2ClG17ZE1OjsFTtwKqSyevL0LddGI_ciCeR1o0", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04eabc248f626e0a63e1eb81c43d461a39a1dba881eb6ee2152b07c32d71bcf4700603caa8b9d33db13af44c6efbec8a198ed6124ac9eb17eaafd2824a545ec000", + "wx": "00eabc248f626e0a63e1eb81c43d461a39a1dba881eb6ee2152b07c32d71bcf470", + "wy": "0603caa8b9d33db13af44c6efbec8a198ed6124ac9eb17eaafd2824a545ec000" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004eabc248f626e0a63e1eb81c43d461a39a1dba881eb6ee2152b07c32d71bcf4700603caa8b9d33db13af44c6efbec8a198ed6124ac9eb17eaafd2824a545ec000", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE6rwkj2JuCmPh64HEPUYaOaHbqIHrbuIV\nKwfDLXG89HAGA8qoudM9sTr0TG777IoZjtYSSsnrF+qv0oJKVF7AAA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 158, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c106c778d4dfff7dee06ed88bc4e0ed34fc553aad67caf796f2a1c6487c1b2e877", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "6rwkj2JuCmPh64HEPUYaOaHbqIHrbuIVKwfDLXG89HA", + "y": "BgPKqLnTPbE69Exu--yKGY7WEkrJ6xfqr9KCSlRewAA", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "049f7a13ada158a55f9ddf1a45f044f073d9b80030efdcfc9f9f58418fbceaf001f8ada0175090f80d47227d6713b6740f9a0091d88a837d0a1cd77b58a8f28d73", + "wx": "009f7a13ada158a55f9ddf1a45f044f073d9b80030efdcfc9f9f58418fbceaf001", + "wy": "00f8ada0175090f80d47227d6713b6740f9a0091d88a837d0a1cd77b58a8f28d73" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200049f7a13ada158a55f9ddf1a45f044f073d9b80030efdcfc9f9f58418fbceaf001f8ada0175090f80d47227d6713b6740f9a0091d88a837d0a1cd77b58a8f28d73", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEn3oTraFYpV+d3xpF8ETwc9m4ADDv3Pyf\nn1hBj7zq8AH4raAXUJD4DUcifWcTtnQPmgCR2IqDfQoc13tYqPKNcw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 159, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c14de459ef9159afa057feb3ec40fef01c45b809f4ab296ea48c206d4249a2b451", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "n3oTraFYpV-d3xpF8ETwc9m4ADDv3Pyfn1hBj7zq8AE", + "y": "-K2gF1CQ-A1HIn1nE7Z0D5oAkdiKg30KHNd7WKjyjXM", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0411c4f3e461cd019b5c06ea0cea4c4090c3cc3e3c5d9f3c6d65b436826da9b4dbbbeb7a77e4cbfda207097c43423705f72c80476da3dac40a483b0ab0f2ead1cb", + "wx": "11c4f3e461cd019b5c06ea0cea4c4090c3cc3e3c5d9f3c6d65b436826da9b4db", + "wy": "00bbeb7a77e4cbfda207097c43423705f72c80476da3dac40a483b0ab0f2ead1cb" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000411c4f3e461cd019b5c06ea0cea4c4090c3cc3e3c5d9f3c6d65b436826da9b4dbbbeb7a77e4cbfda207097c43423705f72c80476da3dac40a483b0ab0f2ead1cb", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEEcTz5GHNAZtcBuoM6kxAkMPMPjxdnzxt\nZbQ2gm2ptNu763p35Mv9ogcJfENCNwX3LIBHbaPaxApIOwqw8urRyw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 160, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1745d294978007302033502e1acc48b63ae6500be43adbea1b258d6b423dbb416", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "EcTz5GHNAZtcBuoM6kxAkMPMPjxdnzxtZbQ2gm2ptNs", + "y": "u-t6d-TL_aIHCXxDQjcF9yyAR22j2sQKSDsKsPLq0cs", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04e2e18682d53123aa01a6c5d00b0c623d671b462ea80bddd65227fd5105988aa4161907b3fd25044a949ea41c8e2ea8459dc6f1654856b8b61b31543bb1b45bdb", + "wx": "00e2e18682d53123aa01a6c5d00b0c623d671b462ea80bddd65227fd5105988aa4", + "wy": "161907b3fd25044a949ea41c8e2ea8459dc6f1654856b8b61b31543bb1b45bdb" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004e2e18682d53123aa01a6c5d00b0c623d671b462ea80bddd65227fd5105988aa4161907b3fd25044a949ea41c8e2ea8459dc6f1654856b8b61b31543bb1b45bdb", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE4uGGgtUxI6oBpsXQCwxiPWcbRi6oC93W\nUif9UQWYiqQWGQez/SUESpSepByOLqhFncbxZUhWuLYbMVQ7sbRb2w==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 161, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c17b2a785e3896f59b2d69da57648e80ad3c133a750a2847fd2098ccd902042b6c", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "4uGGgtUxI6oBpsXQCwxiPWcbRi6oC93WUif9UQWYiqQ", + "y": "FhkHs_0lBEqUnqQcji6oRZ3G8WVIVri2GzFUO7G0W9s", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0490f8d4ca73de08a6564aaf005247b6f0ffe978504dce52605f46b7c3e56197dafadbe528eb70d9ee7ea0e70702db54f721514c7b8604ac2cb214f1decb7e383d", + "wx": "0090f8d4ca73de08a6564aaf005247b6f0ffe978504dce52605f46b7c3e56197da", + "wy": "00fadbe528eb70d9ee7ea0e70702db54f721514c7b8604ac2cb214f1decb7e383d" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000490f8d4ca73de08a6564aaf005247b6f0ffe978504dce52605f46b7c3e56197dafadbe528eb70d9ee7ea0e70702db54f721514c7b8604ac2cb214f1decb7e383d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEkPjUynPeCKZWSq8AUke28P/peFBNzlJg\nX0a3w+Vhl9r62+Uo63DZ7n6g5wcC21T3IVFMe4YErCyyFPHey344PQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 162, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c171ae94a72ca896875e7aa4a4c3d29afdb4b35b6996273e63c47ac519256c5eb1", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "kPjUynPeCKZWSq8AUke28P_peFBNzlJgX0a3w-Vhl9o", + "y": "-tvlKOtw2e5-oOcHAttU9yFRTHuGBKwsshTx3st-OD0", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04824c195c73cffdf038d101bce1687b5c3b6146f395c885976f7753b2376b948e3cdefa6fc347d13e4dcbc63a0b03a165180cd2be1431a0cf74ce1ea25082d2bc", + "wx": "00824c195c73cffdf038d101bce1687b5c3b6146f395c885976f7753b2376b948e", + "wy": "3cdefa6fc347d13e4dcbc63a0b03a165180cd2be1431a0cf74ce1ea25082d2bc" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004824c195c73cffdf038d101bce1687b5c3b6146f395c885976f7753b2376b948e3cdefa6fc347d13e4dcbc63a0b03a165180cd2be1431a0cf74ce1ea25082d2bc", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEgkwZXHPP/fA40QG84Wh7XDthRvOVyIWX\nb3dTsjdrlI483vpvw0fRPk3LxjoLA6FlGAzSvhQxoM90zh6iUILSvA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 163, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c10fa527fa7343c0bc9ec35a6278bfbff4d83301b154fc4bd14aee7eb93445b5f9", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "gkwZXHPP_fA40QG84Wh7XDthRvOVyIWXb3dTsjdrlI4", + "y": "PN76b8NH0T5Ny8Y6CwOhZRgM0r4UMaDPdM4eolCC0rw", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "042788a52f078eb3f202c4fa73e0d3386faf3df6be856003636f599922d4f5268f30b4f207c919bbdf5e67a8be4265a8174754b3aba8f16e575b77ff4d5a7eb64f", + "wx": "2788a52f078eb3f202c4fa73e0d3386faf3df6be856003636f599922d4f5268f", + "wy": "30b4f207c919bbdf5e67a8be4265a8174754b3aba8f16e575b77ff4d5a7eb64f" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200042788a52f078eb3f202c4fa73e0d3386faf3df6be856003636f599922d4f5268f30b4f207c919bbdf5e67a8be4265a8174754b3aba8f16e575b77ff4d5a7eb64f", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEJ4ilLweOs/ICxPpz4NM4b6899r6FYANj\nb1mZItT1Jo8wtPIHyRm7315nqL5CZagXR1Szq6jxbldbd/9NWn62Tw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 164, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c16539c0adadd0525ff42622164ce9314348bd0863b4c80e936b23ca0414264671", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "J4ilLweOs_ICxPpz4NM4b6899r6FYANjb1mZItT1Jo8", + "y": "MLTyB8kZu99eZ6i-QmWoF0dUs6uo8W5XW3f_TVp-tk8", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04d533b789a4af890fa7a82a1fae58c404f9a62a50b49adafab349c513b415087401b4171b803e76b34a9861e10f7bc289a066fd01bd29f84c987a10a5fb18c2d4", + "wx": "00d533b789a4af890fa7a82a1fae58c404f9a62a50b49adafab349c513b4150874", + "wy": "01b4171b803e76b34a9861e10f7bc289a066fd01bd29f84c987a10a5fb18c2d4" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004d533b789a4af890fa7a82a1fae58c404f9a62a50b49adafab349c513b415087401b4171b803e76b34a9861e10f7bc289a066fd01bd29f84c987a10a5fb18c2d4", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE1TO3iaSviQ+nqCofrljEBPmmKlC0mtr6\ns0nFE7QVCHQBtBcbgD52s0qYYeEPe8KJoGb9Ab0p+EyYehCl+xjC1A==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 165, + "comment": "point at infinity during verify", + "flags": [ + "PointDuplication", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "1TO3iaSviQ-nqCofrljEBPmmKlC0mtr6s0nFE7QVCHQ", + "y": "AbQXG4A-drNKmGHhD3vCiaBm_QG9KfhMmHoQpfsYwtQ", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "043a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a", + "wx": "3a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4", + "wy": "221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200043a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOjFQeYyK9p0ebpgfOkVAK6HXMvS+gzDF\nFk9J4Q7FVbQiG9hCvF5Nl+/zcWX2DjmYpCTXKkUM+V6kd8eCh9A0Og==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 166, + "comment": "edge case for signature malleability", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a07fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "OjFQeYyK9p0ebpgfOkVAK6HXMvS-gzDFFk9J4Q7FVbQ", + "y": "IhvYQrxeTZfv83Fl9g45mKQk1ypFDPlepHfHgofQNDo", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "043b37df5fb347c69a0f17d85c0c7ca83736883a825e13143d0fcfc8101e851e800de3c090b6ca21ba543517330c04b12f948c6badf14a63abffdf4ef8c7537026", + "wx": "3b37df5fb347c69a0f17d85c0c7ca83736883a825e13143d0fcfc8101e851e80", + "wy": "0de3c090b6ca21ba543517330c04b12f948c6badf14a63abffdf4ef8c7537026" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200043b37df5fb347c69a0f17d85c0c7ca83736883a825e13143d0fcfc8101e851e800de3c090b6ca21ba543517330c04b12f948c6badf14a63abffdf4ef8c7537026", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOzffX7NHxpoPF9hcDHyoNzaIOoJeExQ9\nD8/IEB6FHoAN48CQtsohulQ1FzMMBLEvlIxrrfFKY6v/3074x1NwJg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 167, + "comment": "edge case for signature malleability", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a07fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "OzffX7NHxpoPF9hcDHyoNzaIOoJeExQ9D8_IEB6FHoA", + "y": "DePAkLbKIbpUNRczDASxL5SMa63xSmOr_99O-MdTcCY", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04feb5163b0ece30ff3e03c7d55c4380fa2fa81ee2c0354942ff6f08c99d0cd82ce87de05ee1bda089d3e4e248fa0f721102acfffdf50e654be281433999df897e", + "wx": "00feb5163b0ece30ff3e03c7d55c4380fa2fa81ee2c0354942ff6f08c99d0cd82c", + "wy": "00e87de05ee1bda089d3e4e248fa0f721102acfffdf50e654be281433999df897e" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004feb5163b0ece30ff3e03c7d55c4380fa2fa81ee2c0354942ff6f08c99d0cd82ce87de05ee1bda089d3e4e248fa0f721102acfffdf50e654be281433999df897e", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE/rUWOw7OMP8+A8fVXEOA+i+oHuLANUlC\n/28IyZ0M2CzofeBe4b2gidPk4kj6D3IRAqz//fUOZUvigUM5md+Jfg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 168, + "comment": "u1 == 1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "_rUWOw7OMP8-A8fVXEOA-i-oHuLANUlC_28IyZ0M2Cw", + "y": "6H3gXuG9oInT5OJI-g9yEQKs__31DmVL4oFDOZnfiX4", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04238ced001cf22b8853e02edc89cbeca5050ba7e042a7a77f9382cd414922897640683d3094643840f295890aa4c18aa39b41d77dd0fb3bb2700e4f9ec284ffc2", + "wx": "238ced001cf22b8853e02edc89cbeca5050ba7e042a7a77f9382cd4149228976", + "wy": "40683d3094643840f295890aa4c18aa39b41d77dd0fb3bb2700e4f9ec284ffc2" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004238ced001cf22b8853e02edc89cbeca5050ba7e042a7a77f9382cd414922897640683d3094643840f295890aa4c18aa39b41d77dd0fb3bb2700e4f9ec284ffc2", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEI4ztABzyK4hT4C7cicvspQULp+BCp6d/\nk4LNQUkiiXZAaD0wlGQ4QPKViQqkwYqjm0HXfdD7O7JwDk+ewoT/wg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 169, + "comment": "u1 == n - 1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b844a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "I4ztABzyK4hT4C7cicvspQULp-BCp6d_k4LNQUkiiXY", + "y": "QGg9MJRkOEDylYkKpMGKo5tB133Q-zuycA5PnsKE_8I", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04961cf64817c06c0e51b3c2736c922fde18bd8c4906fcd7f5ef66c4678508f35ed2c5d18168cfbe70f2f123bd7419232bb92dd69113e2941061889481c5a027bf", + "wx": "00961cf64817c06c0e51b3c2736c922fde18bd8c4906fcd7f5ef66c4678508f35e", + "wy": "00d2c5d18168cfbe70f2f123bd7419232bb92dd69113e2941061889481c5a027bf" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004961cf64817c06c0e51b3c2736c922fde18bd8c4906fcd7f5ef66c4678508f35ed2c5d18168cfbe70f2f123bd7419232bb92dd69113e2941061889481c5a027bf", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAElhz2SBfAbA5Rs8JzbJIv3hi9jEkG/Nf1\n72bEZ4UI817SxdGBaM++cPLxI710GSMruS3WkRPilBBhiJSBxaAnvw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 170, + "comment": "u2 == 1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b855555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "lhz2SBfAbA5Rs8JzbJIv3hi9jEkG_Nf172bEZ4UI814", + "y": "0sXRgWjPvnDy8SO9dBkjK7kt1pET4pQQYYiUgcWgJ78", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0413681eae168cd4ea7cf2e2a45d052742d10a9f64e796867dbdcb829fe0b1028816528760d177376c09df79de39557c329cc1753517acffe8fa2ec298026b8384", + "wx": "13681eae168cd4ea7cf2e2a45d052742d10a9f64e796867dbdcb829fe0b10288", + "wy": "16528760d177376c09df79de39557c329cc1753517acffe8fa2ec298026b8384" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000413681eae168cd4ea7cf2e2a45d052742d10a9f64e796867dbdcb829fe0b1028816528760d177376c09df79de39557c329cc1753517acffe8fa2ec298026b8384", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEE2gerhaM1Op88uKkXQUnQtEKn2TnloZ9\nvcuCn+CxAogWUodg0Xc3bAnfed45VXwynMF1NRes/+j6LsKYAmuDhA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 171, + "comment": "u2 == n - 1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b89", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "E2gerhaM1Op88uKkXQUnQtEKn2TnloZ9vcuCn-CxAog", + "y": "FlKHYNF3N2wJ33neOVV8MpzBdTUXrP_o-i7CmAJrg4Q", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "045aa7abfdb6b4086d543325e5d79c6e95ce42f866d2bb84909633a04bb1aa31c291c80088794905e1da33336d874e2f91ccf45cc59185bede5dd6f3f7acaae18b", + "wx": "5aa7abfdb6b4086d543325e5d79c6e95ce42f866d2bb84909633a04bb1aa31c2", + "wy": "0091c80088794905e1da33336d874e2f91ccf45cc59185bede5dd6f3f7acaae18b" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200045aa7abfdb6b4086d543325e5d79c6e95ce42f866d2bb84909633a04bb1aa31c291c80088794905e1da33336d874e2f91ccf45cc59185bede5dd6f3f7acaae18b", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEWqer/ba0CG1UMyXl15xulc5C+GbSu4SQ\nljOgS7GqMcKRyACIeUkF4dozM22HTi+RzPRcxZGFvt5d1vP3rKrhiw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 172, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffce91e1ba6ba898620a46bcb51dc0b8b4ad1dc35dad892c4552d1847b2ce444637", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "Wqer_ba0CG1UMyXl15xulc5C-GbSu4SQljOgS7GqMcI", + "y": "kcgAiHlJBeHaMzNth04vkcz0XMWRhb7eXdbz96yq4Ys", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0400277791b305a45b2b39590b2f05d3392a6c8182cef4eb540120e0f5c206c3e464108233fb0b8c3ac892d79ef8e0fbf92ed133addb4554270132584dc52eef41", + "wx": "277791b305a45b2b39590b2f05d3392a6c8182cef4eb540120e0f5c206c3e4", + "wy": "64108233fb0b8c3ac892d79ef8e0fbf92ed133addb4554270132584dc52eef41" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000400277791b305a45b2b39590b2f05d3392a6c8182cef4eb540120e0f5c206c3e464108233fb0b8c3ac892d79ef8e0fbf92ed133addb4554270132584dc52eef41", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEACd3kbMFpFsrOVkLLwXTOSpsgYLO9OtU\nASDg9cIGw+RkEIIz+wuMOsiS15744Pv5LtEzrdtFVCcBMlhNxS7vQQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 173, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffce36bf0cec06d9b841da81332812f74f30bbaec9f202319206c6f0b8a0a400ff7", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "ACd3kbMFpFsrOVkLLwXTOSpsgYLO9OtUASDg9cIGw-Q", + "y": "ZBCCM_sLjDrIktee-OD7-S7RM63bRVQnATJYTcUu70E", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "046efa092b68de9460f0bcc919005a5f6e80e19de98968be3cd2c770a9949bfb1ac75e6e5087d6550d5f9beb1e79e5029307bc255235e2d5dc99241ac3ab886c49", + "wx": "6efa092b68de9460f0bcc919005a5f6e80e19de98968be3cd2c770a9949bfb1a", + "wy": "00c75e6e5087d6550d5f9beb1e79e5029307bc255235e2d5dc99241ac3ab886c49" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200046efa092b68de9460f0bcc919005a5f6e80e19de98968be3cd2c770a9949bfb1ac75e6e5087d6550d5f9beb1e79e5029307bc255235e2d5dc99241ac3ab886c49", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEbvoJK2jelGDwvMkZAFpfboDhnemJaL48\n0sdwqZSb+xrHXm5Qh9ZVDV+b6x555QKTB7wlUjXi1dyZJBrDq4hsSQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 174, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcea26b57af884b6c06e348efe139c1e4e9ec9518d60c340f6bac7d278ca08d8a6", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "bvoJK2jelGDwvMkZAFpfboDhnemJaL480sdwqZSb-xo", + "y": "x15uUIfWVQ1fm-seeeUCkwe8JVI14tXcmSQaw6uIbEk", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0472d4a19c4f9d2cf5848ea40445b70d4696b5f02d632c0c654cc7d7eeb0c6d058e8c4cd9943e459174c7ac01fa742198e47e6c19a6bdb0c4f6c237831c1b3f942", + "wx": "72d4a19c4f9d2cf5848ea40445b70d4696b5f02d632c0c654cc7d7eeb0c6d058", + "wy": "00e8c4cd9943e459174c7ac01fa742198e47e6c19a6bdb0c4f6c237831c1b3f942" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000472d4a19c4f9d2cf5848ea40445b70d4696b5f02d632c0c654cc7d7eeb0c6d058e8c4cd9943e459174c7ac01fa742198e47e6c19a6bdb0c4f6c237831c1b3f942", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEctShnE+dLPWEjqQERbcNRpa18C1jLAxl\nTMfX7rDG0FjoxM2ZQ+RZF0x6wB+nQhmOR+bBmmvbDE9sI3gxwbP5Qg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 175, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc5b1d27a7694c146244a5ad0bd0636d9d9ef3b9fb58385418d9c982105077d1b7", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "ctShnE-dLPWEjqQERbcNRpa18C1jLAxlTMfX7rDG0Fg", + "y": "6MTNmUPkWRdMesAfp0IZjkfmwZpr2wxPbCN4McGz-UI", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "042a8ea2f50dcced0c217575bdfa7cd47d1c6f100041ec0e35512794c1be7e740258f8c17122ed303fda7143eb58bede70295b653266013b0b0ebd3f053137f6ec", + "wx": "2a8ea2f50dcced0c217575bdfa7cd47d1c6f100041ec0e35512794c1be7e7402", + "wy": "58f8c17122ed303fda7143eb58bede70295b653266013b0b0ebd3f053137f6ec" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200042a8ea2f50dcced0c217575bdfa7cd47d1c6f100041ec0e35512794c1be7e740258f8c17122ed303fda7143eb58bede70295b653266013b0b0ebd3f053137f6ec", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEKo6i9Q3M7QwhdXW9+nzUfRxvEABB7A41\nUSeUwb5+dAJY+MFxIu0wP9pxQ+tYvt5wKVtlMmYBOwsOvT8FMTf27A==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 176, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcd27a7694c146244a5ad0bd0636d9e12abe687897e8e9998ddbd4e59a78520d0f", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "Ko6i9Q3M7QwhdXW9-nzUfRxvEABB7A41USeUwb5-dAI", + "y": "WPjBcSLtMD_acUPrWL7ecClbZTJmATsLDr0_BTE39uw", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0488de689ce9af1e94be6a2089c8a8b1253ffdbb6c8e9c86249ba220001a4ad3b80c4998e54842f413b9edb1825acbb6335e81e4d184b2b01c8bebdc85d1f28946", + "wx": "0088de689ce9af1e94be6a2089c8a8b1253ffdbb6c8e9c86249ba220001a4ad3b8", + "wy": "0c4998e54842f413b9edb1825acbb6335e81e4d184b2b01c8bebdc85d1f28946" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000488de689ce9af1e94be6a2089c8a8b1253ffdbb6c8e9c86249ba220001a4ad3b80c4998e54842f413b9edb1825acbb6335e81e4d184b2b01c8bebdc85d1f28946", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEiN5onOmvHpS+aiCJyKixJT/9u2yOnIYk\nm6IgABpK07gMSZjlSEL0E7ntsYJay7YzXoHk0YSysByL69yF0fKJRg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 177, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca4f4ed29828c4894b5a17a0c6db3c256c2221449228a92dff7d76ca8206dd8dd", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "iN5onOmvHpS-aiCJyKixJT_9u2yOnIYkm6IgABpK07g", + "y": "DEmY5UhC9BO57bGCWsu2M16B5NGEsrAci-vchdHyiUY", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04fea2d31f70f90d5fb3e00e186ac42ab3c1615cee714e0b4e1131b3d4d8225bf7b037a18df2ac15343f30f74067ddf29e817d5f77f8dce05714da59c094f0cda9", + "wx": "00fea2d31f70f90d5fb3e00e186ac42ab3c1615cee714e0b4e1131b3d4d8225bf7", + "wy": "00b037a18df2ac15343f30f74067ddf29e817d5f77f8dce05714da59c094f0cda9" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004fea2d31f70f90d5fb3e00e186ac42ab3c1615cee714e0b4e1131b3d4d8225bf7b037a18df2ac15343f30f74067ddf29e817d5f77f8dce05714da59c094f0cda9", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE/qLTH3D5DV+z4A4YasQqs8FhXO5xTgtO\nETGz1NgiW/ewN6GN8qwVND8w90Bn3fKegX1fd/jc4FcU2lnAlPDNqQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 178, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc694c146244a5ad0bd0636d9e12bc9e09e60e68b90d0b5e6c5dddd0cb694d8799", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "_qLTH3D5DV-z4A4YasQqs8FhXO5xTgtOETGz1NgiW_c", + "y": "sDehjfKsFTQ_MPdAZ93ynoF9X3f43OBXFNpZwJTwzak", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "047258911e3d423349166479dbe0b8341af7fbd03d0a7e10edccb36b6ceea5a3db17ac2b8992791128fa3b96dc2fbd4ca3bfa782ef2832fc6656943db18e7346b0", + "wx": "7258911e3d423349166479dbe0b8341af7fbd03d0a7e10edccb36b6ceea5a3db", + "wy": "17ac2b8992791128fa3b96dc2fbd4ca3bfa782ef2832fc6656943db18e7346b0" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200047258911e3d423349166479dbe0b8341af7fbd03d0a7e10edccb36b6ceea5a3db17ac2b8992791128fa3b96dc2fbd4ca3bfa782ef2832fc6656943db18e7346b0", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEcliRHj1CM0kWZHnb4Lg0Gvf70D0KfhDt\nzLNrbO6lo9sXrCuJknkRKPo7ltwvvUyjv6eC7ygy/GZWlD2xjnNGsA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 179, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3d7f487c07bfc5f30846938a3dcef696444707cf9677254a92b06c63ab867d22", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "cliRHj1CM0kWZHnb4Lg0Gvf70D0KfhDtzLNrbO6lo9s", + "y": "F6wriZJ5ESj6O5bcL71Mo7-ngu8oMvxmVpQ9sY5zRrA", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "044f28461dea64474d6bb34d1499c97d37b9e95633df1ceeeaacd45016c98b3914c8818810b8cc06ddb40e8a1261c528faa589455d5a6df93b77bc5e0e493c7470", + "wx": "4f28461dea64474d6bb34d1499c97d37b9e95633df1ceeeaacd45016c98b3914", + "wy": "00c8818810b8cc06ddb40e8a1261c528faa589455d5a6df93b77bc5e0e493c7470" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200044f28461dea64474d6bb34d1499c97d37b9e95633df1ceeeaacd45016c98b3914c8818810b8cc06ddb40e8a1261c528faa589455d5a6df93b77bc5e0e493c7470", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAETyhGHepkR01rs00Umcl9N7npVjPfHO7q\nrNRQFsmLORTIgYgQuMwG3bQOihJhxSj6pYlFXVpt+Tt3vF4OSTx0cA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 180, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6c7648fc0fbf8a06adb8b839f97b4ff7a800f11b1e37c593b261394599792ba4", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "TyhGHepkR01rs00Umcl9N7npVjPfHO7qrNRQFsmLORQ", + "y": "yIGIELjMBt20DooSYcUo-qWJRV1abfk7d7xeDkk8dHA", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0474f2a814fb5d8eca91a69b5e60712732b3937de32829be974ed7b68c5c2f5d66eff0f07c56f987a657f42196205f588c0f1d96fd8a63a5f238b48f478788fe3b", + "wx": "74f2a814fb5d8eca91a69b5e60712732b3937de32829be974ed7b68c5c2f5d66", + "wy": "00eff0f07c56f987a657f42196205f588c0f1d96fd8a63a5f238b48f478788fe3b" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000474f2a814fb5d8eca91a69b5e60712732b3937de32829be974ed7b68c5c2f5d66eff0f07c56f987a657f42196205f588c0f1d96fd8a63a5f238b48f478788fe3b", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEdPKoFPtdjsqRppteYHEnMrOTfeMoKb6X\nTte2jFwvXWbv8PB8VvmHplf0IZYgX1iMDx2W/YpjpfI4tI9Hh4j+Ow==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 181, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc9be363a286f23f6322c205449d320baad417953ecb70f6214e90d49d7d1f26a8", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "dPKoFPtdjsqRppteYHEnMrOTfeMoKb6XTte2jFwvXWY", + "y": "7_DwfFb5h6ZX9CGWIF9YjA8dlv2KY6XyOLSPR4eI_js", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04195b51a7cc4a21b8274a70a90de779814c3c8ca358328208c09a29f336b82d6ab2416b7c92fffdc29c3b1282dd2a77a4d04df7f7452047393d849989c5cee9ad", + "wx": "195b51a7cc4a21b8274a70a90de779814c3c8ca358328208c09a29f336b82d6a", + "wy": "00b2416b7c92fffdc29c3b1282dd2a77a4d04df7f7452047393d849989c5cee9ad" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004195b51a7cc4a21b8274a70a90de779814c3c8ca358328208c09a29f336b82d6ab2416b7c92fffdc29c3b1282dd2a77a4d04df7f7452047393d849989c5cee9ad", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEGVtRp8xKIbgnSnCpDed5gUw8jKNYMoII\nwJop8za4LWqyQWt8kv/9wpw7EoLdKnek0E3390UgRzk9hJmJxc7prQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 182, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc29798c5c45bdf58b4a7b2fdc2c46ab4af1218c7eeb9f0f27a88f1267674de3b0", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "GVtRp8xKIbgnSnCpDed5gUw8jKNYMoIIwJop8za4LWo", + "y": "skFrfJL__cKcOxKC3Sp3pNBN9_dFIEc5PYSZicXO6a0", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04622fc74732034bec2ddf3bc16d34b3d1f7a327dd2a8c19bab4bb4fe3a24b58aa736b2f2fae76f4dfaecc9096333b01328d51eb3fda9c9227e90d0b449983c4f0", + "wx": "622fc74732034bec2ddf3bc16d34b3d1f7a327dd2a8c19bab4bb4fe3a24b58aa", + "wy": "736b2f2fae76f4dfaecc9096333b01328d51eb3fda9c9227e90d0b449983c4f0" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004622fc74732034bec2ddf3bc16d34b3d1f7a327dd2a8c19bab4bb4fe3a24b58aa736b2f2fae76f4dfaecc9096333b01328d51eb3fda9c9227e90d0b449983c4f0", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYi/HRzIDS+wt3zvBbTSz0fejJ90qjBm6\ntLtP46JLWKpzay8vrnb0367MkJYzOwEyjVHrP9qckifpDQtEmYPE8A==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 183, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0b70f22ca2bb3cefadca1a5711fa3a59f4695385eb5aedf3495d0b6d00f8fd85", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "Yi_HRzIDS-wt3zvBbTSz0fejJ90qjBm6tLtP46JLWKo", + "y": "c2svL6529N-uzJCWMzsBMo1R6z_anJIn6Q0LRJmDxPA", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "041f7f85caf2d7550e7af9b65023ebb4dce3450311692309db269969b834b611c70827f45b78020ecbbaf484fdd5bfaae6870f1184c21581baf6ef82bd7b530f93", + "wx": "1f7f85caf2d7550e7af9b65023ebb4dce3450311692309db269969b834b611c7", + "wy": "0827f45b78020ecbbaf484fdd5bfaae6870f1184c21581baf6ef82bd7b530f93" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200041f7f85caf2d7550e7af9b65023ebb4dce3450311692309db269969b834b611c70827f45b78020ecbbaf484fdd5bfaae6870f1184c21581baf6ef82bd7b530f93", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEH3+FyvLXVQ56+bZQI+u03ONFAxFpIwnb\nJplpuDS2EccIJ/RbeAIOy7r0hP3Vv6rmhw8RhMIVgbr274K9e1MPkw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 184, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc16e1e459457679df5b9434ae23f474b3e8d2a70bd6b5dbe692ba16da01f1fb0a", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "H3-FyvLXVQ56-bZQI-u03ONFAxFpIwnbJplpuDS2Ecc", + "y": "CCf0W3gCDsu69IT91b-q5ocPEYTCFYG69u-CvXtTD5M", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0449c197dc80ad1da47a4342b93893e8e1fb0bb94fc33a83e783c00b24c781377aefc20da92bac762951f72474becc734d4cc22ba81b895e282fdac4df7af0f37d", + "wx": "49c197dc80ad1da47a4342b93893e8e1fb0bb94fc33a83e783c00b24c781377a", + "wy": "00efc20da92bac762951f72474becc734d4cc22ba81b895e282fdac4df7af0f37d" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000449c197dc80ad1da47a4342b93893e8e1fb0bb94fc33a83e783c00b24c781377aefc20da92bac762951f72474becc734d4cc22ba81b895e282fdac4df7af0f37d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEScGX3ICtHaR6Q0K5OJPo4fsLuU/DOoPn\ng8ALJMeBN3rvwg2pK6x2KVH3JHS+zHNNTMIrqBuJXigv2sTfevDzfQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 185, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc2252d685e831b6cf095e4f0535eeaf0ddd3bfa91c210c9d9dc17224702eaf88f", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "ScGX3ICtHaR6Q0K5OJPo4fsLuU_DOoPng8ALJMeBN3o", + "y": "78INqSusdilR9yR0vsxzTUzCK6gbiV4oL9rE33rw830", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04d8cb68517b616a56400aa3868635e54b6f699598a2f6167757654980baf6acbe7ec8cf449c849aa03461a30efada41453c57c6e6fbc93bbc6fa49ada6dc0555c", + "wx": "00d8cb68517b616a56400aa3868635e54b6f699598a2f6167757654980baf6acbe", + "wy": "7ec8cf449c849aa03461a30efada41453c57c6e6fbc93bbc6fa49ada6dc0555c" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004d8cb68517b616a56400aa3868635e54b6f699598a2f6167757654980baf6acbe7ec8cf449c849aa03461a30efada41453c57c6e6fbc93bbc6fa49ada6dc0555c", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE2MtoUXthalZACqOGhjXlS29plZii9hZ3\nV2VJgLr2rL5+yM9EnISaoDRhow762kFFPFfG5vvJO7xvpJrabcBVXA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 186, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc75135abd7c425b60371a477f09ce0f274f64a8c6b061a07b5d63e93c65046c53", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "2MtoUXthalZACqOGhjXlS29plZii9hZ3V2VJgLr2rL4", + "y": "fsjPRJyEmqA0YaMO-tpBRTxXxub7yTu8b6Sa2m3AVVw", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04030713fb63f2aa6fe2cadf1b20efc259c77445dafa87dac398b84065ca347df3b227818de1a39b589cb071d83e5317cccdc2338e51e312fe31d8dc34a4801750", + "wx": "030713fb63f2aa6fe2cadf1b20efc259c77445dafa87dac398b84065ca347df3", + "wy": "00b227818de1a39b589cb071d83e5317cccdc2338e51e312fe31d8dc34a4801750" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004030713fb63f2aa6fe2cadf1b20efc259c77445dafa87dac398b84065ca347df3b227818de1a39b589cb071d83e5317cccdc2338e51e312fe31d8dc34a4801750", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAwcT+2Pyqm/iyt8bIO/CWcd0Rdr6h9rD\nmLhAZco0ffOyJ4GN4aObWJywcdg+UxfMzcIzjlHjEv4x2Nw0pIAXUA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 187, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcd55555555555555555555555555555547c74934474db157d2a8c3f088aced62a", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "AwcT-2Pyqm_iyt8bIO_CWcd0Rdr6h9rDmLhAZco0ffM", + "y": "sieBjeGjm1icsHHYPlMXzM3CM45R4xL-MdjcNKSAF1A", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04babb3677b0955802d8e929a41355640eaf1ea1353f8a771331c4946e3480afa7252f196c87ed3d2a59d3b1b559137fed0013fecefc19fb5a92682b9bca51b950", + "wx": "00babb3677b0955802d8e929a41355640eaf1ea1353f8a771331c4946e3480afa7", + "wy": "252f196c87ed3d2a59d3b1b559137fed0013fecefc19fb5a92682b9bca51b950" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004babb3677b0955802d8e929a41355640eaf1ea1353f8a771331c4946e3480afa7252f196c87ed3d2a59d3b1b559137fed0013fecefc19fb5a92682b9bca51b950", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEurs2d7CVWALY6SmkE1VkDq8eoTU/incT\nMcSUbjSAr6clLxlsh+09KlnTsbVZE3/tABP+zvwZ+1qSaCubylG5UA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 188, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcc1777c8853938e536213c02464a936000ba1e21c0fc62075d46c624e23b52f31", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "urs2d7CVWALY6SmkE1VkDq8eoTU_incTMcSUbjSAr6c", + "y": "JS8ZbIftPSpZ07G1WRN_7QAT_s78Gftakmgrm8pRuVA", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "041aab2018793471111a8a0e9b143fde02fc95920796d3a63de329b424396fba60bbe4130705174792441b318d3aa31dfe8577821e9b446ec573d272e036c4ebe9", + "wx": "1aab2018793471111a8a0e9b143fde02fc95920796d3a63de329b424396fba60", + "wy": "00bbe4130705174792441b318d3aa31dfe8577821e9b446ec573d272e036c4ebe9" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200041aab2018793471111a8a0e9b143fde02fc95920796d3a63de329b424396fba60bbe4130705174792441b318d3aa31dfe8577821e9b446ec573d272e036c4ebe9", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEGqsgGHk0cREaig6bFD/eAvyVkgeW06Y9\n4ym0JDlvumC75BMHBRdHkkQbMY06ox3+hXeCHptEbsVz0nLgNsTr6Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 189, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc30bbb794db588363b40679f6c182a50d3ce9679acdd3ffbe36d7813dacbdc818", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "GqsgGHk0cREaig6bFD_eAvyVkgeW06Y94ym0JDlvumA", + "y": "u-QTBwUXR5JEGzGNOqMd_oV3gh6bRG7Fc9Jy4DbE6-k", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "048cb0b909499c83ea806cd885b1dd467a0119f06a88a0276eb0cfda274535a8ff47b5428833bc3f2c8bf9d9041158cf33718a69961cd01729bc0011d1e586ab75", + "wx": "008cb0b909499c83ea806cd885b1dd467a0119f06a88a0276eb0cfda274535a8ff", + "wy": "47b5428833bc3f2c8bf9d9041158cf33718a69961cd01729bc0011d1e586ab75" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200048cb0b909499c83ea806cd885b1dd467a0119f06a88a0276eb0cfda274535a8ff47b5428833bc3f2c8bf9d9041158cf33718a69961cd01729bc0011d1e586ab75", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjLC5CUmcg+qAbNiFsd1GegEZ8GqIoCdu\nsM/aJ0U1qP9HtUKIM7w/LIv52QQRWM8zcYpplhzQFym8ABHR5YardQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 190, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc2c37fd995622c4fb7fffffffffffffffc7cee745110cb45ab558ed7c90c15a2f", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "jLC5CUmcg-qAbNiFsd1GegEZ8GqIoCdusM_aJ0U1qP8", + "y": "R7VCiDO8PyyL-dkEEVjPM3GKaZYc0BcpvAAR0eWGq3U", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "048f03cf1a42272bb1532723093f72e6feeac85e1700e9fbe9a6a2dd642d74bf5d3b89a7189dad8cf75fc22f6f158aa27f9c2ca00daca785be3358f2bda3862ca0", + "wx": "008f03cf1a42272bb1532723093f72e6feeac85e1700e9fbe9a6a2dd642d74bf5d", + "wy": "3b89a7189dad8cf75fc22f6f158aa27f9c2ca00daca785be3358f2bda3862ca0" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200048f03cf1a42272bb1532723093f72e6feeac85e1700e9fbe9a6a2dd642d74bf5d3b89a7189dad8cf75fc22f6f158aa27f9c2ca00daca785be3358f2bda3862ca0", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjwPPGkInK7FTJyMJP3Lm/urIXhcA6fvp\npqLdZC10v107iacYna2M91/CL28ViqJ/nCygDaynhb4zWPK9o4YsoA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 191, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc7fd995622c4fb7ffffffffffffffffff5d883ffab5b32652ccdcaa290fccb97d", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "jwPPGkInK7FTJyMJP3Lm_urIXhcA6fvppqLdZC10v10", + "y": "O4mnGJ2tjPdfwi9vFYqif5wsoA2sp4W-M1jyvaOGLKA", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0444de3b9c7a57a8c9e820952753421e7d987bb3d79f71f013805c897e018f8acea2460758c8f98d3fdce121a943659e372c326fff2e5fc2ae7fa3f79daae13c12", + "wx": "44de3b9c7a57a8c9e820952753421e7d987bb3d79f71f013805c897e018f8ace", + "wy": "00a2460758c8f98d3fdce121a943659e372c326fff2e5fc2ae7fa3f79daae13c12" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000444de3b9c7a57a8c9e820952753421e7d987bb3d79f71f013805c897e018f8acea2460758c8f98d3fdce121a943659e372c326fff2e5fc2ae7fa3f79daae13c12", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERN47nHpXqMnoIJUnU0IefZh7s9efcfAT\ngFyJfgGPis6iRgdYyPmNP9zhIalDZZ43LDJv/y5fwq5/o/edquE8Eg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 192, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcffb32ac4589f6ffffffffffffffffffebb107ff56b664ca599b954521f9972fa", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "RN47nHpXqMnoIJUnU0IefZh7s9efcfATgFyJfgGPis4", + "y": "okYHWMj5jT_c4SGpQ2WeNywyb_8uX8Kuf6P3narhPBI", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "046fb8b2b48e33031268ad6a517484dc8839ea90f6669ea0c7ac3233e2ac31394a0ac8bbe7f73c2ff4df9978727ac1dfc2fd58647d20f31f99105316b64671f204", + "wx": "6fb8b2b48e33031268ad6a517484dc8839ea90f6669ea0c7ac3233e2ac31394a", + "wy": "0ac8bbe7f73c2ff4df9978727ac1dfc2fd58647d20f31f99105316b64671f204" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200046fb8b2b48e33031268ad6a517484dc8839ea90f6669ea0c7ac3233e2ac31394a0ac8bbe7f73c2ff4df9978727ac1dfc2fd58647d20f31f99105316b64671f204", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEb7iytI4zAxJorWpRdITciDnqkPZmnqDH\nrDIz4qwxOUoKyLvn9zwv9N+ZeHJ6wd/C/VhkfSDzH5kQUxa2RnHyBA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 193, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc5622c4fb7fffffffffffffffffffffff928a8f1c7ac7bec1808b9f61c01ec327", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "b7iytI4zAxJorWpRdITciDnqkPZmnqDHrDIz4qwxOUo", + "y": "Csi75_c8L_TfmXhyesHfwv1YZH0g8x-ZEFMWtkZx8gQ", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04bea71122a048693e905ff602b3cf9dd18af69b9fc9d8431d2b1dd26b942c95e6f43c7b8b95eb62082c12db9dbda7fe38e45cbe4a4886907fb81bdb0c5ea9246c", + "wx": "00bea71122a048693e905ff602b3cf9dd18af69b9fc9d8431d2b1dd26b942c95e6", + "wy": "00f43c7b8b95eb62082c12db9dbda7fe38e45cbe4a4886907fb81bdb0c5ea9246c" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004bea71122a048693e905ff602b3cf9dd18af69b9fc9d8431d2b1dd26b942c95e6f43c7b8b95eb62082c12db9dbda7fe38e45cbe4a4886907fb81bdb0c5ea9246c", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEvqcRIqBIaT6QX/YCs8+d0Yr2m5/J2EMd\nKx3Sa5Qsleb0PHuLletiCCwS2529p/445Fy+SkiGkH+4G9sMXqkkbA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 194, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc44104104104104104104104104104103b87853fd3b7d3f8e175125b4382f25ed", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "vqcRIqBIaT6QX_YCs8-d0Yr2m5_J2EMdKx3Sa5QsleY", + "y": "9Dx7i5XrYggsEtudvaf-OORcvkpIhpB_uBvbDF6pJGw", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04da918c731ba06a20cb94ef33b778e981a404a305f1941fe33666b45b03353156e2bb2694f575b45183be78e5c9b5210bf3bf488fd4c8294516d89572ca4f5391", + "wx": "00da918c731ba06a20cb94ef33b778e981a404a305f1941fe33666b45b03353156", + "wy": "00e2bb2694f575b45183be78e5c9b5210bf3bf488fd4c8294516d89572ca4f5391" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004da918c731ba06a20cb94ef33b778e981a404a305f1941fe33666b45b03353156e2bb2694f575b45183be78e5c9b5210bf3bf488fd4c8294516d89572ca4f5391", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE2pGMcxugaiDLlO8zt3jpgaQEowXxlB/j\nNma0WwM1MVbiuyaU9XW0UYO+eOXJtSEL879Ij9TIKUUW2JVyyk9TkQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 195, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc2739ce739ce739ce739ce739ce739ce705560298d1f2f08dc419ac273a5b54d9", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "2pGMcxugaiDLlO8zt3jpgaQEowXxlB_jNma0WwM1MVY", + "y": "4rsmlPV1tFGDvnjlybUhC_O_SI_UyClFFtiVcspPU5E", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "043007e92c3937dade7964dfa35b0eff031f7eb02aed0a0314411106cdeb70fe3d5a7546fc0552997b20e3d6f413e75e2cb66e116322697114b79bac734bfc4dc5", + "wx": "3007e92c3937dade7964dfa35b0eff031f7eb02aed0a0314411106cdeb70fe3d", + "wy": "5a7546fc0552997b20e3d6f413e75e2cb66e116322697114b79bac734bfc4dc5" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200043007e92c3937dade7964dfa35b0eff031f7eb02aed0a0314411106cdeb70fe3d5a7546fc0552997b20e3d6f413e75e2cb66e116322697114b79bac734bfc4dc5", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMAfpLDk32t55ZN+jWw7/Ax9+sCrtCgMU\nQREGzetw/j1adUb8BVKZeyDj1vQT514stm4RYyJpcRS3m6xzS/xNxQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 196, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcb777777777777777777777777777777688e6a1fe808a97a348671222ff16b863", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "MAfpLDk32t55ZN-jWw7_Ax9-sCrtCgMUQREGzetw_j0", + "y": "WnVG_AVSmXsg49b0E-deLLZuEWMiaXEUt5usc0v8TcU", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0460e734ef5624d3cbf0ddd375011bd663d6d6aebc644eb599fdf98dbdcd18ce9bd2d90b3ac31f139af832cccf6ccbbb2c6ea11fa97370dc9906da474d7d8a7567", + "wx": "60e734ef5624d3cbf0ddd375011bd663d6d6aebc644eb599fdf98dbdcd18ce9b", + "wy": "00d2d90b3ac31f139af832cccf6ccbbb2c6ea11fa97370dc9906da474d7d8a7567" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000460e734ef5624d3cbf0ddd375011bd663d6d6aebc644eb599fdf98dbdcd18ce9bd2d90b3ac31f139af832cccf6ccbbb2c6ea11fa97370dc9906da474d7d8a7567", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYOc071Yk08vw3dN1ARvWY9bWrrxkTrWZ\n/fmNvc0YzpvS2Qs6wx8TmvgyzM9sy7ssbqEfqXNw3JkG2kdNfYp1Zw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 197, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc6492492492492492492492492492492406dd3a19b8d5fb875235963c593bd2d3", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "YOc071Yk08vw3dN1ARvWY9bWrrxkTrWZ_fmNvc0Yzps", + "y": "0tkLOsMfE5r4MszPbMu7LG6hH6lzcNyZBtpHTX2KdWc", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0485a900e97858f693c0b7dfa261e380dad6ea046d1f65ddeeedd5f7d8af0ba33769744d15add4f6c0bc3b0da2aec93b34cb8c65f9340ddf74e7b0009eeeccce3c", + "wx": "0085a900e97858f693c0b7dfa261e380dad6ea046d1f65ddeeedd5f7d8af0ba337", + "wy": "69744d15add4f6c0bc3b0da2aec93b34cb8c65f9340ddf74e7b0009eeeccce3c" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000485a900e97858f693c0b7dfa261e380dad6ea046d1f65ddeeedd5f7d8af0ba33769744d15add4f6c0bc3b0da2aec93b34cb8c65f9340ddf74e7b0009eeeccce3c", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEhakA6XhY9pPAt9+iYeOA2tbqBG0fZd3u\n7dX32K8LozdpdE0VrdT2wLw7DaKuyTs0y4xl+TQN33TnsACe7szOPA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 198, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc955555555555555555555555555555547c74934474db157d2a8c3f088aced62c", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "hakA6XhY9pPAt9-iYeOA2tbqBG0fZd3u7dX32K8Lozc", + "y": "aXRNFa3U9sC8Ow2irsk7NMuMZfk0Dd9057AAnu7Mzjw", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0438066f75d88efc4c93de36f49e037b234cc18b1de5608750a62cab0345401046a3e84bed8cfcb819ef4d550444f2ce4b651766b69e2e2901f88836ff90034fed", + "wx": "38066f75d88efc4c93de36f49e037b234cc18b1de5608750a62cab0345401046", + "wy": "00a3e84bed8cfcb819ef4d550444f2ce4b651766b69e2e2901f88836ff90034fed" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000438066f75d88efc4c93de36f49e037b234cc18b1de5608750a62cab0345401046a3e84bed8cfcb819ef4d550444f2ce4b651766b69e2e2901f88836ff90034fed", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOAZvddiO/EyT3jb0ngN7I0zBix3lYIdQ\npiyrA0VAEEaj6EvtjPy4Ge9NVQRE8s5LZRdmtp4uKQH4iDb/kANP7Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 199, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3e3a49a23a6d8abe95461f8445676b17", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "OAZvddiO_EyT3jb0ngN7I0zBix3lYIdQpiyrA0VAEEY", + "y": "o-hL7Yz8uBnvTVUERPLOS2UXZraeLikB-Ig2_5ADT-0", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0498f68177dc95c1b4cbfa5245488ca523a7d5629470d035d621a443c72f39aabfa33d29546fa1c648f2c7d5ccf70cf1ce4ab79b5db1ac059dbecd068dbdff1b89", + "wx": "0098f68177dc95c1b4cbfa5245488ca523a7d5629470d035d621a443c72f39aabf", + "wy": "00a33d29546fa1c648f2c7d5ccf70cf1ce4ab79b5db1ac059dbecd068dbdff1b89" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000498f68177dc95c1b4cbfa5245488ca523a7d5629470d035d621a443c72f39aabfa33d29546fa1c648f2c7d5ccf70cf1ce4ab79b5db1ac059dbecd068dbdff1b89", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEmPaBd9yVwbTL+lJFSIylI6fVYpRw0DXW\nIaRDxy85qr+jPSlUb6HGSPLH1cz3DPHOSrebXbGsBZ2+zQaNvf8biQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 200, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcbffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364143", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "mPaBd9yVwbTL-lJFSIylI6fVYpRw0DXWIaRDxy85qr8", + "y": "oz0pVG-hxkjyx9XM9wzxzkq3m12xrAWdvs0Gjb3_G4k", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "045c2bbfa23c9b9ad07f038aa89b4930bf267d9401e4255de9e8da0a5078ec8277e3e882a31d5e6a379e0793983ccded39b95c4353ab2ff01ea5369ba47b0c3191", + "wx": "5c2bbfa23c9b9ad07f038aa89b4930bf267d9401e4255de9e8da0a5078ec8277", + "wy": "00e3e882a31d5e6a379e0793983ccded39b95c4353ab2ff01ea5369ba47b0c3191" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200045c2bbfa23c9b9ad07f038aa89b4930bf267d9401e4255de9e8da0a5078ec8277e3e882a31d5e6a379e0793983ccded39b95c4353ab2ff01ea5369ba47b0c3191", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEXCu/ojybmtB/A4qom0kwvyZ9lAHkJV3p\n6NoKUHjsgnfj6IKjHV5qN54Hk5g8ze05uVxDU6sv8B6lNpukewwxkQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 201, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc185ddbca6dac41b1da033cfb60c152869e74b3cd66e9ffdf1b6bc09ed65ee40c", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "XCu_ojybmtB_A4qom0kwvyZ9lAHkJV3p6NoKUHjsgnc", + "y": "4-iCox1eajeeB5OYPM3tOblcQ1OrL_AepTabpHsMMZE", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a3853547808298448edb5e701ade84cd5fb1ac9567ba5e8fb68a6b933ec4b5cc84cc", + "wx": "2ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385", + "wy": "3547808298448edb5e701ade84cd5fb1ac9567ba5e8fb68a6b933ec4b5cc84cc" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a3853547808298448edb5e701ade84cd5fb1ac9567ba5e8fb68a6b933ec4b5cc84cc", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELqcTNDIznGnSf5smcoG9Ld1fGdYzjUAK\nBc02R7FXo4U1R4CCmESO215wGt6EzV+xrJVnul6Ptoprkz7EtcyEzA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 202, + "comment": "point duplication during verification", + "flags": [ + "PointDuplication" + ], + "msg": "313233343030", + "sig": "32b0d10d8d0e04bc8d4d064d270699e87cffc9b49c5c20730e1c26f6105ddcdad612c2984c2afa416aa7f2882a486d4a8426cb6cfc91ed5b737278f9fca8be68", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "LqcTNDIznGnSf5smcoG9Ld1fGdYzjUAKBc02R7FXo4U", + "y": "NUeAgphEjttecBrehM1fsayVZ7pej7aKa5M-xLXMhMw", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385cab87f7d67bb7124a18fe5217b32a04e536a9845a1704975946cc13a4a337763", + "wx": "2ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385", + "wy": "00cab87f7d67bb7124a18fe5217b32a04e536a9845a1704975946cc13a4a337763" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385cab87f7d67bb7124a18fe5217b32a04e536a9845a1704975946cc13a4a337763", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELqcTNDIznGnSf5smcoG9Ld1fGdYzjUAK\nBc02R7FXo4XKuH99Z7txJKGP5SF7MqBOU2qYRaFwSXWUbME6SjN3Yw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 203, + "comment": "duplication bug", + "flags": [ + "PointDuplication" + ], + "msg": "313233343030", + "sig": "32b0d10d8d0e04bc8d4d064d270699e87cffc9b49c5c20730e1c26f6105ddcdad612c2984c2afa416aa7f2882a486d4a8426cb6cfc91ed5b737278f9fca8be68", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "LqcTNDIznGnSf5smcoG9Ld1fGdYzjUAKBc02R7FXo4U", + "y": "yrh_fWe7cSShj-UhezKgTlNqmEWhcEl1lGzBOkozd2M", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "048aa2c64fa9c6437563abfbcbd00b2048d48c18c152a2a6f49036de7647ebe82e1ce64387995c68a060fa3bc0399b05cc06eec7d598f75041a4917e692b7f51ff", + "wx": "008aa2c64fa9c6437563abfbcbd00b2048d48c18c152a2a6f49036de7647ebe82e", + "wy": "1ce64387995c68a060fa3bc0399b05cc06eec7d598f75041a4917e692b7f51ff" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200048aa2c64fa9c6437563abfbcbd00b2048d48c18c152a2a6f49036de7647ebe82e1ce64387995c68a060fa3bc0399b05cc06eec7d598f75041a4917e692b7f51ff", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEiqLGT6nGQ3Vjq/vL0AsgSNSMGMFSoqb0\nkDbedkfr6C4c5kOHmVxooGD6O8A5mwXMBu7H1Zj3UEGkkX5pK39R/w==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 204, + "comment": "comparison with point at infinity ", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c033333333333333333333333333333332f222f8faefdb533f265d461c29a47373", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "iqLGT6nGQ3Vjq_vL0AsgSNSMGMFSoqb0kDbedkfr6C4", + "y": "HOZDh5lcaKBg-jvAOZsFzAbux9WY91BBpJF-aSt_Uf8", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04391427ff7ee78013c14aec7d96a8a062209298a783835e94fd6549d502fff71fdd6624ec343ad9fcf4d9872181e59f842f9ba4cccae09a6c0972fb6ac6b4c6bd", + "wx": "391427ff7ee78013c14aec7d96a8a062209298a783835e94fd6549d502fff71f", + "wy": "00dd6624ec343ad9fcf4d9872181e59f842f9ba4cccae09a6c0972fb6ac6b4c6bd" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004391427ff7ee78013c14aec7d96a8a062209298a783835e94fd6549d502fff71fdd6624ec343ad9fcf4d9872181e59f842f9ba4cccae09a6c0972fb6ac6b4c6bd", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEORQn/37ngBPBSux9lqigYiCSmKeDg16U\n/WVJ1QL/9x/dZiTsNDrZ/PTZhyGB5Z+EL5ukzMrgmmwJcvtqxrTGvQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 205, + "comment": "extreme value for k and edgecase s", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee555555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "ORQn_37ngBPBSux9lqigYiCSmKeDg16U_WVJ1QL_9x8", + "y": "3WYk7DQ62fz02YchgeWfhC-bpMzK4JpsCXL7asa0xr0", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04e762b8a219b4f180219cc7a9059245e4961bd191c03899789c7a34b89e8c138ec1533ef0419bb7376e0bfde9319d10a06968791d9ea0eed9c1ce6345aed9759e", + "wx": "00e762b8a219b4f180219cc7a9059245e4961bd191c03899789c7a34b89e8c138e", + "wy": "00c1533ef0419bb7376e0bfde9319d10a06968791d9ea0eed9c1ce6345aed9759e" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004e762b8a219b4f180219cc7a9059245e4961bd191c03899789c7a34b89e8c138ec1533ef0419bb7376e0bfde9319d10a06968791d9ea0eed9c1ce6345aed9759e", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE52K4ohm08YAhnMepBZJF5JYb0ZHAOJl4\nnHo0uJ6ME47BUz7wQZu3N24L/ekxnRCgaWh5HZ6g7tnBzmNFrtl1ng==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 206, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5b6db6db6db6db6db6db6db6db6db6db5f30f30127d33e02aad96438927022e9c", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "52K4ohm08YAhnMepBZJF5JYb0ZHAOJl4nHo0uJ6ME44", + "y": "wVM-8EGbtzduC_3pMZ0QoGloeR2eoO7Zwc5jRa7ZdZ4", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "049aedb0d281db164e130000c5697fae0f305ef848be6fffb43ac593fbb950e952fa6f633359bdcd82b56b0b9f965b037789d46b9a8141b791b2aefa713f96c175", + "wx": "009aedb0d281db164e130000c5697fae0f305ef848be6fffb43ac593fbb950e952", + "wy": "00fa6f633359bdcd82b56b0b9f965b037789d46b9a8141b791b2aefa713f96c175" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200049aedb0d281db164e130000c5697fae0f305ef848be6fffb43ac593fbb950e952fa6f633359bdcd82b56b0b9f965b037789d46b9a8141b791b2aefa713f96c175", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEmu2w0oHbFk4TAADFaX+uDzBe+Ei+b/+0\nOsWT+7lQ6VL6b2MzWb3NgrVrC5+WWwN3idRrmoFBt5GyrvpxP5bBdQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 207, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee599999999999999999999999999999998d668eaf0cf91f9bd7317d2547ced5a5a", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "mu2w0oHbFk4TAADFaX-uDzBe-Ei-b_-0OsWT-7lQ6VI", + "y": "-m9jM1m9zYK1awufllsDd4nUa5qBQbeRsq76cT-WwXU", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "048ad445db62816260e4e687fd1884e48b9fc0636d031547d63315e792e19bfaee1de64f99d5f1cd8b6ec9cb0f787a654ae86993ba3db1008ef43cff0684cb22bd", + "wx": "008ad445db62816260e4e687fd1884e48b9fc0636d031547d63315e792e19bfaee", + "wy": "1de64f99d5f1cd8b6ec9cb0f787a654ae86993ba3db1008ef43cff0684cb22bd" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200048ad445db62816260e4e687fd1884e48b9fc0636d031547d63315e792e19bfaee1de64f99d5f1cd8b6ec9cb0f787a654ae86993ba3db1008ef43cff0684cb22bd", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEitRF22KBYmDk5of9GITki5/AY20DFUfW\nMxXnkuGb+u4d5k+Z1fHNi27Jyw94emVK6GmTuj2xAI70PP8GhMsivQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 208, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee566666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "itRF22KBYmDk5of9GITki5_AY20DFUfWMxXnkuGb-u4", + "y": "HeZPmdXxzYtuycsPeHplSuhpk7o9sQCO9Dz_BoTLIr0", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "041f5799c95be89063b24f26e40cb928c1a868a76fb0094607e8043db409c91c32e75724e813a4191e3a839007f08e2e897388b06d4a00de6de60e536d91fab566", + "wx": "1f5799c95be89063b24f26e40cb928c1a868a76fb0094607e8043db409c91c32", + "wy": "00e75724e813a4191e3a839007f08e2e897388b06d4a00de6de60e536d91fab566" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200041f5799c95be89063b24f26e40cb928c1a868a76fb0094607e8043db409c91c32e75724e813a4191e3a839007f08e2e897388b06d4a00de6de60e536d91fab566", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEH1eZyVvokGOyTybkDLkowahop2+wCUYH\n6AQ9tAnJHDLnVyToE6QZHjqDkAfwji6Jc4iwbUoA3m3mDlNtkfq1Zg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 209, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee549249249249249249249249249249248c79facd43214c011123c1b03a93412a5", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "H1eZyVvokGOyTybkDLkowahop2-wCUYH6AQ9tAnJHDI", + "y": "51ck6BOkGR46g5AH8I4uiXOIsG1KAN5t5g5TbZH6tWY", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04a3331a4e1b4223ec2c027edd482c928a14ed358d93f1d4217d39abf69fcb5ccc28d684d2aaabcd6383775caa6239de26d4c6937bb603ecb4196082f4cffd509d", + "wx": "00a3331a4e1b4223ec2c027edd482c928a14ed358d93f1d4217d39abf69fcb5ccc", + "wy": "28d684d2aaabcd6383775caa6239de26d4c6937bb603ecb4196082f4cffd509d" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004a3331a4e1b4223ec2c027edd482c928a14ed358d93f1d4217d39abf69fcb5ccc28d684d2aaabcd6383775caa6239de26d4c6937bb603ecb4196082f4cffd509d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEozMaThtCI+wsAn7dSCySihTtNY2T8dQh\nfTmr9p/LXMwo1oTSqqvNY4N3XKpiOd4m1MaTe7YD7LQZYIL0z/1QnQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 210, + "comment": "extreme value for k", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee50eb10e5ab95f2f275348d82ad2e4d7949c8193800d8c9c75df58e343f0ebba7b", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "ozMaThtCI-wsAn7dSCySihTtNY2T8dQhfTmr9p_LXMw", + "y": "KNaE0qqrzWODd1yqYjneJtTGk3u2A-y0GWCC9M_9UJ0", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "043f3952199774c7cf39b38b66cb1042a6260d8680803845e4d433adba3bb248185ea495b68cbc7ed4173ee63c9042dc502625c7eb7e21fb02ca9a9114e0a3a18d", + "wx": "3f3952199774c7cf39b38b66cb1042a6260d8680803845e4d433adba3bb24818", + "wy": "5ea495b68cbc7ed4173ee63c9042dc502625c7eb7e21fb02ca9a9114e0a3a18d" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200043f3952199774c7cf39b38b66cb1042a6260d8680803845e4d433adba3bb248185ea495b68cbc7ed4173ee63c9042dc502625c7eb7e21fb02ca9a9114e0a3a18d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEPzlSGZd0x885s4tmyxBCpiYNhoCAOEXk\n1DOtujuySBhepJW2jLx+1Bc+5jyQQtxQJiXH634h+wLKmpEU4KOhjQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 211, + "comment": "extreme value for k and edgecase s", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179855555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "PzlSGZd0x885s4tmyxBCpiYNhoCAOEXk1DOtujuySBg", + "y": "XqSVtoy8ftQXPuY8kELcUCYlx-t-IfsCypqRFOCjoY0", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04cdfb8c0f422e144e137c2412c86c171f5fe3fa3f5bbb544e9076288f3ced786e054fd0721b77c11c79beacb3c94211b0a19bda08652efeaf92513a3b0a163698", + "wx": "00cdfb8c0f422e144e137c2412c86c171f5fe3fa3f5bbb544e9076288f3ced786e", + "wy": "054fd0721b77c11c79beacb3c94211b0a19bda08652efeaf92513a3b0a163698" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004cdfb8c0f422e144e137c2412c86c171f5fe3fa3f5bbb544e9076288f3ced786e054fd0721b77c11c79beacb3c94211b0a19bda08652efeaf92513a3b0a163698", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEzfuMD0IuFE4TfCQSyGwXH1/j+j9bu1RO\nkHYojzzteG4FT9ByG3fBHHm+rLPJQhGwoZvaCGUu/q+SUTo7ChY2mA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 212, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798b6db6db6db6db6db6db6db6db6db6db5f30f30127d33e02aad96438927022e9c", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "zfuMD0IuFE4TfCQSyGwXH1_j-j9bu1ROkHYojzzteG4", + "y": "BU_Qcht3wRx5vqyzyUIRsKGb2ghlLv6vklE6OwoWNpg", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0473598a6a1c68278fa6bfd0ce4064e68235bc1c0f6b20a928108be336730f87e3cbae612519b5032ecc85aed811271a95fe7939d5d3460140ba318f4d14aba31d", + "wx": "73598a6a1c68278fa6bfd0ce4064e68235bc1c0f6b20a928108be336730f87e3", + "wy": "00cbae612519b5032ecc85aed811271a95fe7939d5d3460140ba318f4d14aba31d" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000473598a6a1c68278fa6bfd0ce4064e68235bc1c0f6b20a928108be336730f87e3cbae612519b5032ecc85aed811271a95fe7939d5d3460140ba318f4d14aba31d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEc1mKahxoJ4+mv9DOQGTmgjW8HA9rIKko\nEIvjNnMPh+PLrmElGbUDLsyFrtgRJxqV/nk51dNGAUC6MY9NFKujHQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 213, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179899999999999999999999999999999998d668eaf0cf91f9bd7317d2547ced5a5a", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "c1mKahxoJ4-mv9DOQGTmgjW8HA9rIKkoEIvjNnMPh-M", + "y": "y65hJRm1Ay7Mha7YEScalf55OdXTRgFAujGPTRSrox0", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0458debd9a7ee2c9d59132478a5440ae4d5d7ed437308369f92ea86c82183f10a16773e76f5edbf4da0e4f1bdffac0f57257e1dfa465842931309a24245fda6a5d", + "wx": "58debd9a7ee2c9d59132478a5440ae4d5d7ed437308369f92ea86c82183f10a1", + "wy": "6773e76f5edbf4da0e4f1bdffac0f57257e1dfa465842931309a24245fda6a5d" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000458debd9a7ee2c9d59132478a5440ae4d5d7ed437308369f92ea86c82183f10a16773e76f5edbf4da0e4f1bdffac0f57257e1dfa465842931309a24245fda6a5d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEWN69mn7iydWRMkeKVECuTV1+1Dcwg2n5\nLqhsghg/EKFnc+dvXtv02g5PG9/6wPVyV+HfpGWEKTEwmiQkX9pqXQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 214, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179866666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "WN69mn7iydWRMkeKVECuTV1-1Dcwg2n5Lqhsghg_EKE", + "y": "Z3Pnb17b9NoOTxvf-sD1clfh36RlhCkxMJokJF_aal0", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "048b904de47967340c5f8c3572a720924ef7578637feab1949acb241a5a6ac3f5b950904496f9824b1d63f3313bae21b89fae89afdfc811b5ece03fd5aa301864f", + "wx": "008b904de47967340c5f8c3572a720924ef7578637feab1949acb241a5a6ac3f5b", + "wy": "00950904496f9824b1d63f3313bae21b89fae89afdfc811b5ece03fd5aa301864f" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200048b904de47967340c5f8c3572a720924ef7578637feab1949acb241a5a6ac3f5b950904496f9824b1d63f3313bae21b89fae89afdfc811b5ece03fd5aa301864f", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEi5BN5HlnNAxfjDVypyCSTvdXhjf+qxlJ\nrLJBpaasP1uVCQRJb5gksdY/MxO64huJ+uia/fyBG17OA/1aowGGTw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 215, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179849249249249249249249249249249248c79facd43214c011123c1b03a93412a5", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "i5BN5HlnNAxfjDVypyCSTvdXhjf-qxlJrLJBpaasP1s", + "y": "lQkESW-YJLHWPzMTuuIbifromv38gRtezgP9WqMBhk8", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04f4892b6d525c771e035f2a252708f3784e48238604b4f94dc56eaa1e546d941a346b1aa0bce68b1c50e5b52f509fb5522e5c25e028bc8f863402edb7bcad8b1b", + "wx": "00f4892b6d525c771e035f2a252708f3784e48238604b4f94dc56eaa1e546d941a", + "wy": "346b1aa0bce68b1c50e5b52f509fb5522e5c25e028bc8f863402edb7bcad8b1b" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004f4892b6d525c771e035f2a252708f3784e48238604b4f94dc56eaa1e546d941a346b1aa0bce68b1c50e5b52f509fb5522e5c25e028bc8f863402edb7bcad8b1b", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE9IkrbVJcdx4DXyolJwjzeE5II4YEtPlN\nxW6qHlRtlBo0axqgvOaLHFDltS9Qn7VSLlwl4Ci8j4Y0Au23vK2LGw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 216, + "comment": "extreme value for k", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980eb10e5ab95f2f275348d82ad2e4d7949c8193800d8c9c75df58e343f0ebba7b", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "9IkrbVJcdx4DXyolJwjzeE5II4YEtPlNxW6qHlRtlBo", + "y": "NGsaoLzmixxQ5bUvUJ-1Ui5cJeAovI-GNALtt7ytixs", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + "wx": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "wy": "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeb5mfvncu6xVoGKVzocLBwKb/NstzijZ\nWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 217, + "comment": "public key shares x-coordinate with generator", + "flags": [ + "PointDuplication" + ], + "msg": "313233343030", + "sig": "bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca6050232492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", + "result": "invalid" + }, + { + "tcId": 218, + "comment": "public key shares x-coordinate with generator", + "flags": [ + "PointDuplication" + ], + "msg": "313233343030", + "sig": "44a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e2492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "eb5mfvncu6xVoGKVzocLBwKb_NstzijZWfKBWxb4F5g", + "y": "SDradyajxGVdpPv8DhEIqP0XtEimhVQZnEfQj_sQ1Lg", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777", + "wx": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "wy": "00b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeb5mfvncu6xVoGKVzocLBwKb/NstzijZ\nWfKBWxb4F5i3xSWI2Vw7mqJbBAPx7vdXAuhLt1l6q+ZjuC9vBO8ndw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 219, + "comment": "public key shares x-coordinate with generator", + "flags": [ + "PointDuplication" + ], + "msg": "313233343030", + "sig": "bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca6050232492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", + "result": "invalid" + }, + { + "tcId": 220, + "comment": "public key shares x-coordinate with generator", + "flags": [ + "PointDuplication" + ], + "msg": "313233343030", + "sig": "44a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e2492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", + "result": "invalid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "eb5mfvncu6xVoGKVzocLBwKb_NstzijZWfKBWxb4F5g", + "y": "t8UliNlcO5qiWwQD8e73VwLoS7dZeqvmY7gvbwTvJ3c", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04782c8ed17e3b2a783b5464f33b09652a71c678e05ec51e84e2bcfc663a3de963af9acb4280b8c7f7c42f4ef9aba6245ec1ec1712fd38a0fa96418d8cd6aa6152", + "wx": "782c8ed17e3b2a783b5464f33b09652a71c678e05ec51e84e2bcfc663a3de963", + "wy": "00af9acb4280b8c7f7c42f4ef9aba6245ec1ec1712fd38a0fa96418d8cd6aa6152" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004782c8ed17e3b2a783b5464f33b09652a71c678e05ec51e84e2bcfc663a3de963af9acb4280b8c7f7c42f4ef9aba6245ec1ec1712fd38a0fa96418d8cd6aa6152", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeCyO0X47Kng7VGTzOwllKnHGeOBexR6E\n4rz8Zjo96WOvmstCgLjH98QvTvmrpiRewewXEv04oPqWQY2M1qphUg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 221, + "comment": "pseudorandom signature", + "flags": [ + "ValidSignature" + ], + "msg": "", + "sig": "f80ae4f96cdbc9d853f83d47aae225bf407d51c56b7776cd67d0dc195d99a9dcb303e26be1f73465315221f0b331528807a1a9b6eb068ede6eebeaaa49af8a36", + "result": "valid" + }, + { + "tcId": 222, + "comment": "pseudorandom signature", + "flags": [ + "ValidSignature" + ], + "msg": "4d7367", + "sig": "109cd8ae0374358984a8249c0a843628f2835ffad1df1a9a69aa2fe72355545cac6f00daf53bd8b1e34da329359b6e08019c5b037fed79ee383ae39f85a159c6", + "result": "valid" + }, + { + "tcId": 223, + "comment": "pseudorandom signature", + "flags": [ + "ValidSignature" + ], + "msg": "313233343030", + "sig": "d035ee1f17fdb0b2681b163e33c359932659990af77dca632012b30b27a057b31939d9f3b2858bc13e3474cb50e6a82be44faa71940f876c1cba4c3e989202b6", + "result": "valid" + }, + { + "tcId": 224, + "comment": "pseudorandom signature", + "flags": [ + "ValidSignature" + ], + "msg": "0000000000000000000000000000000000000000", + "sig": "4f053f563ad34b74fd8c9934ce59e79c2eb8e6eca0fef5b323ca67d5ac7ed2384d4b05daa0719e773d8617dce5631c5fd6f59c9bdc748e4b55c970040af01be5", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "eCyO0X47Kng7VGTzOwllKnHGeOBexR6E4rz8Zjo96WM", + "y": "r5rLQoC4x_fEL075q6YkXsHsFxL9OKD6lkGNjNaqYVI", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff00000001060492d5a5673e0f25d8d50fb7e58c49d86d46d4216955e0aa3d40e1", + "wx": "6e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff", + "wy": "01060492d5a5673e0f25d8d50fb7e58c49d86d46d4216955e0aa3d40e1" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff00000001060492d5a5673e0f25d8d50fb7e58c49d86d46d4216955e0aa3d40e1", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEboI1VUUpFAmRgsaywdbwtdKNUMzQBa8s\n4bulQapAyv8AAAABBgSS1aVnPg8l2NUPt+WMSdhtRtQhaVXgqj1A4Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 225, + "comment": "y-coordinate of the public key is small", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "6d6a4f556ccce154e7fb9f19e76c3deca13d59cc2aeb4ecad968aab2ded4596553b9fa74803ede0fc4441bf683d56c564d3e274e09ccf47390badd1471c05fb7", + "result": "valid" + }, + { + "tcId": 226, + "comment": "y-coordinate of the public key is small", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "aad503de9b9fd66b948e9acf596f0a0e65e700b28b26ec56e6e45e846489b3c4fff223c5d0765447e8447a3f9d31fd0696e89d244422022ff61a110b2a8c2f04", + "result": "valid" + }, + { + "tcId": 227, + "comment": "y-coordinate of the public key is small", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "9182cebd3bb8ab572e167174397209ef4b1d439af3b200cdf003620089e43225abb88367d15fe62d1efffb6803da03109ee22e90bc9c78e8b4ed23630b82ea9d", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "boI1VUUpFAmRgsaywdbwtdKNUMzQBa8s4bulQapAyv8", + "y": "AAAAAQYEktWlZz4PJdjVD7fljEnYbUbUIWlV4Ko9QOE", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40cafffffffffef9fb6d2a5a98c1f0da272af0481a73b62792b92bde96aa1e55c2bb4e", + "wx": "6e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff", + "wy": "00fffffffef9fb6d2a5a98c1f0da272af0481a73b62792b92bde96aa1e55c2bb4e" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40cafffffffffef9fb6d2a5a98c1f0da272af0481a73b62792b92bde96aa1e55c2bb4e", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEboI1VUUpFAmRgsaywdbwtdKNUMzQBa8s\n4bulQapAyv/////++fttKlqYwfDaJyrwSBpztieSuSvelqoeVcK7Tg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 228, + "comment": "y-coordinate of the public key is large", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3854a3998aebdf2dbc28adac4181462ccac7873907ab7f212c42db0e69b56ed8c12c09475c772fd0c1b2060d5163e42bf71d727e4ae7c03eeba954bf50b43bb3", + "result": "valid" + }, + { + "tcId": 229, + "comment": "y-coordinate of the public key is large", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "e94dbdc38795fe5c904d8f16d969d3b587f0a25d2de90b6d8c5c53ff887e3607856b8c963e9b68dade44750bf97ec4d11b1a0a3804f4cb79aa27bdea78ac14e4", + "result": "valid" + }, + { + "tcId": 230, + "comment": "y-coordinate of the public key is large", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "49fc102a08ca47b60e0858cd0284d22cddd7233f94aaffbb2db1dd2cf08425e15b16fca5a12cdb39701697ad8e39ffd6bdec0024298afaa2326aea09200b14d6", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "boI1VUUpFAmRgsaywdbwtdKNUMzQBa8s4bulQapAyv8", + "y": "_____vn7bSpamMHw2icq8Egac7Ynkrkr3paqHlXCu04", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04000000013fd22248d64d95f73c29b48ab48631850be503fd00f8468b5f0f70e0f6ee7aa43bc2c6fd25b1d8269241cbdd9dbb0dac96dc96231f430705f838717d", + "wx": "013fd22248d64d95f73c29b48ab48631850be503fd00f8468b5f0f70e0", + "wy": "00f6ee7aa43bc2c6fd25b1d8269241cbdd9dbb0dac96dc96231f430705f838717d" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004000000013fd22248d64d95f73c29b48ab48631850be503fd00f8468b5f0f70e0f6ee7aa43bc2c6fd25b1d8269241cbdd9dbb0dac96dc96231f430705f838717d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAAAAAT/SIkjWTZX3PCm0irSGMYUL5QP9\nAPhGi18PcOD27nqkO8LG/SWx2CaSQcvdnbsNrJbcliMfQwcF+DhxfQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 231, + "comment": "x-coordinate of the public key is small", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "41efa7d3f05a0010675fcb918a45c693da4b348df21a59d6f9cd73e0d831d67abbab52596c1a1d9484296cdc92cbf07e665259a13791a8fe8845e2c07cf3fc67", + "result": "valid" + }, + { + "tcId": 232, + "comment": "x-coordinate of the public key is small", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "b615698c358b35920dd883eca625a6c5f7563970cdfc378f8fe0cee17092144cda0b84cd94a41e049ef477aeac157b2a9bfa6b7ac8de06ed3858c5eede6ddd6d", + "result": "valid" + }, + { + "tcId": 233, + "comment": "x-coordinate of the public key is small", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "87cf8c0eb82d44f69c60a2ff5457d3aaa322e7ec61ae5aecfd678ae1c1932b0ec522c4eea7eafb82914cbf5c1ff76760109f55ddddcf58274d41c9bc4311e06e", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "AAAAAT_SIkjWTZX3PCm0irSGMYUL5QP9APhGi18PcOA", + "y": "9u56pDvCxv0lsdgmkkHL3Z27DayW3JYjH0MHBfg4cX0", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0425afd689acabaed67c1f296de59406f8c550f57146a0b4ec2c97876dfffffffffa46a76e520322dfbc491ec4f0cc197420fc4ea5883d8f6dd53c354bc4f67c35", + "wx": "25afd689acabaed67c1f296de59406f8c550f57146a0b4ec2c97876dffffffff", + "wy": "00fa46a76e520322dfbc491ec4f0cc197420fc4ea5883d8f6dd53c354bc4f67c35" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000425afd689acabaed67c1f296de59406f8c550f57146a0b4ec2c97876dfffffffffa46a76e520322dfbc491ec4f0cc197420fc4ea5883d8f6dd53c354bc4f67c35", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEJa/WiayrrtZ8Hylt5ZQG+MVQ9XFGoLTs\nLJeHbf/////6RqduUgMi37xJHsTwzBl0IPxOpYg9j23VPDVLxPZ8NQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 234, + "comment": "x-coordinate of the public key has many trailing 1's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "62f48ef71ace27bf5a01834de1f7e3f948b9dce1ca1e911d5e13d3b104471d82a1570cc0f388768d3ba7df7f212564caa256ff825df997f21f72f5280d53011f", + "result": "valid" + }, + { + "tcId": 235, + "comment": "x-coordinate of the public key has many trailing 1's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "f6b0e2f6fe020cf7c0c20137434344ed7add6c4be51861e2d14cbda472a6ffb49be93722c1a3ad7d4cf91723700cb5486de5479d8c1b38ae4e8e5ba1638e9732", + "result": "valid" + }, + { + "tcId": 236, + "comment": "x-coordinate of the public key has many trailing 1's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "db09d8460f05eff23bc7e436b67da563fa4b4edb58ac24ce201fa8a35812505746da116754602940c8999c8d665f786c50f5772c0a3cdbda075e77eabc64df16", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "Ja_WiayrrtZ8Hylt5ZQG-MVQ9XFGoLTsLJeHbf____8", + "y": "-kanblIDIt-8SR7E8MwZdCD8TqWIPY9t1Tw1S8T2fDU", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04d12e6c66b67734c3c84d2601cf5d35dc097e27637f0aca4a4fdb74b6aadd3bb93f5bdff88bd5736df898e699006ed750f11cf07c5866cd7ad70c7121ffffffff", + "wx": "00d12e6c66b67734c3c84d2601cf5d35dc097e27637f0aca4a4fdb74b6aadd3bb9", + "wy": "3f5bdff88bd5736df898e699006ed750f11cf07c5866cd7ad70c7121ffffffff" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004d12e6c66b67734c3c84d2601cf5d35dc097e27637f0aca4a4fdb74b6aadd3bb93f5bdff88bd5736df898e699006ed750f11cf07c5866cd7ad70c7121ffffffff", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE0S5sZrZ3NMPITSYBz1013Al+J2N/CspK\nT9t0tqrdO7k/W9/4i9VzbfiY5pkAbtdQ8RzwfFhmzXrXDHEh/////w==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 237, + "comment": "y-coordinate of the public key has many trailing 1's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "592c41e16517f12fcabd98267674f974b588e9f35d35406c1a7bb2ed1d19b7b8c19a5f942607c3551484ff0dc97281f0cdc82bc48e2205a0645c0cf3d7f59da0", + "result": "valid" + }, + { + "tcId": 238, + "comment": "y-coordinate of the public key has many trailing 1's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "be0d70887d5e40821a61b68047de4ea03debfdf51cdf4d4b195558b959a032b28266b4d270e24414ecacb14c091a233134b918d37320c6557d60ad0a63544ac4", + "result": "valid" + }, + { + "tcId": 239, + "comment": "y-coordinate of the public key has many trailing 1's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "fae92dfcb2ee392d270af3a5739faa26d4f97bfd39ed3cbee4d29e26af3b206a93645c80605595e02c09a0dc4b17ac2a51846a728b3e8d60442ed6449fd3342b", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "0S5sZrZ3NMPITSYBz1013Al-J2N_CspKT9t0tqrdO7k", + "y": "P1vf-IvVc234mOaZAG7XUPEc8HxYZs161wxxIf____8", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "046d4a7f60d4774a4f0aa8bbdedb953c7eea7909407e3164755664bc2800000000e659d34e4df38d9e8c9eaadfba36612c769195be86c77aac3f36e78b538680fb", + "wx": "6d4a7f60d4774a4f0aa8bbdedb953c7eea7909407e3164755664bc2800000000", + "wy": "00e659d34e4df38d9e8c9eaadfba36612c769195be86c77aac3f36e78b538680fb" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200046d4a7f60d4774a4f0aa8bbdedb953c7eea7909407e3164755664bc2800000000e659d34e4df38d9e8c9eaadfba36612c769195be86c77aac3f36e78b538680fb", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEbUp/YNR3Sk8KqLve25U8fup5CUB+MWR1\nVmS8KAAAAADmWdNOTfONnoyeqt+6NmEsdpGVvobHeqw/NueLU4aA+w==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 240, + "comment": "x-coordinate of the public key has many trailing 0's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "176a2557566ffa518b11226694eb9802ed2098bfe278e5570fe1d5d7af18a943ed6e2095f12a03f2eaf6718f430ec5fe2829fd1646ab648701656fd31221b97d", + "result": "valid" + }, + { + "tcId": 241, + "comment": "x-coordinate of the public key has many trailing 0's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "60be20c3dbc162dd34d26780621c104bbe5dace630171b2daef0d826409ee5c2bd8081b27762ab6e8f425956bf604e332fa066a99b59f87e27dc1198b26f5caa", + "result": "valid" + }, + { + "tcId": 242, + "comment": "x-coordinate of the public key has many trailing 0's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "edf03cf63f658883289a1a593d1007895b9f236d27c9c1f1313089aaed6b16aee5b22903f7eb23adc2e01057e39b0408d495f694c83f306f1216c9bf87506074", + "result": "valid" + } + ], + "publicKeyJwk": { + "kty": "EC", + "crv": "secp256k1", + "x": "bUp_YNR3Sk8KqLve25U8fup5CUB-MWR1VmS8KAAAAAA", + "y": "5lnTTk3zjZ6MnqrfujZhLHaRlb6Gx3qsPzbni1OGgPs", + "kid": "none" + } + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04b9520873424b8b7099104a5a0eb1acac48e189719971b9131bf9ca8c25f436c92ff805b36e40d651ffb7573edd9b4998c2f2fe39891baf3d83670e9242c0d4ad", + "wx": "b9520873424b8b7099104a5a0eb1acac48e189719971b9131bf9ca8c25f436c9", + "wy": "2ff805b36e40d651ffb7573edd9b4998c2f2fe39891baf3d83670e9242c0d4ad" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004b9520873424b8b7099104a5a0eb1acac48e189719971b9131bf9ca8c25f436c92ff805b36e40d651ffb7573edd9b4998c2f2fe39891baf3d83670e9242c0d4ad", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEuVIIc0JLi3CZEEpaDrGsrEjhiXGZcbkT\nG/nKjCX0Nskv+AWzbkDWUf+3Vz7dm0mYwvL+OYkbrz2DZw6SQsDUrQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 243, + "comment": "r = 1, x = 1 is valid", + "flags": [ + "ValidSignature" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "0000000000000000000000000000000000000000000000000000000000000001fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e", + "result": "valid" + } + ] + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0415f0573de014498f1ee256977a7a21ce5663888d223f841b24495d599a2bd2dfaec48b59fbc8ce644a8d0e5feae572a9dce6d94ab5c1cc04ca5b3d82591aa640", + "wx": "15f0573de014498f1ee256977a7a21ce5663888d223f841b24495d599a2bd2df", + "wy": "aec48b59fbc8ce644a8d0e5feae572a9dce6d94ab5c1cc04ca5b3d82591aa640" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000415f0573de014498f1ee256977a7a21ce5663888d223f841b24495d599a2bd2dfaec48b59fbc8ce644a8d0e5feae572a9dce6d94ab5c1cc04ca5b3d82591aa640", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEFfBXPeAUSY8e4laXenohzlZjiI0iP4Qb\nJEldWZor0t+uxItZ+8jOZEqNDl/q5XKp3ObZSrXBzATKWz2CWRqmQA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 244, + "comment": "r = 2, x = 1 is invalid", + "flags": [ + "ArithmeticError" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "0000000000000000000000000000000000000000000000000000000000000002fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04b9520873424b8b7099104a5a0eb1acac48e189719971b9131bf9ca8c25f436c92ff805b36e40d651ffb7573edd9b4998c2f2fe39891baf3d83670e9242c0d4ad", + "wx": "b9520873424b8b7099104a5a0eb1acac48e189719971b9131bf9ca8c25f436c9", + "wy": "2ff805b36e40d651ffb7573edd9b4998c2f2fe39891baf3d83670e9242c0d4ad" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004b9520873424b8b7099104a5a0eb1acac48e189719971b9131bf9ca8c25f436c92ff805b36e40d651ffb7573edd9b4998c2f2fe39891baf3d83670e9242c0d4ad", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEuVIIc0JLi3CZEEpaDrGsrEjhiXGZcbkT\nG/nKjCX0Nskv+AWzbkDWUf+3Vz7dm0mYwvL+OYkbrz2DZw6SQsDUrQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 245, + "comment": "r = 1 + n, x = 1 is invalid; r was not reduced mod n", + "flags": [ + "ArithmeticError" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04b0fa79bc98baff15d39cf88f8343aea79c0df7f4265361e97a2428b355e460d78fa95eec6e2e02d72c259d20e0ca273468e83f36cee40eed76934c57354ca6a3", + "wx": "b0fa79bc98baff15d39cf88f8343aea79c0df7f4265361e97a2428b355e460d7", + "wy": "8fa95eec6e2e02d72c259d20e0ca273468e83f36cee40eed76934c57354ca6a3" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004b0fa79bc98baff15d39cf88f8343aea79c0df7f4265361e97a2428b355e460d78fa95eec6e2e02d72c259d20e0ca273468e83f36cee40eed76934c57354ca6a3", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEsPp5vJi6/xXTnPiPg0Oup5wN9/QmU2Hp\neiQos1XkYNePqV7sbi4C1ywlnSDgyic0aOg/Ns7kDu12k0xXNUymow==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 246, + "comment": "r = n - 3, x = n - 2 is invalid", + "flags": [ + "ArithmeticError" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413efffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0449be80da98fb7ea4165f898c36c696c50bd9da6485038c6cbda36d82dad41cfd64613a0e2c7224a85e29f774726b434e969db2d4765eafdf3c36004b7202ff3f", + "wx": "49be80da98fb7ea4165f898c36c696c50bd9da6485038c6cbda36d82dad41cfd", + "wy": "64613a0e2c7224a85e29f774726b434e969db2d4765eafdf3c36004b7202ff3f" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000449be80da98fb7ea4165f898c36c696c50bd9da6485038c6cbda36d82dad41cfd64613a0e2c7224a85e29f774726b434e969db2d4765eafdf3c36004b7202ff3f", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAESb6A2pj7fqQWX4mMNsaWxQvZ2mSFA4xs\nvaNtgtrUHP1kYToOLHIkqF4p93Rya0NOlp2y1HZer988NgBLcgL/Pw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 247, + "comment": "r = 2, x = n + 2 is the smallest possible x with a reduction", + "flags": [ + "ValidSignature" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "0000000000000000000000000000000000000000000000000000000000000002fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e", + "result": "valid" + } + ] + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04682cb28dfdcb3e72f200307a6146151338a7143914439c046980c805f0e9d12681d073c1b1dd129e3627d75d8a231a80342149abfcdd8ab9b5775fde215ab9a0", + "wx": "682cb28dfdcb3e72f200307a6146151338a7143914439c046980c805f0e9d126", + "wy": "81d073c1b1dd129e3627d75d8a231a80342149abfcdd8ab9b5775fde215ab9a0" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004682cb28dfdcb3e72f200307a6146151338a7143914439c046980c805f0e9d12681d073c1b1dd129e3627d75d8a231a80342149abfcdd8ab9b5775fde215ab9a0", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEaCyyjf3LPnLyADB6YUYVEzinFDkUQ5wE\naYDIBfDp0SaB0HPBsd0SnjYn112KIxqANCFJq/zdirm1d1/eIVq5oA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 248, + "comment": "r = 3, x = n + 2 is invalid", + "flags": [ + "ArithmeticError" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "0000000000000000000000000000000000000000000000000000000000000003fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "0493c9400c1fc5ed1aefb9f463e7650ae09778313fc188e84564e711a7b84588867e72afd2a40cd15f7b92c6ce7ea95dc7327e54a5309312f43628273534a86ae9", + "wx": "93c9400c1fc5ed1aefb9f463e7650ae09778313fc188e84564e711a7b8458886", + "wy": "7e72afd2a40cd15f7b92c6ce7ea95dc7327e54a5309312f43628273534a86ae9" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000493c9400c1fc5ed1aefb9f463e7650ae09778313fc188e84564e711a7b84588867e72afd2a40cd15f7b92c6ce7ea95dc7327e54a5309312f43628273534a86ae9", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEk8lADB/F7RrvufRj52UK4Jd4MT/BiOhF\nZOcRp7hFiIZ+cq/SpAzRX3uSxs5+qV3HMn5UpTCTEvQ2KCc1NKhq6Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 249, + "comment": "r = p - n + 1, x = 1 is invalid; r is too large to compare r + n with x", + "flags": [ + "ArithmeticError" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "000000000000000000000000000000014551231950b75fc4402da1722fc9baeffffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04e7ed253ac1810f174a83443264f57efbc090bb478a1fac8296f637b4694502a86f48fbb04579fa9e3bbce880915211b24de7f21511e3acf63ea49d737fc6459d", + "wx": "e7ed253ac1810f174a83443264f57efbc090bb478a1fac8296f637b4694502a8", + "wy": "6f48fbb04579fa9e3bbce880915211b24de7f21511e3acf63ea49d737fc6459d" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004e7ed253ac1810f174a83443264f57efbc090bb478a1fac8296f637b4694502a86f48fbb04579fa9e3bbce880915211b24de7f21511e3acf63ea49d737fc6459d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE5+0lOsGBDxdKg0QyZPV++8CQu0eKH6yC\nlvY3tGlFAqhvSPuwRXn6nju86ICRUhGyTefyFRHjrPY+pJ1zf8ZFnQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 250, + "comment": "r = 2^256 - n + 1, x = 1 is invalid; r + n is too large to compare r + n with x, and overflows 2^256 bits", + "flags": [ + "ArithmeticError" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "000000000000000000000000000000014551231950b75fc4402da1732fc9bec0fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413e", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaP1363Verify", + "source": { + "name": "github/davidben/ecdsa-s-pow2", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256k1", + "keySize": 256, + "uncompressed": "04315971a60089e7749a8fa1d8374ecbaab259d773b3737e932bbaa960cdbf27f1bfe600bd60a91e650508eab795146b13c766195a447b24709df9d1c561e53dae", + "wx": "315971a60089e7749a8fa1d8374ecbaab259d773b3737e932bbaa960cdbf27f1", + "wy": "bfe600bd60a91e650508eab795146b13c766195a447b24709df9d1c561e53dae" + }, + "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004315971a60089e7749a8fa1d8374ecbaab259d773b3737e932bbaa960cdbf27f1bfe600bd60a91e650508eab795146b13c766195a447b24709df9d1c561e53dae", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMVlxpgCJ53Saj6HYN07LqrJZ13Ozc36T\nK7qpYM2/J/G/5gC9YKkeZQUI6reVFGsTx2YZWkR7JHCd+dHFYeU9rg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 251, + "comment": "s = 2^128", + "flags": [ + "ValidSignature" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "2b698a0f0a4041b77e63488ad48c23e8e8838dd1fb7520408b121697b782ef220000000000000000000000000000000100000000000000000000000000000000", + "result": "valid" + }, + { + "tcId": 252, + "comment": "s = n - 2^128", + "flags": [ + "ValidSignature" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "2b698a0f0a4041b77e63488ad48c23e8e8838dd1fb7520408b121697b782ef22fffffffffffffffffffffffffffffffdbaaedce6af48a03bbfd25e8cd0364141", + "result": "valid" + } + ] + } + ] +} diff --git a/Tests/AtprotoTypesVerifyTests/Secp256k1ECDSADifferentialTests.swift b/Tests/AtprotoTypesVerifyTests/Secp256k1ECDSADifferentialTests.swift new file mode 100644 index 0000000..36d5425 --- /dev/null +++ b/Tests/AtprotoTypesVerifyTests/Secp256k1ECDSADifferentialTests.swift @@ -0,0 +1,104 @@ +// +// Secp256k1ECDSADifferentialTests.swift +// AtprotoTypesVerifyTests +// +// Created by Mark @ Germ on 8/17/26. +// + +import Crypto +import Foundation +import P256K +import Testing + +@testable import AtprotoTypesVerify + +///P256K — a real, C-backed secp256k1 implementation — is the oracle here. +///`Secp256k1.ECDSA` is a from-scratch, verify-only Swift port (see that file's +///header for why), and its own unit tests only pin known-answer values; this +///suite is what checks it against an independent implementation across many +///keypairs, messages, and deliberately mutated inputs. +/// +///Both sides always work from the raw message, never a shared "digest" +///value: P256K's own `Digest` protocol is a different type from swift-crypto's +///(both named `SHA256`, imported into this file at once — `P256K.signature +///(for:)`'s `Digest`-typed overload can't accept a `Crypto.SHA256.Digest`), +///so this uses each library's own message-hashing overload instead of trying +///to share one digest value across both. +@Suite("secp256k1 ECDSA differential against P256K") +struct Secp256k1ECDSADifferentialTests { + static let messages: [Data] = [ + Data(), + Data("atproto commit signature".utf8), + Data(repeating: 0xFF, count: 300), + ] + + @Test("a genuine signature is accepted by both verifiers", arguments: 0..<100) + func agreesOnGenuineSignatures(_ index: Int) throws { + let key = try P256K.Signing.PrivateKey() + let publicKeyBytes = key.publicKey.dataRepresentation + + for message in Self.messages { + let signature = key.signature(for: message).compactRepresentation + + let oracleAccepts = key.publicKey.isValidSignature( + try P256K.Signing.ECDSASignature(compactRepresentation: signature), for: message) + #expect(oracleAccepts) //sanity: P256K agrees with itself + + let digest = Crypto.SHA256.hash(data: message) + let oursAccepts = Secp256k1.ECDSA.verify( + signature: signature, digest: Data(digest), compressedPublicKey: publicKeyBytes) + #expect(oursAccepts) + } + } + + ///One bit flipped in `r`, `s`, the message, or the public key, each + ///checked independently against the same genuine signature. A flip can + ///occasionally still parse as a valid-but-different signature component or + ///point — the assertion is not "fails to parse", it's "both verifiers + ///refuse", which holds either way. + @Test( + "flipping a bit in r, s, the message, or the key is refused by both", + arguments: 0..<20 + ) + func agreesOnMutatedSignatures(_ index: Int) throws { + let key = try P256K.Signing.PrivateKey() + let publicKeyBytes = key.publicKey.dataRepresentation + let message = Data("mutate me \(index)".utf8) + let signature = key.signature(for: message).compactRepresentation + + func flip(_ data: Data, at position: Int) -> Data { + var copy = data + let byteIndex = copy.index(copy.startIndex, offsetBy: position) + copy[byteIndex] ^= 0x01 + return copy + } + + let cases: [(signature: Data, message: Data, key: Data)] = [ + (flip(signature, at: 0), message, publicKeyBytes), //mutate r + (flip(signature, at: 32), message, publicKeyBytes), //mutate s + (signature, flip(message, at: 0), publicKeyBytes), //mutate the message + (signature, message, flip(publicKeyBytes, at: 1)), //mutate the key + ] + + for testCase in cases { + let digest = Crypto.SHA256.hash(data: testCase.message) + let oursAccepts = Secp256k1.ECDSA.verify( + signature: testCase.signature, + digest: Data(digest), + compressedPublicKey: testCase.key) + #expect(!oursAccepts) + + let oracleAccepts: Bool = + if let parsedSignature = try? P256K.Signing.ECDSASignature( + compactRepresentation: testCase.signature), + let parsedKey = try? P256K.Signing.PublicKey( + dataRepresentation: testCase.key, format: .compressed) + { + parsedKey.isValidSignature(parsedSignature, for: testCase.message) + } else { + false + } + #expect(!oracleAccepts) + } + } +} diff --git a/Tests/AtprotoTypesVerifyTests/Secp256k1FieldTests.swift b/Tests/AtprotoTypesVerifyTests/Secp256k1FieldTests.swift new file mode 100644 index 0000000..a701ed4 --- /dev/null +++ b/Tests/AtprotoTypesVerifyTests/Secp256k1FieldTests.swift @@ -0,0 +1,115 @@ +// +// Secp256k1FieldTests.swift +// AtprotoTypesVerifyTests +// +// Created by Mark @ Germ on 8/17/26. +// + +import Foundation +import Testing + +@testable import AtprotoTypesVerify + +@Suite("secp256k1 field arithmetic") +struct Secp256k1FieldTests { + typealias Field = Secp256k1.Field + + static func field(_ hex: String) -> Field { + Field(bigEndian: Array(Data(hex: hex)))! + } + + @Test("p is exactly 2^256 - 2^32 - 977") + func modulusMatchesClosedForm() { + //built independently of the hardcoded limbs, from the textbook + //definition (doubling 256 times from 1 gives 2^256, mod 2^256 — + //which is exactly what four wrapping UInt64 limbs represent), so a + //transcription error in the constant can't hide behind an equally + //wrong derivation + var twoTo256 = Secp256k1.Limbs256.one + for _ in 0..<256 { twoTo256 = Secp256k1.Limbs256.adding(twoTo256, twoTo256).0 } + #expect(Secp256k1.Limbs256.compare(twoTo256, Secp256k1.Limbs256.zero) == 0) + + let twoTo32: UInt64 = 1 << 32 + var reference = Secp256k1.Limbs256.zero + reference = Secp256k1.Limbs256.subtracting(reference, (twoTo32, 0, 0, 0)).0 + reference = Secp256k1.Limbs256.subtracting(reference, (977, 0, 0, 0)).0 + #expect(Secp256k1.Limbs256.compare(reference, Field.modulus) == 0) + } + + @Test("p - 1 + 1 wraps to zero") + func wrapsAtModulus() { + let pMinus1 = Field.zero - Field.one + #expect(pMinus1 + Field.one == Field.zero) + } + + @Test("0 - 1 == p - 1") + func subtractionUnderflowsCorrectly() { + let direct = Field.zero - Field.one + let expected = Self.field("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E") + #expect(direct == expected) + } + + static func small(_ value: UInt8) -> Field { + Field(bigEndian: [UInt8](repeating: 0, count: 31) + [value])! + } + + @Test("multiplication agrees with repeated addition for small values") + func multiplicationMatchesRepeatedAddition() { + let seven = Self.small(7) + var bySum = Field.zero + for _ in 0..<41 { bySum = bySum + seven } + #expect(bySum == seven * Self.small(41)) + } + + ///The case the carry-propagation logic in `Limbs256.multiplyWide` exists + ///for: both operands at the top of the limb range, where a hand-fused + ///carry addition (rather than the rippling one this uses) can silently + ///drop a carry. + @Test("multiplying two near-maximal field elements does not lose a carry") + func multiplicationOfLargeValuesRoundTrips() { + let pMinus1 = Self.field("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E") + let product = pMinus1 * pMinus1 + //p-1 ≡ -1 (mod p), so (p-1)^2 ≡ (-1)^2 == 1 + #expect(product == Field.one) + + //a second, less degenerate near-maximal case: p-2 ≡ -2, so + //(p-2)^2 ≡ 4 — a non-trivial answer a dropped carry is less likely + //to accidentally still land on + let pMinus2 = Self.field("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2D") + #expect(pMinus2 * pMinus2 == Self.small(4)) + } + + @Test("inverse: a * a^-1 == 1 for several values") + func inverseRoundTrips() throws { + for hex in [ + "0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000000000000000000000000000000000000000000003", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2E", + ] { + let value = Self.field(hex) + #expect(value * value.inverted == Field.one) + } + } + + @Test("square root: sqrt(a^2) squares back to a^2") + func squareRootRoundTrips() throws { + let value = Self.field("0000000000000000000000000000000000000000000000000000000000000005") + let squared = value.squared() + let root = try #require(squared.squareRoot) + #expect(root.squared() == squared) + } + + @Test("square root of a non-residue is nil") + func squareRootOfNonResidueIsNil() { + //3 is a quadratic non-residue mod secp256k1's p (p ≡ 3 mod 4, and 3's + //Legendre symbol here is -1 — verified against a reference computation) + let three = Self.field("0000000000000000000000000000000000000000000000000000000000000003") + #expect(three.squareRoot == nil) + } + + @Test("canonical init rejects a value equal to or above the modulus") + func rejectsNonCanonicalValues() { + #expect(Field(bigEndian: Array(Data(hex: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"))) == nil) //== p + #expect(Field(bigEndian: Array(Data(hex: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"))) == nil) //> p + } +} diff --git a/Tests/AtprotoTypesVerifyTests/Secp256k1PointTests.swift b/Tests/AtprotoTypesVerifyTests/Secp256k1PointTests.swift new file mode 100644 index 0000000..b75a75e --- /dev/null +++ b/Tests/AtprotoTypesVerifyTests/Secp256k1PointTests.swift @@ -0,0 +1,179 @@ +// +// Secp256k1PointTests.swift +// AtprotoTypesVerifyTests +// +// Created by Mark @ Germ on 8/17/26. +// + +import Foundation +import Testing + +@testable import AtprotoTypesVerify + +@Suite("secp256k1 points") +struct Secp256k1PointTests { + typealias Point = Secp256k1.Point + typealias Field = Secp256k1.Field + typealias Scalar = Secp256k1.Scalar + + static func field(_ hex: String) -> Field { + Field(bigEndian: Array(Data(hex: hex)))! + } + + static func scalar(_ value: UInt8) -> Scalar { + Scalar(canonicalBigEndian: [UInt8](repeating: 0, count: 31) + [value])! + } + + @Test("the generator is on the curve: y^2 == x^3 + 7") + func generatorIsOnCurve() throws { + let (x, y) = try #require(Point.generator.affine) + #expect(y.squared() == x.squared() * x + Point.b) + } + + ///Independently computed via a plain-Python affine short-Weierstrass + ///implementation (not this codebase), so this pins the whole arithmetic + ///stack — field ops, doubling, addition — against an outside reference + ///rather than against its own internal consistency. + @Test("2G matches an independently computed reference value") + func doublingMatchesKnownAnswer() throws { + let doubled = Point.generator.doubled() + let (x, y) = try #require(doubled.affine) + #expect( + x + == Self.field( + "C6047F9441ED7D6D3045406E95C07CD85C778E4B8CEF3CA7ABAC09B95C709EE5")) + #expect( + y + == Self.field( + "1AE168FEA63DC339A3C58419466CEAEEF7F632653266D0E1236431A950CFE52A")) + + //routed through addition and through scalar multiplication too, since + //doubling is a distinct code path from both + let viaAddition = Point.generator + Point.generator + #expect(viaAddition.affine?.x == x) + #expect(viaAddition.affine?.y == y) + + let viaMultiply = Point.generator.multiplied(by: Self.scalar(2)) + #expect(viaMultiply.affine?.x == x) + #expect(viaMultiply.affine?.y == y) + } + + @Test("3G matches an independently computed reference value") + func triplingMatchesKnownAnswer() throws { + let tripled = (Point.generator + Point.generator) + Point.generator + let (x, y) = try #require(tripled.affine) + #expect( + x + == Self.field( + "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9")) + #expect( + y + == Self.field( + "388F7B0F632DE8140FE337E62A37F3566500A99934C2231B6CB9FD7584B8E672")) + + let viaMultiply = Point.generator.multiplied(by: Self.scalar(3)) + #expect(viaMultiply.affine?.x == x) + #expect(viaMultiply.affine?.y == y) + } + + // MARK: - Infinity + + @Test("infinity is the identity for addition") + func infinityIsIdentity() { + #expect(Point.generator + Point.infinity == Point.generator) + #expect(Point.infinity + Point.generator == Point.generator) + } + + @Test("doubling infinity is infinity") + func doublingInfinityIsInfinity() { + #expect(Point.infinity.doubled() == Point.infinity) + } + + @Test("a point plus its negation is infinity") + func pointPlusNegationIsInfinity() { + #expect(Point.generator + Point.generator.negated == Point.infinity) + } + + @Test("infinity has no affine representation") + func infinityHasNoAffine() { + #expect(Point.infinity.affine == nil) + } + + ///Compared via affine coordinates, not raw `Point ==`: two different + ///Jacobian triples (X, Y, Z) can represent the same affine point — a + ///fresh scalar multiplication accumulates an arbitrary Z, while + ///`negated` reuses the generator's own Z=1 — so `Point`'s structural + ///equality is only meaningful against the literal `.infinity` case, never + ///between two points that might carry different Z scalings. + @Test("(n-1)*G equals -G") + func scalarMultiplicationByOrderMinusOneNegatesTheGenerator() throws { + let nMinus1 = Scalar( + canonicalBigEndian: Array( + Data(hex: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140")))! + let result = try #require(Point.generator.multiplied(by: nMinus1).affine) + let expected = try #require(Point.generator.negated.affine) + #expect(result.x == expected.x) + #expect(result.y == expected.y) + } + + // MARK: - Decompression + + @Test("a compressed generator decompresses to the generator") + func decompressesGenerator() throws { + var compressed = Data([Point.generator.affine!.y.isOdd ? 0x03 : 0x02]) + compressed.append(contentsOf: Point.generator.affine!.x.bigEndianBytes) + + let decoded = try #require(Point(compressed: compressed)) + #expect(decoded.affine?.x == Point.generator.affine?.x) + #expect(decoded.affine?.y == Point.generator.affine?.y) + } + + @Test("both parity prefixes round-trip to the correct y") + func decompressionRespectsParity() throws { + let (x, y) = Point.generator.affine! + let evenPrefix: UInt8 = y.isOdd ? 0x03 : 0x02 //the prefix matching y as-is + let oddPrefix: UInt8 = y.isOdd ? 0x02 : 0x03 //the prefix for -y + + var asIs = Data([evenPrefix]) + asIs.append(contentsOf: x.bigEndianBytes) + let decodedAsIs = try #require(Point(compressed: asIs)) + #expect(decodedAsIs.affine?.y == y) + + var negated = Data([oddPrefix]) + negated.append(contentsOf: x.bigEndianBytes) + let decodedNegated = try #require(Point(compressed: negated)) + #expect(decodedNegated.affine?.y == y.negated) + } + + @Test("an x with no corresponding curve point is rejected") + func rejectsXNotOnCurve() { + //x = 5: 5^3 + 7 = 132, and 132 is not a quadratic residue mod p + //(checked independently — if this ever flips due to a p + //transcription error, this test starts failing loudly rather than + //silently accepting) + var compressed = Data([0x02]) + compressed.append(contentsOf: [UInt8](repeating: 0, count: 31) + [5]) + #expect(Point(compressed: compressed) == nil) + } + + @Test("x equal to or above p is rejected") + func rejectsXAboveModulus() { + var compressed = Data([0x02]) + compressed.append( + contentsOf: Data( + hex: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F")) //== p + #expect(Point(compressed: compressed) == nil) + } + + @Test("wrong length is rejected") + func rejectsWrongLength() { + #expect(Point(compressed: Data([0x02, 0x01])) == nil) + } + + @Test("wrong prefix byte is rejected") + func rejectsWrongPrefix() { + var compressed = Data([0x04]) + compressed.append(contentsOf: Point.generator.affine!.x.bigEndianBytes) + #expect(Point(compressed: compressed) == nil) + } +} diff --git a/Tests/AtprotoTypesVerifyTests/Secp256k1ScalarTests.swift b/Tests/AtprotoTypesVerifyTests/Secp256k1ScalarTests.swift new file mode 100644 index 0000000..30c4c7e --- /dev/null +++ b/Tests/AtprotoTypesVerifyTests/Secp256k1ScalarTests.swift @@ -0,0 +1,108 @@ +// +// Secp256k1ScalarTests.swift +// AtprotoTypesVerifyTests +// +// Created by Mark @ Germ on 8/17/26. +// + +import Foundation +import Testing + +@testable import AtprotoTypesVerify + +@Suite("secp256k1 scalar arithmetic") +struct Secp256k1ScalarTests { + typealias Scalar = Secp256k1.Scalar + + static func scalar(_ hex: String) -> Scalar { + Scalar(canonicalBigEndian: Array(Data(hex: hex)))! + } + + static func small(_ value: UInt8) -> Scalar { + Scalar(canonicalBigEndian: [UInt8](repeating: 0, count: 31) + [value])! + } + + @Test("n - 1, doubled and reduced by hand, matches the closed-form order") + func orderIsInternallyConsistent() { + //n itself is not representable as a canonical Scalar (by definition — + //canonical means < n), so this checks 2n reduces to 0 via the same + //wide-reduction path everything else uses + let wide = Secp256k1.Limbs256.multiplyWide(Scalar.order, (2, 0, 0, 0)) + let reduced = Scalar.reduce(wide) + #expect(reduced == Scalar.zero) + } + + @Test("n - 1 + 1 wraps to zero") + func wrapsAtOrder() { + let nMinus1 = Self.scalar("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140") + #expect(Self.adding(nMinus1, Scalar.one) == Scalar.zero) + } + + ///Adds two scalar values via the shared limb primitive — a plain `+` + ///isn't part of `Scalar`'s production surface (ECDSA verify only needs + ///multiplication and inversion), so tests reach for the same primitive + ///`Scalar.reduce`'s own callers do rather than growing the type an + ///operator nothing else uses. + static func adding(_ lhs: Scalar, _ rhs: Scalar) -> Scalar { + let (sum, carry) = Secp256k1.Limbs256.adding(lhs.value, rhs.value) + return Scalar.reduce([sum.0, sum.1, sum.2, sum.3, carry, 0, 0, 0]) + } + + @Test("multiplication agrees with repeated addition for small values") + func multiplicationMatchesRepeatedAddition() { + let seven = Self.small(7) + var bySum = Scalar.zero + for _ in 0..<41 { bySum = Self.adding(bySum, seven) } + #expect(bySum == seven * Self.small(41)) + } + + ///The same near-maximal carry-loss probe as `Field`'s, against n instead + ///of p, and against `foldFactor` (129 bits, not p's 33) — a wider fold + ///constant is a different opportunity to drop a carry across more limbs. + @Test("multiplying two near-maximal scalars does not lose a carry") + func multiplicationOfLargeValuesRoundTrips() { + let nMinus1 = Self.scalar("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140") + #expect(nMinus1 * nMinus1 == Scalar.one) //(-1)^2 == 1 + + let nMinus2 = Self.scalar("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD036413F") + #expect(nMinus2 * nMinus2 == Self.small(4)) //(-2)^2 == 4 + } + + @Test("inverse: a * a^-1 == 1 for several values") + func inverseRoundTrips() { + for hex in [ + "0000000000000000000000000000000000000000000000000000000000000002", + "0000000000000000000000000000000000000000000000000000000000000003", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140", + ] { + let value = Self.scalar(hex) + #expect(value * value.inverted == Scalar.one) + } + } + + @Test("low-S: exactly n/2 is low, n/2 + 1 is high") + func lowSBoundary() { + let half = Scalar(canonical: Scalar.halfOrder)! + #expect(half.isLowS) + #expect(!Self.adding(half, Scalar.one).isLowS) + } + + @Test("digest reduction folds a full 32-byte SHA-256-sized value mod n") + func digestReductionHandlesOversizedInput() { + //all-0xFF is well above n; the reducing init must fold it, not reject + //it the way the canonical init would + let maxDigest = [UInt8](repeating: 0xFF, count: 32) + let reduced = Scalar(reducingBigEndian: maxDigest) + #expect(Secp256k1.Limbs256.compare(reduced.value, Scalar.order) < 0) + } + + @Test("canonical init rejects a value equal to or above the order") + func rejectsNonCanonicalValues() { + #expect( + Scalar( + canonicalBigEndian: Array( + Data( + hex: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141")) + ) == nil) //== n + } +} diff --git a/Tests/AtprotoTypesVerifyTests/Secp256k1TestSigner.swift b/Tests/AtprotoTypesVerifyTests/Secp256k1TestSigner.swift new file mode 100644 index 0000000..d44e1e2 --- /dev/null +++ b/Tests/AtprotoTypesVerifyTests/Secp256k1TestSigner.swift @@ -0,0 +1,34 @@ +// +// Secp256k1TestSigner.swift +// AtprotoTypesVerifyTests +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypesVerifyMocks +import Foundation +import P256K + +///Wraps `P256K.Signing.PrivateKey` to feed `RepoFixture.commit(signedBy:)` a +///real k256 signature. Lives here, not in `AtprotoTypesVerifyMocks`: P256K is +///a differential-test dependency only, and `AtprotoTypesVerify`'s own +///secp256k1 is deliberately verify-only with no signing path, so nothing +///outside this test target should ever construct a k256 private key. +struct Secp256k1TestSigner: RepoFixtureSigningKey { + let key: P256K.Signing.PrivateKey + + init() { + key = try! P256K.Signing.PrivateKey() + } + + var publicKey: Secp256k1PublicKey { + Secp256k1PublicKey(compressedRepresentation: key.publicKey.dataRepresentation) + } + + ///P256K normalises every signature it produces to low-S + ///(BIP-146/`secp256k1_ecdsa_sign`), same as swift-crypto's P256 does not — + ///so, unlike the P256 conformance, this needs no extra fold. + func repoFixtureSignature(for message: Data) throws -> Data { + key.signature(for: message).compactRepresentation + } +} diff --git a/Tests/AtprotoTypesVerifyTests/Secp256k1WycheproofTests.swift b/Tests/AtprotoTypesVerifyTests/Secp256k1WycheproofTests.swift new file mode 100644 index 0000000..92799af --- /dev/null +++ b/Tests/AtprotoTypesVerifyTests/Secp256k1WycheproofTests.swift @@ -0,0 +1,154 @@ +// +// Secp256k1WycheproofTests.swift +// AtprotoTypesVerifyTests +// +// Created by Mark @ Germ on 8/17/26. +// + +import AtprotoTypes +import AtprotoTypesVerify +import AtprotoTypesVerifyMocks +import Foundation +import Testing + +///Google/C2SP's Wycheproof project publishes adversarially-constructed ECDSA +///test vectors — edge cases in the modular arithmetic (near-order scalars, +///point duplication, small r/s) that a from-scratch implementation's own +///hand-picked known-answer tests are unlikely to stumble onto by chance. +/// +///`ecdsa_secp256k1_sha256_p1363_test.json`, vendored below under Apache-2.0 +///from https://github.com/C2SP/wycheproof (LICENSE copied alongside it in +///`Resources/wycheproof/`) — 252 vectors over 108 groups, IEEE P1363 +///(fixed-width `r ‖ s`) over SHA-256, secp256k1. +private struct WycheproofFile: Decodable { + let numberOfTests: Int + let testGroups: [TestGroup] + + struct TestGroup: Decodable { + let publicKey: PublicKey + let sha: String + let tests: [Vector] + } + + struct PublicKey: Decodable { + let curve: String + ///`0x04 ‖ x ‖ y`, SEC1 uncompressed, hex. + let uncompressed: String + } + + struct Vector: Decodable { + let tcId: Int + let comment: String + ///Hex-encoded raw message — the vectors are pre-hash, so this file + ///hashes it with SHA-256 itself rather than passing a digest. + let msg: String + ///Hex-encoded `r ‖ s`, fixed-width when well-formed; several vectors + ///deliberately vary the length to probe truncated/padded encodings. + let sig: String + ///"valid" or "invalid" — Wycheproof's own verdict, which this suite + ///reclassifies through atproto's stricter low-S policy before + ///comparing against what the verifier actually does. + let result: String + } +} + +@Suite("secp256k1 against Wycheproof test vectors") +struct Secp256k1WycheproofTests { + enum Bucket: Equatable { + case accepted + case refusedHighS + case refusedBadLength + case refusedOther + } + + ///`RepoSigningKey.verify` checks low-S on the raw signature bytes before + ///it ever parses `r`/`s` as scalars — mirroring the pre-existing p256 + ///branch exactly, deliberately, for one shared check instead of a + ///per-curve variant. A consequence: a vector whose `s` is not even in + ///canonical range (`s >= n`, e.g. Wycheproof's `s = p` cases) still has + ///`s > n/2` as raw bytes, so it is classified non-canonical rather than + ///reaching the "other invalid" path — it is refused either way, this only + ///picks which specific `ProofError` case reports it, so the census + ///predicts that reclassification rather than the parse-first ordering a + ///from-scratch design might otherwise choose. + static func expectedBucket(signature: Data, result: String) -> Bucket { + guard signature.count == 64 else { return .refusedBadLength } + let s = Array(signature.suffix(32)) + guard RepoSigningKey.isLowS(s, order: RepoSigningKey.secp256k1Order) else { + return .refusedHighS + } + return result == "valid" ? .accepted : .refusedOther + } + + static func signingKey(uncompressedHex: String) throws -> RepoSigningKey { + let bytes = try data(hex: uncompressedHex) + precondition(bytes.count == 65 && bytes.first == 0x04, "not a SEC1 uncompressed point") + let x = bytes.subdata(in: bytes.index(bytes.startIndex, offsetBy: 1).. Data { + var bytes = [UInt8]() + bytes.reserveCapacity(hex.count / 2) + var index = hex.startIndex + while index < hex.endIndex { + let next = hex.index(index, offsetBy: 2) + bytes.append(try #require(UInt8(hex[index.. n/2`, whether or not `s` was even a valid + ///scalar), 18 refused for a non-64-byte encoding, 23 refused for every + ///other reason. A silent shift in any bucket means the verifier's + ///behavior changed on a real edge case. + @Test("every vector lands in the bucket its own encoding predicts") + func matchesExactCensus() throws { + let url = try #require( + Bundle.module.url( + forResource: "ecdsa_secp256k1_sha256_p1363_test", withExtension: "json", + subdirectory: "wycheproof")) + let file = try JSONDecoder().decode(WycheproofFile.self, from: Data(contentsOf: url)) + #expect(file.numberOfTests == 252) + + var tally: [Bucket: Int] = [:] + + for group in file.testGroups { + #expect(group.publicKey.curve == "secp256k1") + #expect(group.sha == "SHA-256") + let signingKey = try Self.signingKey(uncompressedHex: group.publicKey.uncompressed) + + for vector in group.tests { + let message = try Self.data(hex: vector.msg) + let signature = try Self.data(hex: vector.sig) + let expected = Self.expectedBucket(signature: signature, result: vector.result) + + let actual: Bucket + do { + try signingKey.verify(signature: signature, over: message) + actual = .accepted + } catch Atproto.Repo.ProofError.badSignatureLength { + actual = .refusedBadLength + } catch Atproto.Repo.ProofError.nonCanonicalSignature { + actual = .refusedHighS + } catch Atproto.Repo.ProofError.signatureDidNotVerify { + actual = .refusedOther + } + + #expect(actual == expected, "tcId \(vector.tcId): \(vector.comment)") + tally[actual, default: 0] += 1 + } + } + + #expect(tally[.accepted] == 95) + #expect(tally[.refusedHighS] == 116) + #expect(tally[.refusedBadLength] == 18) + #expect(tally[.refusedOther] == 23) + } +} diff --git a/docs/dependency-choices.md b/docs/dependency-choices.md new file mode 100644 index 0000000..407aac2 --- /dev/null +++ b/docs/dependency-choices.md @@ -0,0 +1,126 @@ +# Why `AtprotoTypesVerify` implements its own primitives + +This target hand-rolls DAG-CBOR, CID, CAR framing, MST proof walking, and +secp256k1 verification rather than importing them. That is a deliberate and +recurring question, so this records the reasoning and — more usefully — what +would change it. + +The general rule: **prefer a dependency**, unless it cannot express a property +this target's correctness depends on. Every case below fails for a specific, +checkable reason, not a general preference for owning code. + +Two constraints apply throughout: + +- **The shipped product is pure Swift with no C dependencies.** A + space-constrained consumer (an App Clip) links `AtprotoTypes` but never this + target, and the boundary is enforced in CI. A C-backed dependency also + widens the audit surface of a security check. +- **This is a verifier.** It answers "does this proof hold", so leniency is a + correctness bug, not a convenience. Anywhere two distinct byte strings can + decode to the same value, content-addressing stops binding and the proof + stops proving anything. + +## DAG-CBOR + +Candidates: [`nnabeyang/swift-cbor`][swift-cbor] (already in the wider +dependency graph, so adopting it would cost nothing) and +[`thecoolwinter/CBOR`][cbor], which ships explicit `DAGCBORDecoder` / +`DAGCBOREncoder` types. + +**Strictness is not the blocker.** Both enforce a real DAG-CBOR profile — +definite lengths, minimal integer encodings, string-only map keys, canonical +key order, duplicate-key rejection, tag allow-lists, float64-only. swift-cbor +0.1.0 in particular exposes `Options.deterministicCbor` and a matching decode +option set. Earlier versions did not, and that is worth knowing if this +question is revisited against stale information. + +**The blocker is that both are Codable-only.** Neither exposes a schemaless +value tree: swift-cbor's `CborValue` is internal, with no public API returning +it, and `thecoolwinter/CBOR` has no public value enum at all. + +A generic tree is not a stylistic preference here. The commit signature is +computed over the commit map **with `sig` removed**, so verification has to +rebuild that preimage and re-encode it canonically. Decoding into a +`Commit: Codable` struct silently drops any field the struct does not model — +so the first time a PDS writes a field we did not anticipate, the re-encoded +preimage would differ from the signed bytes and **every proof would fail**. +The same applies to MST nodes and, more sharply, to records: lexicon data is +arbitrary by definition. + +Round-tripping through a value tree makes `encode(decode(bytes)) == bytes` a +property we can test directly, and lets one comparison rule serve both decode +validation and canonical encoding. + +**What would change this:** either library exposing a public value tree +(swift-cbor promoting `CborValue`, say), or a DAG-CBOR package built around +one. + +## CID + +Candidate: [`swift-libp2p/swift-cid`][swift-cid] — real, maintained, pure +Swift, and from the same organisation as `swift-bases`, which this package +already depends on. This is the closest call of the five. + +It is declined because it reaches CID through `swift-multihash`, which depends +on **CryptoSwift**. That would pull a general-purpose pure-Swift crypto +library into the graph to compute SHA-256, when this target already links +swift-crypto and hashes every CAR block through it. Two crypto implementations +for one hash is a worse audit story and slower, and the package documents +breaking changes across minor versions while pre-1.0. + +The narrowness of `ContentIdentifier` is also load-bearing rather than +incidental: CIDv1 only, two codecs, sha2-256 only, minimal varints required, +CIDv0 rejected outright. A general-purpose library accepts the whole +multiformats space by design, so each of those restrictions would have to be +re-imposed at the call site anyway. + +**What would change this:** `swift-multihash` dropping CryptoSwift in favour +of swift-crypto, or a CID package that accepts an injected hash function. + +## CAR + +No Swift CAR package exists. The implementations that turned up are an app, a +GPL-licensed subdirectory of an unrelated monorepo that SPM cannot depend on +directly, and library code that does not fit this use. + +Notably, the closest reference implementation **never recomputes a block's CID** +— it trusts the archive's own claims about what each block is. That check, done +at load time rather than lookup time, is the single property the rest of the +proof walk rests on, so adopting that code would have meant deleting the reason +the file exists. + +## MST + +atproto's Merkle Search Tree has protocol-specific semantics: node depth from +the count of leading zero bits in the SHA-256 of the key, and keys stored +compressed against the previous key's shared prefix. A generic Merkle-tree +library cannot satisfy this; only an atproto-specific implementation would. + +Searching Swift broadly surfaces essentially one MST implementation, and it +enumerates whole repositories rather than checking inclusion proofs, with no +key-ordering validation — meaning a forged tree shape passes it. Inclusion +proof checking is exactly what this target needs. + +## secp256k1 + +swift-crypto has no secp256k1 at all, and most atproto accounts sign with it, +so this cannot simply be skipped. + +The one mature Swift option wraps Bitcoin Core's C `libsecp256k1`, which +conflicts with the no-C constraint above. It *is* used — as a differential +test oracle in `AtprotoTypesVerifyTests`, never in a shipped product — which +gives the from-scratch implementation an independent implementation to check +against, alongside the Wycheproof vector suite. + +The port is **verify-only and deliberately not constant-time**: every value it +touches (public keys, signatures, message digests) is public, so there is no +secret whose timing could leak. That property is what makes a from-scratch +implementation defensible, and it is why the arithmetic files must never grow +a signing path — a private key there would invalidate the reasoning that +permits this code to exist. + +**What would change this:** a maintained, pure-Swift secp256k1 verifier. + +[swift-cbor]: https://github.com/nnabeyang/swift-cbor +[cbor]: https://github.com/thecoolwinter/CBOR +[swift-cid]: https://github.com/swift-libp2p/swift-cid