From 70d58604c23b8aa37e20d32b970409ee4573a3ca Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Sun, 27 Sep 2026 00:53:03 -0700 Subject: [PATCH] Use same endian-aware buffer copy from message for keys and signatures. --- src/lib/wasm.ts | 183 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 128 insertions(+), 55 deletions(-) diff --git a/src/lib/wasm.ts b/src/lib/wasm.ts index 595540e..cf14aaf 100644 --- a/src/lib/wasm.ts +++ b/src/lib/wasm.ts @@ -82,6 +82,8 @@ const MAX_VERIFY_BLOCKS = exports.MAX_VERIFY_BLOCKS.value const MAX_VERIFY_BLOCKS_BYTELENGTH = exports.MAX_VERIFY_BLOCKS_BYTELENGTH.value const MAX_MESSAGE_BYTELENGTH = exports.MAX_MESSAGE_BYTELENGTH.value +const setInputView = isLE ? words : (b: Bytes): Bytes => b + /** * Packs 32 bytes of message `m` starting at byte `i` into the static message * input buffer of the WASM module in little-endian order to align with the WASM @@ -92,30 +94,30 @@ const MAX_MESSAGE_BYTELENGTH = exports.MAX_MESSAGE_BYTELENGTH.value * This is the path for partial words. It runs for the short final chunk of any * message and ensures little-endian byte order and zero-padded filler. */ -function setInputMsgShift (m: Bytes, i: number): void { +function setInputMsgShift (b: Bytes, i: number): void { exports.setInputMsg(i, - (m[i] ?? 0) | ((m[i + 1] ?? 0) << 8) | ((m[i + 2] ?? 0) << 16) | ((m[i + 3] ?? 0) << 24), - (m[i + 4] ?? 0) | ((m[i + 5] ?? 0) << 8) | ((m[i + 6] ?? 0) << 16) | ((m[i + 7] ?? 0) << 24), - (m[i + 8] ?? 0) | ((m[i + 9] ?? 0) << 8) | ((m[i + 10] ?? 0) << 16) | ((m[i + 11] ?? 0) << 24), - (m[i + 12] ?? 0) | ((m[i + 13] ?? 0) << 8) | ((m[i + 14] ?? 0) << 16) | ((m[i + 15] ?? 0) << 24), - (m[i + 16] ?? 0) | ((m[i + 17] ?? 0) << 8) | ((m[i + 18] ?? 0) << 16) | ((m[i + 19] ?? 0) << 24), - (m[i + 20] ?? 0) | ((m[i + 21] ?? 0) << 8) | ((m[i + 22] ?? 0) << 16) | ((m[i + 23] ?? 0) << 24), - (m[i + 24] ?? 0) | ((m[i + 25] ?? 0) << 8) | ((m[i + 26] ?? 0) << 16) | ((m[i + 27] ?? 0) << 24), - (m[i + 28] ?? 0) | ((m[i + 29] ?? 0) << 8) | ((m[i + 30] ?? 0) << 16) | ((m[i + 31] ?? 0) << 24) + (b[i] ?? 0) | ((b[i + 1] ?? 0) << 8) | ((b[i + 2] ?? 0) << 16) | ((b[i + 3] ?? 0) << 24), + (b[i + 4] ?? 0) | ((b[i + 5] ?? 0) << 8) | ((b[i + 6] ?? 0) << 16) | ((b[i + 7] ?? 0) << 24), + (b[i + 8] ?? 0) | ((b[i + 9] ?? 0) << 8) | ((b[i + 10] ?? 0) << 16) | ((b[i + 11] ?? 0) << 24), + (b[i + 12] ?? 0) | ((b[i + 13] ?? 0) << 8) | ((b[i + 14] ?? 0) << 16) | ((b[i + 15] ?? 0) << 24), + (b[i + 16] ?? 0) | ((b[i + 17] ?? 0) << 8) | ((b[i + 18] ?? 0) << 16) | ((b[i + 19] ?? 0) << 24), + (b[i + 20] ?? 0) | ((b[i + 21] ?? 0) << 8) | ((b[i + 22] ?? 0) << 16) | ((b[i + 23] ?? 0) << 24), + (b[i + 24] ?? 0) | ((b[i + 25] ?? 0) << 8) | ((b[i + 26] ?? 0) << 16) | ((b[i + 27] ?? 0) << 24), + (b[i + 28] ?? 0) | ((b[i + 29] ?? 0) << 8) | ((b[i + 30] ?? 0) << 16) | ((b[i + 31] ?? 0) << 24) ) } -function setInputMsgWords (m: Words, i: number): void { +function setInputMsgWords (w: Words, i: number): void { const p = i >> 2 exports.setInputMsg(i, - m[p], - m[p + 1], - m[p + 2], - m[p + 3], - m[p + 4], - m[p + 5], - m[p + 6], - m[p + 7] + w[p], + w[p + 1], + w[p + 2], + w[p + 3], + w[p + 4], + w[p + 5], + w[p + 6], + w[p + 7] ) } @@ -138,7 +140,7 @@ const setInputMsgEndian = isLE ? setInputMsgWords : setInputMsgShift function setInputMsg (message: Bytes): void { const mlen = byteLength(message) const mlentrunc = mlen & ~31 - const m = words(message) + const m = setInputView(message) for (let i = 0; i < mlentrunc; i += 32) { //@ts-expect-error setInputMsgEndian(m, i) @@ -148,6 +150,34 @@ function setInputMsg (message: Bytes): void { } } +function setInputPrvShift (b: Bytes): void { + exports.setInputPrv( + (b[0] ?? 0) | ((b[1] ?? 0) << 8) | ((b[2] ?? 0) << 16) | ((b[3] ?? 0) << 24), + (b[4] ?? 0) | ((b[5] ?? 0) << 8) | ((b[6] ?? 0) << 16) | ((b[7] ?? 0) << 24), + (b[8] ?? 0) | ((b[9] ?? 0) << 8) | ((b[10] ?? 0) << 16) | ((b[11] ?? 0) << 24), + (b[12] ?? 0) | ((b[13] ?? 0) << 8) | ((b[14] ?? 0) << 16) | ((b[15] ?? 0) << 24), + (b[16] ?? 0) | ((b[17] ?? 0) << 8) | ((b[18] ?? 0) << 16) | ((b[19] ?? 0) << 24), + (b[20] ?? 0) | ((b[21] ?? 0) << 8) | ((b[22] ?? 0) << 16) | ((b[23] ?? 0) << 24), + (b[24] ?? 0) | ((b[25] ?? 0) << 8) | ((b[26] ?? 0) << 16) | ((b[27] ?? 0) << 24), + (b[28] ?? 0) | ((b[29] ?? 0) << 8) | ((b[30] ?? 0) << 16) | ((b[31] ?? 0) << 24) + ) +} + +function setInputPrvWords (w: Words): void { + exports.setInputPrv( + w[0], + w[1], + w[2], + w[3], + w[4], + w[5], + w[6], + w[7] + ) +} + +const setInputPrvEndian = isLE ? setInputPrvWords : setInputPrvShift + /** * Copies the bytes of a private key into the static private key input buffer of * the WASM module. Bytes are packed in little-endian order to align with the @@ -155,18 +185,39 @@ function setInputMsg (message: Bytes): void { * intermediate array because allocating costs more than the copy itself. */ function setInputPrv (prv: Bytes): void { - exports.setInputPrv( - prv[0] | (prv[1] << 8) | (prv[2] << 16) | (prv[3] << 24), - prv[4] | (prv[5] << 8) | (prv[6] << 16) | (prv[7] << 24), - prv[8] | (prv[9] << 8) | (prv[10] << 16) | (prv[11] << 24), - prv[12] | (prv[13] << 8) | (prv[14] << 16) | (prv[15] << 24), - prv[16] | (prv[17] << 8) | (prv[18] << 16) | (prv[19] << 24), - prv[20] | (prv[21] << 8) | (prv[22] << 16) | (prv[23] << 24), - prv[24] | (prv[25] << 8) | (prv[26] << 16) | (prv[27] << 24), - prv[28] | (prv[29] << 8) | (prv[30] << 16) | (prv[31] << 24) + const p = setInputView(prv) + //@ts-expect-error + setInputPrvEndian(p) +} + +function setInputPubShift (b: Bytes): void { + exports.setInputPub( + b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24), + b[4] | (b[5] << 8) | (b[6] << 16) | (b[7] << 24), + b[8] | (b[9] << 8) | (b[10] << 16) | (b[11] << 24), + b[12] | (b[13] << 8) | (b[14] << 16) | (b[15] << 24), + b[16] | (b[17] << 8) | (b[18] << 16) | (b[19] << 24), + b[20] | (b[21] << 8) | (b[22] << 16) | (b[23] << 24), + b[24] | (b[25] << 8) | (b[26] << 16) | (b[27] << 24), + b[28] | (b[29] << 8) | (b[30] << 16) | (b[31] << 24) + ) +} + +function setInputPubWords (w: Words): void { + exports.setInputPub( + w[0], + w[1], + w[2], + w[3], + w[4], + w[5], + w[6], + w[7] ) } +const setInputPubEndian = isLE ? setInputPubWords : setInputPubShift + /** * Copies the bytes of a public key into the static public key input buffer of * the WASM module. Bytes are packed in little-endian order to align with the @@ -174,18 +225,55 @@ function setInputPrv (prv: Bytes): void { * intermediate array because allocating costs more than the copy itself. */ function setInputPub (pub: Bytes): void { - exports.setInputPub( - pub[0] | (pub[1] << 8) | (pub[2] << 16) | (pub[3] << 24), - pub[4] | (pub[5] << 8) | (pub[6] << 16) | (pub[7] << 24), - pub[8] | (pub[9] << 8) | (pub[10] << 16) | (pub[11] << 24), - pub[12] | (pub[13] << 8) | (pub[14] << 16) | (pub[15] << 24), - pub[16] | (pub[17] << 8) | (pub[18] << 16) | (pub[19] << 24), - pub[20] | (pub[21] << 8) | (pub[22] << 16) | (pub[23] << 24), - pub[24] | (pub[25] << 8) | (pub[26] << 16) | (pub[27] << 24), - pub[28] | (pub[29] << 8) | (pub[30] << 16) | (pub[31] << 24) + const p = setInputView(pub) + //@ts-expect-error + setInputPubEndian(p) +} + +function setInputSigShift (b: Bytes): void { + exports.setInputSig( + b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24), + b[4] | (b[5] << 8) | (b[6] << 16) | (b[7] << 24), + b[8] | (b[9] << 8) | (b[10] << 16) | (b[11] << 24), + b[12] | (b[13] << 8) | (b[14] << 16) | (b[15] << 24), + b[16] | (b[17] << 8) | (b[18] << 16) | (b[19] << 24), + b[20] | (b[21] << 8) | (b[22] << 16) | (b[23] << 24), + b[24] | (b[25] << 8) | (b[26] << 16) | (b[27] << 24), + b[28] | (b[29] << 8) | (b[30] << 16) | (b[31] << 24), + b[32] | (b[33] << 8) | (b[34] << 16) | (b[35] << 24), + b[36] | (b[37] << 8) | (b[38] << 16) | (b[39] << 24), + b[40] | (b[41] << 8) | (b[42] << 16) | (b[43] << 24), + b[44] | (b[45] << 8) | (b[46] << 16) | (b[47] << 24), + b[48] | (b[49] << 8) | (b[50] << 16) | (b[51] << 24), + b[52] | (b[53] << 8) | (b[54] << 16) | (b[55] << 24), + b[56] | (b[57] << 8) | (b[58] << 16) | (b[59] << 24), + b[60] | (b[61] << 8) | (b[62] << 16) | (b[63] << 24) ) } +function setInputSigWords (w: Words): void { + exports.setInputSig( + w[0], + w[1], + w[2], + w[3], + w[4], + w[5], + w[6], + w[7], + w[8], + w[9], + w[10], + w[11], + w[12], + w[13], + w[14], + w[15] + ) +} + +const setInputSigEndian = isLE ? setInputSigWords : setInputSigShift + /** * Copies the bytes of a detached signature into the static signature input * buffer of the WASM module. Bytes are packed in little-endian order to align @@ -193,24 +281,9 @@ function setInputPub (pub: Bytes): void { * intermediate array because allocating costs more than the copy itself. */ function setInputSig (sig: Bytes): void { - exports.setInputSig( - sig[0] | (sig[1] << 8) | (sig[2] << 16) | (sig[3] << 24), - sig[4] | (sig[5] << 8) | (sig[6] << 16) | (sig[7] << 24), - sig[8] | (sig[9] << 8) | (sig[10] << 16) | (sig[11] << 24), - sig[12] | (sig[13] << 8) | (sig[14] << 16) | (sig[15] << 24), - sig[16] | (sig[17] << 8) | (sig[18] << 16) | (sig[19] << 24), - sig[20] | (sig[21] << 8) | (sig[22] << 16) | (sig[23] << 24), - sig[24] | (sig[25] << 8) | (sig[26] << 16) | (sig[27] << 24), - sig[28] | (sig[29] << 8) | (sig[30] << 16) | (sig[31] << 24), - sig[32] | (sig[33] << 8) | (sig[34] << 16) | (sig[35] << 24), - sig[36] | (sig[37] << 8) | (sig[38] << 16) | (sig[39] << 24), - sig[40] | (sig[41] << 8) | (sig[42] << 16) | (sig[43] << 24), - sig[44] | (sig[45] << 8) | (sig[46] << 16) | (sig[47] << 24), - sig[48] | (sig[49] << 8) | (sig[50] << 16) | (sig[51] << 24), - sig[52] | (sig[53] << 8) | (sig[54] << 16) | (sig[55] << 24), - sig[56] | (sig[57] << 8) | (sig[58] << 16) | (sig[59] << 24), - sig[60] | (sig[61] << 8) | (sig[62] << 16) | (sig[63] << 24) - ) + const s = setInputView(sig) + //@ts-expect-error + setInputSigEndian(s) } /** -- 2.52.0