crypto-conditions
Version:
Implementation of crypto-conditions in JavaScript
173 lines (125 loc) • 7.48 kB
JavaScript
"use strict";
var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
_Object$defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _fill = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/fill"));
var _concat = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/concat"));
var _slice = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/slice"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/createClass"));
var _crypto = require("crypto");
var _mgf = _interopRequireDefault(require("./mgf1"));
var _xor = _interopRequireDefault(require("../util/xor"));
var Pss = /*#__PURE__*/function () {
function Pss(opts) {
(0, _classCallCheck2.default)(this, Pss);
opts = opts || {};
this.hashAlgorithm = opts.hashAlgorithm || 'sha256';
this.hashLength = (0, _crypto.createHash)(this.hashAlgorithm).digest().length;
this.saltLength = this.hashLength;
}
/**
* Create padded message for signing.
*
* This is an implementation of EMSA-PSS-ENCODE from RFC3447, section 9.1.1.
*
* @param {Buffer} message Message to sign.
* @param {Number} encodedMessageBits Number of bits of the resulting padded
* message.
* @return {Buffer} Padded message of length encodedMessageBits bits
*/
(0, _createClass2.default)(Pss, [{
key: "encode",
value: function encode(message, encodedMessageBits) {
var _context, _context2;
// Calculate emLen
var encodedMessageBytes = Math.ceil(encodedMessageBits / 8); // Step 2. mHash = Hash(M)
var messageHash = (0, _crypto.createHash)(this.hashAlgorithm).update(message).digest(); // Step 3. If emLen < hLen + sLen + 2, output "encoding error" and stop.
if (encodedMessageBytes < this.hashLength + this.saltLength + 2) {
throw new Error('Encoding error: RSA modulus is too small for ' + this.hashAlgorithm);
} // Step 4. Generate a random salt
var salt = (0, _crypto.randomBytes)(this.saltLength); // Step 5. M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt
// Step 6. H = Hash(M')
var hash = (0, _crypto.createHash)(this.hashAlgorithm).update((0, _fill.default)(_context = Buffer.alloc(8)).call(_context, 0)).update(messageHash).update(salt).digest(); // Step 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2
// zero octets.
// Step 8. Let DB = PS || 0x01 || salt
var dataBlock = (0, _concat.default)(Buffer).call(Buffer, [(0, _fill.default)(_context2 = Buffer.alloc(encodedMessageBytes - this.saltLength - this.hashLength - 2)).call(_context2, 0), Buffer.from([1]), salt]); // Step 9. Let dbMask = MGF(H, emLen - hLen - 1)
var mgf1 = new _mgf.default({
hashAlgorithm: this.hashAlgorithm
});
var dataBlockMask = mgf1.generate(hash, encodedMessageBytes - this.hashLength - 1); // Step 10. Let maskedDB = DB \xor dbMask
var maskedDataBlock = (0, _xor.default)(dataBlock, dataBlockMask); // Step 11. Set the leftmost 8emLen - emBits bits of the leftmost octet in
// maskedDB to zero.
maskedDataBlock[0] &= 0xff >>> encodedMessageBytes * 8 - encodedMessageBits; // Step 12. Let EM = maskedDB || H || 0xbc.
// Step 13. Output EM.
return (0, _concat.default)(Buffer).call(Buffer, [maskedDataBlock, hash, Buffer.from([0xbc])]);
}
/**
* Verify that a padded message matches a specimen message.
*
* Used for RSA signature verification.
*
* This is an implementation of EMSA-PSS-VERIFY from RFC3447, section 9.1.2.
*
* @param {Buffer} message Message to be verified.
* @param {Buffer} encodedMessage Padded message to be compared.
* @param {Number} encodedMessageBits Number of bits in the padded message.
* @return {Boolean} Verification result.
*/
}, {
key: "verify",
value: function verify(message, encodedMessage, encodedMessageBits) {
var _context3;
// Calculate emLen
var encodedMessageBytes = Math.ceil(encodedMessageBits / 8); // Step 2. mHash = Hash(M)
var messageHash = (0, _crypto.createHash)(this.hashAlgorithm).update(message).digest(); // Step 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
if (encodedMessageBytes < this.hashLength + this.saltLength + 2) {
return false;
} // Step 4. If the rightmost octet of EM does not have hexadecimal value
// 0xbc, output "inconsistent" and stop.
if (encodedMessage[encodedMessage.length - 1] !== 0xbc) {
return false;
} // Step 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and
// let H be the next hLen octets.
var dataBlockLength = encodedMessageBytes - this.hashLength - 1;
var maskedDataBlock = (0, _slice.default)(encodedMessage).call(encodedMessage, 0, dataBlockLength);
var hash = (0, _slice.default)(encodedMessage).call(encodedMessage, dataBlockLength, dataBlockLength + this.hashLength); // Step 6. If the leftmost 8emLen - emBits bits of the leftmost octet in
// maskedDB are not all equal to zero, output "inconsistent" and
// stop.
var expectedMask = 0xff >>> encodedMessageBytes * 8 - encodedMessageBits;
if (maskedDataBlock[0] & ~expectedMask) {
return false;
} // Step 7. Let dbMask = MGF(H, emLen - hLen - 1).
var mgf1 = new _mgf.default({
hashAlgorithm: this.hashAlgorithm
});
var dataBlockMask = mgf1.generate(hash, encodedMessageBytes - this.hashLength - 1); // Step 8. Let DB = maskedDB \xor dbMask.
var dataBlock = (0, _xor.default)(maskedDataBlock, dataBlockMask); // Step 9. Set the leftmost 8emLen - emBits bits of the leftmost octet in DB
// to zero.
dataBlock[0] &= expectedMask; // Step 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero
// or if the octet at position emLen - hLen - sLen - 1 (the leftmost
// position is "position 1") does not have hexadecimal value 0x01,
// output "inconsistent" and stop.
var prefixLength = encodedMessageBytes - this.hashLength - this.saltLength - 2;
for (var i = 0; i < prefixLength; i++) {
if (dataBlock[i] !== 0) {
return false;
}
}
if (dataBlock[prefixLength] !== 0x01) {
return false;
} // Step 11. Let salt be the last sLen octets of DB.
var salt = (0, _slice.default)(dataBlock).call(dataBlock, dataBlock.length - this.saltLength); // Step 12. Let M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt
// Step 13. Let H' = Hash(M'), an octet string of length hLen.
var reconstructedHash = (0, _crypto.createHash)(this.hashAlgorithm).update((0, _fill.default)(_context3 = Buffer.alloc(8)).call(_context3, 0)).update(messageHash).update(salt).digest(); // Step 14. If H = H', output "consistent." Otherwise, output "inconsistent."
return Buffer.compare(hash, reconstructedHash) === 0;
}
}]);
return Pss;
}();
Pss.EMPTY_BUFFER = Buffer.alloc(0);
var _default = Pss;
exports.default = _default;