mirror of
https://github.com/Instadapp/Swap-Aggregator-Subgraph.git
synced 2024-07-29 21:57:12 +00:00
373 lines
13 KiB
JavaScript
Executable File
373 lines
13 KiB
JavaScript
Executable File
import { RSA } from './rsa';
|
|
import { IllegalArgumentError, IllegalStateError, SecurityError } from '../other/errors';
|
|
import { BigNumber } from '../bignum/bignum';
|
|
import { getRandomValues } from '../other/get-random-values';
|
|
export class RSA_OAEP {
|
|
constructor(key, hash, label) {
|
|
this.rsa = new RSA(key);
|
|
this.hash = hash;
|
|
if (label !== undefined) {
|
|
this.label = label.length > 0 ? label : null;
|
|
}
|
|
else {
|
|
this.label = null;
|
|
}
|
|
}
|
|
encrypt(data, random) {
|
|
const key_size = Math.ceil(this.rsa.key[0].bitLength / 8);
|
|
const hash_size = this.hash.HASH_SIZE;
|
|
const data_length = data.byteLength || data.length || 0;
|
|
const ps_length = key_size - data_length - 2 * hash_size - 2;
|
|
if (data_length > key_size - 2 * this.hash.HASH_SIZE - 2)
|
|
throw new IllegalArgumentError('data too large');
|
|
const message = new Uint8Array(key_size);
|
|
const seed = message.subarray(1, hash_size + 1);
|
|
const data_block = message.subarray(hash_size + 1);
|
|
data_block.set(data, hash_size + ps_length + 1);
|
|
data_block.set(this.hash.process(this.label || new Uint8Array(0)).finish().result, 0);
|
|
data_block[hash_size + ps_length] = 1;
|
|
if (random !== undefined) {
|
|
if (seed.length !== random.length)
|
|
throw new IllegalArgumentError('random size must equal the hash size');
|
|
seed.set(random);
|
|
}
|
|
else {
|
|
getRandomValues(seed);
|
|
}
|
|
const data_block_mask = this.RSA_MGF1_generate(seed, data_block.length);
|
|
for (let i = 0; i < data_block.length; i++)
|
|
data_block[i] ^= data_block_mask[i];
|
|
const seed_mask = this.RSA_MGF1_generate(data_block, seed.length);
|
|
for (let i = 0; i < seed.length; i++)
|
|
seed[i] ^= seed_mask[i];
|
|
this.rsa.encrypt(new BigNumber(message));
|
|
return new Uint8Array(this.rsa.result);
|
|
}
|
|
decrypt(data) {
|
|
if (!this.rsa.key)
|
|
throw new IllegalStateError('no key is associated with the instance');
|
|
const key_size = Math.ceil(this.rsa.key[0].bitLength / 8);
|
|
const hash_size = this.hash.HASH_SIZE;
|
|
const data_length = data.byteLength || data.length || 0;
|
|
if (data_length !== key_size)
|
|
throw new IllegalArgumentError('bad data');
|
|
this.rsa.decrypt(new BigNumber(data));
|
|
const z = this.rsa.result[0];
|
|
const seed = this.rsa.result.subarray(1, hash_size + 1);
|
|
const data_block = this.rsa.result.subarray(hash_size + 1);
|
|
if (z !== 0)
|
|
throw new SecurityError('decryption failed');
|
|
const seed_mask = this.RSA_MGF1_generate(data_block, seed.length);
|
|
for (let i = 0; i < seed.length; i++)
|
|
seed[i] ^= seed_mask[i];
|
|
const data_block_mask = this.RSA_MGF1_generate(seed, data_block.length);
|
|
for (let i = 0; i < data_block.length; i++)
|
|
data_block[i] ^= data_block_mask[i];
|
|
const lhash = this.hash
|
|
.reset()
|
|
.process(this.label || new Uint8Array(0))
|
|
.finish().result;
|
|
for (let i = 0; i < hash_size; i++) {
|
|
if (lhash[i] !== data_block[i])
|
|
throw new SecurityError('decryption failed');
|
|
}
|
|
let ps_end = hash_size;
|
|
for (; ps_end < data_block.length; ps_end++) {
|
|
const psz = data_block[ps_end];
|
|
if (psz === 1)
|
|
break;
|
|
if (psz !== 0)
|
|
throw new SecurityError('decryption failed');
|
|
}
|
|
if (ps_end === data_block.length)
|
|
throw new SecurityError('decryption failed');
|
|
this.rsa.result = data_block.subarray(ps_end + 1);
|
|
return new Uint8Array(this.rsa.result);
|
|
}
|
|
RSA_MGF1_generate(seed, length = 0) {
|
|
const hash_size = this.hash.HASH_SIZE;
|
|
// if ( length > (hash_size * 0x100000000) )
|
|
// throw new IllegalArgumentError("mask length too large");
|
|
const mask = new Uint8Array(length);
|
|
const counter = new Uint8Array(4);
|
|
const chunks = Math.ceil(length / hash_size);
|
|
for (let i = 0; i < chunks; i++) {
|
|
(counter[0] = i >>> 24), (counter[1] = (i >>> 16) & 255), (counter[2] = (i >>> 8) & 255), (counter[3] = i & 255);
|
|
const submask = mask.subarray(i * hash_size);
|
|
let chunk = this.hash
|
|
.reset()
|
|
.process(seed)
|
|
.process(counter)
|
|
.finish().result;
|
|
if (chunk.length > submask.length)
|
|
chunk = chunk.subarray(0, submask.length);
|
|
submask.set(chunk);
|
|
}
|
|
return mask;
|
|
}
|
|
}
|
|
export class RSA_PSS {
|
|
constructor(key, hash, saltLength = 4) {
|
|
this.rsa = new RSA(key);
|
|
this.hash = hash;
|
|
this.saltLength = saltLength;
|
|
if (this.saltLength < 0)
|
|
throw new TypeError('saltLength should be a non-negative number');
|
|
if (this.rsa.key !== null &&
|
|
Math.ceil((this.rsa.key[0].bitLength - 1) / 8) < this.hash.HASH_SIZE + this.saltLength + 2)
|
|
throw new SyntaxError('saltLength is too large');
|
|
}
|
|
sign(data, random) {
|
|
const key_bits = this.rsa.key[0].bitLength;
|
|
const hash_size = this.hash.HASH_SIZE;
|
|
const message_length = Math.ceil((key_bits - 1) / 8);
|
|
const salt_length = this.saltLength;
|
|
const ps_length = message_length - salt_length - hash_size - 2;
|
|
const message = new Uint8Array(message_length);
|
|
const h_block = message.subarray(message_length - hash_size - 1, message_length - 1);
|
|
const d_block = message.subarray(0, message_length - hash_size - 1);
|
|
const d_salt = d_block.subarray(ps_length + 1);
|
|
const m_block = new Uint8Array(8 + hash_size + salt_length);
|
|
const m_hash = m_block.subarray(8, 8 + hash_size);
|
|
const m_salt = m_block.subarray(8 + hash_size);
|
|
m_hash.set(this.hash.process(data).finish().result);
|
|
if (salt_length > 0) {
|
|
if (random !== undefined) {
|
|
if (m_salt.length !== random.length)
|
|
throw new IllegalArgumentError('random size must equal the salt size');
|
|
m_salt.set(random);
|
|
}
|
|
else {
|
|
getRandomValues(m_salt);
|
|
}
|
|
}
|
|
d_block[ps_length] = 1;
|
|
d_salt.set(m_salt);
|
|
h_block.set(this.hash
|
|
.reset()
|
|
.process(m_block)
|
|
.finish().result);
|
|
const d_block_mask = this.RSA_MGF1_generate(h_block, d_block.length);
|
|
for (let i = 0; i < d_block.length; i++)
|
|
d_block[i] ^= d_block_mask[i];
|
|
message[message_length - 1] = 0xbc;
|
|
const zbits = 8 * message_length - key_bits + 1;
|
|
if (zbits % 8)
|
|
message[0] &= 0xff >>> zbits;
|
|
this.rsa.decrypt(new BigNumber(message));
|
|
return this.rsa.result;
|
|
}
|
|
verify(signature, data) {
|
|
const key_bits = this.rsa.key[0].bitLength;
|
|
const hash_size = this.hash.HASH_SIZE;
|
|
const message_length = Math.ceil((key_bits - 1) / 8);
|
|
const salt_length = this.saltLength;
|
|
const ps_length = message_length - salt_length - hash_size - 2;
|
|
this.rsa.encrypt(new BigNumber(signature));
|
|
const message = this.rsa.result;
|
|
if (message[message_length - 1] !== 0xbc)
|
|
throw new SecurityError('bad signature');
|
|
const h_block = message.subarray(message_length - hash_size - 1, message_length - 1);
|
|
const d_block = message.subarray(0, message_length - hash_size - 1);
|
|
const d_salt = d_block.subarray(ps_length + 1);
|
|
const zbits = 8 * message_length - key_bits + 1;
|
|
if (zbits % 8 && message[0] >>> (8 - zbits))
|
|
throw new SecurityError('bad signature');
|
|
const d_block_mask = this.RSA_MGF1_generate(h_block, d_block.length);
|
|
for (let i = 0; i < d_block.length; i++)
|
|
d_block[i] ^= d_block_mask[i];
|
|
if (zbits % 8)
|
|
message[0] &= 0xff >>> zbits;
|
|
for (let i = 0; i < ps_length; i++) {
|
|
if (d_block[i] !== 0)
|
|
throw new SecurityError('bad signature');
|
|
}
|
|
if (d_block[ps_length] !== 1)
|
|
throw new SecurityError('bad signature');
|
|
const m_block = new Uint8Array(8 + hash_size + salt_length);
|
|
const m_hash = m_block.subarray(8, 8 + hash_size);
|
|
const m_salt = m_block.subarray(8 + hash_size);
|
|
m_hash.set(this.hash
|
|
.reset()
|
|
.process(data)
|
|
.finish().result);
|
|
m_salt.set(d_salt);
|
|
const h_block_verify = this.hash
|
|
.reset()
|
|
.process(m_block)
|
|
.finish().result;
|
|
for (let i = 0; i < hash_size; i++) {
|
|
if (h_block[i] !== h_block_verify[i])
|
|
throw new SecurityError('bad signature');
|
|
}
|
|
}
|
|
RSA_MGF1_generate(seed, length = 0) {
|
|
const hash_size = this.hash.HASH_SIZE;
|
|
// if ( length > (hash_size * 0x100000000) )
|
|
// throw new IllegalArgumentError("mask length too large");
|
|
const mask = new Uint8Array(length);
|
|
const counter = new Uint8Array(4);
|
|
const chunks = Math.ceil(length / hash_size);
|
|
for (let i = 0; i < chunks; i++) {
|
|
(counter[0] = i >>> 24), (counter[1] = (i >>> 16) & 255), (counter[2] = (i >>> 8) & 255), (counter[3] = i & 255);
|
|
const submask = mask.subarray(i * hash_size);
|
|
let chunk = this.hash
|
|
.reset()
|
|
.process(seed)
|
|
.process(counter)
|
|
.finish().result;
|
|
if (chunk.length > submask.length)
|
|
chunk = chunk.subarray(0, submask.length);
|
|
submask.set(chunk);
|
|
}
|
|
return mask;
|
|
}
|
|
}
|
|
export class RSA_PKCS1_v1_5 {
|
|
constructor(key, hash) {
|
|
this.rsa = new RSA(key);
|
|
this.hash = hash;
|
|
}
|
|
sign(data) {
|
|
if (!this.rsa.key) {
|
|
throw new IllegalStateError('no key is associated with the instance');
|
|
}
|
|
const prefix = getHashPrefix(this.hash);
|
|
const hash_size = this.hash.HASH_SIZE;
|
|
const t_len = prefix.length + hash_size;
|
|
const k = (this.rsa.key[0].bitLength + 7) >> 3;
|
|
if (k < t_len + 11) {
|
|
throw new Error('Message too long');
|
|
}
|
|
const m_hash = new Uint8Array(hash_size);
|
|
m_hash.set(this.hash.process(data).finish().result);
|
|
// EM = 0x00 || 0x01 || PS || 0x00 || T
|
|
const em = new Uint8Array(k);
|
|
let i = 0;
|
|
em[i++] = 0; // 0x00
|
|
em[i++] = 1; // 0x01
|
|
// PS
|
|
for (i; i < k - t_len - 1; i++) {
|
|
em[i] = 0xff;
|
|
}
|
|
em[i++] = 0;
|
|
em.set(prefix, i); // 0x00
|
|
// T
|
|
em.set(m_hash, em.length - hash_size);
|
|
this.rsa.decrypt(new BigNumber(em));
|
|
return this.rsa.result;
|
|
}
|
|
verify(signature, data) {
|
|
const prefix = getHashPrefix(this.hash);
|
|
const hash_size = this.hash.HASH_SIZE;
|
|
const t_len = prefix.length + hash_size;
|
|
const k = (this.rsa.key[0].bitLength + 7) >> 3;
|
|
if (k < t_len + 11) {
|
|
throw new SecurityError('Bad signature');
|
|
}
|
|
this.rsa.encrypt(new BigNumber(signature));
|
|
const m_hash = new Uint8Array(hash_size);
|
|
m_hash.set(this.hash.process(data).finish().result);
|
|
let res = 1;
|
|
// EM = 0x00 || 0x01 || PS || 0x00 || T
|
|
const decryptedSignature = this.rsa.result;
|
|
let i = 0;
|
|
res &= decryptedSignature[i++] === 0 ? 1 : 0; // 0x00
|
|
res &= decryptedSignature[i++] === 1 ? 1 : 0; // 0x01
|
|
// PS
|
|
for (i; i < k - t_len - 1; i++) {
|
|
res &= decryptedSignature[i] === 0xff ? 1 : 0;
|
|
}
|
|
res &= decryptedSignature[i++] === 0 ? 1 : 0; // 0x00
|
|
// T
|
|
let j = 0;
|
|
let n = i + prefix.length;
|
|
// prefix
|
|
for (i; i < n; i++) {
|
|
res &= decryptedSignature[i] === prefix[j++] ? 1 : 0;
|
|
}
|
|
j = 0;
|
|
n = i + m_hash.length;
|
|
// hash
|
|
for (i; i < n; i++) {
|
|
res &= decryptedSignature[i] === m_hash[j++] ? 1 : 0;
|
|
}
|
|
if (!res) {
|
|
throw new SecurityError('Bad signature');
|
|
}
|
|
}
|
|
}
|
|
const HASH_PREFIXES = {
|
|
sha1: new Uint8Array([0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14]),
|
|
sha256: new Uint8Array([
|
|
0x30,
|
|
0x31,
|
|
0x30,
|
|
0x0d,
|
|
0x06,
|
|
0x09,
|
|
0x60,
|
|
0x86,
|
|
0x48,
|
|
0x01,
|
|
0x65,
|
|
0x03,
|
|
0x04,
|
|
0x02,
|
|
0x01,
|
|
0x05,
|
|
0x00,
|
|
0x04,
|
|
0x20,
|
|
]),
|
|
sha384: new Uint8Array([
|
|
0x30,
|
|
0x41,
|
|
0x30,
|
|
0x0d,
|
|
0x06,
|
|
0x09,
|
|
0x60,
|
|
0x86,
|
|
0x48,
|
|
0x01,
|
|
0x65,
|
|
0x03,
|
|
0x04,
|
|
0x02,
|
|
0x02,
|
|
0x05,
|
|
0x00,
|
|
0x04,
|
|
0x30,
|
|
]),
|
|
sha512: new Uint8Array([
|
|
0x30,
|
|
0x51,
|
|
0x30,
|
|
0x0d,
|
|
0x06,
|
|
0x09,
|
|
0x60,
|
|
0x86,
|
|
0x48,
|
|
0x01,
|
|
0x65,
|
|
0x03,
|
|
0x04,
|
|
0x02,
|
|
0x03,
|
|
0x05,
|
|
0x00,
|
|
0x04,
|
|
0x40,
|
|
]),
|
|
};
|
|
function getHashPrefix(hash) {
|
|
const prefix = HASH_PREFIXES[hash.NAME];
|
|
if (!prefix) {
|
|
throw new Error("Cannot get hash prefix for hash algorithm '" + hash.NAME + "'");
|
|
}
|
|
return prefix;
|
|
}
|