UNPKG

golos-lib-js

Version:

Golos-js the JavaScript library with API for GOLOS blockchain

146 lines (135 loc) 7.11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.decrypt = decrypt; exports.encrypt = encrypt; exports.uniqueNonce = uniqueNonce; var _secureRandom = _interopRequireDefault(require("secure-random")); var _bytebuffer = _interopRequireDefault(require("bytebuffer")); var _browserifyAes = _interopRequireDefault(require("browserify-aes")); var _assert = _interopRequireDefault(require("assert")); var _key_public = _interopRequireDefault(require("./key_public")); var _key_private = _interopRequireDefault(require("./key_private")); var _hash = _interopRequireDefault(require("./hash")); var _core = require("../../../core"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const Long = _bytebuffer.default.Long; /** Spec: http://localhost:3002/steem/@dantheman/how-to-encrypt-a-memo-when-transferring-steem @throws {Error|TypeError} - "Invalid Key, ..." @arg {PrivateKey|Uint8Array} private_key - required and used for decryption. If you have many messages to decrypt, it is faster to pass prepared shared_secret, instead of private_key @arg {PublicKey} public_key - required (if private_key_or_shared_secret is not a shared secret) and used to calculate the shared secret @arg {string} [nonce = uniqueNonce()] - assigned a random unique uint64 @return {object} @property {string} nonce - random or unique uint64, provides entropy when re-using the same private/public keys. @property {Buffer} message - Plain text message @property {number} checksum - shared secret checksum */ function encrypt(private_key_or_shared_secret, public_key, message) { let nonce = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : uniqueNonce(); return crypt(private_key_or_shared_secret, public_key, nonce, message); } /** Spec: http://localhost:3002/steem/@dantheman/how-to-encrypt-a-memo-when-transferring-steem @arg {PrivateKey|Uint8Array} private_key_or_shared_secret - required and used for decryption. If you have many messages to decrypt, it is faster to pass prepared shared_secret, instead of private_key @arg {PublicKey} public_key - required (if private_key_or_shared_secret is not a shared secret) and used to calculate the shared secret @arg {string} nonce - random or unique uint64, provides entropy when re-using the same private/public keys. @arg {Buffer} message - Encrypted or plain text message @arg {number} checksum - shared secret checksum @throws {Error|TypeError} - "Invalid Key, ..." @return {Buffer} - message */ function decrypt(private_key_or_shared_secret, public_key, nonce, message, checksum) { return crypt(private_key_or_shared_secret, public_key, nonce, message, checksum).message; } /** @arg {Buffer} message - Encrypted or plain text message (see checksum) @arg {number} checksum - shared secret checksum (null to encrypt, non-null to decrypt) */ function crypt(private_key_or_shared_secret, public_key, nonce, message, checksum) { let shared_secret; if (!private_key_or_shared_secret.d && typeof private_key_or_shared_secret !== 'string') { shared_secret = private_key_or_shared_secret; } else { const private_key = toPrivateObj(private_key_or_shared_secret); if (!private_key) throw new TypeError('private_key is required'); public_key = toPublicObj(public_key); if (!public_key) throw new TypeError('public_key is required'); shared_secret = private_key.get_shared_secret(public_key); } nonce = toLongObj(nonce); if (!nonce) throw new TypeError('nonce is required'); if (!Buffer.isBuffer(message)) { if (typeof message !== 'string') throw new TypeError('message should be buffer or string'); message = new Buffer(message, 'binary'); } if (checksum && typeof checksum !== 'number') throw new TypeError('checksum should be a number'); let ebuf = new _bytebuffer.default(_bytebuffer.default.DEFAULT_CAPACITY, _bytebuffer.default.LITTLE_ENDIAN); ebuf.writeUint64(nonce); ebuf.append(shared_secret.toString('binary'), 'binary'); ebuf = new Buffer(ebuf.copy(0, ebuf.offset).toBinary(), 'binary'); const encryption_key = _hash.default.sha512(ebuf); // D E B U G // console.log('crypt', { // priv_to_pub: private_key.toPublicKey().toString(), // pub: public_key.toString(), // nonce: nonce.toString(), // message: message.length, // checksum, // shared_secret: shared_secret.toString('hex'), // encryption_key: encryption_key.toString('hex'), // }) const iv = encryption_key.slice(32, 48); const key = encryption_key.slice(0, 32); // check is first 64 bit of sha256 hash treated as uint64_t truncated to 32 bits. let check = _hash.default.sha256(encryption_key); check = check.slice(0, 4); const cbuf = _bytebuffer.default.fromBinary(check.toString('binary'), _bytebuffer.default.DEFAULT_CAPACITY, _bytebuffer.default.LITTLE_ENDIAN); check = cbuf.readUint32(); if (checksum) { (0, _core.assertNativeLib)('Aes.decrypt()'); if (check !== checksum) throw new Error('Invalid key'); message = Buffer.from((0, _core.aes_decrypt)(key, iv, message)); } else { message = cryptoJsEncrypt(message, key, iv); } return { nonce, message, checksum: check }; } /** This method does not use a checksum, the returned data must be validated some other way. @arg {string|Buffer} plaintext - binary format @return {Buffer} binary */ function cryptoJsEncrypt(message, key, iv) { (0, _assert.default)(message, "Missing plain text"); message = toBinaryBuffer(message); const cipher = _browserifyAes.default.createCipheriv('aes-256-cbc', key, iv); // cipher.setAutoPadding(true) message = Buffer.concat([cipher.update(message), cipher.final()]); return message; } /** @return {string} unique 64 bit unsigned number string. Being time based, this is careful to never choose the same nonce twice. This value could be recorded in the blockchain for a long time. */ function uniqueNonce() { if (unique_nonce_entropy === null) { const b = _secureRandom.default.randomUint8Array(2); unique_nonce_entropy = parseInt(b[0] << 8 | b[1], 10); } let long = Long.fromNumber(Date.now()); const entropy = ++unique_nonce_entropy % 0xFFFF; // console.log('uniqueNonce date\t', ByteBuffer.allocate(8).writeUint64(long).toHex(0)) // console.log('uniqueNonce entropy\t', ByteBuffer.allocate(8).writeUint64(Long.fromNumber(entropy)).toHex(0)) long = long.shiftLeft(16).or(Long.fromNumber(entropy)); // console.log('uniqueNonce final\t', ByteBuffer.allocate(8).writeUint64(long).toHex(0)) return long.toString(); } let unique_nonce_entropy = null; // for(let i=1; i < 10; i++) key.uniqueNonce() const toPrivateObj = o => o ? o.d ? o : _key_private.default.fromWif(o) : o /*null or undefined*/; const toPublicObj = o => o ? o.Q ? o : _key_public.default.fromString(o) : o /*null or undefined*/; const toLongObj = o => o ? Long.isLong(o) ? o : Long.fromString(o) : o; const toBinaryBuffer = o => o ? Buffer.isBuffer(o) ? o : new Buffer(o, 'binary') : o;