UNPKG

@scintilla-network/signatures

Version:

Enhanced signature and key exchange functions for blockchain and crypto applications, including post-quantum algorithms

125 lines (118 loc) 4.42 kB
/** * @typedef {import('../types.js').Bytes} Bytes * @typedef {import('../types.js').PublicKey} PublicKey * @typedef {import('../types.js').PrivateKey} PrivateKey * @typedef {import('../types.js').Signature} Signature * @typedef {import('../types.js').Signing} Signing */ import { slh_dsa_sha2_256f as sphincs256f, slh_dsa_sha2_256s as sphincs256s } from '@noble/post-quantum/slh-dsa'; import { isUint8Array } from '../utils/types.js'; import { formatMessage } from '../utils/format.js'; /** * Create a SPHINCS+ instance with given variant * @param {{ * keygen: (seed: Uint8Array) => { publicKey: Uint8Array, privateKey: Uint8Array }, * sign: (privateKey: Uint8Array, message: Uint8Array) => Uint8Array, * verify: (publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array) => boolean * }} variant - SPHINCS+ variant to use * @returns {Signing} SPHINCS+ signing interface */ const createSphincs = (variant) => ({ /** * Generate a new private key * @param {Bytes} [seed] - Optional 32-byte seed for deterministic key generation * @returns {PrivateKey} Private key * @throws {Error} If seed is invalid */ generatePrivateKey(seed) { if (seed !== undefined) { if (!isUint8Array(seed)) { throw new Error('seed must be a Uint8Array'); } if (seed.length !== 96) { throw new Error('seed must be 96 bytes'); } return seed; } return crypto.getRandomValues(new Uint8Array(96)); }, /** * Generate a new key pair * @param {Bytes} [seed] - Optional 32-byte seed for deterministic key generation * @returns {{ publicKey: PublicKey; privateKey: PrivateKey }} Generated key pair * @throws {Error} If seed is invalid */ generateKeyPair(seed) { const genSeed = this.generatePrivateKey(seed); const { publicKey, secretKey: privateKey } = variant.keygen(genSeed); return { publicKey, privateKey }; }, /** * Derive public key from private key * @param {PrivateKey} privateKey - Private key * @returns {PublicKey} Public key * @throws {Error} If private key is invalid */ getPublicKey(privateKey) { if (!isUint8Array(privateKey)) { throw new Error('privateKey must be a Uint8Array'); } const { publicKey } = variant.keygen(privateKey); return publicKey; }, /** * Sign a message * @param {Bytes} message - Message to sign * @param {PrivateKey} privateKey - Private key * @returns {Signature} Signature * @throws {Error} If inputs are invalid */ sign(message, privateKey) { if (!isUint8Array(message)) { throw new Error('message must be a Uint8Array, use utils.formatMessage() for automatic conversion'); } if (!isUint8Array(privateKey)) { throw new Error('privateKey must be a Uint8Array'); } return variant.sign(privateKey, formatMessage(message)); }, /** * Verify a signature * @param {Signature} signature - Signature to verify * @param {Bytes} message - Original message * @param {PublicKey} publicKey - Public key * @returns {boolean} True if signature is valid * @throws {Error} If inputs are invalid */ verify(signature, message, publicKey) { if (!isUint8Array(message)) { throw new Error('message must be a Uint8Array, use utils.formatMessage() for automatic conversion'); } if (!isUint8Array(signature)) { throw new Error('signature must be a Uint8Array'); } if (!isUint8Array(publicKey)) { throw new Error('publicKey must be a Uint8Array'); } return variant.verify(publicKey, formatMessage(message), signature); } }); /** * SPHINCS+-256 signatures (SLH-DSA-SHA2-256) * Implements the {@link Signing} interface * Security level: NIST Level 5 (equivalent to AES-256) * @namespace sphincs256 */ export const sphincs256 = { ...createSphincs(sphincs256f), /** * Fast variant - larger signatures but faster operations * Signature size: ~30KB */ fast: createSphincs(sphincs256f), /** * Small variant - smaller signatures but slower operations * Signature size: ~8KB */ small: createSphincs(sphincs256s) };