import { Group, Field, Scalar, Circuit } from '../snarky.js'; import { Poseidon } from './hash.js'; import { PrivateKey, PublicKey } from './signature.js'; export { encrypt, decrypt }; type CipherText = { publicKey: Group; cipherText: Field[]; }; function encrypt(message: Field[], otherPublicKey: PublicKey) { // key exchange let privateKey = Circuit.witness(Scalar, () => Scalar.random()); let publicKey = Group.generator.scale(privateKey); let sharedSecret = otherPublicKey.toGroup().scale(privateKey); let sponge = new Poseidon.Sponge(); sponge.absorb(sharedSecret.x); // don't think we need y, that's enough entropy // encryption let cipherText = []; for (let i = 0; i < message.length; i++) { let keyStream = sponge.squeeze(); let encryptedChunk = message[i].add(keyStream); cipherText.push(encryptedChunk); // absorb for the auth tag (two at a time for saving permutations) if (i % 2 === 1) sponge.absorb(cipherText[i - 1]); if (i % 2 === 1 || i === message.length - 1) sponge.absorb(cipherText[i]); } // authentication tag let authenticationTag = sponge.squeeze(); cipherText.push(authenticationTag); return { publicKey, cipherText }; } function decrypt( { publicKey, cipherText }: CipherText, privateKey: PrivateKey ) { // key exchange let sharedSecret = publicKey.scale(privateKey.s); let sponge = new Poseidon.Sponge(); sponge.absorb(sharedSecret.x); let authenticationTag = cipherText.pop(); // decryption let message = []; for (let i = 0; i < cipherText.length; i++) { let keyStream = sponge.squeeze(); let messageChunk = cipherText[i].sub(keyStream); message.push(messageChunk); if (i % 2 === 1) sponge.absorb(cipherText[i - 1]); if (i % 2 === 1 || i === cipherText.length - 1) sponge.absorb(cipherText[i]); } // authentication tag sponge.squeeze().assertEquals(authenticationTag!); return message; }