terminate statement

This commit is contained in:
Magnus von Wachenfeldt 2021-09-01 11:10:03 +02:00
parent d894570bd4
commit e3c35c525f
Signed by: magnus
GPG Key ID: A469F7D71D09F795
2 changed files with 183 additions and 148 deletions

View File

@ -9,7 +9,7 @@ module.exports = {
privateExport: function (key, options) { privateExport: function (key, options) {
const nbuf = key.n.toBuffer(); const nbuf = key.n.toBuffer();
let ebuf = Buffer.alloc(4) let ebuf = Buffer.alloc(4);
ebuf.writeUInt32BE(key.e, 0); ebuf.writeUInt32BE(key.e, 0);
//Slice leading zeroes //Slice leading zeroes
while (ebuf[0] === 0) ebuf = ebuf.slice(1); while (ebuf[0] === 0) ebuf = ebuf.slice(1);

View File

@ -1,148 +1,183 @@
var ber = require('asn1').Ber; /**
var _ = require('../utils')._; * PKCS1 padding and signature scheme
var utils = require('../utils'); */
const PRIVATE_OPENING_BOUNDARY = '-----BEGIN RSA PRIVATE KEY-----'; var BigInteger = require('../libs/jsbn');
const PRIVATE_CLOSING_BOUNDARY = '-----END RSA PRIVATE KEY-----'; var crypt = require('crypto');
var SIGN_INFO_HEAD = {
const PUBLIC_OPENING_BOUNDARY = '-----BEGIN RSA PUBLIC KEY-----'; md2: Buffer.from('3020300c06082a864886f70d020205000410', 'hex'),
const PUBLIC_CLOSING_BOUNDARY = '-----END RSA PUBLIC KEY-----'; md5: Buffer.from('3020300c06082a864886f70d020505000410', 'hex'),
sha1: Buffer.from('3021300906052b0e03021a05000414', 'hex'),
module.exports = { sha224: Buffer.from('302d300d06096086480165030402040500041c', 'hex'),
privateExport: function (key, options) { sha256: Buffer.from('3031300d060960864801650304020105000420', 'hex'),
options = options || {}; sha384: Buffer.from('3041300d060960864801650304020205000430', 'hex'),
sha512: Buffer.from('3051300d060960864801650304020305000440', 'hex'),
var n = key.n.toBuffer(); ripemd160: Buffer.from('3021300906052b2403020105000414', 'hex'),
var d = key.d.toBuffer(); rmd160: Buffer.from('3021300906052b2403020105000414', 'hex')
var p = key.p.toBuffer(); };
var q = key.q.toBuffer();
var dmp1 = key.dmp1.toBuffer(); var SIGN_ALG_TO_HASH_ALIASES = {
var dmq1 = key.dmq1.toBuffer(); 'ripemd160': 'rmd160'
var coeff = key.coeff.toBuffer(); };
var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic var DEFAULT_HASH_FUNCTION = 'sha256';
var writer = new ber.Writer({size: length});
module.exports = {
writer.startSequence(); isEncryption: true,
writer.writeInt(0); isSignature: true
writer.writeBuffer(n, 2); };
writer.writeInt(key.e);
writer.writeBuffer(d, 2); module.exports.makeScheme = function (key, options) {
writer.writeBuffer(p, 2); function Scheme(key, options) {
writer.writeBuffer(q, 2); this.key = key;
writer.writeBuffer(dmp1, 2); this.options = options;
writer.writeBuffer(dmq1, 2); }
writer.writeBuffer(coeff, 2);
writer.endSequence(); Scheme.prototype.maxMessageLength = function () {
if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == 3) {
if (options.type === 'der') { return this.key.encryptedDataLength;
return writer.buffer; }
} else { return this.key.encryptedDataLength - 11;
return PRIVATE_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PRIVATE_CLOSING_BOUNDARY; };
}
}, /**
* Pad input Buffer to encryptedDataLength bytes, and return Buffer.from
privateImport: function (key, data, options) { * alg: PKCS#1
options = options || {}; * @param buffer
var buffer; * @returns {Buffer}
*/
if (options.type !== 'der') { Scheme.prototype.encPad = function (buffer, options) {
if (Buffer.isBuffer(data)) { options = options || {};
data = data.toString('utf8'); var filled;
} if (buffer.length > this.key.maxMessageLength) {
throw new Error("Message too long for RSA (n=" + this.key.encryptedDataLength + ", l=" + buffer.length + ")");
if (_.isString(data)) { }
var pem = utils.trimSurroundingText(data, PRIVATE_OPENING_BOUNDARY, PRIVATE_CLOSING_BOUNDARY)
.replace(/\s+|\n\r|\n|\r$/gm, ''); if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == 3) {
buffer = Buffer.from(pem, 'base64'); //RSA_NO_PADDING treated like JAVA left pad with zero character
} else { filled = Buffer.alloc(this.key.maxMessageLength - buffer.length);
throw Error('Unsupported key format'); filled.fill(0);
} return Buffer.concat([filled, buffer]);
} else if (Buffer.isBuffer(data)) { }
buffer = data;
} else { /* Type 1: zeros padding for private key encrypt */
throw Error('Unsupported key format'); if (options.type === 1) {
} filled = Buffer.alloc(this.key.encryptedDataLength - buffer.length - 1);
filled.fill(0xff, 0, filled.length - 1);
var reader = new ber.Reader(buffer); filled[0] = 1;
reader.readSequence(); filled[filled.length - 1] = 0;
reader.readString(2, true); // just zero
key.setPrivate( return Buffer.concat([filled, buffer]);
reader.readString(2, true), // modulus } else {
reader.readString(2, true), // publicExponent /* random padding for public key encrypt */
reader.readString(2, true), // privateExponent filled = Buffer.alloc(this.key.encryptedDataLength - buffer.length);
reader.readString(2, true), // prime1 filled[0] = 0;
reader.readString(2, true), // prime2 filled[1] = 2;
reader.readString(2, true), // exponent1 -- d mod (p1) var rand = crypt.randomBytes(filled.length - 3);
reader.readString(2, true), // exponent2 -- d mod (q-1) for (var i = 0; i < rand.length; i++) {
reader.readString(2, true) // coefficient -- (inverse of q) mod p var r = rand[i];
); while (r === 0) { // non-zero only
}, r = crypt.randomBytes(1)[0];
}
publicExport: function (key, options) { filled[i + 2] = r;
options = options || {}; }
filled[filled.length - 1] = 0;
var n = key.n.toBuffer(); return Buffer.concat([filled, buffer]);
var length = n.length + 512; // magic }
};
var bodyWriter = new ber.Writer({size: length});
bodyWriter.startSequence(); /**
bodyWriter.writeBuffer(n, 2); * Unpad input Buffer and, if valid, return the Buffer object
bodyWriter.writeInt(key.e); * alg: PKCS#1 (type 2, random)
bodyWriter.endSequence(); * @param buffer
* @returns {Buffer}
if (options.type === 'der') { */
return bodyWriter.buffer; Scheme.prototype.encUnPad = function (buffer, options) {
} else { return buffer;
return PUBLIC_OPENING_BOUNDARY + '\n' + utils.linebrk(bodyWriter.buffer.toString('base64'), 64) + '\n' + PUBLIC_CLOSING_BOUNDARY; };
}
}, Scheme.prototype.sign = function (buffer) {
var hashAlgorithm = this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION;
publicImport: function (key, data, options) { hashAlgorithm = SIGN_ALG_TO_HASH_ALIASES[hashAlgorithm] || hashAlgorithm;
options = options || {};
var buffer; var hasher = crypt.createHash(hashAlgorithm);
hasher.update(buffer);
if (options.type !== 'der') { var hash = this.pkcs1pad(hasher.digest(), hashAlgorithm);
if (Buffer.isBuffer(data)) { var res = this.key.$doPrivate(new BigInteger(hash)).toBuffer(this.key.encryptedDataLength);
data = data.toString('utf8');
} return res;
};
if (_.isString(data)) {
var pem = utils.trimSurroundingText(data, PUBLIC_OPENING_BOUNDARY, PUBLIC_CLOSING_BOUNDARY) Scheme.prototype.verify = function (buffer, signature, signature_encoding) {
.replace(/\s+|\n\r|\n|\r$/gm, ''); if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == 3) {
buffer = Buffer.from(pem, 'base64'); //RSA_NO_PADDING has no verify data
} return false;
} else if (Buffer.isBuffer(data)) { }
buffer = data; var hashAlgorithm = this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION;
} else { hashAlgorithm = SIGN_ALG_TO_HASH_ALIASES[hashAlgorithm] || hashAlgorithm;
throw Error('Unsupported key format');
} if (signature_encoding) {
signature = Buffer.from(signature, signature_encoding);
var body = new ber.Reader(buffer); }
body.readSequence();
key.setPublic( var hasher = crypt.createHash(hashAlgorithm);
body.readString(0x02, true), // modulus hasher.update(buffer);
body.readString(0x02, true) // publicExponent var hash = this.pkcs1pad(hasher.digest(), hashAlgorithm);
); var m = this.key.$doPublic(new BigInteger(signature));
},
return m.toBuffer().toString('hex') == hash.toString('hex');
/** };
* Trying autodetect and import key
* @param key /**
* @param data * PKCS#1 zero pad input buffer to max data length
*/ * @param hashBuf
autoImport: function (key, data) { * @param hashAlgorithm
// [\S\s]* matches zero or more of any character * @returns {*}
if (/^[\S\s]*-----BEGIN RSA PRIVATE KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END RSA PRIVATE KEY-----[\S\s]*$/g.test(data)) { */
module.exports.privateImport(key, data); Scheme.prototype.pkcs0pad = function (buffer) {
return true; var filled = Buffer.alloc(this.key.maxMessageLength - buffer.length);
} filled.fill(0);
return Buffer.concat([filled, buffer]);
if (/^[\S\s]*-----BEGIN RSA PUBLIC KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END RSA PUBLIC KEY-----[\S\s]*$/g.test(data)) { };
module.exports.publicImport(key, data);
return true; Scheme.prototype.pkcs0unpad = function (buffer) {
} var unPad;
if (typeof buffer.lastIndexOf == "function") { //patch for old node version
return false; unPad = buffer.slice(buffer.lastIndexOf('\0') + 1, buffer.length);
} } else {
}; unPad = buffer.slice(String.prototype.lastIndexOf.call(buffer, '\0') + 1, buffer.length);
}
return unPad;
};
/**
* PKCS#1 pad input buffer to max data length
* @param hashBuf
* @param hashAlgorithm
* @returns {*}
*/
Scheme.prototype.pkcs1pad = function (hashBuf, hashAlgorithm) {
var digest = SIGN_INFO_HEAD[hashAlgorithm];
if (!digest) {
throw Error('Unsupported hash algorithm');
}
var data = Buffer.concat([digest, hashBuf]);
if (data.length + 10 > this.key.encryptedDataLength) {
throw Error('Key is too short for signing algorithm (' + hashAlgorithm + ')');
}
var filled = Buffer.alloc(this.key.encryptedDataLength - data.length - 1);
filled.fill(0xff, 0, filled.length - 1);
filled[0] = 1;
filled[filled.length - 1] = 0;
var res = Buffer.concat([filled, data]);
return res;
};
return new Scheme(key, options);
};