]> git.codecow.com Git - nano25519.git/commitdiff
Update overloads and README examples with output buffer functionality.
authorChris Duncan <chris@zoso.dev>
Sat, 15 Aug 2026 07:06:26 +0000 (00:06 -0700)
committerChris Duncan <chris@zoso.dev>
Sat, 15 Aug 2026 07:06:26 +0000 (00:06 -0700)
README.md
src/assembly/index.ts
src/lib/nano25519.ts
src/sync.ts

index d3bb4be88f31e458992612d040059dfc0a62a261..4c6baac876a86ff8443d57151e3dabe5982d3388 100644 (file)
--- a/README.md
+++ b/README.md
@@ -63,15 +63,34 @@ If a non-blocking solution is required, asynchronous implementations are
 provided which use Web Workers in the browser or Worker threads in NodeJS.
 
 Inputs can be passed as Uint8Array byte arrays (preferable) or strings. The
-output will return the same type as the input.
+output will return the same type as the input. A preallocated output buffer can
+be passed as an additional optional argument to the sync versions of `derive()`
+and `sign()` into which the result bytes will be written.
 
 ### Derive
 
 ```javascript
-// `prv` is a 32-byte Uint8Array private key
-const prv = new Uint8Array(32);
-const pub = nano25519.derive(prvBytes);
+// `prv` is a 32-byte private key
+const prv = new Uint8Array(usersPrivateKeyBuffer);
+
 // `pub` is a 32-byte Uint8Array public key for a Nano account
+const pub = nano25519.derive(prv);
+// clean up
+prv.fill(0);
+```
+
+### Derive into preallocated buffer
+
+```javascript
+// `prv` is a 32-byte private key
+const prv = new Uint8Array(usersPrivateKeyBuffer);
+// `pub` is an empty 32-byte Uint8Array
+const pub = new Uint8Array(32);
+
+// return discarded, 32-byte public key is written to `pub`
+nano25519.derive(prv, pub);
+// clean up
+prv.fill(0);
 ```
 
 ### Derive (async)
@@ -79,24 +98,39 @@ const pub = nano25519.derive(prvBytes);
 ```javascript
 // `prv` is a 64-character hex string private key
 const prv = "0000000000000000000000000000000000000000000000000000000000000000";
-const pub = await nano25519.deriveAsync(prv);
+
 // `pub` is a 64-character hex string public key for a Nano account
+const pub = await nano25519.deriveAsync(prv);
 ```
 
 ### Sign
 
 ```javascript
-// `msg` is a 32-byte Uint8Array hash of a valid Nano transaction block
-const msg = new Uint8Array(32);
-// `prv` is a 32-byte Uint8Array private key
-const prv = new Uint8Array(32);
-// `pub` is a 32-byte Uint8Array public key derived from `prv`
-const pub = nano25519.derive(prv);
-// `sk` is a 64-byte Uint8Array secret key joining private and public keys
-const sk = new Uint8Array([...prv, ...pub]);
+// `msg` is a 32-byte hash of a valid Nano transaction block
+const msg = new Uint8Array(blockHashBuffer);
+// `sk` is a 64-byte secret key joining private and public keys
+const sk = new Uint8Array(joinPrivateKeyPublicKeyBuffer);
 
-const sig = nano25519.sign(msg, sk);
 // `sig` is a 64-byte Uint8Array signature for the block hash
+const sig = nano25519.sign(msg, sk);
+// clean up
+sk.fill(0);
+```
+
+### Sign into preallocated buffer
+
+```javascript
+// `msg` is a 32-byte hash of a valid Nano transaction block
+const msg = new Uint8Array(blockHashBuffer);
+// `sk` is a 64-byte secret key joining private and public keys
+const sk = new Uint8Array(joinPrivateKeyPublicKeyBuffer);
+// `sig` is an empty 64-byte Uint8Array
+const sig = new Uint8Array(64);
+
+// return discarded, 64-byte signature is written to `sig`
+nano25519.sign(msg, sk);
+// clean up
+sk.fill(0);
 ```
 
 ### Sign (async)
@@ -111,22 +145,22 @@ const pub = await nano25519.deriveAsync(prv);
 // `sk` is a 128-char hex string secret key joining private and public keys
 const sk = prv + pub;
 
-const sig = await nano25519.signAsync(msg, sk);
 // `sig` is a 128-char hex string signature for the block hash
+const sig = await nano25519.signAsync(msg, sk);
 ```
 
 ### Verify
 
 ```javascript
-// `sig` is a 64-byte Uint8Array signature
-const sig = new Uint8Array(64);
-// `msg` is a 32-byte Uint8Array hash of a valid Nano transaction block
-const msg = new Uint8Array(32);
-// `pub` is a 32-byte Uint8Array public key for a Nano account
-const pub = new Uint8Array(32);
+// `sig` is a 64-byte signature
+const sig = new Uint8Array(signatureBuffer);
+// `msg` is a 32-byte hash of a valid Nano transaction block
+const msg = new Uint8Array(blockHashBuffer);
+// `pub` is a 32-byte public key for a Nano account
+const pub = new Uint8Array(usersPublicKeyBuffer);
 
-const v = nano25519.verify(sig, msg, pub);
 // `v` is a boolean 'true' if the same `prv` that derives `pub` was also used to create `sig` by signing `msg`, else 'false'
+const v = nano25519.verify(sig, msg, pub);
 ```
 
 ### Verify (async)
@@ -140,8 +174,8 @@ const msg = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
 // `pub` is a 64-char hex string public key for a Nano account
 const pub = "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210";
 
-const v = await nano25519.verifyAsync(sig, msg, pub);
 // `v` is a boolean 'true' if the same `prv` that derives `pub` was also used to create `sig` by signing `msg`, else 'false'
+const v = await nano25519.verifyAsync(sig, msg, pub);
 ```
 
 ## Notes
index da1d0ee786454d9b80df8aaf567754611c2040b1..549d116cd939dc8d369c8bd8f70339b4e4542188 100644 (file)
@@ -62,7 +62,7 @@ function crypto_sign (s: StaticArray<u8>, m: usize, mlen: i32, sk: StaticArray<u
        ge_scalarmult_base_tobytes(s, nonce)\r
 \r
        // Concatenate public key `A` and message `M` from parameter arguments:\r
-       // `A = sk[0,32], M = m`\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
index ca2fe32b137b37cfc0e832629cb9650142650dc1..288bd417b90de6c9046652a0059e0cfc92655ef1 100644 (file)
@@ -67,15 +67,15 @@ export const nano25519_init = (bytes: number[]): { derive: typeof derive, sign:
                }
        }) as Exports
 
-       function derive (k: unknown, out?: unknown): string | Uint8Array<ArrayBuffer> {
-               out ??= new Uint8Array(32)
+       function derive (prv: unknown, pub?: unknown): string | Uint8Array<ArrayBuffer> | void {
+               const out = pub ?? new Uint8Array(32)
                if (!(isBytes(out) && out.byteLength === 32)) {
                        throw new TypeError('Derive output buffer must be 32-byte Uint8Array<ArrayBuffer>')
                }
                let privateKey = new Uint8Array(32)
                let buffer = new DataView(exports.memory.buffer)
                try {
-                       privateKey.set(normalize('private key', 32, 32, k))
+                       privateKey.set(normalize('private key', 32, 32, prv))
                        let inPtr = exports.getInputPointer()
                        for (let i = 0; i < 32; i++) {
                                buffer.setUint8(inPtr + i, privateKey[i])
@@ -88,7 +88,9 @@ export const nano25519_init = (bytes: number[]): { derive: typeof derive, sign:
                                out[i] = buffer.getUint8(outPtr + i)
                        }
                        clear(buffer)
-                       if (typeof k === 'string') {
+                       if (typeof pub === 'undefined') {
+                               return
+                       } else if (typeof prv === 'string') {
                                let hex = ''
                                for (const byte of out) {
                                        hex += byte.toString(16).padStart(2, '0')
index 59e192ef41e6c599b4f945447e34ffa503fd7ef8..b857bda458d58abfa36735b9786392336617e35c 100644 (file)
@@ -16,17 +16,14 @@ export function derive (k: string): string
  */
 export function derive (k: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>
 /**
- * Nano public key derivation using WebAssembly. Public key bytes are written to
- * the user-supplied output buffer instead of being allocated internally.
- * Technically, the buffer is also returned for API consistency, but the
- * location in memory is the same as the output buffer, so the return value can
- * be ignored.
+ * Nano public key derivation using WebAssembly. Instead of allocating an output
+ * buffer internally for the return value, public key bytes are written to the
+ * the user-supplied output buffer.
  * @param {Uint8Array<ArrayBuffer>} prv - 32-byte private key
  * @param {Uint8Array<ArrayBuffer>} pub - buffer to receive 32-byte public key
- * @returns the same buffer allocated in memory to `pub`
  */
-export function derive (k: Uint8Array<ArrayBuffer>, out: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>
-export function derive (k: string | Uint8Array<ArrayBuffer>, out?: Uint8Array<ArrayBuffer>): string | Uint8Array<ArrayBuffer> {
+export function derive (k: Uint8Array<ArrayBuffer>, out: Uint8Array<ArrayBuffer>): void
+export function derive (k: string | Uint8Array<ArrayBuffer>, out?: Uint8Array<ArrayBuffer>): string | Uint8Array<ArrayBuffer> | void {
        return nano25519.derive(k, out)
 }
 
@@ -48,17 +45,15 @@ export function sign (m: string, k: string): string
 export function sign (m: Uint8Array<ArrayBuffer>, k: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>
 /**
  * Signing using WebAssembly. To sign Nano blocks, the message should be a
- * 32-byte block hash. Signature bytes are written to the user-supplied output
- * buffer instead of being allocated internally. Technically, the buffer is also
- * returned for API consistency, but the location in memory is the same as the
- * output buffer, so the return value can be ignored.
+ * 32-byte block hash. Instead of allocating an output buffer internally for the
+ * return value, signature bytes are written to the the user-supplied output
+ * buffer.
  * @param {Uint8Array<ArrayBuffer>} m - Variable-byte-length message up to 32 KiB
  * @param {Uint8Array<ArrayBuffer>} k - 64-byte secret key (prv + pub)
  * @param {Uint8Array<ArrayBuffer>} out - buffer to receive 64-byte detached signature
- * @returns the same buffer allocated in memory to `s`
  */
-export function sign (m: Uint8Array<ArrayBuffer>, k: Uint8Array<ArrayBuffer>, out: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>
-export function sign (m: string | Uint8Array<ArrayBuffer>, k: string | Uint8Array<ArrayBuffer>, out?: Uint8Array<ArrayBuffer>): string | Uint8Array<ArrayBuffer> {
+export function sign (m: Uint8Array<ArrayBuffer>, k: Uint8Array<ArrayBuffer>, out: Uint8Array<ArrayBuffer>): void
+export function sign (m: string | Uint8Array<ArrayBuffer>, k: string | Uint8Array<ArrayBuffer>, out?: Uint8Array<ArrayBuffer>): string | Uint8Array<ArrayBuffer> | void {
        return nano25519.sign(m, k, out)
 }
 
@@ -73,7 +68,7 @@ export function sign (m: string | Uint8Array<ArrayBuffer>, k: string | Uint8Arra
 export function verify (s: string, m: string, k: string): boolean
 /**
  * Signature verification using WebAssembly. To verify Nano block signatures,
- * the message should be a 64-character block hash.
+ * the message should be a 32-byte block hash.
  * @param {Uint8Array<ArrayBuffer>} s - 64-byte detached signature
  * @param {Uint8Array<ArrayBuffer>} m - Variable-byte-length message up to 32 KiB
  * @param {Uint8Array<ArrayBuffer>} k - 32-byte public key