UNPKG

hive-js-dev

Version:

Steem.js the JavaScript API for Steem blockchain

184 lines (150 loc) 7.31 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.encrypt = encrypt; exports.decrypt = decrypt; var _secureRandom = require('secure-random'); var _secureRandom2 = _interopRequireDefault(_secureRandom); var _bytebuffer = require('bytebuffer'); var _bytebuffer2 = _interopRequireDefault(_bytebuffer); var _browserifyAes = require('browserify-aes'); var _browserifyAes2 = _interopRequireDefault(_browserifyAes); var _assert = require('assert'); var _assert2 = _interopRequireDefault(_assert); var _key_public = require('./key_public'); var _key_public2 = _interopRequireDefault(_key_public); var _key_private = require('./key_private'); var _key_private2 = _interopRequireDefault(_key_private); var _hash = require('./hash'); var _hash2 = _interopRequireDefault(_hash); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var Long = _bytebuffer2.default.Long; /** Spec: http://localhost:3002/steem/@dantheman/how-to-encrypt-a-memo-when-transferring-steem @throws {Error|TypeError} - "Invalid Key, ..." @arg {PrivateKey} private_key - required and used for decryption @arg {PublicKey} public_key - required and used to calcualte 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, public_key, message) { var nonce = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : uniqueNonce(); return crypt(private_key, public_key, nonce, message); } /** Spec: http://localhost:3002/steem/@dantheman/how-to-encrypt-a-memo-when-transferring-steem @arg {PrivateKey} private_key - required and used for decryption @arg {PublicKey} public_key - required and used to calcualte 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, public_key, nonce, message, checksum) { return crypt(private_key, 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, public_key, nonce, message, checksum) { private_key = toPrivateObj(private_key); 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'); 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'); var S = private_key.get_shared_secret(public_key); var ebuf = new _bytebuffer2.default(_bytebuffer2.default.DEFAULT_CAPACITY, _bytebuffer2.default.LITTLE_ENDIAN); ebuf.writeUint64(nonce); ebuf.append(S.toString('binary'), 'binary'); ebuf = new Buffer(ebuf.copy(0, ebuf.offset).toBinary(), 'binary'); var encryption_key = _hash2.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, // S: S.toString('hex'), // encryption_key: encryption_key.toString('hex'), // }) var iv = encryption_key.slice(32, 48); var key = encryption_key.slice(0, 32); // check is first 64 bit of sha256 hash treated as uint64_t truncated to 32 bits. var check = _hash2.default.sha256(encryption_key); check = check.slice(0, 4); var cbuf = _bytebuffer2.default.fromBinary(check.toString('binary'), _bytebuffer2.default.DEFAULT_CAPACITY, _bytebuffer2.default.LITTLE_ENDIAN); check = cbuf.readUint32(); if (checksum) { if (check !== checksum) throw new Error('Invalid key'); message = cryptoJsDecrypt(message, key, iv); } else { message = cryptoJsEncrypt(message, key, iv); } return { nonce: nonce, message: message, checksum: check }; } /** This method does not use a checksum, the returned data must be validated some other way. @arg {string|Buffer} ciphertext - binary format @return {Buffer} */ function cryptoJsDecrypt(message, key, iv) { (0, _assert2.default)(message, "Missing cipher text"); message = toBinaryBuffer(message); var decipher = _browserifyAes2.default.createDecipheriv('aes-256-cbc', key, iv); // decipher.setAutoPadding(true) message = Buffer.concat([decipher.update(message), decipher.final()]); return message; } /** 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, _assert2.default)(message, "Missing plain text"); message = toBinaryBuffer(message); var cipher = _browserifyAes2.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) { var b = _secureRandom2.default.randomUint8Array(2); unique_nonce_entropy = parseInt(b[0] << 8 | b[1], 10); } var long = Long.fromNumber(Date.now()); var 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(); } var unique_nonce_entropy = null; // for(let i=1; i < 10; i++) key.uniqueNonce() var toPrivateObj = function toPrivateObj(o) { return o ? o.d ? o : _key_private2.default.fromWif(o) : o /*null or undefined*/; }; var toPublicObj = function toPublicObj(o) { return o ? o.Q ? o : _key_public2.default.fromString(o) : o /*null or undefined*/; }; var toLongObj = function toLongObj(o) { return o ? Long.isLong(o) ? o : Long.fromString(o) : o; }; var toBinaryBuffer = function toBinaryBuffer(o) { return o ? Buffer.isBuffer(o) ? o : new Buffer(o, 'binary') : o; };