* Original source commit: https://github.com/emilbayes/blake2b/blob/1f63e02e3f226642959506cdaa67c8819ff145cd/index.js
*/
export class Blake2b {
- /**
- * Derives account private keys from a wallet seed using the BLAKE2b hashing
- * algorithm.
- *
- * Separately, account public keys are derived from the private key using the
- * Ed25519 key algorithm, and account addresses are derived from the public key
- * as described in the Nano documentation.
- * https://docs.nano.org/integration-guides/the-basics/
- *
- * @param {ArrayBuffer} seed - 32-byte secret seed of the wallet
- * @param {number} index - 4-byte index of account to derive
- * @returns {Promise<ArrayBuffer>} Private key for the account
- */
- static ckd (seed: ArrayBuffer, index: number): Promise<ArrayBuffer> {
- const b = new ArrayBuffer(4)
- new DataView(b).setUint32(0, index, false)
- const s = new Uint8Array(seed)
- const i = new Uint8Array(b)
- const sk = new this(32).update(s).update(i).digest()
- return Promise.resolve(sk.buffer)
- }
-
#G (r: number, i: number, a: number, b: number, c: number, d: number): void {
this.#v[a] += this.#v[b] + this.#m[SIGMA[r][i << 1]]
this.#v[d] ^= this.#v[a]
}
#blake2bInit (length: number, key?: Bytes, salt?: Bytes, personal?: Bytes): void {
- // output length in bytes
- this.#outlen = length
-
// state, 'param block'
- this.#parameter_block[0] = length
+ this.#parameter_block[0] = this.#outlen = length
this.#parameter_block[1] = key?.length ?? 0
this.#parameter_block[2] = 1 // fanout
this.#parameter_block[3] = 1 // depth
* Update the BLAKE2b streaming hash with additional input. When the 128-byte
* input buffer is full, compress and start refilling.
*/
- #blake2bUpdate (input: Uint8Array): void {
+ #blake2bUpdate (input: Bytes): void {
for (let i = 0; i < input.length; i++) {
if (this.#c === this.#b.byteLength) { // is buffer full?
this.#t += BigInt(this.#b.byteLength) // increment total byte counter
/**
* Completes a BLAKE2b streaming hash.
*
- * @param {Uint8Array} out - Buffer to store the final output
+ * @param {Bytes} out - Buffer to store the final output
*/
#blake2bFinal (out: Bytes): void {
this.#t += BigInt(this.#c) // add final message block size to total bytes
* 32-47: salt
* 48-64: personal
*/
- #parameter_block: Uint8Array = new Uint8Array(64)
+ #parameter_block: Bytes = new Uint8Array(64)
#parameter_view: DataView = new DataView(this.#parameter_block.buffer)
/** Byte buffer which is compressed when full */
- #b: Uint8Array = new Uint8Array(128)
+ #b: Bytes = new Uint8Array(128)
/** Hash chain value */
#h: BigUint64Array = new BigUint64Array(8)
/** Total input byte count (BLAKE2b supports 2¹²⁸-1) */
*/
constructor (length: number, key?: Bytes, salt?: Bytes, personal?: Bytes)
constructor (length: unknown, key?: unknown, salt?: unknown, personal?: unknown) {
- if (length == null) {
- throw new TypeError(`length is required`)
- }
- if (typeof length !== 'number') {
- throw new TypeError(`length must be number`)
+ if (typeof length !== 'number' || length < OUTBYTES_MIN || length > OUTBYTES_MAX) {
+ throw new TypeError(`length is required and must be a number between ${OUTBYTES_MIN}-${OUTBYTES_MAX}`)
}
- if (length < OUTBYTES_MIN || length > OUTBYTES_MAX) {
- throw new RangeError(`length must be ${OUTBYTES_MIN}-${OUTBYTES_MAX} bytes`)
+ if (key !== undefined && (!isBytes(key) || key.length < KEYBYTES_MIN || key.length > KEYBYTES_MAX)) {
+ throw new RangeError(`key must be ${KEYBYTES_MIN}-${KEYBYTES_MAX} bytes`)
}
- if (key !== undefined) {
- if (!isBytes(key)) {
- throw new TypeError(`key must be Uint8Array or Buffer`)
- }
- if (key.length < KEYBYTES_MIN || key.length > KEYBYTES_MAX) {
- throw new RangeError(`key must be ${KEYBYTES_MIN}-${KEYBYTES_MAX} bytes`)
- }
- }
- if (salt !== undefined) {
- if (!isBytes(salt)) {
- throw new TypeError(`salt must be Uint8Array or Buffer`)
- }
- if (salt.length !== SALTBYTES) {
- throw new RangeError(`salt must be ${SALTBYTES} bytes`)
- }
+ if (salt !== undefined && (!isBytes(salt) || salt.length !== SALTBYTES)) {
+ throw new RangeError(`salt must be ${SALTBYTES} bytes`)
}
- if (personal !== undefined) {
- if (!isBytes(personal)) {
- throw new TypeError(`personal must be Uint8Array or Buffer`)
- }
- if (personal.length !== PERSONALBYTES) {
- throw new RangeError(`personal must be ${PERSONALBYTES} bytes`)
- }
+ if (personal !== undefined && (!isBytes(personal) || personal.length !== PERSONALBYTES)) {
+ throw new RangeError(`personal must be ${PERSONALBYTES} bytes`)
}
this.#blake2bInit(length, key, salt, personal)
}
/**
* Adds bytes to the context of the streaming hash.
*
- * @param {(ArrayBuffer|Uint8Array)} input - Bytes to hash
+ * @param {(ArrayBuffer|Bytes)} input - Bytes to hash
* @returns {Blake2b}
*/
- update (input: ArrayBuffer | Uint8Array): Blake2b {
- if (input instanceof ArrayBuffer) {
- input = new Uint8Array(input)
- }
- if (!(input instanceof Uint8Array)) {
- throw new TypeError('Input must be ArrayBuffer or Uint8Array')
+ update (input: ArrayBuffer | Bytes): Blake2b {
+ input = new Uint8Array(input)
+ if (!isBytes(input)) {
+ throw new TypeError('input must be bytes')
}
this.#blake2bUpdate(input)
return this