line_push/node_modules/js-crypto-key-utils/dist/key.js
2022-07-17 13:16:16 +08:00

601 lines
18 KiB
JavaScript

"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Key = void 0;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _converter = require("./converter.js");
var _thumbprint = require("./thumbprint.js");
var _lodash = _interopRequireDefault(require("lodash.clonedeep"));
var _jsEncodingUtils = _interopRequireDefault(require("js-encoding-utils"));
var _util = require("./util.js");
/**
* key.js
*/
/**
* Key class to abstract public and private key objects in string or binary.
* This class provides functions to interchangeably convert key formats,
* and key objects will be used for the root package, js-crypto-utils, as inputs to exposed APIs.
*/
var Key =
/*#__PURE__*/
function () {
/**
* @constructor
* @param {String} format - Key format: 'jwk', 'der', 'pem' or 'oct' (only for ECC key).
* @param {JsonWebKey|PEM|DER|OctetEC} key - Key object in the specified format.
* @param {Object} [options={}] - Required if format='oct', and then it is {namedCurve: String}.
* @throws {Error} - Throws if the input format and key are incompatible to the constructor.
*/
function Key(format, key) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
(0, _classCallCheck2.default)(this, Key);
var localKey = (0, _lodash.default)(key);
var localOpt = (0, _lodash.default)(options);
this._jwk = {};
this._der = null;
this._oct = {}; // only for EC keys
this._current = {
jwk: false,
der: false,
oct: false
};
if (format === 'jwk') {
this._setJwk(localKey);
} else if (format === 'der' || format === 'pem') {
if (format === 'der' && !(localKey instanceof Uint8Array)) throw new Error('DerKeyMustBeUint8Array');
if (format === 'pem' && typeof localKey !== 'string') throw new Error('PemKeyMustBeString');
this._setAsn1(localKey, format);
} else if (format === 'oct') {
if (typeof localOpt.namedCurve !== 'string') throw new Error('namedCurveMustBeSpecified');
if (!(localKey instanceof Uint8Array)) throw new Error('OctetKeyMustBeUint8Array');
this._setSec1(localKey, localOpt.namedCurve);
} else throw new Error('UnsupportedType');
} ///////////////////////////////////////////////////////////
// private method handling instance variables
// all instance variables must be set via these methods
/**
* Set a key in JWK to the Key object.
* @param {JsonWebKey} jwkey - The Json Web Key.
* @private
*/
(0, _createClass2.default)(Key, [{
key: "_setJwk",
value: function _setJwk(jwkey) {
this._type = (0, _util.getJwkType)(jwkey); // this also check key format
this._jwk = jwkey;
if (this._isEncrypted) this._der = null;
this._isEncrypted = false;
this._setCurrentStatus();
}
/**
* Set a key in DER or PEM to the Key object.
* @param {DER|PEM} asn1key - The DER key byte array or PEM key string.
* @param {String} format - 'der' or 'pem' specifying the format.
* @private
*/
}, {
key: "_setAsn1",
value: function _setAsn1(asn1key, format) {
this._type = (0, _util.isAsn1Public)(asn1key, format) ? 'public' : 'private'; // this also check key format
this._isEncrypted = (0, _util.isAsn1Encrypted)(asn1key, format);
this._der = format === 'pem' ? _jsEncodingUtils.default.formatter.pemToBin(asn1key) : asn1key;
if (this._isEncrypted) {
this._jwk = {};
this._oct = {};
}
this._setCurrentStatus();
}
/**
* Set a key in SEC1 = Octet format to the Key Object.
* @param {OctetEC} sec1key - The Octet SEC1 key byte array.
* @param {String} namedCurve - Name of curve like 'P-256'.
* @private
*/
}, {
key: "_setSec1",
value: function _setSec1(sec1key, namedCurve) {
this._type = (0, _util.getSec1KeyType)(sec1key, namedCurve); // this also check key format
this._oct = {
namedCurve: namedCurve,
key: sec1key
};
if (this._isEncrypted) this._der = null;
this._isEncrypted = false;
this._setCurrentStatus();
}
/**
* Set the current internal status. In particular, manage what the object is based on.
* @private
*/
}, {
key: "_setCurrentStatus",
value: function _setCurrentStatus() {
this._current.jwk = typeof this._jwk.kty === 'string' && (this._jwk.kty === 'RSA' || this._jwk.kty === 'EC');
this._current.der = typeof this._der !== 'undefined' && this._der instanceof Uint8Array && this._der.length > 0;
this._current.oct = typeof this._oct.key !== 'undefined' && this._oct.key instanceof Uint8Array && this._oct.key.length > 0 && typeof this._oct.namedCurve === 'string';
} ///////////////////////////////////////////////////////////
// (pseudo) public methods allowed to be accessed from outside
/**
* Convert the stored key and export the key in desired format.
* Imported key must be basically decrypted except the case where the key is exported as-is.
* @param {String} format - Intended format of exported key. 'jwk', 'pem', 'der' or 'oct'
* @param {KeyExportOptions} [options={}] - Optional arguments.
* @return {Promise<JsonWebKey|PEM|DER|OctetEC>} - Exported key object.
*/
}, {
key: "export",
value: function () {
var _export2 = (0, _asyncToGenerator2.default)(
/*#__PURE__*/
_regenerator.default.mark(function _callee() {
var format,
options,
jwkey,
_args = arguments;
return _regenerator.default.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
format = _args.length > 0 && _args[0] !== undefined ? _args[0] : 'jwk';
options = _args.length > 1 && _args[1] !== undefined ? _args[1] : {};
if (!(['pem', 'der', 'jwk', 'oct'].indexOf(format) < 0)) {
_context.next = 4;
break;
}
throw new Error('UnsupportedFormat');
case 4:
if (!(this._isEncrypted && this._type === 'private')) {
_context.next = 10;
break;
}
if (!((format === 'der' || format === 'pem') && Object.keys(options).length === 0 && this._current.der)) {
_context.next = 9;
break;
}
return _context.abrupt("return", format === 'pem' ? _jsEncodingUtils.default.formatter.binToPem(this._der, 'encryptedPrivate') : this._der);
case 9:
throw new Error('DecryptionRequired');
case 10:
if (!this._current.jwk) {
_context.next = 14;
break;
}
jwkey = this._jwk;
_context.next = 27;
break;
case 14:
if (!this._current.oct) {
_context.next = 20;
break;
}
_context.next = 17;
return (0, _converter.toJwkFrom)('oct', this._oct.key, {
namedCurve: this._oct.namedCurve
});
case 17:
jwkey = _context.sent;
_context.next = 27;
break;
case 20:
if (!this._current.der) {
_context.next = 26;
break;
}
_context.next = 23;
return (0, _converter.toJwkFrom)('der', this._der);
case 23:
jwkey = _context.sent;
_context.next = 27;
break;
case 26:
throw new Error('InvalidStatus');
case 27:
this._setJwk(jwkey); // store jwk if the exiting private key is not encrypted
// then export as the key in intended format
if (!(format === 'der' || format === 'pem')) {
_context.next = 35;
break;
}
if (typeof options.encryptParams === 'undefined') options.encryptParams = {};
_context.next = 32;
return (0, _converter.fromJwkTo)(format, jwkey, {
outputPublic: options.outputPublic,
compact: options.compact,
//passphrase: options.encryptParams.passphrase,
encryptParams: options.encryptParams
});
case 32:
return _context.abrupt("return", _context.sent);
case 35:
if (!(format === 'oct')) {
_context.next = 41;
break;
}
_context.next = 38;
return (0, _converter.fromJwkTo)(format, jwkey, {
outputPublic: options.outputPublic,
output: options.output,
compact: options.compact
});
case 38:
return _context.abrupt("return", _context.sent);
case 41:
return _context.abrupt("return", jwkey);
case 42:
case "end":
return _context.stop();
}
}
}, _callee, this);
}));
function _export() {
return _export2.apply(this, arguments);
}
return _export;
}()
/**
* Encrypt stored key and set the encrypted key to this instance.
* @param {String} passphrase - String passphrase.
* @return {Promise<boolean>} - Always true otherwise thrown.
* @throws {Error} - Throws if AlreadyEncrypted.
*/
}, {
key: "encrypt",
value: function () {
var _encrypt = (0, _asyncToGenerator2.default)(
/*#__PURE__*/
_regenerator.default.mark(function _callee2(passphrase) {
var options;
return _regenerator.default.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
if (!this._isEncrypted) {
_context2.next = 2;
break;
}
throw new Error('AlreadyEncrypted');
case 2:
options = {
encryptParams: {
passphrase: passphrase
}
};
_context2.t0 = this;
_context2.next = 6;
return this.export('der', options);
case 6:
_context2.t1 = _context2.sent;
_context2.t0._setAsn1.call(_context2.t0, _context2.t1, 'der');
return _context2.abrupt("return", true);
case 9:
case "end":
return _context2.stop();
}
}
}, _callee2, this);
}));
function encrypt(_x) {
return _encrypt.apply(this, arguments);
}
return encrypt;
}()
/**
* Decrypted stored key and set the decrypted key in JWK to this instance.
* @param {String} passphrase - String passphrase.
* @return {Promise<boolean>} - Always true otherwise thrown.
* @throws {Error} - Throws if NotEncrypted or FailedToDecrypt.
*/
}, {
key: "decrypt",
value: function () {
var _decrypt = (0, _asyncToGenerator2.default)(
/*#__PURE__*/
_regenerator.default.mark(function _callee3(passphrase) {
var jwkey;
return _regenerator.default.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
if (this._isEncrypted) {
_context3.next = 2;
break;
}
throw new Error('NotEncrypted');
case 2:
if (!(this._current.der && typeof passphrase === 'string')) {
_context3.next = 8;
break;
}
_context3.next = 5;
return (0, _converter.toJwkFrom)('der', this._der, {
passphrase: passphrase
});
case 5:
jwkey = _context3.sent;
_context3.next = 9;
break;
case 8:
throw new Error('FailedToDecrypt');
case 9:
this._setJwk(jwkey);
return _context3.abrupt("return", true);
case 11:
case "end":
return _context3.stop();
}
}
}, _callee3, this);
}));
function decrypt(_x2) {
return _decrypt.apply(this, arguments);
}
return decrypt;
}()
/**
* Conpute JWK thumbprint specified in RFC7638 {@link https://tools.ietf.org/html/rfc7638}.
* @param {String} [alg='SHA-256'] - Name of hash algorithm for thumbprint computation like 'SHA-256'.
* @param {JwkThumbpirntFormat} [output='binary'] - Output format of JWK thumbprint. 'binary', 'hex' or 'base64'.
* @return {Promise<Uint8Array|String>} - Computed thumbprint.
* @throws {Error} - Throws if DecryptionRequired.
*/
}, {
key: "getJwkThumbprint",
value: function () {
var _getJwkThumbprint2 = (0, _asyncToGenerator2.default)(
/*#__PURE__*/
_regenerator.default.mark(function _callee4() {
var alg,
output,
_args4 = arguments;
return _regenerator.default.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
alg = _args4.length > 0 && _args4[0] !== undefined ? _args4[0] : 'SHA-256';
output = _args4.length > 1 && _args4[1] !== undefined ? _args4[1] : 'binary';
if (!this._isEncrypted) {
_context4.next = 4;
break;
}
throw new Error('DecryptionRequired');
case 4:
_context4.t0 = _thumbprint.getJwkThumbprint;
_context4.next = 7;
return this.export('jwk');
case 7:
_context4.t1 = _context4.sent;
_context4.t2 = alg;
_context4.t3 = output;
_context4.next = 12;
return (0, _context4.t0)(_context4.t1, _context4.t2, _context4.t3);
case 12:
return _context4.abrupt("return", _context4.sent);
case 13:
case "end":
return _context4.stop();
}
}
}, _callee4, this);
}));
function getJwkThumbprint() {
return _getJwkThumbprint2.apply(this, arguments);
}
return getJwkThumbprint;
}() // getters
/**
* Get keyType in JWK format
* @return {Promise<String>} - 'RSA' or 'EC'
* @throws {Error} - Throws if DecryptionRequired.
*/
}, {
key: "keyType",
get: function get() {
var _this = this;
if (this._isEncrypted) throw new Error('DecryptionRequired');
return new Promise(
/*#__PURE__*/
function () {
var _ref = (0, _asyncToGenerator2.default)(
/*#__PURE__*/
_regenerator.default.mark(function _callee5(resolve, reject) {
var jwkey;
return _regenerator.default.wrap(function _callee5$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
_context5.next = 2;
return _this.export('jwk').catch(function (e) {
reject(e);
});
case 2:
jwkey = _context5.sent;
resolve(jwkey.kty);
case 4:
case "end":
return _context5.stop();
}
}
}, _callee5);
}));
return function (_x3, _x4) {
return _ref.apply(this, arguments);
};
}());
}
/**
* Get jwkThumbprint of this key.
* @return {Promise<Uint8Array>} - Returns binary thumbprint.
*/
}, {
key: "jwkThumbprint",
get: function get() {
return this.getJwkThumbprint();
}
/**
* Check if this is encrypted.
* @return {boolean}
*/
}, {
key: "isEncrypted",
get: function get() {
return this._isEncrypted;
}
/**
* Check if this is a private key.
* @return {boolean}
*/
}, {
key: "isPrivate",
get: function get() {
return this._type === 'private';
}
/**
* Returns the key in DER format.
* @return {Promise<DER>}
*/
}, {
key: "der",
get: function get() {
return this.export('der');
}
/**
* Returns the key in PEM format.
* @return {Promise<PEM>}
*/
}, {
key: "pem",
get: function get() {
return this.export('pem');
}
/**
* Returns the key in JWK format
* @return {Promise<JsonWebKey>}
*/
}, {
key: "jwk",
get: function get() {
return this.export('jwk');
}
/**
* Returns the 'EC' key in Octet SEC1 format.
* @return {Promise<OctetEC>}
*/
}, {
key: "oct",
get: function get() {
return this.export('oct', {
output: 'string'
});
}
}]);
return Key;
}();
exports.Key = Key;