UNPKG

o1js

Version:

TypeScript framework for zk-SNARKs and zkApps

97 lines 3.68 kB
import { Field, Scalar, Group } from '../wrapped.js'; import { Poseidon } from './poseidon.js'; import { Provable } from '../provable.js'; import { bytesToWord, wordToBytes } from '../gadgets/bit-slices.js'; import { Bytes } from '../bytes.js'; import { UInt8 } from '../int.js'; import { chunk } from '../../util/arrays.js'; export { encrypt, decrypt, encryptBytes, decryptBytes }; /** * Decrypts a {@link CipherText} using a {@link PrivateKey}. */ function decrypt({ publicKey, cipherText }, privateKey) { // key exchange const sharedSecret = publicKey.scale(privateKey.s); const sponge = new Poseidon.Sponge(); sponge.absorb(sharedSecret.x); const authenticationTag = cipherText.pop(); // decryption const message = []; for (let i = 0; i < cipherText.length; i++) { // absorb frame tag if (i === cipherText.length - 1) sponge.absorb(Field(1)); else sponge.absorb(Field(0)); const keyStream = sponge.squeeze(); const messageChunk = cipherText[i].sub(keyStream); // push the message to our final messages message.push(messageChunk); // absorb the cipher text chunk sponge.absorb(cipherText[i]); } // authentication tag sponge.squeeze().assertEquals(authenticationTag); return message; } /** * Public Key Encryption, encrypts Field elements using a {@link PublicKey}. */ function encrypt(message, otherPublicKey) { // key exchange const privateKey = Provable.witness(Scalar, () => Scalar.random()); const publicKey = Group.generator.scale(privateKey); const sharedSecret = otherPublicKey.toGroup().scale(privateKey); const sponge = new Poseidon.Sponge(); sponge.absorb(sharedSecret.x); // encryption const cipherText = []; for (let [n, chunk] of message.entries()) { // absorb frame bit if (n === message.length - 1) sponge.absorb(Field(1)); else sponge.absorb(Field(0)); const keyStream = sponge.squeeze(); const encryptedChunk = chunk.add(keyStream); cipherText.push(encryptedChunk); sponge.absorb(encryptedChunk); } // authentication tag const authenticationTag = sponge.squeeze(); cipherText.push(authenticationTag); return { publicKey, cipherText }; } /** * Public Key Encryption, encrypts Bytes using a {@link PublicKey}. */ function encryptBytes(message, otherPublicKey) { const bytes = message.bytes; const messageLength = bytes.length; // pad message to a multiple of 31 so they still fit into one field element const multipleOf = 31; const n = Math.ceil(messageLength / multipleOf) * multipleOf; // create the padding const padding = Array.from({ length: n - messageLength }, () => UInt8.from(0)); // convert message into chunks of 31 bytes const chunks = chunk(bytes.concat(padding), 31); // call into encryption() and convert chunk to field elements return { ...encrypt(chunks.map((chunk) => bytesToWord(chunk)), otherPublicKey), messageLength, }; } /** * Decrypts a {@link CipherText} using a {@link PrivateKey}. */ function decryptBytes(cipherText, privateKey) { // calculate padding const messageLength = cipherText.messageLength; const multipleOf = 31; const n = Math.ceil(messageLength / multipleOf) * multipleOf; // decrypt plain field elements and convert them into bytes const message = decrypt(cipherText, privateKey); const bytes = message.map((m) => wordToBytes(m, 31)); return Bytes.from(bytes.flat().slice(0, messageLength - n)); } //# sourceMappingURL=encryption.js.map