UNPKG

factom-identity-lib

Version:

Library to read and update Factom identities

148 lines (131 loc) 4.31 kB
const base58 = require('base-58'), nacl = require('tweetnacl/nacl-fast'), sign = nacl.sign, { sha256d } = require('../crypto'); const ID_PUB_PREFIX = Buffer.from('0345ef9de0', 'hex'), ID_SEC_PREFIX = Buffer.from('0345f3d0d6', 'hex'); const VALID_ID_PREFIXES = new Set(['idpub', 'idsec']); /** * Check if an identity key is well formed. * @memberof app * @param {string} idKey - Public or secret identity key. * @returns {boolean} - True if the identity key is valid. */ function isValidIdentityKey(idKey) { try { if (typeof idKey !== 'string') { return false; } if (!VALID_ID_PREFIXES.has(idKey.slice(0, 5))) { return false; } const bytes = Buffer.from(base58.decode(idKey)); if (bytes.length !== 41) { return false; } const checksum = sha256d(bytes.slice(0, 37)).slice(0, 4); if (checksum.equals(bytes.slice(37, 41))) { return true; } return false; } catch (err) { return false; } } /** * Check if a public identity key is well formed. * @memberof app * @param {string} pubIdKey - Public identity key. * @returns {boolean} - True if the public identity key is valid. */ function isValidPublicIdentityKey(pubIdKey) { return isValidIdentityKey(pubIdKey) && pubIdKey.startsWith('idpub'); } /** * Check if a secret identity key is well formed. * @memberof app * @param {string} secIdKey - Public identity key. * @returns {boolean} - True if the secret identity key is valid. */ function isValidSecretIdentityKey(secIdKey) { return isValidIdentityKey(secIdKey) && secIdKey.startsWith('idsec'); } /** * Extract the ed25519 cryptographic material encapsulated in the identity key. * @memberof app * @param {string} idKey - Public or secret identity key. * @returns {Buffer} - Either the ed25519 32-byte public key or the 32-byte secret seed. */ function extractCryptoMaterial(idKey) { if (!isValidIdentityKey(idKey)) { throw new Error(`Invalid identity key ${idKey}.`); } return Buffer.from(base58.decode(idKey).slice(5, 37)); } /** * Get the public identity key corresponding to the input identity key. * @memberof app * @param {string} idKey - Secret (or public) identity key. * @returns {string} - Corresponding public identity key. */ function getPublicIdentityKey(idKey) { if (isValidPublicIdentityKey(idKey)) { return idKey; } else if (isValidSecretIdentityKey(idKey)) { const secret = extractCryptoMaterial(idKey); const publicKey = sign.keyPair.fromSeed(secret).publicKey; return keyToPublicIdentityKey(publicKey); } else { throw new Error(`Invalid identity key: ${idKey}`); } } /** * Convert a 32-byte key to a public identity key. * @memberof app * @param {string | Buffer} key - 32-byte key. * @returns {string} - Public identity key. */ function keyToPublicIdentityKey(key) { return keyToIdentityKey(key, ID_PUB_PREFIX); } /** * Convert a 32-byte seed to a secret identity key. * @memberof app * @param {string | Buffer} seed - 32-byte seed. * @returns {string} - Secret identity key. */ function seedToSecretIdentityKey(seed) { return keyToIdentityKey(seed, ID_SEC_PREFIX); } function keyToIdentityKey(key, prefix) { const keyBuffer = Buffer.from(key, 'hex'); if (keyBuffer.length !== 32) { throw new Error('Key/seed must be 32 bytes long.'); } const address = Buffer.concat([prefix, keyBuffer]); const checksum = sha256d(address).slice(0, 4); return base58.encode(Buffer.concat([address, checksum])); } /** * Generate a random identity key pair. * @memberof app * @returns {{public: string, secret: string}} - Random identity key pair. */ function generateRandomIdentityKeyPair() { const seed = nacl.randomBytes(32); const keyPair = sign.keyPair.fromSeed(seed); return { public: keyToPublicIdentityKey(keyPair.publicKey), secret: seedToSecretIdentityKey(seed) }; } module.exports = { isValidIdentityKey, isValidPublicIdentityKey, isValidSecretIdentityKey, extractCryptoMaterial, getPublicIdentityKey, keyToPublicIdentityKey, seedToSecretIdentityKey, generateRandomIdentityKeyPair };