From 9cc0cedeadb29779e9def928789074b644cb7225 Mon Sep 17 00:00:00 2001 From: Andre Staltz Date: Mon, 12 Jun 2023 14:44:25 +0300 Subject: [PATCH] replace Buffer with Uint8Array --- lib/curves/ed25519.js | 53 ++++++++++++++++++++++++------------------- lib/curves/index.js | 12 +++++----- lib/index.js | 34 +++++++++++++-------------- test/generate.test.js | 4 ++-- test/signing.test.js | 19 ++++++++-------- 5 files changed, 65 insertions(+), 57 deletions(-) diff --git a/lib/curves/ed25519.js b/lib/curves/ed25519.js index d62bde2..eb61683 100644 --- a/lib/curves/ed25519.js +++ b/lib/curves/ed25519.js @@ -1,6 +1,10 @@ // @ts-ignore const sodium = require('sodium-universal') const base58 = require('bs58') +const _TextEncoder = + typeof window !== 'undefined' + ? window.TextEncoder + : require('node:util').TextEncoder /** * @typedef {import('.').Keypair} Keypair @@ -16,37 +20,36 @@ const SECRETKEYBYTES = sodium.crypto_sign_SECRETKEYBYTES const ed25519 = { /** - * @param {(string | Buffer)=} seed + * @param {(string | Uint8Array)=} seed * @returns {Keypair} */ generate(seed) { - let seedBuf + let seedBytes if (seed) { - if (Buffer.isBuffer(seed)) { + if (seed instanceof Uint8Array) { // prettier-ignore if (seed.length !== SEEDBYTES) throw new Error(`seed must be ${SEEDBYTES} bytes`) - seedBuf = seed + seedBytes = seed } else if (typeof seed === 'string') { - seedBuf = Buffer.alloc(SEEDBYTES) - const slice = seed.substring(0, SEEDBYTES) - Buffer.from(slice, 'utf-8').copy(seedBuf) + seedBytes = new Uint8Array(SEEDBYTES) + new _TextEncoder().encodeInto(seed, seedBytes) } } - const publicKeyBuf = Buffer.alloc(PUBLICKEYBYTES) - const secretKeyBuf = Buffer.alloc(SECRETKEYBYTES) - if (seedBuf) { - sodium.crypto_sign_seed_keypair(publicKeyBuf, secretKeyBuf, seedBuf) + const publicKeyBytes = new Uint8Array(PUBLICKEYBYTES) + const secretKeyBytes = new Uint8Array(SECRETKEYBYTES) + if (seedBytes) { + sodium.crypto_sign_seed_keypair(publicKeyBytes, secretKeyBytes, seedBytes) } else { - sodium.crypto_sign_keypair(publicKeyBuf, secretKeyBuf) + sodium.crypto_sign_keypair(publicKeyBytes, secretKeyBytes) } return { curve: 'ed25519', - public: base58.encode(publicKeyBuf), - private: base58.encode(secretKeyBuf), - _public: publicKeyBuf, - _private: secretKeyBuf, + public: base58.encode(publicKeyBytes), + private: base58.encode(secretKeyBytes), + _public: publicKeyBytes, + _private: secretKeyBytes, } }, @@ -69,15 +72,15 @@ const ed25519 = { /** * @param {KeypairPrivateSlice} keypair - * @param {Buffer} message + * @param {Uint8Array} message * @returns {string} */ sign(keypair, message) { if (!keypair._private && !keypair.private) { throw new Error(`invalid ed25519 keypair with missing private key`) } - keypair._private ??= Buffer.from(base58.decode(keypair.private)) - const sig = Buffer.alloc(sodium.crypto_sign_BYTES) + keypair._private ??= base58.decode(keypair.private) + const sig = new Uint8Array(sodium.crypto_sign_BYTES) sodium.crypto_sign_detached(sig, message, keypair._private) return base58.encode(sig) }, @@ -85,16 +88,20 @@ const ed25519 = { /** * @param {KeypairPublicSlice} keypair * @param {string} sig - * @param {Buffer} message + * @param {Uint8Array} message * @returns {boolean} */ verify(keypair, sig, message) { if (!keypair._public && !keypair.public) { throw new Error(`invalid ed25519 keypair with missing public key`) } - keypair._public ??= Buffer.from(base58.decode(keypair.public)) - const sigBuf = Buffer.from(base58.decode(sig)) - return sodium.crypto_sign_verify_detached(sigBuf, message, keypair._public) + keypair._public ??= base58.decode(keypair.public) + const sigBytes = new Uint8Array(base58.decode(sig)) + return sodium.crypto_sign_verify_detached( + sigBytes, + message, + keypair._public + ) }, } diff --git a/lib/curves/index.js b/lib/curves/index.js index 8732b0a..3d91c18 100644 --- a/lib/curves/index.js +++ b/lib/curves/index.js @@ -11,8 +11,8 @@ const curves = { * curve: CurveName, * public: string, * private: string, - * _public?: Buffer, - * _private?: Buffer, + * _public?: Uint8Array, + * _private?: Uint8Array, * }} Keypair * * @typedef {(Pick & {_public: never}) | @@ -24,10 +24,10 @@ const curves = { * } KeypairPrivateSlice * * @typedef {{ - * generate: (seed?: Buffer | string) => Keypair, + * generate: (seed?: Uint8Array | string) => Keypair, * toJSON: (keypair: Keypair, opts?: {indented?: boolean}) => string, - * sign: (keypair: KeypairPrivateSlice, message: Buffer) => Buffer, - * verify: (keypair: KeypairPublicSlice, message: Buffer, sig: Buffer) => boolean, + * sign: (keypair: KeypairPrivateSlice, message: Uint8Array) => Uint8Array, + * verify: (keypair: KeypairPublicSlice, message: Uint8Array, sig: Uint8Array) => boolean, * }} Curve */ @@ -46,7 +46,7 @@ function getCurve(curveName) { * This function generates a keypair for the given curve. The seed is optional. * * @param {CurveName=} curveName - * @param {(Buffer | string)=} seed + * @param {(Uint8Array | string)=} seed * @returns {Keypair} */ function generate(curveName, seed) { diff --git a/lib/index.js b/lib/index.js index 3bf72bf..274c56f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -24,36 +24,36 @@ function isString(x) { /** * @param {any} x - * @returns {x is Buffer} + * @returns {x is Uint8Array} */ -function isBuffer(x) { - return Buffer.isBuffer(x) +function isUint8(x) { + return x instanceof Uint8Array } /** - * @param {Buffer} input - * @param {string | Buffer} key - * @returns {Buffer} + * @param {Uint8Array} input + * @param {string | Uint8Array} key + * @returns {Uint8Array} */ function hmac(input, key) { - if (isString(key)) key = Buffer.from(base58.decode(key)) - const output = Buffer.alloc(sodium.crypto_auth_BYTES) + if (isString(key)) key = base58.decode(key) + const output = new Uint8Array(sodium.crypto_auth_BYTES) sodium.crypto_auth(output, input, key) return output } /** * Takes a keypair object (where `.public` is allowed to be undefined), a - * message as a buffer (and an optional hmacKey) and returns a signature of the - * given message. The signature is string encoded in base58. + * message as a Uint8Array (and an optional hmacKey) and returns a signature of + * the given message. The signature is string encoded in base58. * * @param {KeypairPrivateSlice} keypair - * @param {Buffer} msg - * @param {Buffer | string | undefined} hmacKey + * @param {Uint8Array} msg + * @param {Uint8Array | string | undefined} hmacKey * @returns {string} */ function sign(keypair, msg, hmacKey) { - if (!isBuffer(msg)) throw new Error('Signable message should be buffer') + if (!isUint8(msg)) throw new Error('Signable message should be Uint8Array') const curve = getCurve(keypair.curve) if (hmacKey) msg = hmac(msg, hmacKey) @@ -63,18 +63,18 @@ function sign(keypair, msg, hmacKey) { /** * Takes a keypair object (where `private` is allowed to be undefined), a - * message buffer and its signature string (and an optional hmacKey), and + * message Uint8Array and its signature string (and an optional hmacKey), and * returns true if the signature is valid for the message, false otherwise. * * @param {KeypairPublicSlice} keypair - * @param {Buffer} msg + * @param {Uint8Array} msg * @param {string} sig - * @param {Buffer | string | undefined} hmacKey + * @param {Uint8Array | string | undefined} hmacKey * @returns {boolean} */ function verify(keypair, msg, sig, hmacKey) { if (!isString(sig)) throw new Error('sig should be string') - if (!isBuffer(msg)) throw new Error('Signed message should be buffer') + if (!isUint8(msg)) throw new Error('Signed message should be Uint8Array') const curve = getCurve(keypair.curve) if (hmacKey) msg = hmac(msg, hmacKey) diff --git a/test/generate.test.js b/test/generate.test.js index 88e442a..6a9453d 100644 --- a/test/generate.test.js +++ b/test/generate.test.js @@ -7,8 +7,8 @@ test('generate() default', (t) => { assert.equal(keypair.curve, 'ed25519') assert.equal(typeof keypair.public, 'string') assert.equal(typeof keypair.private, 'string') - assert.equal(Buffer.isBuffer(keypair._public), true) - assert.equal(Buffer.isBuffer(keypair._private), true) + assert.equal(keypair._public instanceof Uint8Array, true) + assert.equal(keypair._private instanceof Uint8Array, true) assert.deepEqual(Object.keys(keypair), [ 'curve', 'public', diff --git a/test/signing.test.js b/test/signing.test.js index 43f1664..8b8a3da 100644 --- a/test/signing.test.js +++ b/test/signing.test.js @@ -1,5 +1,6 @@ const test = require('node:test') const assert = require('node:assert') +const TextEncoder = require('node:util').TextEncoder const Keypair = require('../lib/index') const crypto = require('crypto') @@ -11,23 +12,23 @@ test('sign()/verify() does not work on strings', (t) => { }) }) -test('sign()/verify() a buffer without hmac key', (t) => { - const buf = Buffer.from('ppppp') +test('sign()/verify() a uint8arr without hmac key', (t) => { + const bytes = (new TextEncoder()).encode('ppppp') const keypair = Keypair.generate() - const sig = Keypair.sign(keypair, buf) + const sig = Keypair.sign(keypair, bytes) assert.ok(sig) const { public, curve } = keypair - assert.ok(Keypair.verify({ public, curve }, buf, sig)) + assert.ok(Keypair.verify({ public, curve }, bytes, sig)) }) -test('sign()/verify a buffer with hmac key', (t) => { - const str = Buffer.from('ppppp') +test('sign()/verify a uint8arr with hmac key', (t) => { + const bytes = (new TextEncoder()).encode('ppppp') const keypair = Keypair.generate() const hmac_key = crypto.randomBytes(32) const hmac_key2 = crypto.randomBytes(32) - const sig = Keypair.sign(keypair, str, hmac_key) + const sig = Keypair.sign(keypair, bytes, hmac_key) assert.ok(sig) - assert.equal(Keypair.verify(keypair, str, sig, hmac_key), true) - assert.equal(Keypair.verify(keypair, str, sig, hmac_key2), false) + assert.equal(Keypair.verify(keypair, bytes, sig, hmac_key), true) + assert.equal(Keypair.verify(keypair, bytes, sig, hmac_key2), false) })