From 7fa4c48b479f4d99538de380442cbf8168009fb9 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 17:35:02 +0000 Subject: [PATCH 1/8] First slice: one DER element (X.690), read and written, proved The reader accepts exactly a DER encoding of an element, followed by anything, and gives back the element and what follows; the writer writes that encoding; each element and each integer has one encoding. Statements in Spec.lean, definitions written from X.690 (02/2021) in Spec/Defs.lean, proofs in Proofs/. lake comparator --paranoid accepts all ten theorems under every bundled kernel. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AovXVgJBHGiN44TxQM4dcd --- .github/workflows/bar.yml | 21 ++ .gitignore | 1 + Der.lean | 1 + Der/Basic.lean | 235 ++++++++++++++++++++ Main.lean | 196 +++++++++++++++++ Proofs.lean | 5 + Proofs/Digits.lean | 255 ++++++++++++++++++++++ Proofs/Element.lean | 301 ++++++++++++++++++++++++++ Proofs/Header.lean | 445 ++++++++++++++++++++++++++++++++++++++ Proofs/Int.lean | 278 ++++++++++++++++++++++++ Proofs/Main.lean | 77 +++++++ Spec.lean | 113 ++++++++++ Spec/Defs.lean | 170 +++++++++++++++ Spec/Element.lean | 23 ++ bar.toml | 31 +++ comparator.json | 10 + lake-manifest.json | 6 + lakefile.toml | 16 ++ lean-toolchain | 1 + 19 files changed, 2185 insertions(+) create mode 100644 .github/workflows/bar.yml create mode 100644 Der.lean create mode 100644 Der/Basic.lean create mode 100644 Main.lean create mode 100644 Proofs.lean create mode 100644 Proofs/Digits.lean create mode 100644 Proofs/Element.lean create mode 100644 Proofs/Header.lean create mode 100644 Proofs/Int.lean create mode 100644 Proofs/Main.lean create mode 100644 Spec.lean create mode 100644 Spec/Defs.lean create mode 100644 Spec/Element.lean create mode 100644 bar.toml create mode 100644 comparator.json create mode 100644 lake-manifest.json create mode 100644 lakefile.toml create mode 100644 lean-toolchain diff --git a/.github/workflows/bar.yml b/.github/workflows/bar.yml new file mode 100644 index 0000000..32457ed --- /dev/null +++ b/.github/workflows/bar.yml @@ -0,0 +1,21 @@ +name: verified bar + +on: + push: + pull_request: + # Rerun when the spec reviewer approves, or an approval is dismissed. + pull_request_review: + types: [submitted, dismissed] + +jobs: + bar: + # pull-requests: read lets the check confirm the spec approval on GitHub. + permissions: + contents: read + pull-requests: read + # Both lines pin the same commit of Proven-Commons/mission: bar v2, as + # proposed in its pull request #2. Move both to a release tag once + # mission publishes one. + uses: Proven-Commons/mission/.github/workflows/verified-bar.yml@6ba85c27abaaf07905c9316aa96dc29f7b5847a2 + with: + bar-ref: 6ba85c27abaaf07905c9316aa96dc29f7b5847a2 diff --git a/.gitignore b/.gitignore index 01f8cdb..ae73679 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .lake/ +__pycache__/ diff --git a/Der.lean b/Der.lean new file mode 100644 index 0000000..eb34e4c --- /dev/null +++ b/Der.lean @@ -0,0 +1 @@ +import Der.Basic diff --git a/Der/Basic.lean b/Der/Basic.lean new file mode 100644 index 0000000..5141a53 --- /dev/null +++ b/Der/Basic.lean @@ -0,0 +1,235 @@ +/- + A reader and writer for DER (X.690): one element, nested to any depth. +-/ + +import Spec.Element + +namespace Der + +/-! ## Numbers -/ + +/-- The value of digits in base `2 ^ w`, most significant first. It adds + up the two halves separately, so a long input takes close to linear + time rather than the quadratic time of adding one digit at a time to + a growing number. -/ +def ofDigits (w : Nat) (ds : List Nat) : Nat := go ds.length ds +where + /-- `fuel` bounds the depth of the halving; the length is enough. -/ + go : Nat → List Nat → Nat + | 0, _ => 0 + | fuel + 1, ds => + if ds.length ≤ 16 then ds.foldl (fun n d => n * 2 ^ w + d) 0 + else + let h := ds.length / 2 + (go fuel (ds.take h) <<< (w * (ds.length - h))) + go fuel (ds.drop h) + +/-- The digits of `n` in base `2 ^ w`, most significant first, put in + front of `acc`. `fuel` bounds the steps; `n` is enough. -/ +def toDigitsAux (w : Nat) : Nat → Nat → List Nat → List Nat + | 0, _, acc => acc + | fuel + 1, n, acc => if n = 0 then acc else toDigitsAux w fuel (n >>> w) (n % 2 ^ w :: acc) + +/-- The digits of `n` in base `2 ^ w`, most significant first, with no + leading zero: `[0]` for zero. -/ +def toDigits (w n : Nat) : List Nat := if n = 0 then [0] else toDigitsAux w n n [] + +/-! ## Writing -/ + +/-- The identifier octets for a tag (X.690 8.1.2). -/ +def identOctets (cls : Nat) (constructed : Bool) (tag : Nat) : List UInt8 := + let lead := cls % 4 * 64 + if constructed then 32 else 0 + if tag < 31 then [(lead + tag).toUInt8] + else + let ds := toDigits 7 tag + (lead + 31).toUInt8 :: (ds.dropLast.map fun d => (d + 128).toUInt8) ++ [(ds.getLastD 0).toUInt8] + +/-- The length octets for `n` contents octets, in the fewest octets + (X.690 8.1.3, 10.1). -/ +def lengthOctets (n : Nat) : List UInt8 := + if n < 128 then [n.toUInt8] + else + let ds := toDigits 8 n + (128 + ds.length).toUInt8 :: ds.map Nat.toUInt8 + +mutual +/-- The DER encoding of an element. -/ +def encode : Element → List UInt8 + | .prim cls tag c => identOctets cls false tag ++ lengthOctets c.length ++ c + | .cons cls tag cs => + let body := encodeList cs + identOctets cls true tag ++ lengthOctets body.length ++ body + +/-- The encodings of elements, one after another. -/ +def encodeList : List Element → List UInt8 + | [] => [] + | e :: es => encode e ++ encodeList es +end + +/-! ## Reading -/ + +/-- Splits off the subsequent octets of a tag number of 31 or more: the + octets with bit 8 one, then the first with it zero. Gives their bits + 7 to 1, most significant first, after `acc` reversed, and the octets + after them. -/ +def splitTag : List UInt8 → List Nat → Option (List Nat × List UInt8) + | [], _ => none + | b :: bs, acc => + if b.toNat < 128 then some ((b.toNat :: acc).reverse, bs) + else splitTag bs ((b.toNat - 128) :: acc) + +/-- The identifier octets at the start of the input: the class, whether + the element is constructed, the tag number, how many octets they + take, and the octets after them. Refuses a first subsequent octet + with bits 7 to 1 zero (8.1.2.4.2 c) and a number below 31 written in + more than one octet (8.1.2.2). -/ +def readIdentifier : List UInt8 → Option (Nat × Bool × Nat × Nat × List UInt8) + | [] => none + | b :: bs => + let cls := b.toNat / 64 + let constructed := b.toNat / 32 % 2 == 1 + if b.toNat % 32 < 31 then some (cls, constructed, b.toNat % 32, 1, bs) + else + match splitTag bs [] with + | some (d :: ds, rest) => + if d = 0 then none + else + let tag := ofDigits 7 (d :: ds) + if tag < 31 then none else some (cls, constructed, tag, ds.length + 2, rest) + | _ => none + +/-- The length given by `k` subsequent length octets of the long form + at the start of the input, how many length octets there are, and the + octets after them. Refuses a leading zero octet and a length below + 128, which are not the fewest octets (10.1). -/ +def readLong (k : Nat) (bs : List UInt8) : Option (Nat × Nat × List UInt8) := + let ds := bs.take k + if ds.length < k ∨ ds.head? = some 0 then none + else + let n := ofDigits 8 (ds.map UInt8.toNat) + if n < 128 then none else some (n, k + 1, bs.drop k) + +/-- The length octets at the start of the input: the length, how many + octets they take, and the octets after them. Refuses the indefinite + form (10.1), the initial octet all ones (8.1.3.5 c), and any length + not in the fewest octets (10.1). -/ +def readLength : List UInt8 → Option (Nat × Nat × List UInt8) + | [] => none + | b :: bs => + if b.toNat < 128 then some (b.toNat, 1, bs) + else if b.toNat - 128 = 0 ∨ b.toNat - 128 = 127 then none + else readLong (b.toNat - 128) bs + +/-- The universal tags of types encoded as constructed: 8, 11, 16, 17 + and 29. -/ +def constructedUniversal (tag : Nat) : Bool := + tag == 8 || tag == 11 || tag == 16 || tag == 17 || tag == 29 + +/-- The universal tags assigned to no type: 15, and 37 on. -/ +def reservedUniversal (tag : Nat) : Bool := tag == 15 || 37 ≤ tag + +/-- Contents octets of an integer in the fewest octets (8.3.1, 8.3.2). -/ +def intOk : List UInt8 → Bool + | [] => false + | [_] => true + | a :: b :: _ => !(a.toNat == 255 && 128 ≤ b.toNat) && !(a.toNat == 0 && b.toNat < 128) + +/-- Whether the contents of a primitive universal element are allowed: + a boolean is 0 or all ones (8.2, 11.1), an integer is in the fewest + octets (8.3), and a null is empty (8.8.2). Other types are not + checked in this slice. -/ +def contentsOk (tag : Nat) (c : List UInt8) : Bool := + match tag with + | 1 => c == [0] || c == [255] + | 2 => intOk c + | 5 => c.isEmpty + | _ => true + +/-- Whether a primitive element's tag and contents are allowed: in the + universal class, not tag 0 or a constructed type, and contents as + `contentsOk` says. -/ +def primOk (cls tag : Nat) (c : List UInt8) : Bool := + cls != 0 || (tag != 0 && !constructedUniversal tag && contentsOk tag c) + +/-- Whether a constructed element's tag is allowed. -/ +def consOk (cls tag : Nat) : Bool := + cls != 0 || constructedUniversal tag || reservedUniversal tag + +mutual +/-- The element at the start of `b`, if it takes at most `lim` octets: + the element, the octets it takes, and the octets after it. `fuel` + bounds the recursion; the length of the input is enough. -/ +def readElement : Nat → Nat → List UInt8 → Option (Element × Nat × List UInt8) + | 0, _, _ => none + | fuel + 1, lim, b => + match readIdentifier b with + | none => none + | some (cls, constructed, tag, n1, b1) => + match readLength b1 with + | none => none + | some (len, n2, b2) => + if lim < n1 + n2 + len then none + else if constructed then + if consOk cls tag then + match readElements fuel len b2 with + | some (cs, b3) => some (.cons cls tag cs, n1 + n2 + len, b3) + | none => none + else none + else + -- `take` stops at the end of the input, so this costs at most + -- the length of the contents. + let c := b2.take len + if c.length = len ∧ primOk cls tag c = true then + some (.prim cls tag c, n1 + n2 + len, b2.drop len) + else none + +/-- Elements one after another at the start of `b`, taking exactly `lim` + octets, and the octets after them. -/ +def readElements : Nat → Nat → List UInt8 → Option (List Element × List UInt8) + | _, 0, b => some ([], b) + | 0, _ + 1, _ => none + | fuel + 1, lim, b => + match readElement fuel lim b with + | some (e, n, b') => + match readElements fuel (lim - n) b' with + | some (es, b'') => some (e :: es, b'') + | none => none + | none => none +end + +/-- The DER element at the start of the input, and the octets after it, + or `none` when the input does not start with one. -/ +def decode (b : List UInt8) : Option (Element × List UInt8) := + match readElement b.length b.length b with + | some (e, _, rest) => some (e, rest) + | none => none + +/-- The DER element that is the whole input, or `none`. -/ +def decodeAll (b : List UInt8) : Option Element := + match decode b with + | some (e, []) => some e + | _ => none + +/-! ## Integers -/ + +/-- The integer that contents octets encode, or `none` when they are not + in the fewest octets (X.690 8.3). -/ +def readInt (c : List UInt8) : Option Int := + if intOk c then + let u := ofDigits 8 (c.map UInt8.toNat) + match c with + | a :: _ => some (if a.toNat < 128 then (u : Int) else (u : Int) - 2 ^ (8 * c.length)) + | [] => none + else none + +/-- The contents octets of an integer, in the fewest octets: for `z ≥ 0` + the octets of `z`, with a zero octet in front when bit 8 of the first + is one; for `z < 0` the same for `-z - 1`, with every bit flipped. -/ +def writeInt (z : Int) : List UInt8 := + let m := if 0 ≤ z then z.toNat else (-z - 1).toNat + let ds := (toDigits 8 m).map Nat.toUInt8 + let ds := match ds with + | d :: _ => if 128 ≤ d.toNat then 0 :: ds else ds + | [] => [0] + if 0 ≤ z then ds else ds.map (255 - ·) + +end Der diff --git a/Main.lean b/Main.lean new file mode 100644 index 0000000..f12093e --- /dev/null +++ b/Main.lean @@ -0,0 +1,196 @@ +/- + Command line front end. + + der decode HEX + + prints the element at the start of some octets, as a tree, then the + hex of the octets after it, or exits with 1 when they do not start + with an element this library accepts. HEX is lower-case hex digits in + pairs, or `-` for no octets; malformed HEX or wrong arguments exit + with 2. + + der serve + + answers requests on standard input, one per line, for the + differential tests. Octets are hex digits, two per octet, and an empty + field is written `-`. Numbers are decimal, an integer with `-` in + front when it is negative. + + decode HEX prints OK, the tree and the hex of the rest, or FAIL + (decode) + all HEX prints OK and the tree, or FAIL (decodeAll) + encode TREE prints OK and the hex (encode) + readint HEX prints OK and the integer, or FAIL (readInt) + writeint INT prints OK and the hex (writeInt) + + A tree is written with no spaces: a primitive element as `p`, its + class, `.`, its tag number, `.` and the hex of its contents, such as + `p0.2.0080`; a constructed one as `c`, its class, `.`, its tag number, + then its elements between `(` and `)`, separated by `,`, such as + `c0.16(p0.2.01,p0.5.-)`. + + A malformed request prints `error`: an unknown operation, the wrong + number of fields, hex digits that are not in pairs or not lower case, + a number that is not decimal, or a tree that does not parse. A + request line may end in LF or CR LF. + + Every answer comes from the proved functions in `Der/`. The hex and + tree coding and the request handling here are covered by the tests, + not by a proof. +-/ + +import Der + +open Der + +def hexValue (c : Char) : Option Nat := + if '0' ≤ c ∧ c ≤ '9' then some (c.toNat - '0'.toNat) + else if 'a' ≤ c ∧ c ≤ 'f' then some (c.toNat - 'a'.toNat + 10) + else none + +/-- Octets from hex digits in pairs. Tail recursive, so that long + inputs do not use the stack. -/ +def hexBytes (cs : List Char) (acc : Array UInt8) : Option (Array UInt8) := + match cs with + | [] => some acc + | a :: b :: rest => + match hexValue a, hexValue b with + | some hi, some lo => hexBytes rest (acc.push (hi * 16 + lo).toUInt8) + | _, _ => none + | [_] => none + +/-- The octets a field gives: `-` for none, else hex digits in pairs. -/ +def fromHex (f : String) : Option (List UInt8) := + if f = "-" then some [] else if f.isEmpty then none else (hexBytes f.toList #[]).map (·.toList) + +def hexDigit (n : Nat) : Char := + if n < 10 then Char.ofNat ('0'.toNat + n) else Char.ofNat ('a'.toNat + n - 10) + +/-- A field for a list of octets: `-` when it is empty. -/ +def toHex (bs : List UInt8) : String := + if bs.isEmpty then "-" + else String.ofList (bs.flatMap fun b => [hexDigit (b.toNat / 16), hexDigit (b.toNat % 16)]) + +/-- A decimal number, without sign or leading `+`. -/ +def decimal (s : String) : Option Nat := + if !s.isEmpty && s.all Char.isDigit then s.toNat? else none + +/-- A decimal integer, with `-` in front when it is negative. -/ +def integer (s : String) : Option Int := + if s.startsWith "-" then (decimal (String.ofList (s.toList.drop 1))).map fun n => -(n : Int) + else (decimal s).map fun n => (n : Int) + +mutual +/-- A tree as text. -/ +partial def showTree : Element → String + | .prim cls tag c => s!"p{cls}.{tag}.{toHex c}" + | .cons cls tag cs => s!"c{cls}.{tag}(" ++ showTrees cs ++ ")" + +partial def showTrees : List Element → String + | [] => "" + | [e] => showTree e + | e :: es => showTree e ++ "," ++ showTrees es +end + +/-- Digits at the start of the input, as a number, and what follows. -/ +def readNumber (cs : List Char) : Option (Nat × List Char) := + let ds := cs.takeWhile Char.isDigit + if ds.isEmpty then none else some ((String.ofList ds).toNat!, cs.drop ds.length) + +mutual +/-- A tree at the start of the input, and what follows. -/ +partial def parseTree (cs : List Char) : Option (Element × List Char) := do + match cs with + | 'p' :: rest => + let (cls, rest) ← readNumber rest + let '.' :: rest := rest | none + let (tag, rest) ← readNumber rest + let '.' :: rest := rest | none + let field := rest.takeWhile fun c => c != ',' && c != ')' + let c ← fromHex (String.ofList field) + pure (.prim cls tag c, rest.drop field.length) + | 'c' :: rest => + let (cls, rest) ← readNumber rest + let '.' :: rest := rest | none + let (tag, rest) ← readNumber rest + let '(' :: rest := rest | none + match rest with + | ')' :: rest => pure (.cons cls tag [], rest) + | _ => + let (es, rest) ← parseTrees rest + pure (.cons cls tag es, rest) + | _ => none + +/-- Trees separated by `,`, up to and with the closing `)`. -/ +partial def parseTrees (cs : List Char) : Option (List Element × List Char) := do + let (e, rest) ← parseTree cs + match rest with + | ',' :: rest => + let (es, rest) ← parseTrees rest + pure (e :: es, rest) + | ')' :: rest => pure ([e], rest) + | _ => none +end + +/-- A whole field as a tree. -/ +def treeField (f : String) : Option Element := + match parseTree f.toList with + | some (e, []) => some e + | _ => none + +/-- The answer to one request line, or `none` when it is malformed. -/ +def request (line : String) : Option String := + match line.splitOn " " with + | ["decode", h] => do + let b ← fromHex h + pure (match decode b with + | some (e, rest) => s!"OK {showTree e} {toHex rest}" + | none => "FAIL") + | ["all", h] => do + let b ← fromHex h + pure (match decodeAll b with + | some e => s!"OK {showTree e}" + | none => "FAIL") + | ["encode", t] => do + let e ← treeField t + pure s!"OK {toHex (encode e)}" + | ["readint", h] => do + let c ← fromHex h + pure (match readInt c with + | some z => s!"OK {z}" + | none => "FAIL") + | ["writeint", z] => do + let z ← integer z + pure s!"OK {toHex (writeInt z)}" + | _ => none + +/-- A request line without its line ending, LF or CR LF. -/ +def chomp (line : String) : String := + let cs := line.toList + if cs.getLast? = some '\n' then + let cs := cs.dropLast + String.ofList (if cs.getLast? = some '\r' then cs.dropLast else cs) + else line + +def serve : IO Unit := do + let stdin ← IO.getStdin + let stdout ← IO.getStdout + repeat + let line ← stdin.getLine + if line.isEmpty then break + stdout.putStrLn ((request (chomp line)).getD "error") + stdout.flush + +def usage : String := "usage: der decode HEX\n der serve" + +def main (args : List String) : IO UInt32 := do + match args with + | ["decode", h] => + match fromHex h with + | none => IO.eprintln "not lower-case hex digits in pairs"; return 2 + | some b => + match decode b with + | some (e, rest) => IO.println s!"{showTree e} {toHex rest}"; return 0 + | none => IO.eprintln "not a DER element"; return 1 + | ["serve"] => serve; return 0 + | _ => IO.eprintln usage; return 2 diff --git a/Proofs.lean b/Proofs.lean new file mode 100644 index 0000000..ca800ad --- /dev/null +++ b/Proofs.lean @@ -0,0 +1,5 @@ +import Proofs.Digits +import Proofs.Header +import Proofs.Element +import Proofs.Int +import Proofs.Main diff --git a/Proofs/Digits.lean b/Proofs/Digits.lean new file mode 100644 index 0000000..22b3f70 --- /dev/null +++ b/Proofs/Digits.lean @@ -0,0 +1,255 @@ +/- + Numbers written as digits in base `2 ^ w`: the code's `ofDigits` and + `toDigits`, and the specification's `digits`. +-/ + +import Spec.Defs +import Der + +namespace DerProofs + +open Der + +/-- Induction from the end of a list. -/ +theorem snoc_induction {α : Type} {P : List α → Prop} (nil : P []) + (snoc : ∀ l a, P l → P (l ++ [a])) (l : List α) : P l := by + have : ∀ r : List α, P r.reverse := by + intro r + induction r with + | nil => exact nil + | cons a r ih => rw [List.reverse_cons]; exact snoc _ a ih + simpa using this l.reverse + +/-- The value of digits in base `2 ^ w`, most significant first, one + digit at a time. -/ +def digitsVal (w : Nat) (ds : List Nat) : Nat := ds.foldl (fun n d => n * 2 ^ w + d) 0 + +theorem foldl_digits (w : Nat) (ds : List Nat) (a : Nat) : + ds.foldl (fun n d => n * 2 ^ w + d) a = a * 2 ^ (w * ds.length) + digitsVal w ds := by + induction ds generalizing a with + | nil => simp [digitsVal] + | cons d ds ih => + simp only [List.foldl_cons, List.length_cons, digitsVal] + rw [ih, ih (0 * 2 ^ w + d)] + rw [Nat.mul_succ, Nat.pow_add, Nat.zero_mul, Nat.zero_add, Nat.add_mul, Nat.mul_assoc, + Nat.mul_comm (2 ^ w) (2 ^ (w * ds.length)), Nat.add_assoc] + +theorem digitsVal_nil (w : Nat) : digitsVal w [] = 0 := rfl + +theorem digitsVal_append (w : Nat) (l1 l2 : List Nat) : + digitsVal w (l1 ++ l2) = digitsVal w l1 * 2 ^ (w * l2.length) + digitsVal w l2 := by + unfold digitsVal + rw [List.foldl_append, foldl_digits] + rfl + +theorem digitsVal_cons (w d : Nat) (ds : List Nat) : + digitsVal w (d :: ds) = d * 2 ^ (w * ds.length) + digitsVal w ds := by + have := digitsVal_append w [d] ds + simp only [List.singleton_append] at this + rw [this] + simp [digitsVal] + +theorem digitsVal_concat (w : Nat) (ds : List Nat) (d : Nat) : + digitsVal w (ds ++ [d]) = digitsVal w ds * 2 ^ w + d := by + rw [digitsVal_append] + simp [digitsVal] + +theorem ofDigits_go (w fuel : Nat) (ds : List Nat) (h : ds.length ≤ fuel) : + ofDigits.go w fuel ds = digitsVal w ds := by + induction fuel generalizing ds with + | zero => + have : ds = [] := List.eq_nil_of_length_eq_zero (by omega) + subst this + rfl + | succ fuel ih => + unfold ofDigits.go + split + · rfl + · simp only [Nat.shiftLeft_eq] + rw [ih _ (by simp; omega), ih _ (by simp; omega)] + conv => rhs; rw [← List.take_append_drop (ds.length / 2) ds] + rw [digitsVal_append, List.length_drop] + +theorem ofDigits_eq (w : Nat) (ds : List Nat) : ofDigits w ds = digitsVal w ds := + ofDigits_go w _ ds (Nat.le_refl _) + +/-- Digits below the base give a value below the base to the number of + digits. -/ +theorem digitsVal_lt (w : Nat) (ds : List Nat) (h : ∀ d ∈ ds, d < 2 ^ w) : + digitsVal w ds < 2 ^ (w * ds.length) := by + induction ds using snoc_induction with + | nil => simp [digitsVal] + | snoc ds d ih => + rw [digitsVal_concat, List.length_append, List.length_singleton, Nat.mul_succ, Nat.pow_add] + have h1 := ih (fun x hx => h x (by simp [hx])) + have h2 := h d (by simp) + have : (digitsVal w ds + 1) * 2 ^ w ≤ 2 ^ (w * ds.length) * 2 ^ w := + Nat.mul_le_mul_right _ h1 + rw [Nat.add_mul, Nat.one_mul] at this + omega + +/-- A first digit that is not zero gives a value of at least the base to + the number of digits after it. -/ +theorem le_digitsVal (w d : Nat) (ds : List Nat) (hd : d ≠ 0) : + 2 ^ (w * ds.length) ≤ digitsVal w (d :: ds) := by + rw [digitsVal_cons] + have : 1 * 2 ^ (w * ds.length) ≤ d * 2 ^ (w * ds.length) := + Nat.mul_le_mul_right _ (by omega) + omega + +/-! ## The specification's digits -/ + +theorem digits_of_lt {b n : Nat} (h : n < b) : Spec.digits b n = [n] := by + rw [Spec.digits] + simp [h] + +theorem digits_of_ge {b n : Nat} (hb : 2 ≤ b) (h : b ≤ n) : + Spec.digits b n = Spec.digits b (n / b) ++ [n % b] := by + rw [Spec.digits] + simp only [show ¬ (n < b ∨ b < 2) by omega, ↓reduceDIte] + +theorem digits_ne_nil (b n : Nat) : Spec.digits b n ≠ [] := by + rw [Spec.digits] + split <;> simp + +theorem digits_lt {b : Nat} (hb : 2 ≤ b) (n : Nat) : ∀ d ∈ Spec.digits b n, d < b := by + induction n using Nat.strongRecOn with + | ind n ih => + by_cases h : n < b + · rw [digits_of_lt h] + simpa using h + · rw [digits_of_ge hb (by omega)] + intro d hd + rcases List.mem_append.mp hd with hd | hd + · exact ih (n / b) (Nat.div_lt_self (by omega) (by omega)) d hd + · simp at hd + rw [hd] + exact Nat.mod_lt _ (by omega) + +/-- The digits of a number that is not zero start with a digit that is + not zero. -/ +theorem digits_head {b n : Nat} (hb : 2 ≤ b) (hn : n ≠ 0) : + ∃ d ds, Spec.digits b n = d :: ds ∧ d ≠ 0 := by + induction n using Nat.strongRecOn with + | ind n ih => + by_cases h : n < b + · exact ⟨n, [], digits_of_lt h, hn⟩ + · rw [digits_of_ge hb (by omega)] + obtain ⟨d, ds, he, hd⟩ := + ih (n / b) (Nat.div_lt_self (by omega) (by omega)) (by + have := Nat.div_pos (show b ≤ n by omega) (by omega : 0 < b) + omega) + exact ⟨d, ds ++ [n % b], by rw [he]; rfl, hd⟩ + +theorem digitsVal_digits (w : Nat) (hw : 1 ≤ w) (n : Nat) : + digitsVal w (Spec.digits (2 ^ w) n) = n := by + have hb : 2 ≤ 2 ^ w := by + have := Nat.pow_le_pow_right (by omega : 0 < 2) hw + simpa using this + induction n using Nat.strongRecOn with + | ind n ih => + by_cases h : n < 2 ^ w + · rw [digits_of_lt h] + simp [digitsVal] + · rw [digits_of_ge hb (by omega), digitsVal_concat, + ih (n / 2 ^ w) (Nat.div_lt_self (by omega) (by omega))] + exact Nat.div_add_mod' n (2 ^ w) + +/-- Digits below the base, with a first digit that is not zero (or the + single digit zero), are the digits of their value. -/ +theorem digits_digitsVal (w : Nat) (hw : 1 ≤ w) (ds : List Nat) (hne : ds ≠ []) + (hlt : ∀ d ∈ ds, d < 2 ^ w) (hhead : ds.head hne ≠ 0 ∨ ds.length = 1) : + Spec.digits (2 ^ w) (digitsVal w ds) = ds := by + have hb : 2 ≤ 2 ^ w := by + have := Nat.pow_le_pow_right (by omega : 0 < 2) hw + simpa using this + induction ds using snoc_induction with + | nil => exact absurd rfl hne + | snoc init d ih => + have hd : d < 2 ^ w := hlt d (by simp) + by_cases hi : init = [] + · subst hi + simp only [List.nil_append] + rw [show digitsVal w [d] = d by simp [digitsVal], digits_of_lt hd] + · have hlt' : ∀ x ∈ init, x < 2 ^ w := fun x hx => hlt x (by simp [hx]) + have hh : init.head hi ≠ 0 := by + rcases hhead with hh | hh + · obtain ⟨a, t, rfl⟩ := List.exists_cons_of_ne_nil hi + simpa using hh + · obtain ⟨a, t, rfl⟩ := List.exists_cons_of_ne_nil hi + simp at hh + have hpos : 0 < digitsVal w init := by + obtain ⟨a, t, rfl⟩ := List.exists_cons_of_ne_nil hi + have := le_digitsVal w a t (by simpa using hh) + have := Nat.two_pow_pos (w * t.length) + omega + rw [digitsVal_concat] + have hge : 2 ^ w ≤ digitsVal w init * 2 ^ w + d := by + have : 1 * 2 ^ w ≤ digitsVal w init * 2 ^ w := Nat.mul_le_mul_right _ hpos + omega + rw [digits_of_ge hb hge] + have e1 : (digitsVal w init * 2 ^ w + d) / 2 ^ w = digitsVal w init := by + rw [Nat.add_comm, Nat.add_mul_div_right _ _ (by omega), Nat.div_eq_of_lt hd, Nat.zero_add] + have e2 : (digitsVal w init * 2 ^ w + d) % 2 ^ w = d := by + rw [Nat.add_comm, Nat.add_mul_mod_self_right, Nat.mod_eq_of_lt hd] + rw [e1, e2, ih hi hlt' (Or.inl hh)] + +/-- The number of digits is at most `k` exactly when the number is below + the base to the `k`. -/ +theorem digits_length_le (w : Nat) (hw : 1 ≤ w) (n k : Nat) (hk : 1 ≤ k) : + (Spec.digits (2 ^ w) n).length ≤ k ↔ n < 2 ^ (w * k) := by + have hb : 2 ≤ 2 ^ w := by + have := Nat.pow_le_pow_right (by omega : 0 < 2) hw + simpa using this + constructor + · intro h + have hv := digitsVal_lt w (Spec.digits (2 ^ w) n) (digits_lt hb n) + rw [digitsVal_digits w hw] at hv + exact Nat.lt_of_lt_of_le hv (Nat.pow_le_pow_right (by omega) (Nat.mul_le_mul_left _ h)) + · intro h + by_cases hn : n = 0 + · subst hn + rw [digits_of_lt (by omega)] + simpa using hk + · obtain ⟨d, ds, he, hd⟩ := digits_head hb hn + have hle := le_digitsVal w d ds hd + rw [← he, digitsVal_digits w hw] at hle + rw [he] + simp only [List.length_cons] + have : w * ds.length < w * k := by + apply Nat.lt_of_not_le + intro hc + have := Nat.pow_le_pow_right (by omega : 0 < 2) hc + omega + have := Nat.lt_of_mul_lt_mul_left this + omega + +/-! ## The code's digits -/ + +theorem toDigitsAux_eq (w : Nat) (hw : 1 ≤ w) (fuel n : Nat) (acc : List Nat) (hn : 0 < n) + (hf : n ≤ fuel) : toDigitsAux w fuel n acc = Spec.digits (2 ^ w) n ++ acc := by + have hb : 2 ≤ 2 ^ w := by + have := Nat.pow_le_pow_right (by omega : 0 < 2) hw + simpa using this + induction fuel generalizing n acc with + | zero => omega + | succ fuel ih => + rw [toDigitsAux] + simp only [show ¬ n = 0 by omega, ↓reduceIte, Nat.shiftRight_eq_div_pow] + by_cases h : n < 2 ^ w + · rw [Nat.div_eq_of_lt h, digits_of_lt h, Nat.mod_eq_of_lt h] + cases fuel <;> simp [toDigitsAux] + · have hq : 0 < n / 2 ^ w := Nat.div_pos (by omega) (by omega) + have hq' : n / 2 ^ w < n := Nat.div_lt_self (by omega) (by omega) + rw [ih (n / 2 ^ w) _ hq (by omega), digits_of_ge (n := n) hb (by omega), List.append_assoc] + rfl + +theorem toDigits_eq (w : Nat) (hw : 1 ≤ w) (n : Nat) : toDigits w n = Spec.digits (2 ^ w) n := by + unfold toDigits + split + · rename_i h + subst h + rw [digits_of_lt (Nat.two_pow_pos w)] + · rw [toDigitsAux_eq w hw n n [] (by omega) (Nat.le_refl _), List.append_nil] + +end DerProofs diff --git a/Proofs/Element.lean b/Proofs/Element.lean new file mode 100644 index 0000000..271165e --- /dev/null +++ b/Proofs/Element.lean @@ -0,0 +1,301 @@ +/- + Elements: the writer writes `Spec.der`, and the reader accepts exactly + an encoding of an element this slice accepts, followed by anything. +-/ + +import Proofs.Header + +namespace DerProofs + +open Der + +theorem identifier_ne_nil (cls : Nat) (c : Bool) (tag : Nat) : + 1 ≤ (Spec.identifier cls c tag).length := by + by_cases h : tag ≤ 30 + · rw [identifier_low _ _ _ h]; simp + · rw [identifier_high _ _ _ (by omega)]; simp + +theorem lengthOctets_ne_nil (n : Nat) : 1 ≤ (Spec.lengthOctets n).length := by + by_cases h : n ≤ 127 + · rw [lengthOctets_short _ h]; simp + · rw [lengthOctets_long _ h]; simp + +theorem der_prim (cls tag : Nat) (c : List UInt8) : + Spec.der (.prim cls tag c) = Spec.identifier cls false tag ++ Spec.lengthOctets c.length ++ c := by + simp [Spec.der] + +theorem der_cons (cls tag : Nat) (cs : List Element) : + Spec.der (.cons cls tag cs) = + Spec.identifier cls true tag ++ Spec.lengthOctets (Spec.derList cs).length ++ Spec.derList cs := by + simp [Spec.der] + +theorem derList_nil : Spec.derList [] = [] := by simp [Spec.derList] + +theorem derList_cons (v : Element) (vs : List Element) : + Spec.derList (v :: vs) = Spec.der v ++ Spec.derList vs := by + simp [Spec.derList] + +theorem valid_prim (cls tag : Nat) (c : List UInt8) : + Spec.Valid (.prim cls tag c) ↔ cls < 4 ∧ Spec.PrimitiveOk cls tag c ∧ c.length < 256 ^ 126 := by + simp only [Spec.Valid] + +theorem valid_cons (cls tag : Nat) (cs : List Element) : + Spec.Valid (.cons cls tag cs) ↔ + cls < 4 ∧ Spec.ConstructedOk cls tag ∧ Spec.ValidList cs ∧ (Spec.derList cs).length < 256 ^ 126 := by + simp only [Spec.Valid] + +theorem validList_cons (v : Element) (vs : List Element) : + Spec.ValidList (v :: vs) ↔ Spec.Valid v ∧ Spec.ValidList vs := by + simp only [Spec.ValidList] + +theorem der_length_ge (v : Element) : 2 ≤ (Spec.der v).length := by + cases v with + | prim cls tag c => + rw [der_prim] + have := identifier_ne_nil cls false tag + have := lengthOctets_ne_nil c.length + simp only [List.length_append] + omega + | cons cls tag cs => + rw [der_cons] + have := identifier_ne_nil cls true tag + have := lengthOctets_ne_nil (Spec.derList cs).length + simp only [List.length_append] + omega + +mutual +theorem encode_eq' : ∀ v : Element, Spec.Valid v → encode v = Spec.der v + | .prim cls tag c, h => by + rw [valid_prim] at h + rw [der_prim] + simp only [encode, identOctets_eq _ _ _ h.1, lengthOctets_eq] + | .cons cls tag cs, h => by + rw [valid_cons] at h + rw [der_cons] + simp only [encode, identOctets_eq _ _ _ h.1, lengthOctets_eq, encodeList_eq cs h.2.2.1] +theorem encodeList_eq : ∀ vs : List Element, Spec.ValidList vs → encodeList vs = Spec.derList vs + | [], _ => by simp [encodeList, derList_nil] + | v :: vs, h => by + rw [validList_cons] at h + rw [derList_cons] + simp only [encodeList, encode_eq' v h.1, encodeList_eq vs h.2] +end + +theorem readElements_zero (f : Nat) (b : List UInt8) : readElements f 0 b = some ([], b) := by + cases f <;> simp [readElements] + +mutual +theorem readElement_der : ∀ (v : Element) (f lim : Nat) (rest : List UInt8), Spec.Valid v → + (Spec.der v).length ≤ f + 1 → (Spec.der v).length ≤ lim → + readElement f lim (Spec.der v ++ rest) = some (v, (Spec.der v).length, rest) + | .prim cls tag c, f, lim, rest, hv, hf, hl => by + rw [valid_prim] at hv + obtain ⟨hcls, hok, hlen⟩ := hv + have h1 := identifier_ne_nil cls false tag + have h2 := lengthOctets_ne_nil c.length + rw [der_prim] at hf hl ⊢ + simp only [List.length_append] at hf hl ⊢ + obtain ⟨f', rfl⟩ : ∃ f', f = f' + 1 := ⟨f - 1, by omega⟩ + rw [List.append_assoc, List.append_assoc] + unfold readElement + rw [readIdentifier_identifier _ _ _ _ hcls] + dsimp only + rw [readLength_lengthOctets _ _ hlen] + dsimp only + have ht : (c ++ rest).take c.length = c := List.take_left' rfl + have hd : (c ++ rest).drop c.length = rest := List.drop_left' rfl + rw [ht, hd, ite_eq_right (show ¬ lim < _ by omega)] + simp only [Bool.false_eq_true, ↓reduceIte, (primOk_iff cls tag c).mpr hok, and_self] + | .cons cls tag cs, f, lim, rest, hv, hf, hl => by + rw [valid_cons] at hv + obtain ⟨hcls, hok, hcs, hlen⟩ := hv + have h1 := identifier_ne_nil cls true tag + have h2 := lengthOctets_ne_nil (Spec.derList cs).length + rw [der_cons] at hf hl ⊢ + simp only [List.length_append] at hf hl ⊢ + obtain ⟨f', rfl⟩ : ∃ f', f = f' + 1 := ⟨f - 1, by omega⟩ + rw [List.append_assoc, List.append_assoc] + unfold readElement + rw [readIdentifier_identifier _ _ _ _ hcls] + dsimp only + rw [readLength_lengthOctets _ _ hlen] + dsimp only + rw [ite_eq_right (show ¬ lim < _ by omega)] + rw [readElements_der cs f' rest hcs (by omega)] + simp only [↓reduceIte, (consOk_iff cls tag).mpr hok] +theorem readElements_der : ∀ (vs : List Element) (f : Nat) (rest : List UInt8), Spec.ValidList vs → + (Spec.derList vs).length ≤ f → + readElements f (Spec.derList vs).length (Spec.derList vs ++ rest) = some (vs, rest) + | [], f, rest, _, _ => by + rw [derList_nil] + exact readElements_zero f rest + | v :: vs, f, rest, hv, hf => by + rw [validList_cons] at hv + have hd := der_length_ge v + rw [derList_cons] at hf ⊢ + simp only [List.length_append] at hf ⊢ + obtain ⟨f', rfl⟩ : ∃ f', f = f' + 1 := ⟨f - 1, by omega⟩ + obtain ⟨L, hL⟩ : ∃ L, (Spec.der v).length + (Spec.derList vs).length = L + 1 := + ⟨(Spec.der v).length + (Spec.derList vs).length - 1, by omega⟩ + rw [hL, List.append_assoc] + unfold readElements + rw [readElement_der v f' (L + 1) _ hv.1 (by omega) (by omega)] + dsimp only + rw [show L + 1 - (Spec.der v).length = (Spec.derList vs).length by omega, + readElements_der vs f' rest hv.2 (by omega)] +end + +theorem read_sound (f : Nat) : + (∀ lim b v n rest, readElement f lim b = some (v, n, rest) → + Spec.Valid v ∧ b = Spec.der v ++ rest ∧ n = (Spec.der v).length ∧ n ≤ lim) ∧ + (∀ lim b vs rest, readElements f lim b = some (vs, rest) → + Spec.ValidList vs ∧ b = Spec.derList vs ++ rest ∧ (Spec.derList vs).length = lim) := by + induction f with + | zero => + refine ⟨fun lim b v n rest h => by simp [readElement] at h, ?_⟩ + intro lim b vs rest h + cases lim with + | zero => + rw [readElements_zero] at h + simp only [Option.some.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl⟩ := h + exact ⟨by simp [Spec.ValidList], by rw [derList_nil]; rfl, by rw [derList_nil]; rfl⟩ + | succ l => simp [readElements] at h + | succ f ih => + obtain ⟨ih1, ih2⟩ := ih + constructor + · intro lim b v n rest h + unfold readElement at h + cases hid : readIdentifier b with + | none => rw [hid] at h; exact absurd h (Option.some_ne_none _).symm + | some p => + obtain ⟨cls, cons, tag, n1, b1⟩ := p + rw [hid] at h + dsimp only at h + obtain ⟨hcls, hb, hn1⟩ := readIdentifier_sound hid + cases hlr : readLength b1 with + | none => rw [hlr] at h; exact absurd h (Option.some_ne_none _).symm + | some q => + obtain ⟨len, n2, b2⟩ := q + rw [hlr] at h + dsimp only at h + obtain ⟨hlen, hb1, hn2⟩ := readLength_sound hlr + by_cases hlim : lim < n1 + n2 + len + · rw [ite_eq_left hlim] at h; exact absurd h (Option.some_ne_none _).symm + · rw [ite_eq_right hlim] at h + cases cons with + | true => + simp only [↓reduceIte] at h + by_cases hok : consOk cls tag = true + · rw [ite_eq_left hok] at h + cases hrs : readElements f len b2 with + | none => rw [hrs] at h; exact absurd h (Option.some_ne_none _).symm + | some r => + obtain ⟨cs, b3⟩ := r + rw [hrs] at h + simp only [Option.some.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl, rfl⟩ := h + obtain ⟨hcs, hb2, hl⟩ := ih2 _ _ _ _ hrs + subst hl + refine ⟨?_, ?_, ?_, by omega⟩ + · rw [valid_cons] + exact ⟨hcls, (consOk_iff cls tag).mp hok, hcs, hlen⟩ + · rw [der_cons, hb, hb1, hb2] + simp only [List.append_assoc] + · rw [der_cons, hn1, hn2] + simp only [List.length_append] + · rw [ite_eq_right hok] at h; exact absurd h (Option.some_ne_none _).symm + | false => + simp only [Bool.false_eq_true, ↓reduceIte] at h + by_cases hc : (b2.take len).length = len ∧ primOk cls tag (b2.take len) = true + · rw [ite_eq_left hc] at h + simp only [Option.some.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl, rfl⟩ := h + refine ⟨?_, ?_, ?_, by omega⟩ + · rw [valid_prim] + refine ⟨hcls, (primOk_iff _ _ _).mp hc.2, ?_⟩ + rw [hc.1]; exact hlen + · rw [der_prim, hb, hb1, hc.1] + simp only [List.append_assoc, List.take_append_drop] + · rw [der_prim, hn1, hn2] + simp only [List.length_append] + rw [hc.1] + · rw [ite_eq_right hc] at h; exact absurd h (Option.some_ne_none _).symm + · intro lim b vs rest h + cases lim with + | zero => + rw [readElements_zero] at h + simp only [Option.some.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl⟩ := h + exact ⟨by simp [Spec.ValidList], by rw [derList_nil]; rfl, by rw [derList_nil]; rfl⟩ + | succ l => + unfold readElements at h + cases he : readElement f (l + 1) b with + | none => rw [he] at h; exact absurd h (Option.some_ne_none _).symm + | some p => + obtain ⟨e, n, b'⟩ := p + rw [he] at h + dsimp only at h + cases hrs : readElements f (l + 1 - n) b' with + | none => rw [hrs] at h; exact absurd h (Option.some_ne_none _).symm + | some r => + obtain ⟨es, b''⟩ := r + rw [hrs] at h + simp only [Option.some.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl⟩ := h + obtain ⟨hve, hb, hn, hnl⟩ := ih1 _ _ _ _ _ he + obtain ⟨hves, hb', hl⟩ := ih2 _ _ _ _ hrs + refine ⟨(validList_cons _ _).mpr ⟨hve, hves⟩, ?_, ?_⟩ + · rw [derList_cons, hb, hb', List.append_assoc] + · rw [derList_cons, List.length_append, hl] + omega + +theorem decode_iff' (b : List UInt8) (v : Element) (rest : List UInt8) : + decode b = some (v, rest) ↔ Spec.Valid v ∧ b = Spec.der v ++ rest := by + constructor + · intro h + unfold decode at h + cases he : readElement b.length b.length b with + | none => rw [he] at h; exact absurd h (Option.some_ne_none _).symm + | some p => + obtain ⟨e, n, r⟩ := p + rw [he] at h + simp only [Option.some.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl⟩ := h + obtain ⟨hv, hb, -, -⟩ := (read_sound _).1 _ _ _ _ _ he + exact ⟨hv, hb⟩ + · rintro ⟨hv, rfl⟩ + unfold decode + rw [readElement_der v _ _ rest hv (by simp; omega) (by simp)] + +theorem encoding_unique' (v w : Element) (r s : List UInt8) (hv : Spec.Valid v) (hw : Spec.Valid w) + (h : Spec.der v ++ r = Spec.der w ++ s) : v = w ∧ r = s := by + have h1 := (decode_iff' (Spec.der v ++ r) v r).mpr ⟨hv, rfl⟩ + have h2 := (decode_iff' (Spec.der v ++ r) w s).mpr ⟨hw, h⟩ + rw [h1] at h2 + simp only [Option.some.injEq, Prod.mk.injEq] at h2 + exact h2 + +theorem decode_encode' (v : Element) (rest : List UInt8) (h : Spec.Valid v) : + decode (encode v ++ rest) = some (v, rest) := by + rw [encode_eq' v h] + exact (decode_iff' _ _ _).mpr ⟨h, rfl⟩ + +theorem decodeAll_iff' (b : List UInt8) (v : Element) : + decodeAll b = some v ↔ Spec.Valid v ∧ b = Spec.der v := by + unfold decodeAll + constructor + · intro h + split at h + · rename_i e he + simp only [Option.some.injEq] at h + subst h + have := (decode_iff' b e []).mp he + simpa using this + · exact absurd h (Option.some_ne_none _).symm + · rintro ⟨hv, rfl⟩ + have := (decode_iff' (Spec.der v ++ []) v []).mpr ⟨hv, rfl⟩ + rw [List.append_nil] at this + rw [this] + +end DerProofs diff --git a/Proofs/Header.lean b/Proofs/Header.lean new file mode 100644 index 0000000..06c39e8 --- /dev/null +++ b/Proofs/Header.lean @@ -0,0 +1,445 @@ +/- + Identifier octets and length octets: the writer gives what X.690 says, + and the reader accepts exactly that. Then what each node may be: the + code's checks against the specification's. +-/ + +import Proofs.Digits + +namespace DerProofs + +open Der + +theorem toNat_toUInt8 {x : Nat} (h : x < 256) : x.toUInt8.toNat = x := by + simp; omega + +def sub (ds : List Nat) : List UInt8 := + ds.dropLast.map (fun d => (d + 128).toUInt8) ++ [(ds.getLastD 0).toUInt8] + +theorem sub_cons (d : Nat) (ds : List Nat) (h : ds ≠ []) : + sub (d :: ds) = (d + 128).toUInt8 :: sub ds := by + unfold sub + rw [List.dropLast_cons_of_ne_nil h] + obtain ⟨x, xs, rfl⟩ := List.exists_cons_of_ne_nil h + simp [List.getLastD_eq_getLast?, List.getLast?_cons] + +theorem splitTag_cons (b : UInt8) (bs : List UInt8) (acc : List Nat) : + splitTag (b :: bs) acc = + if b.toNat < 128 then some ((b.toNat :: acc).reverse, bs) + else splitTag bs ((b.toNat - 128) :: acc) := rfl + +theorem splitTag_sub (ds : List Nat) (hne : ds ≠ []) (hlt : ∀ d ∈ ds, d < 128) + (rest : List UInt8) (acc : List Nat) : + splitTag (sub ds ++ rest) acc = some (acc.reverse ++ ds, rest) := by + induction ds generalizing acc with + | nil => exact absurd rfl hne + | cons d ds ih => + have hd : d < 128 := hlt d (by simp) + by_cases hds : ds = [] + · subst hds + have : sub [d] = [d.toUInt8] := rfl + rw [this, List.singleton_append, splitTag_cons, toNat_toUInt8 (show d < 256 by omega)] + simp only [hd, ↓reduceIte, List.reverse_cons] + · rw [sub_cons d ds hds, List.cons_append, splitTag_cons, + toNat_toUInt8 (show d + 128 < 256 by omega)] + simp only [show ¬ (d + 128 < 128) by omega, ↓reduceIte, Nat.add_sub_cancel] + rw [ih hds (fun x hx => hlt x (by simp [hx]))] + simp + +theorem two_pow_seven : (2 : Nat) ^ 7 = 128 := rfl + +theorem sub_length (ds : List Nat) (h : ds ≠ []) : (sub ds).length = ds.length := by + unfold sub + simp only [List.length_append, List.length_map, List.length_dropLast, List.length_singleton] + have := List.length_pos_iff.mpr h + omega + +theorem mapIdx_eq_sub (ds : List Nat) (h : ds ≠ []) : + ds.mapIdx (fun i d => (if i + 1 < ds.length then d + 128 else d).toUInt8) = sub ds := by + rw [← List.dropLast_concat_getLast h] + generalize ds.dropLast = init + generalize ds.getLast h = last + rw [List.mapIdx_append] + simp only [List.length_append, List.length_singleton, List.mapIdx_singleton, Nat.zero_add, + Nat.lt_irrefl, ↓reduceIte, sub, List.dropLast_concat, List.getLastD_concat] + congr 1 + apply List.ext_getElem (by simp) + intro i h1 h2 + simp only [List.getElem_mapIdx, List.getElem_map] + have : i + 1 < init.length + 1 := by simp at h1; omega + simp only [this, ↓reduceIte] + +theorem identifier_high (cls : Nat) (c : Bool) (tag : Nat) (h : 31 ≤ tag) : + Spec.identifier cls c tag = + (Spec.classAndForm cls c + 31).toUInt8 :: sub (Spec.digits 128 tag) := by + unfold Spec.identifier + simp only [show ¬ tag ≤ 30 by omega, ↓reduceIte] + rw [mapIdx_eq_sub _ (digits_ne_nil _ _)] + +theorem identifier_low (cls : Nat) (c : Bool) (tag : Nat) (h : tag ≤ 30) : + Spec.identifier cls c tag = [(Spec.classAndForm cls c + tag).toUInt8] := by + unfold Spec.identifier + simp only [h, ↓reduceIte] + +theorem classAndForm_eq (cls : Nat) (c : Bool) : + Spec.classAndForm cls c = cls * 64 + if c then 32 else 0 := rfl + +theorem readIdentifier_cons (b : UInt8) (bs : List UInt8) : + readIdentifier (b :: bs) = + if b.toNat % 32 < 31 then some (b.toNat / 64, b.toNat / 32 % 2 == 1, b.toNat % 32, 1, bs) + else + match splitTag bs [] with + | some (d :: ds, rest) => + if d = 0 then none + else + if ofDigits 7 (d :: ds) < 31 then none + else some (b.toNat / 64, b.toNat / 32 % 2 == 1, ofDigits 7 (d :: ds), ds.length + 2, rest) + | _ => none := rfl + +theorem lead_parts (cls : Nat) (c : Bool) (t : Nat) (hcls : cls < 4) (ht : t ≤ 31) : + (Spec.classAndForm cls c + t) % 32 = t ∧ (Spec.classAndForm cls c + t) / 64 = cls ∧ + ((Spec.classAndForm cls c + t) / 32 % 2 == 1) = c ∧ Spec.classAndForm cls c + t < 256 := by + rw [classAndForm_eq] + cases c <;> simp <;> omega + +theorem readIdentifier_identifier (cls : Nat) (c : Bool) (tag : Nat) (rest : List UInt8) + (hcls : cls < 4) : + readIdentifier (Spec.identifier cls c tag ++ rest) = + some (cls, c, tag, (Spec.identifier cls c tag).length, rest) := by + by_cases ht : tag ≤ 30 + · obtain ⟨h1, h2, h3, h4⟩ := lead_parts cls c tag hcls (by omega) + rw [identifier_low cls c tag ht, List.singleton_append, readIdentifier_cons, + toNat_toUInt8 h4, h1, h2, h3] + simp only [show tag < 31 by omega, ↓reduceIte, List.length_singleton] + · obtain ⟨h1, h2, h3, h4⟩ := lead_parts cls c 31 hcls (by omega) + rw [identifier_high cls c tag (by omega), List.cons_append, readIdentifier_cons, + toNat_toUInt8 h4, h1, h2, h3] + have hne := digits_ne_nil 128 tag + have hlt := digits_lt (by omega : 2 ≤ 128) tag + obtain ⟨d, ds, he, hd⟩ := digits_head (by omega : 2 ≤ 128) (show tag ≠ 0 by omega) + rw [splitTag_sub _ hne hlt rest [], he] + have hv : ofDigits 7 (d :: ds) = tag := by + rw [ofDigits_eq, ← he, ← two_pow_seven, digitsVal_digits 7 (by omega)] + simp only [Nat.lt_irrefl, ↓reduceIte, List.reverse_nil, List.nil_append, hd, hv, + show ¬ tag < 31 by omega, List.length_cons] + rw [sub_length _ (by simp)] + simp + +theorem splitTag_sound (bs : List UInt8) (acc out : List Nat) (rest : List UInt8) + (h : splitTag bs acc = some (out, rest)) : + ∃ ds, ds ≠ [] ∧ (∀ d ∈ ds, d < 128) ∧ bs = sub ds ++ rest ∧ out = acc.reverse ++ ds := by + induction bs generalizing acc with + | nil => simp [splitTag] at h + | cons b bs ih => + have hb := UInt8.toNat_lt b + rw [splitTag_cons] at h + by_cases hlt : b.toNat < 128 + · simp only [hlt, ↓reduceIte, Option.some.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl⟩ := h + refine ⟨[b.toNat], by simp, by simpa using hlt, ?_, by simp⟩ + show b :: bs = [b.toNat.toUInt8] ++ bs + simp + · simp only [hlt, ↓reduceIte] at h + obtain ⟨ds, hne, hlt', rfl, rfl⟩ := ih _ h + refine ⟨(b.toNat - 128) :: ds, by simp, ?_, ?_, by simp⟩ + · intro x hx + simp only [List.mem_cons] at hx + rcases hx with hx | hx + · rw [hx]; omega + · exact hlt' x hx + · have e : (b.toNat - 128 + 128).toUInt8 = b := by + apply UInt8.toNat_inj.mp + rw [toNat_toUInt8 (by omega)] + omega + rw [sub_cons _ _ hne, List.cons_append, e] + +theorem first_octet (b : UInt8) (t : Nat) (ht : t = b.toNat % 32) : + (Spec.classAndForm (b.toNat / 64) (b.toNat / 32 % 2 == 1) + t).toUInt8 = b := by + subst ht + apply UInt8.toNat_inj.mp + have hb := UInt8.toNat_lt b + rw [classAndForm_eq, toNat_toUInt8] + · by_cases h : b.toNat / 32 % 2 = 1 <;> simp [h] <;> omega + · by_cases h : b.toNat / 32 % 2 = 1 <;> simp [h] <;> omega + +theorem readIdentifier_sound {b : List UInt8} {cls : Nat} {c : Bool} {tag n : Nat} + {rest : List UInt8} (h : readIdentifier b = some (cls, c, tag, n, rest)) : + cls < 4 ∧ b = Spec.identifier cls c tag ++ rest ∧ n = (Spec.identifier cls c tag).length := by + cases b with + | nil => simp [readIdentifier] at h + | cons x bs => + have hx := UInt8.toNat_lt x + rw [readIdentifier_cons] at h + by_cases hlow : x.toNat % 32 < 31 + · simp only [hlow, ↓reduceIte, Option.some.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl, rfl, rfl, rfl⟩ := h + rw [identifier_low _ _ _ (by omega), first_octet x _ rfl] + exact ⟨by omega, rfl, rfl⟩ + · simp only [hlow, ↓reduceIte] at h + split at h + · rename_i d ds rest' hs + by_cases hd : d = 0 + · simp [hd] at h + · by_cases ht : ofDigits 7 (d :: ds) < 31 + · simp [hd, ht] at h + · simp only [hd, ht, ↓reduceIte, Option.some.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl, rfl, rfl, rfl⟩ := h + obtain ⟨ds0, hne, hlt, hbs, hout⟩ := splitTag_sound bs [] _ _ hs + simp only [List.reverse_nil, List.nil_append] at hout + subst hout + have hdig : Spec.digits 128 (ofDigits 7 (d :: ds)) = d :: ds := by + rw [ofDigits_eq, ← two_pow_seven] + exact digits_digitsVal 7 (by omega) _ (by simp) (by rwa [two_pow_seven]) + (Or.inl (by simpa using hd)) + refine ⟨by omega, ?_, ?_⟩ + · rw [identifier_high _ _ _ (by omega), hdig, hbs, List.cons_append, + first_octet x 31 (by omega)] + · rw [identifier_high _ _ _ (by omega), hdig, List.length_cons, + sub_length _ (by simp)] + simp + · simp at h + +theorem two_pow_eight : (2 : Nat) ^ 8 = 256 := rfl + +set_option exponentiation.threshold 1024 in +theorem bound_eq : (2 : Nat) ^ (8 * 126) = 256 ^ 126 := by + rw [Nat.pow_mul] + +theorem digits256_length_le (n : Nat) : (Spec.digits 256 n).length ≤ 126 ↔ n < 256 ^ 126 := by + rw [← two_pow_eight, digits_length_le 8 (by omega) n 126 (by omega), two_pow_eight, bound_eq] + +theorem le_bound : 256 ≤ (256 : Nat) ^ 126 := by + have := Nat.pow_le_pow_right (show 0 < 256 by omega) (show 1 ≤ 126 by omega) + rwa [Nat.pow_one] at this + +theorem lengthOctets_eq (n : Nat) : lengthOctets n = Spec.lengthOctets n := by + unfold lengthOctets Spec.lengthOctets + by_cases h : n < 128 + · simp only [h, ↓reduceIte, show n ≤ 127 by omega] + · simp only [h, ↓reduceIte, show ¬ n ≤ 127 by omega] + rw [toDigits_eq 8 (by omega), two_pow_eight] + +theorem lengthOctets_short (n : Nat) (h : n ≤ 127) : Spec.lengthOctets n = [n.toUInt8] := by + unfold Spec.lengthOctets + simp only [h, ↓reduceIte] + +theorem lengthOctets_long (n : Nat) (h : ¬ n ≤ 127) : + Spec.lengthOctets n = + (128 + (Spec.digits 256 n).length).toUInt8 :: (Spec.digits 256 n).map Nat.toUInt8 := by + unfold Spec.lengthOctets + simp only [h, ↓reduceIte] + +theorem readLength_cons (b : UInt8) (bs : List UInt8) : + readLength (b :: bs) = + if b.toNat < 128 then some (b.toNat, 1, bs) + else if b.toNat - 128 = 0 ∨ b.toNat - 128 = 127 then none + else readLong (b.toNat - 128) bs := rfl + +theorem readLong_eq (k : Nat) (bs : List UInt8) : + readLong k bs = + if (bs.take k).length < k ∨ (bs.take k).head? = some 0 then none + else + if ofDigits 8 ((bs.take k).map UInt8.toNat) < 128 then none + else some (ofDigits 8 ((bs.take k).map UInt8.toNat), k + 1, bs.drop k) := rfl + +theorem map_toNat_toUInt8 (ds : List Nat) (h : ∀ d ∈ ds, d < 256) : + (ds.map Nat.toUInt8).map UInt8.toNat = ds := by + rw [List.map_map] + conv => rhs; rw [← List.map_id ds] + apply List.map_congr_left + intro x hx + exact toNat_toUInt8 (h x hx) + +theorem readLength_lengthOctets (n : Nat) (rest : List UInt8) (h : n < 256 ^ 126) : + readLength (Spec.lengthOctets n ++ rest) = some (n, (Spec.lengthOctets n).length, rest) := by + by_cases hs : n ≤ 127 + · rw [lengthOctets_short n hs, List.singleton_append, readLength_cons, + toNat_toUInt8 (show n < 256 by omega)] + simp only [show n < 128 by omega, ↓reduceIte, List.length_singleton] + · rw [lengthOctets_long n hs, List.cons_append, readLength_cons] + have hk := (digits256_length_le n).mpr h + have hne := digits_ne_nil 256 n + have hk0 := List.length_pos_iff.mpr hne + have hlt := digits_lt (by omega : 2 ≤ 256) n + obtain ⟨d, ds', he, hd⟩ := digits_head (by omega : 2 ≤ 256) (show n ≠ 0 by omega) + have hv : ofDigits 8 (((Spec.digits 256 n).map Nat.toUInt8).map UInt8.toNat) = n := by + rw [map_toNat_toUInt8 _ hlt, ofDigits_eq, ← two_pow_eight, digitsVal_digits 8 (by omega)] + generalize hds : Spec.digits 256 n = ds at hk hne hk0 hlt he hv + subst he + rw [toNat_toUInt8 (show 128 + (d :: ds').length < 256 by omega)] + have htake : ((d :: ds').map Nat.toUInt8 ++ rest).take (d :: ds').length = + (d :: ds').map Nat.toUInt8 := List.take_left' (by simp) + have hdrop : ((d :: ds').map Nat.toUInt8 ++ rest).drop (d :: ds').length = rest := + List.drop_left' (by simp) + have hd' : d.toUInt8 ≠ 0 := by + intro h0 + have := congrArg UInt8.toNat h0 + rw [toNat_toUInt8 (hlt d (by simp))] at this + exact hd (by simpa using this) + have hcond : ¬ ((((d :: ds').map Nat.toUInt8).length < (d :: ds').length) ∨ + ((d :: ds').map Nat.toUInt8).head? = some 0) := by + simp [hd'] + clear h + rw [ite_eq_right (show ¬ (128 + (d :: ds').length < 128) by omega), Nat.add_sub_cancel_left, + ite_eq_right (show ¬ ((d :: ds').length = 0 ∨ (d :: ds').length = 127) by omega), + readLong_eq, htake, hdrop, ite_eq_right hcond, hv, ite_eq_right (show ¬ n < 128 by omega)] + simp + +theorem toUInt8_toNat (b : UInt8) : b.toNat.toUInt8 = b := by simp + +theorem readLength_sound {b : List UInt8} {n m : Nat} {rest : List UInt8} + (h : readLength b = some (n, m, rest)) : + n < 256 ^ 126 ∧ b = Spec.lengthOctets n ++ rest ∧ m = (Spec.lengthOctets n).length := by + cases b with + | nil => simp [readLength] at h + | cons x bs => + have hx := UInt8.toNat_lt x + rw [readLength_cons] at h + by_cases hs : x.toNat < 128 + · rw [ite_eq_left hs, Option.some.injEq, Prod.mk.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl, rfl⟩ := h + refine ⟨Nat.lt_of_lt_of_le (by omega) le_bound, ?_, ?_⟩ + · rw [lengthOctets_short _ (by omega), toUInt8_toNat] + rfl + · rw [lengthOctets_short _ (by omega)] + rfl + · rw [ite_eq_right hs] at h + by_cases hk : x.toNat - 128 = 0 ∨ x.toNat - 128 = 127 + · rw [ite_eq_left hk] at h + exact absurd h (Option.some_ne_none _).symm + · rw [ite_eq_right hk, readLong_eq] at h + generalize hkk : x.toNat - 128 = k at h hk + by_cases hbad : (bs.take k).length < k ∨ (bs.take k).head? = some 0 + · rw [ite_eq_left hbad] at h + exact absurd h (Option.some_ne_none _).symm + · rw [ite_eq_right hbad] at h + by_cases hn : ofDigits 8 ((bs.take k).map UInt8.toNat) < 128 + · rw [ite_eq_left hn] at h + exact absurd h (Option.some_ne_none _).symm + · rw [ite_eq_right hn, Option.some.injEq, Prod.mk.injEq, Prod.mk.injEq] at h + obtain ⟨rfl, rfl, rfl⟩ := h + have hk' : (bs.take k).length = k := by + have := List.length_take_le k bs + omega + have hall : ∀ y ∈ (bs.take k).map UInt8.toNat, y < 2 ^ 8 := by + intro y hy + obtain ⟨z, _, rfl⟩ := List.mem_map.mp hy + exact UInt8.toNat_lt z + have hne' : bs.take k ≠ [] := by + intro h0 + rw [h0] at hk' + simp at hk' + omega + have hne : (bs.take k).map UInt8.toNat ≠ [] := by + intro h0 + exact hne' (List.map_eq_nil_iff.mp h0) + have hhead : ((bs.take k).map UInt8.toNat).head hne ≠ 0 := by + obtain ⟨z, zs, hz⟩ := List.exists_cons_of_ne_nil hne' + simp only [hz, List.map_cons, List.head_cons] + intro h0 + apply hbad + right + rw [hz] + simp only [List.head?_cons, Option.some.injEq] + exact UInt8.toNat_inj.mp (by simpa using h0) + have hdig : Spec.digits 256 (ofDigits 8 ((bs.take k).map UInt8.toNat)) = + (bs.take k).map UInt8.toNat := by + rw [ofDigits_eq, ← two_pow_eight] + exact digits_digitsVal 8 (by omega) _ hne hall (Or.inl hhead) + have hlen : (Spec.digits 256 (ofDigits 8 ((bs.take k).map UInt8.toNat))).length ≤ 126 := by + rw [hdig, List.length_map] + omega + refine ⟨(digits256_length_le _).mp hlen, ?_, ?_⟩ + · rw [lengthOctets_long _ (by omega), hdig, List.length_map, hk', List.cons_append] + have e1 : (128 + k).toUInt8 = x := by + apply UInt8.toNat_inj.mp + rw [toNat_toUInt8 (by omega)] + omega + have e2 : ((bs.take k).map UInt8.toNat).map Nat.toUInt8 = bs.take k := by + rw [List.map_map] + conv => rhs; rw [← List.map_id (bs.take k)] + apply List.map_congr_left + intro y _ + simp + rw [e1, e2, List.take_append_drop] + · rw [lengthOctets_long _ (by omega), hdig] + simp only [List.length_cons, List.length_map, hk'] + +theorem identOctets_eq (cls : Nat) (c : Bool) (tag : Nat) (hcls : cls < 4) : + identOctets cls c tag = Spec.identifier cls c tag := by + unfold identOctets + rw [Nat.mod_eq_of_lt hcls] + by_cases ht : tag < 31 + · rw [identifier_low cls c tag (by omega)] + simp only [ht, ↓reduceIte] + rfl + · rw [identifier_high cls c tag (by omega)] + simp only [ht, ↓reduceIte] + rw [toDigits_eq 7 (by omega), two_pow_seven] + rfl + +theorem intOk_iff (c : List UInt8) : intOk c = true ↔ Spec.IntegerContents c := by + unfold Spec.IntegerContents + match c with + | [] => simp [intOk] + | [a] => simp [intOk] + | a :: b :: rest => + have e : intOk (a :: b :: rest) = + (!(a.toNat == 255 && 128 ≤ b.toNat) && !(a.toNat == 0 && b.toNat < 128)) := rfl + rw [e] + constructor + · intro h + refine ⟨by simp, ?_⟩ + rintro a' b' rest' he + simp only [List.cons.injEq] at he + obtain ⟨rfl, rfl, rfl⟩ := he + simp only [Bool.and_eq_true, Bool.not_eq_eq_eq_not, Bool.not_true, Bool.and_eq_false_iff, + beq_eq_false_iff_ne, decide_eq_false_iff_not] at h + omega + · rintro ⟨-, h⟩ + obtain ⟨h1, h2⟩ := h a b rest rfl + simp only [Bool.and_eq_true, Bool.not_eq_eq_eq_not, Bool.not_true, Bool.and_eq_false_iff, + beq_eq_false_iff_ne, decide_eq_false_iff_not] + omega + +theorem constructedUniversal_iff (tag : Nat) : + constructedUniversal tag = true ↔ Spec.ConstructedUniversal tag := by + unfold constructedUniversal Spec.ConstructedUniversal + simp [Bool.or_eq_true, beq_iff_eq, or_assoc] + +theorem reservedUniversal_iff (tag : Nat) : + reservedUniversal tag = true ↔ Spec.ReservedUniversal tag := by + unfold reservedUniversal Spec.ReservedUniversal + simp + +theorem contentsOk_iff (tag : Nat) (c : List UInt8) : + contentsOk tag c = true ↔ + (tag = 1 → c = [0] ∨ c = [255]) ∧ (tag = 2 → Spec.IntegerContents c) ∧ (tag = 5 → c = []) := by + unfold contentsOk + split + · simp + · simp [intOk_iff] + · simp [List.isEmpty_iff] + · rename_i h1 h2 h5 + simp only [true_iff] + exact ⟨fun h => absurd h h1, fun h => absurd h h2, fun h => absurd h h5⟩ + +theorem primOk_iff (cls tag : Nat) (c : List UInt8) : + primOk cls tag c = true ↔ Spec.PrimitiveOk cls tag c := by + unfold primOk Spec.PrimitiveOk + by_cases hc : cls = 0 + · subst hc + simp only [bne_self_eq_false, Bool.false_or, Bool.and_eq_true, bne_iff_ne, ne_eq, + Bool.not_eq_eq_eq_not, Bool.not_true, forall_const, contentsOk_iff] + rw [← Bool.not_eq_true, constructedUniversal_iff] + simp only [and_assoc] + · simp [hc] + +theorem consOk_iff (cls tag : Nat) : consOk cls tag = true ↔ Spec.ConstructedOk cls tag := by + unfold consOk Spec.ConstructedOk + by_cases hc : cls = 0 + · subst hc + simp [constructedUniversal_iff, reservedUniversal_iff] + · simp [hc] + +end DerProofs diff --git a/Proofs/Int.lean b/Proofs/Int.lean new file mode 100644 index 0000000..3d07457 --- /dev/null +++ b/Proofs/Int.lean @@ -0,0 +1,278 @@ +/- + Integers (X.690 8.3): `readInt` reads exactly the contents octets X.690 + allows, `writeInt` writes them, and each integer has one encoding. +-/ + +import Proofs.Header + +namespace DerProofs + +open Der + +/-- The octets as one unsigned number. -/ +def uval (c : List UInt8) : Nat := c.foldl (fun n o => n * 256 + o.toNat) 0 + +theorem uval_eq (c : List UInt8) : uval c = digitsVal 8 (c.map UInt8.toNat) := by + unfold uval digitsVal + rw [List.foldl_map] + +theorem integerValue_cons (a : UInt8) (t : List UInt8) : + Spec.integerValue (a :: t) = + if 128 ≤ a.toNat then (uval (a :: t) : Int) - 2 ^ (8 * (t.length + 1)) else uval (a :: t) := by + simp [Spec.integerValue, uval] + +theorem readInt_iff' (c : List UInt8) (z : Int) : + readInt c = some z ↔ Spec.IntegerContents c ∧ Spec.integerValue c = z := by + rw [← intOk_iff] + unfold readInt + cases c with + | nil => simp [intOk] + | cons a t => + rw [integerValue_cons] + have hu : ofDigits 8 ((a :: t).map UInt8.toNat) = uval (a :: t) := by + rw [ofDigits_eq, uval_eq] + by_cases hok : intOk (a :: t) = true + · simp only [hok, ↓reduceIte, true_and, hu, List.length_cons] + by_cases ha : a.toNat < 128 + · simp [ha, show ¬ 128 ≤ a.toNat by omega] + · simp [ha, show 128 ≤ a.toNat by omega] + · simp [hok] + +theorem toNat_comp (a : UInt8) : (255 - a).toNat = 255 - a.toNat := by + rw [UInt8.toNat_sub] + have := UInt8.toNat_lt a + simp + omega + +theorem comp_comp (a : UInt8) : 255 - (255 - a) = a := by + apply UInt8.toNat_inj.mp + rw [toNat_comp, toNat_comp] + have := UInt8.toNat_lt a + omega + +/-- Every bit flipped. -/ +def comp (c : List UInt8) : List UInt8 := c.map (255 - ·) + +theorem comp_comp_list (c : List UInt8) : comp (comp c) = c := by + unfold comp + rw [List.map_map] + conv => rhs; rw [← List.map_id c] + apply List.map_congr_left + intro a _ + exact comp_comp a + +theorem uval_cons (a : UInt8) (t : List UInt8) : + uval (a :: t) = a.toNat * 2 ^ (8 * t.length) + uval t := by + rw [uval_eq, uval_eq, List.map_cons, digitsVal_cons, List.length_map] + +theorem uval_lt (c : List UInt8) : uval c < 2 ^ (8 * c.length) := by + rw [uval_eq] + have := digitsVal_lt 8 (c.map UInt8.toNat) (by + intro y hy + obtain ⟨z, _, rfl⟩ := List.mem_map.mp hy + exact UInt8.toNat_lt z) + rwa [List.length_map] at this + +theorem uval_comp (c : List UInt8) : uval (comp c) + uval c + 1 = 2 ^ (8 * c.length) := by + induction c with + | nil => simp [comp, uval] + | cons a t ih => + have ha := UInt8.toNat_lt a + have e : comp (a :: t) = (255 - a) :: comp t := rfl + rw [e, uval_cons, uval_cons, toNat_comp, show (comp t).length = t.length by simp [comp], + List.length_cons, Nat.mul_succ, Nat.pow_add] + have : (255 - a.toNat) * 2 ^ (8 * t.length) + a.toNat * 2 ^ (8 * t.length) = + 255 * 2 ^ (8 * t.length) := by + rw [← Nat.add_mul]; congr 1; omega + have h256 : (2 : Nat) ^ 8 = 256 := rfl + rw [h256] + omega + +theorem integerContents_comp (c : List UInt8) : + Spec.IntegerContents (comp c) ↔ Spec.IntegerContents c := by + unfold Spec.IntegerContents comp + constructor + · rintro ⟨hne, h⟩ + refine ⟨fun h0 => hne (by simp [h0]), ?_⟩ + rintro a b rest rfl + obtain ⟨h1, h2⟩ := h (255 - a) (255 - b) (rest.map (255 - ·)) rfl + rw [toNat_comp, toNat_comp] at h1 h2 + have := UInt8.toNat_lt a + have := UInt8.toNat_lt b + constructor <;> omega + · rintro ⟨hne, h⟩ + refine ⟨fun h0 => hne (List.map_eq_nil_iff.mp h0), ?_⟩ + intro a' b' rest' he + cases c with + | nil => exact absurd rfl hne + | cons a t => + cases t with + | nil => simp at he + | cons b rest => + simp only [List.map_cons, List.cons.injEq] at he + obtain ⟨rfl, rfl, rfl⟩ := he + obtain ⟨h1, h2⟩ := h a b rest rfl + rw [toNat_comp, toNat_comp] + have := UInt8.toNat_lt a + have := UInt8.toNat_lt b + constructor <;> omega + +/-- What `writeInt` writes for `m ≥ 0`, before any bits are flipped. -/ +def pos (m : Nat) : List UInt8 := + match (toDigits 8 m).map Nat.toUInt8 with + | d :: ds => if 128 ≤ d.toNat then 0 :: d :: ds else d :: ds + | [] => [0] + +theorem writeInt_nonneg (z : Int) (h : 0 ≤ z) : writeInt z = pos z.toNat := by + unfold writeInt pos + simp only [h, ↓reduceIte] + split <;> simp_all + +theorem writeInt_neg (z : Int) (h : ¬ 0 ≤ z) : writeInt z = comp (pos (-z - 1).toNat) := by + unfold writeInt pos comp + simp only [h, ↓reduceIte] + split <;> simp_all + +theorem map_digits_toUInt8 (m : Nat) : + ((Spec.digits 256 m).map Nat.toUInt8).map UInt8.toNat = Spec.digits 256 m := + map_toNat_toUInt8 _ (digits_lt (by omega) m) + +theorem uval_digits (m : Nat) : uval ((Spec.digits 256 m).map Nat.toUInt8) = m := by + rw [uval_eq, map_digits_toUInt8, ← two_pow_eight, digitsVal_digits 8 (by omega)] + +theorem pos_spec (m : Nat) : + ∃ a t, pos m = a :: t ∧ a.toNat < 128 ∧ uval (pos m) = m ∧ Spec.IntegerContents (pos m) := by + unfold pos + rw [toDigits_eq 8 (by omega), two_pow_eight] + have hu := uval_digits m + have hlt := digits_lt (by omega : 2 ≤ 256) m + obtain ⟨d, ds, he⟩ := List.exists_cons_of_ne_nil (digits_ne_nil 256 m) + rw [he] at hu hlt ⊢ + have hd : d < 256 := hlt d (by simp) + simp only [List.map_cons, toNat_toUInt8 hd] + by_cases h128 : 128 ≤ d + · simp only [h128, ↓reduceIte] + refine ⟨0, _, rfl, by simp, ?_, ?_⟩ + · rw [uval_cons]; simp only [UInt8.toNat_zero, Nat.zero_mul, Nat.zero_add] + simpa using hu + · refine ⟨by simp, ?_⟩ + intro a b rest hab + simp only [List.cons.injEq] at hab + obtain ⟨rfl, rfl, -⟩ := hab + rw [toNat_toUInt8 hd] + simp only [UInt8.toNat_zero] + omega + · simp only [h128, ↓reduceIte] + refine ⟨d.toUInt8, _, rfl, by rw [toNat_toUInt8 hd]; omega, by simpa using hu, ?_⟩ + refine ⟨by simp, ?_⟩ + intro a b rest hab + simp only [List.cons.injEq] at hab + obtain ⟨rfl, hb⟩ := hab + rw [toNat_toUInt8 hd] + have hds : ds ≠ [] := by + intro h0 + rw [h0] at hb + simp at hb + have hd0 : d ≠ 0 := by + intro h0 + have hm : m ≠ 0 := by + intro hm0 + subst hm0 + rw [digits_of_lt (by omega)] at he + simp only [List.cons.injEq] at he + exact hds he.2.symm + obtain ⟨d', ds', he', hd'⟩ := digits_head (by omega : 2 ≤ 256) hm + rw [he] at he' + simp only [List.cons.injEq] at he' + omega + omega + +theorem pos_unique (c : List UInt8) (h : Spec.IntegerContents c) (a : UInt8) (t : List UInt8) + (hc : c = a :: t) (ha : a.toNat < 128) : pos (uval c) = c := by + subst hc + obtain ⟨-, hic⟩ := h + have hall : ∀ y ∈ List.map UInt8.toNat (a :: t), y < 2 ^ 8 := by + intro y hy + obtain ⟨z, _, rfl⟩ := List.mem_map.mp hy + exact UInt8.toNat_lt z + have back : ∀ l : List UInt8, (l.map UInt8.toNat).map Nat.toUInt8 = l := by + intro l + rw [List.map_map] + conv => rhs; rw [← List.map_id l] + apply List.map_congr_left + intro y _ + simp + by_cases ha0 : a.toNat = 0 + · cases t with + | nil => + have : a = 0 := UInt8.toNat_inj.mp (by simpa using ha0) + subst this + decide + | cons b t' => + have hb := (hic a b t' rfl).2 + have hb128 : 128 ≤ b.toNat := by omega + have hu : uval (a :: b :: t') = uval (b :: t') := by + rw [uval_cons, ha0, Nat.zero_mul, Nat.zero_add] + rw [hu] + unfold pos + rw [toDigits_eq 8 (by omega), uval_eq, digits_digitsVal 8 (by omega) _ (by simp) + (fun y hy => hall y (List.mem_cons_of_mem _ hy)) + (Or.inl (by simp; omega)), back] + simp only [hb128, ↓reduceIte, List.cons.injEq, and_true] + exact UInt8.toNat_inj.mp (by simpa using ha0.symm) + · unfold pos + rw [toDigits_eq 8 (by omega), uval_eq, digits_digitsVal 8 (by omega) _ (by simp) hall + (Or.inl (by simpa using ha0)), back] + simp only [show ¬ 128 ≤ a.toNat by omega, ↓reduceIte] + +theorem integerValue_low (a : UInt8) (t : List UInt8) (ha : a.toNat < 128) : + Spec.integerValue (a :: t) = uval (a :: t) := by + rw [integerValue_cons] + simp [show ¬ 128 ≤ a.toNat by omega] + +theorem integerValue_high (a : UInt8) (t : List UInt8) (ha : 128 ≤ a.toNat) : + Spec.integerValue (a :: t) = (uval (a :: t) : Int) - ((2 ^ (8 * (t.length + 1)) : Nat) : Int) := by + rw [integerValue_cons] + simp [ha] + +theorem writeInt_spec' (z : Int) : + Spec.IntegerContents (writeInt z) ∧ Spec.integerValue (writeInt z) = z := by + by_cases hz : 0 ≤ z + · rw [writeInt_nonneg z hz] + obtain ⟨a, t, he, ha, hu, hic⟩ := pos_spec z.toNat + refine ⟨hic, ?_⟩ + rw [he, integerValue_low a t ha, ← he, hu] + omega + · rw [writeInt_neg z hz] + obtain ⟨a, t, he, ha, hu, hic⟩ := pos_spec (-z - 1).toNat + refine ⟨(integerContents_comp _).mpr hic, ?_⟩ + have hcomp := uval_comp (pos (-z - 1).toNat) + rw [hu] at hcomp + have e : comp (pos (-z - 1).toNat) = (255 - a) :: comp t := by rw [he]; rfl + have hl : (comp t).length = t.length := by simp [comp] + rw [e, integerValue_high _ _ (by rw [toNat_comp]; omega), ← e, hl] + have hlen : (pos (-z - 1).toNat).length = t.length + 1 := by rw [he]; rfl + rw [hlen] at hcomp + omega + +theorem writeInt_unique' (c : List UInt8) (h : Spec.IntegerContents c) : + writeInt (Spec.integerValue c) = c := by + obtain ⟨a, t, rfl⟩ := List.exists_cons_of_ne_nil h.1 + by_cases ha : a.toNat < 128 + · rw [integerValue_low a t ha, writeInt_nonneg _ (by omega)] + simp only [Int.toNat_natCast] + exact pos_unique _ h a t rfl ha + · rw [integerValue_high a t (by omega)] + have hlt := uval_lt (a :: t) + have hcomp := uval_comp (a :: t) + rw [writeInt_neg _ (by simp only [List.length_cons] at hlt; omega)] + have hm : (-((uval (a :: t) : Int) - ((2 ^ (8 * (t.length + 1)) : Nat) : Int)) - 1).toNat = + uval (comp (a :: t)) := by + simp only [List.length_cons] at hcomp + omega + rw [hm] + have hic : Spec.IntegerContents (comp (a :: t)) := (integerContents_comp _).mpr h + rw [pos_unique (comp (a :: t)) hic (255 - a) (comp t) rfl (by rw [toNat_comp]; omega), + comp_comp_list] + +end DerProofs diff --git a/Proofs/Main.lean b/Proofs/Main.lean new file mode 100644 index 0000000..727d25e --- /dev/null +++ b/Proofs/Main.lean @@ -0,0 +1,77 @@ +/- + The statements of `Spec.lean`, each proved from the lemmas in the + other files of `Proofs/`. `lake comparator` checks that each theorem + here is exactly the one in `Spec.lean`. +-/ + +import Proofs.Element +import Proofs.Int + +namespace Spec + +open Der + +theorem encode_eq (v : Element) (h : Valid v) : Der.encode v = der v := + DerProofs.encode_eq' v h + +theorem decode_iff (b : List UInt8) (v : Element) (rest : List UInt8) : + Der.decode b = some (v, rest) ↔ Valid v ∧ b = der v ++ rest := + DerProofs.decode_iff' b v rest + +theorem encoding_unique (v w : Element) (r s : List UInt8) (hv : Valid v) (hw : Valid w) + (h : der v ++ r = der w ++ s) : v = w ∧ r = s := + DerProofs.encoding_unique' v w r s hv hw h + +theorem decode_encode (v : Element) (rest : List UInt8) (h : Valid v) : + Der.decode (Der.encode v ++ rest) = some (v, rest) := + DerProofs.decode_encode' v rest h + +theorem decodeAll_iff (b : List UInt8) (v : Element) : + Der.decodeAll b = some v ↔ Valid v ∧ b = der v := + DerProofs.decodeAll_iff' b v + +theorem readInt_iff (c : List UInt8) (z : Int) : + Der.readInt c = some z ↔ IntegerContents c ∧ integerValue c = z := + DerProofs.readInt_iff' c z + +theorem writeInt_spec (z : Int) : + IntegerContents (Der.writeInt z) ∧ integerValue (Der.writeInt z) = z := + DerProofs.writeInt_spec' z + +theorem writeInt_unique (c : List UInt8) (h : IntegerContents c) : + Der.writeInt (integerValue c) = c := + DerProofs.writeInt_unique' c h + +theorem x690_examples : + Spec.lengthOctets 38 = [0x26] ∧ Spec.lengthOctets 201 = [0x81, 0xC9] ∧ + Der.decode [0x01, 0x01, 0xFF] = some (.prim 0 1 [0xFF], []) ∧ + Der.decode [0x1A, 0x05, 0x4A, 0x6F, 0x6E, 0x65, 0x73] = + some (.prim 0 26 [0x4A, 0x6F, 0x6E, 0x65, 0x73], []) := by + refine ⟨?_, ?_, rfl, rfl⟩ + · rw [DerProofs.lengthOctets_short 38 (by omega)] + rfl + · rw [DerProofs.lengthOctets_long 201 (by omega), DerProofs.digits_of_lt (by omega)] + rfl + +theorem x690_rejects : + Der.decode [0x3A, 0x09, 0x04, 0x03, 0x4A, 0x6F, 0x6E, 0x04, 0x02, 0x65, 0x73] = none ∧ + Der.decode [0x3A, 0x80, 0x04, 0x03, 0x4A, 0x6F, 0x6E, 0x04, 0x02, 0x65, 0x73, 0x00, 0x00] = + none ∧ + Der.decode [0x04, 0x81, 0x01, 0x00] = none ∧ + Der.decode [0x04, 0x82, 0x00, 0x01, 0x00] = none ∧ + Der.decode [0x04, 0xFF, 0x00] = none ∧ + Der.decode [0x1F, 0x05, 0x00] = none ∧ + Der.decode [0x1F, 0x80, 0x1F, 0x00] = none ∧ + Der.decode [0x01, 0x01, 0x01] = none ∧ + Der.decode [0x01, 0x02, 0xFF, 0xFF] = none ∧ + Der.decode [0x02, 0x02, 0x00, 0x7F] = none ∧ + Der.decode [0x02, 0x02, 0xFF, 0x80] = none ∧ + Der.decode [0x02, 0x00] = none ∧ + Der.decode [0x05, 0x01, 0x00] = none ∧ + Der.decode [0x22, 0x03, 0x02, 0x01, 0x00] = none ∧ + Der.decode [0x10, 0x00] = none ∧ + Der.decode [0x00, 0x00] = none ∧ + Der.decode [0x04, 0x05, 0x00] = none := by + refine ⟨rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl⟩ + +end Spec diff --git a/Spec.lean b/Spec.lean new file mode 100644 index 0000000..6ba43a8 --- /dev/null +++ b/Spec.lean @@ -0,0 +1,113 @@ +/- + The statements this library is proved to meet, for a reviewer to + approve. Each `sorry` is proved in `Proofs/` under the same name, and + `lake comparator` checks that each proof proves exactly the statement + here. + + The definitions they use are in `Spec/Defs.lean`, written from X.690, + and the tree type is in `Spec/Element.lean`. The functions they are + about are in `Der/Basic.lean`. +-/ + +import Spec.Defs +import Der + +namespace Spec + +open Der + +/-! ## Elements -/ + +/-- The writer writes the DER encoding of every element this slice + accepts. -/ +theorem encode_eq (v : Element) (h : Valid v) : Der.encode v = der v := sorry + +/-- The reader accepts exactly a DER encoding of an element this slice + accepts, followed by anything, and gives back the element and what + follows. So it never reads past the end of the input, every element + it gives is inside the contents of its parent, and writing back what + it read gives the same octets. -/ +theorem decode_iff (b : List UInt8) (v : Element) (rest : List UInt8) : + Der.decode b = some (v, rest) ↔ Valid v ∧ b = der v ++ rest := sorry + +/-- Each element has one encoding: two accepted elements whose encodings + start two inputs that are the same are the same, and so is what + follows them. -/ +theorem encoding_unique (v w : Element) (r s : List UInt8) (hv : Valid v) (hw : Valid w) + (h : der v ++ r = der w ++ s) : v = w ∧ r = s := sorry + +/-- Reading what the writer wrote gives back the element and what + follows. -/ +theorem decode_encode (v : Element) (rest : List UInt8) (h : Valid v) : + Der.decode (Der.encode v ++ rest) = some (v, rest) := sorry + +/-- The whole input is one element exactly when it is a DER encoding of + an element this slice accepts. -/ +theorem decodeAll_iff (b : List UInt8) (v : Element) : + Der.decodeAll b = some v ↔ Valid v ∧ b = der v := sorry + +/-! ## Integers (8.3) -/ + +/-- `readInt` accepts exactly the contents octets X.690 allows for an + integer, and gives the integer they encode. -/ +theorem readInt_iff (c : List UInt8) (z : Int) : + Der.readInt c = some z ↔ IntegerContents c ∧ integerValue c = z := sorry + +/-- `writeInt` writes contents octets X.690 allows, which encode the + integer. -/ +theorem writeInt_spec (z : Int) : + IntegerContents (Der.writeInt z) ∧ integerValue (Der.writeInt z) = z := sorry + +/-- Each integer has one encoding: allowed contents octets are what + `writeInt` writes for the integer they encode. -/ +theorem writeInt_unique (c : List UInt8) (h : IntegerContents c) : + Der.writeInt (integerValue c) = c := sorry + +/-! ## Examples -/ + +/-- The examples of X.690: a length of 38 in the short form (8.1.3.4) + and of 201 in the long form (8.1.3.5), the boolean TRUE as DER + writes it (8.2.2, 11.1), and the primitive form of the VisibleString + "Jones" (8.23.5), which DER uses. -/ +theorem x690_examples : + Spec.lengthOctets 38 = [0x26] ∧ Spec.lengthOctets 201 = [0x81, 0xC9] ∧ + Der.decode [0x01, 0x01, 0xFF] = some (.prim 0 1 [0xFF], []) ∧ + Der.decode [0x1A, 0x05, 0x4A, 0x6F, 0x6E, 0x65, 0x73] = + some (.prim 0 26 [0x4A, 0x6F, 0x6E, 0x65, 0x73], []) := sorry + +/-- Inputs the reader refuses, each for one rule: + - the two constructed forms of "Jones" in 8.23.5, which DER forbids + (10.2), the second with the indefinite length (10.1); + - a length in the long form below 128, and with a leading zero + octet (10.1); the initial length octet all ones (8.1.3.5 c); + - a tag number below 31 in the high form (8.1.2.2), and a first + subsequent octet with bits 7 to 1 zero (8.1.2.4.2 c); + - a boolean TRUE that is not all ones (11.1), and of two octets + (8.2.1); + - integers with a redundant leading octet (8.3.2) and with none + (8.3.1); + - a null with contents (8.8.2), a constructed integer (8.3.1), a + primitive sequence (8.9.1), and the end-of-contents octets + (8.1.5); + - contents that run past the end of the input. -/ +theorem x690_rejects : + Der.decode [0x3A, 0x09, 0x04, 0x03, 0x4A, 0x6F, 0x6E, 0x04, 0x02, 0x65, 0x73] = none ∧ + Der.decode [0x3A, 0x80, 0x04, 0x03, 0x4A, 0x6F, 0x6E, 0x04, 0x02, 0x65, 0x73, 0x00, 0x00] = + none ∧ + Der.decode [0x04, 0x81, 0x01, 0x00] = none ∧ + Der.decode [0x04, 0x82, 0x00, 0x01, 0x00] = none ∧ + Der.decode [0x04, 0xFF, 0x00] = none ∧ + Der.decode [0x1F, 0x05, 0x00] = none ∧ + Der.decode [0x1F, 0x80, 0x1F, 0x00] = none ∧ + Der.decode [0x01, 0x01, 0x01] = none ∧ + Der.decode [0x01, 0x02, 0xFF, 0xFF] = none ∧ + Der.decode [0x02, 0x02, 0x00, 0x7F] = none ∧ + Der.decode [0x02, 0x02, 0xFF, 0x80] = none ∧ + Der.decode [0x02, 0x00] = none ∧ + Der.decode [0x05, 0x01, 0x00] = none ∧ + Der.decode [0x22, 0x03, 0x02, 0x01, 0x00] = none ∧ + Der.decode [0x10, 0x00] = none ∧ + Der.decode [0x00, 0x00] = none ∧ + Der.decode [0x04, 0x05, 0x00] = none := sorry + +end Spec diff --git a/Spec/Defs.lean b/Spec/Defs.lean new file mode 100644 index 0000000..330b4fa --- /dev/null +++ b/Spec/Defs.lean @@ -0,0 +1,170 @@ +/- + The definitions the statements in `Spec.lean` use, written from + Rec. ITU-T X.690 (02/2021), the Basic and Distinguished Encoding Rules + of ASN.1, https://www.itu.int/rec/T-REC-X.690, and from Table 1 of + Rec. ITU-T X.680 (02/2021), the universal class tag assignments. + Section numbers below are those of X.690 unless they name X.680. + + This file is part of the specification: a reviewer approves it with + `Spec.lean` and `Spec/Element.lean`. It shares nothing with the code in + `Der/` but the type `Der.Element`. +-/ + +import Spec.Element + +namespace Spec + +open Der + +/-! ## Numbers written as digits -/ + +/-- The digits of `n` in base `b`, most significant first, with no + leading zero: `[0]` for zero. -/ +def digits (b n : Nat) : List Nat := + if _h : n < b ∨ b < 2 then [n] else digits b (n / b) ++ [n % b] +termination_by n +decreasing_by exact Nat.div_lt_self (by omega) (by omega) + +/-! ## Identifier octets (8.1.2) -/ + +/-- Bits 8 to 6 of the first identifier octet: the class in bits 8 and 7, + as Table 1 of X.690 gives it (8.1.2.2 a, 8.1.2.4.1 a), and bit 6 one + for a constructed encoding, zero for a primitive one (8.1.2.5). -/ +def classAndForm (cls : Nat) (constructed : Bool) : Nat := + cls * 64 + if constructed then 32 else 0 + +/-- The identifier octets for a tag of class `cls` and number `tag`. + + A number from 0 to 30 takes a single octet, the number in bits 5 to 1 + (8.1.2.2). A number of 31 or more takes a leading octet with bits 5 + to 1 all ones (8.1.2.4.1 c), then subsequent octets: the number in + base 128, most significant first, seven bits to an octet, with bit 8 + one in every octet but the last (8.1.2.4.2 a and b). The first + subsequent octet does not have bits 7 to 1 all zero (8.1.2.4.2 c), so + the digits have no leading zero. -/ +def identifier (cls : Nat) (constructed : Bool) (tag : Nat) : List UInt8 := + if tag ≤ 30 then [(classAndForm cls constructed + tag).toUInt8] + else + let ds := digits 128 tag + (classAndForm cls constructed + 31).toUInt8 :: + ds.mapIdx fun i d => (if i + 1 < ds.length then d + 128 else d).toUInt8 + +/-! ## Length octets (8.1.3, 10.1) -/ + +/-- The length octets for `n` contents octets, as DER writes them: "The + definite form of length encoding shall be used, encoded in the + minimum number of octets" (10.1). + + Up to 127, the short form: one octet, bit 8 zero, the length in bits + 7 to 1 (8.1.3.4). Above 127, the long form (8.1.3.5): an initial + octet with bit 8 one and the number of subsequent octets in bits 7 + to 1, then the length in base 256, most significant first, in as few + octets as it takes. -/ +def lengthOctets (n : Nat) : List UInt8 := + if n ≤ 127 then [n.toUInt8] + else + let ds := digits 256 n + (128 + ds.length).toUInt8 :: ds.map Nat.toUInt8 + +/-! ## Encodings (8.1.1) -/ + +mutual +/-- The DER encoding of an element: identifier octets, length octets and + contents octets (8.1.1.1). With the definite form there are no + end-of-contents octets (8.1.5). The contents octets of a constructed + element are the encodings of its elements, one after another + (8.1.1.1 b, 8.9.2, 8.10.2). -/ +def der : Element → List UInt8 + | .prim cls tag c => identifier cls false tag ++ lengthOctets c.length ++ c + | .cons cls tag cs => identifier cls true tag ++ lengthOctets (derList cs).length ++ derList cs + +/-- The encodings of elements, one after another. -/ +def derList : List Element → List UInt8 + | [] => [] + | e :: es => der e ++ derList es +end + +/-! ## Integers (8.3) -/ + +/-- Contents octets X.690 allows for an integer: one or more octets + (8.3.1), and when there are two or more, the bits of the first octet + and bit 8 of the second are not all ones and not all zero (8.3.2). -/ +def IntegerContents (c : List UInt8) : Prop := + c ≠ [] ∧ + ∀ a b rest, c = a :: b :: rest → + ¬ (a.toNat = 255 ∧ 128 ≤ b.toNat) ∧ ¬ (a.toNat = 0 ∧ b.toNat < 128) + +/-- The integer that contents octets encode, a two's complement binary + number (8.3.3 and its note): the octets read as one unsigned number, + most significant first, less 2 to the power of 8 times the number of + octets when bit 8 of the first octet is one. -/ +def integerValue (c : List UInt8) : Int := + let u : Nat := c.foldl (fun n o => n * 256 + o.toNat) 0 + match c with + | a :: _ => if 128 ≤ a.toNat then (u : Int) - 2 ^ (8 * c.length) else u + | [] => 0 + +/-! ## What DER allows (8, 10, 11) -/ + +/-- The universal tags of types that X.690 encodes as constructed: 8, + external and instance-of (8.18.1, 8.16.1); 11, embedded-pdv (8.17.1); + 16, sequence and sequence-of (8.9.1, 8.10.1); 17, set and set-of + (8.11.1, 8.12.1); and 29, the unrestricted character string (8.24.1). + X.680 gives these numbers in 37.2, 36.2, Table 1 and 44.2. -/ +def ConstructedUniversal (tag : Nat) : Prop := + tag = 8 ∨ tag = 11 ∨ tag = 16 ∨ tag = 17 ∨ tag = 29 + +/-- The universal tags that X.680's Table 1 assigns to no type: 15, + "Reserved for future editions", and 37 on, "Reserved for addenda". + This slice accepts both forms for them. -/ +def ReservedUniversal (tag : Nat) : Prop := tag = 15 ∨ 37 ≤ tag + +/-- What this slice requires of a primitive element of class `cls`, + number `tag` and contents `c`. Outside the universal class, nothing. + In it: + - number 0 is "Reserved for use by the encoding rules" (X.680 Table + 1); DER uses it for nothing, since end-of-contents octets go with + the indefinite form only (8.1.5, 10.1); + - the five numbers of `ConstructedUniversal` are not primitive; + - a boolean has one contents octet (8.2.1), 0 for false (8.2.2) and + all ones for true (11.1); + - an integer has the contents `IntegerContents` describes (8.3); + - a null has no contents octets (8.8.2). + + Every other universal type is primitive in DER: by 8.2.1, 8.3.1, + 8.4, 8.5.1, 8.8.1, 8.19.1, 8.20.1, 8.21.1, 8.22.1 and 8.26, and for + bit strings, octet strings and the restricted character strings, + with the useful types defined from them (8.25), by 10.2. This slice + checks the contents of no other type. -/ +def PrimitiveOk (cls tag : Nat) (c : List UInt8) : Prop := + cls = 0 → + tag ≠ 0 ∧ ¬ ConstructedUniversal tag ∧ + (tag = 1 → c = [0] ∨ c = [255]) ∧ + (tag = 2 → IntegerContents c) ∧ + (tag = 5 → c = []) + +/-- What this slice requires of the tag of a constructed element: in the + universal class, one of the five numbers of `ConstructedUniversal`, + or a reserved number. Every other universal type is primitive in DER + (see `PrimitiveOk`). -/ +def ConstructedOk (cls tag : Nat) : Prop := + cls = 0 → ConstructedUniversal tag ∨ ReservedUniversal tag + +mutual +/-- An element DER can encode and this slice accepts. Its class is one of + the four of Table 1. Its length fits the long form, whose initial + octet may not be all ones (8.1.3.5 c), so at most 126 subsequent + octets. And its tag and contents are as `PrimitiveOk` and + `ConstructedOk` say. -/ +def Valid : Element → Prop + | .prim cls tag c => cls < 4 ∧ PrimitiveOk cls tag c ∧ c.length < 256 ^ 126 + | .cons cls tag cs => + cls < 4 ∧ ConstructedOk cls tag ∧ ValidList cs ∧ (derList cs).length < 256 ^ 126 + +/-- Each element of a list is `Valid`. -/ +def ValidList : List Element → Prop + | [] => True + | e :: es => Valid e ∧ ValidList es +end + +end Spec diff --git a/Spec/Element.lean b/Spec/Element.lean new file mode 100644 index 0000000..006713d --- /dev/null +++ b/Spec/Element.lean @@ -0,0 +1,23 @@ +/- + The tree a DER element reads into. Part of the specification: a + reviewer approves it with `Spec.lean` and `Spec/Defs.lean`. The code in + `Der/` and the definitions in `Spec/Defs.lean` both use it, and it + holds no logic. +-/ + +namespace Der + +/-- One element of an encoding (X.690 8.1.1): its tag, given by a class + and a number (8.1.2), and its contents (8.1.4). + + The class is 0 for universal, 1 for application, 2 for + context-specific and 3 for private, as in Table 1 of X.690. + + A primitive element holds its contents octets. A constructed element + holds the elements its contents octets encode, in order. -/ +inductive Element where + | prim (cls tag : Nat) (contents : List UInt8) + | cons (cls tag : Nat) (children : List Element) + deriving Repr, Inhabited + +end Der diff --git a/bar.toml b/bar.toml new file mode 100644 index 0000000..4f7b3d7 --- /dev/null +++ b/bar.toml @@ -0,0 +1,31 @@ +# Read by bar/check.py in Proven-Commons/mission. +# See playbook/verified-bar.md there for what each field means. + +[project] +name = "der" +# "in-progress" until every bar item passes, then "verified". +status = "in-progress" +page = "README.md" +# GitHub handles of everyone who wrote the specification. It was drafted +# by Claude, an AI agent, for the org: agent fdn-claude, for the fdn-0 +# account, posting as zero-aska on GitHub. +spec_authors = ["claude", "fdn-0", "zero-aska"] +# The program people run. A change to it voids the approval. +entry_points = ["Main.lean"] + +[spec] +files = ["SPEC.md", "Spec.lean", "Spec/Defs.lean", "Spec/Element.lean"] +# The code under proof: the functions the statements are about. +code = ["Der.encode", "Der.decode", "Der.decodeAll", "Der.readInt", "Der.writeInt"] + +# One entry per approval, added in a pull request the reviewer approves. +# [[spec.approval]] +# reviewer = "github-handle" +# date = "2026-11-02" +# sha256 = "..." +# record = "https://github.com/Proven-Commons/der/pull/" + +[tests] +results = "tests/TEST-RESULTS.md" +command = "python3 tests/run_tests.py" +files = ["tests/run_tests.py", "tests/plant_bugs.py"] diff --git a/comparator.json b/comparator.json new file mode 100644 index 0000000..e05016e --- /dev/null +++ b/comparator.json @@ -0,0 +1,10 @@ +{ + "challenge_module": "Spec", + "solution_module": "Proofs", + "theorem_names": [ + "Spec.encode_eq", "Spec.decode_iff", "Spec.encoding_unique", "Spec.decode_encode", + "Spec.decodeAll_iff", "Spec.readInt_iff", "Spec.writeInt_spec", "Spec.writeInt_unique", + "Spec.x690_examples", "Spec.x690_rejects" + ], + "permitted_axioms": ["propext", "Quot.sound", "Classical.choice"] +} diff --git a/lake-manifest.json b/lake-manifest.json new file mode 100644 index 0000000..4292a2a --- /dev/null +++ b/lake-manifest.json @@ -0,0 +1,6 @@ +{"version": "1.3.0", + "packagesDir": ".lake/packages", + "packages": [], + "name": "der", + "lakeDir": ".lake", + "fixedToolchain": false} diff --git a/lakefile.toml b/lakefile.toml new file mode 100644 index 0000000..519e31f --- /dev/null +++ b/lakefile.toml @@ -0,0 +1,16 @@ +name = "der" +version = "0.1.0" +defaultTargets = ["Der", "Spec", "Proofs", "der"] + +[[lean_lib]] +name = "Der" + +[[lean_lib]] +name = "Spec" + +[[lean_lib]] +name = "Proofs" + +[[lean_exe]] +name = "der" +root = "Main" diff --git a/lean-toolchain b/lean-toolchain new file mode 100644 index 0000000..a6aff06 --- /dev/null +++ b/lean-toolchain @@ -0,0 +1 @@ +leanprover/lean4:v4.35.0 From 9b6ae76ee3062ab3bde3331650626318ebb477f0 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 18:05:13 +0000 Subject: [PATCH 2/8] Print trees in time linear in their size showTree nested string concatenations, so a million-deep tree took minutes to print; it now appends each piece to one string. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AovXVgJBHGiN44TxQM4dcd --- Main.lean | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Main.lean b/Main.lean index f12093e..d333ba4 100644 --- a/Main.lean +++ b/Main.lean @@ -81,17 +81,22 @@ def integer (s : String) : Option Int := else (decimal s).map fun n => (n : Int) mutual -/-- A tree as text. -/ -partial def showTree : Element → String - | .prim cls tag c => s!"p{cls}.{tag}.{toHex c}" - | .cons cls tag cs => s!"c{cls}.{tag}(" ++ showTrees cs ++ ")" - -partial def showTrees : List Element → String - | [] => "" - | [e] => showTree e - | e :: es => showTree e ++ "," ++ showTrees es +/-- A tree as text, after `s`. Each piece is added to the end of one + string, so a deep tree takes time linear in its size. -/ +partial def showTreeInto (s : String) : Element → String + | .prim cls tag c => s ++ s!"p{cls}.{tag}." ++ toHex c + | .cons cls tag cs => showTreesInto (s ++ s!"c{cls}.{tag}(") cs ++ ")" + +/-- Trees separated by `,`, after `s`. -/ +partial def showTreesInto (s : String) : List Element → String + | [] => s + | [e] => showTreeInto s e + | e :: es => showTreesInto (showTreeInto s e ++ ",") es end +/-- A tree as text. -/ +def showTree (e : Element) : String := showTreeInto "" e + /-- Digits at the start of the input, as a number, and what follows. -/ def readNumber (cs : List Char) : Option (Nat × List Char) := let ds := cs.takeWhile Char.isDigit From b64e157651883c39b43b7bf51236d8643d8726ac Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 18:07:09 +0000 Subject: [PATCH 3/8] README and SPEC for the first slice The page names each theorem behind each claim, what is not proved (other types' contents, schema-dependent rules, time, Main.lean), what must be trusted, and how to re-check. SPEC.md states the encoding and the accepted elements in plain words, from X.690 and X.680 Table 1. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AovXVgJBHGiN44TxQM4dcd --- README.md | 110 +++++++++++++++++++++++++++++++++++++++++------------- SPEC.md | 65 ++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 26 deletions(-) create mode 100644 SPEC.md diff --git a/README.md b/README.md index 1976658..a7e05d6 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,101 @@ -# der +# der: reading and writing DER (X.690), proved -A Lean reader for ASN.1 DER elements. This is a Proven Commons project: it rebuilds a widely used format in Lean and proves the new code follows its written specification. +DER, the Distinguished Encoding Rules of ASN.1, is how X.509 certificates, ECDSA signatures, PKCS and CMS are written. Every TLS client and every signature checker reads it from bytes someone else chose. DER allows exactly one encoding of each value, and much of its security rests on that. When a reader accepts a second encoding of the same value, a signature can be forged against a lenient checker, or two parsers disagree about what a certificate says. Malformed lengths have also given out-of-bounds reads more than once. -Status: not verified. No code has landed yet. Plan issue 1 holds the first slice, and it is unclaimed. The specification has not been approved by a reviewer, so do not call this verified. +This library reads and writes one DER element, nested to any depth, in Lean: -## First slice +- identifier octets, with tag numbers of any size (X.690 8.1.2); +- length octets, definite and in the fewest octets (8.1.3, 10.1); +- the contents of a primitive element, or the elements of a constructed one; +- the form, primitive or constructed, that DER gives each universal type, and the contents of BOOLEAN, INTEGER and NULL. -DER is the strict subset of BER defined in X.690. The first slice reads one DER element, nested to any depth. +Machine-checked proofs show: -- Definite lengths only, in the shortest length form. -- The shortest tag form. -- INTEGER without a redundant leading byte. -- BOOLEAN as 00 or FF. +- **The reader accepts exactly DER.** It accepts an input exactly when the input starts with the DER encoding of an element this slice accepts. It then gives back the element and the octets after it. So it never reads past the end of its input, and every element it gives lies inside its parent's contents. +- **Each element has one encoding.** Two elements whose encodings start the same input are equal. Writing back what the reader read gives the same octets. +- **The writer writes DER.** It writes, for every such element, the encoding X.690 gives it. +- **Integers have one encoding.** `readInt` accepts exactly the contents octets X.690 allows for an INTEGER and gives their value. `writeInt` writes them, and each integer has exactly one. -A second slice reads ECDSA-Sig-Value, a SEQUENCE of two INTEGERs. +It is tested against a model of X.690 and other implementations; `tests/TEST-RESULTS.md` holds the record. It is a project of [Proven Commons](https://github.com/Proven-Commons/mission), built to the verified bar published there. -## Main claim +Status: in progress. The specification has not yet been approved by a reviewer apart from its author, so this is not verified. -The reader is non-malleable. For every input it accepts, writing the result back gives the same bytes, and each value has one encoding. The reader returns an error instead of reading past the end, and each child stays inside its parent. +## Use it -## Test oracles +From Lean, on `List UInt8`, with the tree `Der.Element` of `Spec/Element.lean`: -- Wycheproof ECDSA vectors, which flag signatures in BER form and with broken encodings. -- Go encoding/asn1 and cryptobyte. -- rust-asn1, a DER only Rust library. -- OpenSSL asn1parse. +- `Der.decode : List UInt8 → Option (Element × List UInt8)`: the element at the start of the input, and the octets after it. +- `Der.decodeAll : List UInt8 → Option Element`: the element that is the whole input. +- `Der.encode : Element → List UInt8`. +- `Der.readInt : List UInt8 → Option Int` and `Der.writeInt : Int → List UInt8`: the contents octets of an INTEGER. -Prior proofs exist in F*, Agda, and Verus, so this build is worth doing only if later Lean work imports it. Reading names out of a certificate is one such use: the hostname project needs it. +An element is `.prim cls tag contents` or `.cons cls tag children`, with the class 0 (universal), 1 (application), 2 (context-specific) or 3 (private). `SPEC.md` says what each function does. -## Score +## What is proved -16 out of 18, from two scorers, in the round 3 sheet in the mission repo. The sheet is mission pull request 17. +The statements are in `Spec.lean`. The definitions they use are in `Spec/Defs.lean`, written from X.690 (02/2021) and Table 1 of X.680, and the proofs are in `Proofs/`. -## What is not in this slice +| Claim | Theorem | +|-------|---------| +| The writer writes the DER encoding of every element this slice accepts. | `Spec.encode_eq` | +| The reader accepts exactly a DER encoding of such an element, followed by anything, and gives back the element and what follows. | `Spec.decode_iff` | +| Two such elements whose encodings start the same input are the same, and so is what follows. | `Spec.encoding_unique` | +| Reading what the writer wrote gives back the element. | `Spec.decode_encode` | +| The whole input is one element exactly when it is such an encoding. | `Spec.decodeAll_iff` | +| `readInt` accepts exactly the contents of an INTEGER that X.690 allows, and gives the integer. | `Spec.readInt_iff` | +| `writeInt` writes such contents for the integer, and they are the only ones. | `Spec.writeInt_spec`, `Spec.writeInt_unique` | +| X.690's examples: lengths 38 and 201, BOOLEAN TRUE, and the primitive "Jones" of 8.23.5. Seventeen inputs that each break one rule are refused, among them the two constructed forms of "Jones". | `Spec.x690_examples`, `Spec.x690_rejects` | -- Full X.509 certificate parsing. -- BER forms outside DER. -- Any claim about a full ASN.1 implementation. +The elements this slice accepts, `Spec.Valid`, are those whose class is one of the four, whose lengths fit in at most 126 length octets, and whose universal tags follow the rules of `SPEC.md`: -## Layout, when the first slice lands +- tag 0 is not used; +- 8, 11, 16, 17 and 29 are constructed; +- 15 and 37 and up may be either; +- every other universal tag is primitive; +- a BOOLEAN is 00 or FF, an INTEGER is in the fewest octets, and a NULL is empty. -The layout follows the Proven Commons template in the mission repo, under bar/template. Spec.lean holds the statements a reviewer approves. Proofs.lean holds the proofs under the same names. Main.lean holds the command line tool used by the tests. The bar check runs in CI on every change. +## What is not proved + +- **The contents of other types.** BIT STRING unused bits, OBJECT IDENTIFIER subidentifiers, the string types, the times and REAL are read as octets and not checked. ECDSA-Sig-Value is the second slice. +- **Rules that depend on the ASN.1 type.** DER orders the components of a SET by tag (10.3) and of a SET OF by encoding (11.6), and leaves out components equal to their default (11.5). A reader with no schema cannot check these, so this one does not. +- **Time and memory.** No proof bounds them. The reader is written to take time linear in its input: it never measures more of the input than an element's length, and it adds up long tag numbers and lengths by halves. Its recursion depth is the nesting depth. The tests time deep and long inputs. +- **Reading requests and printing answers in `Main.lean`.** The differential tests cover them. + +## What you must trust + +- The Lean kernel and the five external kernels shipped with Lean, which all check the proofs. +- `lake comparator`, which builds the statements and the proofs in a sandbox, exports them, and checks that each proof proves exactly its statement. +- bubblewrap and the Linux kernel's namespaces, which keep the untrusted build from tampering with that check. +- The Lean compiler, with the C compiler and linker shipped with it, and the runtime, which turn the proved definitions into the program that runs. +- Lean's core library, including the compiled code it puts in place of some definitions the proofs reason about, such as natural number and integer arithmetic, `UInt8`, and the conversion between strings and their characters. +- The operating system and hardware it runs on. +- For the tests only: the model in `tests/run_tests.py`, and the implementations it compares against. The record names their versions. + +This project has no dependencies beyond Lean itself. + +## Re-check it + +Needs Linux, [elan](https://github.com/leanprover/elan), bubblewrap (`apt install bubblewrap`) and Python 3. The oracles also need Go, Rust (cargo) and OpenSSL's headers; the test script leaves out any it cannot build, and the record names them. + +``` +lake build +lake comparator --paranoid +python3 tests/run_tests.py +python3 tests/plant_bugs.py +``` + +`lake comparator --paranoid` builds the statements and the proofs in a sandbox. It checks that each theorem in `Proofs/` proves exactly the statement in `Spec.lean`, using only the axioms `propext`, `Classical.choice`, and `Quot.sound`, and replays it through every kernel shipped with Lean. + +## Layout + +- `Der/Basic.lean`: the code under proof. +- `Spec.lean`: the statements, with `sorry` for proofs. A reviewer approves this file. +- `Spec/Defs.lean`: the definitions the statements use, written from X.690. +- `Spec/Element.lean`: the tree an element reads into. +- `Proofs.lean` and `Proofs/`: the proofs. +- `Main.lean`: the command-line tool the tests drive. +- `lakefile.toml`: the build. It names the package and its version, its libraries and executable, and the default targets, and sets nothing else. +- `comparator.json`: which theorems `lake comparator` checks, and which axioms it allows. +- `bar.toml`: the record for the bar check: status, authors, approvals, and where things are. +- `SPEC.md`: the specification in plain words. +- `tests/`: the differential tests, the oracles, and the planted bugs. diff --git a/SPEC.md b/SPEC.md new file mode 100644 index 0000000..295efef --- /dev/null +++ b/SPEC.md @@ -0,0 +1,65 @@ +# Specification: der + +DER, the Distinguished Encoding Rules of ASN.1 (Rec. ITU-T X.690, https://www.itu.int/rec/T-REC-X.690), writes each value in exactly one way. X.509 certificates, ECDSA signatures, PKCS and CMS all use it. This library reads and writes one DER element, nested to any depth: + +- its identifier octets: the class and number of its tag, and whether it is primitive or constructed (8.1.2); +- its length octets, in the definite form and the fewest octets (8.1.3, 10.1); +- its contents: octets for a primitive element, and the elements they encode for a constructed one (8.1.4). + +It checks the contents of three universal types, BOOLEAN, INTEGER and NULL, and the form (primitive or constructed) of every universal type. The contents of other types (BIT STRING, OBJECT IDENTIFIER, strings, times, REAL) are a later slice. So is the order of SET components, which depends on the ASN.1 type. + +A reviewer who did not write this specification approves it. The approval also covers `Spec.lean`, `Spec/Defs.lean`, `Spec/Element.lean`, and the other files that `bar.toml` lists. + +## Elements + +`Spec/Element.lean` gives the tree: an element is primitive, with a class, a tag number and contents octets, or constructed, with a class, a tag number and a list of elements. The class is 0 (universal), 1 (application), 2 (context-specific) or 3 (private), as in Table 1 of X.690. + +`Spec/Defs.lean` writes the DER encoding of a tree, `Spec.der`, from X.690: + +- **Identifier octets** (8.1.2). A tag number up to 30 is one octet: the class in bits 8 and 7, bit 6 one for constructed, the number in bits 5 to 1. A larger number is a leading octet with bits 5 to 1 all ones, then the number in base 128, most significant first, bit 8 set on every octet but the last, and no leading zero digit. +- **Length octets** (8.1.3, 10.1). DER uses the definite form "encoded in the minimum number of octets": a single octet up to 127, and above that an octet giving the number of octets that follow, then the length in base 256 with no leading zero. +- **Contents octets** (8.1.4). A constructed element's contents are the encodings of its elements, one after another. + +`Spec.Valid` says which trees this slice accepts: + +- the class is one of the four; +- the length fits the long form, whose first octet may not be all ones (8.1.3.5 c), so at most 126 octets follow it; +- in the universal class, tag 0 is not used (X.680 Table 1 reserves it for the encoding rules, and DER has no end-of-contents octets); +- tags 8, 11, 16, 17 and 29 (external, embedded-pdv, sequence, set, character string) are constructed, tags 15 and 37 and up (reserved in X.680) may be either, and every other universal tag is primitive, as clauses 8 and 10.2 require; +- a BOOLEAN's contents are 00 or FF (8.2, 11.1), an INTEGER's are one or more octets in the fewest (8.3.1, 8.3.2), and a NULL's are empty (8.8.2). + +**The writer** writes `Spec.der` for every tree this slice accepts. + +**The reader** accepts exactly the octet strings that start with `Spec.der` of a tree this slice accepts, and gives back the tree and the octets after it. Since `Spec.der` puts each element's encoding inside its parent's contents, every element the reader gives lies inside its parent, and the reader never reads past the end of its input. Since the reader is a function, each tree has one encoding it accepts: DER's main property. Reading the whole input as one element is the same with nothing after it. + +## Integers + +`Spec.IntegerContents` is 8.3.1 and 8.3.2: one or more octets, and when there are two or more, the bits of the first octet and bit 8 of the second are not all ones and not all zero. `Spec.integerValue` is 8.3.3: the octets as a two's complement number. + +**`readInt`** accepts exactly the allowed contents and gives the integer they encode. **`writeInt`** writes allowed contents that encode the integer. Each integer has one encoding: allowed contents are what `writeInt` writes for their value. + +## What must always hold + +Each property is a theorem in `Spec.lean`, under the name given. + +| Property | Theorem | +|----------|---------| +| The writer writes the DER encoding of every tree this slice accepts. | `Spec.encode_eq` | +| The reader accepts exactly an encoding of such a tree followed by anything, and gives back the tree and what follows. | `Spec.decode_iff` | +| Two trees with encodings at the start of the same input are the same, and so is what follows. | `Spec.encoding_unique` | +| Reading what the writer wrote gives back the tree. | `Spec.decode_encode` | +| The whole input is one element exactly when it is such an encoding. | `Spec.decodeAll_iff` | +| `readInt` accepts exactly the allowed contents of an integer and gives its value. | `Spec.readInt_iff` | +| `writeInt` writes allowed contents for the integer. | `Spec.writeInt_spec` | +| Allowed contents are what `writeInt` writes for their value. | `Spec.writeInt_unique` | +| The examples of X.690: lengths 38 and 201, BOOLEAN TRUE, and the primitive "Jones" of 8.23.5. | `Spec.x690_examples` | +| Seventeen inputs that break one rule each, among them the two constructed forms of "Jones", which DER forbids. | `Spec.x690_rejects` | + +## The command-line tool + +`Main.lean` has these modes: + +- `der decode HEX` prints the element at the start of some octets, as a tree, and the octets after it in hex, or exits with 1. +- `der serve` answers requests from the differential tests, one per line. The header comment of `Main.lean` gives the protocol and the syntax of a tree. + +HEX is lower-case hex digits in pairs, or `-` for no octets. Malformed HEX, or wrong arguments, exit with 2. Every answer comes from the functions above. From 10a2e06ae165d2af883645efa4a9f759b152316f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 18:32:01 +0000 Subject: [PATCH 4/8] Self-review: ENUMERATED checked, encode_eq for every tree, Der.valid, page wording - ENUMERATED contents follow the INTEGER rules (X.690 8.4), in Spec.PrimitiveOk and in Der.contentsOk, with two new rejects. - encode_eq holds for every tree, so it pins the writer; the new Der.valid and Spec.valid_iff say whether this slice accepts a tree. The tool's protocol gains a valid request. - x690_examples adds a tag number of 201 in the high form, worked by hand; the leading-zero reject now breaks that rule alone. - README and SPEC no longer say the reader accepts exactly DER: it does for framing and four types, and accepts more for other contents, now listed by clause. encoding_unique is described as one element per input. The SET order condition a reader without a schema could check is named, the length bound is stated right, and a citation is fixed. lake comparator --paranoid accepts all 11 theorems under every kernel. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AovXVgJBHGiN44TxQM4dcd --- Der/Basic.lean | 21 ++++++++++++++++++--- Main.lean | 7 ++++++- Proofs/Element.lean | 39 ++++++++++++++++++++++++++------------- Proofs/Header.lean | 26 ++++++++++++++++++-------- Proofs/Main.lean | 22 +++++++++++++++++----- README.md | 33 +++++++++++++++++++++------------ SPEC.md | 17 ++++++++++------- Spec.lean | 37 ++++++++++++++++++++++++------------- Spec/Defs.lean | 18 +++++++++++++----- bar.toml | 3 ++- comparator.json | 2 +- 11 files changed, 156 insertions(+), 69 deletions(-) diff --git a/Der/Basic.lean b/Der/Basic.lean index 5141a53..59aeb93 100644 --- a/Der/Basic.lean +++ b/Der/Basic.lean @@ -134,14 +134,15 @@ def intOk : List UInt8 → Bool | a :: b :: _ => !(a.toNat == 255 && 128 ≤ b.toNat) && !(a.toNat == 0 && b.toNat < 128) /-- Whether the contents of a primitive universal element are allowed: - a boolean is 0 or all ones (8.2, 11.1), an integer is in the fewest - octets (8.3), and a null is empty (8.8.2). Other types are not - checked in this slice. -/ + a boolean is 0 or all ones (8.2, 11.1), an integer or an enumerated + value is in the fewest octets (8.3, 8.4), and a null is empty + (8.8.2). Other types are not checked in this slice. -/ def contentsOk (tag : Nat) (c : List UInt8) : Bool := match tag with | 1 => c == [0] || c == [255] | 2 => intOk c | 5 => c.isEmpty + | 10 => intOk c | _ => true /-- Whether a primitive element's tag and contents are allowed: in the @@ -209,6 +210,20 @@ def decodeAll (b : List UInt8) : Option Element := | some (e, []) => some e | _ => none +mutual +/-- Whether this slice accepts an element: whether `decode` would read + back what `encode` writes for it. -/ +def valid : Element → Bool + | .prim cls tag c => cls < 4 && primOk cls tag c && c.length < 256 ^ 126 + | .cons cls tag cs => + cls < 4 && consOk cls tag && validList cs && (encodeList cs).length < 256 ^ 126 + +/-- Whether this slice accepts each element of a list. -/ +def validList : List Element → Bool + | [] => true + | e :: es => valid e && validList es +end + /-! ## Integers -/ /-- The integer that contents octets encode, or `none` when they are not diff --git a/Main.lean b/Main.lean index d333ba4..4ee8ea3 100644 --- a/Main.lean +++ b/Main.lean @@ -19,7 +19,9 @@ decode HEX prints OK, the tree and the hex of the rest, or FAIL (decode) all HEX prints OK and the tree, or FAIL (decodeAll) - encode TREE prints OK and the hex (encode) + encode TREE prints OK and the hex, for any tree (encode) + valid TREE prints OK when this slice accepts the tree, else + FAIL (valid) readint HEX prints OK and the integer, or FAIL (readInt) writeint INT prints OK and the hex (writeInt) @@ -159,6 +161,9 @@ def request (line : String) : Option String := | ["encode", t] => do let e ← treeField t pure s!"OK {toHex (encode e)}" + | ["valid", t] => do + let e ← treeField t + pure (if valid e then "OK" else "FAIL") | ["readint", h] => do let c ← fromHex h pure (match readInt c with diff --git a/Proofs/Element.lean b/Proofs/Element.lean index 271165e..707a692 100644 --- a/Proofs/Element.lean +++ b/Proofs/Element.lean @@ -64,21 +64,34 @@ theorem der_length_ge (v : Element) : 2 ≤ (Spec.der v).length := by omega mutual -theorem encode_eq' : ∀ v : Element, Spec.Valid v → encode v = Spec.der v - | .prim cls tag c, h => by - rw [valid_prim] at h +theorem encode_eq' : ∀ v : Element, encode v = Spec.der v + | .prim cls tag c => by rw [der_prim] - simp only [encode, identOctets_eq _ _ _ h.1, lengthOctets_eq] - | .cons cls tag cs, h => by - rw [valid_cons] at h + simp only [encode, identOctets_eq, lengthOctets_eq] + | .cons cls tag cs => by rw [der_cons] - simp only [encode, identOctets_eq _ _ _ h.1, lengthOctets_eq, encodeList_eq cs h.2.2.1] -theorem encodeList_eq : ∀ vs : List Element, Spec.ValidList vs → encodeList vs = Spec.derList vs - | [], _ => by simp [encodeList, derList_nil] - | v :: vs, h => by - rw [validList_cons] at h + simp only [encode, identOctets_eq, lengthOctets_eq, encodeList_eq cs] +theorem encodeList_eq : ∀ vs : List Element, encodeList vs = Spec.derList vs + | [] => by simp [encodeList, derList_nil] + | v :: vs => by rw [derList_cons] - simp only [encodeList, encode_eq' v h.1, encodeList_eq vs h.2] + simp only [encodeList, encode_eq' v, encodeList_eq vs] +end + +mutual +theorem valid_iff' : ∀ v : Element, valid v = true ↔ Spec.Valid v + | .prim cls tag c => by + rw [valid_prim] + simp only [valid, Bool.and_eq_true, decide_eq_true_eq, primOk_iff, and_assoc] + | .cons cls tag cs => by + rw [valid_cons] + simp only [valid, Bool.and_eq_true, decide_eq_true_eq, consOk_iff, validList_iff' cs, + encodeList_eq cs, and_assoc] +theorem validList_iff' : ∀ vs : List Element, validList vs = true ↔ Spec.ValidList vs + | [] => by simp [validList, Spec.ValidList] + | v :: vs => by + rw [validList_cons] + simp only [validList, Bool.and_eq_true, valid_iff' v, validList_iff' vs] end theorem readElements_zero (f : Nat) (b : List UInt8) : readElements f 0 b = some ([], b) := by @@ -278,7 +291,7 @@ theorem encoding_unique' (v w : Element) (r s : List UInt8) (hv : Spec.Valid v) theorem decode_encode' (v : Element) (rest : List UInt8) (h : Spec.Valid v) : decode (encode v ++ rest) = some (v, rest) := by - rw [encode_eq' v h] + rw [encode_eq' v] exact (decode_iff' _ _ _).mpr ⟨h, rfl⟩ theorem decodeAll_iff' (b : List UInt8) (v : Element) : diff --git a/Proofs/Header.lean b/Proofs/Header.lean index 06c39e8..85cd0fb 100644 --- a/Proofs/Header.lean +++ b/Proofs/Header.lean @@ -365,16 +365,23 @@ theorem readLength_sound {b : List UInt8} {n m : Nat} {rest : List UInt8} · rw [lengthOctets_long _ (by omega), hdig] simp only [List.length_cons, List.length_map, hk'] -theorem identOctets_eq (cls : Nat) (c : Bool) (tag : Nat) (hcls : cls < 4) : +/-- Only the class modulo 4 reaches the octet, so the code's `cls % 4` + and the specification's `cls` give the same octet. -/ +theorem lead_octet (cls : Nat) (c : Bool) (k : Nat) : + (cls % 4 * 64 + (if c then 32 else 0) + k).toUInt8 = (Spec.classAndForm cls c + k).toUInt8 := by + apply UInt8.toNat_inj.mp + rw [classAndForm_eq] + simp only [Nat.toUInt8] + cases c <;> simp <;> omega + +theorem identOctets_eq (cls : Nat) (c : Bool) (tag : Nat) : identOctets cls c tag = Spec.identifier cls c tag := by unfold identOctets - rw [Nat.mod_eq_of_lt hcls] by_cases ht : tag < 31 · rw [identifier_low cls c tag (by omega)] - simp only [ht, ↓reduceIte] - rfl + simp only [ht, ↓reduceIte, lead_octet] · rw [identifier_high cls c tag (by omega)] - simp only [ht, ↓reduceIte] + simp only [ht, ↓reduceIte, lead_octet] rw [toDigits_eq 7 (by omega), two_pow_seven] rfl @@ -414,15 +421,18 @@ theorem reservedUniversal_iff (tag : Nat) : theorem contentsOk_iff (tag : Nat) (c : List UInt8) : contentsOk tag c = true ↔ - (tag = 1 → c = [0] ∨ c = [255]) ∧ (tag = 2 → Spec.IntegerContents c) ∧ (tag = 5 → c = []) := by + (tag = 1 → c = [0] ∨ c = [255]) ∧ (tag = 2 ∨ tag = 10 → Spec.IntegerContents c) ∧ + (tag = 5 → c = []) := by unfold contentsOk split · simp · simp [intOk_iff] · simp [List.isEmpty_iff] - · rename_i h1 h2 h5 + · simp [intOk_iff] + · rename_i h1 h2 h5 h10 simp only [true_iff] - exact ⟨fun h => absurd h h1, fun h => absurd h h2, fun h => absurd h h5⟩ + exact ⟨fun h => absurd h h1, fun h => h.elim (absurd · h2) (absurd · h10), + fun h => absurd h h5⟩ theorem primOk_iff (cls tag : Nat) (c : List UInt8) : primOk cls tag c = true ↔ Spec.PrimitiveOk cls tag c := by diff --git a/Proofs/Main.lean b/Proofs/Main.lean index 727d25e..a802e34 100644 --- a/Proofs/Main.lean +++ b/Proofs/Main.lean @@ -11,8 +11,11 @@ namespace Spec open Der -theorem encode_eq (v : Element) (h : Valid v) : Der.encode v = der v := - DerProofs.encode_eq' v h +theorem encode_eq (v : Element) : Der.encode v = der v := + DerProofs.encode_eq' v + +theorem valid_iff (v : Element) : Der.valid v = true ↔ Valid v := + DerProofs.valid_iff' v theorem decode_iff (b : List UInt8) (v : Element) (rest : List UInt8) : Der.decode b = some (v, rest) ↔ Valid v ∧ b = der v ++ rest := @@ -44,21 +47,27 @@ theorem writeInt_unique (c : List UInt8) (h : IntegerContents c) : theorem x690_examples : Spec.lengthOctets 38 = [0x26] ∧ Spec.lengthOctets 201 = [0x81, 0xC9] ∧ + Spec.identifier 1 false 201 = [0x5F, 0x81, 0x49] ∧ + Der.decode [0x5F, 0x81, 0x49, 0x00] = some (.prim 1 201 [], []) ∧ Der.decode [0x01, 0x01, 0xFF] = some (.prim 0 1 [0xFF], []) ∧ Der.decode [0x1A, 0x05, 0x4A, 0x6F, 0x6E, 0x65, 0x73] = some (.prim 0 26 [0x4A, 0x6F, 0x6E, 0x65, 0x73], []) := by - refine ⟨?_, ?_, rfl, rfl⟩ + refine ⟨?_, ?_, ?_, rfl, rfl, rfl⟩ · rw [DerProofs.lengthOctets_short 38 (by omega)] rfl · rw [DerProofs.lengthOctets_long 201 (by omega), DerProofs.digits_of_lt (by omega)] rfl + · rw [DerProofs.identifier_high 1 false 201 (by omega), + DerProofs.digits_of_ge (by omega) (by omega), DerProofs.digits_of_lt (by omega)] + rfl +set_option maxRecDepth 100000 in theorem x690_rejects : Der.decode [0x3A, 0x09, 0x04, 0x03, 0x4A, 0x6F, 0x6E, 0x04, 0x02, 0x65, 0x73] = none ∧ Der.decode [0x3A, 0x80, 0x04, 0x03, 0x4A, 0x6F, 0x6E, 0x04, 0x02, 0x65, 0x73, 0x00, 0x00] = none ∧ Der.decode [0x04, 0x81, 0x01, 0x00] = none ∧ - Der.decode [0x04, 0x82, 0x00, 0x01, 0x00] = none ∧ + Der.decode ([0x04, 0x82, 0x00, 0x80] ++ List.replicate 128 0) = none ∧ Der.decode [0x04, 0xFF, 0x00] = none ∧ Der.decode [0x1F, 0x05, 0x00] = none ∧ Der.decode [0x1F, 0x80, 0x1F, 0x00] = none ∧ @@ -67,11 +76,14 @@ theorem x690_rejects : Der.decode [0x02, 0x02, 0x00, 0x7F] = none ∧ Der.decode [0x02, 0x02, 0xFF, 0x80] = none ∧ Der.decode [0x02, 0x00] = none ∧ + Der.decode [0x0A, 0x02, 0x00, 0x01] = none ∧ + Der.decode [0x0A, 0x00] = none ∧ Der.decode [0x05, 0x01, 0x00] = none ∧ Der.decode [0x22, 0x03, 0x02, 0x01, 0x00] = none ∧ Der.decode [0x10, 0x00] = none ∧ Der.decode [0x00, 0x00] = none ∧ Der.decode [0x04, 0x05, 0x00] = none := by - refine ⟨rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl⟩ + refine ⟨rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, rfl, + rfl⟩ end Spec diff --git a/README.md b/README.md index a7e05d6..a9e179e 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ # der: reading and writing DER (X.690), proved -DER, the Distinguished Encoding Rules of ASN.1, is how X.509 certificates, ECDSA signatures, PKCS and CMS are written. Every TLS client and every signature checker reads it from bytes someone else chose. DER allows exactly one encoding of each value, and much of its security rests on that. When a reader accepts a second encoding of the same value, a signature can be forged against a lenient checker, or two parsers disagree about what a certificate says. Malformed lengths have also given out-of-bounds reads more than once. +DER, the Distinguished Encoding Rules of ASN.1, is how X.509 certificates, ECDSA signatures, PKCS and CMS are written. Every TLS client and every signature checker reads it from bytes someone else chose. DER allows exactly one encoding of each value, and much of its security rests on that. When a reader accepts a second encoding of the same value, a signature can be forged against a lenient checker, or two parsers disagree about what a certificate says. Malformed lengths have also given out-of-bounds reads more than once. This first slice gives that guarantee for the framing of every element and for the contents of four types; "What is not proved" lists the types whose contents it does not yet check. This library reads and writes one DER element, nested to any depth, in Lean: - identifier octets, with tag numbers of any size (X.690 8.1.2); - length octets, definite and in the fewest octets (8.1.3, 10.1); - the contents of a primitive element, or the elements of a constructed one; -- the form, primitive or constructed, that DER gives each universal type, and the contents of BOOLEAN, INTEGER and NULL. +- the form, primitive or constructed, that DER gives each universal type, and the contents of BOOLEAN, INTEGER, ENUMERATED and NULL. Machine-checked proofs show: -- **The reader accepts exactly DER.** It accepts an input exactly when the input starts with the DER encoding of an element this slice accepts. It then gives back the element and the octets after it. So it never reads past the end of its input, and every element it gives lies inside its parent's contents. -- **Each element has one encoding.** Two elements whose encodings start the same input are equal. Writing back what the reader read gives the same octets. -- **The writer writes DER.** It writes, for every such element, the encoding X.690 gives it. +- **The reader accepts exactly the encodings of the elements this slice accepts.** It accepts an input exactly when the input starts with the encoding X.690 gives an element this slice accepts, and then gives back the element and the octets after it. So it never reads past the end of its input, and every element it gives lies inside its parent's contents. That is exactly DER for the identifier, length and structure of every element and for the contents of BOOLEAN, INTEGER, ENUMERATED and NULL. For the contents of other types it accepts more than DER does. +- **One encoding per element, one element per input.** Each element has one encoding the reader accepts, and an input starts with the encoding of at most one element. Writing back what the reader read gives the same octets. For the four checked types this is one encoding per value; for the others, two contents octet strings for the same value are two elements. +- **The writer writes the encoding.** It writes, for every element, the encoding X.690 gives it, and `Der.valid` says whether this slice accepts the element. - **Integers have one encoding.** `readInt` accepts exactly the contents octets X.690 allows for an INTEGER and gives their value. `writeInt` writes them, and each integer has exactly one. It is tested against a model of X.690 and other implementations; `tests/TEST-RESULTS.md` holds the record. It is a project of [Proven Commons](https://github.com/Proven-Commons/mission), built to the verified bar published there. @@ -37,27 +37,36 @@ The statements are in `Spec.lean`. The definitions they use are in `Spec/Defs.le | Claim | Theorem | |-------|---------| -| The writer writes the DER encoding of every element this slice accepts. | `Spec.encode_eq` | +| The writer writes the encoding X.690 gives every element. | `Spec.encode_eq` | +| `Der.valid` says whether this slice accepts an element. | `Spec.valid_iff` | | The reader accepts exactly a DER encoding of such an element, followed by anything, and gives back the element and what follows. | `Spec.decode_iff` | -| Two such elements whose encodings start the same input are the same, and so is what follows. | `Spec.encoding_unique` | +| An input starts with the encoding of at most one element this slice accepts: two whose encodings start the same input are the same, and so is what follows. | `Spec.encoding_unique` | | Reading what the writer wrote gives back the element. | `Spec.decode_encode` | | The whole input is one element exactly when it is such an encoding. | `Spec.decodeAll_iff` | | `readInt` accepts exactly the contents of an INTEGER that X.690 allows, and gives the integer. | `Spec.readInt_iff` | | `writeInt` writes such contents for the integer, and they are the only ones. | `Spec.writeInt_spec`, `Spec.writeInt_unique` | -| X.690's examples: lengths 38 and 201, BOOLEAN TRUE, and the primitive "Jones" of 8.23.5. Seventeen inputs that each break one rule are refused, among them the two constructed forms of "Jones". | `Spec.x690_examples`, `Spec.x690_rejects` | +| X.690's examples: lengths 38 and 201, BOOLEAN TRUE, and the primitive "Jones" of 8.23.5, with a tag number of 201 in the high form worked by hand. Nineteen inputs that each break a rule are refused, among them the two constructed forms of "Jones". | `Spec.x690_examples`, `Spec.x690_rejects` | -The elements this slice accepts, `Spec.Valid`, are those whose class is one of the four, whose lengths fit in at most 126 length octets, and whose universal tags follow the rules of `SPEC.md`: +The elements this slice accepts, `Spec.Valid`, are those whose class is one of the four, whose length octets have at most 126 octets after the first, and whose universal tags follow the rules of `SPEC.md`: - tag 0 is not used; - 8, 11, 16, 17 and 29 are constructed; - 15 and 37 and up may be either; - every other universal tag is primitive; -- a BOOLEAN is 00 or FF, an INTEGER is in the fewest octets, and a NULL is empty. +- a BOOLEAN is 00 or FF, an INTEGER or ENUMERATED is in the fewest octets, and a NULL is empty. ## What is not proved -- **The contents of other types.** BIT STRING unused bits, OBJECT IDENTIFIER subidentifiers, the string types, the times and REAL are read as octets and not checked. ECDSA-Sig-Value is the second slice. -- **Rules that depend on the ASN.1 type.** DER orders the components of a SET by tag (10.3) and of a SET OF by encoding (11.6), and leaves out components equal to their default (11.5). A reader with no schema cannot check these, so this one does not. +- **The contents of other types.** These are read as octets and not checked, so the reader accepts encodings DER forbids, and more than one encoding of the same value: + - REAL (8.5, 11.3); + - BIT STRING: the initial octet and unused bits (8.6.2, 11.2.1); + - OBJECT IDENTIFIER and RELATIVE-OID subidentifiers (8.19.2, 8.20.2), and the IRI types (8.21.2, 8.22.2); + - the string types: UTF-8 in the fewest octets (8.23.10), BMPString and UniversalString lengths (8.23.7, 8.23.8), GeneralString escapes (11.4); + - UTCTime and GeneralizedTime (11.7, 11.8), and TIME and its subtypes (8.26, 11.9); + - the components of EXTERNAL, EMBEDDED PDV and CHARACTER STRING (8.16 to 8.18, 8.24), so even an empty one is accepted. + + ECDSA-Sig-Value is the second slice. +- **Rules that depend on the ASN.1 type.** DER orders the components of a SET by tag (10.3) and of a SET OF by encoding (11.6), leaves out components equal to their default (11.5), and drops trailing zero bits from a named bit list (11.2.2). Without a schema, only a weak condition on a SET can be checked: its components are either in tag order or in encoding order. This slice does not check even that. - **Time and memory.** No proof bounds them. The reader is written to take time linear in its input: it never measures more of the input than an element's length, and it adds up long tag numbers and lengths by halves. Its recursion depth is the nesting depth. The tests time deep and long inputs. - **Reading requests and printing answers in `Main.lean`.** The differential tests cover them. diff --git a/SPEC.md b/SPEC.md index 295efef..1c118a5 100644 --- a/SPEC.md +++ b/SPEC.md @@ -6,7 +6,7 @@ DER, the Distinguished Encoding Rules of ASN.1 (Rec. ITU-T X.690, https://www.it - its length octets, in the definite form and the fewest octets (8.1.3, 10.1); - its contents: octets for a primitive element, and the elements they encode for a constructed one (8.1.4). -It checks the contents of three universal types, BOOLEAN, INTEGER and NULL, and the form (primitive or constructed) of every universal type. The contents of other types (BIT STRING, OBJECT IDENTIFIER, strings, times, REAL) are a later slice. So is the order of SET components, which depends on the ASN.1 type. +It checks the contents of four universal types, BOOLEAN, INTEGER, ENUMERATED and NULL, and the form (primitive or constructed) of every universal type. The contents of other types (REAL, BIT STRING, OBJECT IDENTIFIER, RELATIVE-OID, the IRI types, the strings, the times, and the components of EXTERNAL, EMBEDDED PDV and CHARACTER STRING) are a later slice, and `Spec/Defs.lean` lists the clauses it leaves unchecked. So is the order of SET components, which depends on the ASN.1 type. A reviewer who did not write this specification approves it. The approval also covers `Spec.lean`, `Spec/Defs.lean`, `Spec/Element.lean`, and the other files that `bar.toml` lists. @@ -26,11 +26,13 @@ A reviewer who did not write this specification approves it. The approval also c - the length fits the long form, whose first octet may not be all ones (8.1.3.5 c), so at most 126 octets follow it; - in the universal class, tag 0 is not used (X.680 Table 1 reserves it for the encoding rules, and DER has no end-of-contents octets); - tags 8, 11, 16, 17 and 29 (external, embedded-pdv, sequence, set, character string) are constructed, tags 15 and 37 and up (reserved in X.680) may be either, and every other universal tag is primitive, as clauses 8 and 10.2 require; -- a BOOLEAN's contents are 00 or FF (8.2, 11.1), an INTEGER's are one or more octets in the fewest (8.3.1, 8.3.2), and a NULL's are empty (8.8.2). +- a BOOLEAN's contents are 00 or FF (8.2, 11.1), an INTEGER's are one or more octets in the fewest (8.3.1, 8.3.2), and so are an ENUMERATED's, since it is encoded as its integer (8.4), and a NULL's are empty (8.8.2). -**The writer** writes `Spec.der` for every tree this slice accepts. +`Spec.der v` is a DER encoding of `v` exactly when `Spec.Valid v` holds and the contents of the types this slice does not check are as DER asks. For a tree outside `Spec.Valid`, `Spec.der` still gives octets, but the reader does not read them back as that tree. -**The reader** accepts exactly the octet strings that start with `Spec.der` of a tree this slice accepts, and gives back the tree and the octets after it. Since `Spec.der` puts each element's encoding inside its parent's contents, every element the reader gives lies inside its parent, and the reader never reads past the end of its input. Since the reader is a function, each tree has one encoding it accepts: DER's main property. Reading the whole input as one element is the same with nothing after it. +**The writer** writes `Spec.der` for every tree. `Der.valid` says whether a tree is in `Spec.Valid`. + +**The reader** accepts exactly the octet strings that start with `Spec.der` of a tree this slice accepts, and gives back the tree and the octets after it. Since `Spec.der` puts each element's encoding inside its parent's contents, every element the reader gives lies inside its parent, and the reader never reads past the end of its input. Since `Spec.der` is a function, each tree has one encoding the reader accepts; since the reader is a function, each input starts with the encoding of at most one tree (`encoding_unique`). For BOOLEAN, INTEGER, ENUMERATED and NULL that is one encoding per value. For the contents of other types it is not: two contents octet strings that encode the same value, one of which DER forbids, are two trees here. Reading the whole input as one element is the same with nothing after it. ## Integers @@ -44,7 +46,8 @@ Each property is a theorem in `Spec.lean`, under the name given. | Property | Theorem | |----------|---------| -| The writer writes the DER encoding of every tree this slice accepts. | `Spec.encode_eq` | +| The writer writes `Spec.der` for every tree. | `Spec.encode_eq` | +| `Der.valid` says whether a tree is in `Spec.Valid`. | `Spec.valid_iff` | | The reader accepts exactly an encoding of such a tree followed by anything, and gives back the tree and what follows. | `Spec.decode_iff` | | Two trees with encodings at the start of the same input are the same, and so is what follows. | `Spec.encoding_unique` | | Reading what the writer wrote gives back the tree. | `Spec.decode_encode` | @@ -52,8 +55,8 @@ Each property is a theorem in `Spec.lean`, under the name given. | `readInt` accepts exactly the allowed contents of an integer and gives its value. | `Spec.readInt_iff` | | `writeInt` writes allowed contents for the integer. | `Spec.writeInt_spec` | | Allowed contents are what `writeInt` writes for their value. | `Spec.writeInt_unique` | -| The examples of X.690: lengths 38 and 201, BOOLEAN TRUE, and the primitive "Jones" of 8.23.5. | `Spec.x690_examples` | -| Seventeen inputs that break one rule each, among them the two constructed forms of "Jones", which DER forbids. | `Spec.x690_rejects` | +| The examples of X.690: lengths 38 and 201, BOOLEAN TRUE, and the primitive "Jones" of 8.23.5; and a tag number of 201 in the high form, worked by hand. | `Spec.x690_examples` | +| Nineteen inputs that each break a rule, among them the two constructed forms of "Jones", which DER forbids. | `Spec.x690_rejects` | ## The command-line tool diff --git a/Spec.lean b/Spec.lean index 6ba43a8..1f964fb 100644 --- a/Spec.lean +++ b/Spec.lean @@ -18,9 +18,12 @@ open Der /-! ## Elements -/ -/-- The writer writes the DER encoding of every element this slice - accepts. -/ -theorem encode_eq (v : Element) (h : Valid v) : Der.encode v = der v := sorry +/-- The writer writes `der v` for every tree. That is a DER encoding of + `v` exactly when this slice accepts `v`: see `decode_iff`. -/ +theorem encode_eq (v : Element) : Der.encode v = der v := sorry + +/-- `Der.valid` says whether this slice accepts an element. -/ +theorem valid_iff (v : Element) : Der.valid v = true ↔ Valid v := sorry /-- The reader accepts exactly a DER encoding of an element this slice accepts, followed by anything, and gives back the element and what @@ -30,9 +33,10 @@ theorem encode_eq (v : Element) (h : Valid v) : Der.encode v = der v := sorry theorem decode_iff (b : List UInt8) (v : Element) (rest : List UInt8) : Der.decode b = some (v, rest) ↔ Valid v ∧ b = der v ++ rest := sorry -/-- Each element has one encoding: two accepted elements whose encodings - start two inputs that are the same are the same, and so is what - follows them. -/ +/-- An input starts with the encoding of at most one element: two + accepted elements whose encodings start the same input are the same, + and so is what follows them. With `decode_iff`, the reader gives one + element for each input, and accepts one encoding of each element. -/ theorem encoding_unique (v w : Element) (r s : List UInt8) (hv : Valid v) (hw : Valid w) (h : der v ++ r = der w ++ s) : v = w ∧ r = s := sorry @@ -68,24 +72,29 @@ theorem writeInt_unique (c : List UInt8) (h : IntegerContents c) : /-- The examples of X.690: a length of 38 in the short form (8.1.3.4) and of 201 in the long form (8.1.3.5), the boolean TRUE as DER writes it (8.2.2, 11.1), and the primitive form of the VisibleString - "Jones" (8.23.5), which DER uses. -/ + "Jones" (8.23.5), which DER uses. Then a tag number of 201 in the + high form (8.1.2.4), worked by hand: the leading octet 0x5F for an + application class primitive, then 201 = 1 × 128 + 73 as 0x81 0x49. -/ theorem x690_examples : Spec.lengthOctets 38 = [0x26] ∧ Spec.lengthOctets 201 = [0x81, 0xC9] ∧ + Spec.identifier 1 false 201 = [0x5F, 0x81, 0x49] ∧ + Der.decode [0x5F, 0x81, 0x49, 0x00] = some (.prim 1 201 [], []) ∧ Der.decode [0x01, 0x01, 0xFF] = some (.prim 0 1 [0xFF], []) ∧ Der.decode [0x1A, 0x05, 0x4A, 0x6F, 0x6E, 0x65, 0x73] = some (.prim 0 26 [0x4A, 0x6F, 0x6E, 0x65, 0x73], []) := sorry -/-- Inputs the reader refuses, each for one rule: +/-- Inputs the reader refuses, each for a rule: - the two constructed forms of "Jones" in 8.23.5, which DER forbids - (10.2), the second with the indefinite length (10.1); - - a length in the long form below 128, and with a leading zero - octet (10.1); the initial length octet all ones (8.1.3.5 c); + (10.2), the second also with the indefinite length (10.1); + - a length in the long form below 128, and a length of 128 in the + long form with a leading zero octet (10.1); the initial length + octet all ones (8.1.3.5 c); - a tag number below 31 in the high form (8.1.2.2), and a first subsequent octet with bits 7 to 1 zero (8.1.2.4.2 c); - a boolean TRUE that is not all ones (11.1), and of two octets (8.2.1); - integers with a redundant leading octet (8.3.2) and with none - (8.3.1); + (8.3.1), and the same for enumerated values (8.4); - a null with contents (8.8.2), a constructed integer (8.3.1), a primitive sequence (8.9.1), and the end-of-contents octets (8.1.5); @@ -95,7 +104,7 @@ theorem x690_rejects : Der.decode [0x3A, 0x80, 0x04, 0x03, 0x4A, 0x6F, 0x6E, 0x04, 0x02, 0x65, 0x73, 0x00, 0x00] = none ∧ Der.decode [0x04, 0x81, 0x01, 0x00] = none ∧ - Der.decode [0x04, 0x82, 0x00, 0x01, 0x00] = none ∧ + Der.decode ([0x04, 0x82, 0x00, 0x80] ++ List.replicate 128 0) = none ∧ Der.decode [0x04, 0xFF, 0x00] = none ∧ Der.decode [0x1F, 0x05, 0x00] = none ∧ Der.decode [0x1F, 0x80, 0x1F, 0x00] = none ∧ @@ -104,6 +113,8 @@ theorem x690_rejects : Der.decode [0x02, 0x02, 0x00, 0x7F] = none ∧ Der.decode [0x02, 0x02, 0xFF, 0x80] = none ∧ Der.decode [0x02, 0x00] = none ∧ + Der.decode [0x0A, 0x02, 0x00, 0x01] = none ∧ + Der.decode [0x0A, 0x00] = none ∧ Der.decode [0x05, 0x01, 0x00] = none ∧ Der.decode [0x22, 0x03, 0x02, 0x01, 0x00] = none ∧ Der.decode [0x10, 0x00] = none ∧ diff --git a/Spec/Defs.lean b/Spec/Defs.lean index 330b4fa..edc36c2 100644 --- a/Spec/Defs.lean +++ b/Spec/Defs.lean @@ -73,7 +73,7 @@ mutual contents octets (8.1.1.1). With the definite form there are no end-of-contents octets (8.1.5). The contents octets of a constructed element are the encodings of its elements, one after another - (8.1.1.1 b, 8.9.2, 8.10.2). -/ + (8.1.1.1 c, 3.2, 8.9.2, 8.10.2). -/ def der : Element → List UInt8 | .prim cls tag c => identifier cls false tag ++ lengthOctets c.length ++ c | .cons cls tag cs => identifier cls true tag ++ lengthOctets (derList cs).length ++ derList cs @@ -128,25 +128,33 @@ def ReservedUniversal (tag : Nat) : Prop := tag = 15 ∨ 37 ≤ tag - the five numbers of `ConstructedUniversal` are not primitive; - a boolean has one contents octet (8.2.1), 0 for false (8.2.2) and all ones for true (11.1); - - an integer has the contents `IntegerContents` describes (8.3); + - an integer has the contents `IntegerContents` describes (8.3), and + so has an enumerated value, which is encoded as its integer (8.4); - a null has no contents octets (8.8.2). Every other universal type is primitive in DER: by 8.2.1, 8.3.1, 8.4, 8.5.1, 8.8.1, 8.19.1, 8.20.1, 8.21.1, 8.22.1 and 8.26, and for bit strings, octet strings and the restricted character strings, with the useful types defined from them (8.25), by 10.2. This slice - checks the contents of no other type. -/ + checks the contents of no other type: not those of 8.5 and 11.3 + (REAL), 8.6.2 and 11.2.1 (BIT STRING), 8.19.2 (OBJECT IDENTIFIER), + 8.20.2 (RELATIVE-OID), 8.21.2 and 8.22.2 (the IRI types), 8.23.7, + 8.23.8, 8.23.10 and 11.4 (strings), 11.7 and 11.8 (the useful time + types), or 8.26 and 11.9 (TIME and its subtypes). -/ def PrimitiveOk (cls tag : Nat) (c : List UInt8) : Prop := cls = 0 → tag ≠ 0 ∧ ¬ ConstructedUniversal tag ∧ (tag = 1 → c = [0] ∨ c = [255]) ∧ - (tag = 2 → IntegerContents c) ∧ + (tag = 2 ∨ tag = 10 → IntegerContents c) ∧ (tag = 5 → c = []) /-- What this slice requires of the tag of a constructed element: in the universal class, one of the five numbers of `ConstructedUniversal`, or a reserved number. Every other universal type is primitive in DER - (see `PrimitiveOk`). -/ + (see `PrimitiveOk`). The elements inside are not checked against the + type: not the components of 8.16 to 8.18 and 8.24 for tags 8, 11 + and 29, nor the order of a set's components (10.3, 11.6), which + depends on whether it is a SET or a SET OF. -/ def ConstructedOk (cls tag : Nat) : Prop := cls = 0 → ConstructedUniversal tag ∨ ReservedUniversal tag diff --git a/bar.toml b/bar.toml index 4f7b3d7..312752e 100644 --- a/bar.toml +++ b/bar.toml @@ -16,7 +16,8 @@ entry_points = ["Main.lean"] [spec] files = ["SPEC.md", "Spec.lean", "Spec/Defs.lean", "Spec/Element.lean"] # The code under proof: the functions the statements are about. -code = ["Der.encode", "Der.decode", "Der.decodeAll", "Der.readInt", "Der.writeInt"] +code = ["Der.encode", "Der.decode", "Der.decodeAll", "Der.valid", "Der.readInt", + "Der.writeInt"] # One entry per approval, added in a pull request the reviewer approves. # [[spec.approval]] diff --git a/comparator.json b/comparator.json index e05016e..2f2b4e0 100644 --- a/comparator.json +++ b/comparator.json @@ -2,7 +2,7 @@ "challenge_module": "Spec", "solution_module": "Proofs", "theorem_names": [ - "Spec.encode_eq", "Spec.decode_iff", "Spec.encoding_unique", "Spec.decode_encode", + "Spec.encode_eq", "Spec.valid_iff", "Spec.decode_iff", "Spec.encoding_unique", "Spec.decode_encode", "Spec.decodeAll_iff", "Spec.readInt_iff", "Spec.writeInt_spec", "Spec.writeInt_unique", "Spec.x690_examples", "Spec.x690_rejects" ], From f18cb93dfa40246db16d167e41e20d1bb5fdccc1 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Oct 2026 00:00:52 +0000 Subject: [PATCH 5/8] Differential tests: a model of X.690, four oracles, Wycheproof tests/run_tests.py compares every answer of `der serve` with a Python model of X.690 and with Go cryptobyte, Go encoding/asn1, rust-asn1 and OpenSSL, each pinned. A disagreement with an oracle is allowed only for a named reason the model confirms with that oracle's rules. The model also checks that `valid` holds exactly when a tree's encoding reads back as the tree, and reads the tool's encodings back. 1,612,540 inputs, seed 20261027, 0 failures, no oracle left out: every input of up to two octets, every identifier and initial length octet, lengths around each form boundary, tag numbers up to 2^64, every universal tag in both forms, every INTEGER and ENUMERATED of up to two octets, chains nested 100,000 deep, the Wycheproof ECDSA P-256 signatures, and 100,000 random trees, mutated, and spoiled for `valid`. tests/plant_bugs.py plants bugs in Der/Basic.lean and Main.lean and checks the tests catch each one. The README now says the writer and `Der.valid` take time quadratic in the nesting depth, lists `Der.valid`, and bar.toml lists the test files. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AovXVgJBHGiN44TxQM4dcd --- README.md | 3 +- bar.toml | 6 +- tests/PLANTED-BUGS.md | 40 + tests/TEST-RESULTS.md | 107 + tests/oracles/go/go.mod | 5 + tests/oracles/go/go.sum | 2 + tests/oracles/go/main.go | 314 + tests/oracles/openssl/wrapper.c | 358 + tests/oracles/rust/Cargo.lock | 72 + tests/oracles/rust/Cargo.toml | 11 + tests/oracles/rust/src/main.rs | 208 + tests/plant_bugs.py | 228 + tests/run_tests.py | 1909 +++++ tests/wycheproof/README.md | 14 + .../ecdsa_secp256r1_sha256_test.json | 7243 +++++++++++++++++ 15 files changed, 10518 insertions(+), 2 deletions(-) create mode 100644 tests/PLANTED-BUGS.md create mode 100644 tests/TEST-RESULTS.md create mode 100644 tests/oracles/go/go.mod create mode 100644 tests/oracles/go/go.sum create mode 100644 tests/oracles/go/main.go create mode 100644 tests/oracles/openssl/wrapper.c create mode 100644 tests/oracles/rust/Cargo.lock create mode 100644 tests/oracles/rust/Cargo.toml create mode 100644 tests/oracles/rust/src/main.rs create mode 100644 tests/plant_bugs.py create mode 100644 tests/run_tests.py create mode 100644 tests/wycheproof/README.md create mode 100644 tests/wycheproof/ecdsa_secp256r1_sha256_test.json diff --git a/README.md b/README.md index a9e179e..41f16e2 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ From Lean, on `List UInt8`, with the tree `Der.Element` of `Spec/Element.lean`: - `Der.decode : List UInt8 → Option (Element × List UInt8)`: the element at the start of the input, and the octets after it. - `Der.decodeAll : List UInt8 → Option Element`: the element that is the whole input. - `Der.encode : Element → List UInt8`. +- `Der.valid : Element → Bool`: whether this slice accepts an element. - `Der.readInt : List UInt8 → Option Int` and `Der.writeInt : Int → List UInt8`: the contents octets of an INTEGER. An element is `.prim cls tag contents` or `.cons cls tag children`, with the class 0 (universal), 1 (application), 2 (context-specific) or 3 (private). `SPEC.md` says what each function does. @@ -67,7 +68,7 @@ The elements this slice accepts, `Spec.Valid`, are those whose class is one of t ECDSA-Sig-Value is the second slice. - **Rules that depend on the ASN.1 type.** DER orders the components of a SET by tag (10.3) and of a SET OF by encoding (11.6), leaves out components equal to their default (11.5), and drops trailing zero bits from a named bit list (11.2.2). Without a schema, only a weak condition on a SET can be checked: its components are either in tag order or in encoding order. This slice does not check even that. -- **Time and memory.** No proof bounds them. The reader is written to take time linear in its input: it never measures more of the input than an element's length, and it adds up long tag numbers and lengths by halves. Its recursion depth is the nesting depth. The tests time deep and long inputs. +- **Time and memory.** No proof bounds them. The reader is written to take time linear in its input: it never measures more of the input than an element's length, and it adds up long tag numbers and lengths by halves. Its recursion depth is the nesting depth. The tests time deep and long inputs. The writer and `Der.valid` are not linear: each level of nesting copies and measures the encoding of what it holds, so their time grows with the square of the nesting depth. Writing a tree 10,000 levels deep takes about 1.6 s, and 100,000 levels about 190 s. - **Reading requests and printing answers in `Main.lean`.** The differential tests cover them. ## What you must trust diff --git a/bar.toml b/bar.toml index 312752e..c775cd5 100644 --- a/bar.toml +++ b/bar.toml @@ -29,4 +29,8 @@ code = ["Der.encode", "Der.decode", "Der.decodeAll", "Der.valid", "Der.readInt", [tests] results = "tests/TEST-RESULTS.md" command = "python3 tests/run_tests.py" -files = ["tests/run_tests.py", "tests/plant_bugs.py"] +files = ["tests/run_tests.py", "tests/plant_bugs.py", + "tests/wycheproof/ecdsa_secp256r1_sha256_test.json", + "tests/oracles/go/main.go", "tests/oracles/go/go.mod", "tests/oracles/go/go.sum", + "tests/oracles/rust/Cargo.toml", "tests/oracles/rust/Cargo.lock", + "tests/oracles/rust/src/main.rs", "tests/oracles/openssl/wrapper.c"] diff --git a/tests/PLANTED-BUGS.md b/tests/PLANTED-BUGS.md new file mode 100644 index 0000000..6f7f1e9 --- /dev/null +++ b/tests/PLANTED-BUGS.md @@ -0,0 +1,40 @@ +# Planted bugs + +Written by `tests/plant_bugs.py`. Each bug is a change of one line, to `Der/Basic.lean` (26 bugs) or to `Main.lean` (5), tested with `tests/run_tests.py --count 5000 --no-oracles`, which builds only the executable and compares it with the model alone. Every bug must give failures, and the unchanged code none. A bug that does not build, or a run that gives up or dies, counts as missed. + +| Planted bug | Failures | +|---|---| +| none | 0 | +| the long form is read for a length below 128 | 991 | +| the long form is refused for a length below 256 | 2663 | +| a leading zero length octet is read | 267 | +| the indefinite length octet 0x80 is read as length 0 | 1391 | +| a tag number below 31 is read in the high form | 1939 | +| a first subsequent tag octet 0x80 is read | 1104 | +| the continuation bit of a tag octet is tested as 0x40 | 4714 | +| the constructed bit is read from bit 7 | 12878 | +| a BOOLEAN 01 is read | 493 | +| an INTEGER with a redundant leading zero octet is read | 412 | +| a NULL with one contents octet is read | 48 | +| universal tag 0 is read | 154 | +| a primitive SEQUENCE is read | 81 | +| a constructed INTEGER is read | 15 | +| universal 29 is taken for a primitive type | 662 | +| the reserved universal numbers start at 38, not 37 | 711 | +| an element may run one octet past the one it is in | 432 | +| the elements of a constructed element must take one octet more than it holds | 5444 | +| the reader's fuel is half the length of the input | 444 | +| decodeAll allows octets after the element | 2404 | +| long numbers: the first half is shifted by the length of the first half, not the second | 529 | +| readInt takes a first octet 0x80 for a positive integer | 511 | +| writeInt writes 128 with no zero octet in front | 628 | +| the writer gives no subsequent tag octet but the last its bit 8 | 3239 | +| the writer writes length 128 in the short form | 46 | +| the writer writes tag number 31 in one octet | 406 | +| Main.lean: `all` allows octets after the element | 2404 | +| Main.lean: `der decode` exits with 0 when the input is not an element | 6 | +| Main.lean: the CR of a CR LF line ending is kept | 48069 | +| Main.lean: a primitive element is printed with its number before its class | 82169 | +| Main.lean: upper-case hex digits are read | 4 | + +Bugs caught: 31 of 31 diff --git a/tests/TEST-RESULTS.md b/tests/TEST-RESULTS.md new file mode 100644 index 0000000..8c8ad52 --- /dev/null +++ b/tests/TEST-RESULTS.md @@ -0,0 +1,107 @@ +# Test results + +Written by `tests/run_tests.py`. Rerun it to check these numbers. + +- Lean: Lean (version 4.35.0, x86_64-unknown-linux-gnu, commit eb6cafb7290e8bd5f2937522d8d804f58f02bafc, Release) +- Oracle: the model of X.690 (02/2021) in `tests/run_tests.py`; Go cryptobyte, golang.org/x/crypto/cryptobyte v0.25.0 (h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=), built with go version go1.24.7 linux/amd64; Go encoding/asn1, encoding/asn1 of the standard library of go version go1.24.7 linux/amd64; rust-asn1, the asn1 crate 0.24.1 (checksum c9795210620c0cb3f9a7ce4f882808c38e1ef7b347c90591dceae0886e031fb1), built with rustc 1.97.0 (2d8144b78 2026-07-07); OpenSSL, libcrypto of OpenSSL 3.0.13 30 Jan 2024 (Debian package libssl3t64 3.0.13-0ubuntu3.16), through `tests/oracles/openssl/wrapper.c` compiled with cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 +- Go cryptobyte: golang.org/x/crypto/cryptobyte v0.25.0 (h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=), built with go version go1.24.7 linux/amd64 +- Go encoding/asn1: encoding/asn1 of the standard library of go version go1.24.7 linux/amd64 +- rust-asn1: the asn1 crate 0.24.1 (checksum c9795210620c0cb3f9a7ce4f882808c38e1ef7b347c90591dceae0886e031fb1), built with rustc 1.97.0 (2d8144b78 2026-07-07) +- OpenSSL: libcrypto of OpenSSL 3.0.13 30 Jan 2024 (Debian package libssl3t64 3.0.13-0ubuntu3.16), through `tests/oracles/openssl/wrapper.c` compiled with cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 +- Oracles left out: none +- Seed: 20261027 +- Inputs: 1612540 +- By kind: 0 to 2 octets all 65793, 0 to 2 octets decode 65793, BOOLEAN contents decode 262, ENUMERATED contents decode 66153, INTEGER contents decode 66153, INTEGER contents readint 66153, NULL contents decode 5, Wycheproof ECDSA signatures all 484, X.690 examples all 4, X.690 examples decode 5, X.690 examples encode 5, constructed BOOLEAN, INTEGER and NULL decode 4, high tag numbers all 240, high tag numbers decode 240, high tag numbers encode 240, high tag numbers, cut short decode 232, high tag numbers, first subsequent octet 0x80 all 232, identifier first octets decode 2432, initial length octets decode 2548, integers around 0 and 2^k writeint 10611, lengths 0 to 100,000 all 30, lengths 0 to 100,000 decode 15, lengths 0 to 100,000 encode 30, lengths 0 to 100,000, cut short all 14, lengths 0 to 100,000, length one less decode 14, lengths 0 to 100,000, length one more decode 15, lengths 0 to 100,000, not in the fewest octets all 20, nested 10,000 deep all 5, nested 10,000 deep decode 1, nested 10,000 deep encode 2, nested 100,000 deep all 5, nested 100,000 deep decode 1, random integer contents readint 25000, random integers writeint 25000, random octets decode 50000, random tree all 100000, random tree encode 100000, random tree valid 100000, random tree, BOOLEAN, INTEGER, ENUMERATED or NULL contents changed all 6297, random tree, BOOLEAN, INTEGER, ENUMERATED or NULL contents changed decode 6294, random tree, a bit flipped decode 100000, random tree, an element running past the one it is in all 8434, random tree, an element running past the one it is in decode 8503, random tree, constructed bit flipped all 24803, random tree, constructed bit flipped decode 25257, random tree, cut short decode 100000, random tree, first subsequent tag octet 0x80 all 25179, random tree, first subsequent tag octet 0x80 decode 25024, random tree, high form for a tag below 31 all 19518, random tree, high form for a tag below 31 decode 19483, random tree, indefinite length all 25044, random tree, indefinite length decode 25036, random tree, length not in the fewest octets all 24991, random tree, length not in the fewest octets decode 25157, random tree, length one less all 22163, random tree, length one less decode 22232, random tree, length one more all 25378, random tree, length one more decode 24714, random tree, octets after all 33283, random tree, octets after decode 66717, random tree, one element not accepted encode 100000, random tree, one element not accepted valid 100000, universal tags 0 to 40 decode 410, universal tags 0 to 40 encode 410, universal tags 0 to 40 valid 410, universal tags 0 to 40, high form all 62 +- Answers that differ from the model: 0 +- X.690's examples, answered by the model as the standard gives them: 14 of 14 +- Elements read (`decode` and `all`): 1084679, of which 418556 are accepted; why the model refuses the others: the contents run past the end of the input 191249, the input ends in the length octets 89253, an element runs past the end of the one it is in 74289, the indefinite length (10.1) 53076, a first subsequent tag octet with bits 7 to 1 zero (8.1.2.4.2 c) 51692, octets after the element 47542, a tag number below 31 in the high form (8.1.2.2) 41230, a long-form length with a leading zero octet (10.1) 32709, a primitive universal type in the constructed form 22644, the input ends in the identifier octets 22579, a long-form length below 128 (10.1) 20210, a BOOLEAN contents octet other than 00 and FF (11.1) 4579, universal 8, 11, 16, 17 or 29 in the primitive form 3865, a BOOLEAN without exactly one contents octet (8.2.1) 2884, the initial length octet 0xFF (8.1.3.5 c) 2639, universal tag 0 (X.680 Table 1, 8.1.5) 1589, an INTEGER with a redundant leading octet (8.3.2) 1522, a NULL with contents octets (8.8.2) 1151, an INTEGER with no contents octets (8.3.1) 634, an ENUMERATED with a redundant leading octet (8.4, 8.3.2) 633, an ENUMERATED with no contents octets (8.4, 8.3.1) 154 +- Integer contents read (`readint`): 91153, of which 85899 give a value; why the model refuses the others: an INTEGER with a redundant leading octet (8.3.2) 3560, an INTEGER with no contents octets (8.3.1) 1694 +- Encodings asked for: trees 200687, of which the model reads the tool's encoding back as the tree for 101141 of the 101141 the slice accepts; integers 35611 +- `valid` asked of 200410 trees, of which the slice accepts 100867; the model's answer holds exactly when it reads the tree's encoding back as the tree, for 200410 of 200410 +- Wycheproof: `tests/wycheproof/ecdsa_secp256r1_sha256_test.json`, SHA-256 182db4f3e230f6f9fa9f800d2a614dede30284b8e8438bbfe1171905402e9332: 484 signatures read with `all`, of which the tool refuses 113, every answer as the model's but 0. Flagged as an encoding that is not DER or is broken: BerEncodedSignature 7, of which the tool refuses 7, InvalidEncoding 92, of which the tool refuses 90, MissingZero 1, of which the tool refuses 0. Each flagged one it accepts is an element this slice accepts, with what is wrong in something it does not check: a negative INTEGER where a positive one belongs, a BIT STRING where an INTEGER belongs, or a SEQUENCE given the reserved universal tag 15 +- Requests nested 10,000 and 100,000 deep, each sent to a `der serve` of its own: 14, answered within 60 seconds: 14 +- Agreement with each oracle over the requests it answers: Go cryptobyte 1064404 of 1211443, Go encoding/asn1 1185392 of 1211443, rust-asn1 1150947 of 1173582, OpenSSL 1047170 of 1211443 +- Elements each oracle reads (`decode` and `all`) whose answer the model gives with the rules that oracle reads by, whether or not it agrees with the tool: Go cryptobyte 1084679 of 1084679, Go encoding/asn1 1084679 of 1084679, rust-asn1 1084679 of 1084679, OpenSSL 1084679 of 1084679 +- Requests each oracle answers, by operation: Go cryptobyte: all 381979, decode 702700, readint 91153, writeint 35611; Go encoding/asn1: all 381979, decode 702700, readint 91153, writeint 35611; rust-asn1: all 381979, decode 702700, readint 78101, writeint 10802; OpenSSL: all 381979, decode 702700, readint 91153, writeint 35611 +- Malformed requests answered `error`: 91 of 91; odd but well-formed requests answered as the model says: 33 of 33 +- Requests ending in CR LF: every seventh, 230362 +- Last lines sent alone, with no LF, with CR LF, or with a lone CR, answered right: 4 of 4 +- Command-line mode `der decode` and wrong arguments, printing and exiting as the model says: 29 of 29 +- Failures: 0 + +## Where the oracles differ + +Each disagreement is counted under the reason the model confirms: the model, with the rules the oracle reads by, gives the oracle's answer. The reason names each rule on which those part from DER for the input, with the first input that showed it. + +### Go cryptobyte + +- ReadAnyASN1Element refuses every tag number of 31 or more (the high form): 139048; for example `decode 1f1f00` +- ReadAnyASN1Element reads universal 8, 11, 16, 17 or 29 in the primitive form: 2912; for example `decode 0800` +- ReadAnyASN1Element reads a universal type other than BOOLEAN and INTEGER in the constructed form: 2901; for example `decode 3a0904034a6f6e04026573` +- ReadAnyASN1Element reads universal tag 0: 846; for example `decode 0000` +- cryptobyte has no reader for NULL, so a NULL with contents is read: 839; for example `decode 050100` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet: 383; for example `decode 0a020000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 80; for example `decode 0a00` +- ReadAnyASN1Element reads a universal type other than BOOLEAN and INTEGER in the constructed form; ReadAnyASN1Element reads universal tag 0: 29; for example `decode 23020000` +- ReadAnyASN1Element reads a universal type other than BOOLEAN and INTEGER in the constructed form; ReadAnyASN1Element reads universal 8, 11, 16, 17 or 29 in the primitive form; ReadAnyASN1Element reads universal tag 0: 1; for example `decode ba062a0410000000` + +### Go encoding/asn1 + +- parseBase128Int refuses a tag number above 2^31 - 1: 18944; for example `all 1f8880808000020102` +- Unmarshal into a RawValue reads universal 8, 11, 16, 17 or 29 in the primitive form: 3516; for example `decode 0800` +- Unmarshal into a RawValue reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form: 2170; for example `decode 3a0904034a6f6e04026573` +- Unmarshal into a RawValue reads universal tag 0: 919; for example `decode 0000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet: 386; for example `decode 0a020000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 81; for example `decode 0a00` +- Unmarshal into a RawValue reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form; Unmarshal into a RawValue reads universal tag 0: 33; for example `decode 23020000` +- Unmarshal into a RawValue reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form; Unmarshal into a RawValue reads universal 8, 11, 16, 17 or 29 in the primitive form; Unmarshal into a RawValue reads universal tag 0: 1; for example `decode ba062a0410000000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet; Unmarshal into a RawValue reads universal tag 0: 1; for example `decode 2b82030e0a0700400000000000000900fe8202fe668202fa1f90be5a81d6f3d28148330dd2f01c31542ed157d1361d0efb2723567bfc6cbb70ed5968e1035cae2e98f648c800990ca1e357bbb3323e853994984feb7dc978a0caae794234f7a620297112604d6043b37a78dd2d6ee6e6cfa4b91e2... (1581 characters)` + +### rust-asn1 + +- Tag::from_bytes refuses a tag number above 2^32 - 1: 15480; for example `all 1f9080808000020102` +- strip_tlv reads universal 8, 11, 16, 17 or 29 in the primitive form: 3548; for example `decode 0800` +- strip_tlv reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form: 2186; for example `decode 3a0904034a6f6e04026573` +- strip_tlv reads universal tag 0: 919; for example `decode 0000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet: 386; for example `decode 0a020000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 81; for example `decode 0a00` +- strip_tlv reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form; strip_tlv reads universal tag 0: 33; for example `decode 23020000` +- strip_tlv reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form; strip_tlv reads universal 8, 11, 16, 17 or 29 in the primitive form; strip_tlv reads universal tag 0: 1; for example `decode ba062a0410000000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet; strip_tlv reads universal tag 0: 1; for example `decode 2b82030e0a0700400000000000000900fe8202fe668202fa1f90be5a81d6f3d28148330dd2f01c31542ed157d1361d0efb2723567bfc6cbb70ed5968e1035cae2e98f648c800990ca1e357bbb3323e853994984feb7dc978a0caae794234f7a620297112604d6043b37a78dd2d6ee6e6cfa4b91e2... (1581 characters)` + +### OpenSSL + +- ASN1_get_object reads a first subsequent tag octet 0x80 (BER): 47408; for example `decode 1f801f00` +- ASN1_get_object reads a tag number below 31 in the high form (BER): 36095; for example `decode 1f1e00` +- asn1_get_length reads a long-form length with leading zero octets (BER): 25437; for example `decode 04810001` +- ASN1_get_object refuses a tag number above 2^31 - 1: 18944; for example `all 1f8880808000020102` +- asn1_get_length reads the long form for a length below 128 (BER): 17934; for example `decode 04810105` +- ASN1_get_object reads the indefinite length of a constructed element (BER), to end-of-contents octets 00 00: 7232; for example `decode 30800000` +- d2i_ASN1_TYPE reads any single contents octet as a BOOLEAN (BER, 8.2.2): 3984; for example `decode 010101` +- ASN1_get_object reads universal 8, 11, 16, 17 or 29 in the primitive form: 3516; for example `decode 0800` +- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6): 2170; for example `decode 3a0904034a6f6e04026573` +- ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 919; for example `decode 0000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet: 386; for example `decode 0a020000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 81; for example `decode 0a00` +- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 33; for example `decode 23020000` +- ASN1_get_object reads a first subsequent tag octet 0x80 (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 32; for example `decode 7f801f020000` +- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6); ASN1_get_object reads a tag number below 31 in the high form (BER): 31; for example `decode 3f1e00` +- ASN1_get_object reads a tag number below 31 in the high form (BER); ASN1_get_object reads universal 8, 11, 16, 17 or 29 in the primitive form: 24; for example `all 1f0800` +- asn1_get_length reads the long form for a length below 128 (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 11; for example `decode eb820170758201641a82011ddf500599e69be6a6af311518f2256f323a862ba82ffb64073d982b1375950b8f922b61d4aee7e507b1fe55345889d25f6d2244e53c20072a43ce07deefd66e42d9c2e00acbdf66e4874751496d34c7e3644133833f66e14ecc0bc5e8a0b0d8cb1d74004a67d74bed6... (755 characters)` +- ASN1_get_object reads a first subsequent tag octet 0x80 (BER); ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6): 6; for example `decode 3f801f00` +- d2i_ASN1_TYPE reads any single contents octet as a BOOLEAN (BER, 8.2.2); ASN1_get_object reads a tag number below 31 in the high form (BER): 5; for example `decode 1f010175` +- asn1_get_length reads the initial length octet 0xFF, skipping leading zero octets (BER): 4; for example `decode 04ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000... (267 characters)` +- asn1_get_length reads a long-form length with leading zero octets (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 4; for example `decode 208100f33f209ca08201a06b05` +- ASN1_get_object reads a tag number below 31 in the high form (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 3; for example `decode 7f1e020000` +- asn1_get_length reads the long form for a length below 128 (BER); ASN1_get_object reads a tag number below 31 in the high form (BER): 2; for example `decode 1f17810164349104dd26e413ff1f813f00b10082b800ce` +- asn1_get_length reads a long-form length with leading zero octets (BER); ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6): 2; for example `decode 33810030` +- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6); ASN1_get_object reads the indefinite length of a constructed element (BER), to end-of-contents octets 00 00: 1; for example `decode 3a8004034a6f6e040265730000` +- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6); ASN1_get_object reads a tag number below 31 in the high form (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `decode 3f1e020000` +- ASN1_get_object reads a first subsequent tag octet 0x80 (BER); ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `decode 3f801f020000` +- ASN1_get_object reads a tag number below 31 in the high form (BER); the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 1; for example `all 1f0a00` +- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6); ASN1_get_object reads universal 8, 11, 16, 17 or 29 in the primitive form; ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `decode ba062a0410000000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet; ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `decode 2b82030e0a0700400000000000000900fe8202fe668202fa1f90be5a81d6f3d28148330dd2f01c31542ed157d1361d0efb2723567bfc6cbb70ed5968e1035cae2e98f648c800990ca1e357bbb3323e853994984feb7dc978a0caae794234f7a620297112604d6043b37a78dd2d6ee6e6cfa4b91e2... (1581 characters)` +- asn1_get_length reads a long-form length with leading zero octets (BER); ASN1_get_object reads a tag number below 31 in the high form (BER): 1; for example `decode 5f1d810002a802a97bd15305f601c180318120a73a1f04` +- ASN1_get_object reads a first subsequent tag octet 0x80 (BER); asn1_get_length reads the long form for a length below 128 (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `decode 1f0081011f` +- ASN1_get_object reads a first subsequent tag octet 0x80 (BER); the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 1; for example `decode 1f800a000048059d3f60ff05028080` +- d2i_ASN1_TYPE reads any single contents octet as a BOOLEAN (BER, 8.2.2); asn1_get_length reads the long form for a length below 128 (BER): 1; for example `decode 01810131` diff --git a/tests/oracles/go/go.mod b/tests/oracles/go/go.mod new file mode 100644 index 0000000..4488e83 --- /dev/null +++ b/tests/oracles/go/go.mod @@ -0,0 +1,5 @@ +module der-oracle + +go 1.24.0 + +require golang.org/x/crypto v0.25.0 diff --git a/tests/oracles/go/go.sum b/tests/oracles/go/go.sum new file mode 100644 index 0000000..f7f8a2b --- /dev/null +++ b/tests/oracles/go/go.sum @@ -0,0 +1,2 @@ +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= diff --git a/tests/oracles/go/main.go b/tests/oracles/go/main.go new file mode 100644 index 0000000..65527d4 --- /dev/null +++ b/tests/oracles/go/main.go @@ -0,0 +1,314 @@ +// The Go oracles for tests/run_tests.py, one program with two modes: +// +// der-oracle cryptobyte golang.org/x/crypto/cryptobyte, at the version +// go.mod pins (go.sum holds its hash) +// der-oracle asn1 the standard library's encoding/asn1 +// +// Each line of standard input is one request in the form `der serve` +// reads (octets in lower-case hex, `-` for none; integers in decimal), +// and each answer is in the form it writes: `OK ...` or `FAIL`. A +// request the package cannot answer is answered `n/a`, and a line this +// program cannot read `error`. A line may end in LF or CR LF. +// +// decode HEX the element at the start of the octets, and the rest +// all HEX the element that is the whole input +// readint HEX the integer that contents octets encode +// writeint INT the contents octets of an integer +// encode TREE n/a +// valid TREE n/a +// +// An element is read by the package and printed as a tree in the syntax +// of `der serve`. With cryptobyte, String.ReadAnyASN1Element reads each +// element and String.ReadAnyASN1 its contents; a constructed element's +// contents are read the same way, element by element, to their end. An +// element of the universal class with number 1 or 2 must also be read by +// String.ReadASN1Boolean, or String.ReadASN1Integer into a *big.Int. +// With encoding/asn1, Unmarshal into a RawValue reads each element and +// gives its contents, read the same way when it is constructed; an +// element of the universal class with number 1 or 2 must also unmarshal +// into a bool or a *big.Int, and one with number 5 must equal +// NullRawValue in class, number, form and contents. +// +// readint frames the contents octets as an INTEGER element (cryptobyte +// with a Builder, encoding/asn1 with the length octets written here) and +// reads it with ReadASN1Integer or Unmarshal into a *big.Int. writeint +// writes the integer with Builder.AddASN1BigInt or Marshal, and prints +// the contents octets of what they write. +package main + +import ( + "bufio" + "bytes" + "encoding/asn1" + "encoding/hex" + "fmt" + "io" + "math/big" + "os" + "strings" + + "golang.org/x/crypto/cryptobyte" + cbasn1 "golang.org/x/crypto/cryptobyte/asn1" +) + +func field(f string) ([]byte, bool) { + if f == "-" { + return []byte{}, true + } + if f == "" || strings.ToLower(f) != f { + return nil, false + } + b, err := hex.DecodeString(f) + return b, err == nil +} + +func toField(b []byte) string { + if len(b) == 0 { + return "-" + } + return hex.EncodeToString(b) +} + +// integer reads a decimal integer, with `-` in front when negative. +func integer(f string) (*big.Int, bool) { + digits := strings.TrimPrefix(f, "-") + if digits == "" || strings.Trim(digits, "0123456789") != "" { + return nil, false + } + n, ok := new(big.Int).SetString(digits, 10) + if !ok { + return nil, false + } + if digits != f { + n.Neg(n) + } + return n, true +} + +// cbElement reads one element from s with cryptobyte and writes its tree +// to out. +func cbElement(s *cryptobyte.String, out *bytes.Buffer) bool { + var elem cryptobyte.String + var tag cbasn1.Tag + if !s.ReadAnyASN1Element(&elem, &tag) { + return false + } + cls, num, constructed := int(tag)>>6, int(tag)&0x1f, tag&0x20 != 0 + if cls == 0 && num == 1 { + e := elem + var v bool + if !e.ReadASN1Boolean(&v) || !e.Empty() { + return false + } + } + if cls == 0 && num == 2 { + e := elem + v := new(big.Int) + if !e.ReadASN1Integer(v) || !e.Empty() { + return false + } + } + var body cryptobyte.String + if !elem.ReadAnyASN1(&body, &tag) { + return false + } + if !constructed { + fmt.Fprintf(out, "p%d.%d.%s", cls, num, toField(body)) + return true + } + fmt.Fprintf(out, "c%d.%d(", cls, num) + for first := true; !body.Empty(); first = false { + if !first { + out.WriteByte(',') + } + if !cbElement(&body, out) { + return false + } + } + out.WriteByte(')') + return true +} + +// gaElement reads one element from b with encoding/asn1, writes its tree +// to out, and gives the octets after it. +func gaElement(b []byte, out *bytes.Buffer) ([]byte, bool) { + var raw asn1.RawValue + rest, err := asn1.Unmarshal(b, &raw) + if err != nil { + return nil, false + } + if raw.Class == asn1.ClassUniversal { + switch raw.Tag { + case asn1.TagBoolean: + var v bool + if r, err := asn1.Unmarshal(raw.FullBytes, &v); err != nil || len(r) != 0 { + return nil, false + } + case asn1.TagInteger: + var v *big.Int + if r, err := asn1.Unmarshal(raw.FullBytes, &v); err != nil || len(r) != 0 { + return nil, false + } + case asn1.TagNull: + n := asn1.NullRawValue + if raw.Class != n.Class || raw.Tag != n.Tag || raw.IsCompound != n.IsCompound || + !bytes.Equal(raw.Bytes, n.Bytes) { + return nil, false + } + } + } + if !raw.IsCompound { + fmt.Fprintf(out, "p%d.%d.%s", raw.Class, raw.Tag, toField(raw.Bytes)) + return rest, true + } + fmt.Fprintf(out, "c%d.%d(", raw.Class, raw.Tag) + body := raw.Bytes + for first := true; len(body) > 0; first = false { + if !first { + out.WriteByte(',') + } + var ok bool + if body, ok = gaElement(body, out); !ok { + return nil, false + } + } + out.WriteByte(')') + return rest, true +} + +// element reads the element at the start of b: its tree and the rest. +func element(mode string, b []byte) (string, []byte, bool) { + var out bytes.Buffer + if mode == "cryptobyte" { + s := cryptobyte.String(b) + if !cbElement(&s, &out) { + return "", nil, false + } + return out.String(), s, true + } + rest, ok := gaElement(b, &out) + if !ok { + return "", nil, false + } + return out.String(), rest, true +} + +// derLength writes length octets in the fewest octets, to frame contents +// octets as an element. +func derLength(n int) []byte { + if n < 128 { + return []byte{byte(n)} + } + var digits []byte + for ; n > 0; n >>= 8 { + digits = append([]byte{byte(n)}, digits...) + } + return append([]byte{0x80 | byte(len(digits))}, digits...) +} + +func readint(mode string, c []byte) string { + v := new(big.Int) + if mode == "cryptobyte" { + var b cryptobyte.Builder + b.AddASN1(cbasn1.INTEGER, func(c2 *cryptobyte.Builder) { c2.AddBytes(c) }) + framed, err := b.Bytes() + if err != nil { + return "n/a" + } + s := cryptobyte.String(framed) + if !s.ReadASN1Integer(v) || !s.Empty() { + return "FAIL" + } + return "OK " + v.String() + } + framed := append(append([]byte{0x02}, derLength(len(c))...), c...) + if rest, err := asn1.Unmarshal(framed, &v); err != nil || len(rest) != 0 { + return "FAIL" + } + return "OK " + v.String() +} + +func writeint(mode string, n *big.Int) string { + if mode == "cryptobyte" { + var b cryptobyte.Builder + b.AddASN1BigInt(n) + framed, err := b.Bytes() + if err != nil { + return "FAIL" + } + s := cryptobyte.String(framed) + var body cryptobyte.String + if !s.ReadASN1(&body, cbasn1.INTEGER) || !s.Empty() { + return "unreadable" + } + return "OK " + toField(body) + } + framed, err := asn1.Marshal(n) + if err != nil { + return "FAIL" + } + var raw asn1.RawValue + if rest, err := asn1.Unmarshal(framed, &raw); err != nil || len(rest) != 0 { + return "unreadable" + } + return "OK " + toField(raw.Bytes) +} + +func answer(mode, line string) string { + parts := strings.Split(line, " ") + if len(parts) != 2 { + return "error" + } + op, f := parts[0], parts[1] + switch op { + case "decode", "all", "readint": + b, ok := field(f) + if !ok { + return "error" + } + if op == "readint" { + return readint(mode, b) + } + tree, rest, ok := element(mode, b) + if !ok || (op == "all" && len(rest) != 0) { + return "FAIL" + } + if op == "all" { + return "OK " + tree + } + return "OK " + tree + " " + toField(rest) + case "writeint": + n, ok := integer(f) + if !ok { + return "error" + } + return writeint(mode, n) + case "encode", "valid": + return "n/a" + } + return "error" +} + +func main() { + if len(os.Args) != 2 || (os.Args[1] != "cryptobyte" && os.Args[1] != "asn1") { + fmt.Fprintln(os.Stderr, "usage: der-oracle cryptobyte|asn1") + os.Exit(2) + } + mode := os.Args[1] + in := bufio.NewReaderSize(os.Stdin, 1<<20) + out := bufio.NewWriterSize(os.Stdout, 1<<20) + defer out.Flush() + for { + line, err := in.ReadString('\n') + if line == "" && err != nil { + if err != io.EOF { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + return + } + line = strings.TrimSuffix(line, "\n") + line = strings.TrimSuffix(line, "\r") + fmt.Fprintln(out, answer(mode, line)) + } +} diff --git a/tests/oracles/openssl/wrapper.c b/tests/oracles/openssl/wrapper.c new file mode 100644 index 0000000..19172e6 --- /dev/null +++ b/tests/oracles/openssl/wrapper.c @@ -0,0 +1,358 @@ +/* + The OpenSSL oracle for tests/run_tests.py: the BER reader of libcrypto + (ASN1_get_object, d2i_ASN1_TYPE, d2i_ASN1_INTEGER, d2i_ASN1_NULL), + driven line by line. It is built against the system library, whose + version tests/run_tests.py checks. + + Each line of standard input is one request in the form `der serve` + reads (octets in lower-case hex, `-` for none; integers in decimal), + and each answer is in the form it writes: "OK ..." or "FAIL". A request + the library cannot answer is answered "n/a", and a line this program + cannot read "error". A line may end in LF or CR LF. + + decode HEX the element at the start of the octets, and the rest + all HEX the element that is the whole input + readint HEX the integer that contents octets encode + writeint INT the contents octets of an integer + encode TREE n/a + valid TREE n/a + version "OK" and the version of the library that runs + + ASN1_get_object reads the identifier and length octets of each element, + given the octets left in the input or in the element around it. It + reads BER: the indefinite length for a constructed element, lengths not + in the fewest octets, and tag numbers below 31 in the high form. A + constructed element's contents are read the same way, element by + element: to the end of its length, or, for the indefinite length, to + the end-of-contents octets 00 00 (X.690 8.1.5), which are not part of + the tree. An element of the universal class with number 1, 2 or 5 must + also be read by d2i_ASN1_TYPE as a BOOLEAN, by d2i_ASN1_INTEGER, or by + d2i_ASN1_NULL, given the same octets. The walk keeps its own stack, so + deep nesting does not use the machine stack. + + readint frames the contents octets as an INTEGER element (the length + octets are written here), reads it with d2i_ASN1_INTEGER and prints it + with ASN1_INTEGER_to_BN and BN_bn2dec. writeint reads the integer with + BN_dec2bn, writes it with BN_to_ASN1_INTEGER and i2d_ASN1_INTEGER, and + prints the contents octets of what that writes. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +/* A growing text buffer. */ +typedef struct { + char *s; + size_t n, cap; +} buf_t; + +static void put(buf_t *b, const char *s, size_t n) { + if (b->n + n + 1 > b->cap) { + b->cap = 2 * (b->n + n + 1); + b->s = realloc(b->s, b->cap); + if (b->s == NULL) abort(); + } + memcpy(b->s + b->n, s, n); + b->n += n; + b->s[b->n] = 0; +} + +static void puts_(buf_t *b, const char *s) { put(b, s, strlen(s)); } + +static void put_hex(buf_t *b, const unsigned char *p, long n) { + static const char digits[] = "0123456789abcdef"; + if (n == 0) { + puts_(b, "-"); + return; + } + for (long k = 0; k < n; ++k) { + char two[2] = {digits[p[k] >> 4], digits[p[k] & 15]}; + put(b, two, 2); + } +} + +static int hexval(int c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + return -1; +} + +/* The octets of a field, in a new buffer, with their number in *len; + NULL for a field that is not `-` or lower-case hex digits in pairs. */ +static unsigned char *field(const char *f, long *len) { + size_t n = strlen(f); + unsigned char *out; + if (strcmp(f, "-") == 0) { + *len = 0; + return calloc(1, 1); + } + if (n == 0 || n % 2 != 0) return NULL; + out = calloc(n / 2 + 1, 1); + for (size_t j = 0; j < n / 2; ++j) { + int hi = hexval(f[2 * j]), lo = hexval(f[2 * j + 1]); + if (hi < 0 || lo < 0) { + free(out); + return NULL; + } + out[j] = (unsigned char) (hi * 16 + lo); + } + *len = (long) (n / 2); + return out; +} + +/* Whether the universal BOOLEAN, INTEGER or NULL at p, with max octets + available, is one the library reads. */ +static int typed_ok(int tag, const unsigned char *p, long max) { + const unsigned char *q = p; + int ok = 0; + if (tag == V_ASN1_BOOLEAN) { + ASN1_TYPE *t = d2i_ASN1_TYPE(NULL, &q, max); + ok = t != NULL && ASN1_TYPE_get(t) == V_ASN1_BOOLEAN; + ASN1_TYPE_free(t); + } else if (tag == V_ASN1_INTEGER) { + ASN1_INTEGER *i = d2i_ASN1_INTEGER(NULL, &q, max); + ok = i != NULL; + ASN1_INTEGER_free(i); + } else if (tag == V_ASN1_NULL) { + ASN1_NULL *z = d2i_ASN1_NULL(NULL, &q, max); + ok = z != NULL; + ASN1_NULL_free(z); + } else { + ok = 1; + } + ERR_clear_error(); + return ok; +} + +/* An open constructed element: where its next element starts, where its + contents end (for the indefinite length, where the octets around it + end), whether its length is indefinite, and whether its next element + is its first. */ +typedef struct { + const unsigned char *p, *end; + int inf, first; +} frame_t; + +/* Reads the element at *pp, with the octets up to end available, and + writes the start of its tree. Moves *pp past a primitive element, or + past the identifier and length octets of a constructed one, which is + pushed. 0 when the library refuses it. */ +static int one(const unsigned char **pp, const unsigned char *end, buf_t *out, + frame_t **stack, size_t *depth, size_t *cap) { + const unsigned char *p = *pp; + long len; + int tag, xclass, ret; + char head[64]; + ret = ASN1_get_object(&p, &len, &tag, &xclass, (long) (end - *pp)); + ERR_clear_error(); + if (ret & 0x80) return 0; + if (xclass == V_ASN1_UNIVERSAL && !typed_ok(tag, *pp, (long) (end - *pp))) return 0; + if (ret & V_ASN1_CONSTRUCTED) { + snprintf(head, sizeof head, "c%d.%d(", xclass >> 6, tag); + puts_(out, head); + if (*depth == *cap) { + *cap = *cap ? 2 * *cap : 64; + *stack = realloc(*stack, *cap * sizeof **stack); + if (*stack == NULL) abort(); + } + (*stack)[(*depth)++] = (frame_t) {p, (ret & 1) ? end : p + len, ret & 1, 1}; + *pp = p; + return 1; + } + snprintf(head, sizeof head, "p%d.%d.", xclass >> 6, tag); + puts_(out, head); + put_hex(out, p, len); + *pp = p + len; + return 1; +} + +/* The element at the start of the n octets at data, as a tree in out, + and where the octets after it start in *rest. 0 when refused. */ +static int element(const unsigned char *data, long n, buf_t *out, const unsigned char **rest) { + static frame_t *stack = NULL; + static size_t cap = 0; + size_t depth = 0; + const unsigned char *p = data, *end = data + n; + if (n == 0) return 0; + if (!one(&p, end, out, &stack, &depth, &cap)) return 0; + /* After a constructed element is pushed, p is where its contents start; + the frame holds its own position from then on. */ + while (depth > 0) { + frame_t *f = &stack[depth - 1]; + int done; + if (f->inf) { + done = f->end - f->p >= 2 && f->p[0] == 0 && f->p[1] == 0; + if (done) f->p += 2; + else if (f->p == f->end) return 0; + } else { + done = f->p == f->end; + } + if (done) { + const unsigned char *after = f->p; + --depth; + puts_(out, ")"); + if (depth > 0) stack[depth - 1].p = after; + else p = after; + continue; + } + if (!f->first) puts_(out, ","); + f->first = 0; + { + const unsigned char *q = f->p, *limit = f->end; + size_t before = depth; + if (!one(&q, limit, out, &stack, &depth, &cap)) return 0; + f = &stack[before - 1]; + if (depth == before) f->p = q; + } + } + *rest = p; + return 1; +} + +static void der_length(buf_t *b, long n) { + unsigned char octs[16]; + int k = 0; + if (n < 128) { + unsigned char c = (unsigned char) n; + put(b, (char *) &c, 1); + return; + } + for (long m = n; m > 0; m >>= 8) octs[k++] = (unsigned char) (m & 0xff); + { + unsigned char c = (unsigned char) (0x80 | k); + put(b, (char *) &c, 1); + } + while (k > 0) put(b, (char *) &octs[--k], 1); +} + +static void readint(const unsigned char *c, long n, buf_t *out) { + buf_t framed = {0}; + const unsigned char *p; + ASN1_INTEGER *i; + put(&framed, "\x02", 1); + der_length(&framed, n); + put(&framed, (const char *) c, (size_t) n); + p = (const unsigned char *) framed.s; + i = d2i_ASN1_INTEGER(NULL, &p, (long) framed.n); + if (i == NULL || p != (const unsigned char *) framed.s + framed.n) { + puts_(out, "FAIL"); + } else { + BIGNUM *bn = ASN1_INTEGER_to_BN(i, NULL); + char *dec = bn ? BN_bn2dec(bn) : NULL; + if (dec) { + puts_(out, "OK "); + puts_(out, dec); + } else { + puts_(out, "unreadable"); + } + OPENSSL_free(dec); + BN_free(bn); + } + ASN1_INTEGER_free(i); + ERR_clear_error(); + free(framed.s); +} + +static void writeint(const char *f, buf_t *out) { + const char *digits = f[0] == '-' ? f + 1 : f; + BIGNUM *bn = NULL; + ASN1_INTEGER *i = NULL; + unsigned char *der = NULL; + int n; + if (*digits == 0 || strspn(digits, "0123456789") != strlen(digits)) { + puts_(out, "error"); + return; + } + if (BN_dec2bn(&bn, f) != (int) strlen(f) || (i = BN_to_ASN1_INTEGER(bn, NULL)) == NULL || + (n = i2d_ASN1_INTEGER(i, &der)) <= 0) { + puts_(out, "FAIL"); + } else { + const unsigned char *p = der; + long len; + int tag, xclass; + int ret = ASN1_get_object(&p, &len, &tag, &xclass, n); + if ((ret & 0x80) || p + len != der + n) { + puts_(out, "unreadable"); + } else { + puts_(out, "OK "); + put_hex(out, p, len); + } + } + OPENSSL_free(der); + ASN1_INTEGER_free(i); + BN_free(bn); + ERR_clear_error(); +} + +static void answer(char *line, buf_t *out) { + char *sp = strchr(line, ' '); + char *op = line, *f; + if (strcmp(line, "version") == 0) { + puts_(out, "OK "); + puts_(out, OpenSSL_version(OPENSSL_VERSION)); + return; + } + if (sp == NULL || strchr(sp + 1, ' ') != NULL) { + puts_(out, "error"); + return; + } + *sp = 0; + f = sp + 1; + if (strcmp(op, "decode") == 0 || strcmp(op, "all") == 0 || strcmp(op, "readint") == 0) { + long n; + unsigned char *b = field(f, &n); + if (b == NULL) { + puts_(out, "error"); + return; + } + if (strcmp(op, "readint") == 0) { + readint(b, n, out); + } else { + buf_t tree = {0}; + const unsigned char *rest = NULL; + if (!element(b, n, &tree, &rest) || (strcmp(op, "all") == 0 && rest != b + n)) { + puts_(out, "FAIL"); + } else { + puts_(out, "OK "); + put(out, tree.s, tree.n); + if (strcmp(op, "decode") == 0) { + puts_(out, " "); + put_hex(out, rest, (long) (b + n - rest)); + } + } + free(tree.s); + } + free(b); + } else if (strcmp(op, "writeint") == 0) { + writeint(f, out); + } else if (strcmp(op, "encode") == 0 || strcmp(op, "valid") == 0) { + puts_(out, "n/a"); + } else { + puts_(out, "error"); + } +} + +int main(void) { + char *line = NULL; + size_t cap = 0; + ssize_t got; + buf_t out = {0}; + while ((got = getline(&line, &cap, stdin)) > 0) { + if (line[got - 1] == '\n') line[--got] = 0; + if (got > 0 && line[got - 1] == '\r') line[--got] = 0; + out.n = 0; + answer(line, &out); + put(&out, "\n", 1); + fwrite(out.s, 1, out.n, stdout); + } + free(line); + free(out.s); + return 0; +} diff --git a/tests/oracles/rust/Cargo.lock b/tests/oracles/rust/Cargo.lock new file mode 100644 index 0000000..435845d --- /dev/null +++ b/tests/oracles/rust/Cargo.lock @@ -0,0 +1,72 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "asn1" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9795210620c0cb3f9a7ce4f882808c38e1ef7b347c90591dceae0886e031fb1" +dependencies = [ + "asn1_derive", + "itoa", +] + +[[package]] +name = "asn1_derive" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909e307f1cc32bb8bccbd98f446e6d1bf03fa30f7b53a4337da7181ad30fa11a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "der-oracle" +version = "0.1.0" +dependencies = [ + "asn1", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" diff --git a/tests/oracles/rust/Cargo.toml b/tests/oracles/rust/Cargo.toml new file mode 100644 index 0000000..3464bc9 --- /dev/null +++ b/tests/oracles/rust/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "der-oracle" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +asn1 = { version = "=0.24.1", default-features = false, features = ["std"] } + +[profile.release] +debug = false diff --git a/tests/oracles/rust/src/main.rs b/tests/oracles/rust/src/main.rs new file mode 100644 index 0000000..d545750 --- /dev/null +++ b/tests/oracles/rust/src/main.rs @@ -0,0 +1,208 @@ +//! The Rust oracle for tests/run_tests.py: the `asn1` crate (rust-asn1, +//! a DER-only reader), at the version Cargo.lock pins with its checksum. +//! +//! Each line of standard input is one request in the form `der serve` +//! reads (octets in lower-case hex, `-` for none; integers in decimal), +//! and each answer is in the form it writes: `OK ...` or `FAIL`. A request +//! the crate cannot answer is answered `n/a`, and a line this program +//! cannot read `error`. A line may end in LF or CR LF. +//! +//! decode HEX the element at the start of the octets, and the rest +//! all HEX the element that is the whole input +//! readint HEX the integer that contents octets encode +//! writeint INT the contents octets of an integer +//! encode TREE n/a +//! valid TREE n/a +//! +//! `strip_tlv` reads each element as a `Tlv` and gives the octets after +//! it; a constructed element's contents are read the same way, element by +//! element, to their end. An element of the universal class with number +//! 1, 2 or 5 must also parse, with `parse_single`, as a `bool`, a `BigInt` +//! or a `Null`. The walk keeps its own stack, so deep nesting does not +//! use the machine stack. +//! +//! readint frames the contents octets as an INTEGER element (the length +//! octets are written here) and parses it as a `BigInt`: FAIL when that +//! fails, else the value from parsing it as an `i64`, or n/a when the +//! contents are longer than 8 octets. writeint writes an `i64` with +//! `write_single`, and prints the contents octets of what it writes; an +//! integer outside the `i64` range is n/a. + +use std::fmt::Write as _; +use std::io::{self, BufRead, BufWriter, Write}; + +fn field(f: &str) -> Option> { + if f == "-" { + return Some(Vec::new()); + } + if f.is_empty() || f.len() % 2 != 0 { + return None; + } + let digit = |c: u8| match c { + b'0'..=b'9' => Some(c - b'0'), + b'a'..=b'f' => Some(c - b'a' + 10), + _ => None, + }; + f.as_bytes() + .chunks(2) + .map(|p| Some(digit(p[0])? * 16 + digit(p[1])?)) + .collect() +} + +fn to_field(b: &[u8]) -> String { + if b.is_empty() { + return "-".to_string(); + } + let mut s = String::with_capacity(2 * b.len()); + for x in b { + write!(s, "{x:02x}").unwrap(); + } + s +} + +/// Reads one element with `strip_tlv`, checks its contents if it is a +/// universal BOOLEAN, INTEGER or NULL, and writes the start of its tree. +/// Gives its contents when it is constructed, and the rest. +fn one<'a>(data: &'a [u8], out: &mut String) -> Option<(Option<&'a [u8]>, &'a [u8])> { + let (tlv, rest) = asn1::strip_tlv(data).ok()?; + let tag = tlv.tag(); + // The class as the crate numbers it: universal 0, application 1, + // context-specific 2, private 3, as in Table 1 of X.690. + let cls = tag.class() as u8; + if cls == 0 { + let ok = match tag.value() { + 1 => asn1::parse_single::(tlv.full_data()).is_ok(), + 2 => asn1::parse_single::>(tlv.full_data()).is_ok(), + 5 => asn1::parse_single::(tlv.full_data()).is_ok(), + _ => true, + }; + if !ok { + return None; + } + } + if tag.is_constructed() { + write!(out, "c{}.{}(", cls, tag.value()).unwrap(); + Some((Some(tlv.data()), rest)) + } else { + write!(out, "p{}.{}.{}", cls, tag.value(), to_field(tlv.data())).unwrap(); + Some((None, rest)) + } +} + +/// The element at the start of `data`, as a tree, and the rest. +fn element(data: &[u8]) -> Option<(String, &[u8])> { + let mut out = String::new(); + let (body, rest) = one(data, &mut out)?; + // The unread contents of each open constructed element, and whether + // its next element is its first. + let mut stack: Vec<(&[u8], bool)> = Vec::new(); + if let Some(b) = body { + stack.push((b, true)); + } + while let Some(top) = stack.last_mut() { + if top.0.is_empty() { + stack.pop(); + out.push(')'); + continue; + } + if !top.1 { + out.push(','); + } + top.1 = false; + let (body, after) = one(top.0, &mut out)?; + top.0 = after; + if let Some(b) = body { + stack.push((b, true)); + } + } + Some((out, rest)) +} + +fn der_length(n: usize) -> Vec { + if n < 128 { + return vec![n as u8]; + } + let digits: Vec = n.to_be_bytes().iter().copied().skip_while(|&d| d == 0).collect(); + let mut out = vec![0x80 | digits.len() as u8]; + out.extend(digits); + out +} + +fn readint(c: &[u8]) -> String { + let mut framed = vec![0x02]; + framed.extend(der_length(c.len())); + framed.extend_from_slice(c); + if asn1::parse_single::>(&framed).is_err() { + return "FAIL".to_string(); + } + if c.len() > 8 { + return "n/a".to_string(); + } + match asn1::parse_single::(&framed) { + Ok(v) => format!("OK {v}"), + Err(_) => "unreadable".to_string(), + } +} + +fn writeint(f: &str) -> String { + let digits = f.strip_prefix('-').unwrap_or(f); + if digits.is_empty() || !digits.bytes().all(|c| c.is_ascii_digit()) { + return "error".to_string(); + } + let v: i64 = match f.parse::() { + Ok(v) => v, + Err(_) => return "n/a".to_string(), + }; + let framed = match asn1::write_single(&v) { + Ok(b) => b, + Err(_) => return "FAIL".to_string(), + }; + match asn1::strip_tlv(&framed) { + Ok((tlv, rest)) if rest.is_empty() => format!("OK {}", to_field(tlv.data())), + _ => "unreadable".to_string(), + } +} + +fn answer(line: &str) -> String { + let parts: Vec<&str> = line.split(' ').collect(); + if parts.len() != 2 { + return "error".to_string(); + } + let (op, f) = (parts[0], parts[1]); + match op { + "decode" | "all" | "readint" => { + let b = match field(f) { + Some(b) => b, + None => return "error".to_string(), + }; + if op == "readint" { + return readint(&b); + } + match element(&b) { + Some((tree, rest)) if op == "decode" => format!("OK {} {}", tree, to_field(rest)), + Some((tree, rest)) if rest.is_empty() => format!("OK {tree}"), + _ => "FAIL".to_string(), + } + } + "writeint" => writeint(f), + "encode" | "valid" => "n/a".to_string(), + _ => "error".to_string(), + } +} + +fn main() { + let stdin = io::stdin(); + let mut input = stdin.lock(); + let mut out = BufWriter::with_capacity(1 << 20, io::stdout().lock()); + let mut line = String::new(); + loop { + line.clear(); + if input.read_line(&mut line).expect("reading standard input") == 0 { + break; + } + let l = line.strip_suffix('\n').unwrap_or(&line); + let l = l.strip_suffix('\r').unwrap_or(l); + writeln!(out, "{}", answer(l)).unwrap(); + } + out.flush().unwrap(); +} diff --git a/tests/plant_bugs.py b/tests/plant_bugs.py new file mode 100644 index 0000000..ee31ef3 --- /dev/null +++ b/tests/plant_bugs.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 +"""Check that the differential tests catch planted bugs. + +Each bug is a one-line change to Der/Basic.lean, or to Main.lean where +its description says so. The script copies the project to a temporary +folder, plants one bug at a time, runs tests/run_tests.py there with a +small count and without the oracles, and records how many failures the +tests report. So every failure counted is the tool disagreeing with the +model of X.690; an oracle that crashes or times out cannot pass for a +catch. Every planted bug must give at least one failure. The unchanged +copy must give none. A bug that stops the executable from building is +not a catch: the tests then give up, and the bug counts as missed. So is +any run that gives up (its record shows `- Failures: 1 (why)`), a run +that dies without writing its record, and one whose exit status does +not match the failures it records. The tests build only the executable +(`lake build der`), so a bug that breaks a proof still runs. + + python3 tests/plant_bugs.py [--count N] + +The script writes tests/PLANTED-BUGS.md and exits non-zero if any bug +goes uncaught. +""" + +import argparse +import re +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +RECORD = ROOT / "tests" / "PLANTED-BUGS.md" +CODE = "Der/Basic.lean" + +# (what the bug does, the line as written, the line with the bug[, file]) +BUGS = [ + ("the long form is read for a length below 128", + " if n < 128 then none else some (n, k + 1, bs.drop k)", + " if n < 0 then none else some (n, k + 1, bs.drop k)"), + ("the long form is refused for a length below 256", + " if n < 128 then none else some (n, k + 1, bs.drop k)", + " if n < 256 then none else some (n, k + 1, bs.drop k)"), + ("a leading zero length octet is read", + " if ds.length < k ∨ ds.head? = some 0 then none", + " if ds.length < k then none"), + ("the indefinite length octet 0x80 is read as length 0", + " if b.toNat < 128 then some (b.toNat, 1, bs)", + " if b.toNat ≤ 128 then some (b.toNat % 128, 1, bs)"), + ("a tag number below 31 is read in the high form", + " if tag < 31 then none else some (cls, constructed, tag, ds.length + 2, rest)", + " if tag < 0 then none else some (cls, constructed, tag, ds.length + 2, rest)"), + ("a first subsequent tag octet 0x80 is read", + " if d = 0 then none", + " if d = 128 then none"), + ("the continuation bit of a tag octet is tested as 0x40", + " if b.toNat < 128 then some ((b.toNat :: acc).reverse, bs)", + " if b.toNat < 64 then some ((b.toNat :: acc).reverse, bs)"), + ("the constructed bit is read from bit 7", + " let constructed := b.toNat / 32 % 2 == 1", + " let constructed := b.toNat / 64 % 2 == 1"), + ("a BOOLEAN 01 is read", + " | 1 => c == [0] || c == [255]", + " | 1 => c.length == 1"), + ("an INTEGER with a redundant leading zero octet is read", + " | a :: b :: _ => !(a.toNat == 255 && 128 ≤ b.toNat) && !(a.toNat == 0 && b.toNat < 128)", + " | a :: b :: _ => !(a.toNat == 255 && 128 ≤ b.toNat)"), + ("a NULL with one contents octet is read", + " | 5 => c.isEmpty", + " | 5 => c.length ≤ 1"), + ("universal tag 0 is read", + " cls != 0 || (tag != 0 && !constructedUniversal tag && contentsOk tag c)", + " cls != 0 || (!constructedUniversal tag && contentsOk tag c)"), + ("a primitive SEQUENCE is read", + " cls != 0 || (tag != 0 && !constructedUniversal tag && contentsOk tag c)", + " cls != 0 || (tag != 0 && !(constructedUniversal tag && tag != 16) && contentsOk tag c)"), + ("a constructed INTEGER is read", + " cls != 0 || constructedUniversal tag || reservedUniversal tag", + " cls != 0 || constructedUniversal tag || reservedUniversal tag || tag == 2"), + ("universal 29 is taken for a primitive type", + " tag == 8 || tag == 11 || tag == 16 || tag == 17 || tag == 29", + " tag == 8 || tag == 11 || tag == 16 || tag == 17"), + ("the reserved universal numbers start at 38, not 37", + "def reservedUniversal (tag : Nat) : Bool := tag == 15 || 37 ≤ tag", + "def reservedUniversal (tag : Nat) : Bool := tag == 15 || 38 ≤ tag"), + ("an element may run one octet past the one it is in", + " if lim < n1 + n2 + len then none", + " if lim + 1 < n1 + n2 + len then none"), + ("the elements of a constructed element must take one octet more than it holds", + " match readElements fuel (lim - n) b' with", + " match readElements fuel (lim - n + 1) b' with"), + ("the reader's fuel is half the length of the input", + " match readElement b.length b.length b with", + " match readElement (b.length / 2) b.length b with"), + ("decodeAll allows octets after the element", + " | some (e, []) => some e", + " | some (e, _) => some e"), + ("long numbers: the first half is shifted by the length of the first half, not the second", + " (go fuel (ds.take h) <<< (w * (ds.length - h))) + go fuel (ds.drop h)", + " (go fuel (ds.take h) <<< (w * h)) + go fuel (ds.drop h)"), + ("readInt takes a first octet 0x80 for a positive integer", + " | a :: _ => some (if a.toNat < 128 then (u : Int) else (u : Int) - 2 ^ (8 * c.length))", + " | a :: _ => some (if a.toNat ≤ 128 then (u : Int) else (u : Int) - 2 ^ (8 * c.length))"), + ("writeInt writes 128 with no zero octet in front", + " | d :: _ => if 128 ≤ d.toNat then 0 :: ds else ds", + " | d :: _ => if 128 < d.toNat then 0 :: ds else ds"), + ("the writer gives no subsequent tag octet but the last its bit 8", + " (lead + 31).toUInt8 :: (ds.dropLast.map fun d => (d + 128).toUInt8) ++ " + "[(ds.getLastD 0).toUInt8]", + " (lead + 31).toUInt8 :: (ds.dropLast.map fun d => d.toUInt8) ++ " + "[(ds.getLastD 0).toUInt8]"), + ("the writer writes length 128 in the short form", + " if n < 128 then [n.toUInt8]", + " if n ≤ 128 then [n.toUInt8]"), + ("the writer writes tag number 31 in one octet", + " if tag < 31 then [(lead + tag).toUInt8]", + " if tag ≤ 31 then [(lead + tag).toUInt8]"), + ("Main.lean: `all` allows octets after the element", + " pure (match decodeAll b with", + " pure (match (decode b).map (·.1) with", + "Main.lean"), + ("Main.lean: `der decode` exits with 0 when the input is not an element", + ' | none => IO.eprintln "not a DER element"; return 1', + ' | none => IO.eprintln "not a DER element"; return 0', + "Main.lean"), + ("Main.lean: the CR of a CR LF line ending is kept", + " String.ofList (if cs.getLast? = some '\\r' then cs.dropLast else cs)", + " String.ofList cs", + "Main.lean"), + ("Main.lean: a primitive element is printed with its number before its class", + ' | .prim cls tag c => s ++ s!"p{cls}.{tag}." ++ toHex c', + ' | .prim cls tag c => s ++ s!"p{tag}.{cls}." ++ toHex c', + "Main.lean"), + ("Main.lean: upper-case hex digits are read", + " else if 'a' ≤ c ∧ c ≤ 'f' then some (c.toNat - 'a'.toNat + 10)", + " else if 'a' ≤ c.toLower ∧ c.toLower ≤ 'f' then some (c.toLower.toNat - 'a'.toNat + 10)", + "Main.lean"), + ("an ENUMERATED is not checked", + " | 10 => intOk c", + " | 10 => true"), + ("valid takes a primitive element of class 4", + " | .prim cls tag c => cls < 4 && primOk cls tag c && c.length < 256 ^ 126", + " | .prim cls tag c => cls < 5 && primOk cls tag c && c.length < 256 ^ 126"), + ("valid does not look inside a constructed element", + " cls < 4 && consOk cls tag && validList cs && (encodeList cs).length < 256 ^ 126", + " cls < 4 && consOk cls tag && (encodeList cs).length < 256 ^ 126"), + ("Main.lean: `valid` answers OK for every tree", + ' pure (if valid e then "OK" else "FAIL")', + ' pure "OK"', + "Main.lean"), +] + + +def failures(copy, count): + """The failures a run of the tests reports, or None, with the run's + output, when it reports none: the code did not build, or the run did + not finish. A run that dies writes no record, so the old one is + removed first rather than read again.""" + record = copy / "tests" / "TEST-RESULTS.md" + record.unlink(missing_ok=True) + run = subprocess.run([sys.executable, "tests/run_tests.py", "--count", str(count), + "--no-oracles"], + cwd=copy, capture_output=True, text=True) + out = run.stdout + run.stderr + if not record.exists(): + return None, f"no record written, exit status {run.returncode}\n{out}" + m = re.search(r"^- Failures: (\d+)(.*)$", record.read_text(encoding="utf-8"), re.M) + if m is None or m.group(2).strip(): + return None, out # the run gave up, for example the bug did not build: not a catch + n = int(m.group(1)) + if run.returncode != (1 if n else 0): + return None, f"{n} failures but exit status {run.returncode}\n{out}" + return n, None + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--count", type=int, default=5000) + args = ap.parse_args() + rows, missed = [], 0 + with tempfile.TemporaryDirectory() as tmp: + copy = Path(tmp) / "der" + shutil.copytree(ROOT, copy, symlinks=True, + ignore=shutil.ignore_patterns("PLANTED-BUGS.md", "__pycache__", + "oracle-go", "oracle-rust", + "oracle-openssl")) + n, err = failures(copy, args.count) + rows.append(("none", n)) + if n != 0: + missed += 1 + print(f"none: {n} failures" + (" (did not build or did not finish)" if err else ""), + flush=True) + if err: + print(err[-2000:], flush=True) + for what, line, bug, *where in BUGS: + name = where[0] if where else CODE + code = copy / name + original = code.read_text(encoding="utf-8") + if original.count(line) != 1: + sys.exit(f"{name} no longer holds, once, the line for {what!r}: {line}") + code.write_text(original.replace(line, bug), encoding="utf-8") + n, err = failures(copy, args.count) + code.write_text(original, encoding="utf-8") + rows.append((what, n)) + if not n: + missed += 1 + print(f"{what}: {n} failures" + (" (did not build or did not finish)" if err else ""), + flush=True) + if err: + print(err[-2000:], flush=True) + n_main = sum(1 for b in BUGS if b[3:] == ("Main.lean",)) + lines = ["# Planted bugs", "", + f"Written by `tests/plant_bugs.py`. Each bug is a change of one line, to `{CODE}` " + f"({len(BUGS) - n_main} bugs) or to `Main.lean` ({n_main}), tested with " + f"`tests/run_tests.py --count {args.count} --no-oracles`, which builds only the " + "executable and compares it with the model alone. Every bug must give failures, and " + "the unchanged code none. A bug that does not build, or a run that gives up or " + "dies, counts as missed.", "", + "| Planted bug | Failures |", "|---|---|"] + lines += [f"| {what} | {'did not build or did not finish' if n is None else n} |" + for what, n in rows] + lines += ["", f"Bugs caught: {sum(1 for what, n in rows[1:] if n)} of {len(BUGS)}", ""] + RECORD.write_text("\n".join(lines), encoding="utf-8") + return 1 if missed else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/run_tests.py b/tests/run_tests.py new file mode 100644 index 0000000..bc82929 --- /dev/null +++ b/tests/run_tests.py @@ -0,0 +1,1909 @@ +#!/usr/bin/env python3 +"""Differential tests: the Lean DER reader and writer against a model of +X.690 and four implementations. + +The tool answers `decode`, `all`, `encode`, `valid`, `readint` and +`writeint` requests (see the header of Main.lean). Its answers are +checked two ways. + +1. Against the model, a Python transcription of Rec. ITU-T X.690 + (02/2021), with Table 1 of X.680 (02/2021) for the universal tags, + that shares nothing with the Lean code. `read` reads one element as + clause 8 lays it out, with the restrictions of clauses 10 and 11 and + of this slice: + - identifier octets as 8.1.2: numbers 0 to 30 in one octet, 31 and up + in the high form, whose first subsequent octet does not have bits 7 + to 1 zero, and no number below 31 in the high form; + - length octets in the definite form and in the fewest octets (8.1.3, + 10.1), never the initial octet 0xFF (8.1.3.5 c); + - no universal tag 0; the universal types X.690 encodes as + constructed (8, 11, 16, 17, 29) only constructed; the numbers Table + 1 reserves (15, and 37 on) in either form; every other universal + type only primitive; + - a BOOLEAN of one octet, 00 or FF (8.2.1, 11.1), an INTEGER of one + or more octets in the fewest (8.3.1, 8.3.2), an ENUMERATED the + same, as it is encoded as its integer (8.4), a NULL with none + (8.8.2); + - the contents of a constructed element are elements, one after + another, which end exactly where it ends. + `encode` writes a tree (8.1.2, 8.1.3, 10.1), and `read_int` and + `write_int` the two's complement integers of 8.3. X.690 has four + classes; for a class number of 4 or more, `encode` writes it as + `Spec.der` does, the class times 64 taken as an octet, so modulo 4. + `valid` says whether the slice accepts a tree: every element of a + class from 0 to 3, the universal tags and contents as `read` checks + them, and every length within the long form. The model also checks + that `valid` holds exactly when `read` reads the tree's encoding back + as the tree. No other content rule (bit strings, object identifiers, + strings, times, the order of a SET) is checked in this slice. The + model is checked against the examples X.690 gives. Every answer must + agree. + +2. Against four oracles, on every request each can answer: + + - Go cryptobyte: golang.org/x/crypto/cryptobyte, at the version + tests/oracles/go/go.mod pins (go.sum holds its hash); + - Go encoding/asn1: the standard library's package, built by the + same Go; + - rust-asn1: the `asn1` crate, at the version and checksum + tests/oracles/rust/Cargo.lock pins, built with `cargo --locked`; + - OpenSSL: libcrypto, the version OPENSSL_PINNED, through + tests/oracles/openssl/wrapper.c built with `cc`. + + Each reads elements with the package's own reader and walks a + constructed element's contents element by element, checks a + universal BOOLEAN, INTEGER and NULL with the package's own readers + for them, and prints the tree in the syntax of `der serve` (the + program in each oracle's folder says how). The programs check no + ENUMERATED contents. Each answers `decode`, `all`, `readint` and + `writeint` (rust-asn1 gives a value only within 64 bits), and no + `encode` or `valid`. An oracle that cannot be built or started + is left out, and the record says so on its `- Oracles left out:` + line. + + A disagreement is allowed only for a named reason that the model + confirms: `read`, with the rules the oracle reads by (ORACLE_RULES), + gives the oracle's answer. The reason names each rule where the two + part on the way, and each disagreement is counted under its reason. + Any other disagreement is a failure. The rules: + - cryptobyte reads no tag number of 31 or more and no length of more + than 4 octets, checks the form only of BOOLEAN and INTEGER, and has + no reader for NULL; + - encoding/asn1 reads tag numbers and lengths up to 2^31 - 1, and + checks the form only of BOOLEAN, INTEGER and NULL; + - rust-asn1 reads tag numbers and lengths up to 2^32 - 1, and checks + the form only of BOOLEAN, INTEGER and NULL; + - OpenSSL reads BER: the indefinite length for a constructed element, + to end-of-contents octets 00 00; lengths not in the fewest octets, + even after the initial octet 0xFF, of at most 8 significant octets + and when one more octet follows them; tag numbers in any high + form, up to 2^31 - 1; and any single octet as a BOOLEAN. It checks + the form only of BOOLEAN, INTEGER and NULL. + None of them refuses universal tag 0, and none of the programs + checks the contents of an ENUMERATED. The record also counts, for each + oracle, the elements whose answer `read` gives with its rules, whether + or not it agrees with the tool. + +Inputs come from a recorded seed, after fixed sets: + +- the examples of X.690: BOOLEAN TRUE (8.2.2), the lengths 38 and 201 + (8.1.3.4, 8.1.3.5), NULL (8.8.2), and the three encodings of "Jones" + (8.23.5), of which DER allows the primitive one (10.1, 10.2); +- every input of 0, 1 and 2 octets, read with `decode` and `all`; +- every identifier first octet, with subsequent tag octets when it + takes them, and short contents; +- every initial length octet 0x80 to 0xFF, with several subsequent + octets and contents; +- lengths 0 to 100,000 around 127, 128, 255, 256, 65535 and 65536: + whole, cut short, with the length one more and one less, not in the + fewest octets, and inside a SEQUENCE; +- tag numbers around 31, 127, 128, 16383, 16384, 2^21, 2^28, 2^31, + 2^32, 2^63 and 2^64, and one of 1,000 subsequent octets, in each + class and form, cut short and with a first subsequent octet 0x80; +- every universal tag 0 to 40 in both forms, encoded, read and asked + `valid`, and the high form for each below 31; +- BOOLEAN and NULL contents, and every INTEGER of 0, 1 and 2 octets + with some longer ones, read as an element and with `readint`, and + the same contents read as an ENUMERATED; +- chains nested 10,000 and 100,000 deep: whole, with octets after, with + mixed classes and tags, with a bad BOOLEAN inside, with one element + running past the one around it, and cut short. Each goes to a `der + serve` of its own, which must answer within DEEP_BUDGET seconds; the + 10,000 deep ones are also encoded; +- every signature of the Wycheproof vectors in + tests/wycheproof/ecdsa_secp256r1_sha256_test.json, whose SHA-256 is + checked first, read with `all`. The record says how many of those + flagged BerEncodedSignature, InvalidEncoding and MissingZero the tool + refuses; +- random trees the slice accepts, of every class, with tags in both + forms, up to 6 levels and 8 elements in a constructed one, encoded by + the model and read whole, with octets after, and mutated: an + element's length one more or one less, not in the fewest octets or + indefinite, its tag in the high form below 31 or with a first + subsequent octet 0x80, its constructed bit flipped, its length too + short for the elements in it, so that the last runs past it, its + BOOLEAN, INTEGER or NULL contents changed; and the whole encoding with + a bit flipped or cut short. + Each tree is also encoded, and the model reads the tool's encoding + back as the tree. Each is asked `valid`, and so is a copy with one + element the slice does not accept: of a class from 4 to 7, universal + tag 0, a universal type in the wrong form, or BOOLEAN, INTEGER, + ENUMERATED or NULL contents DER forbids; that copy is also encoded; +- random octet strings; `readint` on random contents; `writeint` on + integers from -300 to 300, around plus and minus 2^k for k up to + 1,000, and random ones of up to 4,096 bits; +- malformed requests, which the tool must answer with `error`, and odd + but well-formed ones; +- the command-line mode `der decode`, on input that decodes, input that + does not, malformed hex, and wrong arguments: what each run prints on + standard output and its exit status. + +Every seventh request ends in CR LF. + + python3 tests/run_tests.py [--seed N] [--count N] [--no-oracles] + +`--count` is the number of random trees; half as many random octet +strings are read, and a quarter as many random integer contents and +integers. `--no-oracles` leaves every oracle out and compares the tool +with the model alone, as tests/plant_bugs.py does. The script builds the +executable and the oracles first, writes tests/TEST-RESULTS.md, and +exits non-zero if any check fails. +""" + +import argparse +import hashlib +import json +import os +import random +import re +import subprocess +import sys +import threading +import time +from dataclasses import dataclass +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +EXE = ROOT / ".lake" / "build" / "bin" / "der" +TESTS = ROOT / "tests" +WYCHEPROOF = TESTS / "wycheproof" / "ecdsa_secp256r1_sha256_test.json" +# https://raw.githubusercontent.com/C2SP/wycheproof/main/testvectors_v1/ecdsa_secp256r1_sha256_test.json +WYCHEPROOF_SHA256 = "182db4f3e230f6f9fa9f800d2a614dede30284b8e8438bbfe1171905402e9332" +ORACLES = TESTS / "oracles" +GO_SRC = ORACLES / "go" +GO_EXE = ROOT / ".lake" / "oracle-go" / "der-oracle" +RUST_SRC = ORACLES / "rust" +RUST_TARGET = ROOT / ".lake" / "oracle-rust" +RUST_EXE = RUST_TARGET / "release" / "der-oracle" +OPENSSL_SRC = ORACLES / "openssl" +OPENSSL_EXE = ROOT / ".lake" / "oracle-openssl" / "openssl-oracle" +OPENSSL_PINNED = "3.0.13" +RESULTS = TESTS / "TEST-RESULTS.md" +TIMEOUT = 3600 +BUILD_TIMEOUT = 3600 +# A request nested 10,000 or 100,000 deep goes to a `der serve` of its +# own, which must answer it within this many seconds. +DEEP_BUDGET = 60 +# The other requests are split among this many `der serve` processes. +JOBS = max(1, min(4, os.cpu_count() or 1)) + +# --------------------------------------------------------------------- +# The model: X.690. +# +# A tree is ("p", class, number, contents) for a primitive element and +# ("c", class, number, [elements]) for a constructed one, the elements +# `der serve` prints. Classes are numbered as Table 1 of X.690 encodes +# them in bits 8 and 7: universal 0, application 1, context-specific 2, +# private 3. + +# The universal types X.690 encodes as constructed: 8, external and +# instance-of (8.18.1, 8.16.1); 11, embedded-pdv (8.17.1); 16, sequence +# and sequence-of (8.9.1, 8.10.1); 17, set and set-of (8.11.1, 8.12.1); +# 29, the unrestricted character string (8.24.1). +CONSTRUCTED_UNIVERSAL = {8, 11, 16, 17, 29} + + +def reserved_universal(n): + """X.680 Table 1: 15 is "Reserved for future editions", and 37 on + "Reserved for addenda". This slice reads them in either form.""" + return n == 15 or n >= 37 + + +# Why the model refuses an input. Each is also the name of a rule on +# which an oracle may part from DER (see `Rules`). +WHY_IDENT_ENDS = "the input ends in the identifier octets" +WHY_LENGTH_ENDS = "the input ends in the length octets" +WHY_CONTENTS_END = "the contents run past the end of the input" +WHY_PAST_PARENT = "an element runs past the end of the one it is in" +WHY_HIGH_LOW = "a tag number below 31 in the high form (8.1.2.2)" +WHY_TAG_80 = "a first subsequent tag octet with bits 7 to 1 zero (8.1.2.4.2 c)" +WHY_INDEFINITE = "the indefinite length (10.1)" +WHY_LEN_FF = "the initial length octet 0xFF (8.1.3.5 c)" +WHY_LEN_ZERO = "a long-form length with a leading zero octet (10.1)" +WHY_LEN_SHORT = "a long-form length below 128 (10.1)" +WHY_UNIVERSAL_0 = "universal tag 0 (X.680 Table 1, 8.1.5)" +WHY_CONS_PRIM = "universal 8, 11, 16, 17 or 29 in the primitive form" +WHY_PRIM_CONS = "a primitive universal type in the constructed form" +WHY_BOOL_SIZE = "a BOOLEAN without exactly one contents octet (8.2.1)" +WHY_BOOL_VALUE = "a BOOLEAN contents octet other than 00 and FF (11.1)" +WHY_INT_EMPTY = "an INTEGER with no contents octets (8.3.1)" +WHY_INT_PAD = "an INTEGER with a redundant leading octet (8.3.2)" +WHY_NULL = "a NULL with contents octets (8.8.2)" +WHY_ENUM_EMPTY = "an ENUMERATED with no contents octets (8.4, 8.3.1)" +WHY_ENUM_PAD = "an ENUMERATED with a redundant leading octet (8.4, 8.3.2)" +WHY_AFTER = "octets after the element" +# Reasons only an oracle's rules give. +WHY_HIGH_FORM = "a tag number of 31 or more" +WHY_TAG_LIMIT = "a tag number past the oracle's limit" +WHY_LEN_LIMIT = "a length past the oracle's limit" +WHY_NO_EOC = "no end-of-contents octets before the end" + + +@dataclass(frozen=True) +class Rules: + """The rules a reader reads by. The defaults are DER's, as this + slice reads it.""" + high_form: bool = True # reads tag numbers of 31 and more + lenient_tags: bool = False # reads any high form, even below 31 or from 0x80 + max_tag: int = None # the largest tag number read + ber_lengths: bool = False # OpenSSL's BER lengths, with the indefinite form + max_length: int = None # the largest length read + checked_forms: frozenset = None # universal numbers whose form is checked; None: all + universal_0: bool = False # reads universal tag 0 + ber_boolean: bool = False # reads any single octet as a BOOLEAN (8.2.2) + null_checked: bool = True # refuses a NULL with contents + enum_checked: bool = True # checks the contents of an ENUMERATED + + +DER = Rules() + + +class Refused(Exception): + def __init__(self, why): + super().__init__(why) + self.why = why + + +def settle(der_why, why, parted): + """Applies one rule: `der_why` is DER's reason to refuse, or None, + and `why` that of the rules in use. Where the two part, the reason + goes in `parted`. Raises Refused when the rules in use refuse.""" + if (der_why is None) != (why is None): + parted.add(why if why is not None else der_why) + if why is not None: + raise Refused(why) + + +def read_identifier(data, pos, end, inner, rules, parted): + """8.1.2: the class, whether the element is constructed, the tag + number, and the position after the identifier octets, which must end + by `end`.""" + ends = WHY_PAST_PARENT if inner else WHY_IDENT_ENDS + if pos >= end: + raise Refused(ends) + b = data[pos] + pos += 1 + # Bits 8 and 7, the class (8.1.2.2 a, Table 1); bit 6 one for a + # constructed encoding (8.1.2.5). + cls, constructed = b >> 6, bool(b & 0x20) + if b & 0x1f != 0x1f: + # 8.1.2.2 c: bits 5 to 1 give a number from 0 to 30. + return cls, constructed, b & 0x1f, pos + # 8.1.2.4: bits 5 to 1 all ones, then subsequent octets, each with + # bit 8 one but the last (8.1.2.4.2 a), whose bits 7 to 1 are the + # number, most significant first (8.1.2.4.2 b). + digits = [] + while True: + if pos >= end: + raise Refused(ends) + o = data[pos] + pos += 1 + digits.append(o & 0x7f) + if not o & 0x80: + break + number = 0 + for d in digits: + number = number * 128 + d + if digits[0] == 0: + der_why = WHY_TAG_80 + elif number < 31: + der_why = WHY_HIGH_LOW # 8.1.2.2: 0 to 30 take a single octet + else: + der_why = None + if not rules.high_form: + why = WHY_HIGH_FORM + else: + why = None if rules.lenient_tags else der_why + if why is None and rules.max_tag is not None and number > rules.max_tag: + why = WHY_TAG_LIMIT + settle(der_why, why, parted) + return cls, constructed, number, pos + + +def read_length(data, pos, end, inner, constructed, rules, parted): + """8.1.3 and 10.1: the number of contents octets, or None for the + indefinite form where the rules read it, and the position after the + length octets, which must end by `end`.""" + ends = WHY_PAST_PARENT if inner else WHY_LENGTH_ENDS + if pos >= end: + raise Refused(ends) + b = data[pos] + pos += 1 + if b < 0x80: + return b, pos # 8.1.3.4, the short form + if b == 0x80: + # 8.1.3.6, the indefinite form, which 10.1 rules out. OpenSSL + # reads it for a constructed element. + settle(WHY_INDEFINITE, None if rules.ber_lengths and constructed else WHY_INDEFINITE, + parted) + return None, pos + k = b & 0x7f # 8.1.3.5 b: the number of subsequent octets + if not rules.ber_lengths: + if b == 0xff: + raise Refused(WHY_LEN_FF) # 8.1.3.5 c + if end - pos < k: + raise Refused(ends) + octets = data[pos:pos + k] + n = int.from_bytes(octets, "big") + # 10.1: "encoded in the minimum number of octets". + if octets[0] == 0: + raise Refused(WHY_LEN_ZERO) + if n < 128: + raise Refused(WHY_LEN_SHORT) + if rules.max_length is not None and n > rules.max_length: + settle(None, WHY_LEN_LIMIT, parted) + return n, pos + k + # OpenSSL's asn1_get_length: one more octet must follow the + # subsequent octets; leading zero octets are skipped; at most 8 + # significant octets, and a value up to 2^63 - 1. + if end - pos < k + 1: + raise Refused(ends) + octets = data[pos:pos + k] + significant = octets.lstrip(b"\0") + n = int.from_bytes(significant, "big") + if len(significant) > 8 or n > 2 ** 63 - 1: + raise Refused(WHY_LEN_LIMIT) + der_why = (WHY_LEN_FF if b == 0xff else WHY_LEN_ZERO if octets[0] == 0 + else WHY_LEN_SHORT if n < 128 else None) + settle(der_why, None, parted) + return n, pos + k + + +def check_tag(cls, constructed, number, rules, parted): + """Universal tag 0 and the form of each universal type: X.680 Table + 1, and the clause of X.690 for each type.""" + if cls != 0: + return + if number == 0: + der_why = WHY_UNIVERSAL_0 + elif constructed and not (number in CONSTRUCTED_UNIVERSAL or reserved_universal(number)): + der_why = WHY_PRIM_CONS + elif not constructed and number in CONSTRUCTED_UNIVERSAL: + der_why = WHY_CONS_PRIM + else: + der_why = None + if number == 0: + why = None if rules.universal_0 else der_why + elif rules.checked_forms is None or number in rules.checked_forms: + why = der_why + else: + why = None + settle(der_why, why, parted) + + +def check_contents(cls, number, c, rules, parted): + """The contents of a universal BOOLEAN (8.2.1, 11.1), INTEGER (8.3.1, + 8.3.2), ENUMERATED (8.4: encoded as its integer) and NULL (8.8.2).""" + if cls != 0 or number not in (1, 2, 5, 10): + return + if number == 1: + der_why = (WHY_BOOL_SIZE if len(c) != 1 else + WHY_BOOL_VALUE if c[0] not in (0x00, 0xff) else None) + why = None if rules.ber_boolean and der_why == WHY_BOOL_VALUE else der_why + elif number == 2: + der_why = why = read_int(c)[1] + elif number == 10: + der_why = {None: None, WHY_INT_EMPTY: WHY_ENUM_EMPTY, + WHY_INT_PAD: WHY_ENUM_PAD}[read_int(c)[1]] + why = der_why if rules.enum_checked else None + else: + der_why = WHY_NULL if c else None + why = der_why if rules.null_checked else None + settle(der_why, why, parted) + + +def read(data, rules=DER): + """The element at the start of `data`: ((tree, the octets after it), + None) or (None, why); and the reasons on which `rules` part from DER + on the way. A constructed element's contents are read element by + element to the end of its length (8.1.1.1, 8.9.2), or for the + indefinite form to the end-of-contents octets 00 00 (8.1.5). The + reader keeps its own stack, so deep nesting takes no recursion.""" + parted = set() + # The open constructed elements: class, number, the elements read so + # far, where the contents end (None for the indefinite form), and + # where the element around it ends. + stack = [] + pos = 0 + try: + while True: + if stack: + end = stack[-1][3] if stack[-1][3] is not None else stack[-1][4] + else: + end = len(data) + inner = bool(stack) + cls, constructed, number, pos = read_identifier(data, pos, end, inner, rules, parted) + length, pos = read_length(data, pos, end, inner, constructed, rules, parted) + if length is not None and length > end - pos: + raise Refused(WHY_PAST_PARENT if inner else WHY_CONTENTS_END) + check_tag(cls, constructed, number, rules, parted) + node = None + if constructed: + stack.append((cls, number, [], None if length is None else pos + length, end)) + else: + c = data[pos:pos + length] + check_contents(cls, number, c, rules, parted) + pos += length + node = ("p", cls, number, c) + # Put the element in the one around it, and close each one + # that ends here. + while True: + if node is not None: + if not stack: + return ((node, data[pos:]), None), parted + stack[-1][2].append(node) + node = None + f_cls, f_number, children, stop, outer = stack[-1] + if stop is None: + if outer - pos >= 2 and data[pos] == 0 and data[pos + 1] == 0: + pos += 2 + elif pos >= outer: + raise Refused(WHY_NO_EOC) + else: + break + elif pos != stop: + break + stack.pop() + node = ("c", f_cls, f_number, children) + except Refused as e: + return (None, e.why), parted + + +def read_all(data, rules=DER): + """The element that is the whole input.""" + (r, why), parted = read(data, rules) + if r is not None and r[1]: + return (None, WHY_AFTER), parted + return (r, why), parted + + +def identifier_octets(cls, constructed, number): + """8.1.2: one octet for a number up to 30; else the leading octet + with bits 5 to 1 all ones, then the number in base 128, most + significant first, bit 8 one in every octet but the last. X.690 + has four classes; a class number of 4 or more is written as + `Spec.der` writes it, the class times 64 taken as an octet, which is + the class modulo 4. No such tree is valid.""" + lead = (cls % 4) << 6 | (0x20 if constructed else 0) + if number <= 30: + return bytes([lead | number]) + digits = [] + while number: + digits.append(number % 128) + number //= 128 + digits.reverse() + return bytes([lead | 0x1f] + [d | 0x80 for d in digits[:-1]] + [digits[-1]]) + + +def length_octets(n): + """8.1.3 and 10.1: the short form up to 127, else the long form in + the fewest octets.""" + if n <= 127: + return bytes([n]) + octets = n.to_bytes((n.bit_length() + 7) // 8, "big") + if len(octets) > 126: + raise ValueError("a length past the long form") + return bytes([0x80 | len(octets)]) + octets + + +def encode(tree): + """8.1.1: the identifier, length and contents octets; the contents of + a constructed element are the encodings of its elements. Two passes + over a stack of its own: the size of each element's contents, then + the octets.""" + size = {} + stack = [(tree, False)] + while stack: + t, done = stack.pop() + if t[0] == "p": + size[id(t)] = len(t[3]) + elif done: + size[id(t)] = sum(len(identifier_octets(c[1], c[0] == "c", c[2])) + + len(length_octets(size[id(c)])) + size[id(c)] for c in t[3]) + else: + stack.append((t, True)) + stack.extend((c, False) for c in t[3]) + out = bytearray() + stack = [tree] + while stack: + t = stack.pop() + out += identifier_octets(t[1], t[0] == "c", t[2]) + length_octets(size[id(t)]) + if t[0] == "p": + out += t[3] + else: + stack.extend(reversed(t[3])) + return bytes(out) + + +def valid(tree): + """Whether this slice accepts a tree (`Spec.Valid`): every element + of a class from 0 to 3, its universal tag and contents as `read` + checks them, and every length within the long form, which `encode` + checks.""" + for t in nodes(tree): + if t[1] > 3: + return False + if t[1] == 0: + try: + check_tag(0, t[0] == "c", t[2], DER, set()) + if t[0] == "p": + check_contents(0, t[2], t[3], DER, set()) + except Refused: + return False + try: + encode(tree) + except ValueError: + return False + return True + + +def read_int(c): + """8.3: the integer contents octets encode, (value, None), or (None, + why). One or more octets (8.3.1), not starting with nine bits all + ones or all zero (8.3.2); the value as the note to 8.3.3 sums it: + every bit but bit 8 of the first octet, less that one's value when it + is set.""" + if not c: + return None, WHY_INT_EMPTY + if len(c) > 1 and ((c[0] == 0xff and c[1] & 0x80) or (c[0] == 0x00 and not c[1] & 0x80)): + return None, WHY_INT_PAD + value = int.from_bytes(c, "big") + if c[0] & 0x80: + value -= 2 ** (8 * len(c)) + return value, None + + +def write_int(z): + """The contents octets of z: the two's complement number of 8.3.3, in + the fewest octets, which 8.3.2 requires.""" + n = max(1, abs(z).bit_length() // 8) + while not -2 ** (8 * n - 1) <= z < 2 ** (8 * n - 1): + n += 1 + return (z % 2 ** (8 * n)).to_bytes(n, "big") + + +def hx(data): + return bytes(data).hex() or "-" + + +def show(tree): + """A tree as `der serve` prints it, built with a stack of its own.""" + out, stack = [], [tree] + while stack: + t = stack.pop() + if isinstance(t, str): + out.append(t) + elif t[0] == "p": + out.append(f"p{t[1]}.{t[2]}.{hx(t[3])}") + else: + out.append(f"c{t[1]}.{t[2]}(") + stack.append(")") + for i, c in enumerate(reversed(t[3])): + if i: + stack.append(",") + stack.append(c) + return "".join(out) + + +# --------------------------------------------------------------------- +# Requests and answers, in the form of `der serve`. + +def request(op, args): + if op in ("encode", "valid"): + return f"{op} {show(args[0])}" + if op == "writeint": + return f"writeint {args[0]}" + return f"{op} {hx(args[0])}" + + +def model(op, args, rules=DER): + """The answer to a request as `der serve` writes it, why the model + refuses or None, and the rules on which `rules` part from DER on the + way.""" + if op in ("decode", "all"): + (r, why), parted = (read if op == "decode" else read_all)(args[0], rules) + if r is None: + return "FAIL", why, parted + if op == "decode": + return f"OK {show(r[0])} {hx(r[1])}", None, parted + return f"OK {show(r[0])}", None, parted + if op == "encode": + return f"OK {hx(encode(args[0]))}", None, set() + if op == "valid": + return ("OK" if valid(args[0]) else "FAIL"), None, set() + if op == "readint": + z, why = read_int(args[0]) + return ("FAIL" if z is None else f"OK {z}"), why, set() + return f"OK {hx(write_int(args[0]))}", None, set() + + +def octets_field(f): + """A field of octets: `-`, or lower-case hex digits in pairs.""" + if f == "-": + return b"" + if f == "" or len(f) % 2 or any(c not in "0123456789abcdef" for c in f): + raise ValueError + return bytes.fromhex(f) + + +def decimal_field(f): + if f == "" or any(c not in "0123456789" for c in f): + raise ValueError + return int(f) + + +def integer_field(f): + """A decimal integer, with `-` in front when it is negative.""" + return -decimal_field(f[1:]) if f.startswith("-") else decimal_field(f) + + +def tree_field(f): + """The tree a field writes, in the syntax Main.lean's header gives, + or ValueError. It keeps a stack of its own.""" + pos = 0 + + def number(): + nonlocal pos + start = pos + while pos < len(f) and f[pos] in "0123456789": + pos += 1 + if pos == start: + raise ValueError + return int(f[start:pos]) + + def expect(ch): + nonlocal pos + if pos >= len(f) or f[pos] != ch: + raise ValueError + pos += 1 + + stack = [] + while True: + if pos >= len(f) or f[pos] not in "pc": + raise ValueError + kind = f[pos] + pos += 1 + cls = number() + expect(".") + num = number() + if kind == "p": + expect(".") + start = pos + while pos < len(f) and f[pos] not in ",)": + pos += 1 + node = ("p", cls, num, octets_field(f[start:pos])) + else: + expect("(") + if pos < len(f) and f[pos] == ")": + pos += 1 + node = ("c", cls, num, []) + else: + stack.append(("c", cls, num, [])) + continue + while True: + if not stack: + if pos != len(f): + raise ValueError + return node + stack[-1][3].append(node) + if pos < len(f) and f[pos] == ",": + pos += 1 + break + expect(")") + node = stack.pop() + + +def model_line(raw): + """The answer to a request line, as sent with its line ending, as + Main.lean's header describes the protocol: the line ends in LF or + CR LF; `error` for a malformed request, else the model's answer.""" + line = raw + if line.endswith("\n"): + line = line[:-1] + if line.endswith("\r"): + line = line[:-1] + parts = line.split(" ") + if len(parts) != 2: + return "error" + op, f = parts + read_field = {"decode": octets_field, "all": octets_field, "readint": octets_field, + "writeint": integer_field, "encode": tree_field, + "valid": tree_field}.get(op) + if read_field is None: + return "error" + try: + arg = read_field(f) + except ValueError: + return "error" + return model(op, (arg,))[0] + + +# --------------------------------------------------------------------- +# Running a program over request lines. + +def run_lines(cmd, lines, timeout=TIMEOUT, cwd=None, env=None): + """Send `lines` to `cmd` and return its answer lines and a note on + how the run failed, or None.""" + raw = "".join(lines).encode("utf-8") + proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, cwd=cwd, env=env) + out, err = bytearray(), bytearray() + + def feed(): + try: + proc.stdin.write(raw) + proc.stdin.close() + except (BrokenPipeError, ValueError): + pass + + def drain(stream, buf): + while chunk := stream.read1(1 << 16): + buf += chunk + + threads = [threading.Thread(target=feed), + threading.Thread(target=drain, args=(proc.stdout, out)), + threading.Thread(target=drain, args=(proc.stderr, err))] + for t in threads: + t.start() + problem = None + try: + proc.wait(timeout=timeout) + except subprocess.TimeoutExpired: + proc.kill() + problem = f"no end after {timeout} seconds" + for t in threads: + t.join() + proc.wait() + if proc.returncode != 0 and not problem: + tail = " / ".join(err.decode("utf-8", "backslashreplace").splitlines()[-3:]) + problem = f"exit status {proc.returncode}: {tail}" + text = bytes(out).decode("utf-8", "backslashreplace") + if text and not text.endswith("\n"): + problem = problem or "the last answer has no LF" + answers = text.split("\n") + if answers and answers[-1] == "": + answers.pop() + return answers, problem + + +def run_parallel(cmd, lines, jobs): + """`run_lines` over `jobs` processes, each given a run of the lines + in order; the answers in order, and the problems.""" + size = -(-len(lines) // jobs) if lines else 0 + chunks = [lines[k:k + size] for k in range(0, len(lines), size)] if size else [[]] + results = [None] * len(chunks) + + def one(k): + results[k] = run_lines(cmd, chunks[k]) + + threads = [threading.Thread(target=one, args=(k,)) for k in range(len(chunks))] + for t in threads: + t.start() + for t in threads: + t.join() + answers, problems = [], [] + for chunk, (got, problem) in zip(chunks, results): + if problem: + problems.append(problem) + if len(got) != len(chunk): + problems.append(f"{len(got)} answers for {len(chunk)} requests") + got = (got + [""] * len(chunk))[:len(chunk)] + answers += got + return answers, problems + + +# --------------------------------------------------------------------- +# The oracles. Each setup returns a list of objects with `name`, +# `version` and `answers(lines)`, or raises OracleUnavailable. + +class OracleUnavailable(Exception): + pass + + +class ProcessOracle: + def __init__(self, name, version, cmd): + self.name, self.version, self.cmd = name, version, cmd + + def answers(self, lines): + got, problem = run_lines(self.cmd, lines) + if problem or len(got) != len(lines): + raise RuntimeError(f"{self.name} oracle: " + f"{problem or f'{len(got)} answers for {len(lines)} requests'}") + return got + + +def first_line(cmd, cwd=None): + try: + out = subprocess.run(cmd, capture_output=True, text=True, timeout=300, cwd=cwd) + return (out.stdout or out.stderr).strip().splitlines()[0] + except (OSError, subprocess.SubprocessError, IndexError): + return "unknown" + + +def setup_go(): + """Both Go oracles: one program, two modes.""" + GO_EXE.parent.mkdir(parents=True, exist_ok=True) + env = dict(os.environ, GOFLAGS="-mod=readonly", CGO_ENABLED="0", GOTOOLCHAIN="local") + try: + subprocess.run(["go", "build", "-o", str(GO_EXE), "."], cwd=GO_SRC, check=True, env=env, + capture_output=True, timeout=BUILD_TIMEOUT) + except (OSError, subprocess.SubprocessError) as e: + raise OracleUnavailable(f"`go build` did not build it: {e}") + mod = re.search(r"^require golang.org/x/crypto (\S+)", + (GO_SRC / "go.mod").read_text(encoding="utf-8"), re.M) + sums = re.search(r"^golang.org/x/crypto \S+ (h1:\S+)", + (GO_SRC / "go.sum").read_text(encoding="utf-8"), re.M) + go = first_line(["go", "version"]) + return [ProcessOracle("Go cryptobyte", f"golang.org/x/crypto/cryptobyte " + f"{mod.group(1) if mod else '?'} ({sums.group(1) if sums else '?'}), " + f"built with {go}", [str(GO_EXE), "cryptobyte"]), + ProcessOracle("Go encoding/asn1", f"encoding/asn1 of the standard library of {go}", + [str(GO_EXE), "asn1"])] + + +def setup_rust(): + RUST_TARGET.mkdir(parents=True, exist_ok=True) + env = dict(os.environ, CARGO_TARGET_DIR=str(RUST_TARGET)) + try: + subprocess.run(["cargo", "build", "--release", "--locked", "--quiet"], cwd=RUST_SRC, + check=True, env=env, capture_output=True, timeout=BUILD_TIMEOUT) + except (OSError, subprocess.SubprocessError) as e: + raise OracleUnavailable(f"`cargo build --locked` did not build it: {e}") + lock = (RUST_SRC / "Cargo.lock").read_text(encoding="utf-8") + m = re.search(r'name = "asn1"\nversion = "(\S+)"\nsource = "[^"]*"\nchecksum = "([0-9a-f]+)"', + lock) + if not m: + raise OracleUnavailable("tests/oracles/rust/Cargo.lock pins no asn1 crate") + return [ProcessOracle("rust-asn1", f"the asn1 crate {m.group(1)} (checksum {m.group(2)}), " + f"built with {first_line(['rustc', '--version'])}", [str(RUST_EXE)])] + + +def setup_openssl(): + OPENSSL_EXE.parent.mkdir(parents=True, exist_ok=True) + try: + subprocess.run(["cc", "-O2", "-o", str(OPENSSL_EXE), str(OPENSSL_SRC / "wrapper.c"), + "-lcrypto"], check=True, capture_output=True, timeout=BUILD_TIMEOUT) + except (OSError, subprocess.SubprocessError) as e: + raise OracleUnavailable(f"`cc` did not build the wrapper against libcrypto (is " + f"libssl-dev installed?): {e}") + got, problem = run_lines([str(OPENSSL_EXE)], ["version\n"], timeout=60) + version = got[0][3:] if got and got[0].startswith("OK ") and not problem else "unknown" + if version.split(" ")[:2] != ["OpenSSL", OPENSSL_PINNED]: + raise OracleUnavailable(f"{version} is installed, not OpenSSL {OPENSSL_PINNED}") + package = first_line(["dpkg-query", "-W", "-f", "${Package} ${Version}\n", "libssl3t64"]) + return [ProcessOracle("OpenSSL", f"libcrypto of {version}" + + (f" (Debian package {package})" if package != "unknown" else "") + + f", through `tests/oracles/openssl/wrapper.c` compiled with " + f"{first_line(['cc', '--version'])}", [str(OPENSSL_EXE)])] + + +ORACLE_SETUPS = [(("Go cryptobyte", "Go encoding/asn1"), setup_go), (("rust-asn1",), setup_rust), + (("OpenSSL",), setup_openssl)] + +# The rules each oracle reads by (see the top of this file). +ORACLE_RULES = { + "Go cryptobyte": Rules(high_form=False, max_length=2 ** 32 - 1, + checked_forms=frozenset({1, 2}), universal_0=True, null_checked=False, + enum_checked=False), + "Go encoding/asn1": Rules(max_tag=2 ** 31 - 1, max_length=2 ** 31 - 1, + checked_forms=frozenset({1, 2, 5}), universal_0=True, enum_checked=False), + "rust-asn1": Rules(max_tag=2 ** 32 - 1, max_length=2 ** 32 - 1, + checked_forms=frozenset({1, 2, 5}), universal_0=True, enum_checked=False), + "OpenSSL": Rules(lenient_tags=True, max_tag=2 ** 31 - 1, ber_lengths=True, + checked_forms=frozenset({1, 2, 5}), universal_0=True, ber_boolean=True, + enum_checked=False), +} + +# The oracle programs check only BOOLEAN, INTEGER and NULL contents. +ENUM_NOT_CHECKED = "the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED " + +# What each oracle does on each rule where it parts from DER. +PARTS = { + "Go cryptobyte": { + WHY_HIGH_FORM: "ReadAnyASN1Element refuses every tag number of 31 or more (the high form)", + WHY_LEN_LIMIT: "readASN1 refuses a length of more than 4 octets", + WHY_UNIVERSAL_0: "ReadAnyASN1Element reads universal tag 0", + WHY_CONS_PRIM: "ReadAnyASN1Element reads universal 8, 11, 16, 17 or 29 in the primitive " + "form", + WHY_PRIM_CONS: "ReadAnyASN1Element reads a universal type other than BOOLEAN and INTEGER " + "in the constructed form", + WHY_NULL: "cryptobyte has no reader for NULL, so a NULL with contents is read", + WHY_ENUM_EMPTY: ENUM_NOT_CHECKED + "with no contents octets", + WHY_ENUM_PAD: ENUM_NOT_CHECKED + "with a redundant leading octet", + }, + "Go encoding/asn1": { + WHY_TAG_LIMIT: "parseBase128Int refuses a tag number above 2^31 - 1", + WHY_LEN_LIMIT: "parseTagAndLength refuses a length above 2^31 - 1", + WHY_UNIVERSAL_0: "Unmarshal into a RawValue reads universal tag 0", + WHY_CONS_PRIM: "Unmarshal into a RawValue reads universal 8, 11, 16, 17 or 29 in the " + "primitive form", + WHY_PRIM_CONS: "Unmarshal into a RawValue reads a universal type other than BOOLEAN, " + "INTEGER and NULL in the constructed form", + WHY_ENUM_EMPTY: ENUM_NOT_CHECKED + "with no contents octets", + WHY_ENUM_PAD: ENUM_NOT_CHECKED + "with a redundant leading octet", + }, + "rust-asn1": { + WHY_TAG_LIMIT: "Tag::from_bytes refuses a tag number above 2^32 - 1", + WHY_LEN_LIMIT: "read_length refuses a length of more than 4 octets", + WHY_UNIVERSAL_0: "strip_tlv reads universal tag 0", + WHY_CONS_PRIM: "strip_tlv reads universal 8, 11, 16, 17 or 29 in the primitive form", + WHY_PRIM_CONS: "strip_tlv reads a universal type other than BOOLEAN, INTEGER and NULL in " + "the constructed form", + WHY_ENUM_EMPTY: ENUM_NOT_CHECKED + "with no contents octets", + WHY_ENUM_PAD: ENUM_NOT_CHECKED + "with a redundant leading octet", + }, + "OpenSSL": { + WHY_HIGH_LOW: "ASN1_get_object reads a tag number below 31 in the high form (BER)", + WHY_TAG_80: "ASN1_get_object reads a first subsequent tag octet 0x80 (BER)", + WHY_TAG_LIMIT: "ASN1_get_object refuses a tag number above 2^31 - 1", + WHY_INDEFINITE: "ASN1_get_object reads the indefinite length of a constructed element " + "(BER), to end-of-contents octets 00 00", + WHY_LEN_FF: "asn1_get_length reads the initial length octet 0xFF, skipping leading " + "zero octets (BER)", + WHY_LEN_ZERO: "asn1_get_length reads a long-form length with leading zero octets (BER)", + WHY_LEN_SHORT: "asn1_get_length reads the long form for a length below 128 (BER)", + WHY_UNIVERSAL_0: "ASN1_get_object reads universal tag 0 where no end-of-contents octets " + "are due", + WHY_CONS_PRIM: "ASN1_get_object reads universal 8, 11, 16, 17 or 29 in the primitive form", + WHY_PRIM_CONS: "ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and " + "NULL in the constructed form (BER, 8.23.6)", + WHY_BOOL_VALUE: "d2i_ASN1_TYPE reads any single contents octet as a BOOLEAN (BER, 8.2.2)", + WHY_ENUM_EMPTY: ENUM_NOT_CHECKED + "with no contents octets", + WHY_ENUM_PAD: ENUM_NOT_CHECKED + "with a redundant leading octet", + }, +} + + +def difference_reason(oracle, op, theirs, theirs_by_model, parted): + """The named reason the oracle's answer differs from the tool's, + confirmed by the model, or None. `theirs_by_model` is the answer of + `read` with the rules the oracle reads by, and `parted` the rules on + which those part from DER on the way. It must be the oracle's answer, + and the reason names each rule in `parted`.""" + if op not in ("decode", "all") or theirs_by_model != theirs or not parted: + return None + return "; ".join(PARTS[oracle].get(p, f"{p} (no description)") for p in sorted(parted)) + + +# --------------------------------------------------------------------- +# The fixed sets. + +def x690_examples(): + """X.690's own examples: (op, args, the answer the standard gives).""" + l38, l201, jones = bytes(range(38)), bytes(range(201)), b"Jones" + return [ + # 8.2.2 and 11.1: TRUE, 01 01 FF. + ("decode", (bytes.fromhex("0101ff"),), "OK p0.1.ff -"), + ("all", (bytes.fromhex("0101ff"),), "OK p0.1.ff"), + ("encode", (("p", 0, 1, b"\xff"),), "OK 0101ff"), + # 8.1.3.4: L = 38 in the short form, 00100110. + ("encode", (("p", 0, 4, l38),), "OK 0426" + l38.hex()), + ("all", (b"\x04\x26" + l38,), f"OK p0.4.{l38.hex()}"), + # 8.1.3.5: L = 201 in the long form, 10000001 11001001. + ("encode", (("p", 0, 4, l201),), "OK 0481c9" + l201.hex()), + ("all", (b"\x04\x81\xc9" + l201,), f"OK p0.4.{l201.hex()}"), + # 8.8.2: NULL, 05 00. + ("all", (bytes.fromhex("0500"),), "OK p0.5.-"), + ("encode", (("p", 0, 5, b""),), "OK 0500"), + # 8.23.5: "Jones" as a VisibleString, in the primitive form, which + # DER uses. + ("decode", (bytes.fromhex("1a054a6f6e6573"),), "OK p0.26.4a6f6e6573 -"), + ("encode", (("p", 0, 26, jones),), "OK 1a054a6f6e6573"), + # 8.23.5: the constructed forms, definite and indefinite, which DER + # rules out (10.2, 10.1). + ("decode", (bytes.fromhex("3a0904034a6f6e04026573"),), "FAIL"), + ("decode", (bytes.fromhex("3a8004034a6f6e040265730000"),), "FAIL"), + # 8.1.5: the end-of-contents octets go with the indefinite form + # only, so they are no element in DER. + ("decode", (bytes.fromhex("0000"),), "FAIL"), + ] + + +def short_sets(): + """Every input of 0, 1 and 2 octets.""" + label = "0 to 2 octets" + for op in ("decode", "all"): + yield (label, op, (b"",)) + for a in range(256): + yield (label, op, (bytes([a]),)) + for b in range(256): + yield (label, op, (bytes([a, b]),)) + + +def identifier_sets(): + """Every identifier first octet with short contents; after a first + octet with bits 5 to 1 all ones, a few runs of subsequent octets.""" + bodies = [b"", b"\x00", b"\xff", b"\x01", b"\x05\x00", b"\x02\x01\x00", + b"\x01\x01\xff\x05\x00", b"\x00\x00"] + for b in range(256): + tails = [b""] if b & 0x1f != 0x1f else [b"\x1e", b"\x1f", b"\x7f", b"\x80\x1f", + b"\x81\x00", b"\xff\x7f", b"\x81"] + for t in tails: + for body in bodies: + yield ("identifier first octets", "decode", + (bytes([b]) + t + length_octets(len(body)) + body,)) + + +def length_sets(): + """Every initial length octet from 0x80 to 0xFF, after the + identifiers of an OCTET STRING and a SEQUENCE, with several runs of + subsequent octets: all zero, ending in 01, starting with 01, all + ones, and one octet short. Contents follow when the length is at + most 300; else three octets.""" + for b in range(0x80, 0x100): + k = b & 0x7f + if k == 0: + runs = [b""] + else: + runs = [bytes(k), bytes(k - 1) + b"\x01", b"\x01" + bytes(k - 1), b"\xff" * k, + bytes(k - 1)] + for run in runs: + n = int.from_bytes(run, "big") if len(run) == k else 0 + for ident in (b"\x04", b"\x30"): + if b == 0x80: + for body in (b"", b"\x00\x00", b"\x05\x00\x00\x00", b"\x05\x00"): + yield ("initial length octets", "decode", (ident + bytes([b]) + body,)) + continue + body = (b"\x05\x00" * 150)[:n] if n <= 300 else b"\x05\x00\x01" + yield ("initial length octets", "decode", (ident + bytes([b]) + run + body,)) + yield ("initial length octets", "decode", + (ident + bytes([b]) + run + body + b"\x01",)) + + +def size_sets(): + """Contents of 0 to 100,000 octets, at the lengths where the length + octets change: whole, with an octet after, cut short, with the + length one more and one less, not in the fewest octets, and inside a + SEQUENCE.""" + rng = random.Random(3) + label = "lengths 0 to 100,000" + for n in (0, 1, 2, 126, 127, 128, 129, 254, 255, 256, 257, 65535, 65536, 65537, 100000): + c = rng.randbytes(n) + e = b"\x04" + length_octets(n) + c + yield (label, "all", (e,)) + yield (label, "decode", (e + b"\x01",)) + if n: + yield (label + ", cut short", "all", (e[:-1],)) + yield (label + ", length one less", "decode", (b"\x04" + length_octets(n - 1) + c,)) + yield (label + ", length one more", "decode", (b"\x04" + length_octets(n + 1) + c,)) + minimal = n.to_bytes(max(1, (n.bit_length() + 7) // 8), "big") + yield (label + ", not in the fewest octets", "all", + (b"\x04" + bytes([0x81 + len(minimal), 0]) + minimal + c,)) + if n < 128: + yield (label + ", not in the fewest octets", "all", (b"\x04\x81" + bytes([n]) + c,)) + yield (label, "encode", (("p", 0, 4, c),)) + yield (label, "all", (b"\x30" + length_octets(len(e)) + e,)) + yield (label, "encode", (("c", 0, 16, [("p", 0, 4, c), ("p", 0, 5, b"")]),)) + + +def high_tag_sets(): + """Tag numbers around each of 31, 127, 128, 16383, 16384, 2^21, + 2^28, 2^31, 2^32, 2^63 and 2^64, and one of 1,000 subsequent octets, + in each class and form: read, encoded, cut in the identifier octets, + and with a first subsequent octet 0x80 put in.""" + rng = random.Random(4) + tags = set() + for t in (31, 127, 128, 16383, 16384, 2 ** 21, 2 ** 28, 2 ** 31, 2 ** 32, 2 ** 63, 2 ** 64): + tags.update((t - 1, t, t + 1)) + tags.add(rng.randrange(128 ** 999, 128 ** 1000)) + label = "high tag numbers" + for number in sorted(tags): + for cls in range(4): + for constructed in (False, True): + tree = (("c", cls, number, [("p", 0, 5, b"")]) if constructed + else ("p", cls, number, b"\x01\x02")) + enc = encode(tree) + ident = identifier_octets(cls, constructed, number) + yield (label, "all", (enc,)) + yield (label, "decode", (enc + b"\x00",)) + yield (label, "encode", (tree,)) + if number >= 31: + yield (label + ", cut short", "decode", (ident[:-1],)) + yield (label + ", first subsequent octet 0x80", "all", + (ident[:1] + b"\x80" + enc[1:],)) + + +def universal_sets(): + """Every universal tag from 0 to 40 in both forms, with a few + contents, read and encoded; and the high form for those below 31.""" + label = "universal tags 0 to 40" + contents = [b"", b"\x00", b"\xff", b"\x01", b"\x00\x80", b"\x80", b"\x05\x00"] + elements = [[], [("p", 0, 5, b"")], [("p", 0, 2, b"\x01"), ("p", 0, 1, b"\xff")]] + for number in range(41): + trees = [("p", 0, number, c) for c in contents] + [("c", 0, number, e) for e in elements] + for tree in trees: + yield (label, "decode", (encode(tree),)) + yield (label, "encode", (tree,)) + yield (label, "valid", (tree,)) + if number < 31: + for lead in (0x1f, 0x3f): + yield (label + ", high form", "all", (bytes([lead, number, 0]),)) + + +def contents_sets(): + """BOOLEAN and NULL contents; every INTEGER of 0, 1 and 2 octets and + some longer, as elements and with `readint`.""" + for c in ([b""] + [bytes([x]) for x in range(256)] + + [b"\x00\x00", b"\xff\xff", b"\xff\x00", b"\x00\xff", b"\x01\x01"]): + yield ("BOOLEAN contents", "decode", (b"\x01" + length_octets(len(c)) + c,)) + for c in (b"", b"\x00", b"\x05", b"\x00\x00", bytes(200)): + yield ("NULL contents", "decode", (b"\x05" + length_octets(len(c)) + c,)) + for e in ("2103010100", "2500", "25020500", "2203020100"): + yield ("constructed BOOLEAN, INTEGER and NULL", "decode", (bytes.fromhex(e),)) + ints = [b""] + [bytes([x]) for x in range(256)] + ints += [bytes([x, y]) for x in range(256) for y in range(256)] + for k in (3, 8, 9, 16, 17, 32, 33, 100, 129): + for first in (0x00, 0x01, 0x7f, 0x80, 0xff): + for second in (0x00, 0x7f, 0x80, 0xff): + ints.append(bytes([first, second]) + bytes(k - 2)) + ints.append(bytes([first, second]) + b"\xff" * (k - 2)) + for c in ints: + yield ("INTEGER contents", "decode", (b"\x02" + length_octets(len(c)) + c,)) + yield ("INTEGER contents", "readint", (c,)) + yield ("ENUMERATED contents", "decode", (b"\x0a" + length_octets(len(c)) + c,)) + + +def chain(depth, leaf, heads): + """A chain of `depth` constructed elements around `leaf`, whose + classes and numbers go round `heads`, from the inside out.""" + t = leaf + for i in range(depth): + cls, number = heads[i % len(heads)] + t = ("c", cls, number, [t]) + return t + + +def chain_octets(depth, leaf, ident, longer_at=None): + """The encoding of a chain of `depth` constructed elements with + identifier octets `ident` around the octets `leaf`, where the one at + `longer_at` from the inside claims one octet more than it holds.""" + size, heads = len(leaf), [] + for i in range(depth): + h = ident + length_octets(size + (1 if i == longer_at else 0)) + heads.append(h) + size += len(h) + heads.reverse() + return b"".join(heads) + leaf + + +def deep_sets(): + """Chains 10,000 and 100,000 deep.""" + mixed = ((0, 16), (2, 0), (0, 17), (1, 31), (3, 1000), (0, 8)) + for d in (10_000, 100_000): + label = f"nested {d:,} deep" + null = chain(d, ("p", 0, 5, b""), ((0, 16),)) + enc = encode(null) + yield (label, "all", (enc,)) + yield (label, "decode", (enc + b"\x01\x01\xff",)) + yield (label, "all", (enc[:-1],)) + many = chain(d, ("p", 2, 3, b"\x01"), mixed) + yield (label, "all", (encode(many),)) + yield (label, "all", (chain_octets(d, b"\x01\x01\x01", b"\x30"),)) + yield (label, "all", (chain_octets(d, b"\x05\x00", b"\x30", longer_at=d // 2),)) + # Only the 10,000 deep chains are encoded: `encode` takes time + # that grows with the square of the depth. + if d == 10_000: + yield (label, "encode", (null,)) + yield (label, "encode", (many,)) + + +def read_wycheproof(): + """The Wycheproof test vectors, after their SHA-256 is checked: each + test's signature octets and flags.""" + raw = WYCHEPROOF.read_bytes() + digest = hashlib.sha256(raw).hexdigest() + if digest != WYCHEPROOF_SHA256: + raise ValueError(f"{WYCHEPROOF.name} has SHA-256 {digest}, not {WYCHEPROOF_SHA256}") + doc = json.loads(raw) + vectors = [(bytes.fromhex(t["sig"]), tuple(t["flags"])) + for g in doc["testGroups"] for t in g["tests"]] + if len(vectors) != doc["numberOfTests"]: + raise ValueError(f"{len(vectors)} tests read, not {doc['numberOfTests']}") + return vectors + + +# Wycheproof flags that mark an encoding that is not DER, or broken. +ENCODING_FLAGS = ("BerEncodedSignature", "InvalidEncoding", "MissingZero") + + +# --------------------------------------------------------------------- +# The random part. + +PRIMITIVE_UNIVERSAL = [n for n in range(1, 37) if n not in CONSTRUCTED_UNIVERSAL] + + +def random_number(rng): + """A tag number: mostly below 31, then in the high form of one, + two, three and more subsequent octets.""" + r = rng.random() + if r < 0.55: + return rng.randrange(31) + if r < 0.8: + return rng.randrange(31, 128) + if r < 0.93: + return rng.randrange(128, 2 ** 14) + return rng.randrange(2 ** 14, 2 ** rng.choice((21, 28, 32, 40, 64))) + + +def random_int(rng): + r = rng.random() + if r < 0.3: + return rng.randint(-300, 300) + if r < 0.6: + return rng.choice((-1, 1)) * 2 ** rng.randrange(80) + rng.randint(-2, 2) + return rng.choice((-1, 1)) * rng.getrandbits(rng.choice((8, 16, 64, 128, 300))) + + +def random_contents(rng): + r = rng.random() + n = rng.randint(0, 4) if r < 0.4 else rng.randint(5, 40) if r < 0.85 else rng.randint(41, 300) + return rng.randbytes(n) + + +def random_tree(rng, depth, budget): + """A random tree this slice accepts, with at most `depth` levels + below it; budget[0] is how many more elements it may hold.""" + budget[0] -= 1 + cls = rng.choice((0, 0, 0, 1, 2, 3)) + constructed = depth > 0 and budget[0] > 0 and rng.random() < 0.45 + if cls == 0 and constructed: + number = rng.choice((16, 16, 16, 17, 17, 8, 11, 29, 15, 37, rng.randrange(37, 2 ** 20))) + elif cls == 0: + number = rng.choice((1, 1, 2, 2, 2, 5, 5, 15, 37, rng.randrange(37, 2 ** 20)) + + tuple(PRIMITIVE_UNIVERSAL)) + else: + number = random_number(rng) + if constructed: + k = rng.choice((0, 1, 1, 2, 2, 3, 4, 5, 8)) + return ("c", cls, number, [random_tree(rng, depth - 1, budget) for _ in range(k)]) + if cls == 0 and number == 1: + c = bytes([rng.choice((0x00, 0xff))]) + elif cls == 0 and number in (2, 10): + c = write_int(random_int(rng)) + elif cls == 0 and number == 5: + c = b"" + else: + c = random_contents(rng) + return ("p", cls, number, c) + + +def nodes(tree): + """The elements of a tree in the order they are written.""" + out, stack = [], [tree] + while stack: + t = stack.pop() + out.append(t) + if t[0] == "c": + stack.extend(reversed(t[3])) + return out + + +DEFECTS = ["length one more", "length one less", "length not in the fewest octets", + "indefinite length", "high form for a tag below 31", "first subsequent tag octet 0x80", + "constructed bit flipped", "an element running past the one it is in", + "BOOLEAN, INTEGER, ENUMERATED or NULL contents changed"] + + +def applies(defect, t): + cons = t[0] == "c" + if defect == "length one less": + return len(encode(t)) > len(identifier_octets(t[1], cons, t[2])) + 1 + if defect == "high form for a tag below 31": + return t[2] < 31 + if defect == "an element running past the one it is in": + return cons and bool(t[3]) + if defect == "BOOLEAN, INTEGER, ENUMERATED or NULL contents changed": + return t[0] == "p" and t[1] == 0 and t[2] in (1, 2, 5, 10) + return True + + +def encode_with(tree, target, defect, rng): + """The encoding of the tree, with the element `target` (an object of + the tree) written with `defect`; the elements around it get the + lengths of what they hold.""" + + def enc(t): + cons = t[0] == "c" + body = b"".join(enc(c) for c in t[3]) if cons else t[3] + ident = identifier_octets(t[1], cons, t[2]) + length = length_octets(len(body)) + tail = b"" + if t is target: + n = len(body) + if defect == "length one more": + length = length_octets(n + 1) + elif defect == "length one less": + length = length_octets(n - 1) + elif defect == "length not in the fewest octets": + minimal = n.to_bytes(max(1, (n.bit_length() + 7) // 8), "big") + length = (bytes([0x81, n]) if n < 128 and rng.random() < 0.5 + else bytes([0x81 + len(minimal), 0]) + minimal) + elif defect == "indefinite length": + length, tail = b"\x80", b"\x00\x00" + elif defect == "high form for a tag below 31": + ident = bytes([ident[0] | 0x1f, t[2]]) + elif defect == "first subsequent tag octet 0x80": + ident = (ident[:1] + b"\x80" + ident[1:] if t[2] >= 31 + else bytes([ident[0] | 0x1f, 0x80, t[2]])) + elif defect == "constructed bit flipped": + ident = bytes([ident[0] ^ 0x20]) + ident[1:] + elif defect == "an element running past the one it is in": + length = length_octets(n - rng.randint(1, min(3, n))) + elif t[2] == 1: + body = bytes([rng.randrange(1, 255)]) + elif t[2] in (2, 10): + body = rng.choice((b"\x00", b"\xff", b"")) + (body if rng.random() < 0.8 else b"") + else: + body = bytes([rng.randrange(256)]) + return ident + length + body + tail + + return enc(tree) + + +INTERESTING = (0x00, 0x01, 0x02, 0x04, 0x05, 0x1f, 0x20, 0x30, 0x31, 0x3f, 0x7f, 0x80, 0x81, 0x82, + 0xa0, 0xff) + + +def replace(tree, target, new): + """A copy of the tree with the element `target` (an object of the + tree) replaced by `new`.""" + if tree is target: + return new + if tree[0] == "p": + return tree + return ("c", tree[1], tree[2], [replace(c, target, new) for c in tree[3]]) + + +def spoil(tree, rng): + """A copy of the tree with one element the slice does not accept.""" + target = rng.choice(nodes(tree)) + kind = rng.randrange(5) + if kind == 0: + new = (target[0], rng.randint(4, 7)) + target[2:] + elif kind == 1: + new = (target[0], 0, 0, target[3]) + elif kind == 2: + new = ("p", 0, rng.choice(sorted(CONSTRUCTED_UNIVERSAL)), random_contents(rng)) + elif kind == 3: + new = ("c", 0, rng.choice(PRIMITIVE_UNIVERSAL), target[3] if target[0] == "c" else []) + else: + new = ("p", 0) + rng.choice(((1, b""), (1, b"\x01"), (1, b"\x00\x00"), (2, b""), + (2, b"\x00\x05"), (2, b"\xff\x80"), (10, b""), + (10, b"\x00\x00"), (10, b"\xff\xff"), (5, b"\x00"))) + return replace(tree, target, new) + + +def random_octets(rng): + n = rng.choice((rng.randint(0, 4), rng.randint(5, 16), rng.randint(17, 60))) + return bytes(rng.choice(INTERESTING) if rng.random() < 0.6 else rng.randrange(256) + for _ in range(n)) + + +def random_int_contents(rng): + n = rng.choice((rng.randint(0, 4), rng.randint(5, 24), rng.randint(25, 300))) + c = bytearray(rng.randbytes(n)) + if n and rng.random() < 0.6: + c[0] = rng.choice((0x00, 0xff, 0x7f, 0x80)) + if n > 1 and rng.random() < 0.6: + c[1] = rng.choice((0x00, 0xff, 0x7f, 0x80)) + return bytes(c) + + +def writeint_sets(): + label = "integers around 0 and 2^k" + for z in range(-300, 301): + yield (label, "writeint", (z,)) + for k in range(1001): + for d in (-2, -1, 0, 1, 2): + yield (label, "writeint", (2 ** k + d,)) + yield (label, "writeint", (-2 ** k + d,)) + + +def random_sets(seed, count): + rng = random.Random(seed) + for _ in range(count): + tree = random_tree(rng, rng.randint(0, 6), [rng.randint(1, 40)]) + enc = encode(tree) + yield ("random tree", "all", (enc,)) + yield ("random tree, octets after", rng.choice(("decode", "decode", "all")), + (enc + rng.randbytes(rng.randint(1, 3)),)) + yield ("random tree", "encode", (tree,)) + yield ("random tree", "valid", (tree,)) + bad = spoil(tree, rng) + yield ("random tree, one element not accepted", "valid", (bad,)) + yield ("random tree, one element not accepted", "encode", (bad,)) + ts = nodes(tree) + for _ in range(rng.randint(3, 6)): + defect = rng.choice(DEFECTS) + fits = [t for t in ts if applies(defect, t)] + if not fits: + continue + m = encode_with(tree, rng.choice(fits), defect, rng) + if rng.random() < 0.5: + yield (f"random tree, {defect}", "all", (m,)) + else: + yield (f"random tree, {defect}", "decode", (m + rng.randbytes(rng.randint(0, 2)),)) + flipped = bytearray(enc) + k = rng.randrange(8 * len(enc)) + flipped[k // 8] ^= 0x80 >> (k % 8) + yield ("random tree, a bit flipped", "decode", (bytes(flipped),)) + yield ("random tree, cut short", "decode", (enc[:rng.randrange(len(enc))],)) + for _ in range(count // 2): + yield ("random octets", "decode", (random_octets(rng),)) + for _ in range(count // 4): + yield ("random integer contents", "readint", (random_int_contents(rng),)) + yield ("random integers", "writeint", + (rng.choice((-1, 1)) * rng.getrandbits(rng.choice((8, 64, 512, 4096))),)) + + +def inputs(seed, count, vectors): + """(label, op, args) for every request.""" + yield from [("X.690 examples", op, a) for op, a, _ in x690_examples()] + yield from short_sets() + yield from identifier_sets() + yield from length_sets() + yield from size_sets() + yield from high_tag_sets() + yield from universal_sets() + yield from contents_sets() + yield from deep_sets() + for sig, _ in vectors: + yield ("Wycheproof ECDSA signatures", "all", (sig,)) + yield from writeint_sets() + yield from random_sets(seed, count) + + +# Request lines, each sent with LF after it, that the tool must answer +# `error`. One ending in CR is sent ending in CR LF. +MALFORMED = ["", " ", "\r", "decode", "decode 0101ff 00", "DECODE 0101ff", "Decode 0101ff", + "decode 0101FF", "decode 0", "decode 010", "decode 0g", "decode --", "decode -0101", + "decode 0101ff", "decode 0101ff ", " decode 0101ff", "decode\t0101ff", + "decode 0101ff\t", "decode ", "decode 0101ff\r\r", "decode\r 0101ff", "decode 0x01", + "decode é", "decode 0101ff\x00", "all", "all 0101FF", "all 0101ff 0101ff", + "all 0101ff", "readint", "readint 0G", "readint 1", "readint 00 00", "readint +01", + "writeint", "writeint +1", "writeint 1.0", "writeint 1e3", "writeint --1", + "writeint -", "writeint 0x10", "writeint - 1", "writeint 1 2", "writeint ١", + "writeint 1", "writeint 1 ", "encode", "encode p0.2", "encode p0.2.", "encode p0.2.0", + "encode p0.2.0G", "encode P0.2.00", "encode p0..00", "encode p.2.00", + "encode p-1.2.00", "encode p0.-2.00", "encode p0.2.00)", "encode p0.2.00,", + "encode c0.16(", "encode c0.16", "encode c0.16)", "encode c0.16(p0.5.-", + "encode c0.16(,)", "encode c0.16(p0.5.-,)", "encode c0.16(p0.5.-))", + "encode c0.16(p0.5.-)x", "encode c0.16.(p0.5.-)", "encode c0.16 (p0.5.-)", + "encode x", "encode -", "encode p0.2.00 p0.2.00", "encode p0x1.2.00", + "encode p0.2.-0", "encode p0.2.00(", "encode c0.16((p0.5.-))", "encode p0.2.0A", + "encode p0.١.00", "encode c0.16(p0.5.-p0.5.-)", "encode ()", "nop", "DECODE", + "decode 0101ff\x0b", "readint -00", "writeint 1-", "valid", "valid p0.2", + "valid p0.5.- p0.5.-", "VALID p0.5.-", "Valid p0.5.-", "valid 0500", "valid p0.5.-", + "valid p0.5.- "] + +# Well-formed but odd: leading zeros, large numbers, `-` fields, CR LF. +ODD = ["decode -", "all -", "readint -", "writeint 0", "writeint -0", "writeint 007", + "writeint -007", "writeint 340282366920938463463374607431768211456", + "writeint -340282366920938463463374607431768211457", "encode p0.5.-", "encode c0.16()", + "encode p00.002.00", "encode c3.000099999999999999999999()", "encode p2.31.-", + "encode c1.30(c2.31(p3.0.-),p0.1.00)", "decode 0101ff\r", "all 0500\r", "readint 0080\r", + "writeint -129\r", "encode c0.16(p0.5.-)\r", "decode 1f8100", "encode p4.2.00", + "encode c5.16(p0.5.-)", "encode c7.40(p6.300.00)", "valid p4.2.00", "valid p3.2.0080", + "valid c0.16()", "valid p0.0.-", "valid c0.10()", "valid p0.10.0001", "valid p0.10.ff", + "valid c1.2(p0.10.-)", "valid c0.16(p0.5.-)\r"] + +# Last lines sent alone, as they are sent, and the answer due: with no +# LF, with CR LF, and with a lone CR, which is not a line ending. +AT_EOF = [("decode 0101ff", "OK p0.1.ff -"), ("decode 0101ff\r\n", "OK p0.1.ff -"), + ("all 0500\r\n", "OK p0.5.-"), ("decode 0101ff\r", "error")] + + +def cli_cases(): + """Runs of `der decode HEX` and wrong arguments: (arguments, what the + run must print on standard output, without the last LF, and its exit + status), from the model. A run that does not decode, malformed HEX + and wrong arguments print nothing on standard output, and exit with + 1, 2 and 2.""" + cases = [] + hexes = ("0101ff", "0101ff00", "30060201010500", "0101", "1a054a6f6e6573", + "3a8004034a6f6e040265730000", "0500ff", "1f2a00", "1f05", "0481c9") + inputs_ = [bytes.fromhex(h) for h in hexes] + inputs_ += [b"", encode(chain(100, ("p", 0, 5, b""), ((0, 16), (2, 3))))] + for data in inputs_: + (r, _), _ = read(data) + cases.append((["decode", hx(data)], f"{show(r[0])} {hx(r[1])}" if r else "", + 0 if r else 1)) + for bad in ["", "0101FF", "1", "0x01", "01 01", "--", "-0101", "zz", "0101ff\r"]: + cases.append((["decode", bad], "", 2)) + for args in [[], ["decode"], ["decode", "0101ff", "00"], ["bogus"], ["serve", "x"], + ["all", "0101ff"], ["DECODE", "0101ff"], ["decode", "-", "-"]]: + cases.append((args, "", 2)) + return cases + + +def run_cli(cases): + """How many runs print and exit as `cli_cases` says, and a failure + for each other.""" + right, failures = 0, [] + for args, out, status in cases: + try: + p = subprocess.run([str(EXE)] + args, capture_output=True, timeout=60, + stdin=subprocess.DEVNULL) + got, code_ = p.stdout.decode("utf-8", "backslashreplace"), p.returncode + if got.endswith("\n"): + got = got[:-1] + except (OSError, subprocess.SubprocessError) as e: + got, code_ = str(e), None + if (got, code_) == (out, status): + right += 1 + else: + failures.append((f"der {' '.join(repr(a) for a in args)}", + f"{short(out, 160)!r}, exit {status}", + f"{short(got, 160)!r}, exit {code_}")) + return right, failures + +# --------------------------------------------------------------------- + + +def lean_version(): + try: + out = subprocess.run(["lake", "env", "lean", "--version"], cwd=ROOT, + capture_output=True, text=True, errors="backslashreplace", + timeout=300) + first = out.stdout.strip().splitlines()[:1] + return first[0] if first else "unknown" + except (OSError, subprocess.TimeoutExpired): + return "unknown" + + +def count_arg(text): + n = int(text) + if n < 1: + raise argparse.ArgumentTypeError("the count must be at least 1") + return n + + +def write(lines): + RESULTS.write_text("\n".join(lines), encoding="utf-8") + + +def code(text): + """`text` as Markdown inline code, whatever backticks it holds.""" + return f"`` {text} ``" if "`" in text else f"`{text}`" + + +def short(text, n=200): + return text if len(text) <= n else text[:n] + f"... ({len(text)} characters)" + + +def main(): + sys.set_int_max_str_digits(0) + ap = argparse.ArgumentParser() + ap.add_argument("--seed", type=int, default=20261027) + ap.add_argument("--count", type=count_arg, default=100000) + ap.add_argument("--no-oracles", action="store_true", + help="compare with the model only, as tests/plant_bugs.py does") + args = ap.parse_args() + os.environ["PATH"] = f"{Path.home() / '.elan' / 'bin'}{os.pathsep}{os.environ.get('PATH', '')}" + + def give_up(why): + write(["# Test results", "", "- Oracle: none ran", f"- Seed: {args.seed}", "- Inputs: 0", + f"- Failures: 1 ({why})", ""]) + sys.exit(why) + + try: + vectors = read_wycheproof() + except (OSError, ValueError, KeyError, TypeError) as e: + give_up(f"the Wycheproof vectors could not be read: {e}") + + try: + build = subprocess.run(["lake", "build", "der"], cwd=ROOT, capture_output=True, + text=True, errors="backslashreplace", timeout=BUILD_TIMEOUT) + if build.returncode != 0: + print(build.stdout + build.stderr) + give_up("`lake build der` failed") + except (OSError, subprocess.SubprocessError) as e: + give_up(f"`lake build der` failed: {e}") + + failures = [] + examples = x690_examples() + examples_right = 0 + for op, a, due in examples: + got = model(op, a)[0] + if got == due: + examples_right += 1 + else: + failures.append((f"the model on {short(request(op, a))}", short(due), short(got))) + + cli = cli_cases() + cli_right, cli_failures = run_cli(cli) + failures += cli_failures + + oracles, left_out = [], [] + for names, setup in ORACLE_SETUPS: + if args.no_oracles: + left_out += [(name, "--no-oracles") for name in names] + continue + try: + oracles += setup() + except Exception as e: # any oracle that cannot start is left out, and recorded + why = str(e).splitlines()[0][:300] if str(e) else type(e).__name__ + left_out += [(name, why) for name in names] + for name, why in left_out: + print(f"oracle left out: {name}: {why}") + + items = list(inputs(args.seed, args.count, vectors)) + requests = [request(op, a) for _, op, a in items] + lines = [r + ("\r\n" if i % 7 == 6 else "\n") for i, r in enumerate(requests)] + deep = [i for i, (label, _, _) in enumerate(items) if label.startswith("nested")] + deep_set = set(deep) + rest = [i for i in range(len(items)) if i not in deep_set] + + # The tool: the deep requests one to a process, under DEEP_BUDGET; + # the others, then the malformed and odd ones, over JOBS processes. + tool = [None] * len(items) + deep_in_time = 0 + for i in deep: + started = time.monotonic() + got, problem = run_lines([str(EXE), "serve"], [lines[i]], timeout=DEEP_BUDGET) + took = time.monotonic() - started + print(f"{items[i][0]} {items[i][1]}: {took:.1f} s") + if problem or len(got) != 1: + failures.append((short(requests[i], 120), f"an answer within {DEEP_BUDGET} seconds", + problem or f"{len(got)} answers")) + got = [""] + else: + deep_in_time += 1 + tool[i] = got[0] + odd = MALFORMED + ODD + out, problems = run_parallel([str(EXE), "serve"], + [lines[i] for i in rest] + [m + "\n" for m in odd], JOBS) + for problem in problems: + failures.append(("(whole run)", "a clean run", problem)) + for i, a in zip(rest, out): + tool[i] = a + odd_out = out[len(rest):] + malformed_right = sum(1 for m, a in zip(MALFORMED, odd_out) if a == "error") + for m in MALFORMED: + if model_line(m + "\n") != "error": + failures.append((f"the model on {m!r}", "error", model_line(m + "\n"))) + odd_right = 0 + for m, a in zip(odd, odd_out): + want = model_line(m + "\n") + if a == want: + odd_right += m in ODD + else: + failures.append((f"request {m!r}", want, a)) + eof_right = 0 + for req, want in AT_EOF: + if model_line(req) != want: + failures.append((f"the model on {req!r} at end of input", want, model_line(req))) + got, problem = run_lines([str(EXE), "serve"], [req], timeout=60) + if got == [want] and not problem: + eof_right += 1 + else: + failures.append((req + " (at end of input)", want, f"{got} {problem or ''}".strip())) + + # Against the model. + wants, whys = [], [] + differ_model = 0 + refused = {} + for (label, op, a), got in zip(items, tool): + want, why, _ = model(op, a) + wants.append(want) + whys.append(why) + if why is not None: + kind = "integer" if op == "readint" else "element" + refused[(kind, why)] = refused.get((kind, why), 0) + 1 + if got != want: + differ_model += 1 + failures.append((short(request(op, a)), short(want, 160), short(got, 160))) + + # What the tool encodes, read back by the model. + encodes = [(i, a) for i, (_, op, a) in enumerate(items) if op == "encode"] + read_back = 0 + for i, a in encodes: + got = tool[i] + try: + (r, _), _ = read(octets_field(got[3:])) if got.startswith("OK ") else ((None, None), 0) + except ValueError: + r = None + ok = valid(a[0]) + if not ok or (r is not None and not r[1] and show(r[0]) == show(a[0])): + read_back += ok + else: + failures.append((f"the model reading back {short(request('encode', a), 120)}", + short(show(a[0]), 120), short(got, 120))) + n_valid_encodes = sum(1 for _, a in encodes if valid(a[0])) + + # `valid` holds exactly when the model reads the encoding back as the tree. + valids = [a for _, op, a in items if op == "valid"] + valid_back = 0 + for a in valids: + (r, _), _ = read_all(encode(a[0])) + if (r is not None and show(r[0]) == show(a[0])) == valid(a[0]): + valid_back += 1 + else: + failures.append((f"the model on {short(request('valid', a), 120)}", + "`valid` exactly when the encoding reads back as the tree", + f"valid {valid(a[0])}")) + n_valid_ok = sum(1 for a in valids if valid(a[0])) + + # The Wycheproof signatures. + first_sig = next(i for i, it in enumerate(items) if it[0] == "Wycheproof ECDSA signatures") + sig_answers = tool[first_sig:first_sig + len(vectors)] + flagged = {f: [0, 0] for f in ENCODING_FLAGS} + sig_differ = 0 + for (sig, flags), got, want in zip(vectors, sig_answers, + wants[first_sig:first_sig + len(vectors)]): + sig_differ += got != want + for f in ENCODING_FLAGS: + if f in flags: + flagged[f][0] += 1 + flagged[f][1] += got == "FAIL" + sig_refused = sum(1 for got in sig_answers if got == "FAIL") + + # Against the oracles. + oracle_out = {} + + def ask(o): + try: + oracle_out[o.name] = o.answers(lines) + except (RuntimeError, OSError) as e: + oracle_out[o.name] = e + + threads = [threading.Thread(target=ask, args=(o,)) for o in oracles] + for t in threads: + t.start() + for t in threads: + t.join() + for o in list(oracles): + if isinstance(oracle_out[o.name], Exception): + failures.append((f"({o.name} oracle run)", "a clean run", str(oracle_out[o.name])[:300])) + oracles.remove(o) + names = [o.name for o in oracles] + # Elements each oracle reads, and how many of its answers the model + # gives with the rules the oracle reads by. + by_rules = {o: [0, 0] for o in names} + asked = {o: 0 for o in names} + agree = {o: 0 for o in names} + by_op = {o: {} for o in names} + named = {o: {} for o in names} + for i, (label, op, a) in enumerate(items): + got = tool[i] + for o in names: + theirs = oracle_out[o][i] + if theirs == "n/a": + continue + asked[o] += 1 + by_op[o][op] = by_op[o].get(op, 0) + 1 + theirs_by_model, parted = None, set() + if op in ("decode", "all"): + theirs_by_model, _, parted = model(op, a, ORACLE_RULES[o]) + by_rules[o][0] += 1 + by_rules[o][1] += theirs_by_model == theirs + if theirs == got: + agree[o] += 1 + continue + reason = difference_reason(o, op, theirs, theirs_by_model, parted) + if reason is None: + failures.append((f"{o}: {short(request(op, a))}", short(got, 160), + short(theirs, 160))) + else: + n, example = named[o].get(reason, (0, None)) + named[o][reason] = (n + 1, example or request(op, a)) + + by_label = {} + for label, op, _ in items: + key = f"{label} {op}" + by_label[key] = by_label.get(key, 0) + 1 + + def refusals(kind): + return ", ".join(f"{why} {n}" for (k, why), n in + sorted(refused.items(), key=lambda kv: -kv[1]) if k == kind) + + n_elements = sum(1 for _, op, _ in items if op in ("decode", "all")) + ok_elements = sum(1 for (_, op, _), y in zip(items, whys) if op in ("decode", "all") and y is None) + n_readint = sum(1 for _, op, _ in items if op == "readint") + ok_readint = sum(1 for (_, op, _), y in zip(items, whys) if op == "readint" and y is None) + n_writeint = sum(1 for _, op, _ in items if op == "writeint") + + lines_out = [ + "# Test results", + "", + "Written by `tests/run_tests.py`. Rerun it to check these numbers.", + "", + f"- Lean: {lean_version()}", + "- Oracle: the model of X.690 (02/2021) in `tests/run_tests.py`" + + "".join(f"; {o.name}, {o.version}" for o in oracles), + ] + lines_out += [f"- {o.name}: {o.version}" for o in oracles] + lines_out += [ + "- Oracles left out: " + ("; ".join(f"{n} ({why})" for n, why in left_out) + if left_out else "none"), + f"- Seed: {args.seed}", + f"- Inputs: {len(items)}", + "- By kind: " + ", ".join(f"{k} {v}" for k, v in sorted(by_label.items())), + f"- Answers that differ from the model: {differ_model}", + f"- X.690's examples, answered by the model as the standard gives them: {examples_right} " + f"of {len(examples)}", + f"- Elements read (`decode` and `all`): {n_elements}, of which {ok_elements} are accepted; " + f"why the model refuses the others: {refusals('element')}", + f"- Integer contents read (`readint`): {n_readint}, of which {ok_readint} give a value; why " + f"the model refuses the others: {refusals('integer')}", + f"- Encodings asked for: trees {len(encodes)}, of which the model reads the tool's " + f"encoding back as the tree for {read_back} of the {n_valid_encodes} the slice accepts; " + f"integers {n_writeint}", + f"- `valid` asked of {len(valids)} trees, of which the slice accepts {n_valid_ok}; the " + f"model's answer holds exactly when it reads the tree's encoding back as the tree, for " + f"{valid_back} of {len(valids)}", + f"- Wycheproof: `tests/wycheproof/{WYCHEPROOF.name}`, SHA-256 {WYCHEPROOF_SHA256}: " + f"{len(vectors)} signatures read with `all`, of which the tool refuses {sig_refused}, " + f"every answer as the model's but {sig_differ}. Flagged as an encoding that is not DER " + f"or is broken: " + + ", ".join(f"{f} {n}, of which the tool refuses {r}" for f, (n, r) in flagged.items()) + + ". Each flagged one it accepts is an element this slice accepts, with what is wrong " + "in something it does not check: a negative INTEGER where a positive one belongs, a BIT " + "STRING where an INTEGER belongs, or a SEQUENCE given the reserved universal tag 15", + f"- Requests nested 10,000 and 100,000 deep, each sent to a `der serve` of its own: " + f"{len(deep)}, answered within {DEEP_BUDGET} seconds: {deep_in_time}", + "- Agreement with each oracle over the requests it answers: " + + ", ".join(f"{o} {agree[o]} of {asked[o]}" for o in names), + "- Elements each oracle reads (`decode` and `all`) whose answer the model gives with the " + "rules that oracle reads by, whether or not it agrees with the tool: " + + ", ".join(f"{o} {by_rules[o][1]} of {by_rules[o][0]}" for o in names), + "- Requests each oracle answers, by operation: " + + "; ".join(f"{o}: " + ", ".join(f"{op} {n}" for op, n in sorted(by_op[o].items())) + for o in names), + f"- Malformed requests answered `error`: {malformed_right} of {len(MALFORMED)}; odd but " + f"well-formed requests answered as the model says: {odd_right} of {len(ODD)}", + f"- Requests ending in CR LF: every seventh, {len(requests) // 7}", + f"- Last lines sent alone, with no LF, with CR LF, or with a lone CR, answered right: " + f"{eof_right} of {len(AT_EOF)}", + f"- Command-line mode `der decode` and wrong arguments, printing and exiting as the model " + f"says: {cli_right} of {len(cli)}", + f"- Failures: {len(failures)}", + "", + "## Where the oracles differ", + "", + "Each disagreement is counted under the reason the model confirms: the model, with the " + "rules the oracle reads by, gives the oracle's answer. The reason names each rule on which " + "those part from DER for the input, with the first input that showed it.", + "", + ] + for o in names: + lines_out.append(f"### {o}") + lines_out.append("") + if not named[o]: + lines_out.append("No disagreement.") + for reason, (n, example) in sorted(named[o].items(), key=lambda kv: -kv[1][0]): + lines_out.append(f"- {reason}: {n}; for example {code(short(example, 240))}") + lines_out.append("") + if failures: + lines_out += ["## Failures", ""] + lines_out += [f"- {code(req)}: expected {code(want)}, got {code(got)}" + for req, want, got in failures[:50]] + lines_out.append("") + write(lines_out) + print(f"{len(items)} inputs, {len(oracles)} oracles, {len(failures)} failures; results in " + f"{RESULTS}") + return 1 if failures else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/wycheproof/README.md b/tests/wycheproof/README.md new file mode 100644 index 0000000..e201966 --- /dev/null +++ b/tests/wycheproof/README.md @@ -0,0 +1,14 @@ +# Wycheproof test vectors + +`ecdsa_secp256r1_sha256_test.json` is a copy of +`testvectors_v1/ecdsa_secp256r1_sha256_test.json` from Project Wycheproof +(https://github.com/C2SP/wycheproof), taken from its `main` branch. Its +SHA-256 is 182db4f3e230f6f9fa9f800d2a614dede30284b8e8438bbfe1171905402e9332; +`tests/run_tests.py` checks it before reading the file. + +Project Wycheproof is licensed under the Apache License, Version 2.0 +(https://www.apache.org/licenses/LICENSE-2.0). The file is unchanged. + +The tests read each signature with `all` and count how many of those +flagged `BerEncodedSignature`, `InvalidEncoding` and `MissingZero` the +tool refuses. diff --git a/tests/wycheproof/ecdsa_secp256r1_sha256_test.json b/tests/wycheproof/ecdsa_secp256r1_sha256_test.json new file mode 100644 index 0000000..19c951d --- /dev/null +++ b/tests/wycheproof/ecdsa_secp256r1_sha256_test.json @@ -0,0 +1,7243 @@ +{ + "algorithm": "ECDSA", + "schema": "ecdsa_verify_schema_v1.json", + "numberOfTests": 484, + "header": [ + "Test vectors of type EcdsaVerify are meant for the verification", + "of ASN 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" + ] + }, + "BerEncodedSignature": { + "bugType": "BER_ENCODING", + "description": "ECDSA signatures are usually DER encoded. This signature contains valid values for r and s, but it uses alternative BER encoding.", + "effect": "Accepting alternative BER encodings may be benign in some cases, or be an issue if protocol requires signature malleability.", + "cves": [ + "CVE-2020-14966", + "CVE-2020-13822", + "CVE-2019-14859", + "CVE-2016-1000342" + ] + }, + "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." + }, + "InvalidEncoding": { + "bugType": "CAN_OF_WORMS", + "description": "ECDSA signatures are encoded using ASN.1. This test vector contains an incorrectly encoded signature. The test vector itself was generated from a valid signature by modifying its encoding.", + "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" + ] + }, + "InvalidTypesInSignature": { + "bugType": "AUTH_BYPASS", + "description": "The signature contains invalid types. Dynamic typed languages sometime coerce such values of different types into integers. If an implementation is careless and has additional bugs, such as not checking integer boundaries then it may be possible that such signatures are accepted.", + "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" + ] + }, + "MissingZero": { + "bugType": "LEGACY", + "description": "Some implementations of ECDSA and DSA incorrectly encode r and s by not including leading zeros in the ASN encoding of integers when necessary. Hence, some implementations (e.g. jdk) allow signatures with incorrect ASN encodings assuming that the signature is otherwise valid.", + "effect": "While signatures are more malleable if such signatures are accepted, this typically leads to no vulnerability, since a badly encoded signature can be reencoded correctly." + }, + "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." + }, + "ModifiedSignature": { + "bugType": "CAN_OF_WORMS", + "description": "The test vector contains an invalid signature that was generated from a valid signature by modifying it.", + "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." + }, + "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": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0404aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad587d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d", + "wx": "04aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad5", + "wy": "0087d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000404aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad587d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBKrsc2NXJvIT+4qeZNo7hjLkFJWp\nRNAEW1IuunJA+tWH2TFXmKqjpboBd1eHztBeqve04J/IHW0apUboNl1SXQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 1, + "comment": "pseudorandom signature", + "flags": [ + "ValidSignature" + ], + "msg": "", + "sig": "3045022100b292a619339f6e567a305c951c0dcbcc42d16e47f219f9e98e76e09d8770b34a02200177e60492c5a8242f76f07bfe3661bde59ec2a17ce5bd2dab2abebdf89a62e2", + "result": "valid" + }, + { + "tcId": 2, + "comment": "pseudorandom signature", + "flags": [ + "ValidSignature" + ], + "msg": "4d7367", + "sig": "30450220530bd6b0c9af2d69ba897f6b5fb59695cfbf33afe66dbadcf5b8d2a2a6538e23022100d85e489cb7a161fd55ededcedbf4cc0c0987e3e3f0f242cae934c72caa3f43e9", + "result": "valid" + }, + { + "tcId": 3, + "comment": "pseudorandom signature", + "flags": [ + "ValidSignature" + ], + "msg": "313233343030", + "sig": "3046022100a8ea150cb80125d7381c4c1f1da8e9de2711f9917060406a73d7904519e51388022100f3ab9fa68bd47973a73b2d40480c2ba50c22c9d76ec217257288293285449b86", + "result": "valid" + }, + { + "tcId": 4, + "comment": "pseudorandom signature", + "flags": [ + "ValidSignature" + ], + "msg": "0000000000000000000000000000000000000000", + "sig": "3045022100986e65933ef2ed4ee5aada139f52b70539aaf63f00a91f29c69178490d57fb7102203dafedfb8da6189d372308cbf1489bbbdabf0c0217d1c0ff0f701aaa7a694b9c", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", + "wx": "2927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838", + "wy": "00c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200042927b10512bae3eddcfe467828128bad2903269919f7086069c8c4df6c732838c7787964eaac00e5921fb1498a60f4606766b3d9685001558d1a974e7341513e", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKSexBRK64+3c/kZ4KBKLrSkDJpkZ\n9whgacjE32xzKDjHeHlk6qwA5ZIfsUmKYPRgZ2az2WhQAVWNGpdOc0FRPg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 5, + "comment": "signature malleability", + "flags": [ + "ValidSignature" + ], + "msg": "313233343030", + "sig": "304402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1802204cd60b855d442f5b3c7b11eb6c4e0ae7525fe710fab9aa7c77a67f79e6fadd76", + "result": "valid" + }, + { + "tcId": 6, + "comment": "Legacy: ASN encoding of s misses leading 0", + "flags": [ + "MissingZero" + ], + "msg": "313233343030", + "sig": "304402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180220b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 7, + "comment": "valid", + "flags": [ + "ValidSignature" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "valid" + }, + { + "tcId": 8, + "comment": "length of sequence [r, s] uses long form encoding", + "flags": [ + "BerEncodedSignature" + ], + "msg": "313233343030", + "sig": "30814502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 9, + "comment": "length of sequence [r, s] contains a leading 0", + "flags": [ + "BerEncodedSignature" + ], + "msg": "313233343030", + "sig": "3082004502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 10, + "comment": "length of sequence [r, s] uses 70 instead of 69", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304602202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 11, + "comment": "length of sequence [r, s] uses 68 instead of 69", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 12, + "comment": "uint32 overflow in length of sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3085010000004502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 13, + "comment": "uint64 overflow in length of sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "308901000000000000004502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 14, + "comment": "length of sequence [r, s] = 2**31 - 1", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30847fffffff02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 15, + "comment": "length of sequence [r, s] = 2**31", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30848000000002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 16, + "comment": "length of sequence [r, s] = 2**32 - 1", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3084ffffffff02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 17, + "comment": "length of sequence [r, s] = 2**40 - 1", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3085ffffffffff02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 18, + "comment": "length of sequence [r, s] = 2**64 - 1", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3088ffffffffffffffff02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 19, + "comment": "incorrect length of sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30ff02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 20, + "comment": "replaced sequence [r, s] by an indefinite length tag without termination", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 21, + "comment": "removing sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "", + "result": "invalid" + }, + { + "tcId": 22, + "comment": "lonely sequence tag", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30", + "result": "invalid" + }, + { + "tcId": 23, + "comment": "appending 0's to sequence [r, s]", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", + "result": "invalid" + }, + { + "tcId": 24, + "comment": "prepending 0's to sequence [r, s]", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "3047000002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 25, + "comment": "appending unused 0's to sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", + "result": "invalid" + }, + { + "tcId": 26, + "comment": "appending null value to sequence [r, s]", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0500", + "result": "invalid" + }, + { + "tcId": 27, + "comment": "prepending garbage to sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304a498177304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 28, + "comment": "prepending garbage to sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30492500304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 29, + "comment": "appending garbage to sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3047304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0004deadbeef", + "result": "invalid" + }, + { + "tcId": 30, + "comment": "including undefined tags", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304daa00bb00cd00304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 31, + "comment": "including undefined tags", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304d2228aa00bb00cd0002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 32, + "comment": "including undefined tags", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304d02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e182229aa00bb00cd00022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 33, + "comment": "truncated length of sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3081", + "result": "invalid" + }, + { + "tcId": 34, + "comment": "including undefined tags to sequence [r, s]", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304baa02aabb304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 35, + "comment": "using composition with indefinite length for sequence [r, s]", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "3080304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", + "result": "invalid" + }, + { + "tcId": 36, + "comment": "using composition with wrong tag for sequence [r, s]", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "3080314502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", + "result": "invalid" + }, + { + "tcId": 37, + "comment": "Replacing sequence [r, s] with NULL", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "0500", + "result": "invalid" + }, + { + "tcId": 38, + "comment": "changing tag value of sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "2e4502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 39, + "comment": "changing tag value of sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "2f4502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 40, + "comment": "changing tag value of sequence [r, s]", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "314502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 41, + "comment": "changing tag value of sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "324502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 42, + "comment": "changing tag value of sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "ff4502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 43, + "comment": "dropping value of sequence [r, s]", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "3000", + "result": "invalid" + }, + { + "tcId": 44, + "comment": "using composition for sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30493001023044202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 45, + "comment": "truncated sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847", + "result": "invalid" + }, + { + "tcId": 46, + "comment": "truncated sequence [r, s]", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3044202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 47, + "comment": "sequence [r, s] of size 4166 to check for overflows", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3082104602202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 48, + "comment": "indefinite length", + "flags": [ + "BerEncodedSignature" + ], + "msg": "313233343030", + "sig": "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", + "result": "invalid" + }, + { + "tcId": 49, + "comment": "indefinite length with truncated delimiter", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db00", + "result": "invalid" + }, + { + "tcId": 50, + "comment": "indefinite length with additional element", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db05000000", + "result": "invalid" + }, + { + "tcId": 51, + "comment": "indefinite length with truncated element", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db060811220000", + "result": "invalid" + }, + { + "tcId": 52, + "comment": "indefinite length with garbage", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000fe02beef", + "result": "invalid" + }, + { + "tcId": 53, + "comment": "indefinite length with nonempty EOC", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "308002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0002beef", + "result": "invalid" + }, + { + "tcId": 54, + "comment": "prepend empty sequence", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "3047300002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 55, + "comment": "append empty sequence", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db3000", + "result": "invalid" + }, + { + "tcId": 56, + "comment": "append zero", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304802202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db020100", + "result": "invalid" + }, + { + "tcId": 57, + "comment": "append garbage with high tag number", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304802202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847dbbf7f00", + "result": "invalid" + }, + { + "tcId": 58, + "comment": "append null with explicit tag", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847dba0020500", + "result": "invalid" + }, + { + "tcId": 59, + "comment": "append null with implicit tag", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847dba000", + "result": "invalid" + }, + { + "tcId": 60, + "comment": "sequence of sequence", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "3047304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 61, + "comment": "truncated sequence: removed last 1 elements", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "302202202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18", + "result": "invalid" + }, + { + "tcId": 62, + "comment": "repeating element in sequence", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "306802202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 63, + "comment": "flipped bit 0 in r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30432ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e19022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 64, + "comment": "flipped bit 32 in r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30432ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af8bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 65, + "comment": "flipped bit 48 in r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30432ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cd6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 66, + "comment": "flipped bit 64 in r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30432ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee858b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 67, + "comment": "length of r uses long form encoding", + "flags": [ + "BerEncodedSignature" + ], + "msg": "313233343030", + "sig": "30460281202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 68, + "comment": "length of r contains a leading 0", + "flags": [ + "BerEncodedSignature" + ], + "msg": "313233343030", + "sig": "3047028200202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 69, + "comment": "length of r uses 33 instead of 32", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304502212ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 70, + "comment": "length of r uses 31 instead of 32", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3045021f2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 71, + "comment": "uint32 overflow in length of r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304a028501000000202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 72, + "comment": "uint64 overflow in length of r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304e02890100000000000000202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 73, + "comment": "length of r = 2**31 - 1", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304902847fffffff2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 74, + "comment": "length of r = 2**31", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30490284800000002ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 75, + "comment": "length of r = 2**32 - 1", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30490284ffffffff2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 76, + "comment": "length of r = 2**40 - 1", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304a0285ffffffffff2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 77, + "comment": "length of r = 2**64 - 1", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304d0288ffffffffffffffff2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 78, + "comment": "incorrect length of r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304502ff2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 79, + "comment": "replaced r by an indefinite length tag without termination", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304502802ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 80, + "comment": "removing r", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "3023022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 81, + "comment": "lonely integer tag", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "302402022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 82, + "comment": "lonely integer tag", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "302302202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1802", + "result": "invalid" + }, + { + "tcId": 83, + "comment": "appending 0's to r", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304702222ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180000022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 84, + "comment": "prepending 0's to r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3047022200002ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 85, + "comment": "appending unused 0's to r", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180000022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 86, + "comment": "appending null value to r", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304702222ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180500022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 87, + "comment": "prepending garbage to r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304a222549817702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 88, + "comment": "prepending garbage to r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30492224250002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 89, + "comment": "appending garbage to r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304d222202202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180004deadbeef022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 90, + "comment": "truncated length of r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30250281022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 91, + "comment": "including undefined tags to r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304b2226aa02aabb02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 92, + "comment": "using composition with indefinite length for r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3049228002202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180000022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 93, + "comment": "using composition with wrong tag for r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3049228003202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180000022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 94, + "comment": "Replacing r with NULL", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "30250500022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 95, + "comment": "changing tag value of r", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304500202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 96, + "comment": "changing tag value of r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304501202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 97, + "comment": "changing tag value of r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304503202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 98, + "comment": "changing tag value of r", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304504202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 99, + "comment": "changing tag value of r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3045ff202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 100, + "comment": "dropping value of r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "30250200022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 101, + "comment": "using composition for r", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "3049222402012b021fa3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 102, + "comment": "modifying first byte of r", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "3045022029a3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 103, + "comment": "modifying last byte of r", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e98022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 104, + "comment": "truncated r", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "3044021f2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 105, + "comment": "truncated r", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "3044021fa3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 106, + "comment": "r of size 4129 to check for overflows", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "30821048028210212ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 107, + "comment": "leading ff in r", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "30460221ff2ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 108, + "comment": "replaced r by infinity", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "3026090180022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 109, + "comment": "replacing r with zero", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "3026020100022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 110, + "comment": "flipped bit 0 in s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304302202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1800b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847da", + "result": "invalid" + }, + { + "tcId": 111, + "comment": "flipped bit 32 in s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304302202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1800b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b48156847db", + "result": "invalid" + }, + { + "tcId": 112, + "comment": "flipped bit 48 in s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304302202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1800b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c124b49156847db", + "result": "invalid" + }, + { + "tcId": 113, + "comment": "flipped bit 64 in s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304302202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1800b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4097c134b49156847db", + "result": "invalid" + }, + { + "tcId": 114, + "comment": "length of s uses long form encoding", + "flags": [ + "BerEncodedSignature" + ], + "msg": "313233343030", + "sig": "304602202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1802812100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 115, + "comment": "length of s contains a leading 0", + "flags": [ + "BerEncodedSignature" + ], + "msg": "313233343030", + "sig": "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180282002100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 116, + "comment": "length of s uses 34 instead of 33", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022200b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 117, + "comment": "length of s uses 32 instead of 33", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022000b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 118, + "comment": "uint32 overflow in length of s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304a02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180285010000002100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 119, + "comment": "uint64 overflow in length of s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304e02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18028901000000000000002100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 120, + "comment": "length of s = 2**31 - 1", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1802847fffffff00b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 121, + "comment": "length of s = 2**31", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1802848000000000b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 122, + "comment": "length of s = 2**32 - 1", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180284ffffffff00b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 123, + "comment": "length of s = 2**40 - 1", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304a02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180285ffffffffff00b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 124, + "comment": "length of s = 2**64 - 1", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304d02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180288ffffffffffffffff00b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 125, + "comment": "incorrect length of s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1802ff00b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 126, + "comment": "replaced s by an indefinite length tag without termination", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18028000b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 127, + "comment": "appending 0's to s", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022300b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", + "result": "invalid" + }, + { + "tcId": 128, + "comment": "prepending 0's to s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180223000000b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 129, + "comment": "appending null value to s", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304702202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022300b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0500", + "result": "invalid" + }, + { + "tcId": 130, + "comment": "prepending garbage to s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304a02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e182226498177022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 131, + "comment": "prepending garbage to s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1822252500022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 132, + "comment": "appending garbage to s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304d02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e182223022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0004deadbeef", + "result": "invalid" + }, + { + "tcId": 133, + "comment": "truncated length of s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "302402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180281", + "result": "invalid" + }, + { + "tcId": 134, + "comment": "including undefined tags to s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304b02202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e182227aa02aabb022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 135, + "comment": "using composition with indefinite length for s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e182280022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", + "result": "invalid" + }, + { + "tcId": 136, + "comment": "using composition with wrong tag for s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e182280032100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000", + "result": "invalid" + }, + { + "tcId": 137, + "comment": "Replacing s with NULL", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "302402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180500", + "result": "invalid" + }, + { + "tcId": 138, + "comment": "changing tag value of s", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18002100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 139, + "comment": "changing tag value of s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18012100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 140, + "comment": "changing tag value of s", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18032100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 141, + "comment": "changing tag value of s", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18042100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 142, + "comment": "changing tag value of s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18ff2100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 143, + "comment": "dropping value of s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "302402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180200", + "result": "invalid" + }, + { + "tcId": 144, + "comment": "using composition for s", + "flags": [ + "InvalidEncoding" + ], + "msg": "313233343030", + "sig": "304902202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e1822250201000220b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 145, + "comment": "modifying first byte of s", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022102b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 146, + "comment": "modifying last byte of s", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b491568475b", + "result": "invalid" + }, + { + "tcId": 147, + "comment": "truncated s", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304402202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022000b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847", + "result": "invalid" + }, + { + "tcId": 148, + "comment": "s of size 4130 to check for overflows", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "3082104802202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180282102200b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 149, + "comment": "leading ff in s", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "304602202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e180222ff00b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 150, + "comment": "replaced s by infinity", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "302502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18090180", + "result": "invalid" + }, + { + "tcId": 151, + "comment": "replacing s with zero", + "flags": [ + "ModifiedSignature" + ], + "msg": "313233343030", + "sig": "302502202ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18020100", + "result": "invalid" + }, + { + "tcId": 152, + "comment": "replaced r by r + n", + "flags": [ + "RangeCheck" + ], + "msg": "313233343030", + "sig": "30460221012ba3a8bd6b94d5ed80a6d9d1190a436ebccc0833490686deac8635bcb9bf5369022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 153, + "comment": "replaced r by r - n", + "flags": [ + "RangeCheck" + ], + "msg": "313233343030", + "sig": "30460221ff2ba3a8bf6b94d5eb80a6d9d1190a436f42fe12d7fad749d4c512a036c0f908c7022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 154, + "comment": "replaced r by r + 256 * n", + "flags": [ + "RangeCheck" + ], + "msg": "313233343030", + "sig": "3047022201002ba3a7be6b94d6ec80a6d9d1190a432be6dfbb2cb98d6d4d72972df620817f18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 155, + "comment": "replaced r by -r", + "flags": [ + "ModifiedInteger" + ], + "msg": "313233343030", + "sig": "30450220d45c5741946b2a137f59262ee6f5bc91001af27a5e1117a64733950642a3d1e8022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 156, + "comment": "replaced r by n - r", + "flags": [ + "ModifiedInteger" + ], + "msg": "313233343030", + "sig": "3046022100d45c5740946b2a147f59262ee6f5bc90bd01ed280528b62b3aed5fc93f06f739022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 157, + "comment": "replaced r by -n - r", + "flags": [ + "ModifiedInteger" + ], + "msg": "313233343030", + "sig": "30460221fed45c5742946b2a127f59262ee6f5bc914333f7ccb6f979215379ca434640ac97022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 158, + "comment": "replaced r by r + 2**256", + "flags": [ + "IntegerOverflow" + ], + "msg": "313233343030", + "sig": "30460221012ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 159, + "comment": "replaced r by r + 2**320", + "flags": [ + "IntegerOverflow" + ], + "msg": "313233343030", + "sig": "304e02290100000000000000002ba3a8be6b94d5ec80a6d9d1190a436effe50d85a1eee859b8cc6af9bd5c2e18022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 160, + "comment": "replaced s by s + n", + "flags": [ + "RangeCheck" + ], + "msg": "313233343030", + "sig": "3046022101b329f478a2bbd0a6c384ee1493b1f518276e0e4a5375928d6fcd160c11cb6d2c022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 161, + "comment": "replaced s by s - n", + "flags": [ + "RangeCheck" + ], + "msg": "313233343030", + "sig": "30450220b329f47aa2bbd0a4c384ee1493b1f518ada018ef05465583885980861905228a022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 162, + "comment": "replaced s by s + 256 * n", + "flags": [ + "RangeCheck" + ], + "msg": "313233343030", + "sig": "304702220100b329f379a2bbd1a5c384ee1493b1f4d55181c143c3fc78fc35de0e45788d98db022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 163, + "comment": "replaced s by -s", + "flags": [ + "ModifiedInteger" + ], + "msg": "313233343030", + "sig": "30460221ff4cd60b865d442f5a3c7b11eb6c4e0ae79578ec6353a20bf783ecb4b6ea97b825022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 164, + "comment": "replaced s by -n - s", + "flags": [ + "ModifiedInteger" + ], + "msg": "313233343030", + "sig": "30460221fe4cd60b875d442f593c7b11eb6c4e0ae7d891f1b5ac8a6d729032e9f3ee3492d4022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 165, + "comment": "replaced s by s + 2**256", + "flags": [ + "IntegerOverflow" + ], + "msg": "313233343030", + "sig": "3046022101b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 166, + "comment": "replaced s by s - 2**256", + "flags": [ + "IntegerOverflow" + ], + "msg": "313233343030", + "sig": "30450220b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 167, + "comment": "replaced s by s + 2**320", + "flags": [ + "IntegerOverflow" + ], + "msg": "313233343030", + "sig": "304e0229010000000000000000b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db022100b329f479a2bbd0a5c384ee1493b1f5186a87139cac5df4087c134b49156847db", + "result": "invalid" + }, + { + "tcId": 168, + "comment": "Signature with special case values r=0 and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3006020100020100", + "result": "invalid" + }, + { + "tcId": 169, + "comment": "Signature with special case values r=0 and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3006020100020101", + "result": "invalid" + }, + { + "tcId": 170, + "comment": "Signature with special case values r=0 and s=-1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "30060201000201ff", + "result": "invalid" + }, + { + "tcId": 171, + "comment": "Signature with special case values r=0 and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026020100022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", + "result": "invalid" + }, + { + "tcId": 172, + "comment": "Signature with special case values r=0 and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026020100022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", + "result": "invalid" + }, + { + "tcId": 173, + "comment": "Signature with special case values r=0 and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026020100022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", + "result": "invalid" + }, + { + "tcId": 174, + "comment": "Signature with special case values r=0 and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026020100022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", + "result": "invalid" + }, + { + "tcId": 175, + "comment": "Signature with special case values r=0 and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026020100022100ffffffff00000001000000000000000000000001000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 176, + "comment": "Signature with special case values r=1 and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3006020101020100", + "result": "invalid" + }, + { + "tcId": 177, + "comment": "Signature with special case values r=1 and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3006020101020101", + "result": "invalid" + }, + { + "tcId": 178, + "comment": "Signature with special case values r=1 and s=-1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "30060201010201ff", + "result": "invalid" + }, + { + "tcId": 179, + "comment": "Signature with special case values r=1 and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026020101022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", + "result": "invalid" + }, + { + "tcId": 180, + "comment": "Signature with special case values r=1 and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026020101022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", + "result": "invalid" + }, + { + "tcId": 181, + "comment": "Signature with special case values r=1 and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026020101022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", + "result": "invalid" + }, + { + "tcId": 182, + "comment": "Signature with special case values r=1 and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026020101022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", + "result": "invalid" + }, + { + "tcId": 183, + "comment": "Signature with special case values r=1 and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026020101022100ffffffff00000001000000000000000000000001000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 184, + "comment": "Signature with special case values r=-1 and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "30060201ff020100", + "result": "invalid" + }, + { + "tcId": 185, + "comment": "Signature with special case values r=-1 and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "30060201ff020101", + "result": "invalid" + }, + { + "tcId": 186, + "comment": "Signature with special case values r=-1 and s=-1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "30060201ff0201ff", + "result": "invalid" + }, + { + "tcId": 187, + "comment": "Signature with special case values r=-1 and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "30260201ff022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", + "result": "invalid" + }, + { + "tcId": 188, + "comment": "Signature with special case values r=-1 and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "30260201ff022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", + "result": "invalid" + }, + { + "tcId": 189, + "comment": "Signature with special case values r=-1 and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "30260201ff022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", + "result": "invalid" + }, + { + "tcId": 190, + "comment": "Signature with special case values r=-1 and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "30260201ff022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", + "result": "invalid" + }, + { + "tcId": 191, + "comment": "Signature with special case values r=-1 and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "30260201ff022100ffffffff00000001000000000000000000000001000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 192, + "comment": "Signature with special case values r=n and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551020100", + "result": "invalid" + }, + { + "tcId": 193, + "comment": "Signature with special case values r=n and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551020101", + "result": "invalid" + }, + { + "tcId": 194, + "comment": "Signature with special case values r=n and s=-1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325510201ff", + "result": "invalid" + }, + { + "tcId": 195, + "comment": "Signature with special case values r=n and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", + "result": "invalid" + }, + { + "tcId": 196, + "comment": "Signature with special case values r=n and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", + "result": "invalid" + }, + { + "tcId": 197, + "comment": "Signature with special case values r=n and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", + "result": "invalid" + }, + { + "tcId": 198, + "comment": "Signature with special case values r=n and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", + "result": "invalid" + }, + { + "tcId": 199, + "comment": "Signature with special case values r=n and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551022100ffffffff00000001000000000000000000000001000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 200, + "comment": "Signature with special case values r=n - 1 and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550020100", + "result": "invalid" + }, + { + "tcId": 201, + "comment": "Signature with special case values r=n - 1 and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550020101", + "result": "invalid" + }, + { + "tcId": 202, + "comment": "Signature with special case values r=n - 1 and s=-1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325500201ff", + "result": "invalid" + }, + { + "tcId": 203, + "comment": "Signature with special case values r=n - 1 and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", + "result": "invalid" + }, + { + "tcId": 204, + "comment": "Signature with special case values r=n - 1 and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", + "result": "invalid" + }, + { + "tcId": 205, + "comment": "Signature with special case values r=n - 1 and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", + "result": "invalid" + }, + { + "tcId": 206, + "comment": "Signature with special case values r=n - 1 and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", + "result": "invalid" + }, + { + "tcId": 207, + "comment": "Signature with special case values r=n - 1 and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550022100ffffffff00000001000000000000000000000001000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 208, + "comment": "Signature with special case values r=n + 1 and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552020100", + "result": "invalid" + }, + { + "tcId": 209, + "comment": "Signature with special case values r=n + 1 and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552020101", + "result": "invalid" + }, + { + "tcId": 210, + "comment": "Signature with special case values r=n + 1 and s=-1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325520201ff", + "result": "invalid" + }, + { + "tcId": 211, + "comment": "Signature with special case values r=n + 1 and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", + "result": "invalid" + }, + { + "tcId": 212, + "comment": "Signature with special case values r=n + 1 and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", + "result": "invalid" + }, + { + "tcId": 213, + "comment": "Signature with special case values r=n + 1 and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", + "result": "invalid" + }, + { + "tcId": 214, + "comment": "Signature with special case values r=n + 1 and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", + "result": "invalid" + }, + { + "tcId": 215, + "comment": "Signature with special case values r=n + 1 and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552022100ffffffff00000001000000000000000000000001000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 216, + "comment": "Signature with special case values r=p and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff020100", + "result": "invalid" + }, + { + "tcId": 217, + "comment": "Signature with special case values r=p and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff020101", + "result": "invalid" + }, + { + "tcId": 218, + "comment": "Signature with special case values r=p and s=-1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff0201ff", + "result": "invalid" + }, + { + "tcId": 219, + "comment": "Signature with special case values r=p and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", + "result": "invalid" + }, + { + "tcId": 220, + "comment": "Signature with special case values r=p and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", + "result": "invalid" + }, + { + "tcId": 221, + "comment": "Signature with special case values r=p and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", + "result": "invalid" + }, + { + "tcId": 222, + "comment": "Signature with special case values r=p and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", + "result": "invalid" + }, + { + "tcId": 223, + "comment": "Signature with special case values r=p and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff022100ffffffff00000001000000000000000000000001000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 224, + "comment": "Signature with special case values r=p + 1 and s=0", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000001000000000000000000000001000000000000000000000000020100", + "result": "invalid" + }, + { + "tcId": 225, + "comment": "Signature with special case values r=p + 1 and s=1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000001000000000000000000000001000000000000000000000000020101", + "result": "invalid" + }, + { + "tcId": 226, + "comment": "Signature with special case values r=p + 1 and s=-1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff000000010000000000000000000000010000000000000000000000000201ff", + "result": "invalid" + }, + { + "tcId": 227, + "comment": "Signature with special case values r=p + 1 and s=n", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000001000000000000000000000001000000000000000000000000022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", + "result": "invalid" + }, + { + "tcId": 228, + "comment": "Signature with special case values r=p + 1 and s=n - 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000001000000000000000000000001000000000000000000000000022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550", + "result": "invalid" + }, + { + "tcId": 229, + "comment": "Signature with special case values r=p + 1 and s=n + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000001000000000000000000000001000000000000000000000000022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552", + "result": "invalid" + }, + { + "tcId": 230, + "comment": "Signature with special case values r=p + 1 and s=p", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000001000000000000000000000001000000000000000000000000022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", + "result": "invalid" + }, + { + "tcId": 231, + "comment": "Signature with special case values r=p + 1 and s=p + 1", + "flags": [ + "InvalidSignature" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000001000000000000000000000001000000000000000000000000022100ffffffff00000001000000000000000000000001000000000000000000000000", + "result": "invalid" + }, + { + "tcId": 232, + "comment": "Signature encoding contains incorrect types: r=0, s=0.25", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3008020100090380fe01", + "result": "invalid" + }, + { + "tcId": 233, + "comment": "Signature encoding contains incorrect types: r=0, s=nan", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3006020100090142", + "result": "invalid" + }, + { + "tcId": 234, + "comment": "Signature encoding contains incorrect types: r=0, s=True", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3006020100010101", + "result": "invalid" + }, + { + "tcId": 235, + "comment": "Signature encoding contains incorrect types: r=0, s=False", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3006020100010100", + "result": "invalid" + }, + { + "tcId": 236, + "comment": "Signature encoding contains incorrect types: r=0, s=Null", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30050201000500", + "result": "invalid" + }, + { + "tcId": 237, + "comment": "Signature encoding contains incorrect types: r=0, s=empyt UTF-8 string", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30050201000c00", + "result": "invalid" + }, + { + "tcId": 238, + "comment": "Signature encoding contains incorrect types: r=0, s=\"0\"", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30060201000c0130", + "result": "invalid" + }, + { + "tcId": 239, + "comment": "Signature encoding contains incorrect types: r=0, s=empty list", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30050201003000", + "result": "invalid" + }, + { + "tcId": 240, + "comment": "Signature encoding contains incorrect types: r=0, s=list containing 0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30080201003003020100", + "result": "invalid" + }, + { + "tcId": 241, + "comment": "Signature encoding contains incorrect types: r=1, s=0.25", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3008020101090380fe01", + "result": "invalid" + }, + { + "tcId": 242, + "comment": "Signature encoding contains incorrect types: r=1, s=nan", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3006020101090142", + "result": "invalid" + }, + { + "tcId": 243, + "comment": "Signature encoding contains incorrect types: r=1, s=True", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3006020101010101", + "result": "invalid" + }, + { + "tcId": 244, + "comment": "Signature encoding contains incorrect types: r=1, s=False", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3006020101010100", + "result": "invalid" + }, + { + "tcId": 245, + "comment": "Signature encoding contains incorrect types: r=1, s=Null", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30050201010500", + "result": "invalid" + }, + { + "tcId": 246, + "comment": "Signature encoding contains incorrect types: r=1, s=empyt UTF-8 string", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30050201010c00", + "result": "invalid" + }, + { + "tcId": 247, + "comment": "Signature encoding contains incorrect types: r=1, s=\"0\"", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30060201010c0130", + "result": "invalid" + }, + { + "tcId": 248, + "comment": "Signature encoding contains incorrect types: r=1, s=empty list", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30050201013000", + "result": "invalid" + }, + { + "tcId": 249, + "comment": "Signature encoding contains incorrect types: r=1, s=list containing 0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30080201013003020100", + "result": "invalid" + }, + { + "tcId": 250, + "comment": "Signature encoding contains incorrect types: r=-1, s=0.25", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30080201ff090380fe01", + "result": "invalid" + }, + { + "tcId": 251, + "comment": "Signature encoding contains incorrect types: r=-1, s=nan", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30060201ff090142", + "result": "invalid" + }, + { + "tcId": 252, + "comment": "Signature encoding contains incorrect types: r=-1, s=True", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30060201ff010101", + "result": "invalid" + }, + { + "tcId": 253, + "comment": "Signature encoding contains incorrect types: r=-1, s=False", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30060201ff010100", + "result": "invalid" + }, + { + "tcId": 254, + "comment": "Signature encoding contains incorrect types: r=-1, s=Null", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30050201ff0500", + "result": "invalid" + }, + { + "tcId": 255, + "comment": "Signature encoding contains incorrect types: r=-1, s=empyt UTF-8 string", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30050201ff0c00", + "result": "invalid" + }, + { + "tcId": 256, + "comment": "Signature encoding contains incorrect types: r=-1, s=\"0\"", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30060201ff0c0130", + "result": "invalid" + }, + { + "tcId": 257, + "comment": "Signature encoding contains incorrect types: r=-1, s=empty list", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30050201ff3000", + "result": "invalid" + }, + { + "tcId": 258, + "comment": "Signature encoding contains incorrect types: r=-1, s=list containing 0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30080201ff3003020100", + "result": "invalid" + }, + { + "tcId": 259, + "comment": "Signature encoding contains incorrect types: r=n, s=0.25", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3028022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551090380fe01", + "result": "invalid" + }, + { + "tcId": 260, + "comment": "Signature encoding contains incorrect types: r=n, s=nan", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551090142", + "result": "invalid" + }, + { + "tcId": 261, + "comment": "Signature encoding contains incorrect types: r=n, s=True", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551010101", + "result": "invalid" + }, + { + "tcId": 262, + "comment": "Signature encoding contains incorrect types: r=n, s=False", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551010100", + "result": "invalid" + }, + { + "tcId": 263, + "comment": "Signature encoding contains incorrect types: r=n, s=Null", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3025022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325510500", + "result": "invalid" + }, + { + "tcId": 264, + "comment": "Signature encoding contains incorrect types: r=n, s=empyt UTF-8 string", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3025022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325510c00", + "result": "invalid" + }, + { + "tcId": 265, + "comment": "Signature encoding contains incorrect types: r=n, s=\"0\"", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325510c0130", + "result": "invalid" + }, + { + "tcId": 266, + "comment": "Signature encoding contains incorrect types: r=n, s=empty list", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3025022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325513000", + "result": "invalid" + }, + { + "tcId": 267, + "comment": "Signature encoding contains incorrect types: r=n, s=list containing 0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3028022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6325513003020100", + "result": "invalid" + }, + { + "tcId": 268, + "comment": "Signature encoding contains incorrect types: r=p, s=0.25", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3028022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff090380fe01", + "result": "invalid" + }, + { + "tcId": 269, + "comment": "Signature encoding contains incorrect types: r=p, s=nan", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff090142", + "result": "invalid" + }, + { + "tcId": 270, + "comment": "Signature encoding contains incorrect types: r=p, s=True", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff010101", + "result": "invalid" + }, + { + "tcId": 271, + "comment": "Signature encoding contains incorrect types: r=p, s=False", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff010100", + "result": "invalid" + }, + { + "tcId": 272, + "comment": "Signature encoding contains incorrect types: r=p, s=Null", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3025022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff0500", + "result": "invalid" + }, + { + "tcId": 273, + "comment": "Signature encoding contains incorrect types: r=p, s=empyt UTF-8 string", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3025022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff0c00", + "result": "invalid" + }, + { + "tcId": 274, + "comment": "Signature encoding contains incorrect types: r=p, s=\"0\"", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff0c0130", + "result": "invalid" + }, + { + "tcId": 275, + "comment": "Signature encoding contains incorrect types: r=p, s=empty list", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3025022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff3000", + "result": "invalid" + }, + { + "tcId": 276, + "comment": "Signature encoding contains incorrect types: r=p, s=list containing 0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3028022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff3003020100", + "result": "invalid" + }, + { + "tcId": 277, + "comment": "Signature encoding contains incorrect types: r=0.25, s=0.25", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "300a090380fe01090380fe01", + "result": "invalid" + }, + { + "tcId": 278, + "comment": "Signature encoding contains incorrect types: r=nan, s=nan", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3006090142090142", + "result": "invalid" + }, + { + "tcId": 279, + "comment": "Signature encoding contains incorrect types: r=True, s=True", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3006010101010101", + "result": "invalid" + }, + { + "tcId": 280, + "comment": "Signature encoding contains incorrect types: r=False, s=False", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3006010100010100", + "result": "invalid" + }, + { + "tcId": 281, + "comment": "Signature encoding contains incorrect types: r=Null, s=Null", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "300405000500", + "result": "invalid" + }, + { + "tcId": 282, + "comment": "Signature encoding contains incorrect types: r=empyt UTF-8 string, s=empyt UTF-8 string", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30040c000c00", + "result": "invalid" + }, + { + "tcId": 283, + "comment": "Signature encoding contains incorrect types: r=\"0\", s=\"0\"", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30060c01300c0130", + "result": "invalid" + }, + { + "tcId": 284, + "comment": "Signature encoding contains incorrect types: r=empty list, s=empty list", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "300430003000", + "result": "invalid" + }, + { + "tcId": 285, + "comment": "Signature encoding contains incorrect types: r=list containing 0, s=list containing 0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "300a30030201003003020100", + "result": "invalid" + }, + { + "tcId": 286, + "comment": "Signature encoding contains incorrect types: r=0.25, s=0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3008090380fe01020100", + "result": "invalid" + }, + { + "tcId": 287, + "comment": "Signature encoding contains incorrect types: r=nan, s=0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3006090142020100", + "result": "invalid" + }, + { + "tcId": 288, + "comment": "Signature encoding contains incorrect types: r=True, s=0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3006010101020100", + "result": "invalid" + }, + { + "tcId": 289, + "comment": "Signature encoding contains incorrect types: r=False, s=0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "3006010100020100", + "result": "invalid" + }, + { + "tcId": 290, + "comment": "Signature encoding contains incorrect types: r=Null, s=0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30050500020100", + "result": "invalid" + }, + { + "tcId": 291, + "comment": "Signature encoding contains incorrect types: r=empyt UTF-8 string, s=0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30050c00020100", + "result": "invalid" + }, + { + "tcId": 292, + "comment": "Signature encoding contains incorrect types: r=\"0\", s=0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30060c0130020100", + "result": "invalid" + }, + { + "tcId": 293, + "comment": "Signature encoding contains incorrect types: r=empty list, s=0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30053000020100", + "result": "invalid" + }, + { + "tcId": 294, + "comment": "Signature encoding contains incorrect types: r=list containing 0, s=0", + "flags": [ + "InvalidTypesInSignature" + ], + "msg": "313233343030", + "sig": "30083003020100020100", + "result": "invalid" + }, + { + "tcId": 295, + "comment": "Edge case for Shamir multiplication", + "flags": [ + "EdgeCaseShamirMultiplication" + ], + "msg": "3639383139", + "sig": "3044022064a1aab5000d0e804f3e2fc02bdee9be8ff312334e2ba16d11547c97711c898e02206af015971cc30be6d1a206d4e013e0997772a2f91d73286ffd683b9bb2cf4f1b", + "result": "valid" + }, + { + "tcId": 296, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "343236343739373234", + "sig": "3044022016aea964a2f6506d6f78c81c91fc7e8bded7d397738448de1e19a0ec580bf2660220252cd762130c6667cfe8b7bc47d27d78391e8e80c578d1cd38c3ff033be928e9", + "result": "valid" + }, + { + "tcId": 297, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "37313338363834383931", + "sig": "30450221009cc98be2347d469bf476dfc26b9b733df2d26d6ef524af917c665baccb23c8820220093496459effe2d8d70727b82462f61d0ec1b7847929d10ea631dacb16b56c32", + "result": "valid" + }, + { + "tcId": 298, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3130333539333331363638", + "sig": "3044022073b3c90ecd390028058164524dde892703dce3dea0d53fa8093999f07ab8aa4302202f67b0b8e20636695bb7d8bf0a651c802ed25a395387b5f4188c0c4075c88634", + "result": "valid" + }, + { + "tcId": 299, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33393439343031323135", + "sig": "3046022100bfab3098252847b328fadf2f89b95c851a7f0eb390763378f37e90119d5ba3dd022100bdd64e234e832b1067c2d058ccb44d978195ccebb65c2aaf1e2da9b8b4987e3b", + "result": "valid" + }, + { + "tcId": 300, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31333434323933303739", + "sig": "30440220204a9784074b246d8bf8bf04a4ceb1c1f1c9aaab168b1596d17093c5cd21d2cd022051cce41670636783dc06a759c8847868a406c2506fe17975582fe648d1d88b52", + "result": "valid" + }, + { + "tcId": 301, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33373036323131373132", + "sig": "3046022100ed66dc34f551ac82f63d4aa4f81fe2cb0031a91d1314f835027bca0f1ceeaa0302210099ca123aa09b13cd194a422e18d5fda167623c3f6e5d4d6abb8953d67c0c48c7", + "result": "valid" + }, + { + "tcId": 302, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "333433363838373132", + "sig": "30450220060b700bef665c68899d44f2356a578d126b062023ccc3c056bf0f60a237012b0221008d186c027832965f4fcc78a3366ca95dedbb410cbef3f26d6be5d581c11d3610", + "result": "valid" + }, + { + "tcId": 303, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31333531353330333730", + "sig": "30460221009f6adfe8d5eb5b2c24d7aa7934b6cf29c93ea76cd313c9132bb0c8e38c96831d022100b26a9c9e40e55ee0890c944cf271756c906a33e66b5bd15e051593883b5e9902", + "result": "valid" + }, + { + "tcId": 304, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "36353533323033313236", + "sig": "3045022100a1af03ca91677b673ad2f33615e56174a1abf6da168cebfa8868f4ba273f16b7022020aa73ffe48afa6435cd258b173d0c2377d69022e7d098d75caf24c8c5e06b1c", + "result": "valid" + }, + { + "tcId": 305, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31353634333436363033", + "sig": "3045022100fdc70602766f8eed11a6c99a71c973d5659355507b843da6e327a28c11893db902203df5349688a085b137b1eacf456a9e9e0f6d15ec0078ca60a7f83f2b10d21350", + "result": "valid" + }, + { + "tcId": 306, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "34343239353339313137", + "sig": "3046022100b516a314f2fce530d6537f6a6c49966c23456f63c643cf8e0dc738f7b876e675022100d39ffd033c92b6d717dd536fbc5efdf1967c4bd80954479ba66b0120cd16fff2", + "result": "valid" + }, + { + "tcId": 307, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3130393533323631333531", + "sig": "304402203b2cbf046eac45842ecb7984d475831582717bebb6492fd0a485c101e29ff0a802204c9b7b47a98b0f82de512bc9313aaf51701099cac5f76e68c8595fc1c1d99258", + "result": "valid" + }, + { + "tcId": 308, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "35393837333530303431", + "sig": "3044022030c87d35e636f540841f14af54e2f9edd79d0312cfa1ab656c3fb15bfde48dcf022047c15a5a82d24b75c85a692bd6ecafeb71409ede23efd08e0db9abf6340677ed", + "result": "valid" + }, + { + "tcId": 309, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33343633303036383738", + "sig": "3044022038686ff0fda2cef6bc43b58cfe6647b9e2e8176d168dec3c68ff262113760f520220067ec3b651f422669601662167fa8717e976e2db5e6a4cf7c2ddabb3fde9d67d", + "result": "valid" + }, + { + "tcId": 310, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "39383137333230323837", + "sig": "3044022044a3e23bf314f2b344fc25c7f2de8b6af3e17d27f5ee844b225985ab6e2775cf02202d48e223205e98041ddc87be532abed584f0411f5729500493c9cc3f4dd15e86", + "result": "valid" + }, + { + "tcId": 311, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33323232303431303436", + "sig": "304402202ded5b7ec8e90e7bf11f967a3d95110c41b99db3b5aa8d330eb9d638781688e902207d5792c53628155e1bfc46fb1a67e3088de049c328ae1f44ec69238a009808f9", + "result": "valid" + }, + { + "tcId": 312, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "36363636333037313034", + "sig": "3046022100bdae7bcb580bf335efd3bc3d31870f923eaccafcd40ec2f605976f15137d8b8f022100f6dfa12f19e525270b0106eecfe257499f373a4fb318994f24838122ce7ec3c7", + "result": "valid" + }, + { + "tcId": 313, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31303335393531383938", + "sig": "3045022050f9c4f0cd6940e162720957ffff513799209b78596956d21ece251c2401f1c6022100d7033a0a787d338e889defaaabb106b95a4355e411a59c32aa5167dfab244726", + "result": "valid" + }, + { + "tcId": 314, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31383436353937313935", + "sig": "3045022100f612820687604fa01906066a378d67540982e29575d019aabe90924ead5c860d02203f9367702dd7dd4f75ea98afd20e328a1a99f4857b316525328230ce294b0fef", + "result": "valid" + }, + { + "tcId": 315, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33313336303436313839", + "sig": "30460221009505e407657d6e8bc93db5da7aa6f5081f61980c1949f56b0f2f507da5782a7a022100c60d31904e3669738ffbeccab6c3656c08e0ed5cb92b3cfa5e7f71784f9c5021", + "result": "valid" + }, + { + "tcId": 316, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "32363633373834323534", + "sig": "3046022100bbd16fbbb656b6d0d83e6a7787cd691b08735aed371732723e1c68a40404517d0221009d8e35dba96028b7787d91315be675877d2d097be5e8ee34560e3e7fd25c0f00", + "result": "valid" + }, + { + "tcId": 317, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31363532313030353234", + "sig": "304402202ec9760122db98fd06ea76848d35a6da442d2ceef7559a30cf57c61e92df327e02207ab271da90859479701fccf86e462ee3393fb6814c27b760c4963625c0a19878", + "result": "valid" + }, + { + "tcId": 318, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "35373438303831363936", + "sig": "3044022054e76b7683b6650baa6a7fc49b1c51eed9ba9dd463221f7a4f1005a89fe00c5902202ea076886c773eb937ec1cc8374b7915cfd11b1c1ae1166152f2f7806a31c8fd", + "result": "valid" + }, + { + "tcId": 319, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "36333433393133343638", + "sig": "304402205291deaf24659ffbbce6e3c26f6021097a74abdbb69be4fb10419c0c496c9466022065d6fcf336d27cc7cdb982bb4e4ecef5827f84742f29f10abf83469270a03dc3", + "result": "valid" + }, + { + "tcId": 320, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31353431313033353938", + "sig": "30450220207a3241812d75d947419dc58efb05e8003b33fc17eb50f9d15166a88479f107022100cdee749f2e492b213ce80b32d0574f62f1c5d70793cf55e382d5caadf7592767", + "result": "valid" + }, + { + "tcId": 321, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3130343738353830313238", + "sig": "304502206554e49f82a855204328ac94913bf01bbe84437a355a0a37c0dee3cf81aa7728022100aea00de2507ddaf5c94e1e126980d3df16250a2eaebc8be486effe7f22b4f929", + "result": "valid" + }, + { + "tcId": 322, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3130353336323835353638", + "sig": "3046022100a54c5062648339d2bff06f71c88216c26c6e19b4d80a8c602990ac82707efdfc022100e99bbe7fcfafae3e69fd016777517aa01056317f467ad09aff09be73c9731b0d", + "result": "valid" + }, + { + "tcId": 323, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "393533393034313035", + "sig": "3045022100975bd7157a8d363b309f1f444012b1a1d23096593133e71b4ca8b059cff37eaf02207faa7a28b1c822baa241793f2abc930bd4c69840fe090f2aacc46786bf919622", + "result": "valid" + }, + { + "tcId": 324, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "393738383438303339", + "sig": "304402205694a6f84b8f875c276afd2ebcfe4d61de9ec90305afb1357b95b3e0da43885e02200dffad9ffd0b757d8051dec02ebdf70d8ee2dc5c7870c0823b6ccc7c679cbaa4", + "result": "valid" + }, + { + "tcId": 325, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33363130363732343432", + "sig": "3045022100a0c30e8026fdb2b4b4968a27d16a6d08f7098f1a98d21620d7454ba9790f1ba602205e470453a8a399f15baf463f9deceb53acc5ca64459149688bd2760c65424339", + "result": "valid" + }, + { + "tcId": 326, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31303534323430373035", + "sig": "30440220614ea84acf736527dd73602cd4bb4eea1dfebebd5ad8aca52aa0228cf7b99a880220737cc85f5f2d2f60d1b8183f3ed490e4de14368e96a9482c2a4dd193195c902f", + "result": "valid" + }, + { + "tcId": 327, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "35313734343438313937", + "sig": "3045022100bead6734ebe44b810d3fb2ea00b1732945377338febfd439a8d74dfbd0f942fa02206bb18eae36616a7d3cad35919fd21a8af4bbe7a10f73b3e036a46b103ef56e2a", + "result": "valid" + }, + { + "tcId": 328, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31393637353631323531", + "sig": "30440220499625479e161dacd4db9d9ce64854c98d922cbf212703e9654fae182df9bad2022042c177cf37b8193a0131108d97819edd9439936028864ac195b64fca76d9d693", + "result": "valid" + }, + { + "tcId": 329, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33343437323533333433", + "sig": "3045022008f16b8093a8fb4d66a2c8065b541b3d31e3bfe694f6b89c50fb1aaa6ff6c9b20221009d6455e2d5d1779748573b611cb95d4a21f967410399b39b535ba3e5af81ca2e", + "result": "valid" + }, + { + "tcId": 330, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "333638323634333138", + "sig": "3046022100be26231b6191658a19dd72ddb99ed8f8c579b6938d19bce8eed8dc2b338cb5f8022100e1d9a32ee56cffed37f0f22b2dcb57d5c943c14f79694a03b9c5e96952575c89", + "result": "valid" + }, + { + "tcId": 331, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33323631313938363038", + "sig": "3045022015e76880898316b16204ac920a02d58045f36a229d4aa4f812638c455abe0443022100e74d357d3fcb5c8c5337bd6aba4178b455ca10e226e13f9638196506a1939123", + "result": "valid" + }, + { + "tcId": 332, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "39363738373831303934", + "sig": "30440220352ecb53f8df2c503a45f9846fc28d1d31e6307d3ddbffc1132315cc07f16dad02201348dfa9c482c558e1d05c5242ca1c39436726ecd28258b1899792887dd0a3c6", + "result": "valid" + }, + { + "tcId": 333, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "34393538383233383233", + "sig": "304402204a40801a7e606ba78a0da9882ab23c7677b8642349ed3d652c5bfa5f2a9558fb02203a49b64848d682ef7f605f2832f7384bdc24ed2925825bf8ea77dc5981725782", + "result": "valid" + }, + { + "tcId": 334, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "383234363337383337", + "sig": "3045022100eacc5e1a8304a74d2be412b078924b3bb3511bac855c05c9e5e9e44df3d61e9602207451cd8e18d6ed1885dd827714847f96ec4bb0ed4c36ce9808db8f714204f6d1", + "result": "valid" + }, + { + "tcId": 335, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3131303230383333373736", + "sig": "304502202f7a5e9e5771d424f30f67fdab61e8ce4f8cd1214882adb65f7de94c31577052022100ac4e69808345809b44acb0b2bd889175fb75dd050c5a449ab9528f8f78daa10c", + "result": "valid" + }, + { + "tcId": 336, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "313333383731363438", + "sig": "3045022100ffcda40f792ce4d93e7e0f0e95e1a2147dddd7f6487621c30a03d710b3300219022079938b55f8a17f7ed7ba9ade8f2065a1fa77618f0b67add8d58c422c2453a49a", + "result": "valid" + }, + { + "tcId": 337, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "333232313434313632", + "sig": "304602210081f2359c4faba6b53d3e8c8c3fcc16a948350f7ab3a588b28c17603a431e39a8022100cd6f6a5cc3b55ead0ff695d06c6860b509e46d99fccefb9f7f9e101857f74300", + "result": "valid" + }, + { + "tcId": 338, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3130363836363535353436", + "sig": "3045022100dfc8bf520445cbb8ee1596fb073ea283ea130251a6fdffa5c3f5f2aaf75ca8080220048e33efce147c9dd92823640e338e68bfd7d0dc7a4905b3a7ac711e577e90e7", + "result": "valid" + }, + { + "tcId": 339, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "3632313535323436", + "sig": "3046022100ad019f74c6941d20efda70b46c53db166503a0e393e932f688227688ba6a576202210093320eb7ca0710255346bdbb3102cdcf7964ef2e0988e712bc05efe16c199345", + "result": "valid" + }, + { + "tcId": 340, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "37303330383138373734", + "sig": "3046022100ac8096842e8add68c34e78ce11dd71e4b54316bd3ebf7fffdeb7bd5a3ebc1883022100f5ca2f4f23d674502d4caf85d187215d36e3ce9f0ce219709f21a3aac003b7a8", + "result": "valid" + }, + { + "tcId": 341, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "35393234353233373434", + "sig": "30440220677b2d3a59b18a5ff939b70ea002250889ddcd7b7b9d776854b4943693fb92f702206b4ba856ade7677bf30307b21f3ccda35d2f63aee81efd0bab6972cc0795db55", + "result": "valid" + }, + { + "tcId": 342, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31343935353836363231", + "sig": "30450220479e1ded14bcaed0379ba8e1b73d3115d84d31d4b7c30e1f05e1fc0d5957cfb0022100918f79e35b3d89487cf634a4f05b2e0c30857ca879f97c771e877027355b2443", + "result": "valid" + }, + { + "tcId": 343, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "34303035333134343036", + "sig": "3044022043dfccd0edb9e280d9a58f01164d55c3d711e14b12ac5cf3b64840ead512a0a302201dbe33fa8ba84533cd5c4934365b3442ca1174899b78ef9a3199f49584389772", + "result": "valid" + }, + { + "tcId": 344, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "33303936343537353132", + "sig": "304402205b09ab637bd4caf0f4c7c7e4bca592fea20e9087c259d26a38bb4085f0bbff11022045b7eb467b6748af618e9d80d6fdcd6aa24964e5a13f885bca8101de08eb0d75", + "result": "valid" + }, + { + "tcId": 345, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "32373834303235363230", + "sig": "304502205e9b1c5a028070df5728c5c8af9b74e0667afa570a6cfa0114a5039ed15ee06f022100b1360907e2d9785ead362bb8d7bd661b6c29eeffd3c5037744edaeb9ad990c20", + "result": "valid" + }, + { + "tcId": 346, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "32363138373837343138", + "sig": "304502200671a0a85c2b72d54a2fb0990e34538b4890050f5a5712f6d1a7a5fb8578f32e022100db1846bab6b7361479ab9c3285ca41291808f27fd5bd4fdac720e5854713694c", + "result": "valid" + }, + { + "tcId": 347, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "31363432363235323632", + "sig": "304402207673f8526748446477dbbb0590a45492c5d7d69859d301abbaedb35b2095103a02203dc70ddf9c6b524d886bed9e6af02e0e4dec0d417a414fed3807ef4422913d7c", + "result": "valid" + }, + { + "tcId": 348, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "36383234313839343336", + "sig": "304402207f085441070ecd2bb21285089ebb1aa6450d1a06c36d3ff39dfd657a796d12b50220249712012029870a2459d18d47da9aa492a5e6cb4b2d8dafa9e4c5c54a2b9a8b", + "result": "valid" + }, + { + "tcId": 349, + "comment": "special case hash", + "flags": [ + "SpecialCaseHash" + ], + "msg": "343834323435343235", + "sig": "3046022100914c67fb61dd1e27c867398ea7322d5ab76df04bc5aa6683a8e0f30a5d287348022100fa07474031481dda4953e3ac1959ee8cea7e66ec412b38d6c96d28f6d37304ea", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "040ad99500288d466940031d72a9f5445a4d43784640855bf0a69874d2de5fe103c5011e6ef2c42dcd50d5d3d29f99ae6eba2c80c9244f4c5422f0979ff0c3ba5e", + "wx": "0ad99500288d466940031d72a9f5445a4d43784640855bf0a69874d2de5fe103", + "wy": "00c5011e6ef2c42dcd50d5d3d29f99ae6eba2c80c9244f4c5422f0979ff0c3ba5e" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200040ad99500288d466940031d72a9f5445a4d43784640855bf0a69874d2de5fe103c5011e6ef2c42dcd50d5d3d29f99ae6eba2c80c9244f4c5422f0979ff0c3ba5e", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECtmVACiNRmlAAx1yqfVEWk1DeEZA\nhVvwpph00t5f4QPFAR5u8sQtzVDV09Kfma5uuiyAySRPTFQi8Jef8MO6Xg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 350, + "comment": "k*G has a large x-coordinate", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "303502104319055358e8617b0c46353d039cdaab022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", + "result": "valid" + }, + { + "tcId": 351, + "comment": "r too large", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000001000000000000000000000000fffffffffffffffffffffffc022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04ab05fd9d0de26b9ce6f4819652d9fc69193d0aa398f0fba8013e09c58220455419235271228c786759095d12b75af0692dd4103f19f6a8c32f49435a1e9b8d45", + "wx": "00ab05fd9d0de26b9ce6f4819652d9fc69193d0aa398f0fba8013e09c582204554", + "wy": "19235271228c786759095d12b75af0692dd4103f19f6a8c32f49435a1e9b8d45" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004ab05fd9d0de26b9ce6f4819652d9fc69193d0aa398f0fba8013e09c58220455419235271228c786759095d12b75af0692dd4103f19f6a8c32f49435a1e9b8d45", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqwX9nQ3ia5zm9IGWUtn8aRk9CqOY\n8PuoAT4JxYIgRVQZI1JxIox4Z1kJXRK3WvBpLdQQPxn2qMMvSUNaHpuNRQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 352, + "comment": "r,s are large", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254f022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0480984f39a1ff38a86a68aa4201b6be5dfbfecf876219710b07badf6fdd4c6c5611feb97390d9826e7a06dfb41871c940d74415ed3cac2089f1445019bb55ed95", + "wx": "0080984f39a1ff38a86a68aa4201b6be5dfbfecf876219710b07badf6fdd4c6c56", + "wy": "11feb97390d9826e7a06dfb41871c940d74415ed3cac2089f1445019bb55ed95" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000480984f39a1ff38a86a68aa4201b6be5dfbfecf876219710b07badf6fdd4c6c5611feb97390d9826e7a06dfb41871c940d74415ed3cac2089f1445019bb55ed95", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgJhPOaH/OKhqaKpCAba+Xfv+z4di\nGXELB7rfb91MbFYR/rlzkNmCbnoG37QYcclA10QV7TysIInxRFAZu1XtlQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 353, + "comment": "r and s^-1 have a large Hamming weight", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100909135bdb6799286170f5ead2de4f6511453fe50914f3df2de54a36383df8dd4", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "044201b4272944201c3294f5baa9a3232b6dd687495fcc19a70a95bc602b4f7c0595c37eba9ee8171c1bb5ac6feaf753bc36f463e3aef16629572c0c0a8fb0800e", + "wx": "4201b4272944201c3294f5baa9a3232b6dd687495fcc19a70a95bc602b4f7c05", + "wy": "0095c37eba9ee8171c1bb5ac6feaf753bc36f463e3aef16629572c0c0a8fb0800e" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200044201b4272944201c3294f5baa9a3232b6dd687495fcc19a70a95bc602b4f7c0595c37eba9ee8171c1bb5ac6feaf753bc36f463e3aef16629572c0c0a8fb0800e", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQgG0JylEIBwylPW6qaMjK23Wh0lf\nzBmnCpW8YCtPfAWVw366nugXHBu1rG/q91O8NvRj467xZilXLAwKj7CADg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 354, + "comment": "r and s^-1 have a large Hamming weight", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022027b4577ca009376f71303fd5dd227dcef5deb773ad5f5a84360644669ca249a5", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04a71af64de5126a4a4e02b7922d66ce9415ce88a4c9d25514d91082c8725ac9575d47723c8fbe580bb369fec9c2665d8e30a435b9932645482e7c9f11e872296b", + "wx": "00a71af64de5126a4a4e02b7922d66ce9415ce88a4c9d25514d91082c8725ac957", + "wy": "5d47723c8fbe580bb369fec9c2665d8e30a435b9932645482e7c9f11e872296b" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004a71af64de5126a4a4e02b7922d66ce9415ce88a4c9d25514d91082c8725ac9575d47723c8fbe580bb369fec9c2665d8e30a435b9932645482e7c9f11e872296b", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpxr2TeUSakpOAreSLWbOlBXOiKTJ\n0lUU2RCCyHJayVddR3I8j75YC7Np/snCZl2OMKQ1uZMmRUgufJ8R6HIpaw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 355, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3006020105020101", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "046627cec4f0731ea23fc2931f90ebe5b7572f597d20df08fc2b31ee8ef16b15726170ed77d8d0a14fc5c9c3c4c9be7f0d3ee18f709bb275eaf2073e258fe694a5", + "wx": "6627cec4f0731ea23fc2931f90ebe5b7572f597d20df08fc2b31ee8ef16b1572", + "wy": "6170ed77d8d0a14fc5c9c3c4c9be7f0d3ee18f709bb275eaf2073e258fe694a5" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200046627cec4f0731ea23fc2931f90ebe5b7572f597d20df08fc2b31ee8ef16b15726170ed77d8d0a14fc5c9c3c4c9be7f0d3ee18f709bb275eaf2073e258fe694a5", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZifOxPBzHqI/wpMfkOvlt1cvWX0g\n3wj8KzHujvFrFXJhcO132NChT8XJw8TJvn8NPuGPcJuyderyBz4lj+aUpQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 356, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3006020105020103", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "045a7c8825e85691cce1f5e7544c54e73f14afc010cb731343262ca7ec5a77f5bfef6edf62a4497c1bd7b147fb6c3d22af3c39bfce95f30e13a16d3d7b2812f813", + "wx": "5a7c8825e85691cce1f5e7544c54e73f14afc010cb731343262ca7ec5a77f5bf", + "wy": "00ef6edf62a4497c1bd7b147fb6c3d22af3c39bfce95f30e13a16d3d7b2812f813" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200045a7c8825e85691cce1f5e7544c54e73f14afc010cb731343262ca7ec5a77f5bfef6edf62a4497c1bd7b147fb6c3d22af3c39bfce95f30e13a16d3d7b2812f813", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWnyIJehWkczh9edUTFTnPxSvwBDL\ncxNDJiyn7Fp39b/vbt9ipEl8G9exR/tsPSKvPDm/zpXzDhOhbT17KBL4Ew==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 357, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3006020105020105", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04cbe0c29132cd738364fedd603152990c048e5e2fff996d883fa6caca7978c73770af6a8ce44cb41224b2603606f4c04d188e80bff7cc31ad5189d4ab0d70e8c1", + "wx": "00cbe0c29132cd738364fedd603152990c048e5e2fff996d883fa6caca7978c737", + "wy": "70af6a8ce44cb41224b2603606f4c04d188e80bff7cc31ad5189d4ab0d70e8c1" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004cbe0c29132cd738364fedd603152990c048e5e2fff996d883fa6caca7978c73770af6a8ce44cb41224b2603606f4c04d188e80bff7cc31ad5189d4ab0d70e8c1", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEy+DCkTLNc4Nk/t1gMVKZDASOXi//\nmW2IP6bKynl4xzdwr2qM5Ey0EiSyYDYG9MBNGI6Av/fMMa1RidSrDXDowQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 358, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3006020105020106", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "042ef747671c97d9c7f9cb2f6a30d678c3d84757ba241ef7183d51a29f52d87c2ea8fb2ea635b761baefc1c4ded2099281b844e13e044c328553bbbafa337d8a76", + "wx": "2ef747671c97d9c7f9cb2f6a30d678c3d84757ba241ef7183d51a29f52d87c2e", + "wy": "00a8fb2ea635b761baefc1c4ded2099281b844e13e044c328553bbbafa337d8a76" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200042ef747671c97d9c7f9cb2f6a30d678c3d84757ba241ef7183d51a29f52d87c2ea8fb2ea635b761baefc1c4ded2099281b844e13e044c328553bbbafa337d8a76", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELvdHZxyX2cf5yy9qMNZ4w9hHV7ok\nHvcYPVGin1LYfC6o+y6mNbdhuu/BxN7SCZKBuEThPgRMMoVTu7r6M32Kdg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 359, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3006020106020101", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04931cc49cda4d87d25b1601c56c3b83b4f45e44971998f2d3e7d3c55152214edf058dc140abbba42fc1ddbf30dab8eb9b46ee7338b3f7ee96242bf45e1df5e995", + "wx": "00931cc49cda4d87d25b1601c56c3b83b4f45e44971998f2d3e7d3c55152214edf", + "wy": "058dc140abbba42fc1ddbf30dab8eb9b46ee7338b3f7ee96242bf45e1df5e995" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004931cc49cda4d87d25b1601c56c3b83b4f45e44971998f2d3e7d3c55152214edf058dc140abbba42fc1ddbf30dab8eb9b46ee7338b3f7ee96242bf45e1df5e995", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkxzEnNpNh9JbFgHFbDuDtPReRJcZ\nmPLT59PFUVIhTt8FjcFAq7ukL8HdvzDauOubRu5zOLP37pYkK/ReHfXplQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 360, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3006020106020103", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04899a4af61867e3f3c190dbb48f8bc9fc74b70a467a4a1f06477b3af2f39ab8ed47ac000f9ea8a3034939bf48ad5d061a69fc8495ae4df2dbec7effa03a0062b3", + "wx": "00899a4af61867e3f3c190dbb48f8bc9fc74b70a467a4a1f06477b3af2f39ab8ed", + "wy": "47ac000f9ea8a3034939bf48ad5d061a69fc8495ae4df2dbec7effa03a0062b3" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004899a4af61867e3f3c190dbb48f8bc9fc74b70a467a4a1f06477b3af2f39ab8ed47ac000f9ea8a3034939bf48ad5d061a69fc8495ae4df2dbec7effa03a0062b3", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEiZpK9hhn4/PBkNu0j4vJ/HS3CkZ6\nSh8GR3s68vOauO1HrAAPnqijA0k5v0itXQYaafyEla5N8tvsfv+gOgBisw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 361, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3006020106020106", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04d03eb09913cc20c6a8d0070f0d8d2a7f63527fafa44117fce6bd1ef2aa4ae3c46d5df3f45ac58fa334c6d102381b3120b7a2455600dcaff3d1a845514f12bf46", + "wx": "00d03eb09913cc20c6a8d0070f0d8d2a7f63527fafa44117fce6bd1ef2aa4ae3c4", + "wy": "6d5df3f45ac58fa334c6d102381b3120b7a2455600dcaff3d1a845514f12bf46" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004d03eb09913cc20c6a8d0070f0d8d2a7f63527fafa44117fce6bd1ef2aa4ae3c46d5df3f45ac58fa334c6d102381b3120b7a2455600dcaff3d1a845514f12bf46", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0D6wmRPMIMao0AcPDY0qf2NSf6+k\nQRf85r0e8qpK48RtXfP0WsWPozTG0QI4GzEgt6JFVgDcr/PRqEVRTxK/Rg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 362, + "comment": "small r and s", + "flags": [ + "SmallRandS", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3006020106020107", + "result": "valid" + }, + { + "tcId": 363, + "comment": "r is larger than n", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3026022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632557020107", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "043a72476291571193b4d109b2c37b59f2807e8fe9cffd804eacded903e77ca0da592dbc74fee0ca7508cc7bc282b0c51a143286ff53c60131668e7a0929e4ed04", + "wx": "3a72476291571193b4d109b2c37b59f2807e8fe9cffd804eacded903e77ca0da", + "wy": "592dbc74fee0ca7508cc7bc282b0c51a143286ff53c60131668e7a0929e4ed04" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200043a72476291571193b4d109b2c37b59f2807e8fe9cffd804eacded903e77ca0da592dbc74fee0ca7508cc7bc282b0c51a143286ff53c60131668e7a0929e4ed04", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOnJHYpFXEZO00Qmyw3tZ8oB+j+nP\n/YBOrN7ZA+d8oNpZLbx0/uDKdQjMe8KCsMUaFDKG/1PGATFmjnoJKeTtBA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 364, + "comment": "s is larger than n", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3026020106022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc75fbd8", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04d0f73792203716afd4be4329faa48d269f15313ebbba379d7783c97bf3e890d9971f4a3206605bec21782bf5e275c714417e8f566549e6bc68690d2363c89cc1", + "wx": "00d0f73792203716afd4be4329faa48d269f15313ebbba379d7783c97bf3e890d9", + "wy": "00971f4a3206605bec21782bf5e275c714417e8f566549e6bc68690d2363c89cc1" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004d0f73792203716afd4be4329faa48d269f15313ebbba379d7783c97bf3e890d9971f4a3206605bec21782bf5e275c714417e8f566549e6bc68690d2363c89cc1", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0Pc3kiA3Fq/UvkMp+qSNJp8VMT67\nujedd4PJe/PokNmXH0oyBmBb7CF4K/XidccUQX6PVmVJ5rxoaQ0jY8icwQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 365, + "comment": "small r and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3027020201000221008f1e3c7862c58b16bb76eddbb76eddbb516af4f63f2d74d76e0d28c9bb75ea88", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "044838b2be35a6276a80ef9e228140f9d9b96ce83b7a254f71ccdebbb8054ce05ffa9cbc123c919b19e00238198d04069043bd660a828814051fcb8aac738a6c6b", + "wx": "4838b2be35a6276a80ef9e228140f9d9b96ce83b7a254f71ccdebbb8054ce05f", + "wy": "00fa9cbc123c919b19e00238198d04069043bd660a828814051fcb8aac738a6c6b" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200044838b2be35a6276a80ef9e228140f9d9b96ce83b7a254f71ccdebbb8054ce05ffa9cbc123c919b19e00238198d04069043bd660a828814051fcb8aac738a6c6b", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESDiyvjWmJ2qA754igUD52bls6Dt6\nJU9xzN67uAVM4F/6nLwSPJGbGeACOBmNBAaQQ71mCoKIFAUfy4qsc4psaw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 366, + "comment": "smallish r and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "302c02072d9b4d347952d6022100ef3043e7329581dbb3974497710ab11505ee1c87ff907beebadd195a0ffe6d7a", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "047393983ca30a520bbc4783dc9960746aab444ef520c0a8e771119aa4e74b0f64e9d7be1ab01a0bf626e709863e6a486dbaf32793afccf774e2c6cd27b1857526", + "wx": "7393983ca30a520bbc4783dc9960746aab444ef520c0a8e771119aa4e74b0f64", + "wy": "00e9d7be1ab01a0bf626e709863e6a486dbaf32793afccf774e2c6cd27b1857526" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200047393983ca30a520bbc4783dc9960746aab444ef520c0a8e771119aa4e74b0f64e9d7be1ab01a0bf626e709863e6a486dbaf32793afccf774e2c6cd27b1857526", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc5OYPKMKUgu8R4PcmWB0aqtETvUg\nwKjncRGapOdLD2Tp174asBoL9ibnCYY+akhtuvMnk6/M93Tixs0nsYV1Jg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 367, + "comment": "100-bit r and small s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3032020d1033e67e37b32b445580bf4eff0221008b748b74000000008b748b748b748b7466e769ad4a16d3dcd87129b8e91d1b4d", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "045ac331a1103fe966697379f356a937f350588a05477e308851b8a502d5dfcdc5fe9993df4b57939b2b8da095bf6d794265204cfe03be995a02e65d408c871c0b", + "wx": "5ac331a1103fe966697379f356a937f350588a05477e308851b8a502d5dfcdc5", + "wy": "00fe9993df4b57939b2b8da095bf6d794265204cfe03be995a02e65d408c871c0b" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200045ac331a1103fe966697379f356a937f350588a05477e308851b8a502d5dfcdc5fe9993df4b57939b2b8da095bf6d794265204cfe03be995a02e65d408c871c0b", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWsMxoRA/6WZpc3nzVqk381BYigVH\nfjCIUbilAtXfzcX+mZPfS1eTmyuNoJW/bXlCZSBM/gO+mVoC5l1AjIccCw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 368, + "comment": "small r and 100 bit s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "302702020100022100ef9f6ba4d97c09d03178fa20b4aaad83be3cf9cb824a879fec3270fc4b81ef5b", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "041d209be8de2de877095a399d3904c74cc458d926e27bb8e58e5eae5767c41509dd59e04c214f7b18dce351fc2a549893a6860e80163f38cc60a4f2c9d040d8c9", + "wx": "1d209be8de2de877095a399d3904c74cc458d926e27bb8e58e5eae5767c41509", + "wy": "00dd59e04c214f7b18dce351fc2a549893a6860e80163f38cc60a4f2c9d040d8c9" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200041d209be8de2de877095a399d3904c74cc458d926e27bb8e58e5eae5767c41509dd59e04c214f7b18dce351fc2a549893a6860e80163f38cc60a4f2c9d040d8c9", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHSCb6N4t6HcJWjmdOQTHTMRY2Sbi\ne7jljl6uV2fEFQndWeBMIU97GNzjUfwqVJiTpoYOgBY/OMxgpPLJ0EDYyQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 369, + "comment": "100-bit r and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3032020d062522bbd3ecbe7c39e93e7c25022100ef9f6ba4d97c09d03178fa20b4aaad83be3cf9cb824a879fec3270fc4b81ef5b", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04083539fbee44625e3acaafa2fcb41349392cef0633a1b8fabecee0c133b10e99915c1ebe7bf00df8535196770a58047ae2a402f26326bb7d41d4d7616337911e", + "wx": "083539fbee44625e3acaafa2fcb41349392cef0633a1b8fabecee0c133b10e99", + "wy": "00915c1ebe7bf00df8535196770a58047ae2a402f26326bb7d41d4d7616337911e" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004083539fbee44625e3acaafa2fcb41349392cef0633a1b8fabecee0c133b10e99915c1ebe7bf00df8535196770a58047ae2a402f26326bb7d41d4d7616337911e", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECDU5++5EYl46yq+i/LQTSTks7wYz\nobj6vs7gwTOxDpmRXB6+e/AN+FNRlncKWAR64qQC8mMmu31B1NdhYzeRHg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 370, + "comment": "r and s^-1 are close to n", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3045022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc6324d50220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04e075effd9607d08d5f34e3652f64cfa3bd6d20c58d0a232f058491260ab212a4cc61760ac8b0680c1b644c03cc628ba9dc4a3c0561368489c692bd40f43aa3ca", + "wx": "00e075effd9607d08d5f34e3652f64cfa3bd6d20c58d0a232f058491260ab212a4", + "wy": "00cc61760ac8b0680c1b644c03cc628ba9dc4a3c0561368489c692bd40f43aa3ca" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004e075effd9607d08d5f34e3652f64cfa3bd6d20c58d0a232f058491260ab212a4cc61760ac8b0680c1b644c03cc628ba9dc4a3c0561368489c692bd40f43aa3ca", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4HXv/ZYH0I1fNONlL2TPo71tIMWN\nCiMvBYSRJgqyEqTMYXYKyLBoDBtkTAPMYoup3Eo8BWE2hInGkr1A9Dqjyg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 371, + "comment": "r and s are 64-bit integer", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30160209009c44febf31c3594f020900839ed28247c2b06b", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04cffb758c3073ea3c08efd9f7f17a85b6ae385c5a140c146ad5f1f5a826718bc8dfdc6bebc894144c6d418ac5d97339726ad2ae925df868426e5628e9f4e62342", + "wx": "00cffb758c3073ea3c08efd9f7f17a85b6ae385c5a140c146ad5f1f5a826718bc8", + "wy": "00dfdc6bebc894144c6d418ac5d97339726ad2ae925df868426e5628e9f4e62342" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004cffb758c3073ea3c08efd9f7f17a85b6ae385c5a140c146ad5f1f5a826718bc8dfdc6bebc894144c6d418ac5d97339726ad2ae925df868426e5628e9f4e62342", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEz/t1jDBz6jwI79n38XqFtq44XFoU\nDBRq1fH1qCZxi8jf3GvryJQUTG1BisXZczlyatKukl34aEJuVijp9OYjQg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 372, + "comment": "r and s are 100-bit integer", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "301e020d09df8b682430beef6f5fd7c7cd020d0fd0a62e13778f4222a0d61c8a", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04b98740e69e61a325d5f772e3b5c4f67fb7150b16a9afeca9ddc4afcbb6fa0549c446e814138e4ebc82dbf86a390056d4595dcf45e381fef217a4597d7bd51498", + "wx": "00b98740e69e61a325d5f772e3b5c4f67fb7150b16a9afeca9ddc4afcbb6fa0549", + "wy": "00c446e814138e4ebc82dbf86a390056d4595dcf45e381fef217a4597d7bd51498" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004b98740e69e61a325d5f772e3b5c4f67fb7150b16a9afeca9ddc4afcbb6fa0549c446e814138e4ebc82dbf86a390056d4595dcf45e381fef217a4597d7bd51498", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuYdA5p5hoyXV93LjtcT2f7cVCxap\nr+yp3cSvy7b6BUnERugUE45OvILb+Go5AFbUWV3PReOB/vIXpFl9e9UUmA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 373, + "comment": "r and s are 128-bit integer", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30260211008a598e563a89f526c32ebec8de26367c02110084f633e2042630e99dd0f1e16f7a04bf", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0484536a270c3932bb2084732adf2c768efc6d3977e5220229ea9a44888b8f9d7b1766398cdac2fc8000017b29a7ba15a58f196037f35f7008ed4286ddff00fd46", + "wx": "0084536a270c3932bb2084732adf2c768efc6d3977e5220229ea9a44888b8f9d7b", + "wy": "1766398cdac2fc8000017b29a7ba15a58f196037f35f7008ed4286ddff00fd46" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000484536a270c3932bb2084732adf2c768efc6d3977e5220229ea9a44888b8f9d7b1766398cdac2fc8000017b29a7ba15a58f196037f35f7008ed4286ddff00fd46", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhFNqJww5MrsghHMq3yx2jvxtOXfl\nIgIp6ppEiIuPnXsXZjmM2sL8gAABeymnuhWljxlgN/NfcAjtQobd/wD9Rg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 374, + "comment": "r and s are 160-bit integer", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "302e021500aa6eeb5823f7fa31b466bb473797f0d0314c0bdf021500e2977c479e6d25703cebbc6bd561938cc9d1bfb9", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "048aeb368a7027a4d64abdea37390c0c1d6a26f399e2d9734de1eb3d0e1937387405bd13834715e1dbae9b875cf07bd55e1b6691c7f7536aef3b19bf7a4adf576d", + "wx": "008aeb368a7027a4d64abdea37390c0c1d6a26f399e2d9734de1eb3d0e19373874", + "wy": "05bd13834715e1dbae9b875cf07bd55e1b6691c7f7536aef3b19bf7a4adf576d" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200048aeb368a7027a4d64abdea37390c0c1d6a26f399e2d9734de1eb3d0e1937387405bd13834715e1dbae9b875cf07bd55e1b6691c7f7536aef3b19bf7a4adf576d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEius2inAnpNZKveo3OQwMHWom85ni\n2XNN4es9Dhk3OHQFvRODRxXh266bh1zwe9VeG2aRx/dTau87Gb96St9XbQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 375, + "comment": "s == 1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30250220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70020101", + "result": "valid" + }, + { + "tcId": 376, + "comment": "s == 0", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30250220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70020100", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0461722eaba731c697c7a9ba4d0afdbb5713d8aa12b0eab601bb33dbaf792c5adc272cd993b2b663aba5b3a26c101182ff178684945e83879e71598b95fe647dfc", + "wx": "61722eaba731c697c7a9ba4d0afdbb5713d8aa12b0eab601bb33dbaf792c5adc", + "wy": "272cd993b2b663aba5b3a26c101182ff178684945e83879e71598b95fe647dfc" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000461722eaba731c697c7a9ba4d0afdbb5713d8aa12b0eab601bb33dbaf792c5adc272cd993b2b663aba5b3a26c101182ff178684945e83879e71598b95fe647dfc", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYXIuq6cxxpfHqbpNCv27VxPYqhKw\n6rYBuzPbr3ksWtwnLNmTsrZjq6WzomwQEYL/F4aElF6Dh55xWYuV/mR9/A==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 377, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70022002f676969f451a8ccafa4c4f09791810e6d632dbd60b1d5540f3284fbe1889b0", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04c4c91981e720e20d7e478ff19d09b95a98f58c0f469b72801a8ce844a347316594afcd4188182e7779889b3258d0368ece1e66797fe7c648c6f0b9e26bd71871", + "wx": "00c4c91981e720e20d7e478ff19d09b95a98f58c0f469b72801a8ce844a3473165", + "wy": "0094afcd4188182e7779889b3258d0368ece1e66797fe7c648c6f0b9e26bd71871" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004c4c91981e720e20d7e478ff19d09b95a98f58c0f469b72801a8ce844a347316594afcd4188182e7779889b3258d0368ece1e66797fe7c648c6f0b9e26bd71871", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExMkZgecg4g1+R4/xnQm5Wpj1jA9G\nm3KAGozoRKNHMWWUr81BiBgud3mImzJY0DaOzh5meX/nxkjG8Lnia9cYcQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 378, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c7002204e260962e33362ef0046126d2d5a4edc6947ab20e19b8ec19cf79e5908b6e628", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04d58d47bf49bc8f416641f6f760fcbca80aa52a814e56a5fa40bab44fd6f6317216deaa84d45d8e0e29cc9ecf5653f8ee6444750813becae8deb42b04ba07a634", + "wx": "00d58d47bf49bc8f416641f6f760fcbca80aa52a814e56a5fa40bab44fd6f63172", + "wy": "16deaa84d45d8e0e29cc9ecf5653f8ee6444750813becae8deb42b04ba07a634" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004d58d47bf49bc8f416641f6f760fcbca80aa52a814e56a5fa40bab44fd6f6317216deaa84d45d8e0e29cc9ecf5653f8ee6444750813becae8deb42b04ba07a634", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1Y1Hv0m8j0FmQfb3YPy8qAqlKoFO\nVqX6QLq0T9b2MXIW3qqE1F2ODinMns9WU/juZER1CBO+yujetCsEugemNA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 379, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c700220077ed0d8f20f697d8fc591ac64dd5219c7932122b4f9b9ec6441e44a0092cf21", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0491e305822e5e44f3fdb616e2ef42cd98f241b86e9f68815bc4dba6a945e4eefb3c5937e2ac1d9466f6d65e49b35fc8d75ffc22e1fe2f32af42f5fa3c26f9b4b0", + "wx": "0091e305822e5e44f3fdb616e2ef42cd98f241b86e9f68815bc4dba6a945e4eefb", + "wy": "3c5937e2ac1d9466f6d65e49b35fc8d75ffc22e1fe2f32af42f5fa3c26f9b4b0" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000491e305822e5e44f3fdb616e2ef42cd98f241b86e9f68815bc4dba6a945e4eefb3c5937e2ac1d9466f6d65e49b35fc8d75ffc22e1fe2f32af42f5fa3c26f9b4b0", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkeMFgi5eRPP9thbi70LNmPJBuG6f\naIFbxNumqUXk7vs8WTfirB2UZvbWXkmzX8jXX/wi4f4vMq9C9fo8Jvm0sA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 380, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c7002203e0292a67e181c6c0105ee35e956e78e9bdd033c6e71ae57884039a245e4175f", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0424a0bc4d16dbbd40d2fd81a7c3f8d8ec741607d5bb406a0611cc60d0e683bd46b575cad039c15f7f3dffcfc007b4b0f743c871ecc76a504a32672fd84526d861", + "wx": "24a0bc4d16dbbd40d2fd81a7c3f8d8ec741607d5bb406a0611cc60d0e683bd46", + "wy": "00b575cad039c15f7f3dffcfc007b4b0f743c871ecc76a504a32672fd84526d861" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000424a0bc4d16dbbd40d2fd81a7c3f8d8ec741607d5bb406a0611cc60d0e683bd46b575cad039c15f7f3dffcfc007b4b0f743c871ecc76a504a32672fd84526d861", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJKC8TRbbvUDS/YGnw/jY7HQWB9W7\nQGoGEcxg0OaDvUa1dcrQOcFffz3/z8AHtLD3Q8hx7MdqUEoyZy/YRSbYYQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 381, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70022013d22b06d6b8f5d97e0c64962b4a3bae30f668ca6217ef5b35d799f159e23ebe", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04d24dd06745cafb39186d22a92aa0e58169a79ab69488628a9da5ed3ef747269b7e9209d98faeb95355948adae61d5291c6015d3ee9513486d886fb05cbd25c6a", + "wx": "00d24dd06745cafb39186d22a92aa0e58169a79ab69488628a9da5ed3ef747269b", + "wy": "7e9209d98faeb95355948adae61d5291c6015d3ee9513486d886fb05cbd25c6a" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004d24dd06745cafb39186d22a92aa0e58169a79ab69488628a9da5ed3ef747269b7e9209d98faeb95355948adae61d5291c6015d3ee9513486d886fb05cbd25c6a", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0k3QZ0XK+zkYbSKpKqDlgWmnmraU\niGKKnaXtPvdHJpt+kgnZj665U1WUitrmHVKRxgFdPulRNIbYhvsFy9Jcag==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 382, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c7002204523ce342e4994bb8968bf6613f60c06c86111f15a3a389309e72cd447d5dd99", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "048200f148e7eab1581bcd1e23946f8a9b8191d9641f9560341721f9d3fec3d63ece795669e0481e035de8623d716a6984d0a4809d6c65519443ee55260f7f3dcb", + "wx": "008200f148e7eab1581bcd1e23946f8a9b8191d9641f9560341721f9d3fec3d63e", + "wy": "00ce795669e0481e035de8623d716a6984d0a4809d6c65519443ee55260f7f3dcb" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200048200f148e7eab1581bcd1e23946f8a9b8191d9641f9560341721f9d3fec3d63ece795669e0481e035de8623d716a6984d0a4809d6c65519443ee55260f7f3dcb", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEggDxSOfqsVgbzR4jlG+Km4GR2WQf\nlWA0FyH50/7D1j7OeVZp4EgeA13oYj1xammE0KSAnWxlUZRD7lUmD389yw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 383, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70022037d765be3c9c78189ad30edb5097a4db670de11686d01420e37039d4677f4809", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04a8a69c5ed33b150ce8d37ac197070ed894c05d47258a80c9041d92486622024de85997c9666b60a393568efede8f4ca0167c1e10f626e62fc1b8c8e9c6ba6ed7", + "wx": "00a8a69c5ed33b150ce8d37ac197070ed894c05d47258a80c9041d92486622024d", + "wy": "00e85997c9666b60a393568efede8f4ca0167c1e10f626e62fc1b8c8e9c6ba6ed7" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004a8a69c5ed33b150ce8d37ac197070ed894c05d47258a80c9041d92486622024de85997c9666b60a393568efede8f4ca0167c1e10f626e62fc1b8c8e9c6ba6ed7", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqKacXtM7FQzo03rBlwcO2JTAXUcl\nioDJBB2SSGYiAk3oWZfJZmtgo5NWjv7ej0ygFnweEPYm5i/BuMjpxrpu1w==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 384, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70022044237823b54e0c74c2bf5f759d9ac5f8cb897d537ffa92effd4f0bb6c9acd860", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04ed0587e75b3b9a1dd0794f41d1729fcd432b2436cbf51c230d8bc7273273181735a57f09c7873d3964aa8102c9e25fa53070cd924cb7e3a459174740b8b71c34", + "wx": "00ed0587e75b3b9a1dd0794f41d1729fcd432b2436cbf51c230d8bc72732731817", + "wy": "35a57f09c7873d3964aa8102c9e25fa53070cd924cb7e3a459174740b8b71c34" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004ed0587e75b3b9a1dd0794f41d1729fcd432b2436cbf51c230d8bc7273273181735a57f09c7873d3964aa8102c9e25fa53070cd924cb7e3a459174740b8b71c34", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7QWH51s7mh3QeU9B0XKfzUMrJDbL\n9RwjDYvHJzJzGBc1pX8Jx4c9OWSqgQLJ4l+lMHDNkky346RZF0dAuLccNA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 385, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c700220266d30a485385906054ca86d46f5f2b17e7f4646a3092092ad92877126538111", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04077091d99004a99ee08224e59a46a70495e6fba4eff681c3ce42127e588681ef4f1c16c77dfa440dde18245c9de76243d8f2fd9dea3f2782d6c04974d02f25dc", + "wx": "077091d99004a99ee08224e59a46a70495e6fba4eff681c3ce42127e588681ef", + "wy": "4f1c16c77dfa440dde18245c9de76243d8f2fd9dea3f2782d6c04974d02f25dc" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004077091d99004a99ee08224e59a46a70495e6fba4eff681c3ce42127e588681ef4f1c16c77dfa440dde18245c9de76243d8f2fd9dea3f2782d6c04974d02f25dc", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB3CR2ZAEqZ7ggiTlmkanBJXm+6Tv\n9oHDzkISfliGge9PHBbHffpEDd4YJFyd52JD2PL9neo/J4LWwEl00C8l3A==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 386, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c700220538c7b3798e84d0ce90340165806348971ed44db8f0c674f5f215968390f92ee", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04616a8b8e57d82c11678f5827911024cd23a16cb52a65f230fb554a7b110c35a5bb466660be5cab3e4b587c12b45bd998bd56c7d66c2f94d03a1a6d2028d8a154", + "wx": "616a8b8e57d82c11678f5827911024cd23a16cb52a65f230fb554a7b110c35a5", + "wy": "00bb466660be5cab3e4b587c12b45bd998bd56c7d66c2f94d03a1a6d2028d8a154" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004616a8b8e57d82c11678f5827911024cd23a16cb52a65f230fb554a7b110c35a5bb466660be5cab3e4b587c12b45bd998bd56c7d66c2f94d03a1a6d2028d8a154", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYWqLjlfYLBFnj1gnkRAkzSOhbLUq\nZfIw+1VKexEMNaW7RmZgvlyrPktYfBK0W9mYvVbH1mwvlNA6Gm0gKNihVA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 387, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c7002206fef0ef15d1688e15e704c4e6bb8bb7f40d52d3af5c661bb78c4ed9b408699b3", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0471dc92b2b1baa7612c4a53427a0d2dfe548fa9cf829bb6b248f736a5eb30b513f91c7dff1144cb36057c2b859f35bd666a7961833b06de0f45159fbae208e326", + "wx": "71dc92b2b1baa7612c4a53427a0d2dfe548fa9cf829bb6b248f736a5eb30b513", + "wy": "00f91c7dff1144cb36057c2b859f35bd666a7961833b06de0f45159fbae208e326" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000471dc92b2b1baa7612c4a53427a0d2dfe548fa9cf829bb6b248f736a5eb30b513f91c7dff1144cb36057c2b859f35bd666a7961833b06de0f45159fbae208e326", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcdySsrG6p2EsSlNCeg0t/lSPqc+C\nm7aySPc2peswtRP5HH3/EUTLNgV8K4WfNb1manlhgzsG3g9FFZ+64gjjJg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 388, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c7002206f44275e9aeb1331efcb8d58f35c0252791427e403ad84daad51d247cc2a64c6", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04662f43ae614bd9c90ff3fcded25cf0ef186b6967a47aa6aa7ae7f396594df931f5f94a525edd50d3738f7a28d03d7a2a70095c8f89de9bb2c645fea8d8bac9e0", + "wx": "662f43ae614bd9c90ff3fcded25cf0ef186b6967a47aa6aa7ae7f396594df931", + "wy": "00f5f94a525edd50d3738f7a28d03d7a2a70095c8f89de9bb2c645fea8d8bac9e0" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004662f43ae614bd9c90ff3fcded25cf0ef186b6967a47aa6aa7ae7f396594df931f5f94a525edd50d3738f7a28d03d7a2a70095c8f89de9bb2c645fea8d8bac9e0", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZi9DrmFL2ckP8/ze0lzw7xhraWek\neqaqeufzlllN+TH1+UpSXt1Q03OPeijQPXoqcAlcj4nem7LGRf6o2LrJ4A==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 389, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70022021323755b103d2f9da6ab83eccab9ad8598bcf625652f10e7a3eeee3c3945fb3", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04dff107959bd2f7386497a5624430a0ab35e552c1a4e4dc9c298caeb96353170dcb5065d7947a676c76287ca8e430324f8a534b0ba6f21200e033c4b88852a3cc", + "wx": "00dff107959bd2f7386497a5624430a0ab35e552c1a4e4dc9c298caeb96353170d", + "wy": "00cb5065d7947a676c76287ca8e430324f8a534b0ba6f21200e033c4b88852a3cc" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004dff107959bd2f7386497a5624430a0ab35e552c1a4e4dc9c298caeb96353170dcb5065d7947a676c76287ca8e430324f8a534b0ba6f21200e033c4b88852a3cc", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3/EHlZvS9zhkl6ViRDCgqzXlUsGk\n5NycKYyuuWNTFw3LUGXXlHpnbHYofKjkMDJPilNLC6byEgDgM8S4iFKjzA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 390, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c7002206c50acfe76de1289e7a5edb240f1c2a7879db6873d5d931f3c6ac467a6eac171", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04bd0862b0bfba85036922e06f5458754aafc3075b603a814b3ac75659bf24d7528258a607ffca2cfe05a300cb4c3c4e1963bbb1bc54d320e16969f85aad243385", + "wx": "00bd0862b0bfba85036922e06f5458754aafc3075b603a814b3ac75659bf24d752", + "wy": "008258a607ffca2cfe05a300cb4c3c4e1963bbb1bc54d320e16969f85aad243385" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004bd0862b0bfba85036922e06f5458754aafc3075b603a814b3ac75659bf24d7528258a607ffca2cfe05a300cb4c3c4e1963bbb1bc54d320e16969f85aad243385", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvQhisL+6hQNpIuBvVFh1Sq/DB1tg\nOoFLOsdWWb8k11KCWKYH/8os/gWjAMtMPE4ZY7uxvFTTIOFpafharSQzhQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 391, + "comment": "edge case modular inverse", + "flags": [ + "ModularInverse", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c700220755b7fffb0b17ad57dca50fcefb7fe297b029df25e5ccb5069e8e70c2742c2a6", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04b533d4695dd5b8c5e07757e55e6e516f7e2c88fa0239e23f60e8ec07dd70f2871b134ee58cc583278456863f33c3a85d881f7d4a39850143e29d4eaf009afe47", + "wx": "00b533d4695dd5b8c5e07757e55e6e516f7e2c88fa0239e23f60e8ec07dd70f287", + "wy": "1b134ee58cc583278456863f33c3a85d881f7d4a39850143e29d4eaf009afe47" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004b533d4695dd5b8c5e07757e55e6e516f7e2c88fa0239e23f60e8ec07dd70f2871b134ee58cc583278456863f33c3a85d881f7d4a39850143e29d4eaf009afe47", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEtTPUaV3VuMXgd1flXm5Rb34siPoC\nOeI/YOjsB91w8ocbE07ljMWDJ4RWhj8zw6hdiB99SjmFAUPinU6vAJr+Rw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 392, + "comment": "point at infinity during verify", + "flags": [ + "PointDuplication", + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a80220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04f50d371b91bfb1d7d14e1323523bc3aa8cbf2c57f9e284de628c8b4536787b86f94ad887ac94d527247cd2e7d0c8b1291c553c9730405380b14cbb209f5fa2dd", + "wx": "00f50d371b91bfb1d7d14e1323523bc3aa8cbf2c57f9e284de628c8b4536787b86", + "wy": "00f94ad887ac94d527247cd2e7d0c8b1291c553c9730405380b14cbb209f5fa2dd" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004f50d371b91bfb1d7d14e1323523bc3aa8cbf2c57f9e284de628c8b4536787b86f94ad887ac94d527247cd2e7d0c8b1291c553c9730405380b14cbb209f5fa2dd", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9Q03G5G/sdfRThMjUjvDqoy/LFf5\n4oTeYoyLRTZ4e4b5StiHrJTVJyR80ufQyLEpHFU8lzBAU4CxTLsgn1+i3Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 393, + "comment": "edge case for signature malleability", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a902207fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a8", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0468ec6e298eafe16539156ce57a14b04a7047c221bafc3a582eaeb0d857c4d94697bed1af17850117fdb39b2324f220a5698ed16c426a27335bb385ac8ca6fb30", + "wx": "68ec6e298eafe16539156ce57a14b04a7047c221bafc3a582eaeb0d857c4d946", + "wy": "0097bed1af17850117fdb39b2324f220a5698ed16c426a27335bb385ac8ca6fb30" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000468ec6e298eafe16539156ce57a14b04a7047c221bafc3a582eaeb0d857c4d94697bed1af17850117fdb39b2324f220a5698ed16c426a27335bb385ac8ca6fb30", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaOxuKY6v4WU5FWzlehSwSnBHwiG6\n/DpYLq6w2FfE2UaXvtGvF4UBF/2zmyMk8iClaY7RbEJqJzNbs4WsjKb7MA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 394, + "comment": "edge case for signature malleability", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a902207fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a9", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0469da0364734d2e530fece94019265fefb781a0f1b08f6c8897bdf6557927c8b866d2d3c7dcd518b23d726960f069ad71a933d86ef8abbcce8b20f71e2a847002", + "wx": "69da0364734d2e530fece94019265fefb781a0f1b08f6c8897bdf6557927c8b8", + "wy": "66d2d3c7dcd518b23d726960f069ad71a933d86ef8abbcce8b20f71e2a847002" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000469da0364734d2e530fece94019265fefb781a0f1b08f6c8897bdf6557927c8b866d2d3c7dcd518b23d726960f069ad71a933d86ef8abbcce8b20f71e2a847002", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEadoDZHNNLlMP7OlAGSZf77eBoPGw\nj2yIl732VXknyLhm0tPH3NUYsj1yaWDwaa1xqTPYbvirvM6LIPceKoRwAg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 395, + "comment": "u1 == 1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30450220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70022100bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca605023", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04d8adc00023a8edc02576e2b63e3e30621a471e2b2320620187bf067a1ac1ff3233e2b50ec09807accb36131fff95ed12a09a86b4ea9690aa32861576ba2362e1", + "wx": "00d8adc00023a8edc02576e2b63e3e30621a471e2b2320620187bf067a1ac1ff32", + "wy": "33e2b50ec09807accb36131fff95ed12a09a86b4ea9690aa32861576ba2362e1" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004d8adc00023a8edc02576e2b63e3e30621a471e2b2320620187bf067a1ac1ff3233e2b50ec09807accb36131fff95ed12a09a86b4ea9690aa32861576ba2362e1", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2K3AACOo7cAlduK2Pj4wYhpHHisj\nIGIBh78GehrB/zIz4rUOwJgHrMs2Ex//le0SoJqGtOqWkKoyhhV2uiNi4Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 396, + "comment": "u1 == n - 1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70022044a5ad0ad0636d9f12bc9e0a6bdd5e1cbcb012ea7bf091fcec15b0c43202d52e", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "043623ac973ced0a56fa6d882f03a7d5c7edca02cfc7b2401fab3690dbe75ab7858db06908e64b28613da7257e737f39793da8e713ba0643b92e9bb3252be7f8fe", + "wx": "3623ac973ced0a56fa6d882f03a7d5c7edca02cfc7b2401fab3690dbe75ab785", + "wy": "008db06908e64b28613da7257e737f39793da8e713ba0643b92e9bb3252be7f8fe" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200043623ac973ced0a56fa6d882f03a7d5c7edca02cfc7b2401fab3690dbe75ab7858db06908e64b28613da7257e737f39793da8e713ba0643b92e9bb3252be7f8fe", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENiOslzztClb6bYgvA6fVx+3KAs/H\nskAfqzaQ2+dat4WNsGkI5ksoYT2nJX5zfzl5PajnE7oGQ7kum7MlK+f4/g==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 397, + "comment": "u2 == 1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c700220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04cf04ea77e9622523d894b93ff52dc3027b31959503b6fa3890e5e04263f922f1e8528fb7c006b3983c8b8400e57b4ed71740c2f3975438821199bedeaecab2e9", + "wx": "00cf04ea77e9622523d894b93ff52dc3027b31959503b6fa3890e5e04263f922f1", + "wy": "00e8528fb7c006b3983c8b8400e57b4ed71740c2f3975438821199bedeaecab2e9" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004cf04ea77e9622523d894b93ff52dc3027b31959503b6fa3890e5e04263f922f1e8528fb7c006b3983c8b8400e57b4ed71740c2f3975438821199bedeaecab2e9", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzwTqd+liJSPYlLk/9S3DAnsxlZUD\ntvo4kOXgQmP5IvHoUo+3wAazmDyLhADle07XF0DC85dUOIIRmb7ersqy6Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 398, + "comment": "u2 == n - 1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30450220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70022100aaaaaaaa00000000aaaaaaaaaaaaaaaa7def51c91a0fbf034d26872ca84218e1", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04db7a2c8a1ab573e5929dc24077b508d7e683d49227996bda3e9f78dbeff773504f417f3bc9a88075c2e0aadd5a13311730cf7cc76a82f11a36eaf08a6c99a206", + "wx": "00db7a2c8a1ab573e5929dc24077b508d7e683d49227996bda3e9f78dbeff77350", + "wy": "4f417f3bc9a88075c2e0aadd5a13311730cf7cc76a82f11a36eaf08a6c99a206" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004db7a2c8a1ab573e5929dc24077b508d7e683d49227996bda3e9f78dbeff773504f417f3bc9a88075c2e0aadd5a13311730cf7cc76a82f11a36eaf08a6c99a206", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE23osihq1c+WSncJAd7UI1+aD1JIn\nmWvaPp942+/3c1BPQX87yaiAdcLgqt1aEzEXMM98x2qC8Ro26vCKbJmiBg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 399, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100e91e1ba60fdedb76a46bcb51dc0b8b4b7e019f0a28721885fa5d3a8196623397", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04dead11c7a5b396862f21974dc4752fadeff994efe9bbd05ab413765ea80b6e1f1de3f0640e8ac6edcf89cff53c40e265bb94078a343736df07aa0318fc7fe1ff", + "wx": "00dead11c7a5b396862f21974dc4752fadeff994efe9bbd05ab413765ea80b6e1f", + "wy": "1de3f0640e8ac6edcf89cff53c40e265bb94078a343736df07aa0318fc7fe1ff" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004dead11c7a5b396862f21974dc4752fadeff994efe9bbd05ab413765ea80b6e1f1de3f0640e8ac6edcf89cff53c40e265bb94078a343736df07aa0318fc7fe1ff", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3q0Rx6WzloYvIZdNxHUvre/5lO/p\nu9BatBN2XqgLbh8d4/BkDorG7c+Jz/U8QOJlu5QHijQ3Nt8HqgMY/H/h/w==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 400, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100fdea5843ffeb73af94313ba4831b53fe24f799e525b1e8e8c87b59b95b430ad9", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04d0bc472e0d7c81ebaed3a6ef96c18613bb1fea6f994326fbe80e00dfde67c7e9986c723ea4843d48389b946f64ad56c83ad70ff17ba85335667d1bb9fa619efd", + "wx": "00d0bc472e0d7c81ebaed3a6ef96c18613bb1fea6f994326fbe80e00dfde67c7e9", + "wy": "00986c723ea4843d48389b946f64ad56c83ad70ff17ba85335667d1bb9fa619efd" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004d0bc472e0d7c81ebaed3a6ef96c18613bb1fea6f994326fbe80e00dfde67c7e9986c723ea4843d48389b946f64ad56c83ad70ff17ba85335667d1bb9fa619efd", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0LxHLg18geuu06bvlsGGE7sf6m+Z\nQyb76A4A395nx+mYbHI+pIQ9SDiblG9krVbIOtcP8XuoUzVmfRu5+mGe/Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 401, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022003ffcabf2f1b4d2a65190db1680d62bb994e41c5251cd73b3c3dfc5e5bafc035", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04a0a44ca947d66a2acb736008b9c08d1ab2ad03776e02640f78495d458dd51c326337fe5cf8c4604b1f1c409dc2d872d4294a4762420df43a30a2392e40426add", + "wx": "00a0a44ca947d66a2acb736008b9c08d1ab2ad03776e02640f78495d458dd51c32", + "wy": "6337fe5cf8c4604b1f1c409dc2d872d4294a4762420df43a30a2392e40426add" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004a0a44ca947d66a2acb736008b9c08d1ab2ad03776e02640f78495d458dd51c326337fe5cf8c4604b1f1c409dc2d872d4294a4762420df43a30a2392e40426add", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoKRMqUfWairLc2AIucCNGrKtA3du\nAmQPeEldRY3VHDJjN/5c+MRgSx8cQJ3C2HLUKUpHYkIN9DowojkuQEJq3Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 402, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02204dfbc401f971cd304b33dfdb17d0fed0fe4c1a88ae648e0d2847f74977534989", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04c9c2115290d008b45fb65fad0f602389298c25420b775019d42b62c3ce8a96b73877d25a8080dc02d987ca730f0405c2c9dbefac46f9e601cc3f06e9713973fd", + "wx": "00c9c2115290d008b45fb65fad0f602389298c25420b775019d42b62c3ce8a96b7", + "wy": "3877d25a8080dc02d987ca730f0405c2c9dbefac46f9e601cc3f06e9713973fd" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004c9c2115290d008b45fb65fad0f602389298c25420b775019d42b62c3ce8a96b73877d25a8080dc02d987ca730f0405c2c9dbefac46f9e601cc3f06e9713973fd", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEycIRUpDQCLRftl+tD2AjiSmMJUIL\nd1AZ1Ctiw86Klrc4d9JagIDcAtmHynMPBAXCydvvrEb55gHMPwbpcTlz/Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 403, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100bc4024761cd2ffd43dfdb17d0fed112b988977055cd3a8e54971eba9cda5ca71", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "045eca1ef4c287dddc66b8bccf1b88e8a24c0018962f3c5e7efa83bc1a5ff6033e5e79c4cb2c245b8c45abdce8a8e4da758d92a607c32cd407ecaef22f1c934a71", + "wx": "5eca1ef4c287dddc66b8bccf1b88e8a24c0018962f3c5e7efa83bc1a5ff6033e", + "wy": "5e79c4cb2c245b8c45abdce8a8e4da758d92a607c32cd407ecaef22f1c934a71" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200045eca1ef4c287dddc66b8bccf1b88e8a24c0018962f3c5e7efa83bc1a5ff6033e5e79c4cb2c245b8c45abdce8a8e4da758d92a607c32cd407ecaef22f1c934a71", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXsoe9MKH3dxmuLzPG4jookwAGJYv\nPF5++oO8Gl/2Az5eecTLLCRbjEWr3Oio5Np1jZKmB8Ms1AfsrvIvHJNKcQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 404, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0220788048ed39a5ffa77bfb62fa1fda2257742bf35d128fb3459f2a0c909ee86f91", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "045caaa030e7fdf0e4936bc7ab5a96353e0a01e4130c3f8bf22d473e317029a47adeb6adc462f7058f2a20d371e9702254e9b201642005b3ceda926b42b178bef9", + "wx": "5caaa030e7fdf0e4936bc7ab5a96353e0a01e4130c3f8bf22d473e317029a47a", + "wy": "00deb6adc462f7058f2a20d371e9702254e9b201642005b3ceda926b42b178bef9" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200045caaa030e7fdf0e4936bc7ab5a96353e0a01e4130c3f8bf22d473e317029a47adeb6adc462f7058f2a20d371e9702254e9b201642005b3ceda926b42b178bef9", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXKqgMOf98OSTa8erWpY1PgoB5BMM\nP4vyLUc+MXAppHretq3EYvcFjyog03HpcCJU6bIBZCAFs87akmtCsXi++Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 405, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0220476d9131fd381bd917d0fed112bc9e0a5924b5ed5b11167edd8b23582b3cb15e", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04c2fd20bac06e555bb8ac0ce69eb1ea20f83a1fc3501c8a66469b1a31f619b0986237050779f52b615bd7b8d76a25fc95ca2ed32525c75f27ffc87ac397e6cbaf", + "wx": "00c2fd20bac06e555bb8ac0ce69eb1ea20f83a1fc3501c8a66469b1a31f619b098", + "wy": "6237050779f52b615bd7b8d76a25fc95ca2ed32525c75f27ffc87ac397e6cbaf" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004c2fd20bac06e555bb8ac0ce69eb1ea20f83a1fc3501c8a66469b1a31f619b0986237050779f52b615bd7b8d76a25fc95ca2ed32525c75f27ffc87ac397e6cbaf", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwv0gusBuVVu4rAzmnrHqIPg6H8NQ\nHIpmRpsaMfYZsJhiNwUHefUrYVvXuNdqJfyVyi7TJSXHXyf/yHrDl+bLrw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 406, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0221008374253e3e21bd154448d0a8f640fe46fafa8b19ce78d538f6cc0a19662d3601", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "043fd6a1ca7f77fb3b0bbe726c372010068426e11ea6ae78ce17bedae4bba86ced03ce5516406bf8cfaab8745eac1cd69018ad6f50b5461872ddfc56e0db3c8ff4", + "wx": "3fd6a1ca7f77fb3b0bbe726c372010068426e11ea6ae78ce17bedae4bba86ced", + "wy": "03ce5516406bf8cfaab8745eac1cd69018ad6f50b5461872ddfc56e0db3c8ff4" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200043fd6a1ca7f77fb3b0bbe726c372010068426e11ea6ae78ce17bedae4bba86ced03ce5516406bf8cfaab8745eac1cd69018ad6f50b5461872ddfc56e0db3c8ff4", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEP9ahyn93+zsLvnJsNyAQBoQm4R6m\nrnjOF77a5LuobO0DzlUWQGv4z6q4dF6sHNaQGK1vULVGGHLd/Fbg2zyP9A==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 407, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0220357cfd3be4d01d413c5b9ede36cba5452c11ee7fe14879e749ae6a2d897a52d6", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "049cb8e51e27a5ae3b624a60d6dc32734e4989db20e9bca3ede1edf7b086911114b4c104ab3c677e4b36d6556e8ad5f523410a19f2e277aa895fc57322b4427544", + "wx": "009cb8e51e27a5ae3b624a60d6dc32734e4989db20e9bca3ede1edf7b086911114", + "wy": "00b4c104ab3c677e4b36d6556e8ad5f523410a19f2e277aa895fc57322b4427544" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200049cb8e51e27a5ae3b624a60d6dc32734e4989db20e9bca3ede1edf7b086911114b4c104ab3c677e4b36d6556e8ad5f523410a19f2e277aa895fc57322b4427544", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnLjlHielrjtiSmDW3DJzTkmJ2yDp\nvKPt4e33sIaRERS0wQSrPGd+SzbWVW6K1fUjQQoZ8uJ3qolfxXMitEJ1RA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 408, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022029798c5c0ee287d4a5e8e6b799fd86b8df5225298e6ffc807cd2f2bc27a0a6d8", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04a3e52c156dcaf10502620b7955bc2b40bc78ef3d569e1223c262512d8f49602a4a2039f31c1097024ad3cc86e57321de032355463486164cf192944977df147f", + "wx": "00a3e52c156dcaf10502620b7955bc2b40bc78ef3d569e1223c262512d8f49602a", + "wy": "4a2039f31c1097024ad3cc86e57321de032355463486164cf192944977df147f" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004a3e52c156dcaf10502620b7955bc2b40bc78ef3d569e1223c262512d8f49602a4a2039f31c1097024ad3cc86e57321de032355463486164cf192944977df147f", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEo+UsFW3K8QUCYgt5VbwrQLx47z1W\nnhIjwmJRLY9JYCpKIDnzHBCXAkrTzIblcyHeAyNVRjSGFkzxkpRJd98Ufw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 409, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02200b70f22c781092452dca1a5711fa3a5a1f72add1bf52c2ff7cae4820b30078dd", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04f19b78928720d5bee8e670fb90010fb15c37bf91b58a5157c3f3c059b2655e88cf701ec962fb4a11dcf273f5dc357e58468560c7cfeb942d074abd4329260509", + "wx": "00f19b78928720d5bee8e670fb90010fb15c37bf91b58a5157c3f3c059b2655e88", + "wy": "00cf701ec962fb4a11dcf273f5dc357e58468560c7cfeb942d074abd4329260509" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004f19b78928720d5bee8e670fb90010fb15c37bf91b58a5157c3f3c059b2655e88cf701ec962fb4a11dcf273f5dc357e58468560c7cfeb942d074abd4329260509", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8Zt4kocg1b7o5nD7kAEPsVw3v5G1\nilFXw/PAWbJlXojPcB7JYvtKEdzyc/XcNX5YRoVgx8/rlC0HSr1DKSYFCQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 410, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022016e1e458f021248a5b9434ae23f474b43ee55ba37ea585fef95c90416600f1ba", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0483a744459ecdfb01a5cf52b27a05bb7337482d242f235d7b4cb89345545c90a8c05d49337b9649813287de9ffe90355fd905df5f3c32945828121f37cc50de6e", + "wx": "0083a744459ecdfb01a5cf52b27a05bb7337482d242f235d7b4cb89345545c90a8", + "wy": "00c05d49337b9649813287de9ffe90355fd905df5f3c32945828121f37cc50de6e" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000483a744459ecdfb01a5cf52b27a05bb7337482d242f235d7b4cb89345545c90a8c05d49337b9649813287de9ffe90355fd905df5f3c32945828121f37cc50de6e", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEg6dERZ7N+wGlz1KyegW7czdILSQv\nI117TLiTRVRckKjAXUkze5ZJgTKH3p/+kDVf2QXfXzwylFgoEh83zFDebg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 411, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02202252d6856831b6cf895e4f0535eeaf0e5e5809753df848fe760ad86219016a97", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04dd13c6b34c56982ddae124f039dfd23f4b19bbe88cee8e528ae51e5d6f3a21d7bfad4c2e6f263fe5eb59ca974d039fc0e4c3345692fb5320bdae4bd3b42a45ff", + "wx": "00dd13c6b34c56982ddae124f039dfd23f4b19bbe88cee8e528ae51e5d6f3a21d7", + "wy": "00bfad4c2e6f263fe5eb59ca974d039fc0e4c3345692fb5320bdae4bd3b42a45ff" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004dd13c6b34c56982ddae124f039dfd23f4b19bbe88cee8e528ae51e5d6f3a21d7bfad4c2e6f263fe5eb59ca974d039fc0e4c3345692fb5320bdae4bd3b42a45ff", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3RPGs0xWmC3a4STwOd/SP0sZu+iM\n7o5SiuUeXW86Ide/rUwubyY/5etZypdNA5/A5MM0VpL7UyC9rkvTtCpF/w==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 412, + "comment": "edge case for u1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02210081ffe55f178da695b28c86d8b406b15dab1a9e39661a3ae017fbe390ac0972c3", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0467e6f659cdde869a2f65f094e94e5b4dfad636bbf95192feeed01b0f3deb7460a37e0a51f258b7aeb51dfe592f5cfd5685bbe58712c8d9233c62886437c38ba0", + "wx": "67e6f659cdde869a2f65f094e94e5b4dfad636bbf95192feeed01b0f3deb7460", + "wy": "00a37e0a51f258b7aeb51dfe592f5cfd5685bbe58712c8d9233c62886437c38ba0" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000467e6f659cdde869a2f65f094e94e5b4dfad636bbf95192feeed01b0f3deb7460a37e0a51f258b7aeb51dfe592f5cfd5685bbe58712c8d9233c62886437c38ba0", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZ+b2Wc3ehpovZfCU6U5bTfrWNrv5\nUZL+7tAbDz3rdGCjfgpR8li3rrUd/lkvXP1WhbvlhxLI2SM8YohkN8OLoA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 413, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02207fffffffaaaaaaaaffffffffffffffffe9a2538f37b28a2c513dee40fecbb71a", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "042eb6412505aec05c6545f029932087e490d05511e8ec1f599617bb367f9ecaaf805f51efcc4803403f9b1ae0124890f06a43fedcddb31830f6669af292895cb0", + "wx": "2eb6412505aec05c6545f029932087e490d05511e8ec1f599617bb367f9ecaaf", + "wy": "00805f51efcc4803403f9b1ae0124890f06a43fedcddb31830f6669af292895cb0" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200042eb6412505aec05c6545f029932087e490d05511e8ec1f599617bb367f9ecaaf805f51efcc4803403f9b1ae0124890f06a43fedcddb31830f6669af292895cb0", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELrZBJQWuwFxlRfApkyCH5JDQVRHo\n7B9Zlhe7Nn+eyq+AX1HvzEgDQD+bGuASSJDwakP+3N2zGDD2ZprykolcsA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 414, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100b62f26b5f2a2b26f6de86d42ad8a13da3ab3cccd0459b201de009e526adf21f2", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0484db645868eab35e3a9fd80e056e2e855435e3a6b68d75a50a854625fe0d7f356d2589ac655edc9a11ef3e075eddda9abf92e72171570ef7bf43a2ee39338cfe", + "wx": "0084db645868eab35e3a9fd80e056e2e855435e3a6b68d75a50a854625fe0d7f35", + "wy": "6d2589ac655edc9a11ef3e075eddda9abf92e72171570ef7bf43a2ee39338cfe" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000484db645868eab35e3a9fd80e056e2e855435e3a6b68d75a50a854625fe0d7f356d2589ac655edc9a11ef3e075eddda9abf92e72171570ef7bf43a2ee39338cfe", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhNtkWGjqs146n9gOBW4uhVQ146a2\njXWlCoVGJf4NfzVtJYmsZV7cmhHvPgde3dqav5LnIXFXDve/Q6LuOTOM/g==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 415, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100bb1d9ac949dd748cd02bbbe749bd351cd57b38bb61403d700686aa7b4c90851e", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0491b9e47c56278662d75c0983b22ca8ea6aa5059b7a2ff7637eb2975e386ad66349aa8ff283d0f77c18d6d11dc062165fd13c3c0310679c1408302a16854ecfbd", + "wx": "0091b9e47c56278662d75c0983b22ca8ea6aa5059b7a2ff7637eb2975e386ad663", + "wy": "49aa8ff283d0f77c18d6d11dc062165fd13c3c0310679c1408302a16854ecfbd" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000491b9e47c56278662d75c0983b22ca8ea6aa5059b7a2ff7637eb2975e386ad66349aa8ff283d0f77c18d6d11dc062165fd13c3c0310679c1408302a16854ecfbd", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkbnkfFYnhmLXXAmDsiyo6mqlBZt6\nL/djfrKXXjhq1mNJqo/yg9D3fBjW0R3AYhZf0Tw8AxBnnBQIMCoWhU7PvQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 416, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022066755a00638cdaec1c732513ca0234ece52545dac11f816e818f725b4f60aaf2", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04f3ec2f13caf04d0192b47fb4c5311fb6d4dc6b0a9e802e5327f7ec5ee8e4834df97e3e468b7d0db867d6ecfe81e2b0f9531df87efdb47c1338ac321fefe5a432", + "wx": "00f3ec2f13caf04d0192b47fb4c5311fb6d4dc6b0a9e802e5327f7ec5ee8e4834d", + "wy": "00f97e3e468b7d0db867d6ecfe81e2b0f9531df87efdb47c1338ac321fefe5a432" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004f3ec2f13caf04d0192b47fb4c5311fb6d4dc6b0a9e802e5327f7ec5ee8e4834df97e3e468b7d0db867d6ecfe81e2b0f9531df87efdb47c1338ac321fefe5a432", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8+wvE8rwTQGStH+0xTEfttTcawqe\ngC5TJ/fsXujkg035fj5Gi30NuGfW7P6B4rD5Ux34fv20fBM4rDIf7+WkMg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 417, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022055a00c9fcdaebb6032513ca0234ecfffe98ebe492fdf02e48ca48e982beb3669", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04d92b200aefcab6ac7dafd9acaf2fa10b3180235b8f46b4503e4693c670fccc885ef2f3aebf5b317475336256768f7c19efb7352d27e4cccadc85b6b8ab922c72", + "wx": "00d92b200aefcab6ac7dafd9acaf2fa10b3180235b8f46b4503e4693c670fccc88", + "wy": "5ef2f3aebf5b317475336256768f7c19efb7352d27e4cccadc85b6b8ab922c72" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004d92b200aefcab6ac7dafd9acaf2fa10b3180235b8f46b4503e4693c670fccc885ef2f3aebf5b317475336256768f7c19efb7352d27e4cccadc85b6b8ab922c72", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE2SsgCu/Ktqx9r9msry+hCzGAI1uP\nRrRQPkaTxnD8zIhe8vOuv1sxdHUzYlZ2j3wZ77c1LSfkzMrchba4q5Iscg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 418, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100ab40193f9b5d76c064a27940469d9fffd31d7c925fbe05c919491d3057d66cd2", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "040a88361eb92ecca2625b38e5f98bbabb96bf179b3d76fc48140a3bcd881523cde6bdf56033f84a5054035597375d90866aa2c96b86a41ccf6edebf47298ad489", + "wx": "0a88361eb92ecca2625b38e5f98bbabb96bf179b3d76fc48140a3bcd881523cd", + "wy": "00e6bdf56033f84a5054035597375d90866aa2c96b86a41ccf6edebf47298ad489" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200040a88361eb92ecca2625b38e5f98bbabb96bf179b3d76fc48140a3bcd881523cde6bdf56033f84a5054035597375d90866aa2c96b86a41ccf6edebf47298ad489", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECog2HrkuzKJiWzjl+Yu6u5a/F5s9\ndvxIFAo7zYgVI83mvfVgM/hKUFQDVZc3XZCGaqLJa4akHM9u3r9HKYrUiQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 419, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100ca0234ebb5fdcb13ca0234ecffffffffcb0dadbbc7f549f8a26b4408d0dc8600", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04d0fb17ccd8fafe827e0c1afc5d8d80366e2b20e7f14a563a2ba50469d84375e868612569d39e2bb9f554355564646de99ac602cc6349cf8c1e236a7de7637d93", + "wx": "00d0fb17ccd8fafe827e0c1afc5d8d80366e2b20e7f14a563a2ba50469d84375e8", + "wy": "68612569d39e2bb9f554355564646de99ac602cc6349cf8c1e236a7de7637d93" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004d0fb17ccd8fafe827e0c1afc5d8d80366e2b20e7f14a563a2ba50469d84375e868612569d39e2bb9f554355564646de99ac602cc6349cf8c1e236a7de7637d93", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0PsXzNj6/oJ+DBr8XY2ANm4rIOfx\nSlY6K6UEadhDdehoYSVp054rufVUNVVkZG3pmsYCzGNJz4weI2p952N9kw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 420, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100bfffffff3ea3677e082b9310572620ae19933a9e65b285598711c77298815ad3", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04836f33bbc1dc0d3d3abbcef0d91f11e2ac4181076c9af0a22b1e4309d3edb2769ab443ff6f901e30c773867582997c2bec2b0cb8120d760236f3a95bbe881f75", + "wx": "00836f33bbc1dc0d3d3abbcef0d91f11e2ac4181076c9af0a22b1e4309d3edb276", + "wy": "009ab443ff6f901e30c773867582997c2bec2b0cb8120d760236f3a95bbe881f75" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004836f33bbc1dc0d3d3abbcef0d91f11e2ac4181076c9af0a22b1e4309d3edb2769ab443ff6f901e30c773867582997c2bec2b0cb8120d760236f3a95bbe881f75", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEg28zu8HcDT06u87w2R8R4qxBgQds\nmvCiKx5DCdPtsnaatEP/b5AeMMdzhnWCmXwr7CsMuBINdgI286lbvogfdQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 421, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd0220266666663bbbbbbbe6666666666666665b37902e023fab7c8f055d86e5cc41f4", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0492f99fbe973ed4a299719baee4b432741237034dec8d72ba5103cb33e55feeb8033dd0e91134c734174889f3ebcf1b7a1ac05767289280ee7a794cebd6e69697", + "wx": "0092f99fbe973ed4a299719baee4b432741237034dec8d72ba5103cb33e55feeb8", + "wy": "033dd0e91134c734174889f3ebcf1b7a1ac05767289280ee7a794cebd6e69697" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000492f99fbe973ed4a299719baee4b432741237034dec8d72ba5103cb33e55feeb8033dd0e91134c734174889f3ebcf1b7a1ac05767289280ee7a794cebd6e69697", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkvmfvpc+1KKZcZuu5LQydBI3A03s\njXK6UQPLM+Vf7rgDPdDpETTHNBdIifPrzxt6GsBXZyiSgO56eUzr1uaWlw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 422, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100bfffffff36db6db7a492492492492492146c573f4c6dfc8d08a443e258970b09", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04d35ba58da30197d378e618ec0fa7e2e2d12cffd73ebbb2049d130bba434af09eff83986e6875e41ea432b7585a49b3a6c77cbb3c47919f8e82874c794635c1d2", + "wx": "00d35ba58da30197d378e618ec0fa7e2e2d12cffd73ebbb2049d130bba434af09e", + "wy": "00ff83986e6875e41ea432b7585a49b3a6c77cbb3c47919f8e82874c794635c1d2" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004d35ba58da30197d378e618ec0fa7e2e2d12cffd73ebbb2049d130bba434af09eff83986e6875e41ea432b7585a49b3a6c77cbb3c47919f8e82874c794635c1d2", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE01uljaMBl9N45hjsD6fi4tEs/9c+\nu7IEnRMLukNK8J7/g5huaHXkHqQyt1haSbOmx3y7PEeRn46Ch0x5RjXB0g==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 423, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd022100bfffffff2aaaaaab7fffffffffffffffc815d0e60b3e596ecb1ad3a27cfd49c4", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "048651ce490f1b46d73f3ff475149be29136697334a519d7ddab0725c8d0793224e11c65bd8ca92dc8bc9ae82911f0b52751ce21dd9003ae60900bd825f590cc28", + "wx": "008651ce490f1b46d73f3ff475149be29136697334a519d7ddab0725c8d0793224", + "wy": "00e11c65bd8ca92dc8bc9ae82911f0b52751ce21dd9003ae60900bd825f590cc28" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200048651ce490f1b46d73f3ff475149be29136697334a519d7ddab0725c8d0793224e11c65bd8ca92dc8bc9ae82911f0b52751ce21dd9003ae60900bd825f590cc28", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhlHOSQ8bRtc/P/R1FJvikTZpczSl\nGdfdqwclyNB5MiThHGW9jKktyLya6CkR8LUnUc4h3ZADrmCQC9gl9ZDMKA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 424, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02207fffffff55555555ffffffffffffffffd344a71e6f651458a27bdc81fd976e37", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "046d8e1b12c831a0da8795650ff95f101ed921d9e2f72b15b1cdaca9826b9cfc6def6d63e2bc5c089570394a4bc9f892d5e6c7a6a637b20469a58c106ad486bf37", + "wx": "6d8e1b12c831a0da8795650ff95f101ed921d9e2f72b15b1cdaca9826b9cfc6d", + "wy": "00ef6d63e2bc5c089570394a4bc9f892d5e6c7a6a637b20469a58c106ad486bf37" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200046d8e1b12c831a0da8795650ff95f101ed921d9e2f72b15b1cdaca9826b9cfc6def6d63e2bc5c089570394a4bc9f892d5e6c7a6a637b20469a58c106ad486bf37", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbY4bEsgxoNqHlWUP+V8QHtkh2eL3\nKxWxzaypgmuc/G3vbWPivFwIlXA5SkvJ+JLV5sempjeyBGmljBBq1Ia/Nw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 425, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02203fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192aa", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "040ae580bae933b4ef2997cbdbb0922328ca9a410f627a0f7dff24cb4d920e15428911e7f8cc365a8a88eb81421a361ccc2b99e309d8dcd9a98ba83c3949d893e3", + "wx": "0ae580bae933b4ef2997cbdbb0922328ca9a410f627a0f7dff24cb4d920e1542", + "wy": "008911e7f8cc365a8a88eb81421a361ccc2b99e309d8dcd9a98ba83c3949d893e3" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200040ae580bae933b4ef2997cbdbb0922328ca9a410f627a0f7dff24cb4d920e15428911e7f8cc365a8a88eb81421a361ccc2b99e309d8dcd9a98ba83c3949d893e3", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAECuWAuukztO8pl8vbsJIjKMqaQQ9i\neg99/yTLTZIOFUKJEef4zDZaiojrgUIaNhzMK5njCdjc2amLqDw5SdiT4w==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 426, + "comment": "edge case for u2", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd02205d8ecd64a4eeba466815ddf3a4de9a8e6abd9c5db0a01eb80343553da648428f", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "045b812fd521aafa69835a849cce6fbdeb6983b442d2444fe70e134c027fc46963838a40f2a36092e9004e92d8d940cf5638550ce672ce8b8d4e15eba5499249e9", + "wx": "5b812fd521aafa69835a849cce6fbdeb6983b442d2444fe70e134c027fc46963", + "wy": "00838a40f2a36092e9004e92d8d940cf5638550ce672ce8b8d4e15eba5499249e9" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200045b812fd521aafa69835a849cce6fbdeb6983b442d2444fe70e134c027fc46963838a40f2a36092e9004e92d8d940cf5638550ce672ce8b8d4e15eba5499249e9", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEW4Ev1SGq+mmDWoSczm+962mDtELS\nRE/nDhNMAn/EaWODikDyo2CS6QBOktjZQM9WOFUM5nLOi41OFeulSZJJ6Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 427, + "comment": "point duplication during verification", + "flags": [ + "PointDuplication" + ], + "msg": "313233343030", + "sig": "304502206f2347cab7dd76858fe0555ac3bc99048c4aacafdfb6bcbe05ea6c42c4934569022100bb726660235793aa9957a61e76e00c2c435109cf9a15dd624d53f4301047856b", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "045b812fd521aafa69835a849cce6fbdeb6983b442d2444fe70e134c027fc469637c75bf0c5c9f6d17ffb16d2726bf30a9c7aaf31a8d317472b1ea145ab66db616", + "wx": "5b812fd521aafa69835a849cce6fbdeb6983b442d2444fe70e134c027fc46963", + "wy": "7c75bf0c5c9f6d17ffb16d2726bf30a9c7aaf31a8d317472b1ea145ab66db616" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200045b812fd521aafa69835a849cce6fbdeb6983b442d2444fe70e134c027fc469637c75bf0c5c9f6d17ffb16d2726bf30a9c7aaf31a8d317472b1ea145ab66db616", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEW4Ev1SGq+mmDWoSczm+962mDtELS\nRE/nDhNMAn/EaWN8db8MXJ9tF/+xbScmvzCpx6rzGo0xdHKx6hRatm22Fg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 428, + "comment": "duplication bug", + "flags": [ + "PointDuplication" + ], + "msg": "313233343030", + "sig": "304502206f2347cab7dd76858fe0555ac3bc99048c4aacafdfb6bcbe05ea6c42c4934569022100bb726660235793aa9957a61e76e00c2c435109cf9a15dd624d53f4301047856b", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "046adda82b90261b0f319faa0d878665a6b6da497f09c903176222c34acfef72a647e6f50dcc40ad5d9b59f7602bb222fad71a41bf5e1f9df4959a364c62e488d9", + "wx": "6adda82b90261b0f319faa0d878665a6b6da497f09c903176222c34acfef72a6", + "wy": "47e6f50dcc40ad5d9b59f7602bb222fad71a41bf5e1f9df4959a364c62e488d9" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200046adda82b90261b0f319faa0d878665a6b6da497f09c903176222c34acfef72a647e6f50dcc40ad5d9b59f7602bb222fad71a41bf5e1f9df4959a364c62e488d9", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEat2oK5AmGw8xn6oNh4ZlprbaSX8J\nyQMXYiLDSs/vcqZH5vUNzECtXZtZ92ArsiL61xpBv14fnfSVmjZMYuSI2Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 429, + "comment": "point with x-coordinate 0", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30250201010220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "042fca0d0a47914de77ed56e7eccc3276a601120c6df0069c825c8f6a01c9f382065f3450a1d17c6b24989a39beb1c7decfca8384fbdc294418e5d807b3c6ed7de", + "wx": "2fca0d0a47914de77ed56e7eccc3276a601120c6df0069c825c8f6a01c9f3820", + "wy": "65f3450a1d17c6b24989a39beb1c7decfca8384fbdc294418e5d807b3c6ed7de" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200042fca0d0a47914de77ed56e7eccc3276a601120c6df0069c825c8f6a01c9f382065f3450a1d17c6b24989a39beb1c7decfca8384fbdc294418e5d807b3c6ed7de", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEL8oNCkeRTed+1W5+zMMnamARIMbf\nAGnIJcj2oByfOCBl80UKHRfGskmJo5vrHH3s/Kg4T73ClEGOXYB7PG7X3g==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 430, + "comment": "point with x-coordinate 0", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "3045022101000000000000000000000000000000000000000000000000000000000000000002203333333300000000333333333333333325c7cbbc549e52e763f1f55a327a3aa9", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04dd86d3b5f4a13e8511083b78002081c53ff467f11ebd98a51a633db76665d25045d5c8200c89f2fa10d849349226d21d8dfaed6ff8d5cb3e1b7e17474ebc18f7", + "wx": "00dd86d3b5f4a13e8511083b78002081c53ff467f11ebd98a51a633db76665d250", + "wy": "45d5c8200c89f2fa10d849349226d21d8dfaed6ff8d5cb3e1b7e17474ebc18f7" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004dd86d3b5f4a13e8511083b78002081c53ff467f11ebd98a51a633db76665d25045d5c8200c89f2fa10d849349226d21d8dfaed6ff8d5cb3e1b7e17474ebc18f7", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE3YbTtfShPoURCDt4ACCBxT/0Z/Ee\nvZilGmM9t2Zl0lBF1cggDIny+hDYSTSSJtIdjfrtb/jVyz4bfhdHTrwY9w==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 431, + "comment": "comparison with point at infinity ", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "30440220555555550000000055555555555555553ef7a8e48d07df81a693439654210c7002203333333300000000333333333333333325c7cbbc549e52e763f1f55a327a3aa9", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "044fea55b32cb32aca0c12c4cd0abfb4e64b0f5a516e578c016591a93f5a0fbcc5d7d3fd10b2be668c547b212f6bb14c88f0fecd38a8a4b2c785ed3be62ce4b280", + "wx": "4fea55b32cb32aca0c12c4cd0abfb4e64b0f5a516e578c016591a93f5a0fbcc5", + "wy": "00d7d3fd10b2be668c547b212f6bb14c88f0fecd38a8a4b2c785ed3be62ce4b280" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200044fea55b32cb32aca0c12c4cd0abfb4e64b0f5a516e578c016591a93f5a0fbcc5d7d3fd10b2be668c547b212f6bb14c88f0fecd38a8a4b2c785ed3be62ce4b280", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAET+pVsyyzKsoMEsTNCr+05ksPWlFu\nV4wBZZGpP1oPvMXX0/0Qsr5mjFR7IS9rsUyI8P7NOKiksseF7TvmLOSygA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 432, + "comment": "extreme value for k and edgecase s", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc476699780220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04c6a771527024227792170a6f8eee735bf32b7f98af669ead299802e32d7c3107bc3b4b5e65ab887bbd343572b3e5619261fe3a073e2ffd78412f726867db589e", + "wx": "00c6a771527024227792170a6f8eee735bf32b7f98af669ead299802e32d7c3107", + "wy": "00bc3b4b5e65ab887bbd343572b3e5619261fe3a073e2ffd78412f726867db589e" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004c6a771527024227792170a6f8eee735bf32b7f98af669ead299802e32d7c3107bc3b4b5e65ab887bbd343572b3e5619261fe3a073e2ffd78412f726867db589e", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExqdxUnAkIneSFwpvju5zW/Mrf5iv\nZp6tKZgC4y18MQe8O0teZauIe700NXKz5WGSYf46Bz4v/XhBL3JoZ9tYng==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 433, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978022100b6db6db6249249254924924924924924625bd7a09bec4ca81bcdd9f8fd6b63cc", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04851c2bbad08e54ec7a9af99f49f03644d6ec6d59b207fec98de85a7d15b956efcee9960283045075684b410be8d0f7494b91aa2379f60727319f10ddeb0fe9d6", + "wx": "00851c2bbad08e54ec7a9af99f49f03644d6ec6d59b207fec98de85a7d15b956ef", + "wy": "00cee9960283045075684b410be8d0f7494b91aa2379f60727319f10ddeb0fe9d6" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004851c2bbad08e54ec7a9af99f49f03644d6ec6d59b207fec98de85a7d15b956efcee9960283045075684b410be8d0f7494b91aa2379f60727319f10ddeb0fe9d6", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhRwrutCOVOx6mvmfSfA2RNbsbVmy\nB/7JjehafRW5Vu/O6ZYCgwRQdWhLQQvo0PdJS5GqI3n2BycxnxDd6w/p1g==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 434, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502207cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978022100cccccccc00000000cccccccccccccccc971f2ef152794b9d8fc7d568c9e8eaa7", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04f6417c8a670584e388676949e53da7fc55911ff68318d1bf3061205acb19c48f8f2b743df34ad0f72674acb7505929784779cd9ac916c3669ead43026ab6d43f", + "wx": "00f6417c8a670584e388676949e53da7fc55911ff68318d1bf3061205acb19c48f", + "wy": "008f2b743df34ad0f72674acb7505929784779cd9ac916c3669ead43026ab6d43f" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004f6417c8a670584e388676949e53da7fc55911ff68318d1bf3061205acb19c48f8f2b743df34ad0f72674acb7505929784779cd9ac916c3669ead43026ab6d43f", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9kF8imcFhOOIZ2lJ5T2n/FWRH/aD\nGNG/MGEgWssZxI+PK3Q980rQ9yZ0rLdQWSl4R3nNmskWw2aerUMCarbUPw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 435, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc4766997802203333333300000000333333333333333325c7cbbc549e52e763f1f55a327a3aaa", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04501421277be45a5eefec6c639930d636032565af420cf3373f557faa7f8a06438673d6cb6076e1cfcdc7dfe7384c8e5cac08d74501f2ae6e89cad195d0aa1371", + "wx": "501421277be45a5eefec6c639930d636032565af420cf3373f557faa7f8a0643", + "wy": "008673d6cb6076e1cfcdc7dfe7384c8e5cac08d74501f2ae6e89cad195d0aa1371" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004501421277be45a5eefec6c639930d636032565af420cf3373f557faa7f8a06438673d6cb6076e1cfcdc7dfe7384c8e5cac08d74501f2ae6e89cad195d0aa1371", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUBQhJ3vkWl7v7GxjmTDWNgMlZa9C\nDPM3P1V/qn+KBkOGc9bLYHbhz83H3+c4TI5crAjXRQHyrm6JytGV0KoTcQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 436, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978022049249248db6db6dbb6db6db6db6db6db5a8b230d0b2b51dcd7ebf0c9fef7c185", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "040d935bf9ffc115a527735f729ca8a4ca23ee01a4894adf0e3415ac84e808bb343195a3762fea29ed38912bd9ea6c4fde70c3050893a4375850ce61d82eba33c5", + "wx": "0d935bf9ffc115a527735f729ca8a4ca23ee01a4894adf0e3415ac84e808bb34", + "wy": "3195a3762fea29ed38912bd9ea6c4fde70c3050893a4375850ce61d82eba33c5" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200040d935bf9ffc115a527735f729ca8a4ca23ee01a4894adf0e3415ac84e808bb343195a3762fea29ed38912bd9ea6c4fde70c3050893a4375850ce61d82eba33c5", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDZNb+f/BFaUnc19ynKikyiPuAaSJ\nSt8ONBWshOgIuzQxlaN2L+op7TiRK9nqbE/ecMMFCJOkN1hQzmHYLrozxQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 437, + "comment": "extreme value for k", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402207cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978022016a4502e2781e11ac82cbc9d1edd8c981584d13e18411e2f6e0478c34416e3bb", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "045e59f50708646be8a589355014308e60b668fb670196206c41e748e64e4dca215de37fee5c97bcaf7144d5b459982f52eeeafbdf03aacbafef38e213624a01de", + "wx": "5e59f50708646be8a589355014308e60b668fb670196206c41e748e64e4dca21", + "wy": "5de37fee5c97bcaf7144d5b459982f52eeeafbdf03aacbafef38e213624a01de" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200045e59f50708646be8a589355014308e60b668fb670196206c41e748e64e4dca215de37fee5c97bcaf7144d5b459982f52eeeafbdf03aacbafef38e213624a01de", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXln1Bwhka+iliTVQFDCOYLZo+2cB\nliBsQedI5k5NyiFd43/uXJe8r3FE1bRZmC9S7ur73wOqy6/vOOITYkoB3g==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 438, + "comment": "extreme value for k and edgecase s", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402206b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2960220555555550000000055555555555555553ef7a8e48d07df81a693439654210c70", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04169fb797325843faff2f7a5b5445da9e2fd6226f7ef90ef0bfe924104b02db8e7bbb8de662c7b9b1cf9b22f7a2e582bd46d581d68878efb2b861b131d8a1d667", + "wx": "169fb797325843faff2f7a5b5445da9e2fd6226f7ef90ef0bfe924104b02db8e", + "wy": "7bbb8de662c7b9b1cf9b22f7a2e582bd46d581d68878efb2b861b131d8a1d667" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004169fb797325843faff2f7a5b5445da9e2fd6226f7ef90ef0bfe924104b02db8e7bbb8de662c7b9b1cf9b22f7a2e582bd46d581d68878efb2b861b131d8a1d667", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFp+3lzJYQ/r/L3pbVEXani/WIm9+\n+Q7wv+kkEEsC2457u43mYse5sc+bIvei5YK9RtWB1oh477K4YbEx2KHWZw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 439, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502206b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296022100b6db6db6249249254924924924924924625bd7a09bec4ca81bcdd9f8fd6b63cc", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04271cd89c000143096b62d4e9e4ca885aef2f7023d18affdaf8b7b548981487540a1c6e954e32108435b55fa385b0f76481a609b9149ccb4b02b2ca47fe8e4da5", + "wx": "271cd89c000143096b62d4e9e4ca885aef2f7023d18affdaf8b7b54898148754", + "wy": "0a1c6e954e32108435b55fa385b0f76481a609b9149ccb4b02b2ca47fe8e4da5" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004271cd89c000143096b62d4e9e4ca885aef2f7023d18affdaf8b7b548981487540a1c6e954e32108435b55fa385b0f76481a609b9149ccb4b02b2ca47fe8e4da5", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJxzYnAABQwlrYtTp5MqIWu8vcCPR\niv/a+Le1SJgUh1QKHG6VTjIQhDW1X6OFsPdkgaYJuRScy0sCsspH/o5NpQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 440, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304502206b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296022100cccccccc00000000cccccccccccccccc971f2ef152794b9d8fc7d568c9e8eaa7", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "043d0bc7ed8f09d2cb7ddb46ebc1ed799ab1563a9ab84bf524587a220afe499c12e22dc3b3c103824a4f378d96adb0a408abf19ce7d68aa6244f78cb216fa3f8df", + "wx": "3d0bc7ed8f09d2cb7ddb46ebc1ed799ab1563a9ab84bf524587a220afe499c12", + "wy": "00e22dc3b3c103824a4f378d96adb0a408abf19ce7d68aa6244f78cb216fa3f8df" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200043d0bc7ed8f09d2cb7ddb46ebc1ed799ab1563a9ab84bf524587a220afe499c12e22dc3b3c103824a4f378d96adb0a408abf19ce7d68aa6244f78cb216fa3f8df", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPQvH7Y8J0st920brwe15mrFWOpq4\nS/UkWHoiCv5JnBLiLcOzwQOCSk83jZatsKQIq/Gc59aKpiRPeMshb6P43w==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 441, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402206b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c29602203333333300000000333333333333333325c7cbbc549e52e763f1f55a327a3aaa", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04a6c885ade1a4c566f9bb010d066974abb281797fa701288c721bcbd23663a9b72e424b690957168d193a6096fc77a2b004a9c7d467e007e1f2058458f98af316", + "wx": "00a6c885ade1a4c566f9bb010d066974abb281797fa701288c721bcbd23663a9b7", + "wy": "2e424b690957168d193a6096fc77a2b004a9c7d467e007e1f2058458f98af316" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004a6c885ade1a4c566f9bb010d066974abb281797fa701288c721bcbd23663a9b72e424b690957168d193a6096fc77a2b004a9c7d467e007e1f2058458f98af316", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpsiFreGkxWb5uwENBml0q7KBeX+n\nASiMchvL0jZjqbcuQktpCVcWjRk6YJb8d6KwBKnH1GfgB+HyBYRY+YrzFg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 442, + "comment": "extreme value for k and s^-1", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402206b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296022049249248db6db6dbb6db6db6db6db6db5a8b230d0b2b51dcd7ebf0c9fef7c185", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "048d3c2c2c3b765ba8289e6ac3812572a25bf75df62d87ab7330c3bdbad9ebfa5c4c6845442d66935b238578d43aec54f7caa1621d1af241d4632e0b780c423f5d", + "wx": "008d3c2c2c3b765ba8289e6ac3812572a25bf75df62d87ab7330c3bdbad9ebfa5c", + "wy": "4c6845442d66935b238578d43aec54f7caa1621d1af241d4632e0b780c423f5d" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200048d3c2c2c3b765ba8289e6ac3812572a25bf75df62d87ab7330c3bdbad9ebfa5c4c6845442d66935b238578d43aec54f7caa1621d1af241d4632e0b780c423f5d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjTwsLDt2W6gonmrDgSVyolv3XfYt\nh6tzMMO9utnr+lxMaEVELWaTWyOFeNQ67FT3yqFiHRryQdRjLgt4DEI/XQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 443, + "comment": "extreme value for k", + "flags": [ + "ArithmeticError" + ], + "msg": "313233343030", + "sig": "304402206b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296022016a4502e2781e11ac82cbc9d1edd8c981584d13e18411e2f6e0478c34416e3bb", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", + "wx": "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", + "wy": "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaxfR8uEsQkf4vOblY6RA8ncDfYEt\n6zOg9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9Q==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 444, + "comment": "public key shares x-coordinate with generator", + "flags": [ + "PointDuplication" + ], + "msg": "313233343030", + "sig": "3045022100bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca6050230220249249246db6db6ddb6db6db6db6db6dad4591868595a8ee6bf5f864ff7be0c2", + "result": "invalid" + }, + { + "tcId": 445, + "comment": "public key shares x-coordinate with generator", + "flags": [ + "PointDuplication" + ], + "msg": "313233343030", + "sig": "3044022044a5ad0ad0636d9f12bc9e0a6bdd5e1cbcb012ea7bf091fcec15b0c43202d52e0220249249246db6db6ddb6db6db6db6db6dad4591868595a8ee6bf5f864ff7be0c2", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296b01cbd1c01e58065711814b583f061e9d431cca994cea1313449bf97c840ae0a", + "wx": "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", + "wy": "00b01cbd1c01e58065711814b583f061e9d431cca994cea1313449bf97c840ae0a" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296b01cbd1c01e58065711814b583f061e9d431cca994cea1313449bf97c840ae0a", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaxfR8uEsQkf4vOblY6RA8ncDfYEt\n6zOg9KE5RdiYwpawHL0cAeWAZXEYFLWD8GHp1DHMqZTOoTE0Sb+XyECuCg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 446, + "comment": "public key shares x-coordinate with generator", + "flags": [ + "PointDuplication" + ], + "msg": "313233343030", + "sig": "3045022100bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca6050230220249249246db6db6ddb6db6db6db6db6dad4591868595a8ee6bf5f864ff7be0c2", + "result": "invalid" + }, + { + "tcId": 447, + "comment": "public key shares x-coordinate with generator", + "flags": [ + "PointDuplication" + ], + "msg": "313233343030", + "sig": "3044022044a5ad0ad0636d9f12bc9e0a6bdd5e1cbcb012ea7bf091fcec15b0c43202d52e0220249249246db6db6ddb6db6db6db6db6dad4591868595a8ee6bf5f864ff7be0c2", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "044f337ccfd67726a805e4f1600ae2849df3807eca117380239fbd816900000000ed9dea124cc8c396416411e988c30f427eb504af43a3146cd5df7ea60666d685", + "wx": "4f337ccfd67726a805e4f1600ae2849df3807eca117380239fbd816900000000", + "wy": "00ed9dea124cc8c396416411e988c30f427eb504af43a3146cd5df7ea60666d685" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200044f337ccfd67726a805e4f1600ae2849df3807eca117380239fbd816900000000ed9dea124cc8c396416411e988c30f427eb504af43a3146cd5df7ea60666d685", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETzN8z9Z3JqgF5PFgCuKEnfOAfsoR\nc4Ajn72BaQAAAADtneoSTMjDlkFkEemIww9CfrUEr0OjFGzV336mBmbWhQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 448, + "comment": "x-coordinate of the public key has many trailing 0's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3046022100d434e262a49eab7781e353a3565e482550dd0fd5defa013c7f29745eff3569f10221009b0c0a93f267fb6052fd8077be769c2b98953195d7bc10de844218305c6ba17a", + "result": "valid" + }, + { + "tcId": 449, + "comment": "x-coordinate of the public key has many trailing 0's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "304402200fe774355c04d060f76d79fd7a772e421463489221bf0a33add0be9b1979110b0220500dcba1c69a8fbd43fa4f57f743ce124ca8b91a1f325f3fac6181175df55737", + "result": "valid" + }, + { + "tcId": 450, + "comment": "x-coordinate of the public key has many trailing 0's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3045022100bb40bf217bed3fb3950c7d39f03d36dc8e3b2cd79693f125bfd06595ee1135e30220541bf3532351ebb032710bdb6a1bf1bfc89a1e291ac692b3fa4780745bb55677", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "043cf03d614d8939cfd499a07873fac281618f06b8ff87e8015c3f49726500493584fa174d791c72bf2ce3880a8960dd2a7c7a1338a82f85a9e59cdbde80000000", + "wx": "3cf03d614d8939cfd499a07873fac281618f06b8ff87e8015c3f497265004935", + "wy": "0084fa174d791c72bf2ce3880a8960dd2a7c7a1338a82f85a9e59cdbde80000000" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200043cf03d614d8939cfd499a07873fac281618f06b8ff87e8015c3f49726500493584fa174d791c72bf2ce3880a8960dd2a7c7a1338a82f85a9e59cdbde80000000", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPPA9YU2JOc/UmaB4c/rCgWGPBrj/\nh+gBXD9JcmUASTWE+hdNeRxyvyzjiAqJYN0qfHoTOKgvhanlnNvegAAAAA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 451, + "comment": "y-coordinate of the public key has many trailing 0's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "30440220664eb7ee6db84a34df3c86ea31389a5405badd5ca99231ff556d3e75a233e73a022059f3c752e52eca46137642490a51560ce0badc678754b8f72e51a2901426a1bd", + "result": "valid" + }, + { + "tcId": 452, + "comment": "y-coordinate of the public key has many trailing 0's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "304502204cd0429bbabd2827009d6fcd843d4ce39c3e42e2d1631fd001985a79d1fd8b430221009638bf12dd682f60be7ef1d0e0d98f08b7bca77a1a2b869ae466189d2acdabe3", + "result": "valid" + }, + { + "tcId": 453, + "comment": "y-coordinate of the public key has many trailing 0's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3046022100e56c6ea2d1b017091c44d8b6cb62b9f460e3ce9aed5e5fd41e8added97c56c04022100a308ec31f281e955be20b457e463440b4fcf2b80258078207fc1378180f89b55", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "043cf03d614d8939cfd499a07873fac281618f06b8ff87e8015c3f4972650049357b05e8b186e38d41d31c77f5769f22d58385ecc857d07a561a6324217fffffff", + "wx": "3cf03d614d8939cfd499a07873fac281618f06b8ff87e8015c3f497265004935", + "wy": "7b05e8b186e38d41d31c77f5769f22d58385ecc857d07a561a6324217fffffff" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200043cf03d614d8939cfd499a07873fac281618f06b8ff87e8015c3f4972650049357b05e8b186e38d41d31c77f5769f22d58385ecc857d07a561a6324217fffffff", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPPA9YU2JOc/UmaB4c/rCgWGPBrj/\nh+gBXD9JcmUASTV7BeixhuONQdMcd/V2nyLVg4XsyFfQelYaYyQhf////w==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 454, + "comment": "y-coordinate of the public key has many trailing 1's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "304402201158a08d291500b4cabed3346d891eee57c176356a2624fb011f8fbbf34668300220228a8c486a736006e082325b85290c5bc91f378b75d487dda46798c18f285519", + "result": "valid" + }, + { + "tcId": 455, + "comment": "y-coordinate of the public key has many trailing 1's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3045022100b1db9289649f59410ea36b0c0fc8d6aa2687b29176939dd23e0dde56d309fa9d02203e1535e4280559015b0dbd987366dcf43a6d1af5c23c7d584e1c3f48a1251336", + "result": "valid" + }, + { + "tcId": 456, + "comment": "y-coordinate of the public key has many trailing 1's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3046022100b7b16e762286cb96446aa8d4e6e7578b0a341a79f2dd1a220ac6f0ca4e24ed86022100ddc60a700a139b04661c547d07bbb0721780146df799ccf55e55234ecb8f12bc", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "042829c31faa2e400e344ed94bca3fcd0545956ebcfe8ad0f6dfa5ff8effffffffa01aafaf000e52585855afa7676ade284113099052df57e7eb3bd37ebeb9222e", + "wx": "2829c31faa2e400e344ed94bca3fcd0545956ebcfe8ad0f6dfa5ff8effffffff", + "wy": "00a01aafaf000e52585855afa7676ade284113099052df57e7eb3bd37ebeb9222e" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200042829c31faa2e400e344ed94bca3fcd0545956ebcfe8ad0f6dfa5ff8effffffffa01aafaf000e52585855afa7676ade284113099052df57e7eb3bd37ebeb9222e", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKCnDH6ouQA40TtlLyj/NBUWVbrz+\nitD236X/jv////+gGq+vAA5SWFhVr6dnat4oQRMJkFLfV+frO9N+vrkiLg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 457, + "comment": "x-coordinate of the public key has many trailing 1's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3045022100d82a7c2717261187c8e00d8df963ff35d796edad36bc6e6bd1c91c670d9105b402203dcabddaf8fcaa61f4603e7cbac0f3c0351ecd5988efb23f680d07debd139929", + "result": "valid" + }, + { + "tcId": 458, + "comment": "x-coordinate of the public key has many trailing 1's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "304402205eb9c8845de68eb13d5befe719f462d77787802baff30ce96a5cba063254af7802202c026ae9be2e2a5e7ca0ff9bbd92fb6e44972186228ee9a62b87ddbe2ef66fb5", + "result": "valid" + }, + { + "tcId": 459, + "comment": "x-coordinate of the public key has many trailing 1's", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "304602210096843dd03c22abd2f3b782b170239f90f277921becc117d0404a8e4e36230c28022100f2be378f526f74a543f67165976de9ed9a31214eb4d7e6db19e1ede123dd991d", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04fffffff948081e6a0458dd8f9e738f2665ff9059ad6aac0708318c4ca9a7a4f55a8abcba2dda8474311ee54149b973cae0c0fb89557ad0bf78e6529a1663bd73", + "wx": "00fffffff948081e6a0458dd8f9e738f2665ff9059ad6aac0708318c4ca9a7a4f5", + "wy": "5a8abcba2dda8474311ee54149b973cae0c0fb89557ad0bf78e6529a1663bd73" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004fffffff948081e6a0458dd8f9e738f2665ff9059ad6aac0708318c4ca9a7a4f55a8abcba2dda8474311ee54149b973cae0c0fb89557ad0bf78e6529a1663bd73", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE////+UgIHmoEWN2PnnOPJmX/kFmt\naqwHCDGMTKmnpPVairy6LdqEdDEe5UFJuXPK4MD7iVV60L945lKaFmO9cw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 460, + "comment": "x-coordinate of the public key is large", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "30440220766456dce1857c906f9996af729339464d27e9d98edc2d0e3b760297067421f60220402385ecadae0d8081dccaf5d19037ec4e55376eced699e93646bfbbf19d0b41", + "result": "valid" + }, + { + "tcId": 461, + "comment": "x-coordinate of the public key is large", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3046022100c605c4b2edeab20419e6518a11b2dbc2b97ed8b07cced0b19c34f777de7b9fd9022100edf0f612c5f46e03c719647bc8af1b29b2cde2eda700fb1cff5e159d47326dba", + "result": "valid" + }, + { + "tcId": 462, + "comment": "x-coordinate of the public key is large", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3046022100d48b68e6cabfe03cf6141c9ac54141f210e64485d9929ad7b732bfe3b7eb8a84022100feedae50c61bd00e19dc26f9b7e2265e4508c389109ad2f208f0772315b6c941", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0400000003fa15f963949d5f03a6f5c7f86f9e0015eeb23aebbff1173937ba748e1099872070e8e87c555fa13659cca5d7fadcfcb0023ea889548ca48af2ba7e71", + "wx": "03fa15f963949d5f03a6f5c7f86f9e0015eeb23aebbff1173937ba748e", + "wy": "1099872070e8e87c555fa13659cca5d7fadcfcb0023ea889548ca48af2ba7e71" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000400000003fa15f963949d5f03a6f5c7f86f9e0015eeb23aebbff1173937ba748e1099872070e8e87c555fa13659cca5d7fadcfcb0023ea889548ca48af2ba7e71", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAAAAA/oV+WOUnV8DpvXH+G+eABXu\nsjrrv/EXOTe6dI4QmYcgcOjofFVfoTZZzKXX+tz8sAI+qIlUjKSK8rp+cQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 463, + "comment": "x-coordinate of the public key is small", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3046022100b7c81457d4aeb6aa65957098569f0479710ad7f6595d5874c35a93d12a5dd4c7022100b7961a0b652878c2d568069a432ca18a1a9199f2ca574dad4b9e3a05c0a1cdb3", + "result": "valid" + }, + { + "tcId": 464, + "comment": "x-coordinate of the public key is small", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "304402206b01332ddb6edfa9a30a1321d5858e1ee3cf97e263e669f8de5e9652e76ff3f702205939545fced457309a6a04ace2bd0f70139c8f7d86b02cb1cc58f9e69e96cd5a", + "result": "valid" + }, + { + "tcId": 465, + "comment": "x-coordinate of the public key is small", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3046022100efdb884720eaeadc349f9fc356b6c0344101cd2fd8436b7d0e6a4fb93f106361022100f24bee6ad5dc05f7613975473aadf3aacba9e77de7d69b6ce48cb60d8113385d", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04bcbb2914c79f045eaa6ecbbc612816b3be5d2d6796707d8125e9f851c18af015000000001352bb4a0fa2ea4cceb9ab63dd684ade5a1127bcf300a698a7193bc2", + "wx": "00bcbb2914c79f045eaa6ecbbc612816b3be5d2d6796707d8125e9f851c18af015", + "wy": "1352bb4a0fa2ea4cceb9ab63dd684ade5a1127bcf300a698a7193bc2" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004bcbb2914c79f045eaa6ecbbc612816b3be5d2d6796707d8125e9f851c18af015000000001352bb4a0fa2ea4cceb9ab63dd684ade5a1127bcf300a698a7193bc2", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvLspFMefBF6qbsu8YSgWs75dLWeW\ncH2BJen4UcGK8BUAAAAAE1K7Sg+i6kzOuatj3WhK3loRJ7zzAKaYpxk7wg==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 466, + "comment": "y-coordinate of the public key is small", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3044022031230428405560dcb88fb5a646836aea9b23a23dd973dcbe8014c87b8b20eb0702200f9344d6e812ce166646747694a41b0aaf97374e19f3c5fb8bd7ae3d9bd0beff", + "result": "valid" + }, + { + "tcId": 467, + "comment": "y-coordinate of the public key is small", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3046022100caa797da65b320ab0d5c470cda0b36b294359c7db9841d679174db34c4855743022100cf543a62f23e212745391aaf7505f345123d2685ee3b941d3de6d9b36242e5a0", + "result": "valid" + }, + { + "tcId": 468, + "comment": "y-coordinate of the public key is small", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "304502207e5f0ab5d900d3d3d7867657e5d6d36519bc54084536e7d21c336ed8001859450221009450c07f201faec94b82dfb322e5ac676688294aad35aa72e727ff0b19b646aa", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "google-wycheproof", + "version": "0.9rc5" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04bcbb2914c79f045eaa6ecbbc612816b3be5d2d6796707d8125e9f851c18af015fffffffeecad44b6f05d15b33146549c2297b522a5eed8430cff596758e6c43d", + "wx": "00bcbb2914c79f045eaa6ecbbc612816b3be5d2d6796707d8125e9f851c18af015", + "wy": "00fffffffeecad44b6f05d15b33146549c2297b522a5eed8430cff596758e6c43d" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004bcbb2914c79f045eaa6ecbbc612816b3be5d2d6796707d8125e9f851c18af015fffffffeecad44b6f05d15b33146549c2297b522a5eed8430cff596758e6c43d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvLspFMefBF6qbsu8YSgWs75dLWeW\ncH2BJen4UcGK8BX////+7K1EtvBdFbMxRlScIpe1IqXu2EMM/1lnWObEPQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 469, + "comment": "y-coordinate of the public key is large", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3046022100d7d70c581ae9e3f66dc6a480bf037ae23f8a1e4a2136fe4b03aa69f0ca25b35602210089c460f8a5a5c2bbba962c8a3ee833a413e85658e62a59e2af41d9127cc47224", + "result": "valid" + }, + { + "tcId": 470, + "comment": "y-coordinate of the public key is large", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "30440220341c1b9ff3c83dd5e0dfa0bf68bcdf4bb7aa20c625975e5eeee34bb396266b34022072b69f061b750fd5121b22b11366fad549c634e77765a017902a67099e0a4469", + "result": "valid" + }, + { + "tcId": 471, + "comment": "y-coordinate of the public key is large", + "flags": [ + "EdgeCasePublicKey" + ], + "msg": "4d657373616765", + "sig": "3045022070bebe684cdcb5ca72a42f0d873879359bd1781a591809947628d313a3814f67022100aec03aca8f5587a4d535fa31027bbe9cc0e464b1c3577f4c2dcde6b2094798a9", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "github/davidben/ecdsa-non-minimal-tag", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "0404aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad587d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d", + "wx": "04aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad5", + "wy": "0087d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d0301070342000404aaec73635726f213fb8a9e64da3b8632e41495a944d0045b522eba7240fad587d9315798aaa3a5ba01775787ced05eaaf7b4e09fc81d6d1aa546e8365d525d", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBKrsc2NXJvIT+4qeZNo7hjLkFJWp\nRNAEW1IuunJA+tWH2TFXmKqjpboBd1eHztBeqve04J/IHW0apUboNl1SXQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 472, + "comment": "signature with non-minimal SEQUENCE tag", + "flags": [ + "InvalidEncoding" + ], + "msg": "", + "sig": "3f1045022100b292a619339f6e567a305c951c0dcbcc42d16e47f219f9e98e76e09d8770b34a02200177e60492c5a8242f76f07bfe3661bde59ec2a17ce5bd2dab2abebdf89a62e2", + "result": "invalid" + }, + { + "tcId": 473, + "comment": "signature with non-minimal INTEGER tag on r", + "flags": [ + "InvalidEncoding" + ], + "msg": "", + "sig": "30461f022100b292a619339f6e567a305c951c0dcbcc42d16e47f219f9e98e76e09d8770b34a02200177e60492c5a8242f76f07bfe3661bde59ec2a17ce5bd2dab2abebdf89a62e2", + "result": "invalid" + }, + { + "tcId": 474, + "comment": "signature with non-minimal INTEGER tag on s", + "flags": [ + "InvalidEncoding" + ], + "msg": "", + "sig": "3046022100b292a619339f6e567a305c951c0dcbcc42d16e47f219f9e98e76e09d8770b34a1f02200177e60492c5a8242f76f07bfe3661bde59ec2a17ce5bd2dab2abebdf89a62e2", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04264d796a0dab9b376d34eea6fe297dde1c7b73e53944bc96c8f1e8a6850bb6c9cf5308020eed460c649ddae61d4ef8bb79958113f106befaf4f18876d12a5e64", + "wx": "264d796a0dab9b376d34eea6fe297dde1c7b73e53944bc96c8f1e8a6850bb6c9", + "wy": "cf5308020eed460c649ddae61d4ef8bb79958113f106befaf4f18876d12a5e64" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004264d796a0dab9b376d34eea6fe297dde1c7b73e53944bc96c8f1e8a6850bb6c9cf5308020eed460c649ddae61d4ef8bb79958113f106befaf4f18876d12a5e64", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJk15ag2rmzdtNO6m/il93hx7c+U5\nRLyWyPHopoULtsnPUwgCDu1GDGSd2uYdTvi7eZWBE/EGvvr08Yh20SpeZA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 475, + "comment": "r = 5, x = 5 is valid", + "flags": [ + "ValidSignature" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "3026020105022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04add76677e54a92e79bbee36a70e1754838273ec4b39295e4018a587290a7fc73f240d625642c943e610d80d953c5c6b8a12760a624ba3b90a0b7902755e1ae79", + "wx": "add76677e54a92e79bbee36a70e1754838273ec4b39295e4018a587290a7fc73", + "wy": "f240d625642c943e610d80d953c5c6b8a12760a624ba3b90a0b7902755e1ae79" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004add76677e54a92e79bbee36a70e1754838273ec4b39295e4018a587290a7fc73f240d625642c943e610d80d953c5c6b8a12760a624ba3b90a0b7902755e1ae79", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErddmd+VKkuebvuNqcOF1SDgnPsSz\nkpXkAYpYcpCn/HPyQNYlZCyUPmENgNlTxca4oSdgpiS6O5Cgt5AnVeGueQ==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 476, + "comment": "r = 6, x = 5 is invalid", + "flags": [ + "ArithmeticError" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "3026020106022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04264d796a0dab9b376d34eea6fe297dde1c7b73e53944bc96c8f1e8a6850bb6c9cf5308020eed460c649ddae61d4ef8bb79958113f106befaf4f18876d12a5e64", + "wx": "264d796a0dab9b376d34eea6fe297dde1c7b73e53944bc96c8f1e8a6850bb6c9", + "wy": "cf5308020eed460c649ddae61d4ef8bb79958113f106befaf4f18876d12a5e64" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004264d796a0dab9b376d34eea6fe297dde1c7b73e53944bc96c8f1e8a6850bb6c9cf5308020eed460c649ddae61d4ef8bb79958113f106befaf4f18876d12a5e64", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJk15ag2rmzdtNO6m/il93hx7c+U5\nRLyWyPHopoULtsnPUwgCDu1GDGSd2uYdTvi7eZWBE/EGvvr08Yh20SpeZA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 477, + "comment": "r = 5 + n, x = 5 is invalid; r was not reduced mod n", + "flags": [ + "ArithmeticError" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632556022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04e7e49dd82812432744fefe723f7f69663214ad1b85c02b2650b4ca0354743a325223c51c415f457aec69cb019bbe3d27585b38f8b79c39dfea6c27c2f0d8146a", + "wx": "e7e49dd82812432744fefe723f7f69663214ad1b85c02b2650b4ca0354743a32", + "wy": "5223c51c415f457aec69cb019bbe3d27585b38f8b79c39dfea6c27c2f0d8146a" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004e7e49dd82812432744fefe723f7f69663214ad1b85c02b2650b4ca0354743a325223c51c415f457aec69cb019bbe3d27585b38f8b79c39dfea6c27c2f0d8146a", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5+Sd2CgSQydE/v5yP39pZjIUrRuF\nwCsmULTKA1R0OjJSI8UcQV9FeuxpywGbvj0nWFs4+LecOd/qbCfC8NgUag==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 478, + "comment": "r = n - 3, x = n - 2 is invalid", + "flags": [ + "ArithmeticError" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "3046022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04ce24c99032d52ac6ead23c0ae3ec68ef41e51a281fd457808c83136d7dcce90e8f7a154b551e9f39c59279357aa491b2a62bdebc2bb78613883fc72936c057e0", + "wx": "ce24c99032d52ac6ead23c0ae3ec68ef41e51a281fd457808c83136d7dcce90e", + "wy": "8f7a154b551e9f39c59279357aa491b2a62bdebc2bb78613883fc72936c057e0" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004ce24c99032d52ac6ead23c0ae3ec68ef41e51a281fd457808c83136d7dcce90e8f7a154b551e9f39c59279357aa491b2a62bdebc2bb78613883fc72936c057e0", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEziTJkDLVKsbq0jwK4+xo70HlGigf\n1FeAjIMTbX3M6Q6PehVLVR6fOcWSeTV6pJGypivevCu3hhOIP8cpNsBX4A==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 479, + "comment": "r = 3, x = n + 3 is the smallest possible x with a reduction", + "flags": [ + "ValidSignature" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "3026020103022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", + "result": "valid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04c981c75ddfd3910387eb147098fe5658c43aaaa34c681cd6d514b6ad5b6baa6d14fbddb53a6e6db18e90602bc60f2e736d625765f7f2cca4be76f4eeccf12c18", + "wx": "c981c75ddfd3910387eb147098fe5658c43aaaa34c681cd6d514b6ad5b6baa6d", + "wy": "14fbddb53a6e6db18e90602bc60f2e736d625765f7f2cca4be76f4eeccf12c18" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004c981c75ddfd3910387eb147098fe5658c43aaaa34c681cd6d514b6ad5b6baa6d14fbddb53a6e6db18e90602bc60f2e736d625765f7f2cca4be76f4eeccf12c18", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyYHHXd/TkQOH6xRwmP5WWMQ6qqNM\naBzW1RS2rVtrqm0U+921Om5tsY6QYCvGDy5zbWJXZffyzKS+dvTuzPEsGA==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 480, + "comment": "r = 4, x = n + 3 is invalid", + "flags": [ + "ArithmeticError" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "3026020104022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "040ec505bc19b14a43e05678cccf07a443d3e871a2e19b68a4da91859a0650f32477300e4f64e9982d94dff5d294428bb37cc9be66117cae9c389d2d495f68b987", + "wx": "0ec505bc19b14a43e05678cccf07a443d3e871a2e19b68a4da91859a0650f324", + "wy": "77300e4f64e9982d94dff5d294428bb37cc9be66117cae9c389d2d495f68b987" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200040ec505bc19b14a43e05678cccf07a443d3e871a2e19b68a4da91859a0650f32477300e4f64e9982d94dff5d294428bb37cc9be66117cae9c389d2d495f68b987", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDsUFvBmxSkPgVnjMzwekQ9PocaLh\nm2ik2pGFmgZQ8yR3MA5PZOmYLZTf9dKUQouzfMm+ZhF8rpw4nS1JX2i5hw==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 481, + "comment": "r = p - n + 5, x = 5 is invalid; r is too large to compare r + n with x", + "flags": [ + "ArithmeticError" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "303502104319055358e8617b0c46353d039cdab3022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "github/davidben/ecdsa-r-s-edge-cases", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "04d42ce21e730f3d2a84e949828574b4dceeb77d4f89556735f34fba03028ee3ed32daf8bfd3f0a7a0821170840f4e032056722386b35d9bdd4f4f450696f4fb52", + "wx": "d42ce21e730f3d2a84e949828574b4dceeb77d4f89556735f34fba03028ee3ed", + "wy": "32daf8bfd3f0a7a0821170840f4e032056722386b35d9bdd4f4f450696f4fb52" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d03010703420004d42ce21e730f3d2a84e949828574b4dceeb77d4f89556735f34fba03028ee3ed32daf8bfd3f0a7a0821170840f4e032056722386b35d9bdd4f4f450696f4fb52", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1CziHnMPPSqE6UmChXS03O63fU+J\nVWc180+6AwKO4+0y2vi/0/CnoIIRcIQPTgMgVnIjhrNdm91PT0UGlvT7Ug==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 482, + "comment": "r = 2^256 - n + 5, x = 5 is invalid; r + n is too large to compare r + n with x, and overflows 2^256 bits", + "flags": [ + "ArithmeticError" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "3042021d00ffffffff00000000000000004319055258e8617b0c46353d039cdab4022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254e", + "result": "invalid" + } + ] + }, + { + "type": "EcdsaVerify", + "source": { + "name": "github/davidben/ecdsa-s-pow2", + "version": "0.1" + }, + "publicKey": { + "type": "EcPublicKey", + "curve": "secp256r1", + "keySize": 256, + "uncompressed": "046955a72c0dad4ed60356f7692649b951fa8ea474bf1cc9549b96c68de4782dfae012ffc24e92c72be71ff2622ee7e549dcb59e7b708f6921c4d4ea29512e2aba", + "wx": "6955a72c0dad4ed60356f7692649b951fa8ea474bf1cc9549b96c68de4782dfa", + "wy": "e012ffc24e92c72be71ff2622ee7e549dcb59e7b708f6921c4d4ea29512e2aba" + }, + "publicKeyDer": "3059301306072a8648ce3d020106082a8648ce3d030107034200046955a72c0dad4ed60356f7692649b951fa8ea474bf1cc9549b96c68de4782dfae012ffc24e92c72be71ff2622ee7e549dcb59e7b708f6921c4d4ea29512e2aba", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaVWnLA2tTtYDVvdpJkm5UfqOpHS/\nHMlUm5bGjeR4LfrgEv/CTpLHK+cf8mIu5+VJ3LWee3CPaSHE1OopUS4qug==\n-----END PUBLIC KEY-----\n", + "sha": "SHA-256", + "tests": [ + { + "tcId": 483, + "comment": "s = 2^128", + "flags": [ + "ValidSignature" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "303502205f6d3aa0d327c13702513d4299f297ab182fcf670f96291fab19572e7a6e01c402110100000000000000000000000000000000", + "result": "valid" + }, + { + "tcId": 484, + "comment": "s = n - 2^128", + "flags": [ + "ValidSignature" + ], + "msg": "68656c6c6f2c20776f726c64", + "sig": "304502205f6d3aa0d327c13702513d4299f297ab182fcf670f96291fab19572e7a6e01c4022100ffffffff00000000fffffffffffffffebce6faada7179e84f3b9cac2fc632551", + "result": "valid" + } + ] + } + ] +} From b9b604e2e2bbca5e305c186e289634783b51bf54 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Oct 2026 00:07:23 +0000 Subject: [PATCH 6/8] Planted bugs: 35 of 35 caught, with ENUMERATED and valid Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AovXVgJBHGiN44TxQM4dcd --- tests/PLANTED-BUGS.md | 64 +++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/tests/PLANTED-BUGS.md b/tests/PLANTED-BUGS.md index 6f7f1e9..ad9d8dc 100644 --- a/tests/PLANTED-BUGS.md +++ b/tests/PLANTED-BUGS.md @@ -1,40 +1,44 @@ # Planted bugs -Written by `tests/plant_bugs.py`. Each bug is a change of one line, to `Der/Basic.lean` (26 bugs) or to `Main.lean` (5), tested with `tests/run_tests.py --count 5000 --no-oracles`, which builds only the executable and compares it with the model alone. Every bug must give failures, and the unchanged code none. A bug that does not build, or a run that gives up or dies, counts as missed. +Written by `tests/plant_bugs.py`. Each bug is a change of one line, to `Der/Basic.lean` (29 bugs) or to `Main.lean` (6), tested with `tests/run_tests.py --count 5000 --no-oracles`, which builds only the executable and compares it with the model alone. Every bug must give failures, and the unchanged code none. A bug that does not build, or a run that gives up or dies, counts as missed. | Planted bug | Failures | |---|---| | none | 0 | | the long form is read for a length below 128 | 991 | -| the long form is refused for a length below 256 | 2663 | -| a leading zero length octet is read | 267 | -| the indefinite length octet 0x80 is read as length 0 | 1391 | -| a tag number below 31 is read in the high form | 1939 | -| a first subsequent tag octet 0x80 is read | 1104 | -| the continuation bit of a tag octet is tested as 0x40 | 4714 | -| the constructed bit is read from bit 7 | 12878 | -| a BOOLEAN 01 is read | 493 | -| an INTEGER with a redundant leading zero octet is read | 412 | -| a NULL with one contents octet is read | 48 | -| universal tag 0 is read | 154 | -| a primitive SEQUENCE is read | 81 | -| a constructed INTEGER is read | 15 | -| universal 29 is taken for a primitive type | 662 | -| the reserved universal numbers start at 38, not 37 | 711 | -| an element may run one octet past the one it is in | 432 | -| the elements of a constructed element must take one octet more than it holds | 5444 | -| the reader's fuel is half the length of the input | 444 | -| decodeAll allows octets after the element | 2404 | -| long numbers: the first half is shifted by the length of the first half, not the second | 529 | -| readInt takes a first octet 0x80 for a positive integer | 511 | -| writeInt writes 128 with no zero octet in front | 628 | -| the writer gives no subsequent tag octet but the last its bit 8 | 3239 | -| the writer writes length 128 in the short form | 46 | -| the writer writes tag number 31 in one octet | 406 | -| Main.lean: `all` allows octets after the element | 2404 | +| the long form is refused for a length below 256 | 2739 | +| a leading zero length octet is read | 274 | +| the indefinite length octet 0x80 is read as length 0 | 1405 | +| a tag number below 31 is read in the high form | 1977 | +| a first subsequent tag octet 0x80 is read | 1128 | +| the continuation bit of a tag octet is tested as 0x40 | 4611 | +| the constructed bit is read from bit 7 | 12880 | +| a BOOLEAN 01 is read | 571 | +| an INTEGER with a redundant leading zero octet is read | 788 | +| a NULL with one contents octet is read | 159 | +| universal tag 0 is read | 1038 | +| a primitive SEQUENCE is read | 300 | +| a constructed INTEGER is read | 51 | +| universal 29 is taken for a primitive type | 1133 | +| the reserved universal numbers start at 38, not 37 | 982 | +| an element may run one octet past the one it is in | 423 | +| the elements of a constructed element must take one octet more than it holds | 5359 | +| the reader's fuel is half the length of the input | 458 | +| decodeAll allows octets after the element | 2378 | +| long numbers: the first half is shifted by the length of the first half, not the second | 530 | +| readInt takes a first octet 0x80 for a positive integer | 512 | +| writeInt writes 128 with no zero octet in front | 631 | +| the writer gives no subsequent tag octet but the last its bit 8 | 4067 | +| the writer writes length 128 in the short form | 36 | +| the writer writes tag number 31 in one octet | 682 | +| Main.lean: `all` allows octets after the element | 2378 | | Main.lean: `der decode` exits with 0 when the input is not an element | 6 | -| Main.lean: the CR of a CR LF line ending is kept | 48069 | -| Main.lean: a primitive element is printed with its number before its class | 82169 | +| Main.lean: the CR of a CR LF line ending is kept | 59695 | +| Main.lean: a primitive element is printed with its number before its class | 148044 | | Main.lean: upper-case hex digits are read | 4 | +| an ENUMERATED is not checked | 621 | +| valid takes a primitive element of class 4 | 208 | +| valid does not look inside a constructed element | 1292 | +| Main.lean: `valid` answers OK for every tree | 5130 | -Bugs caught: 31 of 31 +Bugs caught: 35 of 35 From 7fb4f0bf9513646c2d3f55699a7f8408f7fbf677 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Oct 2026 00:28:11 +0000 Subject: [PATCH 7/8] encoding_unique: state it on the writer's encoding The bar check asks every approved statement to mention the code under proof; encoding_unique spoke of `Spec.der` alone, so CI failed item 3. It now takes the writer's output, `Der.encode v ++ r = Der.encode w ++ s`, which encode_eq shows is `der`. The proof rewrites by encode_eq. lake comparator --paranoid accepts all 11 statements, and bar/check.py passes item 3. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AovXVgJBHGiN44TxQM4dcd --- Proofs/Main.lean | 5 +++-- Spec.lean | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Proofs/Main.lean b/Proofs/Main.lean index a802e34..12b86a1 100644 --- a/Proofs/Main.lean +++ b/Proofs/Main.lean @@ -22,8 +22,9 @@ theorem decode_iff (b : List UInt8) (v : Element) (rest : List UInt8) : DerProofs.decode_iff' b v rest theorem encoding_unique (v w : Element) (r s : List UInt8) (hv : Valid v) (hw : Valid w) - (h : der v ++ r = der w ++ s) : v = w ∧ r = s := - DerProofs.encoding_unique' v w r s hv hw h + (h : Der.encode v ++ r = Der.encode w ++ s) : v = w ∧ r = s := by + rw [DerProofs.encode_eq' v, DerProofs.encode_eq' w] at h + exact DerProofs.encoding_unique' v w r s hv hw h theorem decode_encode (v : Element) (rest : List UInt8) (h : Valid v) : Der.decode (Der.encode v ++ rest) = some (v, rest) := diff --git a/Spec.lean b/Spec.lean index 1f964fb..b651505 100644 --- a/Spec.lean +++ b/Spec.lean @@ -34,11 +34,13 @@ theorem decode_iff (b : List UInt8) (v : Element) (rest : List UInt8) : Der.decode b = some (v, rest) ↔ Valid v ∧ b = der v ++ rest := sorry /-- An input starts with the encoding of at most one element: two - accepted elements whose encodings start the same input are the same, - and so is what follows them. With `decode_iff`, the reader gives one - element for each input, and accepts one encoding of each element. -/ + accepted elements whose encodings, as the writer writes them, start + the same input are the same, and so is what follows them. The + writer's encoding is `der` (`encode_eq`). With `decode_iff`, the + reader gives one element for each input, and accepts one encoding of + each element. -/ theorem encoding_unique (v w : Element) (r s : List UInt8) (hv : Valid v) (hw : Valid w) - (h : der v ++ r = der w ++ s) : v = w ∧ r = s := sorry + (h : Der.encode v ++ r = Der.encode w ++ s) : v = w ∧ r = s := sorry /-- Reading what the writer wrote gives back the element and what follows. -/ From 3cdbf7fb6a3bd841918e35fbcda5700416a5a8ad Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 28 Oct 2026 02:39:15 +0000 Subject: [PATCH 8/8] Tests and page: fix the eight findings of the second self-review - Lengths of 5 to 126 octets, with a leading zero and with values that wrap at 32 and 64 bits, each followed by contents a wrong reading would fit; padded lengths of random size. Two planted bugs for them. - A failure whenever the model with an oracle's rules does not give that oracle's answer, whether or not the oracle agrees with the tool. - OpenSSL now checks ENUMERATED with d2i_ASN1_ENUMERATED. - A deep `valid` request; the page says valid's time grows about with the cube of the depth. - Spoiled trees are always refused; the oracle descriptions, the BER wording and the writer's claim say no more than is true. 1,613,447 inputs, 0 failures, every oracle in; 37 of 37 planted bugs caught. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AovXVgJBHGiN44TxQM4dcd --- Der/Basic.lean | 2 +- README.md | 8 +- SPEC.md | 2 +- Spec.lean | 6 +- Spec/Defs.lean | 6 +- Spec/Element.lean | 5 +- tests/PLANTED-BUGS.md | 70 ++++++------- tests/TEST-RESULTS.md | 139 +++++++++++++------------- tests/oracles/go/main.go | 4 +- tests/oracles/openssl/wrapper.c | 32 +++--- tests/plant_bugs.py | 6 ++ tests/run_tests.py | 172 ++++++++++++++++++++++---------- 12 files changed, 267 insertions(+), 185 deletions(-) diff --git a/Der/Basic.lean b/Der/Basic.lean index 59aeb93..04cb5bb 100644 --- a/Der/Basic.lean +++ b/Der/Basic.lean @@ -52,7 +52,7 @@ def lengthOctets (n : Nat) : List UInt8 := (128 + ds.length).toUInt8 :: ds.map Nat.toUInt8 mutual -/-- The DER encoding of an element. -/ +/-- The encoding of an element, as `Spec.der` writes it. -/ def encode : Element → List UInt8 | .prim cls tag c => identOctets cls false tag ++ lengthOctets c.length ++ c | .cons cls tag cs => diff --git a/README.md b/README.md index 41f16e2..2e1a37a 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Machine-checked proofs show: - **The reader accepts exactly the encodings of the elements this slice accepts.** It accepts an input exactly when the input starts with the encoding X.690 gives an element this slice accepts, and then gives back the element and the octets after it. So it never reads past the end of its input, and every element it gives lies inside its parent's contents. That is exactly DER for the identifier, length and structure of every element and for the contents of BOOLEAN, INTEGER, ENUMERATED and NULL. For the contents of other types it accepts more than DER does. - **One encoding per element, one element per input.** Each element has one encoding the reader accepts, and an input starts with the encoding of at most one element. Writing back what the reader read gives the same octets. For the four checked types this is one encoding per value; for the others, two contents octet strings for the same value are two elements. -- **The writer writes the encoding.** It writes, for every element, the encoding X.690 gives it, and `Der.valid` says whether this slice accepts the element. +- **The writer writes the encoding.** It writes `Spec.der` for every tree, and `Der.valid` says whether this slice accepts the tree. For a tree this slice accepts, `Spec.der` is the encoding X.690 gives it, apart from the contents of the types listed under "What is not proved". - **Integers have one encoding.** `readInt` accepts exactly the contents octets X.690 allows for an INTEGER and gives their value. `writeInt` writes them, and each integer has exactly one. It is tested against a model of X.690 and other implementations; `tests/TEST-RESULTS.md` holds the record. It is a project of [Proven Commons](https://github.com/Proven-Commons/mission), built to the verified bar published there. @@ -30,7 +30,7 @@ From Lean, on `List UInt8`, with the tree `Der.Element` of `Spec/Element.lean`: - `Der.valid : Element → Bool`: whether this slice accepts an element. - `Der.readInt : List UInt8 → Option Int` and `Der.writeInt : Int → List UInt8`: the contents octets of an INTEGER. -An element is `.prim cls tag contents` or `.cons cls tag children`, with the class 0 (universal), 1 (application), 2 (context-specific) or 3 (private). `SPEC.md` says what each function does. +An element is `.prim cls tag contents` or `.cons cls tag children`. The class can be any number in the type; `Der.valid` accepts only 0 (universal), 1 (application), 2 (context-specific) and 3 (private). `SPEC.md` says what each function does. ## What is proved @@ -38,7 +38,7 @@ The statements are in `Spec.lean`. The definitions they use are in `Spec/Defs.le | Claim | Theorem | |-------|---------| -| The writer writes the encoding X.690 gives every element. | `Spec.encode_eq` | +| The writer writes `Spec.der` for every tree. For a tree this slice accepts, that is the encoding X.690 gives it, apart from the contents of the types listed under "What is not proved". | `Spec.encode_eq` | | `Der.valid` says whether this slice accepts an element. | `Spec.valid_iff` | | The reader accepts exactly a DER encoding of such an element, followed by anything, and gives back the element and what follows. | `Spec.decode_iff` | | An input starts with the encoding of at most one element this slice accepts: two whose encodings start the same input are the same, and so is what follows. | `Spec.encoding_unique` | @@ -68,7 +68,7 @@ The elements this slice accepts, `Spec.Valid`, are those whose class is one of t ECDSA-Sig-Value is the second slice. - **Rules that depend on the ASN.1 type.** DER orders the components of a SET by tag (10.3) and of a SET OF by encoding (11.6), leaves out components equal to their default (11.5), and drops trailing zero bits from a named bit list (11.2.2). Without a schema, only a weak condition on a SET can be checked: its components are either in tag order or in encoding order. This slice does not check even that. -- **Time and memory.** No proof bounds them. The reader is written to take time linear in its input: it never measures more of the input than an element's length, and it adds up long tag numbers and lengths by halves. Its recursion depth is the nesting depth. The tests time deep and long inputs. The writer and `Der.valid` are not linear: each level of nesting copies and measures the encoding of what it holds, so their time grows with the square of the nesting depth. Writing a tree 10,000 levels deep takes about 1.6 s, and 100,000 levels about 190 s. +- **Time and memory.** No proof bounds them. The reader is written to take time linear in its input: it never measures more of the input than an element's length, and it adds up long tag numbers and lengths by halves. Its recursion depth is the nesting depth. The writer is not linear: each level of nesting copies the encoding of what it holds, so its time grows with the square of the nesting depth. Writing a tree 10,000 levels deep takes about 1.6 s, and 100,000 levels about 190 s. `Der.valid` is slower: at each level it writes and measures the encoding of what that level holds, so its time grows about with the cube of the nesting depth. It takes about 3.4 s for a tree 1,000 levels deep, and 38 s for 2,000. The tests time only nested requests: each one goes to a process of its own, which must answer within 60 seconds. They read trees 10,000 and 100,000 levels deep, write ones 10,000 deep, and ask `Der.valid` of ones 1,000 deep. - **Reading requests and printing answers in `Main.lean`.** The differential tests cover them. ## What you must trust diff --git a/SPEC.md b/SPEC.md index 1c118a5..a785375 100644 --- a/SPEC.md +++ b/SPEC.md @@ -12,7 +12,7 @@ A reviewer who did not write this specification approves it. The approval also c ## Elements -`Spec/Element.lean` gives the tree: an element is primitive, with a class, a tag number and contents octets, or constructed, with a class, a tag number and a list of elements. The class is 0 (universal), 1 (application), 2 (context-specific) or 3 (private), as in Table 1 of X.690. +`Spec/Element.lean` gives the tree: an element is primitive, with a class, a tag number and contents octets, or constructed, with a class, a tag number and a list of elements. The class can be any number in the type. Table 1 of X.690 has four classes, 0 (universal), 1 (application), 2 (context-specific) and 3 (private), and `Spec.Valid` accepts only these. `Spec/Defs.lean` writes the DER encoding of a tree, `Spec.der`, from X.690: diff --git a/Spec.lean b/Spec.lean index b651505..70fa618 100644 --- a/Spec.lean +++ b/Spec.lean @@ -18,8 +18,10 @@ open Der /-! ## Elements -/ -/-- The writer writes `der v` for every tree. That is a DER encoding of - `v` exactly when this slice accepts `v`: see `decode_iff`. -/ +/-- The writer writes `der v` for every tree. When this slice accepts + `v`, the reader reads it back (see `decode_iff`), and it is the DER + encoding of `v`, apart from the contents of the types this slice does + not check. -/ theorem encode_eq (v : Element) : Der.encode v = der v := sorry /-- `Der.valid` says whether this slice accepts an element. -/ diff --git a/Spec/Defs.lean b/Spec/Defs.lean index edc36c2..ec697b0 100644 --- a/Spec/Defs.lean +++ b/Spec/Defs.lean @@ -69,8 +69,10 @@ def lengthOctets (n : Nat) : List UInt8 := /-! ## Encodings (8.1.1) -/ mutual -/-- The DER encoding of an element: identifier octets, length octets and - contents octets (8.1.1.1). With the definite form there are no +/-- The encoding of an element: identifier octets, length octets and + contents octets (8.1.1.1). For an element this slice accepts, it is + the DER encoding, apart from the contents of the types this slice + does not check. With the definite form there are no end-of-contents octets (8.1.5). The contents octets of a constructed element are the encodings of its elements, one after another (8.1.1.1 c, 3.2, 8.9.2, 8.10.2). -/ diff --git a/Spec/Element.lean b/Spec/Element.lean index 006713d..2a0f607 100644 --- a/Spec/Element.lean +++ b/Spec/Element.lean @@ -10,8 +10,9 @@ namespace Der /-- One element of an encoding (X.690 8.1.1): its tag, given by a class and a number (8.1.2), and its contents (8.1.4). - The class is 0 for universal, 1 for application, 2 for - context-specific and 3 for private, as in Table 1 of X.690. + The class can be any number in the type. Table 1 of X.690 has four: + 0 for universal, 1 for application, 2 for context-specific and 3 + for private. `Spec.Valid` accepts only these four. A primitive element holds its contents octets. A constructed element holds the elements its contents octets encode, in order. -/ diff --git a/tests/PLANTED-BUGS.md b/tests/PLANTED-BUGS.md index ad9d8dc..24ab450 100644 --- a/tests/PLANTED-BUGS.md +++ b/tests/PLANTED-BUGS.md @@ -1,44 +1,46 @@ # Planted bugs -Written by `tests/plant_bugs.py`. Each bug is a change of one line, to `Der/Basic.lean` (29 bugs) or to `Main.lean` (6), tested with `tests/run_tests.py --count 5000 --no-oracles`, which builds only the executable and compares it with the model alone. Every bug must give failures, and the unchanged code none. A bug that does not build, or a run that gives up or dies, counts as missed. +Written by `tests/plant_bugs.py`. Each bug is a change of one line, to `Der/Basic.lean` (31 bugs) or to `Main.lean` (6), tested with `tests/run_tests.py --count 5000 --no-oracles`, which builds only the executable and compares it with the model alone. Every bug must give failures, and the unchanged code none. A bug that does not build, or a run that gives up or dies, counts as missed. | Planted bug | Failures | |---|---| | none | 0 | -| the long form is read for a length below 128 | 991 | -| the long form is refused for a length below 256 | 2739 | -| a leading zero length octet is read | 274 | -| the indefinite length octet 0x80 is read as length 0 | 1405 | -| a tag number below 31 is read in the high form | 1977 | -| a first subsequent tag octet 0x80 is read | 1128 | -| the continuation bit of a tag octet is tested as 0x40 | 4611 | -| the constructed bit is read from bit 7 | 12880 | +| the long form is read for a length below 128 | 1000 | +| the long form is refused for a length below 256 | 2823 | +| a leading zero length octet is read | 1269 | +| a leading zero length octet is read when 5 or more length octets follow it | 1127 | +| a long-form length is kept modulo 2^32 | 488 | +| the indefinite length octet 0x80 is read as length 0 | 1364 | +| a tag number below 31 is read in the high form | 2002 | +| a first subsequent tag octet 0x80 is read | 1098 | +| the continuation bit of a tag octet is tested as 0x40 | 4760 | +| the constructed bit is read from bit 7 | 12792 | | a BOOLEAN 01 is read | 571 | -| an INTEGER with a redundant leading zero octet is read | 788 | -| a NULL with one contents octet is read | 159 | -| universal tag 0 is read | 1038 | -| a primitive SEQUENCE is read | 300 | -| a constructed INTEGER is read | 51 | -| universal 29 is taken for a primitive type | 1133 | -| the reserved universal numbers start at 38, not 37 | 982 | -| an element may run one octet past the one it is in | 423 | -| the elements of a constructed element must take one octet more than it holds | 5359 | -| the reader's fuel is half the length of the input | 458 | -| decodeAll allows octets after the element | 2378 | -| long numbers: the first half is shifted by the length of the first half, not the second | 530 | -| readInt takes a first octet 0x80 for a positive integer | 512 | -| writeInt writes 128 with no zero octet in front | 631 | -| the writer gives no subsequent tag octet but the last its bit 8 | 4067 | -| the writer writes length 128 in the short form | 36 | -| the writer writes tag number 31 in one octet | 682 | -| Main.lean: `all` allows octets after the element | 2378 | +| an INTEGER with a redundant leading zero octet is read | 781 | +| a NULL with one contents octet is read | 148 | +| universal tag 0 is read | 1001 | +| a primitive SEQUENCE is read | 277 | +| a constructed INTEGER is read | 59 | +| universal 29 is taken for a primitive type | 1118 | +| the reserved universal numbers start at 38, not 37 | 845 | +| an element may run one octet past the one it is in | 408 | +| the elements of a constructed element must take one octet more than it holds | 5388 | +| the reader's fuel is half the length of the input | 479 | +| decodeAll allows octets after the element | 2392 | +| long numbers: the first half is shifted by the length of the first half, not the second | 526 | +| readInt takes a first octet 0x80 for a positive integer | 517 | +| writeInt writes 128 with no zero octet in front | 632 | +| the writer gives no subsequent tag octet but the last its bit 8 | 4200 | +| the writer writes length 128 in the short form | 64 | +| the writer writes tag number 31 in one octet | 623 | +| Main.lean: `all` allows octets after the element | 2392 | | Main.lean: `der decode` exits with 0 when the input is not an element | 6 | -| Main.lean: the CR of a CR LF line ending is kept | 59695 | -| Main.lean: a primitive element is printed with its number before its class | 148044 | +| Main.lean: the CR of a CR LF line ending is kept | 59924 | +| Main.lean: a primitive element is printed with its number before its class | 147997 | | Main.lean: upper-case hex digits are read | 4 | -| an ENUMERATED is not checked | 621 | -| valid takes a primitive element of class 4 | 208 | -| valid does not look inside a constructed element | 1292 | -| Main.lean: `valid` answers OK for every tree | 5130 | +| an ENUMERATED is not checked | 649 | +| valid takes a primitive element of class 4 | 214 | +| valid does not look inside a constructed element | 1337 | +| Main.lean: `valid` answers OK for every tree | 5154 | -Bugs caught: 35 of 35 +Bugs caught: 37 of 37 diff --git a/tests/TEST-RESULTS.md b/tests/TEST-RESULTS.md index 8c8ad52..d5d6481 100644 --- a/tests/TEST-RESULTS.md +++ b/tests/TEST-RESULTS.md @@ -3,28 +3,28 @@ Written by `tests/run_tests.py`. Rerun it to check these numbers. - Lean: Lean (version 4.35.0, x86_64-unknown-linux-gnu, commit eb6cafb7290e8bd5f2937522d8d804f58f02bafc, Release) -- Oracle: the model of X.690 (02/2021) in `tests/run_tests.py`; Go cryptobyte, golang.org/x/crypto/cryptobyte v0.25.0 (h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=), built with go version go1.24.7 linux/amd64; Go encoding/asn1, encoding/asn1 of the standard library of go version go1.24.7 linux/amd64; rust-asn1, the asn1 crate 0.24.1 (checksum c9795210620c0cb3f9a7ce4f882808c38e1ef7b347c90591dceae0886e031fb1), built with rustc 1.97.0 (2d8144b78 2026-07-07); OpenSSL, libcrypto of OpenSSL 3.0.13 30 Jan 2024 (Debian package libssl3t64 3.0.13-0ubuntu3.16), through `tests/oracles/openssl/wrapper.c` compiled with cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 +- Oracle: the model of X.690 (02/2021) in `tests/run_tests.py`; Go cryptobyte, golang.org/x/crypto/cryptobyte v0.25.0 (h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=), built with go version go1.24.7 linux/amd64; Go encoding/asn1, encoding/asn1 of the standard library of the installed go version go1.24.7 linux/amd64 (not pinned); rust-asn1, the asn1 crate 0.24.1 (checksum c9795210620c0cb3f9a7ce4f882808c38e1ef7b347c90591dceae0886e031fb1), built with rustc 1.97.0 (2d8144b78 2026-07-07); OpenSSL, libcrypto of OpenSSL 3.0.13 30 Jan 2024 (Debian package libssl3t64 3.0.13-0ubuntu3.16), through `tests/oracles/openssl/wrapper.c` compiled with cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 - Go cryptobyte: golang.org/x/crypto/cryptobyte v0.25.0 (h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=), built with go version go1.24.7 linux/amd64 -- Go encoding/asn1: encoding/asn1 of the standard library of go version go1.24.7 linux/amd64 +- Go encoding/asn1: encoding/asn1 of the standard library of the installed go version go1.24.7 linux/amd64 (not pinned) - rust-asn1: the asn1 crate 0.24.1 (checksum c9795210620c0cb3f9a7ce4f882808c38e1ef7b347c90591dceae0886e031fb1), built with rustc 1.97.0 (2d8144b78 2026-07-07) - OpenSSL: libcrypto of OpenSSL 3.0.13 30 Jan 2024 (Debian package libssl3t64 3.0.13-0ubuntu3.16), through `tests/oracles/openssl/wrapper.c` compiled with cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0 - Oracles left out: none - Seed: 20261027 -- Inputs: 1612540 -- By kind: 0 to 2 octets all 65793, 0 to 2 octets decode 65793, BOOLEAN contents decode 262, ENUMERATED contents decode 66153, INTEGER contents decode 66153, INTEGER contents readint 66153, NULL contents decode 5, Wycheproof ECDSA signatures all 484, X.690 examples all 4, X.690 examples decode 5, X.690 examples encode 5, constructed BOOLEAN, INTEGER and NULL decode 4, high tag numbers all 240, high tag numbers decode 240, high tag numbers encode 240, high tag numbers, cut short decode 232, high tag numbers, first subsequent octet 0x80 all 232, identifier first octets decode 2432, initial length octets decode 2548, integers around 0 and 2^k writeint 10611, lengths 0 to 100,000 all 30, lengths 0 to 100,000 decode 15, lengths 0 to 100,000 encode 30, lengths 0 to 100,000, cut short all 14, lengths 0 to 100,000, length one less decode 14, lengths 0 to 100,000, length one more decode 15, lengths 0 to 100,000, not in the fewest octets all 20, nested 10,000 deep all 5, nested 10,000 deep decode 1, nested 10,000 deep encode 2, nested 100,000 deep all 5, nested 100,000 deep decode 1, random integer contents readint 25000, random integers writeint 25000, random octets decode 50000, random tree all 100000, random tree encode 100000, random tree valid 100000, random tree, BOOLEAN, INTEGER, ENUMERATED or NULL contents changed all 6297, random tree, BOOLEAN, INTEGER, ENUMERATED or NULL contents changed decode 6294, random tree, a bit flipped decode 100000, random tree, an element running past the one it is in all 8434, random tree, an element running past the one it is in decode 8503, random tree, constructed bit flipped all 24803, random tree, constructed bit flipped decode 25257, random tree, cut short decode 100000, random tree, first subsequent tag octet 0x80 all 25179, random tree, first subsequent tag octet 0x80 decode 25024, random tree, high form for a tag below 31 all 19518, random tree, high form for a tag below 31 decode 19483, random tree, indefinite length all 25044, random tree, indefinite length decode 25036, random tree, length not in the fewest octets all 24991, random tree, length not in the fewest octets decode 25157, random tree, length one less all 22163, random tree, length one less decode 22232, random tree, length one more all 25378, random tree, length one more decode 24714, random tree, octets after all 33283, random tree, octets after decode 66717, random tree, one element not accepted encode 100000, random tree, one element not accepted valid 100000, universal tags 0 to 40 decode 410, universal tags 0 to 40 encode 410, universal tags 0 to 40 valid 410, universal tags 0 to 40, high form all 62 +- Inputs: 1613447 +- By kind: 0 to 2 octets all 65793, 0 to 2 octets decode 65793, BOOLEAN contents decode 262, ENUMERATED contents decode 66153, INTEGER contents decode 66153, INTEGER contents readint 66153, NULL contents decode 5, Wycheproof ECDSA signatures all 484, X.690 examples all 4, X.690 examples decode 5, X.690 examples encode 5, constructed BOOLEAN, INTEGER and NULL decode 4, high tag numbers all 240, high tag numbers decode 240, high tag numbers encode 240, high tag numbers, cut short decode 232, high tag numbers, first subsequent octet 0x80 all 232, identifier first octets decode 2432, initial length octets decode 2548, integers around 0 and 2^k writeint 10611, lengths 0 to 100,000 all 30, lengths 0 to 100,000 decode 15, lengths 0 to 100,000 encode 30, lengths 0 to 100,000, cut short all 14, lengths 0 to 100,000, length one less decode 14, lengths 0 to 100,000, length one more decode 15, lengths 0 to 100,000, not in the fewest octets all 65, lengths of 5 to 126 octets that a wrong reading fits all 732, lengths of 5 to 126 octets that a wrong reading fits decode 732, nested 1,000 deep valid 2, nested 10,000 deep all 5, nested 10,000 deep decode 1, nested 10,000 deep encode 2, nested 100,000 deep all 5, nested 100,000 deep decode 1, random integer contents readint 25000, random integers writeint 25000, random octets decode 50000, random tree all 100000, random tree encode 100000, random tree valid 100000, random tree, BOOLEAN, INTEGER, ENUMERATED or NULL contents changed all 6332, random tree, BOOLEAN, INTEGER, ENUMERATED or NULL contents changed decode 6274, random tree, a bit flipped decode 100000, random tree, an element running past the one it is in all 8413, random tree, an element running past the one it is in decode 8302, random tree, constructed bit flipped all 24982, random tree, constructed bit flipped decode 25008, random tree, cut short decode 100000, random tree, first subsequent tag octet 0x80 all 24886, random tree, first subsequent tag octet 0x80 decode 25099, random tree, high form for a tag below 31 all 19580, random tree, high form for a tag below 31 decode 19429, random tree, indefinite length all 24820, random tree, indefinite length decode 24992, random tree, length not in the fewest octets all 25127, random tree, length not in the fewest octets decode 24832, random tree, length one less all 22355, random tree, length one less decode 22225, random tree, length one more all 25079, random tree, length one more decode 25168, random tree, octets after all 33480, random tree, octets after decode 66520, random tree, one element not accepted encode 100000, random tree, one element not accepted valid 100000, universal tags 0 to 40 decode 410, universal tags 0 to 40 encode 410, universal tags 0 to 40 valid 410, universal tags 0 to 40, high form all 62 - Answers that differ from the model: 0 - X.690's examples, answered by the model as the standard gives them: 14 of 14 -- Elements read (`decode` and `all`): 1084679, of which 418556 are accepted; why the model refuses the others: the contents run past the end of the input 191249, the input ends in the length octets 89253, an element runs past the end of the one it is in 74289, the indefinite length (10.1) 53076, a first subsequent tag octet with bits 7 to 1 zero (8.1.2.4.2 c) 51692, octets after the element 47542, a tag number below 31 in the high form (8.1.2.2) 41230, a long-form length with a leading zero octet (10.1) 32709, a primitive universal type in the constructed form 22644, the input ends in the identifier octets 22579, a long-form length below 128 (10.1) 20210, a BOOLEAN contents octet other than 00 and FF (11.1) 4579, universal 8, 11, 16, 17 or 29 in the primitive form 3865, a BOOLEAN without exactly one contents octet (8.2.1) 2884, the initial length octet 0xFF (8.1.3.5 c) 2639, universal tag 0 (X.680 Table 1, 8.1.5) 1589, an INTEGER with a redundant leading octet (8.3.2) 1522, a NULL with contents octets (8.8.2) 1151, an INTEGER with no contents octets (8.3.1) 634, an ENUMERATED with a redundant leading octet (8.4, 8.3.2) 633, an ENUMERATED with no contents octets (8.4, 8.3.1) 154 -- Integer contents read (`readint`): 91153, of which 85899 give a value; why the model refuses the others: an INTEGER with a redundant leading octet (8.3.2) 3560, an INTEGER with no contents octets (8.3.1) 1694 -- Encodings asked for: trees 200687, of which the model reads the tool's encoding back as the tree for 101141 of the 101141 the slice accepts; integers 35611 -- `valid` asked of 200410 trees, of which the slice accepts 100867; the model's answer holds exactly when it reads the tree's encoding back as the tree, for 200410 of 200410 +- Elements read (`decode` and `all`): 1085584, of which 418267 are accepted; why the model refuses the others: the contents run past the end of the input 191528, the input ends in the length octets 89289, an element runs past the end of the one it is in 74239, the indefinite length (10.1) 52794, a first subsequent tag octet with bits 7 to 1 zero (8.1.2.4.2 c) 51453, octets after the element 48089, a tag number below 31 in the high form (8.1.2.2) 41274, a long-form length with a leading zero octet (10.1) 33807, the input ends in the identifier octets 22752, a primitive universal type in the constructed form 22460, a long-form length below 128 (10.1) 19893, a BOOLEAN contents octet other than 00 and FF (11.1) 4666, universal 8, 11, 16, 17 or 29 in the primitive form 3944, a BOOLEAN without exactly one contents octet (8.2.1) 2952, the initial length octet 0xFF (8.1.3.5 c) 2642, universal tag 0 (X.680 Table 1, 8.1.5) 1555, an INTEGER with a redundant leading octet (8.3.2) 1458, a NULL with contents octets (8.8.2) 1222, an ENUMERATED with a redundant leading octet (8.4, 8.3.2) 595, an INTEGER with no contents octets (8.3.1) 586, an ENUMERATED with no contents octets (8.4, 8.3.1) 119 +- Integer contents read (`readint`): 91153, of which 85882 give a value; why the model refuses the others: an INTEGER with a redundant leading octet (8.3.2) 3565, an INTEGER with no contents octets (8.3.1) 1706 +- Encodings asked for: trees 200687, of which the model reads the tool's encoding back as the tree for 100536 of the 100536 the slice accepts; integers 35611 +- `valid` asked of 200412 trees, of which the slice accepts 100263; the model's answer holds exactly when it reads the tree's encoding back as the tree, for 200412 of 200412 - Wycheproof: `tests/wycheproof/ecdsa_secp256r1_sha256_test.json`, SHA-256 182db4f3e230f6f9fa9f800d2a614dede30284b8e8438bbfe1171905402e9332: 484 signatures read with `all`, of which the tool refuses 113, every answer as the model's but 0. Flagged as an encoding that is not DER or is broken: BerEncodedSignature 7, of which the tool refuses 7, InvalidEncoding 92, of which the tool refuses 90, MissingZero 1, of which the tool refuses 0. Each flagged one it accepts is an element this slice accepts, with what is wrong in something it does not check: a negative INTEGER where a positive one belongs, a BIT STRING where an INTEGER belongs, or a SEQUENCE given the reserved universal tag 15 -- Requests nested 10,000 and 100,000 deep, each sent to a `der serve` of its own: 14, answered within 60 seconds: 14 -- Agreement with each oracle over the requests it answers: Go cryptobyte 1064404 of 1211443, Go encoding/asn1 1185392 of 1211443, rust-asn1 1150947 of 1173582, OpenSSL 1047170 of 1211443 -- Elements each oracle reads (`decode` and `all`) whose answer the model gives with the rules that oracle reads by, whether or not it agrees with the tool: Go cryptobyte 1084679 of 1084679, Go encoding/asn1 1084679 of 1084679, rust-asn1 1084679 of 1084679, OpenSSL 1084679 of 1084679 -- Requests each oracle answers, by operation: Go cryptobyte: all 381979, decode 702700, readint 91153, writeint 35611; Go encoding/asn1: all 381979, decode 702700, readint 91153, writeint 35611; rust-asn1: all 381979, decode 702700, readint 78101, writeint 10802; OpenSSL: all 381979, decode 702700, readint 91153, writeint 35611 +- Requests nested 1,000 (`valid`), 10,000 and 100,000 deep, each sent to a `der serve` of its own: 16, answered within 60 seconds: 16 +- Agreement with each oracle over the requests it answers: Go cryptobyte 1065120 of 1212348, Go encoding/asn1 1186145 of 1212348, rust-asn1 1151685 of 1174506, OpenSSL 1047774 of 1212348 +- Elements each oracle reads (`decode` and `all`) whose answer the model gives with the rules that oracle reads by, whether or not it agrees with the tool (any other is a failure): Go cryptobyte 1085584 of 1085584, Go encoding/asn1 1085584 of 1085584, rust-asn1 1085584 of 1085584, OpenSSL 1085584 of 1085584 +- Requests each oracle answers, by operation: Go cryptobyte: all 382720, decode 702864, readint 91153, writeint 35611; Go encoding/asn1: all 382720, decode 702864, readint 91153, writeint 35611; rust-asn1: all 382720, decode 702864, readint 78092, writeint 10830; OpenSSL: all 382720, decode 702864, readint 91153, writeint 35611 - Malformed requests answered `error`: 91 of 91; odd but well-formed requests answered as the model says: 33 of 33 -- Requests ending in CR LF: every seventh, 230362 +- Requests ending in CR LF: every seventh, 230492 - Last lines sent alone, with no LF, with CR LF, or with a lone CR, answered right: 4 of 4 - Command-line mode `der decode` and wrong arguments, printing and exiting as the model says: 29 of 29 - Failures: 0 @@ -35,73 +35,68 @@ Each disagreement is counted under the reason the model confirms: the model, wit ### Go cryptobyte -- ReadAnyASN1Element refuses every tag number of 31 or more (the high form): 139048; for example `decode 1f1f00` -- ReadAnyASN1Element reads universal 8, 11, 16, 17 or 29 in the primitive form: 2912; for example `decode 0800` +- ReadAnyASN1Element refuses every tag number of 31 or more (the high form): 139219; for example `decode 1f1f00` +- ReadAnyASN1Element reads universal 8, 11, 16, 17 or 29 in the primitive form: 2946; for example `decode 0800` - ReadAnyASN1Element reads a universal type other than BOOLEAN and INTEGER in the constructed form: 2901; for example `decode 3a0904034a6f6e04026573` -- ReadAnyASN1Element reads universal tag 0: 846; for example `decode 0000` -- cryptobyte has no reader for NULL, so a NULL with contents is read: 839; for example `decode 050100` -- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet: 383; for example `decode 0a020000` -- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 80; for example `decode 0a00` -- ReadAnyASN1Element reads a universal type other than BOOLEAN and INTEGER in the constructed form; ReadAnyASN1Element reads universal tag 0: 29; for example `decode 23020000` -- ReadAnyASN1Element reads a universal type other than BOOLEAN and INTEGER in the constructed form; ReadAnyASN1Element reads universal 8, 11, 16, 17 or 29 in the primitive form; ReadAnyASN1Element reads universal tag 0: 1; for example `decode ba062a0410000000` +- cryptobyte has no reader for NULL, so a NULL with contents is read: 875; for example `decode 050100` +- ReadAnyASN1Element reads universal tag 0: 834; for example `decode 0000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet: 373; for example `decode 0a020000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 49; for example `decode 0a00` +- ReadAnyASN1Element reads a universal type other than BOOLEAN and INTEGER in the constructed form; ReadAnyASN1Element reads universal tag 0: 30; for example `decode 23020000` +- ReadAnyASN1Element reads a universal type other than BOOLEAN and INTEGER in the constructed form; ReadAnyASN1Element reads universal 8, 11, 16, 17 or 29 in the primitive form; ReadAnyASN1Element reads universal tag 0: 1; for example `decode 2a080800000000000000` ### Go encoding/asn1 -- parseBase128Int refuses a tag number above 2^31 - 1: 18944; for example `all 1f8880808000020102` -- Unmarshal into a RawValue reads universal 8, 11, 16, 17 or 29 in the primitive form: 3516; for example `decode 0800` -- Unmarshal into a RawValue reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form: 2170; for example `decode 3a0904034a6f6e04026573` -- Unmarshal into a RawValue reads universal tag 0: 919; for example `decode 0000` -- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet: 386; for example `decode 0a020000` -- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 81; for example `decode 0a00` -- Unmarshal into a RawValue reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form; Unmarshal into a RawValue reads universal tag 0: 33; for example `decode 23020000` -- Unmarshal into a RawValue reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form; Unmarshal into a RawValue reads universal 8, 11, 16, 17 or 29 in the primitive form; Unmarshal into a RawValue reads universal tag 0: 1; for example `decode ba062a0410000000` -- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet; Unmarshal into a RawValue reads universal tag 0: 1; for example `decode 2b82030e0a0700400000000000000900fe8202fe668202fa1f90be5a81d6f3d28148330dd2f01c31542ed157d1361d0efb2723567bfc6cbb70ed5968e1035cae2e98f648c800990ca1e357bbb3323e853994984feb7dc978a0caae794234f7a620297112604d6043b37a78dd2d6ee6e6cfa4b91e2... (1581 characters)` +- parseBase128Int refuses a tag number above 2^31 - 1: 19168; for example `all 1f8880808000020102` +- Unmarshal into a RawValue reads universal 8, 11, 16, 17 or 29 in the primitive form: 3562; for example `decode 0800` +- Unmarshal into a RawValue reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form: 2105; for example `decode 3a0904034a6f6e04026573` +- Unmarshal into a RawValue reads universal tag 0: 908; for example `decode 0000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet: 378; for example `decode 0a020000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 50; for example `decode 0a00` +- Unmarshal into a RawValue reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form; Unmarshal into a RawValue reads universal tag 0: 30; for example `decode 23020000` +- Unmarshal into a RawValue reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form; Unmarshal into a RawValue reads universal 8, 11, 16, 17 or 29 in the primitive form; Unmarshal into a RawValue reads universal tag 0: 1; for example `decode 2a080800000000000000` +- Unmarshal into a RawValue reads universal 8, 11, 16, 17 or 29 in the primitive form; Unmarshal into a RawValue reads universal tag 0: 1; for example `all 3082017b30820109b681d765079ff45a034557b41903bd7a2b0801010002031000023d41571c3013e8c64ccf9f459f9d68920090b018565058718628b2fa824c39c99f3510f89021f4b9420312e2e49f7f4fc0d4178f066b1ce7737fb8c302bcd3c6023a9d317b1f2525acf4f08bc10c487a9769b4a2... (770 characters)` ### rust-asn1 -- Tag::from_bytes refuses a tag number above 2^32 - 1: 15480; for example `all 1f9080808000020102` -- strip_tlv reads universal 8, 11, 16, 17 or 29 in the primitive form: 3548; for example `decode 0800` -- strip_tlv reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form: 2186; for example `decode 3a0904034a6f6e04026573` -- strip_tlv reads universal tag 0: 919; for example `decode 0000` -- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet: 386; for example `decode 0a020000` -- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 81; for example `decode 0a00` -- strip_tlv reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form; strip_tlv reads universal tag 0: 33; for example `decode 23020000` -- strip_tlv reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form; strip_tlv reads universal 8, 11, 16, 17 or 29 in the primitive form; strip_tlv reads universal tag 0: 1; for example `decode ba062a0410000000` -- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet; strip_tlv reads universal tag 0: 1; for example `decode 2b82030e0a0700400000000000000900fe8202fe668202fa1f90be5a81d6f3d28148330dd2f01c31542ed157d1361d0efb2723567bfc6cbb70ed5968e1035cae2e98f648c800990ca1e357bbb3323e853994984feb7dc978a0caae794234f7a620297112604d6043b37a78dd2d6ee6e6cfa4b91e2... (1581 characters)` +- Tag::from_bytes refuses a tag number above 2^32 - 1: 15732; for example `all 1f9080808000020102` +- strip_tlv reads universal 8, 11, 16, 17 or 29 in the primitive form: 3594; for example `decode 0800` +- strip_tlv reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form: 2122; for example `decode 3a0904034a6f6e04026573` +- strip_tlv reads universal tag 0: 912; for example `decode 0000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet: 379; for example `decode 0a020000` +- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 50; for example `decode 0a00` +- strip_tlv reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form; strip_tlv reads universal tag 0: 30; for example `decode 23020000` +- strip_tlv reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form; strip_tlv reads universal 8, 11, 16, 17 or 29 in the primitive form; strip_tlv reads universal tag 0: 1; for example `decode 2a080800000000000000` +- strip_tlv reads universal 8, 11, 16, 17 or 29 in the primitive form; strip_tlv reads universal tag 0: 1; for example `all 3082017b30820109b681d765079ff45a034557b41903bd7a2b0801010002031000023d41571c3013e8c64ccf9f459f9d68920090b018565058718628b2fa824c39c99f3510f89021f4b9420312e2e49f7f4fc0d4178f066b1ce7737fb8c302bcd3c6023a9d317b1f2525acf4f08bc10c487a9769b4a2... (770 characters)` ### OpenSSL -- ASN1_get_object reads a first subsequent tag octet 0x80 (BER): 47408; for example `decode 1f801f00` -- ASN1_get_object reads a tag number below 31 in the high form (BER): 36095; for example `decode 1f1e00` -- asn1_get_length reads a long-form length with leading zero octets (BER): 25437; for example `decode 04810001` -- ASN1_get_object refuses a tag number above 2^31 - 1: 18944; for example `all 1f8880808000020102` -- asn1_get_length reads the long form for a length below 128 (BER): 17934; for example `decode 04810105` -- ASN1_get_object reads the indefinite length of a constructed element (BER), to end-of-contents octets 00 00: 7232; for example `decode 30800000` -- d2i_ASN1_TYPE reads any single contents octet as a BOOLEAN (BER, 8.2.2): 3984; for example `decode 010101` -- ASN1_get_object reads universal 8, 11, 16, 17 or 29 in the primitive form: 3516; for example `decode 0800` -- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6): 2170; for example `decode 3a0904034a6f6e04026573` -- ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 919; for example `decode 0000` -- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet: 386; for example `decode 0a020000` -- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 81; for example `decode 0a00` -- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 33; for example `decode 23020000` -- ASN1_get_object reads a first subsequent tag octet 0x80 (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 32; for example `decode 7f801f020000` -- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6); ASN1_get_object reads a tag number below 31 in the high form (BER): 31; for example `decode 3f1e00` -- ASN1_get_object reads a tag number below 31 in the high form (BER); ASN1_get_object reads universal 8, 11, 16, 17 or 29 in the primitive form: 24; for example `all 1f0800` -- asn1_get_length reads the long form for a length below 128 (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 11; for example `decode eb820170758201641a82011ddf500599e69be6a6af311518f2256f323a862ba82ffb64073d982b1375950b8f922b61d4aee7e507b1fe55345889d25f6d2244e53c20072a43ce07deefd66e42d9c2e00acbdf66e4874751496d34c7e3644133833f66e14ecc0bc5e8a0b0d8cb1d74004a67d74bed6... (755 characters)` -- ASN1_get_object reads a first subsequent tag octet 0x80 (BER); ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6): 6; for example `decode 3f801f00` -- d2i_ASN1_TYPE reads any single contents octet as a BOOLEAN (BER, 8.2.2); ASN1_get_object reads a tag number below 31 in the high form (BER): 5; for example `decode 1f010175` -- asn1_get_length reads the initial length octet 0xFF, skipping leading zero octets (BER): 4; for example `decode 04ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000... (267 characters)` +- ASN1_get_object reads a first subsequent tag octet 0x80, which BER forbids as well (8.1.2.4.2 c): 47215; for example `decode 1f801f00` +- ASN1_get_object reads a tag number below 31 in the high form, which BER forbids as well (8.1.2.2): 36142; for example `decode 1f1e00` +- asn1_get_length reads a long-form length with leading zero octets (BER): 26465; for example `decode 04810001` +- ASN1_get_object refuses a tag number above 2^31 - 1: 19168; for example `all 1f8880808000020102` +- asn1_get_length reads the long form for a length below 128 (BER): 17622; for example `decode 04810105` +- ASN1_get_object reads the indefinite length of a constructed element (BER), to end-of-contents octets 00 00: 7214; for example `decode 30800000` +- d2i_ASN1_TYPE reads any single contents octet as a BOOLEAN (BER, 8.2.2): 4087; for example `decode 010101` +- ASN1_get_object reads universal 8, 11, 16, 17 or 29 in the primitive form: 3562; for example `decode 0800` +- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER, ENUMERATED and NULL in the constructed form, which BER allows only for the string types and the types encoded as strings (8.6, 8.7, 8.23, and ObjectDescriptor, UTCTime and GeneralizedTime, 8.25): 2052; for example `decode 3a0904034a6f6e04026573` +- ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 908; for example `decode 0000` +- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER, ENUMERATED and NULL in the constructed form, which BER allows only for the string types and the types encoded as strings (8.6, 8.7, 8.23, and ObjectDescriptor, UTCTime and GeneralizedTime, 8.25); ASN1_get_object reads a tag number below 31 in the high form, which BER forbids as well (8.1.2.2): 29; for example `decode 3f1e00` +- ASN1_get_object reads a first subsequent tag octet 0x80, which BER forbids as well (8.1.2.4.2 c); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 25; for example `decode 7f801f020000` +- ASN1_get_object reads a tag number below 31 in the high form, which BER forbids as well (8.1.2.2); ASN1_get_object reads universal 8, 11, 16, 17 or 29 in the primitive form: 22; for example `all 1f0800` +- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER, ENUMERATED and NULL in the constructed form, which BER allows only for the string types and the types encoded as strings (8.6, 8.7, 8.23, and ObjectDescriptor, UTCTime and GeneralizedTime, 8.25); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 21; for example `decode 23020000` +- asn1_get_length reads the long form for a length below 128 (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 12; for example `decode 7f517e020200a930789f672044f0cfc0d454738324971cb28cb0a737ff1909e1118d9489d1ed16c3a18cf49c2b003d2cc420a238857a90b3dde4c4c3e26616baacc0986866e7ad3e9813475bdaadd1eebc0a060080000000008101514c230a679b3753fadbe7e0dca849ca9422cb1a0248ebe6ec7... (269 characters)` +- ASN1_get_object reads a first subsequent tag octet 0x80, which BER forbids as well (8.1.2.4.2 c); ASN1_get_object reads a universal type other than BOOLEAN, INTEGER, ENUMERATED and NULL in the constructed form, which BER allows only for the string types and the types encoded as strings (8.6, 8.7, 8.23, and ObjectDescriptor, UTCTime and GeneralizedTime, 8.25): 6; for example `decode 3f801f00` +- asn1_get_length reads the initial length octet 0xFF, which BER forbids as well (8.1.3.5 c), skipping leading zero octets: 4; for example `decode 04ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000... (267 characters)` - asn1_get_length reads a long-form length with leading zero octets (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 4; for example `decode 208100f33f209ca08201a06b05` -- ASN1_get_object reads a tag number below 31 in the high form (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 3; for example `decode 7f1e020000` -- asn1_get_length reads the long form for a length below 128 (BER); ASN1_get_object reads a tag number below 31 in the high form (BER): 2; for example `decode 1f17810164349104dd26e413ff1f813f00b10082b800ce` -- asn1_get_length reads a long-form length with leading zero octets (BER); ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6): 2; for example `decode 33810030` -- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6); ASN1_get_object reads the indefinite length of a constructed element (BER), to end-of-contents octets 00 00: 1; for example `decode 3a8004034a6f6e040265730000` -- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6); ASN1_get_object reads a tag number below 31 in the high form (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `decode 3f1e020000` -- ASN1_get_object reads a first subsequent tag octet 0x80 (BER); ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `decode 3f801f020000` -- ASN1_get_object reads a tag number below 31 in the high form (BER); the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 1; for example `all 1f0a00` -- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and NULL in the constructed form (BER, 8.23.6); ASN1_get_object reads universal 8, 11, 16, 17 or 29 in the primitive form; ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `decode ba062a0410000000` -- the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with a redundant leading octet; ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `decode 2b82030e0a0700400000000000000900fe8202fe668202fa1f90be5a81d6f3d28148330dd2f01c31542ed157d1361d0efb2723567bfc6cbb70ed5968e1035cae2e98f648c800990ca1e357bbb3323e853994984feb7dc978a0caae794234f7a620297112604d6043b37a78dd2d6ee6e6cfa4b91e2... (1581 characters)` -- asn1_get_length reads a long-form length with leading zero octets (BER); ASN1_get_object reads a tag number below 31 in the high form (BER): 1; for example `decode 5f1d810002a802a97bd15305f601c180318120a73a1f04` -- ASN1_get_object reads a first subsequent tag octet 0x80 (BER); asn1_get_length reads the long form for a length below 128 (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `decode 1f0081011f` -- ASN1_get_object reads a first subsequent tag octet 0x80 (BER); the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED with no contents octets: 1; for example `decode 1f800a000048059d3f60ff05028080` -- d2i_ASN1_TYPE reads any single contents octet as a BOOLEAN (BER, 8.2.2); asn1_get_length reads the long form for a length below 128 (BER): 1; for example `decode 01810131` +- ASN1_get_object reads a tag number below 31 in the high form, which BER forbids as well (8.1.2.2); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 3; for example `decode 7f1e020000` +- d2i_ASN1_TYPE reads any single contents octet as a BOOLEAN (BER, 8.2.2); asn1_get_length reads the long form for a length below 128 (BER): 2; for example `decode 3faa9f74820190c400ef82018aff2382017e1e820126df00a4a731ecc8b7f590fc0d815fa15b36e8a687d98923a16a8cf97523e0ef8bc676c3932deae658e87d75ceb09e96c6681394b1e040dba22e1cb0f039c521c50cc8f92340c6c85854e95b6cb9e8b8ea60a01bcf4f642b229cb4e7c83bd3d... (821 characters)` +- asn1_get_length reads the long form for a length below 128 (BER); ASN1_get_object reads a tag number below 31 in the high form, which BER forbids as well (8.1.2.2): 2; for example `decode 1f17810164349104dd26e413ff1f813f00b10082b800ce` +- asn1_get_length reads a long-form length with leading zero octets (BER); ASN1_get_object reads a universal type other than BOOLEAN, INTEGER, ENUMERATED and NULL in the constructed form, which BER allows only for the string types and the types encoded as strings (8.6, 8.7, 8.23, and ObjectDescriptor, UTCTime and GeneralizedTime, 8.25): 2; for example `decode 33810030` +- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER, ENUMERATED and NULL in the constructed form, which BER allows only for the string types and the types encoded as strings (8.6, 8.7, 8.23, and ObjectDescriptor, UTCTime and GeneralizedTime, 8.25); ASN1_get_object reads the indefinite length of a constructed element (BER), to end-of-contents octets 00 00: 1; for example `decode 3a8004034a6f6e040265730000` +- ASN1_get_object reads a universal type other than BOOLEAN, INTEGER, ENUMERATED and NULL in the constructed form, which BER allows only for the string types and the types encoded as strings (8.6, 8.7, 8.23, and ObjectDescriptor, UTCTime and GeneralizedTime, 8.25); ASN1_get_object reads a tag number below 31 in the high form, which BER forbids as well (8.1.2.2); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `decode 3f1e020000` +- ASN1_get_object reads a first subsequent tag octet 0x80, which BER forbids as well (8.1.2.4.2 c); ASN1_get_object reads a universal type other than BOOLEAN, INTEGER, ENUMERATED and NULL in the constructed form, which BER allows only for the string types and the types encoded as strings (8.6, 8.7, 8.23, and ObjectDescriptor, UTCTime and GeneralizedTime, 8.25); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `decode 3f801f020000` +- ASN1_get_object reads universal 8, 11, 16, 17 or 29 in the primitive form; ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `all 3082017b30820109b681d765079ff45a034557b41903bd7a2b0801010002031000023d41571c3013e8c64ccf9f459f9d68920090b018565058718628b2fa824c39c99f3510f89021f4b9420312e2e49f7f4fc0d4178f066b1ce7737fb8c302bcd3c6023a9d317b1f2525acf4f08bc10c487a9769b4a2... (770 characters)` +- asn1_get_length reads a long-form length with leading zero octets (BER); ASN1_get_object reads a tag number below 31 in the high form, which BER forbids as well (8.1.2.2): 1; for example `decode 5f1d810002a802a97bd15305f601c180318120a73a1f04` +- ASN1_get_object reads a first subsequent tag octet 0x80, which BER forbids as well (8.1.2.4.2 c); asn1_get_length reads the long form for a length below 128 (BER); ASN1_get_object reads universal tag 0 where no end-of-contents octets are due: 1; for example `decode 1f0081011f` +- asn1_get_length reads the long form for a length below 128 (BER); ASN1_get_object reads universal 8, 11, 16, 17 or 29 in the primitive form: 1; for example `decode 08810402801fea8080df7fc120183a3000` diff --git a/tests/oracles/go/main.go b/tests/oracles/go/main.go index 65527d4..eca2287 100644 --- a/tests/oracles/go/main.go +++ b/tests/oracles/go/main.go @@ -2,7 +2,9 @@ // // der-oracle cryptobyte golang.org/x/crypto/cryptobyte, at the version // go.mod pins (go.sum holds its hash) -// der-oracle asn1 the standard library's encoding/asn1 +// der-oracle asn1 the standard library's encoding/asn1, of the +// installed Go (not pinned; the test record +// gives its version) // // Each line of standard input is one request in the form `der serve` // reads (octets in lower-case hex, `-` for none; integers in decimal), diff --git a/tests/oracles/openssl/wrapper.c b/tests/oracles/openssl/wrapper.c index 19172e6..ea29194 100644 --- a/tests/oracles/openssl/wrapper.c +++ b/tests/oracles/openssl/wrapper.c @@ -1,8 +1,8 @@ /* The OpenSSL oracle for tests/run_tests.py: the BER reader of libcrypto - (ASN1_get_object, d2i_ASN1_TYPE, d2i_ASN1_INTEGER, d2i_ASN1_NULL), - driven line by line. It is built against the system library, whose - version tests/run_tests.py checks. + (ASN1_get_object, d2i_ASN1_TYPE, d2i_ASN1_INTEGER, d2i_ASN1_ENUMERATED, + d2i_ASN1_NULL), driven line by line. It is built against the system + library, whose version tests/run_tests.py checks. Each line of standard input is one request in the form `der serve` reads (octets in lower-case hex, `-` for none; integers in decimal), @@ -20,15 +20,19 @@ ASN1_get_object reads the identifier and length octets of each element, given the octets left in the input or in the element around it. It - reads BER: the indefinite length for a constructed element, lengths not - in the fewest octets, and tag numbers below 31 in the high form. A + reads what BER allows and DER does not: the indefinite length for a + constructed element, and lengths not in the fewest octets. Beyond BER, + it also reads encodings that BER forbids as well: tag numbers below 31 + in the high form (X.690 8.1.2.2), a first subsequent tag octet 0x80 + (8.1.2.4.2 c), and the initial length octet 0xFF (8.1.3.5 c). A constructed element's contents are read the same way, element by element: to the end of its length, or, for the indefinite length, to - the end-of-contents octets 00 00 (X.690 8.1.5), which are not part of - the tree. An element of the universal class with number 1, 2 or 5 must - also be read by d2i_ASN1_TYPE as a BOOLEAN, by d2i_ASN1_INTEGER, or by - d2i_ASN1_NULL, given the same octets. The walk keeps its own stack, so - deep nesting does not use the machine stack. + the end-of-contents octets 00 00 (8.1.5), which are not part of the + tree. An element of the universal class with number 1, 2, 5 or 10 must + also be read by d2i_ASN1_TYPE as a BOOLEAN, by d2i_ASN1_INTEGER, by + d2i_ASN1_NULL, or by d2i_ASN1_ENUMERATED, given the same octets. The + walk keeps its own stack, so deep nesting does not use the machine + stack. readint frames the contents octets as an INTEGER element (the length octets are written here), reads it with d2i_ASN1_INTEGER and prints it @@ -107,8 +111,8 @@ static unsigned char *field(const char *f, long *len) { return out; } -/* Whether the universal BOOLEAN, INTEGER or NULL at p, with max octets - available, is one the library reads. */ +/* Whether the universal BOOLEAN, INTEGER, NULL or ENUMERATED at p, with + max octets available, is one the library reads. */ static int typed_ok(int tag, const unsigned char *p, long max) { const unsigned char *q = p; int ok = 0; @@ -124,6 +128,10 @@ static int typed_ok(int tag, const unsigned char *p, long max) { ASN1_NULL *z = d2i_ASN1_NULL(NULL, &q, max); ok = z != NULL; ASN1_NULL_free(z); + } else if (tag == V_ASN1_ENUMERATED) { + ASN1_ENUMERATED *e = d2i_ASN1_ENUMERATED(NULL, &q, max); + ok = e != NULL; + ASN1_ENUMERATED_free(e); } else { ok = 1; } diff --git a/tests/plant_bugs.py b/tests/plant_bugs.py index ee31ef3..a7599ba 100644 --- a/tests/plant_bugs.py +++ b/tests/plant_bugs.py @@ -44,6 +44,12 @@ ("a leading zero length octet is read", " if ds.length < k ∨ ds.head? = some 0 then none", " if ds.length < k then none"), + ("a leading zero length octet is read when 5 or more length octets follow it", + " if ds.length < k ∨ ds.head? = some 0 then none", + " if ds.length < k ∨ (ds.head? = some 0 ∧ k < 5) then none"), + ("a long-form length is kept modulo 2^32", + " let n := ofDigits 8 (ds.map UInt8.toNat)", + " let n := ofDigits 8 (ds.map UInt8.toNat) % 2 ^ 32"), ("the indefinite length octet 0x80 is read as length 0", " if b.toNat < 128 then some (b.toNat, 1, bs)", " if b.toNat ≤ 128 then some (b.toNat % 128, 1, bs)"), diff --git a/tests/run_tests.py b/tests/run_tests.py index bc82929..ccb164a 100644 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -43,8 +43,9 @@ class from 0 to 3, the universal tags and contents as `read` checks - Go cryptobyte: golang.org/x/crypto/cryptobyte, at the version tests/oracles/go/go.mod pins (go.sum holds its hash); - - Go encoding/asn1: the standard library's package, built by the - same Go; + - Go encoding/asn1: the standard library's package of the installed + Go, which builds both Go oracles. It is not pinned; the record + gives the Go version; - rust-asn1: the `asn1` crate, at the version and checksum tests/oracles/rust/Cargo.lock pins, built with `cargo --locked`; - OpenSSL: libcrypto, the version OPENSSL_PINNED, through @@ -52,10 +53,12 @@ class from 0 to 3, the universal tags and contents as `read` checks Each reads elements with the package's own reader and walks a constructed element's contents element by element, checks a - universal BOOLEAN, INTEGER and NULL with the package's own readers - for them, and prints the tree in the syntax of `der serve` (the - program in each oracle's folder says how). The programs check no - ENUMERATED contents. Each answers `decode`, `all`, `readint` and + universal BOOLEAN and INTEGER with the package's own readers for + them, and prints the tree in the syntax of `der serve` (the program + in each oracle's folder says how). The programs for encoding/asn1, + rust-asn1 and OpenSSL also check a NULL; cryptobyte has no reader + for NULL. Only the OpenSSL program checks ENUMERATED contents, with + d2i_ASN1_ENUMERATED. Each answers `decode`, `all`, `readint` and `writeint` (rust-asn1 gives a value only within 64 bits), and no `encode` or `valid`. An oracle that cannot be built or started is left out, and the record says so on its `- Oracles left out:` @@ -73,16 +76,24 @@ class from 0 to 3, the universal tags and contents as `read` checks checks the form only of BOOLEAN, INTEGER and NULL; - rust-asn1 reads tag numbers and lengths up to 2^32 - 1, and checks the form only of BOOLEAN, INTEGER and NULL; - - OpenSSL reads BER: the indefinite length for a constructed element, - to end-of-contents octets 00 00; lengths not in the fewest octets, - even after the initial octet 0xFF, of at most 8 significant octets - and when one more octet follows them; tag numbers in any high - form, up to 2^31 - 1; and any single octet as a BOOLEAN. It checks - the form only of BOOLEAN, INTEGER and NULL. - None of them refuses universal tag 0, and none of the programs - checks the contents of an ENUMERATED. The record also counts, for each - oracle, the elements whose answer `read` gives with its rules, whether - or not it agrees with the tool. + - OpenSSL reads what BER allows and DER does not: the indefinite + length for a constructed element, to end-of-contents octets 00 00; + lengths not in the fewest octets, of at most 8 significant octets + and when one more octet follows them; and any single octet as a + BOOLEAN. Beyond BER, it also reads encodings that BER forbids as + well: the initial length octet 0xFF (8.1.3.5 c), its length read + as above, and tag numbers below 31 in the high form (8.1.2.2) or + with a first subsequent octet 0x80 (8.1.2.4.2 c). It reads tag + numbers up to 2^31 - 1. It checks the form only of BOOLEAN, + INTEGER, ENUMERATED and NULL, so it reads the other universal types + in either form, though BER allows the constructed form only for + the string types and the types encoded as strings (8.6, 8.7, 8.23, + and ObjectDescriptor, UTCTime and GeneralizedTime, 8.25). + The Go and Rust programs check no ENUMERATED contents. None of the + oracles refuses universal tag 0. Beyond that, for every element + an oracle reads (`decode` and `all`), `read` with its rules must give + its answer, whether or not it agrees with the tool; any element on + which it does not is a failure. Inputs come from a recorded seed, after fixed sets: @@ -93,10 +104,12 @@ class from 0 to 3, the universal tags and contents as `read` checks - every identifier first octet, with subsequent tag octets when it takes them, and short contents; - every initial length octet 0x80 to 0xFF, with several subsequent - octets and contents; + octets and contents; and lengths of 5 to 126 octets that a reader + skipping leading zero octets, or keeping the length modulo 2^32 or + 2^64, would fit to the contents after them; - lengths 0 to 100,000 around 127, 128, 255, 256, 65535 and 65536: whole, cut short, with the length one more and one less, not in the - fewest octets, and inside a SEQUENCE; + fewest octets (up to 126 length octets), and inside a SEQUENCE; - tag numbers around 31, 127, 128, 16383, 16384, 2^21, 2^28, 2^31, 2^32, 2^63 and 2^64, and one of 1,000 subsequent octets, in each class and form, cut short and with a first subsequent octet 0x80; @@ -107,9 +120,11 @@ class and form, cut short and with a first subsequent octet 0x80; the same contents read as an ENUMERATED; - chains nested 10,000 and 100,000 deep: whole, with octets after, with mixed classes and tags, with a bad BOOLEAN inside, with one element - running past the one around it, and cut short. Each goes to a `der - serve` of its own, which must answer within DEEP_BUDGET seconds; the - 10,000 deep ones are also encoded; + running past the one around it, and cut short; the 10,000 deep ones + are also encoded. Chains 1,000 deep, one the slice accepts and one + with a bad BOOLEAN inside, are asked `valid`. Each of these requests + goes to a `der serve` of its own, which must answer within + DEEP_BUDGET seconds; - every signature of the Wycheproof vectors in tests/wycheproof/ecdsa_secp256r1_sha256_test.json, whose SHA-256 is checked first, read with `all`. The record says how many of those @@ -118,17 +133,20 @@ class and form, cut short and with a first subsequent octet 0x80; - random trees the slice accepts, of every class, with tags in both forms, up to 6 levels and 8 elements in a constructed one, encoded by the model and read whole, with octets after, and mutated: an - element's length one more or one less, not in the fewest octets or + element's length one more or one less, not in the fewest octets (in + the long form below 128, or with 1 to 125 leading zero octets) or indefinite, its tag in the high form below 31 or with a first subsequent octet 0x80, its constructed bit flipped, its length too short for the elements in it, so that the last runs past it, its - BOOLEAN, INTEGER or NULL contents changed; and the whole encoding with - a bit flipped or cut short. + BOOLEAN, INTEGER, ENUMERATED or NULL contents changed; and the whole + encoding with a bit flipped or cut short. Each tree is also encoded, and the model reads the tool's encoding back as the tree. Each is asked `valid`, and so is a copy with one element the slice does not accept: of a class from 4 to 7, universal - tag 0, a universal type in the wrong form, or BOOLEAN, INTEGER, - ENUMERATED or NULL contents DER forbids; that copy is also encoded; + tag 0, a universal type in the wrong form (never a reserved number, + which may take either), or BOOLEAN, INTEGER, ENUMERATED or NULL + contents DER forbids. The model checks that it does not accept that + copy, which is also encoded; - random octet strings; `readint` on random contents; `writeint` on integers from -300 to 300, around plus and minus 2^k for k up to 1,000, and random ones of up to 4,096 bits; @@ -181,8 +199,8 @@ class and form, cut short and with a first subsequent octet 0x80; RESULTS = TESTS / "TEST-RESULTS.md" TIMEOUT = 3600 BUILD_TIMEOUT = 3600 -# A request nested 10,000 or 100,000 deep goes to a `der serve` of its -# own, which must answer it within this many seconds. +# A request nested 1,000, 10,000 or 100,000 deep goes to a `der serve` +# of its own, which must answer it within this many seconds. DEEP_BUDGET = 60 # The other requests are split among this many `der serve` processes. JOBS = max(1, min(4, os.cpu_count() or 1)) @@ -246,7 +264,7 @@ class Rules: high_form: bool = True # reads tag numbers of 31 and more lenient_tags: bool = False # reads any high form, even below 31 or from 0x80 max_tag: int = None # the largest tag number read - ber_lengths: bool = False # OpenSSL's BER lengths, with the indefinite form + ber_lengths: bool = False # OpenSSL's lengths: BER's, and after the octet 0xFF max_length: int = None # the largest length read checked_forms: frozenset = None # universal numbers whose form is checked; None: all universal_0: bool = False # reads universal tag 0 @@ -841,7 +859,9 @@ def first_line(cmd, cwd=None): def setup_go(): - """Both Go oracles: one program, two modes.""" + """Both Go oracles: one program, two modes, built by the installed Go. + cryptobyte is at the version go.mod pins; encoding/asn1 is that Go's + own, not pinned, and its version is recorded.""" GO_EXE.parent.mkdir(parents=True, exist_ok=True) env = dict(os.environ, GOFLAGS="-mod=readonly", CGO_ENABLED="0", GOTOOLCHAIN="local") try: @@ -857,7 +877,8 @@ def setup_go(): return [ProcessOracle("Go cryptobyte", f"golang.org/x/crypto/cryptobyte " f"{mod.group(1) if mod else '?'} ({sums.group(1) if sums else '?'}), " f"built with {go}", [str(GO_EXE), "cryptobyte"]), - ProcessOracle("Go encoding/asn1", f"encoding/asn1 of the standard library of {go}", + ProcessOracle("Go encoding/asn1", f"encoding/asn1 of the standard library of the " + f"installed {go} (not pinned)", [str(GO_EXE), "asn1"])] @@ -910,11 +931,10 @@ def setup_openssl(): "rust-asn1": Rules(max_tag=2 ** 32 - 1, max_length=2 ** 32 - 1, checked_forms=frozenset({1, 2, 5}), universal_0=True, enum_checked=False), "OpenSSL": Rules(lenient_tags=True, max_tag=2 ** 31 - 1, ber_lengths=True, - checked_forms=frozenset({1, 2, 5}), universal_0=True, ber_boolean=True, - enum_checked=False), + checked_forms=frozenset({1, 2, 5, 10}), universal_0=True, ber_boolean=True), } -# The oracle programs check only BOOLEAN, INTEGER and NULL contents. +# The Go and Rust oracle programs check no ENUMERATED contents. ENUM_NOT_CHECKED = "the oracle program checks no ENUMERATED contents, so it reads an ENUMERATED " # What each oracle does on each rule where it parts from DER. @@ -953,23 +973,25 @@ def setup_openssl(): WHY_ENUM_PAD: ENUM_NOT_CHECKED + "with a redundant leading octet", }, "OpenSSL": { - WHY_HIGH_LOW: "ASN1_get_object reads a tag number below 31 in the high form (BER)", - WHY_TAG_80: "ASN1_get_object reads a first subsequent tag octet 0x80 (BER)", + WHY_HIGH_LOW: "ASN1_get_object reads a tag number below 31 in the high form, which BER " + "forbids as well (8.1.2.2)", + WHY_TAG_80: "ASN1_get_object reads a first subsequent tag octet 0x80, which BER forbids " + "as well (8.1.2.4.2 c)", WHY_TAG_LIMIT: "ASN1_get_object refuses a tag number above 2^31 - 1", WHY_INDEFINITE: "ASN1_get_object reads the indefinite length of a constructed element " "(BER), to end-of-contents octets 00 00", - WHY_LEN_FF: "asn1_get_length reads the initial length octet 0xFF, skipping leading " - "zero octets (BER)", + WHY_LEN_FF: "asn1_get_length reads the initial length octet 0xFF, which BER forbids as " + "well (8.1.3.5 c), skipping leading zero octets", WHY_LEN_ZERO: "asn1_get_length reads a long-form length with leading zero octets (BER)", WHY_LEN_SHORT: "asn1_get_length reads the long form for a length below 128 (BER)", WHY_UNIVERSAL_0: "ASN1_get_object reads universal tag 0 where no end-of-contents octets " "are due", WHY_CONS_PRIM: "ASN1_get_object reads universal 8, 11, 16, 17 or 29 in the primitive form", - WHY_PRIM_CONS: "ASN1_get_object reads a universal type other than BOOLEAN, INTEGER and " - "NULL in the constructed form (BER, 8.23.6)", + WHY_PRIM_CONS: "ASN1_get_object reads a universal type other than BOOLEAN, INTEGER, " + "ENUMERATED and NULL in the constructed form, which BER allows only for " + "the string types and the types encoded as strings (8.6, 8.7, 8.23, and " + "ObjectDescriptor, UTCTime and GeneralizedTime, 8.25)", WHY_BOOL_VALUE: "d2i_ASN1_TYPE reads any single contents octet as a BOOLEAN (BER, 8.2.2)", - WHY_ENUM_EMPTY: ENUM_NOT_CHECKED + "with no contents octets", - WHY_ENUM_PAD: ENUM_NOT_CHECKED + "with a redundant leading octet", }, } @@ -1049,7 +1071,9 @@ def length_sets(): identifiers of an OCTET STRING and a SEQUENCE, with several runs of subsequent octets: all zero, ending in 01, starting with 01, all ones, and one octet short. Contents follow when the length is at - most 300; else three octets.""" + most 300; else three octets. Then lengths of 5 to 126 octets that a + reader would fit to the contents after them if it skipped leading + zero octets or kept the length modulo 2^32 or 2^64.""" for b in range(0x80, 0x100): k = b & 0x7f if k == 0: @@ -1068,12 +1092,27 @@ def length_sets(): yield ("initial length octets", "decode", (ident + bytes([b]) + run + body,)) yield ("initial length octets", "decode", (ident + bytes([b]) + run + body + b"\x01",)) + # Long-form lengths of 5 to 126 octets that a wrong reader would take + # for a length the contents fit: 128 and 256 with leading zero octets, + # and 2^(8(k - 1)) + 128, which is 128 modulo 2^32 from 5 octets on + # and modulo 2^64 from 9 on. Each is followed by as many contents + # octets as the wrong reading gives. + label = "lengths of 5 to 126 octets that a wrong reading fits" + for k in range(5, 127): + for run, fit in ((bytes(k - 1) + b"\x80", 128), (bytes(k - 2) + b"\x01\x00", 256), + (b"\x01" + bytes(k - 2) + b"\x80", 128)): + for ident in (b"\x04", b"\x30"): + e = ident + bytes([0x80 | k]) + run + (b"\x05\x00" * 128)[:fit] + yield (label, "all", (e,)) + yield (label, "decode", (e + b"\x01",)) def size_sets(): """Contents of 0 to 100,000 octets, at the lengths where the length octets change: whole, with an octet after, cut short, with the - length one more and one less, not in the fewest octets, and inside a + length one more and one less, not in the fewest octets (in the long + form below 128, and with leading zero octets: in one subsequent + length octet more than it needs, and in 5, 9 and 126), and inside a SEQUENCE.""" rng = random.Random(3) label = "lengths 0 to 100,000" @@ -1087,8 +1126,9 @@ def size_sets(): yield (label + ", length one less", "decode", (b"\x04" + length_octets(n - 1) + c,)) yield (label + ", length one more", "decode", (b"\x04" + length_octets(n + 1) + c,)) minimal = n.to_bytes(max(1, (n.bit_length() + 7) // 8), "big") - yield (label + ", not in the fewest octets", "all", - (b"\x04" + bytes([0x81 + len(minimal), 0]) + minimal + c,)) + for total in sorted({len(minimal) + 1, 5, 9, 126}): + yield (label + ", not in the fewest octets", "all", + (b"\x04" + bytes([0x80 + total]) + bytes(total - len(minimal)) + minimal + c,)) if n < 128: yield (label + ", not in the fewest octets", "all", (b"\x04\x81" + bytes([n]) + c,)) yield (label, "encode", (("p", 0, 4, c),)) @@ -1187,8 +1227,13 @@ def chain_octets(depth, leaf, ident, longer_at=None): def deep_sets(): - """Chains 10,000 and 100,000 deep.""" + """Chains 1,000, 10,000 and 100,000 deep.""" mixed = ((0, 16), (2, 0), (0, 17), (1, 31), (3, 1000), (0, 8)) + # Only chains 1,000 deep are asked `valid`: its time grows about with + # the cube of the depth. + label = "nested 1,000 deep" + yield (label, "valid", (chain(1000, ("p", 2, 3, b"\x01"), mixed),)) + yield (label, "valid", (chain(1000, ("p", 0, 1, b"\x01"), ((0, 16),)),)) for d in (10_000, 100_000): label = f"nested {d:,} deep" null = chain(d, ("p", 0, 5, b""), ((0, 16),)) @@ -1335,9 +1380,13 @@ def enc(t): elif defect == "length one less": length = length_octets(n - 1) elif defect == "length not in the fewest octets": + # The long form below 128, or leading zero octets: half + # the time one, else from one up to as many as keep the + # length within 126 octets. minimal = n.to_bytes(max(1, (n.bit_length() + 7) // 8), "big") + pad = rng.choice((1, rng.randint(1, 126 - len(minimal)))) length = (bytes([0x81, n]) if n < 128 and rng.random() < 0.5 - else bytes([0x81 + len(minimal), 0]) + minimal) + else bytes([0x80 + pad + len(minimal)]) + bytes(pad) + minimal) elif defect == "indefinite length": length, tail = b"\x80", b"\x00\x00" elif defect == "high form for a tag below 31": @@ -1385,12 +1434,17 @@ def spoil(tree, rng): elif kind == 2: new = ("p", 0, rng.choice(sorted(CONSTRUCTED_UNIVERSAL)), random_contents(rng)) elif kind == 3: - new = ("c", 0, rng.choice(PRIMITIVE_UNIVERSAL), target[3] if target[0] == "c" else []) + # Not a reserved number, which this slice reads in either form. + new = ("c", 0, rng.choice([n for n in PRIMITIVE_UNIVERSAL if not reserved_universal(n)]), + target[3] if target[0] == "c" else []) else: new = ("p", 0) + rng.choice(((1, b""), (1, b"\x01"), (1, b"\x00\x00"), (2, b""), (2, b"\x00\x05"), (2, b"\xff\x80"), (10, b""), (10, b"\x00\x00"), (10, b"\xff\xff"), (5, b"\x00"))) - return replace(tree, target, new) + bad = replace(tree, target, new) + if valid(bad): + raise AssertionError(f"a spoiled tree the slice accepts: {show(bad)}") + return bad def random_octets(rng): @@ -1794,12 +1848,21 @@ def ask(o): by_op[o][op] = by_op[o].get(op, 0) + 1 theirs_by_model, parted = None, set() if op in ("decode", "all"): + # The model, with the rules the oracle reads by, must give + # its answer, whether or not it agrees with the tool. theirs_by_model, _, parted = model(op, a, ORACLE_RULES[o]) by_rules[o][0] += 1 - by_rules[o][1] += theirs_by_model == theirs + if theirs_by_model == theirs: + by_rules[o][1] += 1 + else: + failures.append((f"{o}, as the model reads with its rules: " + f"{short(request(op, a))}", short(theirs_by_model, 160), + short(theirs, 160))) if theirs == got: agree[o] += 1 continue + if theirs_by_model is not None and theirs_by_model != theirs: + continue # already a failure reason = difference_reason(o, op, theirs, theirs_by_model, parted) if reason is None: failures.append((f"{o}: {short(request(op, a))}", short(got, 160), @@ -1860,12 +1923,13 @@ def refusals(kind): + ". Each flagged one it accepts is an element this slice accepts, with what is wrong " "in something it does not check: a negative INTEGER where a positive one belongs, a BIT " "STRING where an INTEGER belongs, or a SEQUENCE given the reserved universal tag 15", - f"- Requests nested 10,000 and 100,000 deep, each sent to a `der serve` of its own: " - f"{len(deep)}, answered within {DEEP_BUDGET} seconds: {deep_in_time}", + f"- Requests nested 1,000 (`valid`), 10,000 and 100,000 deep, each sent to a `der serve` " + f"of its own: {len(deep)}, answered within {DEEP_BUDGET} seconds: {deep_in_time}", "- Agreement with each oracle over the requests it answers: " + ", ".join(f"{o} {agree[o]} of {asked[o]}" for o in names), "- Elements each oracle reads (`decode` and `all`) whose answer the model gives with the " - "rules that oracle reads by, whether or not it agrees with the tool: " + "rules that oracle reads by, whether or not it agrees with the tool (any other is a " + "failure): " + ", ".join(f"{o} {by_rules[o][1]} of {by_rules[o][0]}" for o in names), "- Requests each oracle answers, by operation: " + "; ".join(f"{o}: " + ", ".join(f"{op} {n}" for op, n in sorted(by_op[o].items()))