//! SPDX-FileCopyrightText: 2013-2026 Frank Denis <j@pureftpd.org>
//! SPDX-License-Identifier: ISC
-import { FieldElement } from './fe'
-
-/**
- * √-1
- */
-export const fe_sqrtm1: FieldElement = StaticArray.fromArray<i32>([
- -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482, 0, 0
-])
-
-/**
- * d = −121665 / 121666
- * = 37095705934669439343138083508754565189542113879843219016388785533085940283555
- */
-export const ed25519_d: FieldElement = StaticArray.fromArray<i32>([
- -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116, 0, 0
-])
-
-/**
- * 2d = −121665 / 60833
- * = 16295367250680780974490674513165176452449235426866156013048779062215315747161
- */
-export const ed25519_d2: FieldElement = StaticArray.fromArray<i32>([
- -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199, 0, 0
-])
-
const PAGE_BYTELENGTH: i32 = 1 << 16
export const BLOCKHASH_BYTELENGTH: i32 = 32
export const KEY_BYTELENGTH: i32 = 32
//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
-import { Blake2b } from './blake2b'
-import { ge_scalarmult_base_tobytes } from './ge'
-import { clamp } from './utils'
+import { Blake2b } from './ed25519/blake2b'
+import { ge_scalarmult_base_tobytes } from './ed25519/ge'
+import { clamp } from './ed25519/utils'
const PRIVATEKEY_BYTES: i32 = 32
const PUBLICKEY_BYTES: i32 = 32
//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
-import { Blake2b } from './blake2b'
import { KEY_BYTELENGTH } from './constants'
import { crypto_derive } from './crypto_derive'
-import { ge_scalarmult_base_tobytes } from './ge'
-import { sc_muladd, sc_reduce } from './sc'
-import { clamp } from './utils'
+import { Blake2b } from './ed25519/blake2b'
+import { ge_scalarmult_base_tobytes } from './ed25519/ge'
+import { sc_muladd, sc_reduce } from './ed25519/sc'
+import { clamp } from './ed25519/utils'
// crypto_hash function
const blake2b = new Blake2b()
//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
-import { Blake2b } from './blake2b'
import { BLOCKHASH_BYTELENGTH, KEY_BYTELENGTH } from './constants'
-import { ge_double_scalarmult_vartime_to_p3, ge_frombytes, ge_frombytes_negate_vartime, ge_has_small_order, ge_is_canonical } from './ge'
-import { ge_p3, ge_p3_tobytes, ge_sub_p3 } from './p'
-import { sc_is_canonical, sc_reduce } from './sc'
-import { equalbytes } from './utils'
+import { Blake2b } from './ed25519/blake2b'
+import { ge_double_scalarmult_vartime_to_p3, ge_frombytes, ge_frombytes_negate_vartime, ge_has_small_order, ge_is_canonical } from './ed25519/ge'
+import { ge_p3, ge_p3_tobytes, ge_sub_p3 } from './ed25519/p'
+import { sc_is_canonical, sc_reduce } from './ed25519/sc'
+import { equalbytes } from './ed25519/utils'
// crypto_hash function
const blake2b = new Blake2b()
--- /dev/null
+//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
+//! SPDX-License-Identifier: GPL-3.0-or-later
+
+import { KEY_BYTELENGTH } from '../constants'
+import { crypto_derive } from '../crypto_derive'
+import { Blake2b } from './blake2b'
+import { ge_scalarmult_base_tobytes } from './ge'
+import { sc_muladd, sc_reduce } from './sc'
+import { clamp } from './utils'
+
+// crypto_hash function
+const blake2b = new Blake2b()
+
+// algorithm variables
+const A = new StaticArray<u8>(32)
+const h = new StaticArray<u8>(64)
+const s = new StaticArray<u8>(32)
+const prefix = new StaticArray<u8>(32)
+const r = new StaticArray<u8>(64)
+const R = new StaticArray<u8>(64)
+const k = new StaticArray<u8>(64)
+const S = new StaticArray<u8>(64)
+/**
+ * Sign a message with a private key. The Nano specification uses BLAKE2b as the
+ * hash function instead of SHA-512 specified by RFC 8032.
+ *
+ * In this implementation, both the private key and the public key are required.
+ * This ensures the user has a full correct keypair for data integrity. It also
+ * offers a small performance improvement in the case of an invalid public key
+ * by returning early and avoiding expensive point multiplication; RFC 8032
+ * indicates the public key should be recomputed from the private key when
+ * signing anyway, so there is no penalty for checking key validity first.
+ *
+ * https://www.rfc-editor.org/info/rfc8032/#section-5.1.6
+ *
+ * @param {StaticArray<u8>} RS 64-byte output buffer for detached signature
+ * @param {StaticArray<u8>} M variable-length message to be signed
+ * @param {i32} mlen bytelength of `m`
+ * @param {StaticArray<u8>} prv 32-byte private key from input buffer
+ * @param {StaticArray<u8>} pub 32-byte public key from input buffer
+ */
+export function crypto_sign (RS: StaticArray<u8>, M: StaticArray<u8>, mlen: i32, prv: StaticArray<u8>, pub: StaticArray<u8>): i32 {
+ // Derive `A` from private key
+ crypto_derive(A, prv)
+ let c = 0
+ for (let i = 0; i < KEY_BYTELENGTH; i++) {
+ c |= A[i] ^ pub[i]
+ }
+ // Return early if `A` does not match public key
+ if (c != 0) return -1
+
+ // Hash private key to `h`
+ blake2b.init().update(prv, KEY_BYTELENGTH).digest(h)
+ prv.fill(0)
+
+ // Split `h` into clamped secret scalar `s` and nonce prefix
+ memory.copy(changetype<usize>(s), changetype<usize>(h), 32)
+ clamp(s)
+ memory.copy(changetype<usize>(prefix), changetype<usize>(h) + 32, 32)
+ h.fill(0)
+
+ // Hash prefix and message `M` to nonce `r`
+ blake2b.init().update(prefix, 32).update(M, mlen).digest(r)
+ prefix.fill(0)
+
+ // Reduce `r` modulo `L`, the group order of the base point `B`
+ sc_reduce(r)
+
+ // Perform fixed-base scalar multiplication `[r]B`, output to point `R`
+ ge_scalarmult_base_tobytes(R, r)
+
+ // Compute challenge hash `blake2b(R || A || M)`, output to `k`
+ blake2b.init().update(R, 32).update(A, 32).update(M, mlen).digest(k)
+ A.fill(0)
+
+ // Reduce `k` modulo `L` for efficiency
+ sc_reduce(k)
+
+ // Compute `S = (k * s + r) mod L`
+ sc_muladd(S, k, s, r)
+ k.fill(0)
+ s.fill(0)
+ r.fill(0)
+
+ // Construct final signature `(R || S)`
+ memory.copy(changetype<usize>(RS), changetype<usize>(R), 32)
+ R.fill(0)
+ memory.copy(changetype<usize>(RS) + 32, changetype<usize>(S), 32)
+ S.fill(0)
+
+ return 0
+}
*/
import { base } from './base'
import { base2 } from './base2'
-import { ed25519_d, fe_sqrtm1 } from './constants'
import { FieldElement, fe, fe_1, fe_add, fe_cmov, fe_frombytes, fe_isnegative, fe_iszero, fe_memcmov, fe_memcopy, fe_memneg, fe_mul, fe_neg, fe_pow22523, fe_sq, fe_sub } from './fe'
import { ge_add_cached, ge_add_precomp, ge_cached, ge_p1p1, ge_p1p1_0, ge_p1p1_to_p2, ge_p1p1_to_p3, ge_p2, ge_p2_0, ge_p2_dbl, ge_p2_to_p3, ge_p3, ge_p3_0, ge_p3_dbl, ge_p3_to_cached, ge_p3_tobytes, ge_precomp, ge_precomp_0, ge_sub_cached, ge_sub_precomp } from './p'
import { equal, negative } from './utils'
+/**
+ * √-1
+ */
+const fe_sqrtm1: FieldElement = StaticArray.fromArray<i32>([
+ -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482, 0, 0
+])
+
+/**
+ * d = −121665 / 121666
+ * = 37095705934669439343138083508754565189542113879843219016388785533085940283555
+ */
+const ed25519_d: FieldElement = StaticArray.fromArray<i32>([
+ -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116, 0, 0
+])
+
//@ts-expect-error
@inline
function ge_cmov (t: ge_precomp, u: usize, b: u8): void {
//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
-import { ed25519_d2 } from './constants'
import { FieldElement, fe, fe_0, fe_1, fe_add, fe_copy, fe_dbl, fe_invert, fe_isnegative, fe_mul, fe_neg, fe_sq, fe_sq2, fe_sq_vec, fe_sub, fe_tobytes } from './fe'
+/**
+ * 2d = −121665 / 60833
+ * = 16295367250680780974490674513165176452449235426866156013048779062215315747161
+ */
+const ed25519_d2: FieldElement = StaticArray.fromArray<i32>([
+ -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199, 0, 0
+])
+
export class ge_p2 {
X: FieldElement = fe()
Y: FieldElement = fe()
//! SPDX-License-Identifier: GPL-3.0-or-later
import { KEY_BYTELENGTH } from './constants'
+import { equalbytes } from './ed25519/utils'
import { raise, } from './env'
import { ERROR_SELFTEST_XFAIL_ONE, ERROR_SELFTEST_XFAIL_PAIR, ERROR_SELFTEST_XPASS, } from './errors'
-import { equalbytes } from './utils'
/**
* Tests run on initialization to ensure build is correct.
try {
result = sign(NANO_ORG_VECTOR.blockHash, NANO_ORG_VECTOR.privateKey, NANO_ORG_VECTOR.publicKey.replace('3', '1'))
- console.log(result)
result = false
} catch (err) {
- console.log(err)
if (err != null && typeof err === 'object' && 'message' in err && typeof err.message === 'string') {
result = err.constructor.name === 'Nano25519WasmError' && err.message === 'Invalid public key'
} else {