]> git.codecow.com Git - nano25519.git/commitdiff
Check signature R canonicity in strict verification.
authorChris Duncan <chris@zoso.dev>
Sat, 3 Oct 2026 07:16:49 +0000 (00:16 -0700)
committerChris Duncan <chris@zoso.dev>
Sat, 3 Oct 2026 07:16:49 +0000 (00:16 -0700)
src/assembly/crypto_verify.ts
src/assembly/ed25519/ge.ts

index 752981d3e6dd64913eb468fd5a35e985a9accf25..46741d5c5c4e4112459c9a878ed2b38d44a4b3c0 100644 (file)
@@ -130,7 +130,7 @@ export function crypto_verify_strict (sig: StaticArray<u8>, M: StaticArray<u8>,
 function verify_pubkey (pub: StaticArray<u8>): boolean {
 
        // fail if public key `k` is non-canonical (`p = 2²⁵⁵-19 ≤ k`)
-       if (!ge_is_canonical(pub)) return false
+       if (ge_is_canonical(pub) != 0) return false
 
        // fail if public key cannot be decoded
        if (ge_frombytes_negate_vartime(A, pub) != 0) return false
@@ -142,7 +142,7 @@ function verify_pubkey (pub: StaticArray<u8>): boolean {
 }
 
 /**
- * Verify signature `s` is canonical, decodeable, and not small order. Used by
+ * Verify signature `sig` is canonical, decodeable, and not small order. Used by
  * `crypto_verify_sodium` and `crypto_verify_strict`.
  * @returns 0 if signature fails checks, else return 1 if signature is good
  */
@@ -151,8 +151,13 @@ function verify_signature (sig: StaticArray<u8>): boolean {
        memory.copy(changetype<usize>(S), changetype<usize>(sig) + 32, 32)
        if (!sc_is_canonical(S)) return false
 
-       // fail if nonce `R` is not a valid point on the curve
+       // fail if nonce `R` is non-canonical (`p ≤ R`)
+       if (ge_is_canonical(sig) != 0) return false
+
+       // fail if nonce `R` does not decode to a valid curve point
        if (ge_frombytes(expected_r, sig) != 0) return false
+
+       // fail if nonce `R` is small order
        if (ge_has_small_order(expected_r) != 0) return false
 
        return true
index 6143eca2004329be441db09b41131b0165cbae56..55a31e1680fa4e675d12124925fa1b4b16096bfa 100644 (file)
@@ -265,16 +265,16 @@ export function ge_has_small_order (p: ge_p3): i32 {
 }
 
 /**
- * true if `s < p`
+ * @returns 0 if `s < p`, else -1
  */
-export function ge_is_canonical (s: StaticArray<u8>): u8 {
+export function ge_is_canonical (s: StaticArray<u8>): i32 {
        let c: i32 = (s[31] & 0x7f) ^ 0x7f
        for (let i = 30; i > 0; i--) {
-               c |= s[i] ^ 0xff
+               c |= s[i]
        }
        c = (c - 1) >> 8
        const d: i32 = (0xec - s[0]) >> 8
-       return u8(1 - (c & d & 1))
+       return 0 - (c & d & 1)
 }
 
 const ge_scalarmult_base_tobytes_h: ge_p3 = new ge_p3()