pzp-keypair/lib/curves/ed25519.js

105 lines
2.8 KiB
JavaScript

// @ts-ignore
const sodium = require('sodium-universal')
const b4a = require('b4a')
const base58 = require('bs58')
/**
* @typedef {import('.').Keypair} Keypair
* @typedef {import('.').KeypairPublicSlice} KeypairPublicSlice
* @typedef {import('.').KeypairPrivateSlice} KeypairPrivateSlice
* @typedef {Buffer | Uint8Array} B4A
*/
const SEEDBYTES = sodium.crypto_sign_SEEDBYTES
const PUBLICKEYBYTES = sodium.crypto_sign_PUBLICKEYBYTES
const SECRETKEYBYTES = sodium.crypto_sign_SECRETKEYBYTES
const ed25519 = {
/**
* @param {(string | B4A)=} seed
* @returns {Keypair}
*/
generate(seed) {
let seedBytes
if (seed) {
if (b4a.isBuffer(seed)) {
// prettier-ignore
if (seed.length !== SEEDBYTES) throw new Error(`seed must be ${SEEDBYTES} bytes`)
seedBytes = seed
} else if (typeof seed === 'string') {
seedBytes = b4a.alloc(SEEDBYTES)
b4a.copy(b4a.from(seed.substring(0, 32), 'utf-8'), seedBytes)
}
}
const publicKeyBytes = b4a.alloc(PUBLICKEYBYTES)
const secretKeyBytes = b4a.alloc(SECRETKEYBYTES)
if (seedBytes) {
sodium.crypto_sign_seed_keypair(publicKeyBytes, secretKeyBytes, seedBytes)
} else {
sodium.crypto_sign_keypair(publicKeyBytes, secretKeyBytes)
}
return {
curve: 'ed25519',
public: base58.encode(publicKeyBytes),
private: base58.encode(secretKeyBytes),
_public: publicKeyBytes,
_private: secretKeyBytes,
}
},
/**
* @param {Keypair} keypair
* @param {{indented?: boolean}=} opts
*/
toJSON(keypair, opts) {
const stringifiable = {
curve: keypair.curve,
public: keypair.public,
private: keypair.private,
}
if (opts?.indented) {
return JSON.stringify(stringifiable, null, 2)
} else {
return JSON.stringify(stringifiable)
}
},
/**
* @param {KeypairPrivateSlice} keypair
* @param {B4A} message
* @returns {string}
*/
sign(keypair, message) {
if (!keypair._private && !keypair.private) {
throw new Error(`invalid ed25519 keypair with missing private key`)
}
keypair._private ??= b4a.from(base58.decode(keypair.private))
const sig = b4a.alloc(sodium.crypto_sign_BYTES)
sodium.crypto_sign_detached(sig, message, keypair._private)
return base58.encode(sig)
},
/**
* @param {KeypairPublicSlice} keypair
* @param {string} sig
* @param {B4A} message
* @returns {boolean}
*/
verify(keypair, sig, message) {
if (!keypair._public && !keypair.public) {
throw new Error(`invalid ed25519 keypair with missing public key`)
}
keypair._public ??= b4a.from(base58.decode(keypair.public))
const sigBytes = b4a.from(base58.decode(sig))
return sodium.crypto_sign_verify_detached(
sigBytes,
message,
keypair._public
)
},
}
module.exports = ed25519