-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcrypto.js
119 lines (106 loc) · 3.87 KB
/
crypto.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
const { Buffer } = require('buffer/');
const forge = require('node-forge');
const elliptic = require('elliptic');
const jsSHA256 = require('js-sha256');
const jsrsasign = require('jsrsasign');
const ascii = require('./ascii');
// eslint-disable-next-line new-cap
const curve = new elliptic.ec('p384');
export const sha256 = (value, asHex = false) => {
const buffer = new TextEncoder().encode(value);
const sha = jsSHA256.sha256(buffer);
if (asHex) {
return sha.toString('hex');
}
return sha.toString();
};
// eslint-disable-next-line camelcase
const ecc_point_to_256_bit_key = async (point) => {
const value = point.getX().toString() + point.getY().toString();
const buffer = new TextEncoder().encode(value);
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
return Array.from(new Uint8Array(hashBuffer));
};
const generateRandomBytes = (length) => {
const array = new Uint16Array(length);
crypto.getRandomValues(array);
return array;
};
const encryptMsg = (msg, secretKey) => {
if (secretKey.length !== 32) {
throw new Error('Secret key must be 32 bytes (256 bits) long');
}
const iv = generateRandomBytes(16);
// encrypt some bytes using GCM mode
const cipher = forge.cipher.createCipher('AES-GCM', secretKey);
cipher.start({ iv });
cipher.update(forge.util.createBuffer(msg));
cipher.finish();
const encrypted = cipher.output;
const { tag } = cipher.mode;
// outputs encrypted hex
return {
ciphertext: forge.util.bytesToHex(encrypted.data),
iv: forge.util.bytesToHex(iv),
authTag: forge.util.bytesToHex(tag.data)
};
};
const encryptedDataToBase64Json = (encryptedMsg) => {
const key = curve.keyFromPublic(encryptedMsg.cipherTextPublicKey, 'hex');
const jsonObj = {
ciphertext: encryptedMsg.ciphertext,
nonce: encryptedMsg.nonce,
authTag: encryptedMsg.authTag,
x: key.getPublic().getX().toString(16),
y: key.getPublic().getY().toString(16)
};
const jsonString = JSON.stringify(jsonObj);
return btoa(jsonString);
};
// eslint-disable-next-line camelcase
const encrypt_ecc = async (msg, publicKey) => {
const cipherTextPrivateKey = generateRandomBytes(32);
const sharedEccKey = publicKey.getPublic().mul(cipherTextPrivateKey);
const secretKey = await ecc_point_to_256_bit_key(sharedEccKey);
const encrypted = encryptMsg(msg, secretKey);
const cipherTextPublicKey = curve.g.mul(cipherTextPrivateKey);
return {
ciphertext: encrypted.ciphertext,
secretKey,
nonce: encrypted.iv,
authTag: encrypted.authTag,
cipherTextPublicKey: cipherTextPublicKey.encode('hex')
};
};
const encrypt = async (pubKey, message) => {
const publicKeyPoint = curve.keyFromPublic(pubKey, 'hex');
const encryptedMessage = await encrypt_ecc(message, publicKeyPoint);
return encryptedDataToBase64Json(encryptedMessage);
};
const loadCertificate = (certificate) => {
const c = new jsrsasign.X509();
c.readCertPEM(certificate);
return c.getPublicKey().pubKeyHex;
};
export const encryptWithCertificate = async (message, certificate) => {
const pubKey = loadCertificate(certificate);
return encrypt(pubKey, message);
};
export const decryptWithPrivateKey = async (encryptedMessage, account) => {
try {
const data = Buffer.from(encryptedMessage, 'hex');
const structuredData = {
version: 'x25519-xsalsa20-poly1305',
ephemPublicKey: data.slice(0, 32).toString('base64'),
nonce: data.slice(32, 56).toString('base64'),
ciphertext: data.slice(56).toString('base64')
};
const ct = `0x${Buffer.from(JSON.stringify(structuredData), 'utf8').toString('hex')}`;
const decryptedMessage = await window.ethereum.request({ method: 'eth_decrypt', params: [ct, account] });
const decodedMessage = ascii.decode(decryptedMessage).toString();
return { success: true, data: decodedMessage };
} catch (error) {
console.log(error.message);
return { success: false };
}
};