UNPKG

@velas/account-agent

Version:

sdk

188 lines (158 loc) 5.32 kB
import jwt from 'jsonwebtoken'; import jwtDecode from 'jwt-decode'; import jwkToPem from 'jwk-to-pem'; import nacl from 'tweetnacl'; import bs58 from 'bs58'; import ttl from './ttl'; function strToUint8(str) { return new TextEncoder().encode(str); }; function utf8ToBinaryString(str) { const escstr = encodeURIComponent(str); const binstr = escstr.replace(/%([0-9A-F]{2})/g, (match, p1) => { return String.fromCharCode(parseInt(p1, 16)); }); return binstr; }; function binToUrlBase64(bin) { return btoa(bin) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+/g, ''); }; function strToUrlBase64(str) { return binToUrlBase64(utf8ToBinaryString(str)); }; function uint8ToUrlBase64(uint8) { let bin = ''; uint8.forEach((code) => { bin += String.fromCharCode(code); }); return binToUrlBase64(bin); }; const head = { "RSASSA-PKCS1-v1_5": { alg: 'RS256', typ: 'JWT', }, "ECDSA": { alg: 'ES256', typ: 'JWT', }, "ED25519": { alg: 'ED25519', typ: 'JWT', }, }; function pem(str) { return `-----BEGIN PUBLIC KEY-----\n${str}\n-----END PUBLIC KEY-----` }; function jwkToBase64PubKey(jwk) { return jwkToPem(jwk) .replace('-----BEGIN PUBLIC KEY-----\n', '') .replace('\n-----END PUBLIC KEY-----\n', '') .replace( /[\r\n]+/gm, '' ); }; function KeyStorage({ KeyStorageHandler, issuer }) { this.handler = new KeyStorageHandler({ jwkToBase64PubKey, }); this.issuer = issuer; }; KeyStorage.prototype.signWithKey = function(id, data) { return this.handler.signWithKey(id, data) }; KeyStorage.prototype.parametersToJWT = async function(base64PubKey, body = {}) { try { const algorithm = await this.handler.getAlgorithm(base64PubKey); const jwtHead = strToUrlBase64(JSON.stringify(head[algorithm])); const jwtBody = strToUrlBase64(JSON.stringify(body)); const signature = await this.handler.signWithKey(base64PubKey, strToUint8(`${jwtHead}.${jwtBody}`)); const jwtSignature = uint8ToUrlBase64(new Uint8Array(signature)) return `${jwtHead}.${jwtBody}.${jwtSignature}`; } catch (e) { throw new Error('jwt token generation error'); }; }; KeyStorage.prototype.validateJWT = async function({access_token}) { try { const { alg } = jwtDecode(access_token, {header: true}); const { iss } = jwtDecode(access_token); if (!alg || !iss) throw new Error('access_token payload is not valid'); return jwt.verify(access_token, pem(iss)); } catch(e) { throw new Error("Invalid access_token"); }; }; KeyStorage.prototype.updateKeyWithAccount = function(id, account) { return this.handler.updateKey(id, { expires: new Date().getTime() + ttl.interaction, account, }); }; KeyStorage.prototype.destroy = function(id) { return this.handler.destroy(id); }; KeyStorage.prototype.extract = function(id) { return this.handler.extract(id); }; KeyStorage.prototype.encryptWithExchangeKey = async function(JWKPubKey, data) { const string = JSON.stringify(data); const buffer = Buffer.from(string, "utf-8"); const encrypted = await this.handler.encryptWithAsymmetricEncryptionKey(JWKPubKey, buffer); return bs58.encode(Buffer.from(encrypted)); }; KeyStorage.prototype.decryptWithExchangeKey = async function(id, data) { const buffer = bs58.decode(data) const decrypted = await this.handler.decryptWithAsymmetricEncryptionKey(id, buffer); const bytes = new Buffer.from(decrypted); const string = bytes.toString('utf8'); return JSON.parse(string); }; KeyStorage.prototype.uploadOperationalKey = function({ keys, account, } = {}) { const pair = keys || nacl.sign.keyPair(); return this.handler.uploadKey({ id: bs58.encode(pair.publicKey), type: 'symmetric-encryption-key', issuer: this.issuer, expires: new Date().getTime() + ttl.interaction, encryptSecret: pair.secretKey, account, }); }; KeyStorage.prototype.uploadAgentKey = async function() { const keys = await this.handler.loadAllKeys(); if (!keys.agent) { return this.handler.uploadKey({ id: 'agent', type: 'jwt-signing-key', issuer: this.issuer, expires: new Date().getTime() + new Date().getTime(), }); }; return keys.agent.pubKey; }; KeyStorage.prototype.uploadExchangeKey = function() { return this.handler.uploadKey({ id: 'exchange', type: 'asymmetric-encryption-key', issuer: this.issuer, expires: new Date().getTime() + ttl.interaction, }); }; KeyStorage.prototype.removeExpiredItems = async function() { let keys = await this.handler.loadAllKeys(); for (var i in keys) { const key = keys[i]; if (key.issuer && key.issuer === this.issuer) { if (!key.account && key.expires && key.expires < new Date().getTime()) { await this.handler.destroy(i); }; }; }; return await this.handler.loadAllKeys() || {}; }; export default KeyStorage;