diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 951b8a70..9c7253ad 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9248,6 +9248,11 @@ "@sideway/pinpoint": "^2.0.0" } }, + "js-sha256": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", + "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 0e41091f..040d304a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -52,6 +52,7 @@ "i18next-browser-languagedetector": "^6.1.4", "i18next-http-backend": "^1.4.0", "i18next-xhr-backend": "^3.2.2", + "js-sha256": "^0.9.0", "material-ui-image": "^3.3.2", "openpgp": "^5.2.1", "react": "^18.0.0", diff --git a/frontend/src/utils/pgp.js b/frontend/src/utils/pgp.js new file mode 100644 index 00000000..889d7763 --- /dev/null +++ b/frontend/src/utils/pgp.js @@ -0,0 +1,66 @@ +import * as openpgp from 'openpgp/lightweight'; + +// Generate KeyPair. Private Key is encrypted with the highEntropyToken +export async function genKeys(highEntropyToken) { + + const keyPair = await openpgp.generateKey({ + type: 'ecc', // Type of the key, defaults to ECC + curve: 'curve25519', // ECC curve name, defaults to curve25519 + userIDs: [{name: 'RoboSats Avatar'}], + passphrase: highEntropyToken, + format: 'armored' + }) + + console.log(keyPair) + + const publicKeyArmored = keyPair.publicKey; + const privateKeyArmored = keyPair.privateKey; // encrypted private key + + return {publicKeyArmored: publicKeyArmored, privateKeyArmored: privateKeyArmored} +}; + +// Encrypt and sign a message +export async function encryptMessage(plainMessage, publicKeyArmored, privateKeyArmored, passphrase) { + + const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored }); + const privateKey = await openpgp.decryptKey({ + privateKey: await openpgp.readPrivateKey({ armoredKey: privateKeyArmored }), + passphrase + }); + + const encryptedMessage = await openpgp.encrypt({ + message: await openpgp.createMessage({ text: plainMessage }), // input as Message object, message must be string + encryptionKeys: publicKey, + signingKeys: privateKey // optional + }); + + return encryptedMessage; // '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----' +}; + +// Decrypt and check signature of a message +export async function decryptMessage(encryptedMessage, publicKeyArmored, privateKeyArmored, passphrase) { + + const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored }); + const privateKey = await openpgp.decryptKey({ + privateKey: await openpgp.readPrivateKey({ armoredKey: privateKeyArmored }), + passphrase + }); + + const message = await openpgp.readMessage({ + armoredMessage: encryptedMessage // parse armored message + }); + const { data: decrypted, signatures } = await openpgp.decrypt({ + message, + verificationKeys: publicKey, // optional + decryptionKeys: privateKey + }); + + // check signature validity (signed messages only) + try { + await signatures[0].verified; // throws on invalid signature + console.log('Signature is valid'); + return {decryptedMessage: decrypted, validSignature: true} + } catch (e) { + return {decryptedMessage: decrypted, validSignature: false}; + } +}; \ No newline at end of file