/** Type alias for legibility. */
export type Bytes = Uint8Array<ArrayBuffer>
-export type View = DataView<ArrayBuffer>
-export const isArray = Array.isArray
+export type Words = Uint32Array<ArrayBuffer>
/** `TypedArray` constructors, bound before anything can replace the globals. */
const Bytes = Uint8Array
+const Words = Uint32Array
const TypedArray = Object.getPrototypeOf(Bytes.prototype)
-const View = DataView
const bytesSet = Function.prototype.call.bind(Bytes.prototype.set)
const bytesFill = Function.prototype.call.bind(Bytes.prototype.fill)
bytesFill(target, value, start, end)
}
+/** Captured version of `Array.isArray(value)`. */
+export const isArray = Array.isArray
+
/**
* True only for a genuine `Uint8Array` backed by a non-shared `ArrayBuffer`.
* Reads internal slot `[[TypedArrayName]]`, so a subclass overriding
return getTag(a) === 'Uint8Array' && isArrayBuffer(getBuffer(a))
}
+/** Checks system endianness. Returns true if little-endian, else false. */
+export const isLE = new Bytes(getBuffer(new Words([1])))[0] === 1
+
/**
- * Captured version of `DataView` reusing input offset and length.
- * Enables reading multiple bytes in a single
- * load which is more efficient than reading and combining individual bytes
- * with bitshifts. The caller is responsible for managing endianness.
+ * Captured version of `Uint32Array` reusing input offset and length.
+ * Enables reading multiple bytes in a single load which is more efficient than
+ * reading and combining individual bytes with bitshifts. The caller is
+ * responsible for managing endianness.
*
* Every boundary-crossing buffer comes from here or `normalize()`, so the
* offset is always 0 in practice; the offset check is a guard against mishap.
*/
-export function view (bytes: Bytes): View {
+export function words (bytes: Bytes): Words {
if (!isBytes(bytes)) {
- throw new TypeError('Error creating DataView', { cause: bytes })
+ throw new TypeError('Error creating Uint32Array', { cause: bytes })
}
const offset = getByteOffset(bytes)
if (offset !== 0) {
throw new RangeError('Unexpected byte offset', { cause: offset })
}
- return new View(getBuffer(bytes) as ArrayBuffer, offset, getByteLength(bytes))
+ return new Words(getBuffer(bytes) as ArrayBuffer, offset, getByteLength(bytes) >> 2)
}
const LOWER = '0123456789abcdef'
//@ts-expect-error
import nano25519_wasm from '../../build/nano25519.wasm'
import { Nano25519MutexError, Nano25519TypeError, abort, errorCodes, } from './errors'
-import { Bytes, View, byteLength, copy, view, } from './primordials'
+import { Bytes, Words, byteLength, copy, isLE, words } from './primordials'
type Exports = WebAssembly.Instance['exports'] & {
clearMemory: () => void
* 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 setInputMsgEnd (m: Bytes, i: number): void {
+function setInputMsgShift (m: 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),
)
}
+function setInputMsgWords (m: 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]
+ )
+}
+
+const setInputMsgEndian = isLE ? setInputMsgWords : setInputMsgShift
+
/**
* Copies the bytes of a message for signing, or signature verification, into
* the static message input buffer of the WASM module. The entire input is
function setInputMsg (message: Bytes): void {
const mlen = byteLength(message)
const mlentrunc = mlen & ~31
- const m = view(message)
+ const m = words(message)
for (let i = 0; i < mlentrunc; i += 32) {
- exports.setInputMsg(i,
- m.getUint32(i, true),
- m.getUint32(i + 4, true),
- m.getUint32(i + 8, true),
- m.getUint32(i + 12, true),
- m.getUint32(i + 16, true),
- m.getUint32(i + 20, true),
- m.getUint32(i + 24, true),
- m.getUint32(i + 28, true)
- )
+ //@ts-expect-error
+ setInputMsgEndian(m, i)
}
if (mlentrunc < mlen) {
- setInputMsgEnd(message, mlentrunc)
+ setInputMsgShift(message, mlentrunc)
}
}
const TypedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype)
/** @type {[string, (() => () => void)][]} */
const patches = [
+ ['Function.prototype.call', () => {
+ const real = Function.prototype.call
+ Function.prototype.call = () => { throw new Error('patched') }
+ return () => { Function.prototype.call = real }
+ }],
['Uint8Array.prototype.slice', () => {
const real = Uint8Array.prototype.slice
Uint8Array.prototype.slice = () => { throw new Error('patched') }