diff --git a/lib/MGF1.js b/lib/MGF1.js new file mode 100644 index 0000000..e968b36 --- /dev/null +++ b/lib/MGF1.js @@ -0,0 +1,67 @@ +'use strict' + +const crypto = require("crypto"); +const BN = require('bn.js'); + +module.exports = class MGF1 { + + constructor() { + this._len = 32; + } + + /** + * + * @param {Buffer} seed + * @param {Number} masklen + * + * @returns Buffer + */ + static generate(seed, masklen) { + const mgf1 = new MGF1(); + + if ( masklen > 4294967296 * this._len) { + throw new Error('Mask too long'); + } + + const b = []; + + for (let i = 0; i < mgf1._divceil(masklen, mgf1._len); i++) { + b[i] = crypto.createHash('sha256').update(Buffer.concat([seed, mgf1._i2osp(i, 4)])).digest(); + } + + return (Buffer.concat(b)).slice(0, masklen); + } + + static xor(a, b) { + if ( a.length != b.length ) { + throw new Error('Different length for a and b'); + } + + for ( let i = 0; i < a.length; i++ ) { + a[i] ^= b[i]; + } + + return a; + } + + _divceil(a, b) { + return ~~((a + b - 1) / b); + } + + _i2osp(x, len) { + if ( x >= 256 ** len ) { + throw new Error('Integer too large'); + } + + return Buffer.from(this._rjust( (Buffer.from((new BN(x)).toArray('be', 4)).toString()).replace(/\x00/gi, ''), len, "\x00" )); + } + + _rjust( string, width, padding ) { + padding = padding || " "; + padding = padding.substr( 0, 1 ); + if ( string.length < width ) + return padding.repeat( width - string.length ) + string; + else + return string; + } +}