diff --git a/src/js/ripple/crypt.js b/src/js/ripple/crypt.js deleted file mode 100644 index e17c6f84..00000000 --- a/src/js/ripple/crypt.js +++ /dev/null @@ -1,338 +0,0 @@ -/* eslint-disable new-cap */ -'use strict'; -var sjcl = require('./utils').sjcl; -var base = require('./base').Base; -var Seed = require('./seed').Seed; -var UInt160 = require('./uint160').UInt160; -var UInt256 = require('./uint256').UInt256; -var request = require('superagent'); -var extend = require('extend'); -var Crypt = { }; - -var cryptConfig = { - cipher: 'aes', - mode: 'ccm', - ts: 128, // tag length - ks: 256, // key size - iter: 1000 // iterations (key derivation) -}; - -/** - * Full domain hash based on SHA512 - */ - -function fdh(data, bytelen) { - var bitlen = bytelen << 3; - - if (typeof data === 'string') { - data = sjcl.codec.utf8String.toBits(data); - } - - // Add hashing rounds until we exceed desired length in bits - var counter = 0, output = []; - - while (sjcl.bitArray.bitLength(output) < bitlen) { - var hash = sjcl.hash.sha512.hash(sjcl.bitArray.concat([counter], data)); - output = sjcl.bitArray.concat(output, hash); - counter++; - } - - // Truncate to desired length - output = sjcl.bitArray.clamp(output, bitlen); - - return output; -} - -/** - * This is a function to derive different hashes from the same key. - * Each hash is derived as HMAC-SHA512HALF(key, token). - * - * @param {string} key - * @param {string} hash - */ - -function keyHash(key, token) { - var hmac = new sjcl.misc.hmac(key, sjcl.hash.sha512); - return sjcl.codec.hex.fromBits( - sjcl.bitArray.bitSlice(hmac.encrypt(token), 0, 256)); -} - -/** - * add entropy at each call to get random words - * @param {number} nWords - number of words to generate - * @return {array} random words (4 byte unsigned integers) - */ -function randomWords(nWords) { - var PARANOIA_256_BITS = 6; // sjcl constant for ensuring 256 bits of entropy - return sjcl.random.randomWords(nWords, PARANOIA_256_BITS); -} - -/****** exposed functions ******/ - -/** - * KEY DERIVATION FUNCTION - * - * This service takes care of the key derivation, i.e. converting low-entropy - * secret into higher entropy secret via either computationally expensive - * processes or peer-assisted key derivation (PAKDF). - * - * @param {object} opts - * @param {string} purpose - Key type/purpose - * @param {string} username - * @param {string} secret - Also known as passphrase/password - * @param {function} fn - */ - -Crypt.derive = function(opts, purpose, username, secret, fn) { - var tokens; - - if (purpose === 'login') { - tokens = ['id', 'crypt']; - } else { - tokens = ['unlock']; - } - - var iExponent = new sjcl.bn(String(opts.exponent)); - var iModulus = new sjcl.bn(String(opts.modulus)); - var iAlpha = new sjcl.bn(String(opts.alpha)); - - var publicInfo = ['PAKDF_1_0_0', opts.host.length, opts.host, - username.length, username, purpose.length, purpose].join(':') + ':'; - var publicSize = Math.ceil(Math.min( - (7 + iModulus.bitLength()) >>> 3, 256) / 8); - var publicHash = fdh(publicInfo, publicSize); - var publicHex = sjcl.codec.hex.fromBits(publicHash); - var iPublic = new sjcl.bn(String(publicHex)).setBitM(0); - var secretInfo = [publicInfo, secret.length, secret].join(':') + ':'; - var secretSize = (7 + iModulus.bitLength()) >>> 3; - var secretHash = fdh(secretInfo, secretSize); - var secretHex = sjcl.codec.hex.fromBits(secretHash); - var iSecret = new sjcl.bn(String(secretHex)).mod(iModulus); - - if (iSecret.jacobi(iModulus) !== 1) { - iSecret = iSecret.mul(iAlpha).mod(iModulus); - } - - var iRandom; - var PARANOIA_256_BITS = 6; // sjcl constant for ensuring 256 bits of entropy - - for (;;) { - iRandom = sjcl.bn.random(iModulus, PARANOIA_256_BITS); - if (iRandom.jacobi(iModulus) === 1) { - break; - } - } - - var iBlind = iRandom.powermodMontgomery(iPublic.mul(iExponent), iModulus); - var iSignreq = iSecret.mulmod(iBlind, iModulus); - var signreq = sjcl.codec.hex.fromBits(iSignreq.toBits()); - - request.post(opts.url) - .send({info: publicInfo, signreq: signreq}) - .end(function(err, resp) { - if (err || !resp) { - return fn(new Error('Could not query PAKDF server ' + opts.host)); - } - - var data = resp.body || resp.text ? JSON.parse(resp.text) : {}; - - if (data.result !== 'success') { - return fn(new Error('Could not query PAKDF server ' + opts.host)); - } - - var iSignres = new sjcl.bn(String(data.signres)); - var iRandomInv = iRandom.inverseMod(iModulus); - var iSigned = iSignres.mulmod(iRandomInv, iModulus); - var key = iSigned.toBits(); - var result = { }; - - tokens.forEach(function(token) { - result[token] = keyHash(key, token); - }); - - fn(null, result); - }); -}; - -/** - * Imported from ripple-client - */ - -/** - * Encrypt data - * - * @param {string} key - * @param {string} data - */ - -Crypt.encrypt = function(key, data) { - key = sjcl.codec.hex.toBits(key); - - var opts = extend(true, {}, cryptConfig); - - var encryptedObj = JSON.parse(sjcl.encrypt(key, data, opts)); - var version = [sjcl.bitArray.partial(8, 0)]; - var initVector = sjcl.codec.base64.toBits(encryptedObj.iv); - var ciphertext = sjcl.codec.base64.toBits(encryptedObj.ct); - - var encryptedBits = sjcl.bitArray.concat(version, initVector); - encryptedBits = sjcl.bitArray.concat(encryptedBits, ciphertext); - - return sjcl.codec.base64.fromBits(encryptedBits); -}; - -/** - * Decrypt data - * - * @param {string} key - * @param {string} data - */ - -Crypt.decrypt = function(key, data) { - - key = sjcl.codec.hex.toBits(key); - var encryptedBits = sjcl.codec.base64.toBits(data); - - var version = sjcl.bitArray.extract(encryptedBits, 0, 8); - - if (version !== 0) { - throw new Error('Unsupported encryption version: ' + version); - } - - var encrypted = extend(true, {}, cryptConfig, { - iv: sjcl.codec.base64.fromBits( - sjcl.bitArray.bitSlice(encryptedBits, 8, 8 + 128)), - ct: sjcl.codec.base64.fromBits( - sjcl.bitArray.bitSlice(encryptedBits, 8 + 128)) - }); - - return sjcl.decrypt(key, JSON.stringify(encrypted)); -}; - - -/** - * Validate a ripple address - * - * @param {string} address - */ - -Crypt.isValidAddress = function(address) { - return UInt160.is_valid(address); -}; - -/** - * Create an encryption key - * - * @param {integer} nWords - number of words - */ - -Crypt.createSecret = function(nWords) { - return sjcl.codec.hex.fromBits(randomWords(nWords)); -}; - -/** - * Create a new master key - */ - -Crypt.createMaster = function() { - return base.encode_check(33, sjcl.codec.bytes.fromBits(randomWords(4))); -}; - - -/** - * Create a ripple address from a master key - * - * @param {string} masterkey - */ - -Crypt.getAddress = function(masterkey) { - return Seed.from_json(masterkey).get_key().get_address().to_json(); -}; - -/** - * Hash data using SHA-512. - * - * @param {string|bitArray} data - * @return {string} Hash of the data - */ - -Crypt.hashSha512 = function(data) { - // XXX Should return a UInt512 - return sjcl.codec.hex.fromBits(sjcl.hash.sha512.hash(data)); -}; - -/** - * Hash data using SHA-512 and return the first 256 bits. - * - * @param {string|bitArray} data - * @return {UInt256} Hash of the data - */ - -Crypt.hashSha512Half = function(data) { - return UInt256.from_hex(Crypt.hashSha512(data).substr(0, 64)); -}; - - -/** - * Sign a data string with a secret key - * - * @param {string} secret - * @param {string} data - */ - -Crypt.signString = function(secret, data) { - var hmac = new sjcl.misc.hmac( - sjcl.codec.hex.toBits(secret), sjcl.hash.sha512); - return sjcl.codec.hex.fromBits(hmac.mac(data)); -}; - -/** - * Create an an accout recovery key - * - * @param {string} secret - */ - -Crypt.deriveRecoveryEncryptionKeyFromSecret = function(secret) { - var seed = Seed.from_json(secret).to_bits(); - var hmac = new sjcl.misc.hmac(seed, sjcl.hash.sha512); - var key = sjcl.bitArray.bitSlice( - hmac.mac('ripple/hmac/recovery_encryption_key/v1'), 0, 256); - return sjcl.codec.hex.fromBits(key); -}; - -/** - * Convert base64 encoded data into base64url encoded data. - * - * @param {String} base64 Data - */ - -Crypt.base64ToBase64Url = function(encodedData) { - return encodedData.replace(/\+/g, '-') - .replace(/\//g, '_').replace(/[=]+$/, ''); -}; - -/** - * Convert base64url encoded data into base64 encoded data. - * - * @param {String} base64 Data - */ - -Crypt.base64UrlToBase64 = function(encodedData) { - encodedData = encodedData.replace(/-/g, '+').replace(/_/g, '/'); - - while (encodedData.length % 4) { - encodedData += '='; - } - - return encodedData; -}; - -/** - * base64 to UTF8 - */ - -Crypt.decodeBase64 = function(data) { - return sjcl.codec.utf8String.fromBits(sjcl.codec.base64.toBits(data)); -}; - -exports.Crypt = Crypt; diff --git a/src/js/ripple/ledger.js b/src/js/ripple/ledger.js index 08a21ce8..7831558b 100644 --- a/src/js/ripple/ledger.js +++ b/src/js/ripple/ledger.js @@ -9,7 +9,6 @@ var UInt160 = require('./uint160').UInt160; var Currency = require('./currency').Currency; var stypes = require('./serializedtypes'); var sjcl = require('./utils').sjcl; -var Crypt = require('./crypt').Crypt; function Ledger() { diff --git a/src/js/ripple/serializedobject.js b/src/js/ripple/serializedobject.js index e8b45494..8766f27f 100644 --- a/src/js/ripple/serializedobject.js +++ b/src/js/ripple/serializedobject.js @@ -5,8 +5,8 @@ var assert = require('assert'); var extend = require('extend'); var binformat = require('./binformat'); var stypes = require('./serializedtypes'); -var Crypt = require('./crypt').Crypt; var utils = require('./utils'); +var UInt256 = require('./uint256').UInt256; var sjcl = utils.sjcl; @@ -280,9 +280,9 @@ SerializedObject.prototype.hash = function(prefix) { // Copy buffer to temporary buffer sign_buffer.append(this.buffer); - // XXX We need a proper Buffer class then Crypt could accept that var bits = sjcl.codec.bytes.toBits(sign_buffer.buffer); - return Crypt.hashSha512Half(bits); + var sha512hex = sjcl.codec.hex.fromBits(sjcl.hash.sha512.hash(bits)); + return UInt256.from_hex(sha512hex.substr(0, 64)); }; // DEPRECATED