]> git.codecow.com Git - nano25519.git/commitdiff
Extract public key validation in verify to enable bulk ops.
authorChris Duncan <chris@codecow.com>
Thu, 27 Aug 2026 01:55:29 +0000 (18:55 -0700)
committerChris Duncan <chris@codecow.com>
Thu, 27 Aug 2026 01:55:29 +0000 (18:55 -0700)
src/assembly/crypto_verify.ts
src/assembly/index.ts

index bab9018fadf9a6ccc415b8a24f60b4762f998a61..9a6a3ae7f831862a92d36dba90e19532d8227ad3 100644 (file)
@@ -18,21 +18,33 @@ const sb_ah = new ge_p3()
 const S = new StaticArray<u8>(32)
 
 /**
- * Verify signature `s` was made by signing message `M` using public key `pub`.
- * @returns -1 if signature fails to verify, else return 0 if signature is good
+ * Verify public key `pub` is canonical, non-malleable, and correct.
+ * @returns -1 if public key fails to verify, else return 0 if public key is good
  */
-export function crypto_verify_strict (s: StaticArray<u8>, M: StaticArray<u8>, mlen: i32, pub: StaticArray<u8>): i32 {
+export function crypto_verify_pubkey (pub: StaticArray<u8>): i32 {
 
        // fail if public key `k` is non-canonical (`p = 2²⁵⁵-19 ≤ k`)
        if (!ge_is_canonical(pub)) return -1
 
+       if (ge_frombytes_negate_vartime(A, pub) != 0) return -1
+       if (ge_has_small_order(A) != 0) return -1
+
+       return 0
+}
+
+/**
+ * Verify signature `s` was made by signing message `M` using public key `pub`.
+ *
+ * IMPORTANT: Callers MUST call `crypto_verify_pubkey` first in order to set `A`
+ * for the scalar multiplication step before checking `sB = R + hA`.
+ * @returns -1 if signature fails to verify, else return 0 if signature is good
+ */
+export function crypto_verify_relaxed (s: StaticArray<u8>, M: StaticArray<u8>, mlen: i32, pub: StaticArray<u8>): i32 {
+
        // fail if private scalar `S` is non-canonical (`L ≤ S`)
        memory.copy(changetype<usize>(S), changetype<usize>(s) + 32, 32)
        if (!sc_is_canonical(S)) return -1
 
-       if (ge_frombytes_negate_vartime(A, pub) != 0) return -1
-       if (ge_has_small_order(A) != 0) return -1
-
        if (ge_frombytes(expected_r, s) != 0) return -1
        if (ge_has_small_order(expected_r) != 0) return -1
 
@@ -51,20 +63,19 @@ export function crypto_verify_strict (s: StaticArray<u8>, M: StaticArray<u8>, ml
 
 /**
  * Verify signature `s` was made by signing message `M` using public key `pub`.
+ *
+ * Unlike `crypto_verify_relaxed`, this function is intended for single-use
+ * verifications and thus can compute and set `A` by calling
+ * `crypto_verify_pubkey` itself.
  * @returns -1 if signature fails to verify, else return 0 if signature is good
  */
-export function crypto_verify_relaxed (s: StaticArray<u8>, M: StaticArray<u8>, mlen: i32, pub: StaticArray<u8>): i32 {
-
-       // fail if public key `k` is non-canonical (`p = 2²⁵⁵-19 ≤ k`)
-       if (!ge_is_canonical(pub)) return -1
+export function crypto_verify_strict (s: StaticArray<u8>, M: StaticArray<u8>, mlen: i32, pub: StaticArray<u8>): i32 {
+       if (crypto_verify_pubkey(pub) != 0) return -1
 
        // fail if private scalar `S` is non-canonical (`L ≤ S`)
        memory.copy(changetype<usize>(S), changetype<usize>(s) + 32, 32)
        if (!sc_is_canonical(S)) return -1
 
-       if (ge_frombytes_negate_vartime(A, pub) != 0) return -1
-       if (ge_has_small_order(A) != 0) return -1
-
        if (ge_frombytes(expected_r, s) != 0) return -1
        if (ge_has_small_order(expected_r) != 0) return -1
 
index 89cfc553a77fe7bf889aad6da6f266d519e89405..07552cdaae7d53472619e00acb7c71ea5a64da66 100644 (file)
@@ -3,7 +3,7 @@
 
 import { crypto_derive } from './crypto_derive'
 import { crypto_sign } from './crypto_sign'
-import { crypto_verify_relaxed, crypto_verify_strict } from './crypto_verify'
+import { crypto_verify_pubkey, crypto_verify_relaxed, crypto_verify_strict } from './crypto_verify'
 
 export const BLOCKHASH_BYTELENGTH: i32 = 32
 export const KEY_BYTELENGTH: i32 = 32
@@ -195,6 +195,10 @@ export function verify_blocks (count: i32): void {
        memory.copy(changetype<usize>(verify_blocks_pub), changetype<usize>(PUB_BUFFER), KEY_BYTELENGTH)
        PUB_BUFFER.fill(0)
 
+       // Verify public key, then clear local input
+       const validPubkey = crypto_verify_pubkey(verify_blocks_pub)
+       verify_blocks_pub.fill(0)
+
        // Iterate over block hash/signature pairs
        for (let i = 0; i < count; i++) {
                // Copy message buffer to local block hash/signature buffers
@@ -202,8 +206,7 @@ export function verify_blocks (count: i32): void {
                memory.copy(changetype<usize>(verify_blocks_sig), changetype<usize>(MESSAGE_BUFFER) + BLOCKHASH_BYTELENGTH + (96 * i), SIGNATURE_BYTELENGTH)
 
                // Verify hash and signature, then copy local result to output buffer
-               const verified = crypto_verify_relaxed(verify_blocks_sig, verify_blocks_msg, BLOCKHASH_BYTELENGTH, verify_blocks_pub)
-               verify_blocks_out[i] = u8(verified)
+               verify_blocks_out[i] = u8(validPubkey | crypto_verify_relaxed(verify_blocks_sig, verify_blocks_msg, BLOCKHASH_BYTELENGTH, verify_blocks_pub))
        }
 
        // Clear local input