2018-06-11 08:38:32 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const crypto = require('crypto');
|
|
|
|
|
2019-11-07 08:27:08 +00:00
|
|
|
const BigNumber = require('./BigNumber.js');
|
2018-06-11 08:38:32 +00:00
|
|
|
const mgf1 = require('./MGF1');
|
|
|
|
|
|
|
|
const modPow = (base, power, mod) => {
|
2019-11-07 06:58:52 +00:00
|
|
|
let result = new BigNumber(1);
|
2018-06-11 08:38:32 +00:00
|
|
|
|
|
|
|
while (power > 0) {
|
2019-11-07 06:58:52 +00:00
|
|
|
result = power.and(new BigNumber(1)) == 1 ? (result.mul(base)).mod(mod) : result; // eslint-disable-line
|
2018-06-11 08:38:32 +00:00
|
|
|
base = (base.mul(base)).mod(mod);
|
|
|
|
power = power.shrn(1);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
|
|
|
const emsaPSS = (msg, salt) => {
|
|
|
|
const eightNullBytes = Buffer.from('\x00'.repeat(8));
|
|
|
|
const digestedMsg = crypto.createHash('sha256').update(msg).digest();
|
|
|
|
const mTickHash = crypto.createHash('sha256').update(Buffer.concat([eightNullBytes, digestedMsg, salt]), 'binary').digest();
|
|
|
|
|
|
|
|
const ps = Buffer.from('\x00'.repeat(190));
|
|
|
|
const db = Buffer.concat([ps, Buffer.from('\x01'), salt]);
|
|
|
|
|
|
|
|
const dbMask = mgf1.generate(mTickHash, db.length);
|
|
|
|
const maskedDb = mgf1.xor(db, dbMask);
|
|
|
|
|
2019-11-07 08:27:08 +00:00
|
|
|
let maskedDbMsb = mgf1.rjust(new BigNumber(maskedDb.slice(0, 1)).toString(2), 8, '0');
|
2019-11-07 06:58:52 +00:00
|
|
|
|
2018-06-11 08:38:32 +00:00
|
|
|
|
|
|
|
maskedDbMsb = `0${maskedDbMsb.substr(1)}`;
|
2019-11-07 06:58:52 +00:00
|
|
|
// console.log((new BN(maskedDbMsb, 2).toBuffer())[0], new BigNumber(maskedDbMsb, 2).toBuffer()[0]);
|
|
|
|
// maskedDb[0] = (new BN(maskedDbMsb, 2).toBuffer())[0]; // eslint-disable-line
|
2019-11-07 08:30:03 +00:00
|
|
|
maskedDb[0] = new BigNumber(maskedDbMsb, 2).toBEBuffer()[0]; // eslint-disable-line
|
2018-06-11 08:38:32 +00:00
|
|
|
|
|
|
|
return Buffer.concat([maskedDb, mTickHash, Buffer.from('BC', 'hex')]);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = class Crypto {
|
|
|
|
static digestPublicKey(key) {
|
|
|
|
const str = [key.e('hex').replace(/^(0+)/g, ''), key.n('hex').replace(/^(0+)/g, '')].map(x => x.toLowerCase()).join(' ');
|
|
|
|
|
|
|
|
return crypto.createHash('sha256').update(str).digest('base64').trim();
|
|
|
|
}
|
|
|
|
|
|
|
|
static publicEncrypt(key, data) {
|
|
|
|
return crypto.publicEncrypt({
|
|
|
|
key: key.toPem(),
|
|
|
|
padding: crypto.constants.RSA_PKCS1_PADDING,
|
|
|
|
}, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static privateDecrypt(key, data) {
|
|
|
|
return crypto.privateDecrypt({
|
|
|
|
key: key.toPem(),
|
|
|
|
padding: crypto.constants.RSA_PKCS1_PADDING,
|
|
|
|
}, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static privateSign(key, data, outputEncoding = 'base64') {
|
|
|
|
const signer = crypto.createSign('SHA256');
|
|
|
|
|
|
|
|
return signer.update(data).sign(key.toPem(), outputEncoding);
|
|
|
|
}
|
|
|
|
|
|
|
|
static sign(key, msg, salt = crypto.randomBytes(32)) {
|
2019-11-07 06:58:52 +00:00
|
|
|
// console.log(key.d());
|
|
|
|
const base = new BigNumber(emsaPSS(msg, salt));
|
|
|
|
const power = new BigNumber(key.d());
|
|
|
|
const mod = new BigNumber(key.n());
|
2018-06-11 08:38:32 +00:00
|
|
|
|
2021-03-17 10:13:39 +00:00
|
|
|
//Somehow sometimes we have a 514 byte hex key that starts with "00". In that case use only the last 512 bytes
|
|
|
|
var hexString = modPow(base, power, mod).toBEBuffer().toString('hex');
|
|
|
|
if(hexString.substr(0,2) === "00" && hexString.length == 514)
|
|
|
|
{
|
|
|
|
console.log("hex string of key starts with \"00\" and is 514 bytes long, fixing it to be 512 bytes long by stripping leading \"00\"");
|
|
|
|
hexString = hexString.substr(2);
|
|
|
|
}
|
2021-03-29 07:12:23 +00:00
|
|
|
return Buffer.from(hexString, 'hex').toString('base64');
|
2018-06-11 08:38:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static pad(d) {
|
|
|
|
const dLen = d.length;
|
|
|
|
const len = 16 * (Math.trunc(dLen / 16) + 1);
|
|
|
|
|
|
|
|
return Buffer.concat([d, Buffer.from(Buffer.from([0]).toString().repeat(len - dLen - 1)), Buffer.from([len - dLen])]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static digestWithHash(data, algorith = 'sha256') {
|
|
|
|
return crypto.createHash(algorith).update(data).digest();
|
|
|
|
}
|
|
|
|
|
|
|
|
static nonce(outputEncoding = 'hex') {
|
|
|
|
return crypto.randomBytes(16).toString(outputEncoding);
|
|
|
|
}
|
|
|
|
|
|
|
|
static timestamp() {
|
|
|
|
return new Date().toISOString();
|
|
|
|
}
|
|
|
|
};
|