-//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>\r
-//! SPDX-License-Identifier: GPL-3.0-or-later\r
-\r
-import { Blake2b } from './blake2b'\r
-import { ge_double_scalarmult_vartime_to_p3, ge_frombytes, ge_frombytes_negate_vartime, ge_has_small_order, ge_is_canonical, ge_scalarmult_base_tobytes } from './ge'\r
-import { ge_p3, ge_sub_p3 } from './p'\r
-import { sc_is_canonical, sc_muladd, sc_reduce } from './sc'\r
-\r
-const MESSAGE_BYTES: i32 = 32\r
-const PRIVATEKEY_BYTES: i32 = 32\r
-const PUBLICKEY_BYTES: i32 = 32\r
-const SECRETKEY_BYTES: i32 = PRIVATEKEY_BYTES + PUBLICKEY_BYTES\r
-const SIGNATURE_BYTES: i32 = 64\r
-const SIGNEDMESSAGE_BYTES: i32 = SIGNATURE_BYTES + MESSAGE_BYTES\r
-\r
-// Static I/O buffers\r
-const OUTPUT_BUFFER = memory.data(64)\r
-const INPUT_BUFFER = memory.data(128)\r
-const MESSAGE_BUFFER = memory.data(32768)\r
-\r
-// crypto_hash function\r
-const blake2b = new Blake2b()\r
-\r
-/**\r
- * Clears the 3 least significant bits to guarantee multiple of 8 and prevent\r
- * small-subgroup attacks based on Curve25519's cofactor of 8. Also sets bit 254\r
- * and clears bit 255 to guarantee a fixed bit length for constant time\r
- * performance.\r
- * @param k 32-byte scalar\r
- */\r
-//@ts-expect-error\r
-@inline\r
-function clamp (k: StaticArray<u8>): void {\r
- k[0] &= 248\r
- k[31] &= 127\r
- k[31] |= 64\r
-}\r
-\r
-const crypto_derive_a: StaticArray<u8> = new StaticArray<u8>(PRIVATEKEY_BYTES)\r
-const crypto_derive_az: StaticArray<u8> = new StaticArray<u8>(SECRETKEY_BYTES)\r
-/**\r
- * Hash seed to 32-byte scalar `a`, clamp it, then point-multiply it by the\r
- * Ed25519 base point.\r
- * @param pk 32-byte output buffer for the compressed public key\r
- * @param seed 32-byte input buffer for the securely-random secret seed value\r
- */\r
-function crypto_derive (pk: StaticArray<u8>, seed: StaticArray<u8>): void {\r
- const a = crypto_derive_a\r
- const az = crypto_derive_az\r
- blake2b.init().update(changetype<usize>(seed), 32).digest(az)\r
- memory.copy(changetype<usize>(a), changetype<usize>(az), 32)\r
- az.fill(0)\r
- clamp(a)\r
- ge_scalarmult_base_tobytes(pk, a)\r
- a.fill(0)\r
-}\r
-\r
-const crypto_sign_az = new StaticArray<u8>(64)\r
-const crypto_sign_nonce = new StaticArray<u8>(64)\r
-const crypto_sign_hram = new StaticArray<u8>(64)\r
-const crypto_sign_t = new StaticArray<u8>(PRIVATEKEY_BYTES)\r
-function crypto_sign (s: StaticArray<u8>, m: usize, mlen: i32, sk: StaticArray<u8>): void {\r
- const az = crypto_sign_az\r
- const nonce = crypto_sign_nonce\r
- const hram = crypto_sign_hram\r
- const t = changetype<usize>(crypto_sign_t)\r
-\r
- // Hash secret key to private scalar `a` and prefix for nonce derivation `z`\r
- memory.copy(t, changetype<usize>(sk), PRIVATEKEY_BYTES)\r
- blake2b.init().update(t, PRIVATEKEY_BYTES).digest(az)\r
- clamp(az)\r
-\r
- // Derive nonce from prefix `z` and message `m`\r
- blake2b.init().update(changetype<usize>(az) + 32, 32).update(m, mlen).digest(nonce)\r
- sc_reduce(nonce)\r
-\r
- // Compute R = rB, output to bytes s\r
- ge_scalarmult_base_tobytes(s, nonce)\r
-\r
- // Concatenate public key `A` and message `M` from parameter arguments:\r
- // `A = sk[32,63], M = m`\r
- // Compute challenge hash using `s = (R || A || M)`\r
- blake2b.init().update(changetype<usize>(s), 32).update(changetype<usize>(sk) + 32, 32).update(m, mlen).digest(hram)\r
- sc_reduce(hram)\r
-\r
- // Compute `S = (r + h*a) mod L` and construct final signature `s = (R || S)`\r
- sc_muladd(changetype<StaticArray<u8>>(changetype<usize>(s) + 32), changetype<StaticArray<u8>>(az), hram, nonce)\r
-\r
- // Clean up sensitive data\r
- az.fill(0)\r
- hram.fill(0)\r
- nonce.fill(0)\r
- memory.fill(t, 0, PRIVATEKEY_BYTES)\r
-}\r
-\r
-const crypto_verify_h = new StaticArray<u8>(64)\r
-const crypto_verify_check = new ge_p3()\r
-const crypto_verify_expected_r = new ge_p3()\r
-const crypto_verify_A = new ge_p3()\r
-const crypto_verify_sb_ah = new ge_p3()\r
-const crypto_verify_S = new StaticArray<u8>(32)\r
-/**\r
- * Verify signature `s` was made by signing message `m` using public key `pk`.\r
- */\r
-function crypto_verify (s: StaticArray<u8>, m: usize, mlen: i32, pk: StaticArray<u8>): i32 {\r
- const h = crypto_verify_h\r
- const check = crypto_verify_check\r
- const expected_r = crypto_verify_expected_r\r
- const A = crypto_verify_A\r
- const sb_ah = crypto_verify_sb_ah\r
- const S = crypto_verify_S\r
-\r
- // fail if public key `k` is non-canonical (`p = 2²⁵⁵-19 <= k`)\r
- if (!ge_is_canonical(pk)) return -1\r
-\r
- // fail if private scalar `S` is non-canonical (`L <= S`)\r
- memory.copy(changetype<usize>(S), changetype<usize>(s) + 32, 32)\r
- if (!sc_is_canonical(S)) return -1\r
-\r
- if (ge_frombytes_negate_vartime(A, pk) != 0) return -1\r
- if (ge_has_small_order(A) != 0) return -1\r
-\r
- if (ge_frombytes(expected_r, s) != 0) return -1\r
- if (ge_has_small_order(expected_r) != 0) return -1\r
-\r
- // signature is nonce point R and scalar S (R || S)\r
- // data to hash is nonce point R, public key A, and message M\r
- // from parameter arguments: R = s[0,32], A = pk, M = m\r
- // R, S, A, and M are all 32-byte values in this implementation\r
- blake2b.init().update(changetype<usize>(s), 32).update(changetype<usize>(pk), 32).update(m, mlen).digest(h)\r
- sc_reduce(h)\r
-\r
- ge_double_scalarmult_vartime_to_p3(sb_ah, h, A, S)\r
- ge_sub_p3(check, expected_r, sb_ah)\r
-\r
- return ge_has_small_order(check) - 1\r
-}\r
-\r
-// Returns the pointer to the static output buffer (64 bytes).\r
-export function getOutputPointer (): usize {\r
- return OUTPUT_BUFFER\r
-}\r
-\r
-// Returns the pointer to the static input buffer (128 bytes).\r
-export function getInputPointer (): usize {\r
- return INPUT_BUFFER\r
-}\r
-\r
-// Returns the pointer to the static message buffer (32 KiB).\r
-export function getMessagePointer (): usize {\r
- return MESSAGE_BUFFER\r
-}\r
-\r
-/**\r
- * Uses a private key to derive a Nano public key which is then written to the\r
- * static output buffer.\r
- */\r
-export function derive (): void {\r
- const pk = changetype<StaticArray<u8>>(OUTPUT_BUFFER)\r
- const seed = changetype<StaticArray<u8>>(INPUT_BUFFER)\r
- memory.fill(OUTPUT_BUFFER, 0, SECRETKEY_BYTES)\r
- crypto_derive(pk, seed)\r
- memory.fill(INPUT_BUFFER, 0, PRIVATEKEY_BYTES)\r
-}\r
-\r
-const sign_s = new StaticArray<u8>(SIGNEDMESSAGE_BYTES)\r
-/**\r
- * Sign a message using a private key. The signature is written to the static\r
- * output buffer. This mirrors the functionality of `nacl.sign.detached()`.\r
- *\r
- * @param {u64} m - Message to sign (variable byte length up to 32 KiB)\r
- * @param {i32} mlen - Byte length of message\r
- * @param {u64} sk - 64-byte secret key (32-byte private key + 32-byte public key)\r
- */\r
-export function sign (mlen: i32): void {\r
- if (mlen < 0 || mlen > 32768) throw new Error()\r
- const s = sign_s\r
- const m = MESSAGE_BUFFER\r
- const sk = changetype<StaticArray<u8>>(INPUT_BUFFER)\r
-\r
- memory.fill(OUTPUT_BUFFER, 0, SIGNATURE_BYTES)\r
- crypto_sign(s, m, mlen, sk)\r
- memory.copy(OUTPUT_BUFFER, changetype<usize>(s), SIGNATURE_BYTES)\r
- memory.fill(INPUT_BUFFER, 0, SECRETKEY_BYTES)\r
-}\r
-\r
-const verify_s = new StaticArray<u8>(SIGNATURE_BYTES)\r
-const verify_k = new StaticArray<u8>(PUBLICKEY_BYTES)\r
-/**\r
- * Verify a signature on a message against a public key. This mirrors the\r
- * functionality of `nacl.sign.detached.verify()`.\r
- *\r
- * @param {u64} s - 64-byte signature\r
- * @param {u64} m - Message that was signed (variable byte-length up to 32 KiB)\r
- * @param {u64} k - 32-byte public key\r
- */\r
-export function verify (mlen: i32): void {\r
- if (mlen < 0 || mlen > 32768) throw new Error('invalid message length')\r
- const s = verify_s\r
- const m = MESSAGE_BUFFER\r
- const k = verify_k\r
-\r
- memory.fill(OUTPUT_BUFFER, 0, SIGNATURE_BYTES)\r
- memory.copy(changetype<usize>(s), INPUT_BUFFER, SIGNATURE_BYTES)\r
- memory.copy(changetype<usize>(k), INPUT_BUFFER + SIGNATURE_BYTES, PUBLICKEY_BYTES)\r
- const v = crypto_verify(s, m, mlen, k)\r
- store<u8>(OUTPUT_BUFFER, v)\r
- memory.fill(INPUT_BUFFER, 0, SIGNATURE_BYTES)\r
-}\r
+//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { Blake2b } from './blake2b'
+import { ge_double_scalarmult_vartime_to_p3, ge_frombytes, ge_frombytes_negate_vartime, ge_has_small_order, ge_is_canonical, ge_scalarmult_base_tobytes } from './ge'
+import { ge_p3, ge_sub_p3 } from './p'
+import { sc_is_canonical, sc_muladd, sc_reduce } from './sc'
+
+const MESSAGE_BYTES: i32 = 32
+const PRIVATEKEY_BYTES: i32 = 32
+const PUBLICKEY_BYTES: i32 = 32
+const SECRETKEY_BYTES: i32 = PRIVATEKEY_BYTES + PUBLICKEY_BYTES
+const SIGNATURE_BYTES: i32 = 64
+const SIGNEDMESSAGE_BYTES: i32 = SIGNATURE_BYTES + MESSAGE_BYTES
+
+// Static I/O buffers
+const OUTPUT_BUFFER = memory.data(64)
+const INPUT_BUFFER = memory.data(128)
+const MESSAGE_BUFFER = memory.data(32768)
+
+// crypto_hash function
+const blake2b = new Blake2b()
+
+/**
+ * Clears the 3 least significant bits to guarantee multiple of 8 and prevent
+ * small-subgroup attacks based on Curve25519's cofactor of 8. Also sets bit 254
+ * and clears bit 255 to guarantee a fixed bit length for constant time
+ * performance.
+ * @param k 32-byte scalar
+ */
+//@ts-expect-error
+@inline
+function clamp (k: StaticArray<u8>): void {
+ k[0] &= 248
+ k[31] &= 127
+ k[31] |= 64
+}
+
+const crypto_derive_a: StaticArray<u8> = new StaticArray<u8>(PRIVATEKEY_BYTES)
+const crypto_derive_az: StaticArray<u8> = new StaticArray<u8>(SECRETKEY_BYTES)
+/**
+ * Hash seed to 32-byte scalar `a`, clamp it, then point-multiply it by the
+ * Ed25519 base point.
+ * @param pk 32-byte output buffer for the compressed public key
+ * @param seed 32-byte input buffer for the securely-random secret seed value
+ */
+function crypto_derive (pk: StaticArray<u8>, seed: StaticArray<u8>): void {
+ const a = crypto_derive_a
+ const az = crypto_derive_az
+ blake2b.init().update(changetype<usize>(seed), 32).digest(az)
+ memory.copy(changetype<usize>(a), changetype<usize>(az), 32)
+ az.fill(0)
+ clamp(a)
+ ge_scalarmult_base_tobytes(pk, a)
+ a.fill(0)
+}
+
+const crypto_sign_az = new StaticArray<u8>(64)
+const crypto_sign_nonce = new StaticArray<u8>(64)
+const crypto_sign_hram = new StaticArray<u8>(64)
+const crypto_sign_t = new StaticArray<u8>(PRIVATEKEY_BYTES)
+function crypto_sign (s: StaticArray<u8>, m: usize, mlen: i32, sk: StaticArray<u8>): void {
+ const az = crypto_sign_az
+ const nonce = crypto_sign_nonce
+ const hram = crypto_sign_hram
+ const t = changetype<usize>(crypto_sign_t)
+
+ // Hash secret key to private scalar `a` and prefix for nonce derivation `z`
+ memory.copy(t, changetype<usize>(sk), PRIVATEKEY_BYTES)
+ blake2b.init().update(t, PRIVATEKEY_BYTES).digest(az)
+ clamp(az)
+
+ // Derive nonce from prefix `z` and message `m`
+ blake2b.init().update(changetype<usize>(az) + 32, 32).update(m, mlen).digest(nonce)
+ sc_reduce(nonce)
+
+ // Compute R = rB, output to bytes s
+ ge_scalarmult_base_tobytes(s, nonce)
+
+ // Concatenate public key `A` and message `M` from parameter arguments:
+ // `A = sk[32,63], M = m`
+ // Compute challenge hash using `s = (R || A || M)`
+ blake2b.init().update(changetype<usize>(s), 32).update(changetype<usize>(sk) + 32, 32).update(m, mlen).digest(hram)
+ sc_reduce(hram)
+
+ // Compute `S = (r + h*a) mod L` and construct final signature `s = (R || S)`
+ sc_muladd(changetype<StaticArray<u8>>(changetype<usize>(s) + 32), changetype<StaticArray<u8>>(az), hram, nonce)
+
+ // Clean up sensitive data
+ az.fill(0)
+ hram.fill(0)
+ nonce.fill(0)
+ memory.fill(t, 0, PRIVATEKEY_BYTES)
+}
+
+const crypto_verify_h = new StaticArray<u8>(64)
+const crypto_verify_check = new ge_p3()
+const crypto_verify_expected_r = new ge_p3()
+const crypto_verify_A = new ge_p3()
+const crypto_verify_sb_ah = new ge_p3()
+const crypto_verify_S = new StaticArray<u8>(32)
+/**
+ * Verify signature `s` was made by signing message `m` using public key `pk`.
+ * @returns -1 if signature fails to verify, else return 0 if signature is good
+ */
+function crypto_verify (s: StaticArray<u8>, m: usize, mlen: i32, pk: StaticArray<u8>): i32 {
+ const h = crypto_verify_h
+ const check = crypto_verify_check
+ const expected_r = crypto_verify_expected_r
+ const A = crypto_verify_A
+ const sb_ah = crypto_verify_sb_ah
+ const S = crypto_verify_S
+
+ // fail if public key `k` is non-canonical (`p = 2²⁵⁵-19 <= k`)
+ if (!ge_is_canonical(pk)) 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, pk) != 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
+
+ // signature is nonce point R and scalar S (R || S)
+ // data to hash is nonce point R, public key A, and message M
+ // from parameter arguments: R = s[0,32], A = pk, M = m
+ // R, S, A, and M are all 32-byte values in this implementation
+ blake2b.init().update(changetype<usize>(s), 32).update(changetype<usize>(pk), 32).update(m, mlen).digest(h)
+ sc_reduce(h)
+
+ ge_double_scalarmult_vartime_to_p3(sb_ah, h, A, S)
+ ge_sub_p3(check, expected_r, sb_ah)
+
+ return ge_has_small_order(check) - 1
+}
+
+// Returns the pointer to the static output buffer (64 bytes).
+export function getOutputPointer (): usize {
+ return OUTPUT_BUFFER
+}
+
+// Returns the pointer to the static input buffer (128 bytes).
+export function getInputPointer (): usize {
+ return INPUT_BUFFER
+}
+
+// Returns the pointer to the static message buffer (32 KiB).
+export function getMessagePointer (): usize {
+ return MESSAGE_BUFFER
+}
+
+/**
+ * Uses a private key to derive a Nano public key which is then written to the
+ * static output buffer.
+ */
+export function derive (): void {
+ const pk = changetype<StaticArray<u8>>(OUTPUT_BUFFER)
+ const seed = changetype<StaticArray<u8>>(INPUT_BUFFER)
+ memory.fill(OUTPUT_BUFFER, 0, SECRETKEY_BYTES)
+ crypto_derive(pk, seed)
+ memory.fill(INPUT_BUFFER, 0, PRIVATEKEY_BYTES)
+}
+
+const sign_s = new StaticArray<u8>(SIGNEDMESSAGE_BYTES)
+/**
+ * Sign a message using a private key. The signature is written to the static
+ * output buffer. This mirrors the functionality of `nacl.sign.detached()`.
+ *
+ * @param {u64} m - Message to sign (variable byte length up to 32 KiB)
+ * @param {i32} mlen - Byte length of message
+ * @param {u64} sk - 64-byte secret key (32-byte private key + 32-byte public key)
+ */
+export function sign (mlen: i32): void {
+ if (mlen < 0 || mlen > 32768) throw new Error()
+ const s = sign_s
+ const m = MESSAGE_BUFFER
+ const sk = changetype<StaticArray<u8>>(INPUT_BUFFER)
+
+ memory.fill(OUTPUT_BUFFER, 0, SIGNATURE_BYTES)
+ crypto_sign(s, m, mlen, sk)
+ memory.copy(OUTPUT_BUFFER, changetype<usize>(s), SIGNATURE_BYTES)
+ memory.fill(INPUT_BUFFER, 0, SECRETKEY_BYTES)
+}
+
+const verify_s = new StaticArray<u8>(SIGNATURE_BYTES)
+const verify_k = new StaticArray<u8>(PUBLICKEY_BYTES)
+/**
+ * Verify a signature on a message against a public key. This mirrors the
+ * functionality of `nacl.sign.detached.verify()`.
+ *
+ * @param {u64} s - 64-byte signature
+ * @param {u64} m - Message that was signed (variable byte-length up to 32 KiB)
+ * @param {u64} k - 32-byte public key
+ */
+export function verify (mlen: i32): void {
+ if (mlen < 0 || mlen > 32768) throw new Error('invalid message length')
+ const s = verify_s
+ const m = MESSAGE_BUFFER
+ const k = verify_k
+
+ memory.fill(OUTPUT_BUFFER, 0, SIGNATURE_BYTES)
+ memory.copy(changetype<usize>(s), INPUT_BUFFER, SIGNATURE_BYTES)
+ memory.copy(changetype<usize>(k), INPUT_BUFFER + SIGNATURE_BYTES, PUBLICKEY_BYTES)
+ const v = crypto_verify(s, m, mlen, k)
+ store<u8>(OUTPUT_BUFFER, v)
+ memory.fill(INPUT_BUFFER, 0, SIGNATURE_BYTES)
+}