signature: P-256 ECDSA verification, ES256 (slice 3 of #16) #225

Closed
opened 2026-10-08 21:26:56 +00:00 by orion · 1 comment
Collaborator

Goal

ECDSA verification over the NIST curve P-256 with SHA-256 (JOSE/COSE ES256) in std/signature, in TorbScript with no native. It is the algorithm most passkeys sign with, so the registry's WebAuthn slice needs it; std/signature has Ed25519 only today.

Owner decision (recorded on #16)

Passkeys accept ES256, EdDSA and RS256, with a new pure-TorbScript P-256 ECDSA verifier in std/signature (new public std surface, no native, no seed change).

Pinned records

  • RELEASE.md 7.6 Security (passkey as second factor)
  • FIPS 186-5 section 6.4.2 (ECDSA signature verification), SEC 1 v2 sections 2.3.4 (point decoding) and 3.2.2.1 (public key validation), RFC 7518 section 3.4 (ES256, the 64-byte r || s form), W3C WebAuthn Level 3 section 6.5.6 (an ES256 assertion signature is DER ECDSA-Sig-Value)
  • docs/standard-library/signature.md

Acceptance criteria

  • P256PublicKey (Show, Equals, Hash, TryFrom<String, SignatureError>): from the 65 bytes of an uncompressed SEC 1 point and from its two 32-byte coordinates (COSE key, JWK); both coordinates below p, the point on the curve, refused otherwise (SignatureError.NotAPoint); bytes(), hex().
  • P256Signature: from 64 bytes r || s (JWS) and from strict DER (WebAuthn): one SEQUENCE of two positive minimal INTEGERs, short-form lengths, no trailing bytes, anything else refused with a reason; r and s both in [1, n - 1], refused otherwise.
  • P256PublicKey.verifies(message, signature) per FIPS 186-5 6.4.2 with SHA-256; a result point at infinity is false; both the low-S and the high-S form of a signature verify (FIPS 186-5, WebAuthn and OpenSSL accept both), documented as a pitfall (a signature is malleable, never an identifier).
  • Test vectors from independent sources: RFC 6979 A.2.5, vectors made by Node's crypto (OpenSSL) in both signature forms, and edge cases from Project Wycheproof (r/s = 0, = n, above n, high S, points not on the curve). Every vector once as accepted and once as refused (changed message, signature or key); torb test std/signature/tests green natively and in the VM.
  • docs/standard-library/signature.md documents the new types, what is and is not constant time, and the measured speed; skills/ regenerated.

Seed impact

None: new public types in std/signature in plain TorbScript over Int and std/digest's Sha256; no syntax change, no native, no runtime change, no driver flag, and the compiler looks up none of the new names by string. Additive only.

Size and dependencies

One PR, one feat(std/signature) commit (plus tests and docs). No dependency. Parent: #16; slice 4 (#226, passkeys) depends on it. Security-relevant: architect review of the diff.

## Goal ECDSA verification over the NIST curve P-256 with SHA-256 (JOSE/COSE `ES256`) in `std/signature`, in TorbScript with no native. It is the algorithm most passkeys sign with, so the registry's WebAuthn slice needs it; `std/signature` has Ed25519 only today. ## Owner decision (recorded on #16) Passkeys accept ES256, EdDSA and RS256, with a new pure-TorbScript P-256 ECDSA verifier in `std/signature` (new public std surface, no native, no seed change). ## Pinned records - [RELEASE.md 7.6 Security](https://git.torb.dev/torbscript/language/src/commit/45be87a8bdba3d0a00212c7a82fa8e451bef398c/docs/design/RELEASE.md#L1029) (passkey as second factor) - FIPS 186-5 section 6.4.2 (ECDSA signature verification), SEC 1 v2 sections 2.3.4 (point decoding) and 3.2.2.1 (public key validation), RFC 7518 section 3.4 (ES256, the 64-byte `r || s` form), W3C WebAuthn Level 3 section 6.5.6 (an ES256 assertion signature is DER `ECDSA-Sig-Value`) - `docs/standard-library/signature.md` ## Acceptance criteria - [ ] `P256PublicKey` (`Show`, `Equals`, `Hash`, `TryFrom<String, SignatureError>`): from the 65 bytes of an uncompressed SEC 1 point and from its two 32-byte coordinates (COSE key, JWK); both coordinates below p, the point on the curve, refused otherwise (`SignatureError.NotAPoint`); `bytes()`, `hex()`. - [ ] `P256Signature`: from 64 bytes `r || s` (JWS) and from strict DER (WebAuthn): one `SEQUENCE` of two positive minimal `INTEGER`s, short-form lengths, no trailing bytes, anything else refused with a reason; `r` and `s` both in `[1, n - 1]`, refused otherwise. - [ ] `P256PublicKey.verifies(message, signature)` per FIPS 186-5 6.4.2 with SHA-256; a result point at infinity is `false`; both the low-S and the high-S form of a signature verify (FIPS 186-5, WebAuthn and OpenSSL accept both), documented as a pitfall (a signature is malleable, never an identifier). - [ ] Test vectors from independent sources: RFC 6979 A.2.5, vectors made by Node's `crypto` (OpenSSL) in both signature forms, and edge cases from Project Wycheproof (`r`/`s` = 0, = n, above n, high S, points not on the curve). Every vector once as accepted and once as refused (changed message, signature or key); `torb test std/signature/tests` green natively and in the VM. - [ ] `docs/standard-library/signature.md` documents the new types, what is and is not constant time, and the measured speed; `skills/` regenerated. ## Seed impact None: new public types in `std/signature` in plain TorbScript over `Int` and `std/digest`'s `Sha256`; no syntax change, no native, no runtime change, no driver flag, and the compiler looks up none of the new names by string. Additive only. ## Size and dependencies One PR, one `feat(std/signature)` commit (plus tests and docs). No dependency. Parent: #16; slice 4 (#226, passkeys) depends on it. Security-relevant: architect review of the diff.
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261008 until 2026-10-09T05:30Z (worker of #16, branch loop/16-p256-verify)

[loop:torbscript] claim implement by session:loop-torbscript-20261008 until 2026-10-09T05:30Z (worker of #16, branch `loop/16-p256-verify`)
argus closed this issue 2026-10-09 03:54:34 +00:00
Sign in to join this conversation.
No labels
loop
ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
torbscript/language#225
No description provided.