UNPKG

chattervox

Version:

An AX.25 packet radio chat protocol with support for digital signatures and binary compression. Like IRC over radio waves 📡〰.

174 lines • 5.88 kB
"use strict"; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); const fs = __importStar(require("fs")); const crypto_1 = require("crypto"); const elliptic_1 = require("elliptic"); const ec = new elliptic_1.ec('p192'); /** * @class Keystore * A class for managing keys, signatures, and signature verification */ class Keystore { /**@constructor * @param {string} path The path to a JSON keystore file. If path does not exist it is created. */ constructor(path) { this.path = path; this._keystore = {}; if (!this._exists()) { this._save(); } else { this._keystore = this._load(); } } /** * @method addPublicKey * @param {string} callsign * @param {string} pubkeyHex */ addPublicKey(callsign, pubkeyHex) { this._addKey(callsign, pubkeyHex, undefined); } // only used for you /** * @param {string} callsign * @returns {object} keypair object */ genKeyPair(callsign) { const key = ec.genKeyPair(); const pub = key.getPublic('hex'); const priv = key.getPrivate('hex'); this._addKey(callsign, pub, priv); return { public: pub, private: priv, curve: 'p192' }; } /** * Remove a Key object from the keystore, using the public key. * @param {string} callsign * @param {string} pubkeyHex * @returns {boolean} True if a key was removed from the keystore. */ revoke(callsign, pubkeyHex) { // callsign not in keystore, no key is revoked if (!this._keystore.hasOwnProperty(callsign)) return false; // number of keys before the filter const length = this._keystore[callsign].length; this._keystore[callsign] = this._keystore[callsign].filter((key) => { return key.public !== pubkeyHex; }); if (this._keystore[callsign].length !== length) { this._save(); return true; } return false; } /** Get a list of callsigns. * @method getCallsigns * @returns {string[]} */ getCallsigns() { return Object.keys(this._keystore); } /** * @method getPublicKeys * @param {string} callsign * @returns {string[]} An array of public keys associated with a call sign. */ getPublicKeys(callsign) { if (!this._keystore.hasOwnProperty(callsign)) return []; return this._keystore[callsign].map(key => key.public); } /** * Get Key objects that have both public and private keys * @method getKeyPairs * @param {string} callsign * @returns {Key[]} An array of keypair objects that have both { public: 'hex', private: 'hex' } */ getKeyPairs(callsign) { return this.getKeys(callsign).filter(key => { return typeof key.public === 'string' && typeof key.private === 'string'; }); } /** Get Key objects that have at least a public key * @method getKeys * @param {string} callsign * @returns {Key[]} An array of key objects that have at least a public key */ getKeys(callsign) { if (!this._keystore.hasOwnProperty(callsign)) return []; return this._keystore[callsign]; } /**@method sign * @param {string} message A string containing a message to sign. * @param {string} privateHex A private key as a string of hex characters. * @returns {Buffer} A message signature. */ sign(message, privateHex) { const key = ec.keyFromPrivate(privateHex); const hash = this.sha256(message); const signature = key.sign(hash).toDER(); return Buffer.from(signature); } /**@method verify * @param {string} callsign * @param {string} message * @param {Buffer} signature * @returns {boolean} True if the message signature is valid */ verify(callsign, message, signature) { const hash = this.sha256(message); for (let publicKey of this.getPublicKeys(callsign)) { const key = ec.keyFromPublic(publicKey, 'hex'); if (key.verify(hash, signature)) return true; } return false; } /** * @method sha256 * @param {string} message * @return {Buffer} The SHA 256 message digest */ sha256(message) { return crypto_1.createHash('sha256') .update(message, 'utf8') .digest(); } _addKey(callsign, publicHex, privateHex) { if (typeof callsign !== 'string') throw TypeError('callsign must be a string type'); if (typeof publicHex !== 'string') throw TypeError('publicHex must be a string type'); if (typeof privateHex !== 'undefined' && typeof privateHex !== 'string') throw TypeError('if privateHex is not undefined it must be a string type'); if (!this._keystore.hasOwnProperty(callsign)) { this._keystore[callsign] = []; } const key = { public: publicHex, curve: 'p192' }; if (privateHex) key.private = privateHex; this._keystore[callsign].push(key); this._save(); } _save() { fs.writeFileSync(this.path, JSON.stringify(this._keystore, null, '\t')); } _load() { return JSON.parse(fs.readFileSync(this.path).toString('utf8')); } _exists() { return fs.existsSync(this.path); } } exports.Keystore = Keystore; //# sourceMappingURL=Keystore.js.map