mirror of https://codeberg.org/pzp/pzp-keypair.git
105 lines
2.8 KiB
JavaScript
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
|