[
  {
    "path": ".gitignore",
    "content": "/target\n\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"badcrypto\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\neyre = \"0.6.8\"\nhex = \"0.4.3\"\nrand = \"0.8.5\"\ncurv-kzen = { version = \"0.10.0\", features = [\"num-bigint\"], default-features = false }\n"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2022 Noah Citron\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "## Learning Cryptography in Public\nThis repository serves to document me learning cryptography. Each Rust module implements a different cryptographic protocol and will have it's own readme describing the protocol and any interesting learnings I bumped into along the way.\n\n## Warning\nDo not use this in production. It is called badcrypto for a reason. Some of these protocols may be implemented incorrectly. Even when I do implement them correctly, there may be side channel attacks. I have made no effort to ensure that these algorithms are constant-time, which means some of them may be susceptible to timing attacks. If you use this, you will get wrecked. I won't even feel bad, the repo is literally called badcrypto. Just to reiterate, don't use this. Don't roll your own crypto. Don't let me roll your crypto for you. \n\n## Contents\n- [ElGamal](./src/elgamal)\n- [ChaCha20](./src/chacha)\n"
  },
  {
    "path": "src/chacha/README.md",
    "content": "## ChaCha20\n\nChaCha20 is a stream cipher designed by Daniel J. Bernstein in 2008. It is an improvement on the Salsa20 cipher, which was also designed by Bernstein. The ChaCha cipher operates on a 64 byte block of data at a time. It uses a key, nonce, and block counter to generate a stream of pseudo-random bits, which are then XORed with the plaintext to produce the ciphertext.\n\n## Pseudo Random Generators (PRGs)\n\nThe ChaCha cipher uses a pseudo-random generator (PRG) to produce a stream of pseudo-random bits. It is a deterministic algorithm that takes the key, nonce, and block counter as input and produces a sequence of bits that appears random, but which can be reproduced given the same inputs.\n\nChaCha holds data in a 4x4 matrix consisting of 16 32 bit words containing the following contents:\n\n- The first row contains the constants \"0x61707865\", \"0x3320646e\", \"0x79622d32\", \"0x6b206574\" (ascii encoding of \"expand 32-byte k\")\n- The second row contains the first 4 words of the key\n- The third row contains the last 4 words of the key\n- The fourth row contains the block counter (1 word) and the nonce (3 words)\n\nMore succinctly:\n\n```math\n\\begin{bmatrix} c_1 & c_2 & c_3 & c_4 \\\\ k_1 & k_2 & k_3 & k_4 \\\\ k_5 & k_6 & k_7 & k_8 \\\\ b & n_1 & n_2 & n_3 \\end{bmatrix}\n```\n\nEach iteration of the PRG produces a single 512 bit block of random data. The bit stream produced by the PRG is generated as needed, with the block counter being incremented and the algorithm being rerun to produce each successive block.\n\nThe nonce value is typically chosen at random. It must be unique for each message that is encrypted, because reusing the same nonce is effectively creating a \"two-time pad\". With a two-time pad, the attacker has access to the ciphertext of two messages that were encrypted with the same random stream. An attacker can XOR these two ciphertexts together to cancel out the random stream and produce the XOR of the two plaintexts. If the plaintexts have any common words or patterns, the attacker may be able to recover parts of the message.\n\n## Encryption\n\nTo encrypt a message with the ChaCha cipher, the following steps are performed:\n\n1. The key and nonce are used to initialize the PRG.\n2. The PRG produces a stream of pseudo-random bits.\n3. The pseudo-random bits are XORed with the plaintext to produce the ciphertext.\n4. The sender sends both the ciphertext and the nonce to the receiver.\n\n## Decryption\n\nTo decrypt a message that has been encrypted with the ChaCha cipher, the following steps are performed:\n\n1. The key and nonce are used to initialize the PRG.\n2. The PRG produces the same stream of pseudo-random bits that was used to encrypt the message.\n3. The pseudo-random bits are XORed with the ciphertext to produce the plaintext.\n\n## How It Works\n\nThe PRG in the ChaCha cipher operates by applying a series of operations to the input matrix. The basic building block for these operations is called the quarter-round, which takes in four 32-bit integers, and scrambles them by applying a series of XOR, bitwise rotation, and overflowing addition between the values. Here is the code for the quarter-round function we use in our implementation.\n\n```rust\n/// Quarter-round function as defined by section 2.1 of RFC 8439\nfn quarter_round(a: &mut u32, b: &mut u32, c: &mut u32, d: &mut u32) {\n    *a = a.wrapping_add(*b);\n    *d ^= *a;\n    *d = rotl(*d, 16);\n\n    *c = c.wrapping_add(*d);\n    *b ^= *c;\n    *b = rotl(*b, 12);\n\n    *a = a.wrapping_add(*b);\n    *d ^= *a;\n    *d = rotl(*d, 8);\n\n    *c = c.wrapping_add(*d);\n    *b ^= *c;\n    *b = rotl(*b, 7);\n}\n\n/// Circular left shift. Panics if shift if greater than 32.\nfn rotl(value: u32, shift: u32) -> u32 {\n    value << shift | value >> (32 - shift)\n}\n```\n\nIn order to sufficiently scramble the input matrix, ChaCha20 applies the quarter-round on each column of the matrix and then on each diagonal. These two steps combined are called the double-round, and are performed a total of 10 times in ChaCha20. Finally, we perform a element-wise overflowing addition to combine the scrambled matrix with the original input matrix. This makes the output noninvertable. After these operations we concatenate all elements of the matrix to produce the 512 bit block of pseudo-random bits.\n\nThe constants used in the ChaCha cipher are a set of fixed values that are included in the input to the PRG. They were added to the cipher to reduce the amount of attacker-controlled input. This has the effect of removing certain correlations between inputs that are maintained in the outputs, increasing security.\n\nThe constants used in the ChaCha cipher are also known as \"nothing up my sleeve numbers.\" This phrase refers to the idea that the constants are chosen in a transparent and verifiable way, to ensure that they do not contain any hidden information or backdoors. In other words, the constants are chosen in a way that makes it clear that there is \"nothing up my sleeve\" – no hidden tricks or secrets.\n\n## Security\n\nThe security of the ChaCha cipher relies on the PRG being able to produce a stream of bits that is difficult for an attacker to predict or distinguish from a truly random stream. If the PRG is predictable, then an attacker may be able to determine the output of the PRG and recover the plaintext of a message.\n\nAs far as we know, the double-round technique for scrambling the matrix is secure. The state of the art attacks against ChaCha can currently only break the first 7 of 20 quarter-rounds.\n"
  },
  {
    "path": "src/chacha/mod.rs",
    "content": "use eyre::Result;\nuse rand::random;\n\n/// ChaCha20 encryption with 96 bit nonce and 32 bit counter\npub struct ChaCha20 {\n    key: Key,\n}\n\n/// ChaCha20 ciphertext\npub struct CipherText {\n    /// Variable length ciphertext bytes\n    pub c: Vec<u8>,\n    /// Nonce used to seed PRG state\n    pub nonce: Nonce,\n}\n\n/// 256 bit ChaCha private key\npub struct Key([u32; 8]);\n\n/// 96 bit nonce used in PRG\npub struct Nonce([u32; 3]);\n\n/// Internal ChaCha state representing the 4x4 u32 matrix\nstruct ChaChaState(Vec<u32>);\n\nimpl ChaCha20 {\n    /// Creates a new ChaCha cipher from a private key\n    pub fn new(key: Key) -> Self {\n        Self { key }\n    }\n\n    /// Encrypts a message\n    pub fn encrypt(&self, message: &str) -> CipherText {\n        let nonce = Nonce::new(random::<[u32; 3]>());\n        let message_bytes = message.as_bytes().to_vec();\n        let c = self.apply_cipher_with_nonce(&message_bytes, &nonce);\n\n        CipherText { c, nonce }\n    }\n\n    /// Decrypts a message. Errors if the message is not valid utf-8\n    pub fn decrypt(&self, ciphertext: &CipherText) -> Result<String> {\n        let message_bytes = self.apply_cipher_with_nonce(&ciphertext.c, &ciphertext.nonce);\n        Ok(String::from_utf8(message_bytes)?)\n    }\n\n    /// Applies cipher to the message with a given nonce. Can be used to encrypt\n    /// or decrypt. Nonce must be unique. If a nonce is reused, messages may be\n    /// decrypted.\n    fn apply_cipher_with_nonce(&self, message: &Vec<u8>, nonce: &Nonce) -> Vec<u8> {\n        let counter_start = 1u32;\n\n        let c = message\n            .chunks(64)\n            .enumerate()\n            .map(|(i, chunk)| {\n                let j = counter_start + i as u32;\n                let mut state = ChaChaState::new(&self.key, j, &nonce);\n                state.chacha_block();\n\n                let key_stream = state\n                    .0\n                    .iter()\n                    .flat_map(|n| n.to_le_bytes())\n                    .collect::<Vec<u8>>();\n\n                key_stream\n                    .iter()\n                    .zip(chunk)\n                    .map(|(key_byte, message_byte)| key_byte ^ message_byte)\n                    .collect::<Vec<u8>>()\n            })\n            .flatten()\n            .collect::<Vec<u8>>();\n\n        c\n    }\n}\n\nimpl ChaChaState {\n    /// Creates a new ChaChaState\n    pub fn new(key: &Key, counter: u32, nonce: &Nonce) -> Self {\n        let constants = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574];\n\n        let mut block = Vec::<u32>::new();\n        block.extend(constants);\n        block.extend(key.0);\n        block.push(counter);\n        block.extend(nonce.0);\n\n        Self(block)\n    }\n\n    /// Runs the ChaCha PRG function to generate a random block\n    pub fn chacha_block(&mut self) {\n        let original = self.0.clone();\n\n        for _ in 0..10 {\n            // column round\n            self.quarter_round_state(0, 4, 8, 12);\n            self.quarter_round_state(1, 5, 9, 13);\n            self.quarter_round_state(2, 6, 10, 14);\n            self.quarter_round_state(3, 7, 11, 15);\n\n            // diagonal round\n            self.quarter_round_state(0, 5, 10, 15);\n            self.quarter_round_state(1, 6, 11, 12);\n            self.quarter_round_state(2, 7, 8, 13);\n            self.quarter_round_state(3, 4, 9, 14);\n        }\n\n        for i in 0..16 {\n            self.0[i] = self.0[i].wrapping_add(original[i]);\n        }\n    }\n\n    /// Applies the quarter round function with the given state idices\n    fn quarter_round_state(&mut self, ai: usize, bi: usize, ci: usize, di: usize) {\n        let mut a = self.0[ai];\n        let mut b = self.0[bi];\n        let mut c = self.0[ci];\n        let mut d = self.0[di];\n\n        quarter_round(&mut a, &mut b, &mut c, &mut d);\n\n        self.0[ai] = a;\n        self.0[bi] = b;\n        self.0[ci] = c;\n        self.0[di] = d;\n    }\n}\n\nimpl Key {\n    /// Create a new Key. Errors if the string is not a 32 byte hex value.\n    pub fn new(s: &str) -> Result<Self> {\n        let key: [u32; 8] = hex::decode(s)\n            .map_err(|_| eyre::eyre!(\"cannot parse key\"))?\n            .chunks(4)\n            .map(|chunk| Ok(u32::from_le_bytes(chunk.try_into()?)))\n            .collect::<Result<Vec<u32>>>()\n            .map_err(|_| eyre::eyre!(\"cannot parse key\"))?\n            .try_into()\n            .map_err(|_| eyre::eyre!(\"cannot parse key\"))?;\n\n        Ok(Self(key))\n    }\n}\n\nimpl Nonce {\n    /// Create a new Nonce from a string. Fails if the string is not a 12 byte hex value.\n    pub fn from_str(s: &str) -> Result<Self> {\n        let nonce: [u32; 3] = hex::decode(s)\n            .map_err(|_| eyre::eyre!(\"cannot parse nonce\"))?\n            .chunks(4)\n            .map(|chunk| Ok(u32::from_le_bytes(chunk.try_into()?)))\n            .collect::<Result<Vec<u32>>>()\n            .map_err(|_| eyre::eyre!(\"cannot parse nonce\"))?\n            .try_into()\n            .map_err(|_| eyre::eyre!(\"cannot parse nonce\"))?;\n\n        Ok(Self(nonce))\n    }\n\n    /// Creates a new nonce from a fixed length u32 array\n    pub fn new(n: [u32; 3]) -> Self {\n        Self(n)\n    }\n}\n\n/// Quarter-round function as defined by section 2.1 of RFC 8439\nfn quarter_round(a: &mut u32, b: &mut u32, c: &mut u32, d: &mut u32) {\n    *a = a.wrapping_add(*b);\n    *d ^= *a;\n    *d = rotl(*d, 16);\n\n    *c = c.wrapping_add(*d);\n    *b ^= *c;\n    *b = rotl(*b, 12);\n\n    *a = a.wrapping_add(*b);\n    *d ^= *a;\n    *d = rotl(*d, 8);\n\n    *c = c.wrapping_add(*d);\n    *b ^= *c;\n    *b = rotl(*b, 7);\n}\n\n/// Circular left shift. Panics if shift if greater than 32.\nfn rotl(value: u32, shift: u32) -> u32 {\n    value << shift | value >> (32 - shift)\n}\n\n#[test]\nfn test_full_cycle() {\n    let message = \"Hello, World!\";\n    let key = Key::new(&hex::encode(random::<[u8; 32]>())).unwrap();\n\n    let cipher = ChaCha20::new(key);\n    let ciphertext = cipher.encrypt(message);\n    let decrypted_message = cipher.decrypt(&ciphertext).unwrap();\n\n    assert_eq!(decrypted_message, message);\n}\n\n#[test]\nfn test_apply_cipher() {\n    // test vector from RFC 8439\n    let key = Key::new(\"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\").unwrap();\n    let nonce = \"000000000000004a00000000\";\n    let message = \"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.\";\n\n    let nonce = Nonce::from_str(nonce).unwrap();\n\n    let k = ChaCha20::new(key);\n\n    let c = k.apply_cipher_with_nonce(&message.as_bytes().to_vec(), &nonce);\n\n    let expected = \"6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b40b8eedf2785e42874d\";\n\n    assert_eq!(c, hex::decode(expected).unwrap());\n}\n\n#[test]\nfn test_chacha_block() {\n    // test vector from RFC 8439\n    let key = Key::new(\"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\").unwrap();\n    let nonce = Nonce::from_str(\"000000090000004a00000000\").unwrap();\n    let counter = 1;\n\n    let mut state = ChaChaState::new(&key, counter, &nonce);\n    state.chacha_block();\n\n    let block = hex::encode(\n        state\n            .0\n            .iter()\n            .flat_map(|chunk| chunk.to_le_bytes())\n            .collect::<Vec<_>>(),\n    );\n\n    let expected_block = \"10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4ed2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e\";\n\n    assert_eq!(block, expected_block);\n}\n\n#[test]\nfn test_quarter_round() {\n    // test vector from RFC 8439\n    let mut a = 0x11111111;\n    let mut b = 0x01020304;\n    let mut c = 0x9b8d6f43;\n    let mut d = 0x01234567;\n\n    quarter_round(&mut a, &mut b, &mut c, &mut d);\n\n    assert_eq!(a, 0xea2a92f4);\n    assert_eq!(b, 0xcb1cf8ce);\n    assert_eq!(c, 0x4581472e);\n    assert_eq!(d, 0x5881c4bb);\n}\n"
  },
  {
    "path": "src/elgamal/README.md",
    "content": "## ElGamal\nElGamal encryption is a public-key encryption scheme that is based on the difficulty of finding discrete logarithms. It is less commonly used as it is less secure than other methods, and is vulnerable to chosen ciphertext attacks. It does however have some interesting properties, such as multiplicative homomorphism.\n\nThe elliptic curve variant of ElGamal encryption uses elliptic curve cryptography (ECC) instead of the traditional integer-based modular arithmetic used in the original scheme.\n\nOne advantage of the elliptic curve variant of ElGamal encryption is that it is more efficient than the original scheme, due to the faster point operations in ECC. It is also more secure, since the underlying elliptic curve is more difficult to break than modular arithmetic. However, the security of the elliptic curve variant of ElGamal encryption depends on the choice of the elliptic curve and the size of the key, so it is important to carefully select these parameters to ensure the desired level of security.\n\n## Key Generation\nTo generate a key, first select a random number for the secret key $s$. The public key $P$ is calculated by multiplying the secret key by our curve's generator point.\n\n$$ P = s \\cdot G $$\n\n## Encryption\nElGamal encryption can only encrypt messages that are valid points on the elliptic curve. Later in this document we will describe a mechanism to embed an arbitrary integer inside of a curve point to create a more practical encryption scheme. We are going to encrypt some point $M$ using public key $P$. The ciphertext in ElGamal is actually two curve points, denoted as $C_1$ and $C_2$. First, we must generate some random scalar $r$. We can then encrypt the point using the function below.\n\n$$ \\mathrm{enc_{P}}(M) = (C_1, C_2)= (r \\cdot G, r \\cdot P + M) $$\n\n## Decryption\nTo recover the encrypted point using the secret key, use the below function.\n\n$$ \\mathrm{dec_{s}}(C_1, C_2) = C_2 - s \\cdot C_1 $$\n\n## How it Works\nWe can actually show that ElGamal decryption is in fact the inverse of encryption with simple algebra.\n\n$$ \\mathrm{dec_{s}}(\\mathrm{enc_{P}}(M)) = (r \\cdot P + M) - (s \\cdot r \\cdot G) = (r \\cdot s \\cdot G + M) - (s \\cdot r \\cdot G) = M $$\n\nSo now we have some intuition as to how recovering the original point works. However, how is secrecy preserved? This property relies on something called the discrete log problem for elliptical curves. We will only discuss the discrete log problem for elliptical curves here, but it originally is used in the context of prime fields.\n\nThe discrete log problem (for elliptical curves) is to find some scalar $k$ such that multiplying point $P$ times $k$ yields point $Q$.\n\n$$ P \\cdot k = Q $$\n\nIt turns out that given $P$ and $Q$, finding $k$ is extremely difficult.\n\nIf we take a look at our $\\mathrm{enc}$ function, you can see that even though our adversaries know $C_1$ and $G$, it will still be computationally difficult to recover $r$. Since $r$ cannot be recovered, there is no way to subtract $r \\cdot P$ from $C_2$ to yield $M$.\n\nFortunately for the receiver of the message, $r \\cdot P$ can easily be recovered by multiplying the secret key $s$ by $C_1$. Given that $C_2$ is just equal to $r \\cdot P + M$, we just need to subtract our recovered $r \\cdot P$ from $C_2$ to yield the original message $M$.\n\n## Encrypting Arbitrary Messages\nWe noted earlier that the message $M$ to be encrypted must be a valid point on the curve. However, we often want to send messages that contain arbitrary data. To do this, we need a way to convert any integer $n$ into a curve point $P$. This method is based on [this paper](https://arxiv.org/pdf/1707.04892.pdf) by Ahmad Steef, A. Alkhatib, and M. N. Shamma. It is actually quite simple.\n\nSelect some integer value $k$. The larger $k$ is, the greater the likelyhood we can find a good embedding for $n$. However, as $k$ increases in size, the maximum size $n$ that can be embedded in a single point decreases.\n\nTo calculate the x value of our point, use the below equation with $i$ starting at 0.\n\n$$ x = k \\cdot n + i $$\n\nThen, attempt to calculate the $y$ coordinate on the curve that corresponds to the $x$ coordinate. This may not be possible since not every $x$ value is on the curve. If there is no valid $y$, simply increment $i$ and try again.\n\nTo recover the message $n$ from the point's $x$ coordinate, use the equation below.\n\n$$ n = \\frac{x}{k} $$\n\nAs long as $i < k$, this method should decode the correct message. If $i \\geq k$, then it is impossible to differentiate between $n$ and $n + 1$. This is why increasing the size of $k$ lowers the failure rate.\n"
  },
  {
    "path": "src/elgamal/mod.rs",
    "content": "use std::str::from_utf8;\n\nuse curv::{\n    arithmetic::{BasicOps, Converter, Modulo, Primes, Zero},\n    elliptic::curves::{Point, Scalar, Secp256k1},\n    BigInt,\n};\nuse eyre::Result;\n\n/// An ElGamal public key using the secp256k1 curve\npub struct PubKey {\n    point: Point<Secp256k1>,\n}\n\n/// An ElGamal private key using the secp256k1 curve\npub struct SecretKey {\n    secret: Scalar<Secp256k1>,\n}\n\n/// An encypted ciphertext\n#[derive(Debug)]\npub struct CipherText {\n    c1: Point<Secp256k1>,\n    c2: Point<Secp256k1>,\n}\n\nimpl SecretKey {\n    /// Generates a new private key using a random seed\n    pub fn new() -> Self {\n        Self {\n            secret: Scalar::random(),\n        }\n    }\n\n    /// Decryps a CipherText into a String\n    pub fn decrypt_point(&self, c: &CipherText) -> Point<Secp256k1> {\n        let s = self.secret.clone() * &c.c1;\n        &c.c2 - s\n    }\n\n    /// Decrypts a CipherText into a String. Fails if point does not\n    /// decrypt into a valid utf-8 string.\n    pub fn decrypt_message(&self, c: &CipherText) -> Result<String> {\n        let point = self.decrypt_point(c);\n        let bytes = unembed(&point).to_bytes();\n        from_utf8(&bytes)\n            .map_err(|_| eyre::eyre!(\"cannot decode point\"))\n            .map(|s| s.to_string())\n    }\n}\n\nimpl PubKey {\n    /// Encypts a Point into a CipherText\n    pub fn encrypt_point(&self, m: &Point<Secp256k1>) -> CipherText {\n        let r = Scalar::random();\n        let c1 = r.clone() * Point::generator();\n        let c2 = r * &self.point + m;\n\n        CipherText { c1, c2 }\n    }\n\n    /// Encypts a message into a CipgerText. Fails if message is\n    /// too long to fit into the curve point.\n    pub fn encrypt_message(&self, m: &str) -> Result<CipherText> {\n        let m = embed(&BigInt::from_bytes(m.as_bytes()))?;\n        Ok(self.encrypt_point(&m))\n    }\n}\n\nimpl From<&SecretKey> for PubKey {\n    /// Converts a private key into a public key\n    fn from(sk: &SecretKey) -> Self {\n        let point = sk.secret.clone() * Point::generator();\n        Self { point }\n    }\n}\n\n/// Encodes a BigInt message as a Point. Fails if message is too long\n/// or if no valid encoding can be found the message.\n/// Method based on https://arxiv.org/pdf/1707.04892.pdf\nfn embed(m: &BigInt) -> Result<Point<Secp256k1>> {\n    let k = 30;\n\n    let n = BigInt::from_hex(\"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F\")\n        .unwrap();\n\n    if (m + 1) * k >= n {\n        eyre::bail!(\"message too long\");\n    }\n\n    for i in 0..k {\n        let x = m * k + i;\n        let y_squared = (x.pow(3) + 7) % n.clone();\n        let y = sqrt_mod_p(&y_squared, &n)?;\n\n        if let Some(y) = y {\n            return Point::from_coords(&x, &y).map_err(|_| eyre::eyre!(\"cannot embed message\"));\n        }\n    }\n\n    Err(eyre::eyre!(\"cannot embed message\"))\n}\n\n/// Decodes a Point into its origianl BigInt message\n/// Method based on https://arxiv.org/pdf/1707.04892.pdf\nfn unembed(p: &Point<Secp256k1>) -> BigInt {\n    let k = 30;\n    p.x_coord().unwrap() / BigInt::from(k)\n}\n\n/// Tonelli-Shanks algorithm for finding a square root in a prime field.\n/// If there is no square root None is returned. Fails if p is composite.\nfn sqrt_mod_p(n: &BigInt, p: &BigInt) -> Result<Option<BigInt>> {\n    let ls = |x: &BigInt| -> BigInt { BigInt::mod_pow(x, &((p - 1) / 2), p) };\n\n    if !Primes::is_probable_prime(p, 64) {\n        eyre::bail!(\"p is composite\")\n    }\n\n    if ls(n) != BigInt::from(1) {\n        return Ok(None);\n    }\n\n    let mut q = p - 1;\n    let mut s = BigInt::zero();\n\n    while &q & BigInt::from(1) == BigInt::zero() {\n        s += 1;\n        q >>= 1;\n    }\n\n    if s == BigInt::from(1) {\n        return Ok(Some(BigInt::mod_pow(n, &((p + 1) / 4), p)));\n    }\n\n    let mut z = BigInt::from(2);\n    while ls(&z) != p - 1 {\n        z += 1;\n    }\n\n    let mut c = BigInt::mod_pow(&z, &q, p);\n    let mut r = BigInt::mod_pow(n, &((&q + 1) / 2), p);\n    let mut t = BigInt::mod_pow(n, &q, p);\n    let mut m = s;\n\n    loop {\n        if t == BigInt::from(1) {\n            return Ok(Some(r));\n        }\n\n        let mut i = BigInt::zero();\n        let mut z = t.clone();\n        while z != BigInt::from(1) && i < &m - 1 {\n            z = z.pow(2) % p;\n            i += 1;\n        }\n\n        let mut b = c.clone();\n        let e = &m - &i - 1;\n        while e > BigInt::zero() {\n            b = b.pow(2) % p;\n        }\n\n        r = r * &b % p;\n        c = &b * &b % p;\n        t = t * &c % p;\n        m = i;\n    }\n}\n\n#[test]\nfn test_encrypt_message() {\n    let sk = SecretKey::new();\n    let pk = PubKey::from(&sk);\n\n    let message = \"Hello, World!\".to_string();\n\n    let enc = pk.encrypt_message(&message).unwrap();\n    let message_dec = sk.decrypt_message(&enc).unwrap();\n\n    assert_eq!(message_dec, message);\n}\n\n#[test]\nfn test_encrypt_point() {\n    let sk = SecretKey::new();\n    let pk = PubKey::from(&sk);\n\n    let point = Scalar::from(5) * Point::generator();\n\n    let enc = pk.encrypt_point(&point);\n    let point_dec = sk.decrypt_point(&enc);\n\n    assert_eq!(point_dec, point);\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "pub mod chacha;\npub mod elgamal;\n"
  }
]