├── .gitignore ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── issues.yml │ └── autocloser.yml ├── src ├── crypto │ ├── mod.rs │ ├── util.rs │ ├── cryptoutil.rs │ ├── ed25519.rs │ ├── blake2b.rs │ ├── sha512.rs │ └── curve25519.rs ├── keynum.rs ├── signature.rs ├── constants.rs ├── signature_bones.rs ├── helpers.rs ├── errors.rs ├── keypair.rs ├── signature_box.rs ├── public_key.rs ├── lib.rs ├── tests.rs └── secret_key.rs ├── Cargo.toml ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | *.key 4 | *.pub 5 | *.rsign 6 | *.minisig 7 | Cargo.lock 8 | testfile.txt 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v5 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /src/crypto/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | clippy::needless_range_loop, 3 | clippy::many_single_char_names, 4 | clippy::unreadable_literal, 5 | clippy::let_and_return, 6 | clippy::needless_lifetimes, 7 | clippy::cast_lossless, 8 | clippy::suspicious_arithmetic_impl, 9 | clippy::identity_op 10 | )] 11 | mod cryptoutil; 12 | mod curve25519; 13 | mod sha512; 14 | 15 | pub mod blake2b; 16 | pub mod ed25519; 17 | pub mod util; 18 | -------------------------------------------------------------------------------- /src/crypto/util.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 or the MIT license 3 | // , at your 4 | // option. This file may not be copied, modified, or distributed 5 | // except according to those terms. 6 | 7 | #[inline(never)] 8 | pub fn fixed_time_eq(lhs: &[u8], rhs: &[u8]) -> bool { 9 | if lhs.len() != rhs.len() { 10 | false 11 | } else { 12 | lhs.iter().zip(rhs.iter()).fold(0, |z, (x, y)| z | (x ^ y)) == 0 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/issues.yml: -------------------------------------------------------------------------------- 1 | name: Close inactive issues 2 | on: 3 | schedule: 4 | - cron: "30 1 * * *" 5 | 6 | jobs: 7 | close-issues: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | pull-requests: write 12 | steps: 13 | - uses: actions/stale@v9 14 | with: 15 | stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." 16 | close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." 17 | repo-token: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/autocloser.yml: -------------------------------------------------------------------------------- 1 | name: Autocloser 2 | on: [issues] 3 | jobs: 4 | autoclose: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Autoclose issues that did not follow issue template 8 | uses: roots/issue-closer@v1.2 9 | with: 10 | repo-token: ${{ secrets.GITHUB_TOKEN }} 11 | issue-close-message: "This issue was automatically closed because it did not follow the issue template. We use the issue tracker exclusively for bug reports and feature additions that have been previously discussed. However, this issue appears to be a support request. Please use the discussion forums for support requests." 12 | issue-pattern: ".*(do we replicate the issue|Expected behavior|raised as discussion|# Impact).*" 13 | -------------------------------------------------------------------------------- /src/keynum.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use std::fmt::{self, Formatter}; 3 | 4 | use crate::constants::*; 5 | use crate::crypto::util::fixed_time_eq; 6 | 7 | #[derive(Debug, Clone)] 8 | pub(crate) struct KeynumPK { 9 | pub(crate) keynum: [u8; KEYNUM_BYTES], 10 | pub(crate) pk: [u8; PUBLICKEY_BYTES], 11 | } 12 | 13 | impl cmp::PartialEq for KeynumPK { 14 | fn eq(&self, other: &KeynumPK) -> bool { 15 | self.keynum == other.keynum && fixed_time_eq(&self.pk, &other.pk) 16 | } 17 | } 18 | impl cmp::Eq for KeynumPK {} 19 | 20 | #[derive(Clone)] 21 | pub(crate) struct KeynumSK { 22 | pub keynum: [u8; KEYNUM_BYTES], 23 | pub sk: [u8; SECRETKEY_BYTES], 24 | pub chk: [u8; CHK_BYTES], 25 | } 26 | 27 | #[allow(clippy::len_without_is_empty, dead_code)] 28 | impl KeynumSK { 29 | pub fn len(&self) -> usize { 30 | std::mem::size_of::() 31 | } 32 | } 33 | 34 | impl fmt::Debug for KeynumSK { 35 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 36 | for byte in self.sk.iter() { 37 | write!(f, "{byte:x}")? 38 | } 39 | Ok(()) 40 | } 41 | } 42 | 43 | impl cmp::PartialEq for KeynumSK { 44 | fn eq(&self, other: &KeynumSK) -> bool { 45 | self.keynum == other.keynum && fixed_time_eq(&self.sk, &other.sk) 46 | } 47 | } 48 | impl cmp::Eq for KeynumSK {} 49 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minisign" 3 | version = "0.8.0" 4 | authors = [ 5 | "Frank Denis ", 6 | "Daniel Rangel ", 7 | ] 8 | description = "A crate to sign files and verify signatures." 9 | edition = "2018" 10 | license = "MIT" 11 | readme = "README.md" 12 | categories = ["cryptography"] 13 | keywords = ["command-line", "sign", "publickey", "cryptography", "minisign"] 14 | repository = "https://github.com/jedisct1/rust-minisign" 15 | homepage = "https://github.com/jedisct1/rust-minisign" 16 | 17 | [target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dependencies] 18 | getrandom = { version = "0.3", optional = false, default-features = false, features = ["wasm_js"] } 19 | 20 | [target.'cfg(not(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown")))'.dependencies] 21 | getrandom = { version = "0.3", optional = false, default-features = false } 22 | 23 | [dependencies] 24 | scrypt = { version = "0.11.0", default-features = false } 25 | ct-codecs = "1.1.6" 26 | 27 | [target.'cfg(any(windows, unix))'.dependencies] 28 | rpassword = "7.4.0" 29 | 30 | [profile.dev] 31 | lto = "thin" 32 | opt-level = 3 33 | 34 | [profile.test] 35 | lto = "thin" 36 | opt-level = 3 37 | 38 | [profile.bench] 39 | lto = true 40 | opt-level = 3 41 | 42 | [profile.release] 43 | lto = true 44 | panic = "abort" 45 | opt-level = 3 46 | -------------------------------------------------------------------------------- /src/signature.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Cursor, Read}; 2 | 3 | use ct_codecs::{Base64, Encoder}; 4 | 5 | use crate::constants::*; 6 | use crate::Result; 7 | 8 | #[derive(Clone)] 9 | pub(crate) struct Signature { 10 | pub sig_alg: [u8; TWOBYTES], 11 | pub keynum: [u8; KEYNUM_BYTES], 12 | pub sig: [u8; SIGNATURE_BYTES], 13 | } 14 | 15 | impl Signature { 16 | pub const BYTES: usize = 2 + KEYNUM_BYTES + SIGNATURE_BYTES; 17 | 18 | pub fn to_bytes(&self) -> Vec { 19 | let mut v: Vec = Vec::with_capacity(Self::BYTES); 20 | v.extend(self.sig_alg); 21 | v.extend(self.keynum); 22 | v.extend(&self.sig[..]); 23 | debug_assert_eq!(v.len(), Self::BYTES); 24 | v 25 | } 26 | 27 | pub fn from_bytes(bytes_buf: &[u8]) -> Result { 28 | let mut buf = Cursor::new(bytes_buf); 29 | let mut sig_alg = [0u8; 2]; 30 | let mut keynum = [0u8; KEYNUM_BYTES]; 31 | let mut sig = [0u8; SIGNATURE_BYTES]; 32 | buf.read_exact(&mut sig_alg)?; 33 | buf.read_exact(&mut keynum)?; 34 | buf.read_exact(&mut sig)?; 35 | debug_assert_eq!(buf.position() as usize, Self::BYTES); 36 | Ok(Signature { 37 | sig_alg, 38 | keynum, 39 | sig, 40 | }) 41 | } 42 | } 43 | 44 | impl Default for Signature { 45 | fn default() -> Self { 46 | Signature { 47 | sig_alg: [0u8; TWOBYTES], 48 | keynum: [0u8; KEYNUM_BYTES], 49 | sig: [0u8; SIGNATURE_BYTES], 50 | } 51 | } 52 | } 53 | 54 | impl std::fmt::Display for Signature { 55 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 56 | let encoded = Base64::encode_to_string(self.to_bytes().as_slice()).unwrap(); 57 | write!(f, "{}", encoded) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/constants.rs: -------------------------------------------------------------------------------- 1 | /// The default untrusted comment. 2 | pub const DEFAULT_COMMENT: &str = "signature from rsign secret key"; 3 | 4 | /// The default environment variable for the directory of the `rsign` tool. 5 | pub const SIG_DEFAULT_CONFIG_DIR_ENV_VAR: &str = "RSIGN_CONFIG_DIR"; 6 | 7 | /// The default configuration directory of the `rsign` tool. 8 | pub const SIG_DEFAULT_CONFIG_DIR: &str = ".rsign"; 9 | 10 | /// The default file name for the public key. 11 | pub const SIG_DEFAULT_PKFILE: &str = "rsign.pub"; 12 | 13 | /// The default file name for the secret key. 14 | pub const SIG_DEFAULT_SKFILE: &str = "rsign.key"; 15 | 16 | /// The default suffix for signatures. 17 | pub const SIG_SUFFIX: &str = ".minisig"; 18 | 19 | pub(crate) const CHK_ALG: [u8; 2] = *b"B2"; 20 | pub(crate) const CHK_BYTES: usize = 32; 21 | pub(crate) const COMMENT_PREFIX: &str = "untrusted comment: "; 22 | pub(crate) const KDF_ALG: [u8; 2] = *b"Sc"; 23 | pub(crate) const KDF_NONE: [u8; 2] = [0, 0]; 24 | pub(crate) const KDF_SALTBYTES: usize = 32; 25 | pub(crate) const KEYNUM_BYTES: usize = 8; 26 | pub(crate) const MEMLIMIT: usize = 33_554_432; 27 | pub(crate) const OPSLIMIT: u64 = 1_048_576; 28 | pub(crate) const MEMLIMIT_MAX: usize = 1_073_741_824; 29 | pub(crate) const N_LOG2_MAX: u8 = 20; 30 | pub(crate) const PASSWORD_MAXBYTES: usize = 1024; 31 | pub(crate) const PK_B64_ENCODED_LEN: usize = 56; 32 | pub(crate) const PREHASH_BYTES: usize = 64; 33 | pub(crate) const PUBLICKEY_BYTES: usize = 32; 34 | pub(crate) const SECRETKEY_BYTES: usize = 64; 35 | pub(crate) const SECRETKEY_DEFAULT_COMMENT: &str = "rsign encrypted secret key"; 36 | pub(crate) const SIGALG_PREHASHED: [u8; 2] = *b"ED"; 37 | pub(crate) const SIGALG: [u8; 2] = *b"Ed"; 38 | pub(crate) const SIGNATURE_BYTES: usize = 64; 39 | pub(crate) const TRUSTED_COMMENT_PREFIX_LEN: usize = 17; 40 | pub(crate) const TRUSTED_COMMENT_PREFIX: &str = "trusted comment: "; 41 | pub(crate) const TWOBYTES: usize = 2; 42 | -------------------------------------------------------------------------------- /src/signature_bones.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::*; 2 | use crate::errors::*; 3 | use crate::signature::*; 4 | use crate::signature_box::*; 5 | 6 | /// A trimmed-down signature, without any comments section, with binary 7 | /// serialization only 8 | #[derive(Clone)] 9 | pub struct SignatureBones { 10 | pub(crate) signature: Signature, 11 | pub(crate) is_prehashed: bool, 12 | } 13 | 14 | impl SignatureBones { 15 | /// Size of a minimal signature in bytes 16 | pub const BYTES: usize = Signature::BYTES; 17 | 18 | /// Returns `true` if the signed data was pre-hashed. 19 | pub fn is_prehashed(&self) -> bool { 20 | self.is_prehashed 21 | } 22 | 23 | /// Create a new `SignatureBones` from a &[u8]. 24 | pub fn from_bytes(bytes: &[u8]) -> Result { 25 | let signature = Signature::from_bytes(bytes)?; 26 | let is_prehashed = match signature.sig_alg { 27 | SIGALG => false, 28 | SIGALG_PREHASHED => true, 29 | _ => { 30 | return Err(PError::new( 31 | ErrorKind::Verify, 32 | "Unsupported signature algorithm".to_string(), 33 | )) 34 | } 35 | }; 36 | Ok(SignatureBones { 37 | signature, 38 | is_prehashed, 39 | }) 40 | } 41 | 42 | /// Return a `SignatureBones` as bytes, for storage. 43 | pub fn to_bytes(&self) -> Vec { 44 | self.signature.to_bytes() 45 | } 46 | } 47 | 48 | impl From for SignatureBox { 49 | fn from(signature: SignatureBones) -> SignatureBox { 50 | let is_prehashed = signature.is_prehashed(); 51 | SignatureBox { 52 | untrusted_comment: String::new(), 53 | signature: signature.signature, 54 | sig_and_trusted_comment: None, 55 | global_sig: None, 56 | is_prehashed, 57 | } 58 | } 59 | } 60 | 61 | impl From for SignatureBones { 62 | fn from(signature: SignatureBox) -> SignatureBones { 63 | let is_prehashed = signature.is_prehashed(); 64 | SignatureBones { 65 | signature: signature.signature, 66 | is_prehashed, 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019-2025 Frank Denis 2 | Copyright (c) 2017 Daniel Rangel 3 | 4 | Permission is hereby granted, free of charge, to any 5 | person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the 7 | Software without restriction, including without 8 | limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software 11 | is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice 15 | shall be included in all copies or substantial portions 16 | of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 19 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 20 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 21 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 22 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 25 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 | DEALINGS IN THE SOFTWARE. 27 | 28 | -- 29 | 30 | Code in the src/crypto folder is derived from the rust-crypto project: 31 | https://github.com/DaGenix/rust-crypto 32 | 33 | Original ISC license follows: 34 | 35 | Copyright (c) 2006-2009 Graydon Hoare 36 | Copyright (c) 2009-2013 Mozilla Foundation 37 | 38 | Permission is hereby granted, free of charge, to any 39 | person obtaining a copy of this software and associated 40 | documentation files (the "Software"), to deal in the 41 | Software without restriction, including without 42 | limitation the rights to use, copy, modify, merge, 43 | publish, distribute, sublicense, and/or sell copies of 44 | the Software, and to permit persons to whom the Software 45 | is furnished to do so, subject to the following 46 | conditions: 47 | 48 | The above copyright notice and this permission notice 49 | shall be included in all copies or substantial portions 50 | of the Software. 51 | 52 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 53 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 54 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 55 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 56 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 57 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 58 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 59 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 60 | DEALINGS IN THE SOFTWARE. 61 | -------------------------------------------------------------------------------- /src/helpers.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use std::time::{SystemTime, UNIX_EPOCH}; 3 | 4 | #[cfg(any(windows, unix))] 5 | use rpassword::prompt_password; 6 | 7 | use crate::constants::*; 8 | use crate::errors::*; 9 | 10 | #[cfg(not(any(windows, unix)))] 11 | fn prompt_password(prompt: &str) -> Result { 12 | use std::io::{stdin, stdout, Write}; 13 | 14 | stdout().write_all(prompt.as_bytes())?; 15 | stdout().flush()?; 16 | let mut password = String::new(); 17 | stdin().read_line(&mut password)?; 18 | Ok(password) 19 | } 20 | 21 | pub fn store_u64_le(x: u64) -> [u8; 8] { 22 | let b1: u8 = (x & 0xff) as u8; 23 | let b2: u8 = ((x >> 8) & 0xff) as u8; 24 | let b3: u8 = ((x >> 16) & 0xff) as u8; 25 | let b4: u8 = ((x >> 24) & 0xff) as u8; 26 | let b5: u8 = ((x >> 32) & 0xff) as u8; 27 | let b6: u8 = ((x >> 40) & 0xff) as u8; 28 | let b7: u8 = ((x >> 48) & 0xff) as u8; 29 | let b8: u8 = ((x >> 56) & 0xff) as u8; 30 | [b1, b2, b3, b4, b5, b6, b7, b8] 31 | } 32 | 33 | #[allow(clippy::cast_lossless)] 34 | pub fn load_u64_le(x: &[u8]) -> u64 { 35 | (x[0] as u64) 36 | | (x[1] as u64) << 8 37 | | (x[2] as u64) << 16 38 | | (x[3] as u64) << 24 39 | | (x[4] as u64) << 32 40 | | (x[5] as u64) << 40 41 | | (x[6] as u64) << 48 42 | | (x[7] as u64) << 56 43 | } 44 | 45 | pub fn raw_scrypt_params(memlimit: usize, opslimit: u64, n_log2_max: u8) -> Result { 46 | let opslimit = cmp::max(32768, opslimit); 47 | let mut n_log2 = 1u8; 48 | let r = 8u32; 49 | let p; 50 | if opslimit < (memlimit / 32) as u64 { 51 | p = 1; 52 | let maxn = opslimit / (u64::from(r) * 4); 53 | while n_log2 < 63 { 54 | if 1u64 << n_log2 > maxn / 2 { 55 | break; 56 | } 57 | n_log2 += 1; 58 | } 59 | } else { 60 | let maxn = memlimit as u64 / (u64::from(r) * 128); 61 | while n_log2 < 63 { 62 | if 1u64 << n_log2 > maxn / 2 { 63 | break; 64 | } 65 | n_log2 += 1; 66 | } 67 | let maxrp = cmp::min(0x3fff_ffff_u32, ((opslimit / 4) / (1u64 << n_log2)) as u32); 68 | p = maxrp / r; 69 | } 70 | if n_log2 > n_log2_max { 71 | return Err(PError::new(ErrorKind::KDF, "scrypt parameters too high")); 72 | } 73 | scrypt::Params::new(n_log2, r, p, scrypt::Params::RECOMMENDED_LEN).map_err(Into::into) 74 | } 75 | 76 | pub fn get_password(prompt: &str) -> Result { 77 | let pwd = prompt_password(prompt)?; 78 | if pwd.is_empty() { 79 | println!(""); 80 | Ok(pwd) 81 | } else if pwd.len() > PASSWORD_MAXBYTES { 82 | Err(PError::new( 83 | ErrorKind::Misc, 84 | "passphrase can't exceed 1024 bytes length", 85 | )) 86 | } else { 87 | Ok(pwd) 88 | } 89 | } 90 | 91 | pub fn unix_timestamp() -> u64 { 92 | let start = SystemTime::now(); 93 | let since_the_epoch = start 94 | .duration_since(UNIX_EPOCH) 95 | .expect("system clock is incorrect"); 96 | since_the_epoch.as_secs() 97 | } 98 | -------------------------------------------------------------------------------- /src/crypto/cryptoutil.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // http://rust-lang.org/COPYRIGHT. 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | use std::ptr; 12 | use std::{io, mem::MaybeUninit}; 13 | 14 | pub fn write_u64_le(dst: &mut [u8], mut input: u64) { 15 | assert!(dst.len() == 8); 16 | input = input.to_le(); 17 | unsafe { 18 | let tmp = &input as *const _ as *const u8; 19 | ptr::copy_nonoverlapping(tmp, dst.get_unchecked_mut(0), 8); 20 | } 21 | } 22 | 23 | pub fn write_u64v_le(dst: &mut [u8], input: &[u64]) { 24 | assert!(dst.len() == 8 * input.len()); 25 | unsafe { 26 | let mut x: *mut u8 = dst.get_unchecked_mut(0); 27 | let mut y: *const u64 = input.get_unchecked(0); 28 | for _ in 0..input.len() { 29 | let tmp = (*y).to_le(); 30 | ptr::copy_nonoverlapping(&tmp as *const _ as *const u8, x, 8); 31 | x = x.offset(8); 32 | y = y.offset(1); 33 | } 34 | } 35 | } 36 | 37 | pub fn write_u32_le(dst: &mut [u8], mut input: u32) { 38 | assert!(dst.len() == 4); 39 | input = input.to_le(); 40 | unsafe { 41 | let tmp = &input as *const _ as *const u8; 42 | ptr::copy_nonoverlapping(tmp, dst.get_unchecked_mut(0), 4); 43 | } 44 | } 45 | 46 | pub fn read_u64v_le(dst: &mut [u64], input: &[u8]) { 47 | assert!(dst.len() * 8 == input.len()); 48 | unsafe { 49 | let mut x: *mut u64 = dst.get_unchecked_mut(0); 50 | let mut y: *const u8 = input.get_unchecked(0); 51 | for _ in 0..dst.len() { 52 | let mut tmp = MaybeUninit::::uninit(); 53 | ptr::copy_nonoverlapping(y, tmp.as_mut_ptr() as *mut u8, 8); 54 | *x = u64::from_le(tmp.assume_init()); 55 | x = x.offset(1); 56 | y = y.offset(8); 57 | } 58 | } 59 | } 60 | 61 | #[inline] 62 | pub fn copy_memory(src: &[u8], dst: &mut [u8]) { 63 | assert!(dst.len() >= src.len()); 64 | unsafe { 65 | let srcp = src.as_ptr(); 66 | let dstp = dst.as_mut_ptr(); 67 | ptr::copy_nonoverlapping(srcp, dstp, src.len()); 68 | } 69 | } 70 | 71 | pub trait WriteExt { 72 | fn write_u8(&mut self, val: u8) -> io::Result<()>; 73 | fn write_u32_le(&mut self, val: u32) -> io::Result<()>; 74 | fn write_u64_le(&mut self, val: u64) -> io::Result<()>; 75 | } 76 | 77 | impl WriteExt for T 78 | where 79 | T: io::Write, 80 | { 81 | fn write_u8(&mut self, val: u8) -> io::Result<()> { 82 | let buff = [val]; 83 | self.write_all(&buff) 84 | } 85 | 86 | fn write_u32_le(&mut self, val: u32) -> io::Result<()> { 87 | let mut buff = [0u8; 4]; 88 | write_u32_le(&mut buff, val); 89 | self.write_all(&buff) 90 | } 91 | 92 | fn write_u64_le(&mut self, val: u64) -> io::Result<()> { 93 | let mut buff = [0u8; 8]; 94 | write_u64_le(&mut buff, val); 95 | self.write_all(&buff) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![CI status](https://github.com/jedisct1/rust-minisign/workflows/Rust/badge.svg) 2 | [![Last version](https://img.shields.io/crates/v/minisign.svg)](https://crates.io/crates/minisign) 3 | [![Documentation](https://docs.rs/minisign/badge.svg)](https://docs.rs/minisign) 4 | 5 | # rust-minisign 6 | 7 | A pure Rust implementation of the [Minisign](https://jedisct1.github.io/minisign/) signature system. 8 | 9 | This is a crate designed to be used by applications. 10 | 11 | For a command-line tool reimplementing the Minisign utility in Rust, and based on this crate, check out [rsign2](https://github.com/jedisct1/rsign2). 12 | 13 | For a minimal crate that only verifies signatures, check out [minisign-verify](https://github.com/jedisct1/rust-minisign-verify). 14 | 15 | ## API documentation 16 | 17 | [API documentation on docs.rs](https://docs.rs/minisign) 18 | 19 | ## Example 20 | 21 | ```rust 22 | use minisign::{KeyPair, PublicKeyBox, SecretKeyBox, SignatureBox}; 23 | use std::io::Cursor; 24 | 25 | // Generate and return a new key pair 26 | // The key is encrypted using a password. 27 | // If `None` is given, the password will be asked for interactively. 28 | let KeyPair { pk, sk } = 29 | KeyPair::generate_encrypted_keypair(Some("key password".to_string())).unwrap(); 30 | 31 | // In order to be stored to disk, keys have to be converted to "boxes". 32 | // A box is just a container, with some metadata about its content. 33 | // Boxes can be converted to/from strings, making them convenient to use for storage. 34 | let pk_box_str = pk.to_box().unwrap().to_string(); 35 | let sk_box_str = sk 36 | .to_box(None) // Optional comment about the key 37 | .unwrap() 38 | .to_string(); 39 | 40 | // `pk_box_str` and `sk_box_str` can now be saved to disk. 41 | // This is a long-term key pair, that can be used to sign as many files as needed. 42 | // For convenience, the `KeyPair::generate_and_write_encrypted_keypair()` function 43 | // is available: it generates a new key pair, and saves it to disk (or any `Writer`) 44 | // before returning it. 45 | 46 | // Assuming that `sk_box_str` is something we previously saved and just reloaded, 47 | // it can be converted back to a secret key box: 48 | let sk_box = SecretKeyBox::from_string(&sk_box_str).unwrap(); 49 | 50 | // and the box can be opened using the password to reveal the original secret key: 51 | let sk = sk_box 52 | .into_secret_key(Some("key password".to_string())) 53 | .unwrap(); 54 | 55 | // Now, we can use the secret key to sign anything. 56 | let data = b"lorem ipsum"; 57 | let data_reader = Cursor::new(data); 58 | let signature_box = minisign::sign(None, &sk, data_reader, None, None).unwrap(); 59 | 60 | // We have a signature! Let's inspect it a little bit. 61 | println!( 62 | "Untrusted comment: [{}]", 63 | signature_box.untrusted_comment().unwrap() 64 | ); 65 | println!( 66 | "Trusted comment: [{}]", 67 | signature_box.trusted_comment().unwrap() 68 | ); 69 | 70 | // Converting the signature box to a string in order to save it is easy. 71 | let signature_box_str = signature_box.into_string(); 72 | 73 | // Now, let's verify the signature. 74 | // Assuming we just loaded it into `signature_box_str`, get the box back. 75 | let signature_box = SignatureBox::from_string(&signature_box_str).unwrap(); 76 | 77 | // Load the public key from the string. 78 | let pk_box = PublicKeyBox::from_string(&pk_box_str).unwrap(); 79 | let pk = pk_box.into_public_key().unwrap(); 80 | 81 | // And verify the data. 82 | let data_reader = Cursor::new(data); 83 | let verified = minisign::verify(&pk, &signature_box, data_reader, true, false, false); 84 | match verified { 85 | Ok(()) => println!("Success!"), 86 | Err(_) => println!("Verification failed"), 87 | }; 88 | ``` 89 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error as StdError; 2 | use std::{self, fmt, io}; 3 | 4 | macro_rules! werr( 5 | ($($arg:tt)*) => ({ 6 | use std::io::{Write, stderr}; 7 | write!(&mut stderr(), $($arg)*).unwrap(); 8 | }) 9 | ); 10 | 11 | pub type Result = std::result::Result; 12 | 13 | #[derive(Debug)] 14 | pub enum ErrorKind { 15 | Generate, 16 | Sign, 17 | Verify, 18 | Io, 19 | Misc, 20 | Hash, 21 | KDF, 22 | RNG, 23 | Encoding, 24 | EncryptedKey, 25 | } 26 | 27 | /// Error structure for the `minisign` crate. 28 | #[derive(Debug)] 29 | pub struct PError { 30 | kind: ErrorKind, 31 | err: Box, 32 | } 33 | 34 | impl PError { 35 | pub fn exit(&self) -> ! { 36 | werr!("{}\n", self); 37 | ::std::process::exit(1) 38 | } 39 | 40 | pub fn new(kind: ErrorKind, err: E) -> PError 41 | where 42 | E: Into>, 43 | { 44 | PError { 45 | kind, 46 | err: err.into(), 47 | } 48 | } 49 | 50 | /// Get the kind of error. 51 | pub fn kind(&self) -> &ErrorKind { 52 | &self.kind 53 | } 54 | } 55 | 56 | impl fmt::Display for PError { 57 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 58 | match self.kind { 59 | ErrorKind::Generate => write!(f, "{}", self.err), 60 | ErrorKind::Sign => write!(f, "{}", self.err), 61 | ErrorKind::Verify => write!(f, "{}", self.err), 62 | ErrorKind::Misc => write!(f, "{}", self.err), 63 | ErrorKind::Io => write!(f, "{}", self.err), 64 | ErrorKind::Hash => write!(f, "{}", self.err), 65 | ErrorKind::KDF => write!(f, "{}", self.err), 66 | ErrorKind::RNG => write!(f, "{}", self.err), 67 | ErrorKind::Encoding => write!(f, "{}", self.err), 68 | ErrorKind::EncryptedKey => write!(f, "{}", self.err), 69 | } 70 | } 71 | } 72 | impl StdError for PError { 73 | fn description(&self) -> &str { 74 | match self.kind { 75 | ErrorKind::Generate => "generate error", 76 | ErrorKind::Sign => "sign error", 77 | ErrorKind::Verify => "verify error", 78 | ErrorKind::Misc => "misc error", 79 | ErrorKind::Io => "io error", 80 | ErrorKind::Hash => "hash error", 81 | ErrorKind::KDF => "key derivation error", 82 | ErrorKind::RNG => "random number generator error", 83 | ErrorKind::Encoding => "encoding error", 84 | ErrorKind::EncryptedKey => "encrypted key error", 85 | } 86 | } 87 | } 88 | 89 | impl From for PError { 90 | fn from(err: io::Error) -> PError { 91 | PError::new(ErrorKind::Io, err) 92 | } 93 | } 94 | 95 | impl From for PError { 96 | fn from(err: fmt::Error) -> PError { 97 | PError::new(ErrorKind::Io, err) 98 | } 99 | } 100 | 101 | impl From for PError { 102 | fn from(err: ct_codecs::Error) -> PError { 103 | PError::new(ErrorKind::Encoding, err) 104 | } 105 | } 106 | 107 | impl From for PError { 108 | fn from(err: std::string::FromUtf8Error) -> PError { 109 | PError::new(ErrorKind::Encoding, err) 110 | } 111 | } 112 | 113 | impl From for PError { 114 | fn from(err: scrypt::errors::InvalidParams) -> PError { 115 | PError::new(ErrorKind::KDF, err.to_string()) 116 | } 117 | } 118 | 119 | impl From for PError { 120 | fn from(err: scrypt::errors::InvalidOutputLen) -> PError { 121 | PError::new(ErrorKind::KDF, err.to_string()) 122 | } 123 | } 124 | 125 | impl From for PError { 126 | fn from(err: getrandom::Error) -> PError { 127 | PError::new(ErrorKind::RNG, format!("{err}")) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/crypto/ed25519.rs: -------------------------------------------------------------------------------- 1 | use super::curve25519::{ge_scalarmult_base, is_identity, sc_muladd, sc_reduce, GeP2, GeP3}; 2 | use super::sha512; 3 | 4 | static L: [u8; 32] = [ 5 | 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6 | 0x14, 0xde, 0xf9, 0xde, 0xa2, 0xf7, 0x9c, 0xd6, 0x58, 0x12, 0x63, 0x1a, 0x5c, 0xf5, 0xd3, 0xed, 7 | ]; 8 | 9 | fn check_s_lt_l(s: &[u8]) -> bool { 10 | let mut c: u8 = 0; 11 | let mut n: u8 = 1; 12 | 13 | let mut i = 31; 14 | loop { 15 | c |= ((((s[i] as i32) - (L[i] as i32)) >> 8) as u8) & n; 16 | n &= ((((s[i] ^ L[i]) as i32) - 1) >> 8) as u8; 17 | if i == 0 { 18 | break; 19 | } 20 | i -= 1; 21 | } 22 | c == 0 23 | } 24 | 25 | pub fn verify(message: &[u8], public_key: &[u8], signature: &[u8]) -> bool { 26 | if check_s_lt_l(&signature[32..64]) || is_identity(public_key) { 27 | return false; 28 | } 29 | let a = match GeP3::from_bytes_negate_vartime(public_key) { 30 | Some(g) => g, 31 | None => { 32 | return false; 33 | } 34 | }; 35 | if public_key.iter().fold(0, |acc, x| acc | x) == 0 { 36 | return false; 37 | } 38 | 39 | let mut hasher = sha512::Hash::new(); 40 | hasher.update(&signature[0..32]); 41 | hasher.update(public_key); 42 | hasher.update(message); 43 | let mut hash = hasher.finalize(); 44 | sc_reduce(&mut hash); 45 | 46 | let r = GeP2::double_scalarmult_vartime(hash.as_ref(), a, &signature[32..64]); 47 | r.to_bytes() 48 | .as_ref() 49 | .iter() 50 | .zip(signature.iter()) 51 | .fold(0, |acc, (x, y)| acc | (x ^ y)) 52 | == 0 53 | } 54 | 55 | pub fn keypair(seed: &[u8]) -> ([u8; 64], [u8; 32]) { 56 | let mut secret: [u8; 64] = { 57 | let mut hash_output = sha512::Hash::hash(seed); 58 | hash_output[0] &= 248; 59 | hash_output[31] &= 63; 60 | hash_output[31] |= 64; 61 | hash_output 62 | }; 63 | let a = ge_scalarmult_base(&secret[0..32]); 64 | let public_key = a.to_bytes(); 65 | for (dest, src) in secret[32..64].iter_mut().zip(public_key.iter()) { 66 | *dest = *src; 67 | } 68 | for (dest, src) in secret[0..32].iter_mut().zip(seed.iter()) { 69 | *dest = *src; 70 | } 71 | (secret, public_key) 72 | } 73 | 74 | pub fn signature(message: &[u8], secret_key: &[u8], z: Option<&[u8]>) -> [u8; 64] { 75 | let seed = &secret_key[0..32]; 76 | let public_key = &secret_key[32..64]; 77 | let az: [u8; 64] = { 78 | let mut hash_output = sha512::Hash::hash(seed); 79 | hash_output[0] &= 248; 80 | hash_output[31] &= 63; 81 | hash_output[31] |= 64; 82 | hash_output 83 | }; 84 | let nonce = { 85 | let mut hasher = sha512::Hash::new(); 86 | if let Some(z) = z { 87 | hasher.update(z); 88 | hasher.update(&az[..]); 89 | } else { 90 | hasher.update(&az[32..64]); 91 | } 92 | hasher.update(message); 93 | let mut hash_output = hasher.finalize(); 94 | sc_reduce(&mut hash_output[0..64]); 95 | hash_output 96 | }; 97 | let mut signature: [u8; 64] = [0; 64]; 98 | let r: GeP3 = ge_scalarmult_base(&nonce[0..32]); 99 | for (result_byte, source_byte) in signature[0..32].iter_mut().zip(r.to_bytes().iter()) { 100 | *result_byte = *source_byte; 101 | } 102 | for (result_byte, source_byte) in signature[32..64].iter_mut().zip(public_key.iter()) { 103 | *result_byte = *source_byte; 104 | } 105 | { 106 | let mut hasher = sha512::Hash::new(); 107 | hasher.update(signature.as_ref()); 108 | hasher.update(message); 109 | let mut hram = hasher.finalize(); 110 | sc_reduce(&mut hram); 111 | sc_muladd( 112 | &mut signature[32..64], 113 | &hram[0..32], 114 | &az[0..32], 115 | &nonce[0..32], 116 | ); 117 | } 118 | signature 119 | } 120 | -------------------------------------------------------------------------------- /src/keypair.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Write}; 2 | 3 | use getrandom::fill as getrandom; 4 | 5 | use crate::constants::*; 6 | use crate::crypto::ed25519; 7 | use crate::errors::*; 8 | use crate::helpers::*; 9 | use crate::keynum::*; 10 | use crate::public_key::*; 11 | use crate::secret_key::*; 12 | 13 | /// A key pair (`PublicKey` and `SecretKey`). 14 | #[derive(Clone, Debug)] 15 | pub struct KeyPair { 16 | pub pk: PublicKey, 17 | pub sk: SecretKey, 18 | } 19 | 20 | impl KeyPair { 21 | /// Create an unencrypted key pair. 22 | /// 23 | /// The secret key will not be protected by a password. 24 | /// 25 | /// This is not recommended and incompatible with other implementations, 26 | /// but can be necessary if using a password is really not an option 27 | /// for your application. 28 | /// 29 | /// You generally want to use `generated_encrypted_keypair()` instead. 30 | pub fn generate_unencrypted_keypair() -> Result { 31 | let mut seed = vec![0u8; 32]; 32 | getrandom(&mut seed)?; 33 | let (sk, pk) = ed25519::keypair(&seed); 34 | let mut keynum = [0u8; KEYNUM_BYTES]; 35 | getrandom(&mut keynum)?; 36 | 37 | let pk = PublicKey { 38 | sig_alg: SIGALG, 39 | keynum_pk: KeynumPK { keynum, pk }, 40 | }; 41 | let mut sk = SecretKey { 42 | sig_alg: SIGALG, 43 | kdf_alg: KDF_NONE, 44 | chk_alg: CHK_ALG, 45 | kdf_salt: Default::default(), 46 | kdf_opslimit_le: Default::default(), 47 | kdf_memlimit_le: Default::default(), 48 | keynum_sk: KeynumSK { 49 | keynum, 50 | sk, 51 | chk: [0; CHK_BYTES], 52 | }, 53 | }; 54 | sk.write_checksum() 55 | .map_err(|_| PError::new(ErrorKind::Generate, "failed to hash and write checksum!"))?; 56 | 57 | Ok(KeyPair { pk, sk }) 58 | } 59 | 60 | /// Create and encrypt a new key pair. 61 | /// 62 | /// If `password` is `None`, a password will be interactively asked for. 63 | /// 64 | /// A key can be converted to a box in order to be serialized and saved. 65 | /// Ex: `pk.to_box()?.to_bytes()` 66 | pub fn generate_encrypted_keypair(password: Option) -> Result { 67 | let KeyPair { pk, mut sk } = Self::generate_unencrypted_keypair()?; 68 | 69 | let opslimit = OPSLIMIT; 70 | let memlimit = MEMLIMIT; 71 | let mut kdf_salt = [0u8; KDF_SALTBYTES]; 72 | getrandom(&mut kdf_salt)?; 73 | sk.kdf_alg = KDF_ALG; 74 | sk.kdf_salt = kdf_salt; 75 | sk.kdf_opslimit_le = store_u64_le(opslimit); 76 | sk.kdf_memlimit_le = store_u64_le(memlimit as u64); 77 | sk.write_checksum() 78 | .map_err(|_| PError::new(ErrorKind::Generate, "failed to hash and write checksum!"))?; 79 | 80 | let interactive = password.is_none(); 81 | let password = match password { 82 | Some(password) => password, 83 | None => { 84 | writeln!( 85 | io::stdout(), 86 | "Please enter a password to protect the secret key." 87 | )?; 88 | let password = get_password("Password: ")?; 89 | let password2 = get_password("Password (one more time): ")?; 90 | if password != password2 { 91 | return Err(PError::new(ErrorKind::Generate, "passwords don't match!")); 92 | } 93 | write!( 94 | io::stdout(), 95 | "Deriving a key from the password in order to encrypt the secret key... " 96 | ) 97 | .map_err(|e| PError::new(ErrorKind::Io, e))?; 98 | io::stdout().flush()?; 99 | password 100 | } 101 | }; 102 | if !password.is_empty() { 103 | sk = sk.encrypt(password)?; 104 | } else if interactive { 105 | writeln!(io::stdout(), "done").map_err(|e| PError::new(ErrorKind::Io, e))?; 106 | } 107 | Ok(KeyPair { pk, sk }) 108 | } 109 | 110 | /// Create, encrypt and save a new key pair. 111 | /// 112 | /// # Arguments 113 | /// 114 | /// * `pk_writer` - Where to store the public key box. 115 | /// * `sk_writer` - Where to store the secret key box. 116 | /// * `comment` - An optional untrusted comment to replace the default one. 117 | /// * `password` - If `None`, a password will be interactively asked for. 118 | pub fn generate_and_write_encrypted_keypair( 119 | mut pk_writer: W, 120 | mut sk_writer: X, 121 | comment: Option<&str>, 122 | password: Option, 123 | ) -> Result 124 | where 125 | W: Write, 126 | X: Write, 127 | { 128 | let KeyPair { pk, sk } = Self::generate_encrypted_keypair(password)?; 129 | pk_writer.write_all(&pk.to_box()?.to_bytes())?; 130 | pk_writer.flush()?; 131 | 132 | sk_writer.write_all(&sk.to_box(comment)?.to_bytes())?; 133 | sk_writer.flush()?; 134 | 135 | Ok(KeyPair { pk, sk }) 136 | } 137 | 138 | /// Create and save an unencrypted key pair. 139 | /// 140 | /// The secret key will not be protected by a password, 141 | /// and keys will be stored as raw bytes, not as a box. 142 | /// 143 | /// This is not recommended and incompatible with other implementations, 144 | /// but can be necessary if using a password is not an option 145 | /// for your application. 146 | /// 147 | /// You generally want to use `generated_encrypted_keypair()` instead. 148 | /// 149 | /// # Arguments 150 | /// 151 | /// * `pk_writer` - Where to store the public key box. 152 | /// * `sk_writer` - Where to store the secret key box. 153 | pub fn generate_and_write_unencrypted_keypair( 154 | mut pk_writer: W, 155 | mut sk_writer: X, 156 | ) -> Result 157 | where 158 | W: Write, 159 | X: Write, 160 | { 161 | let KeyPair { pk, sk } = Self::generate_unencrypted_keypair()?; 162 | 163 | pk_writer.write_all(&pk.to_bytes())?; 164 | pk_writer.flush()?; 165 | 166 | sk_writer.write_all(&sk.to_bytes())?; 167 | sk_writer.flush()?; 168 | 169 | Ok(KeyPair { pk, sk }) 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/signature_box.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Write as fmtWrite; 2 | use std::fs; 3 | use std::path::Path; 4 | 5 | use ct_codecs::{Base64, Decoder, Encoder}; 6 | 7 | use crate::constants::*; 8 | use crate::errors::*; 9 | use crate::signature::*; 10 | 11 | /// A signature, as well as the metadata required to verify it. 12 | #[derive(Clone)] 13 | pub struct SignatureBox { 14 | pub(crate) untrusted_comment: String, 15 | pub(crate) signature: Signature, 16 | pub(crate) sig_and_trusted_comment: Option>, 17 | pub(crate) global_sig: Option>, 18 | pub(crate) is_prehashed: bool, 19 | } 20 | 21 | impl From for String { 22 | fn from(sig_box: SignatureBox) -> String { 23 | sig_box.into_string() 24 | } 25 | } 26 | 27 | impl From for SignatureBox { 28 | fn from(s: String) -> SignatureBox { 29 | SignatureBox::from_string(&s).unwrap() 30 | } 31 | } 32 | 33 | impl SignatureBox { 34 | /// Returns `true` if the signed data was pre-hashed. 35 | pub fn is_prehashed(&self) -> bool { 36 | self.is_prehashed 37 | } 38 | 39 | /// The untrusted comment present in the signature. 40 | pub fn untrusted_comment(&self) -> Result { 41 | Ok(self.untrusted_comment.clone()) 42 | } 43 | 44 | /// The trusted comment present in the signature. 45 | pub fn trusted_comment(&self) -> Result { 46 | let sig_and_trusted_comment = match &self.sig_and_trusted_comment { 47 | None => { 48 | return Err(PError::new( 49 | ErrorKind::Misc, 50 | "trusted comment is not present", 51 | )) 52 | } 53 | Some(sig_and_trusted_comment) => sig_and_trusted_comment, 54 | }; 55 | if sig_and_trusted_comment.len() < SIGNATURE_BYTES { 56 | return Err(PError::new( 57 | ErrorKind::Encoding, 58 | "invalid trusted comment encoding", 59 | )); 60 | } 61 | let just_comment = String::from_utf8(sig_and_trusted_comment[SIGNATURE_BYTES..].to_vec())?; 62 | Ok(just_comment) 63 | } 64 | 65 | /// The key identifier used to create the signature. 66 | pub fn keynum(&self) -> &[u8] { 67 | &self.signature.keynum[..] 68 | } 69 | 70 | /// Create a new `SignatureBox` from a string. 71 | pub fn from_string(s: &str) -> Result { 72 | let mut lines = s.lines(); 73 | let untrusted_comment = lines 74 | .next() 75 | .ok_or_else(|| PError::new(ErrorKind::Io, "Missing untrusted comment"))? 76 | .to_string(); 77 | let signature_str = lines 78 | .next() 79 | .ok_or_else(|| PError::new(ErrorKind::Io, "Missing signature"))? 80 | .to_string(); 81 | let mut trusted_comment_str = lines 82 | .next() 83 | .ok_or_else(|| PError::new(ErrorKind::Io, "Missing trusted comment"))? 84 | .to_string(); 85 | let global_sig = lines 86 | .next() 87 | .ok_or_else(|| PError::new(ErrorKind::Io, "Missing global signature"))? 88 | .to_string(); 89 | if !untrusted_comment.starts_with(COMMENT_PREFIX) { 90 | return Err(PError::new( 91 | ErrorKind::Verify, 92 | format!("Untrusted comment must start with: {COMMENT_PREFIX}"), 93 | )); 94 | } 95 | let untrusted_comment = untrusted_comment[COMMENT_PREFIX.len()..].to_string(); 96 | let sig_bytes = Base64::decode_to_vec(signature_str.trim().as_bytes(), None) 97 | .map_err(|e| PError::new(ErrorKind::Io, e))?; 98 | let signature = Signature::from_bytes(&sig_bytes)?; 99 | if !trusted_comment_str.starts_with(TRUSTED_COMMENT_PREFIX) { 100 | return Err(PError::new( 101 | ErrorKind::Verify, 102 | format!("Trusted comment should start with: {TRUSTED_COMMENT_PREFIX}"), 103 | )); 104 | } 105 | let is_prehashed = match signature.sig_alg { 106 | SIGALG => false, 107 | SIGALG_PREHASHED => true, 108 | _ => { 109 | return Err(PError::new( 110 | ErrorKind::Verify, 111 | "Unsupported signature algorithm".to_string(), 112 | )) 113 | } 114 | }; 115 | let _ = trusted_comment_str 116 | .drain(..TRUSTED_COMMENT_PREFIX_LEN) 117 | .count(); 118 | let mut sig_and_trusted_comment = signature.sig.to_vec(); 119 | sig_and_trusted_comment.extend_from_slice(trusted_comment_str.trim().as_bytes()); 120 | let global_sig = Base64::decode_to_vec(global_sig.trim().as_bytes(), None) 121 | .map_err(|e| PError::new(ErrorKind::Io, e))?; 122 | Ok(SignatureBox { 123 | untrusted_comment, 124 | signature, 125 | sig_and_trusted_comment: Some(sig_and_trusted_comment), 126 | global_sig: Some(global_sig), 127 | is_prehashed, 128 | }) 129 | } 130 | 131 | /// Return a `SignatureBox` for a string, for storage. 132 | #[allow(clippy::inherent_to_string)] 133 | pub fn to_string(&self) -> String { 134 | let mut signature_box = String::new(); 135 | writeln!( 136 | signature_box, 137 | "{}{}", 138 | COMMENT_PREFIX, self.untrusted_comment 139 | ) 140 | .unwrap(); 141 | writeln!(signature_box, "{}", self.signature).unwrap(); 142 | writeln!( 143 | signature_box, 144 | "{}{}", 145 | TRUSTED_COMMENT_PREFIX, 146 | self.trusted_comment() 147 | .expect("Incomplete SignatureBox: trusted comment is missing") 148 | ) 149 | .unwrap(); 150 | let global_sig = self 151 | .global_sig 152 | .as_ref() 153 | .expect("Incomplete SignatureBox: global signature is missing"); 154 | writeln!( 155 | signature_box, 156 | "{}", 157 | Base64::encode_to_string(&global_sig[..]).unwrap() 158 | ) 159 | .unwrap(); 160 | signature_box 161 | } 162 | 163 | /// Convert a `SignatureBox` to a string, for storage. 164 | pub fn into_string(self) -> String { 165 | self.to_string() 166 | } 167 | 168 | /// Return a byte representation of the signature, for storage. 169 | pub fn to_bytes(&self) -> Vec { 170 | self.to_string().as_bytes().to_vec() 171 | } 172 | 173 | /// Load a `SignatureBox` from a file. 174 | pub fn from_file

(sig_path: P) -> Result 175 | where 176 | P: AsRef, 177 | { 178 | let s = fs::read_to_string(sig_path)?; 179 | SignatureBox::from_string(&s) 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/public_key.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use std::convert::TryInto; 3 | use std::fmt::Write as fmtWrite; 4 | use std::fs; 5 | use std::io::{Cursor, Read}; 6 | use std::path::Path; 7 | 8 | use ct_codecs::{Base64, Decoder, Encoder}; 9 | 10 | use crate::crypto::util::fixed_time_eq; 11 | use crate::errors::*; 12 | use crate::helpers::*; 13 | use crate::keynum::*; 14 | use crate::{constants::*, SecretKey}; 15 | 16 | /// A public key and its metadata. 17 | /// 18 | /// A `PublicKeyBox` represents a raw public key, along with a key 19 | /// identifier and an untrusted description. 20 | /// 21 | /// This is what usually gets exported to disk. 22 | /// 23 | /// A `PublicKeyBox` can be directly converted to/from a single-line string. 24 | #[derive(Clone, Debug)] 25 | pub struct PublicKeyBox(String); 26 | 27 | impl From for String { 28 | fn from(pkb: PublicKeyBox) -> String { 29 | pkb.0 30 | } 31 | } 32 | 33 | impl From for PublicKeyBox { 34 | fn from(s: String) -> PublicKeyBox { 35 | PublicKeyBox(s) 36 | } 37 | } 38 | 39 | impl std::fmt::Display for PublicKeyBox { 40 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 41 | write!(f, "{}", self.0) 42 | } 43 | } 44 | 45 | impl From for PublicKey { 46 | fn from(pkb: PublicKeyBox) -> PublicKey { 47 | pkb.into_public_key().unwrap() 48 | } 49 | } 50 | 51 | impl PublicKeyBox { 52 | /// Create a new `PublicKeyBox` from a string. 53 | pub fn from_string(s: &str) -> Result { 54 | Ok(s.to_string().into()) 55 | } 56 | 57 | /// Return a `PublicKeyBox` for a string, for storage. 58 | pub fn into_string(self) -> String { 59 | self.into() 60 | } 61 | 62 | /// Convert a `PublicKeyBox` to a string, for storage. 63 | pub fn into_public_key(self) -> Result { 64 | PublicKey::from_box(self) 65 | } 66 | 67 | /// Return a byte representation of the public key, for storage. 68 | pub fn to_bytes(&self) -> Vec { 69 | self.to_string().as_bytes().to_vec() 70 | } 71 | } 72 | 73 | /// A `PublicKey` is used to verify signatures. 74 | #[derive(Clone, Debug)] 75 | pub struct PublicKey { 76 | pub(crate) sig_alg: [u8; TWOBYTES], 77 | pub(crate) keynum_pk: KeynumPK, 78 | } 79 | 80 | impl PublicKey { 81 | /// The key identifier of this public key. 82 | pub fn keynum(&self) -> &[u8] { 83 | &self.keynum_pk.keynum[..] 84 | } 85 | 86 | /// Deserialize a `PublicKey`. 87 | /// 88 | /// For storage, a `PublicKeyBox` is usually what you need instead. 89 | pub fn from_bytes(buf: &[u8]) -> Result { 90 | let mut buf = Cursor::new(buf); 91 | let mut sig_alg = [0u8; TWOBYTES]; 92 | let mut keynum = [0u8; KEYNUM_BYTES]; 93 | let mut pk = [0u8; PUBLICKEY_BYTES]; 94 | buf.read_exact(&mut sig_alg)?; 95 | buf.read_exact(&mut keynum)?; 96 | buf.read_exact(&mut pk)?; 97 | Ok(PublicKey { 98 | sig_alg, 99 | keynum_pk: KeynumPK { keynum, pk }, 100 | }) 101 | } 102 | 103 | /// Regenerate a `PublicKey` from `SecretKey` 104 | pub fn from_secret_key(sk: &SecretKey) -> Result { 105 | let sig_alg = sk.sig_alg; 106 | let keynum = sk.keynum_sk.keynum; 107 | let pk = sk.keynum_sk.sk[PUBLICKEY_BYTES..].try_into().map_err(|_| { 108 | PError::new( 109 | ErrorKind::Misc, 110 | "secret key does not contain public key".to_string(), 111 | ) 112 | })?; 113 | 114 | Ok(PublicKey { 115 | sig_alg, 116 | keynum_pk: KeynumPK { keynum, pk }, 117 | }) 118 | } 119 | 120 | /// Serialize a `PublicKey`. 121 | /// 122 | /// For storage, a `PublicKeyBox` is usually what you want to use instead. 123 | pub fn to_bytes(&self) -> Vec { 124 | let mut iters = Vec::new(); 125 | iters.push(self.sig_alg.iter()); 126 | iters.push(self.keynum_pk.keynum.iter()); 127 | iters.push(self.keynum_pk.pk.iter()); 128 | let v: Vec = iters 129 | .iter() 130 | .flat_map(|b| { 131 | let b = b.clone(); 132 | b.cloned() 133 | }) 134 | .collect(); 135 | v 136 | } 137 | 138 | /// Convert a `PublicKeyBox` to a `PublicKey`. 139 | pub fn from_box(pk_box: PublicKeyBox) -> Result { 140 | let s = pk_box.0; 141 | let mut lines = s.lines(); 142 | lines.next().ok_or_else(|| { 143 | PError::new(ErrorKind::Io, "Missing comment in public key".to_string()) 144 | })?; 145 | let encoded_pk = lines.next().ok_or_else(|| { 146 | PError::new( 147 | ErrorKind::Io, 148 | "Missing encoded key in public key".to_string(), 149 | ) 150 | })?; 151 | if encoded_pk.len() != PK_B64_ENCODED_LEN { 152 | return Err(PError::new( 153 | ErrorKind::Io, 154 | "Base64 conversion failed - was an actual public key given?".to_string(), 155 | )); 156 | } 157 | let decoded_buf = Base64::decode_to_vec(encoded_pk.trim(), None).map_err(|e| { 158 | PError::new( 159 | ErrorKind::Io, 160 | format!("Base64 conversion failed - was an actual public key given?: {e}"), 161 | ) 162 | })?; 163 | PublicKey::from_bytes(&decoded_buf) 164 | } 165 | 166 | /// Convert a `PublicKey` to a `PublicKeyBox`. 167 | pub fn to_box(&self) -> Result { 168 | let mut s = String::new(); 169 | write!(s, "{COMMENT_PREFIX}minisign public key: ")?; 170 | writeln!(s, "{:016X}", load_u64_le(&self.keynum_pk.keynum[..]))?; 171 | writeln!(s, "{}", self.to_base64())?; 172 | Ok(s.into()) 173 | } 174 | 175 | /// Create a minimal public key from a base64-encoded string. 176 | pub fn from_base64(pk_string: &str) -> Result { 177 | let encoded_string = pk_string.to_string(); 178 | if encoded_string.trim().len() != PK_B64_ENCODED_LEN { 179 | return Err(PError::new( 180 | ErrorKind::Io, 181 | "base64 conversion failed - was an actual public key given?".to_string(), 182 | )); 183 | } 184 | let decoded_string = 185 | Base64::decode_to_vec(encoded_string.as_bytes(), None).map_err(|e| { 186 | PError::new( 187 | ErrorKind::Io, 188 | format!("base64 conversion failed - was an actual public key given?: {e}"), 189 | ) 190 | })?; 191 | PublicKey::from_bytes(&decoded_string) 192 | } 193 | 194 | /// Encode a public key as a base64-encoded string. 195 | pub fn to_base64(&self) -> String { 196 | Base64::encode_to_string(self.to_bytes().as_slice()).unwrap() 197 | } 198 | 199 | /// Load a `PublicKeyBox` from a file, and returns a `PublicKey` from it. 200 | pub fn from_file

(pk_path: P) -> Result 201 | where 202 | P: AsRef, 203 | { 204 | let s = fs::read_to_string(pk_path)?; 205 | PublicKey::from_box(s.into()) 206 | } 207 | } 208 | 209 | impl cmp::PartialEq for PublicKey { 210 | fn eq(&self, other: &PublicKey) -> bool { 211 | fixed_time_eq(&self.keynum_pk.pk, &other.keynum_pk.pk) 212 | } 213 | } 214 | 215 | impl cmp::Eq for PublicKey {} 216 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![allow( 3 | clippy::inherent_to_string, 4 | clippy::wrong_self_convention, 5 | clippy::derivable_impls, 6 | clippy::field_reassign_with_default, 7 | clippy::vec_init_then_push 8 | )] 9 | 10 | mod constants; 11 | mod crypto; 12 | mod errors; 13 | mod helpers; 14 | mod keynum; 15 | mod keypair; 16 | mod public_key; 17 | mod secret_key; 18 | mod signature; 19 | mod signature_bones; 20 | mod signature_box; 21 | 22 | #[cfg(test)] 23 | mod tests; 24 | 25 | use std::io::{self, Read, Seek, Write}; 26 | 27 | use getrandom::fill as getrandom; 28 | 29 | pub use crate::constants::*; 30 | use crate::crypto::blake2b::Blake2b; 31 | use crate::crypto::ed25519; 32 | pub use crate::errors::*; 33 | use crate::helpers::*; 34 | pub use crate::keypair::*; 35 | pub use crate::public_key::*; 36 | pub use crate::secret_key::*; 37 | use crate::signature::*; 38 | pub use crate::signature_bones::*; 39 | pub use crate::signature_box::*; 40 | 41 | fn prehash(data_reader: &mut R) -> Result> 42 | where 43 | R: Read, 44 | { 45 | let mut h = vec![0u8; PREHASH_BYTES]; 46 | let mut buf = vec![0u8; 65536]; 47 | let mut state = Blake2b::new(PREHASH_BYTES); 48 | loop { 49 | let len = data_reader.read(&mut buf)?; 50 | if len == 0 { 51 | break; 52 | } 53 | state.update(&buf[..len]); 54 | } 55 | state.finalize(&mut h); 56 | Ok(h) 57 | } 58 | 59 | /// Compute a signature. 60 | /// 61 | /// # Arguments 62 | /// 63 | /// * `pk` - an optional public key. If provided, it must be the public key from 64 | /// the original key pair. 65 | /// * `sk` - the secret key 66 | /// * `data_reader` - the source of the data to be signed 67 | /// * `trusted_comment` - overrides the default trusted comment 68 | /// * `untrusted_comment` - overrides the default untrusted comment 69 | pub fn sign( 70 | pk: Option<&PublicKey>, 71 | sk: &SecretKey, 72 | mut data_reader: R, 73 | trusted_comment: Option<&str>, 74 | untrusted_comment: Option<&str>, 75 | ) -> Result 76 | where 77 | R: Read, 78 | { 79 | if sk.is_encrypted() { 80 | return Err(PError::new( 81 | ErrorKind::EncryptedKey, 82 | "Cannot sign with an encrypted secret key. The key must be decrypted first. Options include: SecretKeyBox::into_secret_key(password), SecretKey::from_box(box, password), SecretKey::from_file(path, password), or use KeyPair::generate_unencrypted_keypair() for passwordless keys.", 83 | )); 84 | } 85 | let data = prehash(&mut data_reader)?; 86 | let trusted_comment = match trusted_comment { 87 | Some(trusted_comment) => trusted_comment.to_string(), 88 | None => format!("timestamp:{}", unix_timestamp()), 89 | }; 90 | let untrusted_comment = match untrusted_comment { 91 | Some(untrusted_comment) => untrusted_comment.to_string(), 92 | None => DEFAULT_COMMENT.to_string(), 93 | }; 94 | let mut signature = Signature::default(); 95 | signature.sig_alg = SIGALG_PREHASHED; 96 | 97 | signature.keynum.copy_from_slice(&sk.keynum_sk.keynum[..]); 98 | let mut z = vec![0; 64]; 99 | getrandom(&mut z)?; 100 | let signature_raw = ed25519::signature(&data, &sk.keynum_sk.sk, Some(&z)); 101 | signature.sig.copy_from_slice(&signature_raw[..]); 102 | 103 | let mut sig_and_trusted_comment: Vec = vec![]; 104 | sig_and_trusted_comment.extend(signature.sig.iter()); 105 | sig_and_trusted_comment.extend(trusted_comment.as_bytes().iter()); 106 | 107 | getrandom(&mut z)?; 108 | let global_sig = ed25519::signature(&sig_and_trusted_comment, &sk.keynum_sk.sk, Some(&z)); 109 | if let Some(pk) = pk { 110 | if !ed25519::verify(&sig_and_trusted_comment, &pk.keynum_pk.pk[..], &global_sig) { 111 | return Err(PError::new( 112 | ErrorKind::Verify, 113 | format!( 114 | "Could not verify signature with the provided public key ID: {:016X}", 115 | load_u64_le(&pk.keynum_pk.keynum[..]) 116 | ), 117 | )); 118 | } 119 | } 120 | let signature_box = SignatureBox { 121 | untrusted_comment, 122 | signature, 123 | sig_and_trusted_comment: Some(sig_and_trusted_comment), 124 | global_sig: Some(global_sig.to_vec()), 125 | is_prehashed: true, 126 | }; 127 | Ok(signature_box) 128 | } 129 | 130 | /// Verify a signature using a public key. 131 | /// 132 | /// # Arguments 133 | /// 134 | /// * `pk` - the public key 135 | /// * `signature_box` - the signature and its metadata 136 | /// * `data_reader` - the data source 137 | /// * `quiet` - use `false` to output status information to `stderr` 138 | /// * `output` - use `true` to output a copy of the data to `stdout` 139 | /// * `allow_legacy` - accept signatures from legacy versions of minisign 140 | pub fn verify( 141 | pk: &PublicKey, 142 | signature_box: &SignatureBox, 143 | mut data_reader: R, 144 | quiet: bool, 145 | output: bool, 146 | allow_legacy: bool, 147 | ) -> Result<()> 148 | where 149 | R: Read + Seek, 150 | { 151 | let data = if signature_box.is_prehashed() { 152 | prehash(&mut data_reader)? 153 | } else { 154 | let mut data = vec![]; 155 | data_reader.read_to_end(&mut data)?; 156 | data 157 | }; 158 | let sig = &signature_box.signature; 159 | if sig.keynum != pk.keynum_pk.keynum { 160 | return Err(PError::new( 161 | ErrorKind::Verify, 162 | format!( 163 | "Signature key id: {:016X} is different from public key: {:016X}", 164 | load_u64_le(&sig.keynum[..]), 165 | load_u64_le(&pk.keynum_pk.keynum[..]) 166 | ), 167 | )); 168 | } 169 | if !allow_legacy && !signature_box.is_prehashed() { 170 | return Err(PError::new( 171 | ErrorKind::Verify, 172 | "Legacy signatures are not accepted", 173 | )); 174 | } 175 | if !ed25519::verify(&data, &pk.keynum_pk.pk, &sig.sig) { 176 | return Err(PError::new( 177 | ErrorKind::Verify, 178 | "Signature verification failed", 179 | )); 180 | } 181 | match ( 182 | &signature_box.sig_and_trusted_comment, 183 | &signature_box.global_sig, 184 | ) { 185 | (Some(sig_and_trusted_comment), Some(global_sig)) => { 186 | if !ed25519::verify(sig_and_trusted_comment, &pk.keynum_pk.pk, &global_sig[..]) { 187 | return Err(PError::new( 188 | ErrorKind::Verify, 189 | "Comment signature verification failed", 190 | )); 191 | } 192 | } 193 | (None, None) => {} 194 | _ => { 195 | return Err(PError::new( 196 | ErrorKind::Verify, 197 | "Inconsistent signature presence for trusted comment presence", 198 | )) 199 | } 200 | }; 201 | if !quiet { 202 | eprintln!("Signature and comment signature verified"); 203 | if signature_box.global_sig.is_some() { 204 | eprintln!("Trusted comment: {}", signature_box.trusted_comment()?); 205 | } 206 | } 207 | if output { 208 | data_reader.rewind()?; 209 | let mut buf = vec![0; 65536]; 210 | loop { 211 | let len = data_reader.read(&mut buf)?; 212 | if len == 0 { 213 | break; 214 | } 215 | io::stdout().write_all(&buf[..len])?; 216 | } 217 | io::stdout().flush()?; 218 | } 219 | Ok(()) 220 | } 221 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn byte_array_store() { 3 | use crate::store_u64_le; 4 | 5 | assert_eq!([0xff, 0, 0, 0, 0, 0, 0, 0], store_u64_le(0xff)); 6 | } 7 | 8 | #[test] 9 | fn byte_array_load() { 10 | use crate::load_u64_le; 11 | 12 | assert_eq!(255, load_u64_le(&[0xff, 0, 0, 0, 0, 0, 0, 0])); 13 | } 14 | 15 | #[test] 16 | fn pk_key_struct_conversion() { 17 | use crate::{KeyPair, PublicKey}; 18 | 19 | let KeyPair { pk, .. } = KeyPair::generate_unencrypted_keypair().unwrap(); 20 | assert_eq!(pk, PublicKey::from_bytes(&pk.to_bytes()).unwrap()); 21 | } 22 | 23 | #[test] 24 | fn sk_key_struct_conversion() { 25 | use crate::{KeyPair, SecretKey}; 26 | 27 | let KeyPair { sk, .. } = KeyPair::generate_unencrypted_keypair().unwrap(); 28 | assert_eq!(sk, SecretKey::from_bytes(&sk.to_bytes()).unwrap()); 29 | } 30 | 31 | #[test] 32 | fn xor_keynum() { 33 | use getrandom::fill as getrandom; 34 | 35 | use crate::KeyPair; 36 | 37 | let KeyPair { mut sk, .. } = KeyPair::generate_unencrypted_keypair().unwrap(); 38 | let mut key = vec![0u8; sk.keynum_sk.len()]; 39 | getrandom(&mut key).unwrap(); 40 | let original_keynum = sk.keynum_sk.clone(); 41 | sk.xor_keynum(&key); 42 | assert_ne!(original_keynum, sk.keynum_sk); 43 | sk.xor_keynum(&key); 44 | assert_eq!(original_keynum, sk.keynum_sk); 45 | } 46 | 47 | #[test] 48 | fn sk_checksum() { 49 | use crate::KeyPair; 50 | 51 | let KeyPair { mut sk, .. } = KeyPair::generate_unencrypted_keypair().unwrap(); 52 | assert!(sk.write_checksum().is_ok()); 53 | assert_eq!(sk.keynum_sk.chk.to_vec(), sk.read_checksum().unwrap()); 54 | } 55 | 56 | #[test] 57 | fn load_public_key_string() { 58 | use crate::PublicKey; 59 | 60 | assert!( 61 | PublicKey::from_base64("RWRzq51bKcS8oJvZ4xEm+nRvGYPdsNRD3ciFPu1YJEL8Bl/3daWaj72r").is_ok() 62 | ); 63 | assert!( 64 | PublicKey::from_base64("RWQt7oYqpar/yePp+nonossdnononovlOSkkckMMfvHuGc+0+oShmJyN5Y") 65 | .is_err() 66 | ); 67 | } 68 | 69 | #[test] 70 | fn public_key_regenerate() { 71 | use std::io::Cursor; 72 | 73 | use crate::{sign, verify, KeyPair, PublicKey}; 74 | 75 | let KeyPair { pk, sk } = KeyPair::generate_unencrypted_keypair().unwrap(); 76 | let pk_regen = PublicKey::from_secret_key(&sk).unwrap(); 77 | assert!(pk_regen.to_base64() == pk.to_base64()); 78 | 79 | let data = b"test"; 80 | let sb = sign(None, &sk, Cursor::new(data), None, None).unwrap(); 81 | assert!(verify(&pk_regen, &sb, Cursor::new(data), true, false, false).is_ok()); 82 | } 83 | 84 | #[test] 85 | fn signature() { 86 | use std::io::Cursor; 87 | 88 | use crate::{sign, verify, KeyPair}; 89 | 90 | let KeyPair { pk, sk } = KeyPair::generate_unencrypted_keypair().unwrap(); 91 | let data = b"test"; 92 | let signature_box = sign(None, &sk, Cursor::new(data), None, None).unwrap(); 93 | verify(&pk, &signature_box, Cursor::new(data), true, false, false).unwrap(); 94 | let data = b"test2"; 95 | assert!(verify(&pk, &signature_box, Cursor::new(data), true, false, false).is_err()); 96 | } 97 | 98 | #[test] 99 | fn encrypted_key_signing_fails() { 100 | use std::io::Cursor; 101 | 102 | use crate::{sign, ErrorKind, KeyPair}; 103 | 104 | let KeyPair { sk, .. } = 105 | KeyPair::generate_encrypted_keypair(Some("password".to_string())).unwrap(); 106 | let data = b"test"; 107 | let result = sign(None, &sk, Cursor::new(data), None, None); 108 | 109 | assert!(result.is_err()); 110 | if let Err(err) = result { 111 | // Check that we get the specific EncryptedKey error 112 | match err.kind() { 113 | ErrorKind::EncryptedKey => { 114 | // This is expected - we should get an EncryptedKey error 115 | } 116 | _ => panic!("Expected EncryptedKey error, got: {:?}", err.kind()), 117 | } 118 | } 119 | } 120 | 121 | #[test] 122 | fn is_encrypted_detection() { 123 | use crate::KeyPair; 124 | 125 | let KeyPair { 126 | sk: unencrypted_sk, .. 127 | } = KeyPair::generate_unencrypted_keypair().unwrap(); 128 | assert!(!unencrypted_sk.is_encrypted()); 129 | 130 | let KeyPair { 131 | sk: encrypted_sk, .. 132 | } = KeyPair::generate_encrypted_keypair(Some("password".to_string())).unwrap(); 133 | assert!(encrypted_sk.is_encrypted()); 134 | } 135 | 136 | #[test] 137 | fn decrypt_key_process() { 138 | use crate::{KeyPair, SecretKeyBox}; 139 | 140 | // Generate encrypted keypair 141 | let KeyPair { 142 | sk: encrypted_sk, .. 143 | } = KeyPair::generate_encrypted_keypair(Some("password".to_string())).unwrap(); 144 | assert!( 145 | encrypted_sk.is_encrypted(), 146 | "Generated key should be encrypted" 147 | ); 148 | 149 | // Convert to box 150 | let sk_box_str = encrypted_sk.to_box(None).unwrap().to_string(); 151 | let sk_box = SecretKeyBox::from_string(&sk_box_str).unwrap(); 152 | 153 | // Decrypt the key 154 | let decrypted_sk = sk_box 155 | .into_secret_key(Some("password".to_string())) 156 | .unwrap(); 157 | assert!( 158 | !decrypted_sk.is_encrypted(), 159 | "Decrypted key should not be encrypted" 160 | ); 161 | } 162 | 163 | #[test] 164 | fn signature_bones() { 165 | use std::io::Cursor; 166 | 167 | use crate::{sign, verify, KeyPair, SignatureBones}; 168 | 169 | let KeyPair { pk, sk } = KeyPair::generate_unencrypted_keypair().unwrap(); 170 | let data = b"test"; 171 | let signature_box = sign(None, &sk, Cursor::new(data), None, None).unwrap(); 172 | let signature_bones: SignatureBones = signature_box.into(); 173 | verify( 174 | &pk, 175 | &signature_bones.clone().into(), 176 | Cursor::new(data), 177 | true, 178 | false, 179 | false, 180 | ) 181 | .unwrap(); 182 | let data = b"test2"; 183 | assert!(verify( 184 | &pk, 185 | &signature_bones.into(), 186 | Cursor::new(data), 187 | true, 188 | false, 189 | false 190 | ) 191 | .is_err()); 192 | } 193 | 194 | #[test] 195 | fn verify_det() { 196 | use std::io::Cursor; 197 | 198 | use crate::{verify, PublicKey, SignatureBox}; 199 | 200 | let pk = 201 | PublicKey::from_base64("RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3").unwrap(); 202 | let signature_box = SignatureBox::from_string( 203 | "untrusted comment: signature from minisign secret key 204 | RWQf6LRCGA9i59SLOFxz6NxvASXDJeRtuZykwQepbDEGt87ig1BNpWaVWuNrm73YiIiJbq71Wi+dP9eKL8OC351vwIasSSbXxwA= 205 | trusted comment: timestamp:1555779966\tfile:test 206 | QtKMXWyYcwdpZAlPF7tE2ENJkRd1ujvKjlj1m9RtHTBnZPa5WKU5uWRs5GoP5M/VqE81QFuMKI5k/SfNQUaOAA==", 207 | ) 208 | .unwrap(); 209 | assert!(!signature_box.is_prehashed()); 210 | assert_eq!( 211 | signature_box.untrusted_comment().unwrap(), 212 | "signature from minisign secret key" 213 | ); 214 | assert_eq!( 215 | signature_box.trusted_comment().unwrap(), 216 | "timestamp:1555779966\tfile:test" 217 | ); 218 | let bin = b"test"; 219 | verify(&pk, &signature_box, Cursor::new(bin), false, false, true) 220 | .expect("Signature didn't verify"); 221 | } 222 | 223 | #[test] 224 | fn verify_prehashed_det() { 225 | use std::io::Cursor; 226 | 227 | use crate::{verify, PublicKey, SignatureBox}; 228 | 229 | let pk = 230 | PublicKey::from_base64("RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3").unwrap(); 231 | let signature_box = SignatureBox::from_string( 232 | "untrusted comment: signature from minisign secret key 233 | RUQf6LRCGA9i559r3g7V1qNyJDApGip8MfqcadIgT9CuhV3EMhHoN1mGTkUidF/\ 234 | z7SrlQgXdy8ofjb7bNJJylDOocrCo8KLzZwo= 235 | trusted comment: timestamp:1556193335\tfile:test 236 | y/rUw2y8/hOUYjZU71eHp/Wo1KZ40fGy2VJEDl34XMJM+TX48Ss/17u3IvIfbVR1FkZZSNCisQbuQY+bHwhEBg==", 237 | ) 238 | .unwrap(); 239 | assert!(signature_box.is_prehashed()); 240 | assert_eq!( 241 | signature_box.untrusted_comment().unwrap(), 242 | "signature from minisign secret key" 243 | ); 244 | assert_eq!( 245 | signature_box.trusted_comment().unwrap(), 246 | "timestamp:1556193335\tfile:test" 247 | ); 248 | let bin = b"test"; 249 | verify(&pk, &signature_box, Cursor::new(bin), false, false, false) 250 | .expect("Signature with prehashing didn't verify"); 251 | } 252 | 253 | #[test] 254 | fn unencrypted_key() { 255 | use crate::{KeyPair, SecretKey}; 256 | 257 | let KeyPair { pk, sk } = KeyPair::generate_unencrypted_keypair().unwrap(); 258 | _ = pk; 259 | let sk_box = sk.to_box(None).unwrap(); 260 | let sk2 = SecretKey::from_box(sk_box.clone(), Some("".to_string())); 261 | assert!(sk2.is_err()); 262 | SecretKey::from_unencrypted_box(sk_box).unwrap(); 263 | } 264 | -------------------------------------------------------------------------------- /src/crypto/blake2b.rs: -------------------------------------------------------------------------------- 1 | // Licensed under the Apache License, Version 2.0 or the MIT license 3 | // , at your 4 | // option. This file may not be copied, modified, or distributed 5 | // except according to those terms. 6 | 7 | use super::cryptoutil::{copy_memory, read_u64v_le, write_u64v_le}; 8 | 9 | static IV: [u64; 8] = [ 10 | 0x6a09e667f3bcc908, 11 | 0xbb67ae8584caa73b, 12 | 0x3c6ef372fe94f82b, 13 | 0xa54ff53a5f1d36f1, 14 | 0x510e527fade682d1, 15 | 0x9b05688c2b3e6c1f, 16 | 0x1f83d9abfb41bd6b, 17 | 0x5be0cd19137e2179, 18 | ]; 19 | 20 | static SIGMA: [[usize; 16]; 12] = [ 21 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 22 | [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], 23 | [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], 24 | [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], 25 | [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], 26 | [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], 27 | [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], 28 | [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], 29 | [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], 30 | [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], 31 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 32 | [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], 33 | ]; 34 | 35 | pub const BLAKE2B_BLOCKBYTES: usize = 128; 36 | pub const BLAKE2B_OUTBYTES: usize = 64; 37 | pub const BLAKE2B_KEYBYTES: usize = 64; 38 | pub const BLAKE2B_SALTBYTES: usize = 16; 39 | pub const BLAKE2B_PERSONALBYTES: usize = 16; 40 | 41 | #[derive(Copy)] 42 | pub struct Blake2b { 43 | h: [u64; 8], 44 | t: [u64; 2], 45 | f: [u64; 2], 46 | buf: [u8; 2 * BLAKE2B_BLOCKBYTES], 47 | buflen: usize, 48 | _key: [u8; BLAKE2B_KEYBYTES], 49 | _key_length: u8, 50 | last_node: u8, 51 | digest_length: u8, 52 | computed: bool, // whether the final digest has been computed 53 | param: Blake2bParam, 54 | } 55 | 56 | impl Clone for Blake2b { 57 | fn clone(&self) -> Blake2b { 58 | *self 59 | } 60 | } 61 | 62 | #[derive(Copy, Clone)] 63 | struct Blake2bParam { 64 | digest_length: u8, 65 | key_length: u8, 66 | fanout: u8, 67 | depth: u8, 68 | leaf_length: u32, 69 | node_offset: u64, 70 | node_depth: u8, 71 | inner_length: u8, 72 | reserved: [u8; 14], 73 | salt: [u8; BLAKE2B_SALTBYTES], 74 | personal: [u8; BLAKE2B_PERSONALBYTES], 75 | } 76 | 77 | macro_rules! G( ($r:expr, $i:expr, $a:expr, $b:expr, $c:expr, $d:expr, $m:expr) => ({ 78 | $a = $a.wrapping_add($b).wrapping_add($m[SIGMA[$r][2*$i+0]]); 79 | $d = ($d ^ $a).rotate_right(32); 80 | $c = $c.wrapping_add($d); 81 | $b = ($b ^ $c).rotate_right(24); 82 | $a = $a.wrapping_add($b).wrapping_add($m[SIGMA[$r][2*$i+1]]); 83 | $d = ($d ^ $a).rotate_right(16); 84 | $c = $c .wrapping_add($d); 85 | $b = ($b ^ $c).rotate_right(63); 86 | })); 87 | 88 | macro_rules! round( ($r:expr, $v:expr, $m:expr) => ( { 89 | G!($r,0,$v[ 0],$v[ 4],$v[ 8],$v[12], $m); 90 | G!($r,1,$v[ 1],$v[ 5],$v[ 9],$v[13], $m); 91 | G!($r,2,$v[ 2],$v[ 6],$v[10],$v[14], $m); 92 | G!($r,3,$v[ 3],$v[ 7],$v[11],$v[15], $m); 93 | G!($r,4,$v[ 0],$v[ 5],$v[10],$v[15], $m); 94 | G!($r,5,$v[ 1],$v[ 6],$v[11],$v[12], $m); 95 | G!($r,6,$v[ 2],$v[ 7],$v[ 8],$v[13], $m); 96 | G!($r,7,$v[ 3],$v[ 4],$v[ 9],$v[14], $m); 97 | } 98 | )); 99 | 100 | impl Blake2b { 101 | fn set_lastnode(&mut self) { 102 | self.f[1] = 0xffffffffffffffff; 103 | } 104 | 105 | fn set_lastblock(&mut self) { 106 | if self.last_node != 0 { 107 | self.set_lastnode(); 108 | } 109 | self.f[0] = 0xffffffffffffffff; 110 | } 111 | 112 | fn increment_counter(&mut self, inc: u64) { 113 | self.t[0] += inc; 114 | self.t[1] += if self.t[0] < inc { 1 } else { 0 }; 115 | } 116 | 117 | fn init0(param: Blake2bParam, digest_length: u8) -> Blake2b { 118 | Blake2b { 119 | h: IV, 120 | t: [0, 0], 121 | f: [0, 0], 122 | buf: [0; 2 * BLAKE2B_BLOCKBYTES], 123 | buflen: 0, 124 | last_node: 0, 125 | digest_length, 126 | computed: false, 127 | _key: [0; BLAKE2B_KEYBYTES], 128 | _key_length: 0u8, 129 | param, 130 | } 131 | } 132 | 133 | fn apply_param(&mut self) { 134 | use std::io::Write; 135 | 136 | use super::cryptoutil::WriteExt; 137 | 138 | let mut param_bytes: [u8; 64] = [0; 64]; 139 | { 140 | let mut writer: &mut [u8] = &mut param_bytes; 141 | writer.write_u8(self.param.digest_length).unwrap(); 142 | writer.write_u8(self.param.key_length).unwrap(); 143 | writer.write_u8(self.param.fanout).unwrap(); 144 | writer.write_u8(self.param.depth).unwrap(); 145 | writer.write_u32_le(self.param.leaf_length).unwrap(); 146 | writer.write_u64_le(self.param.node_offset).unwrap(); 147 | writer.write_u8(self.param.node_depth).unwrap(); 148 | writer.write_u8(self.param.inner_length).unwrap(); 149 | writer.write_all(&self.param.reserved).unwrap(); 150 | writer.write_all(&self.param.salt).unwrap(); 151 | writer.write_all(&self.param.personal).unwrap(); 152 | } 153 | 154 | let mut param_words: [u64; 8] = [0; 8]; 155 | read_u64v_le(&mut param_words, ¶m_bytes); 156 | for (h, param_word) in self.h.iter_mut().zip(param_words.iter()) { 157 | *h ^= *param_word; 158 | } 159 | } 160 | 161 | // init xors IV with input parameter block 162 | fn init_param(p: Blake2bParam) -> Blake2b { 163 | let mut b = Blake2b::init0(p, p.digest_length); 164 | b.apply_param(); 165 | b 166 | } 167 | 168 | fn default_param(outlen: u8) -> Blake2bParam { 169 | Blake2bParam { 170 | digest_length: outlen, 171 | key_length: 0, 172 | fanout: 1, 173 | depth: 1, 174 | leaf_length: 0, 175 | node_offset: 0, 176 | node_depth: 0, 177 | inner_length: 0, 178 | reserved: [0; 14], 179 | salt: [0; BLAKE2B_SALTBYTES], 180 | personal: [0; BLAKE2B_PERSONALBYTES], 181 | } 182 | } 183 | 184 | pub fn new(outlen: usize) -> Blake2b { 185 | assert!(outlen > 0 && outlen <= BLAKE2B_OUTBYTES); 186 | Blake2b::init_param(Blake2b::default_param(outlen as u8)) 187 | } 188 | 189 | fn compress(&mut self) { 190 | let mut ms: [u64; 16] = [0; 16]; 191 | let mut vs: [u64; 16] = [0; 16]; 192 | 193 | read_u64v_le(&mut ms, &self.buf[0..BLAKE2B_BLOCKBYTES]); 194 | 195 | for (v, h) in vs.iter_mut().zip(self.h.iter()) { 196 | *v = *h; 197 | } 198 | 199 | vs[8] = IV[0]; 200 | vs[9] = IV[1]; 201 | vs[10] = IV[2]; 202 | vs[11] = IV[3]; 203 | vs[12] = self.t[0] ^ IV[4]; 204 | vs[13] = self.t[1] ^ IV[5]; 205 | vs[14] = self.f[0] ^ IV[6]; 206 | vs[15] = self.f[1] ^ IV[7]; 207 | round!(0, vs, ms); 208 | round!(1, vs, ms); 209 | round!(2, vs, ms); 210 | round!(3, vs, ms); 211 | round!(4, vs, ms); 212 | round!(5, vs, ms); 213 | round!(6, vs, ms); 214 | round!(7, vs, ms); 215 | round!(8, vs, ms); 216 | round!(9, vs, ms); 217 | round!(10, vs, ms); 218 | round!(11, vs, ms); 219 | 220 | for (h_elem, (v_low, v_high)) in 221 | self.h.iter_mut().zip(vs[0..8].iter().zip(vs[8..16].iter())) 222 | { 223 | *h_elem = *h_elem ^ *v_low ^ *v_high; 224 | } 225 | } 226 | 227 | pub fn update(&mut self, mut input: &[u8]) { 228 | while !input.is_empty() { 229 | let left = self.buflen; 230 | let fill = 2 * BLAKE2B_BLOCKBYTES - left; 231 | 232 | if input.len() > fill { 233 | copy_memory(&input[0..fill], &mut self.buf[left..]); // Fill buffer 234 | self.buflen += fill; 235 | self.increment_counter(BLAKE2B_BLOCKBYTES as u64); 236 | self.compress(); 237 | 238 | let mut halves = self.buf.chunks_mut(BLAKE2B_BLOCKBYTES); 239 | let first_half = halves.next().unwrap(); 240 | let second_half = halves.next().unwrap(); 241 | copy_memory(second_half, first_half); 242 | 243 | self.buflen -= BLAKE2B_BLOCKBYTES; 244 | input = &input[fill..input.len()]; 245 | } else { 246 | // inlen <= fill 247 | copy_memory(input, &mut self.buf[left..]); 248 | self.buflen += input.len(); 249 | break; 250 | } 251 | } 252 | } 253 | 254 | pub fn finalize(&mut self, out: &mut [u8]) { 255 | assert!(out.len() == self.digest_length as usize); 256 | if !self.computed { 257 | if self.buflen > BLAKE2B_BLOCKBYTES { 258 | self.increment_counter(BLAKE2B_BLOCKBYTES as u64); 259 | self.compress(); 260 | self.buflen -= BLAKE2B_BLOCKBYTES; 261 | 262 | let mut halves = self.buf.chunks_mut(BLAKE2B_BLOCKBYTES); 263 | let first_half = halves.next().unwrap(); 264 | let second_half = halves.next().unwrap(); 265 | copy_memory(second_half, first_half); 266 | } 267 | 268 | let incby = self.buflen as u64; 269 | self.increment_counter(incby); 270 | self.set_lastblock(); 271 | for b in self.buf[self.buflen..].iter_mut() { 272 | *b = 0; 273 | } 274 | self.compress(); 275 | 276 | write_u64v_le(&mut self.buf[0..64], &self.h); 277 | self.computed = true; 278 | } 279 | let outlen = out.len(); 280 | copy_memory(&self.buf[0..outlen], out); 281 | } 282 | 283 | #[allow(dead_code)] 284 | pub fn blake2b(out: &mut [u8], input: &[u8]) { 285 | let mut hasher: Blake2b = Blake2b::new(out.len()); 286 | hasher.update(input); 287 | hasher.finalize(out); 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /src/crypto/sha512.rs: -------------------------------------------------------------------------------- 1 | //! A small, self-contained SHA512 implementation 2 | //! (C) Frank Denis , public domain 3 | 4 | #![allow( 5 | non_snake_case, 6 | clippy::cast_lossless, 7 | clippy::eq_op, 8 | clippy::identity_op, 9 | clippy::many_single_char_names, 10 | clippy::unreadable_literal 11 | )] 12 | 13 | #[inline(always)] 14 | fn load_be(base: &[u8], offset: usize) -> u64 { 15 | let addr = &base[offset..]; 16 | (addr[7] as u64) 17 | | (addr[6] as u64) << 8 18 | | (addr[5] as u64) << 16 19 | | (addr[4] as u64) << 24 20 | | (addr[3] as u64) << 32 21 | | (addr[2] as u64) << 40 22 | | (addr[1] as u64) << 48 23 | | (addr[0] as u64) << 56 24 | } 25 | 26 | #[inline(always)] 27 | fn store_be(base: &mut [u8], offset: usize, x: u64) { 28 | let addr = &mut base[offset..]; 29 | addr[7] = x as u8; 30 | addr[6] = (x >> 8) as u8; 31 | addr[5] = (x >> 16) as u8; 32 | addr[4] = (x >> 24) as u8; 33 | addr[3] = (x >> 32) as u8; 34 | addr[2] = (x >> 40) as u8; 35 | addr[1] = (x >> 48) as u8; 36 | addr[0] = (x >> 56) as u8; 37 | } 38 | 39 | struct W([u64; 16]); 40 | 41 | #[derive(Copy, Clone)] 42 | struct State([u64; 8]); 43 | 44 | impl W { 45 | fn new(input: &[u8]) -> Self { 46 | let mut w = [0u64; 16]; 47 | for (i, e) in w.iter_mut().enumerate() { 48 | *e = load_be(input, i * 8) 49 | } 50 | W(w) 51 | } 52 | 53 | #[inline(always)] 54 | fn Ch(x: u64, y: u64, z: u64) -> u64 { 55 | (x & y) ^ (!x & z) 56 | } 57 | 58 | #[inline(always)] 59 | fn Maj(x: u64, y: u64, z: u64) -> u64 { 60 | (x & y) ^ (x & z) ^ (y & z) 61 | } 62 | 63 | #[inline(always)] 64 | fn Sigma0(x: u64) -> u64 { 65 | x.rotate_right(28) ^ x.rotate_right(34) ^ x.rotate_right(39) 66 | } 67 | 68 | #[inline(always)] 69 | fn Sigma1(x: u64) -> u64 { 70 | x.rotate_right(14) ^ x.rotate_right(18) ^ x.rotate_right(41) 71 | } 72 | 73 | #[inline(always)] 74 | fn sigma0(x: u64) -> u64 { 75 | x.rotate_right(1) ^ x.rotate_right(8) ^ (x >> 7) 76 | } 77 | 78 | #[inline(always)] 79 | fn sigma1(x: u64) -> u64 { 80 | x.rotate_right(19) ^ x.rotate_right(61) ^ (x >> 6) 81 | } 82 | 83 | #[inline(always)] 84 | fn M(&mut self, a: usize, b: usize, c: usize, d: usize) { 85 | let w = &mut self.0; 86 | w[a] = w[a] 87 | .wrapping_add(Self::sigma1(w[b])) 88 | .wrapping_add(w[c]) 89 | .wrapping_add(Self::sigma0(w[d])); 90 | } 91 | 92 | #[inline] 93 | fn expand(&mut self) { 94 | self.M(0, (0 + 14) & 15, (0 + 9) & 15, (0 + 1) & 15); 95 | self.M(1, (1 + 14) & 15, (1 + 9) & 15, (1 + 1) & 15); 96 | self.M(2, (2 + 14) & 15, (2 + 9) & 15, (2 + 1) & 15); 97 | self.M(3, (3 + 14) & 15, (3 + 9) & 15, (3 + 1) & 15); 98 | self.M(4, (4 + 14) & 15, (4 + 9) & 15, (4 + 1) & 15); 99 | self.M(5, (5 + 14) & 15, (5 + 9) & 15, (5 + 1) & 15); 100 | self.M(6, (6 + 14) & 15, (6 + 9) & 15, (6 + 1) & 15); 101 | self.M(7, (7 + 14) & 15, (7 + 9) & 15, (7 + 1) & 15); 102 | self.M(8, (8 + 14) & 15, (8 + 9) & 15, (8 + 1) & 15); 103 | self.M(9, (9 + 14) & 15, (9 + 9) & 15, (9 + 1) & 15); 104 | self.M(10, (10 + 14) & 15, (10 + 9) & 15, (10 + 1) & 15); 105 | self.M(11, (11 + 14) & 15, (11 + 9) & 15, (11 + 1) & 15); 106 | self.M(12, (12 + 14) & 15, (12 + 9) & 15, (12 + 1) & 15); 107 | self.M(13, (13 + 14) & 15, (13 + 9) & 15, (13 + 1) & 15); 108 | self.M(14, (14 + 14) & 15, (14 + 9) & 15, (14 + 1) & 15); 109 | self.M(15, (15 + 14) & 15, (15 + 9) & 15, (15 + 1) & 15); 110 | } 111 | 112 | #[inline(always)] 113 | fn F(&mut self, state: &mut State, i: usize, k: u64) { 114 | let t = &mut state.0; 115 | t[(16 - i + 7) & 7] = t[(16 - i + 7) & 7] 116 | .wrapping_add(Self::Sigma1(t[(16 - i + 4) & 7])) 117 | .wrapping_add(Self::Ch( 118 | t[(16 - i + 4) & 7], 119 | t[(16 - i + 5) & 7], 120 | t[(16 - i + 6) & 7], 121 | )) 122 | .wrapping_add(k) 123 | .wrapping_add(self.0[i]); 124 | t[(16 - i + 3) & 7] = t[(16 - i + 3) & 7].wrapping_add(t[(16 - i + 7) & 7]); 125 | t[(16 - i + 7) & 7] = t[(16 - i + 7) & 7] 126 | .wrapping_add(Self::Sigma0(t[(16 - i + 0) & 7])) 127 | .wrapping_add(Self::Maj( 128 | t[(16 - i + 0) & 7], 129 | t[(16 - i + 1) & 7], 130 | t[(16 - i + 2) & 7], 131 | )); 132 | } 133 | 134 | fn G(&mut self, state: &mut State, s: usize) { 135 | const ROUND_CONSTANTS: [u64; 80] = [ 136 | 0x428a2f98d728ae22, 137 | 0x7137449123ef65cd, 138 | 0xb5c0fbcfec4d3b2f, 139 | 0xe9b5dba58189dbbc, 140 | 0x3956c25bf348b538, 141 | 0x59f111f1b605d019, 142 | 0x923f82a4af194f9b, 143 | 0xab1c5ed5da6d8118, 144 | 0xd807aa98a3030242, 145 | 0x12835b0145706fbe, 146 | 0x243185be4ee4b28c, 147 | 0x550c7dc3d5ffb4e2, 148 | 0x72be5d74f27b896f, 149 | 0x80deb1fe3b1696b1, 150 | 0x9bdc06a725c71235, 151 | 0xc19bf174cf692694, 152 | 0xe49b69c19ef14ad2, 153 | 0xefbe4786384f25e3, 154 | 0x0fc19dc68b8cd5b5, 155 | 0x240ca1cc77ac9c65, 156 | 0x2de92c6f592b0275, 157 | 0x4a7484aa6ea6e483, 158 | 0x5cb0a9dcbd41fbd4, 159 | 0x76f988da831153b5, 160 | 0x983e5152ee66dfab, 161 | 0xa831c66d2db43210, 162 | 0xb00327c898fb213f, 163 | 0xbf597fc7beef0ee4, 164 | 0xc6e00bf33da88fc2, 165 | 0xd5a79147930aa725, 166 | 0x06ca6351e003826f, 167 | 0x142929670a0e6e70, 168 | 0x27b70a8546d22ffc, 169 | 0x2e1b21385c26c926, 170 | 0x4d2c6dfc5ac42aed, 171 | 0x53380d139d95b3df, 172 | 0x650a73548baf63de, 173 | 0x766a0abb3c77b2a8, 174 | 0x81c2c92e47edaee6, 175 | 0x92722c851482353b, 176 | 0xa2bfe8a14cf10364, 177 | 0xa81a664bbc423001, 178 | 0xc24b8b70d0f89791, 179 | 0xc76c51a30654be30, 180 | 0xd192e819d6ef5218, 181 | 0xd69906245565a910, 182 | 0xf40e35855771202a, 183 | 0x106aa07032bbd1b8, 184 | 0x19a4c116b8d2d0c8, 185 | 0x1e376c085141ab53, 186 | 0x2748774cdf8eeb99, 187 | 0x34b0bcb5e19b48a8, 188 | 0x391c0cb3c5c95a63, 189 | 0x4ed8aa4ae3418acb, 190 | 0x5b9cca4f7763e373, 191 | 0x682e6ff3d6b2b8a3, 192 | 0x748f82ee5defb2fc, 193 | 0x78a5636f43172f60, 194 | 0x84c87814a1f0ab72, 195 | 0x8cc702081a6439ec, 196 | 0x90befffa23631e28, 197 | 0xa4506cebde82bde9, 198 | 0xbef9a3f7b2c67915, 199 | 0xc67178f2e372532b, 200 | 0xca273eceea26619c, 201 | 0xd186b8c721c0c207, 202 | 0xeada7dd6cde0eb1e, 203 | 0xf57d4f7fee6ed178, 204 | 0x06f067aa72176fba, 205 | 0x0a637dc5a2c898a6, 206 | 0x113f9804bef90dae, 207 | 0x1b710b35131c471b, 208 | 0x28db77f523047d84, 209 | 0x32caab7b40c72493, 210 | 0x3c9ebe0a15c9bebc, 211 | 0x431d67c49c100d4c, 212 | 0x4cc5d4becb3e42b6, 213 | 0x597f299cfc657e2a, 214 | 0x5fcb6fab3ad6faec, 215 | 0x6c44198c4a475817, 216 | ]; 217 | let rc = &ROUND_CONSTANTS[s * 16..]; 218 | self.F(state, 0, rc[0]); 219 | self.F(state, 1, rc[1]); 220 | self.F(state, 2, rc[2]); 221 | self.F(state, 3, rc[3]); 222 | self.F(state, 4, rc[4]); 223 | self.F(state, 5, rc[5]); 224 | self.F(state, 6, rc[6]); 225 | self.F(state, 7, rc[7]); 226 | self.F(state, 8, rc[8]); 227 | self.F(state, 9, rc[9]); 228 | self.F(state, 10, rc[10]); 229 | self.F(state, 11, rc[11]); 230 | self.F(state, 12, rc[12]); 231 | self.F(state, 13, rc[13]); 232 | self.F(state, 14, rc[14]); 233 | self.F(state, 15, rc[15]); 234 | } 235 | } 236 | 237 | impl State { 238 | fn new() -> Self { 239 | const IV: [u8; 64] = [ 240 | 0x6a, 0x09, 0xe6, 0x67, 0xf3, 0xbc, 0xc9, 0x08, 0xbb, 0x67, 0xae, 0x85, 0x84, 0xca, 241 | 0xa7, 0x3b, 0x3c, 0x6e, 0xf3, 0x72, 0xfe, 0x94, 0xf8, 0x2b, 0xa5, 0x4f, 0xf5, 0x3a, 242 | 0x5f, 0x1d, 0x36, 0xf1, 0x51, 0x0e, 0x52, 0x7f, 0xad, 0xe6, 0x82, 0xd1, 0x9b, 0x05, 243 | 0x68, 0x8c, 0x2b, 0x3e, 0x6c, 0x1f, 0x1f, 0x83, 0xd9, 0xab, 0xfb, 0x41, 0xbd, 0x6b, 244 | 0x5b, 0xe0, 0xcd, 0x19, 0x13, 0x7e, 0x21, 0x79, 245 | ]; 246 | let mut t = [0u64; 8]; 247 | for (i, e) in t.iter_mut().enumerate() { 248 | *e = load_be(&IV, i * 8) 249 | } 250 | State(t) 251 | } 252 | 253 | #[inline(always)] 254 | fn add(&mut self, x: &State) { 255 | let sx = &mut self.0; 256 | let ex = &x.0; 257 | sx[0] = sx[0].wrapping_add(ex[0]); 258 | sx[1] = sx[1].wrapping_add(ex[1]); 259 | sx[2] = sx[2].wrapping_add(ex[2]); 260 | sx[3] = sx[3].wrapping_add(ex[3]); 261 | sx[4] = sx[4].wrapping_add(ex[4]); 262 | sx[5] = sx[5].wrapping_add(ex[5]); 263 | sx[6] = sx[6].wrapping_add(ex[6]); 264 | sx[7] = sx[7].wrapping_add(ex[7]); 265 | } 266 | 267 | fn store(&self, out: &mut [u8]) { 268 | for (i, &e) in self.0.iter().enumerate() { 269 | store_be(out, i * 8, e); 270 | } 271 | } 272 | 273 | fn blocks(&mut self, mut input: &[u8]) -> usize { 274 | let mut t = *self; 275 | let mut inlen = input.len(); 276 | while inlen >= 128 { 277 | let mut w = W::new(input); 278 | w.G(&mut t, 0); 279 | w.expand(); 280 | w.G(&mut t, 1); 281 | w.expand(); 282 | w.G(&mut t, 2); 283 | w.expand(); 284 | w.G(&mut t, 3); 285 | w.expand(); 286 | w.G(&mut t, 4); 287 | t.add(self); 288 | self.0 = t.0; 289 | input = &input[128..]; 290 | inlen -= 128; 291 | } 292 | inlen 293 | } 294 | } 295 | 296 | #[derive(Copy, Clone)] 297 | pub struct Hash { 298 | state: State, 299 | w: [u8; 128], 300 | r: usize, 301 | len: usize, 302 | } 303 | 304 | impl Hash { 305 | pub fn new() -> Hash { 306 | Hash { 307 | state: State::new(), 308 | r: 0, 309 | w: [0u8; 128], 310 | len: 0, 311 | } 312 | } 313 | 314 | /// Absorb content 315 | pub fn update>(&mut self, input: T) { 316 | let input = input.as_ref(); 317 | let mut n = input.len(); 318 | self.len += n; 319 | let av = 128 - self.r; 320 | let tc = ::core::cmp::min(n, av); 321 | self.w[self.r..self.r + tc].copy_from_slice(&input[0..tc]); 322 | self.r += tc; 323 | n -= tc; 324 | let pos = tc; 325 | if self.r == 128 { 326 | self.state.blocks(&self.w); 327 | self.r = 0; 328 | } 329 | if self.r == 0 && n > 0 { 330 | let rb = self.state.blocks(&input[pos..]); 331 | if rb > 0 { 332 | self.w[..rb].copy_from_slice(&input[pos + n - rb..]); 333 | self.r = rb; 334 | } 335 | } 336 | } 337 | 338 | /// Compute SHA512(absorbed content) 339 | pub fn finalize(mut self) -> [u8; 64] { 340 | let mut padded = [0u8; 256]; 341 | padded[..self.r].copy_from_slice(&self.w[..self.r]); 342 | padded[self.r] = 0x80; 343 | let r = if self.r < 112 { 128 } else { 256 }; 344 | let bits = self.len * 8; 345 | for i in 0..8 { 346 | padded[r - 8 + i] = (bits as u64 >> (56 - i * 8)) as u8; 347 | } 348 | self.state.blocks(&padded[..r]); 349 | let mut out = [0u8; 64]; 350 | self.state.store(&mut out); 351 | out 352 | } 353 | 354 | /// Compute SHA512(`input`) 355 | pub fn hash>(input: T) -> [u8; 64] { 356 | let mut h = Hash::new(); 357 | h.update(input); 358 | h.finalize() 359 | } 360 | } 361 | 362 | impl Default for Hash { 363 | fn default() -> Self { 364 | Self::new() 365 | } 366 | } 367 | -------------------------------------------------------------------------------- /src/secret_key.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use std::fmt::Write as fmtWrite; 3 | use std::fmt::{self, Formatter}; 4 | use std::fs; 5 | use std::io::{self, Write}; 6 | use std::io::{Cursor, Read}; 7 | use std::path::Path; 8 | 9 | use ct_codecs::{Base64, Decoder, Encoder}; 10 | 11 | use crate::constants::*; 12 | use crate::crypto::blake2b::Blake2b; 13 | use crate::crypto::util::fixed_time_eq; 14 | use crate::errors::*; 15 | use crate::helpers::*; 16 | use crate::keynum::*; 17 | use crate::Result; 18 | 19 | /// A secret key and its metadata. 20 | /// 21 | /// A `SecretKeyBox` represents a raw secret key, along with a key 22 | /// identifier, an untrusted description, and information required to 23 | /// decrypt it using a password. 24 | /// 25 | /// This is what usually gets exported to disk. 26 | /// 27 | /// A `SecretKeyBox` can be directly converted to/from a single-line string. 28 | #[derive(Clone, Debug)] 29 | pub struct SecretKeyBox(String); 30 | 31 | impl From for String { 32 | fn from(skb: SecretKeyBox) -> String { 33 | skb.0 34 | } 35 | } 36 | 37 | impl From for SecretKeyBox { 38 | fn from(s: String) -> SecretKeyBox { 39 | SecretKeyBox(s) 40 | } 41 | } 42 | 43 | impl std::fmt::Display for SecretKeyBox { 44 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 45 | write!(f, "{}", self.0) 46 | } 47 | } 48 | 49 | impl SecretKeyBox { 50 | /// Create a new `SecretKeyBox` from a string. 51 | pub fn from_string(s: &str) -> Result { 52 | Ok(s.to_string().into()) 53 | } 54 | 55 | /// Return a `SecretKeyBox` for a string, for storage. 56 | pub fn into_string(self) -> String { 57 | self.into() 58 | } 59 | 60 | /// Convert a `SecretKeyBox` to a string, for storage. 61 | /// If `password` is `None`, a password is going to be prompted interactively. 62 | pub fn into_secret_key(self, password: Option) -> Result { 63 | SecretKey::from_box(self, password) 64 | } 65 | 66 | /// Convert an unencrypted `SecretKeyBox` to a string, for storage. 67 | pub fn into_unencrypted_secret_key(self) -> Result { 68 | SecretKey::from_unencrypted_box(self) 69 | } 70 | 71 | /// Return a byte representation of the secret key, for storage. 72 | pub fn to_bytes(&self) -> Vec { 73 | self.to_string().as_bytes().to_vec() 74 | } 75 | } 76 | 77 | /// A `SecretKey` is used to create signatures. 78 | #[derive(Clone)] 79 | pub struct SecretKey { 80 | pub(crate) sig_alg: [u8; TWOBYTES], 81 | pub(crate) kdf_alg: [u8; TWOBYTES], 82 | pub(crate) chk_alg: [u8; TWOBYTES], 83 | pub(crate) kdf_salt: [u8; KDF_SALTBYTES], 84 | pub(crate) kdf_opslimit_le: [u8; KEYNUM_BYTES], 85 | pub(crate) kdf_memlimit_le: [u8; KEYNUM_BYTES], 86 | pub(crate) keynum_sk: KeynumSK, 87 | } 88 | 89 | impl SecretKey { 90 | pub(crate) fn write_checksum(&mut self) -> Result<()> { 91 | let h = self.read_checksum()?; 92 | self.keynum_sk.chk.copy_from_slice(&h[..]); 93 | Ok(()) 94 | } 95 | 96 | pub(crate) fn read_checksum(&self) -> Result> { 97 | let mut state = Blake2b::new(CHK_BYTES); 98 | state.update(&self.sig_alg); 99 | state.update(&self.keynum_sk.keynum); 100 | state.update(&self.keynum_sk.sk); 101 | let mut h = vec![0u8; CHK_BYTES]; 102 | state.finalize(&mut h); 103 | Ok(h) 104 | } 105 | 106 | pub(crate) fn xor_keynum(&mut self, stream: &[u8]) { 107 | for (byte, stream) in self.keynum_sk.keynum.iter_mut().zip(stream.iter()) { 108 | *byte ^= *stream 109 | } 110 | let keynum_len = self.keynum_sk.keynum.len(); 111 | for (byte, stream) in self 112 | .keynum_sk 113 | .sk 114 | .iter_mut() 115 | .zip(stream[keynum_len..].iter()) 116 | { 117 | *byte ^= *stream 118 | } 119 | let sk_len = self.keynum_sk.sk.len(); 120 | for (byte, stream) in self 121 | .keynum_sk 122 | .chk 123 | .iter_mut() 124 | .zip(stream[keynum_len + sk_len..].iter()) 125 | { 126 | *byte ^= *stream 127 | } 128 | } 129 | 130 | pub(crate) fn encrypt(mut self, password: String) -> Result { 131 | let mut stream = [0u8; CHK_BYTES + SECRETKEY_BYTES + KEYNUM_BYTES]; 132 | let opslimit = load_u64_le(&self.kdf_opslimit_le); 133 | let memlimit = load_u64_le(&self.kdf_memlimit_le) as usize; 134 | if memlimit > MEMLIMIT_MAX { 135 | return Err(PError::new(ErrorKind::KDF, "scrypt parameters too high")); 136 | } 137 | let params = raw_scrypt_params(memlimit, opslimit, N_LOG2_MAX)?; 138 | scrypt::scrypt(password.as_bytes(), &self.kdf_salt, ¶ms, &mut stream)?; 139 | self.xor_keynum(&stream); 140 | Ok(self) 141 | } 142 | 143 | /// The key identifier of this secret key. 144 | pub fn keynum(&self) -> &[u8] { 145 | &self.keynum_sk.keynum[..] 146 | } 147 | 148 | /// Returns `true` if this secret key is encrypted and requires a password to use. 149 | /// This checks both the encryption algorithm and whether the key material has been properly decrypted. 150 | pub fn is_encrypted(&self) -> bool { 151 | if self.kdf_alg == KDF_NONE { 152 | return false; 153 | } 154 | 155 | // For encrypted keys, verify that the key material is valid by checking the checksum 156 | // If the checksum doesn't match, the key is still encrypted 157 | match self.read_checksum() { 158 | Ok(checksum_vec) => { 159 | let mut expected_chk = [0u8; CHK_BYTES]; 160 | expected_chk.copy_from_slice(&checksum_vec[..]); 161 | expected_chk != self.keynum_sk.chk 162 | } 163 | Err(_) => true, // If we can't read checksum, assume encrypted 164 | } 165 | } 166 | 167 | /// Deserialize a `SecretKey`. 168 | /// 169 | /// For storage, a `SecretKeyBox` is usually what you need instead. 170 | pub fn from_bytes(bytes_buf: &[u8]) -> Result { 171 | let mut buf = Cursor::new(bytes_buf); 172 | let mut sig_alg = [0u8; TWOBYTES]; 173 | let mut kdf_alg = [0u8; TWOBYTES]; 174 | let mut chk_alg = [0u8; TWOBYTES]; 175 | let mut kdf_salt = [0u8; KDF_SALTBYTES]; 176 | let mut ops_limit = [0u8; KEYNUM_BYTES]; 177 | let mut mem_limit = [0u8; KEYNUM_BYTES]; 178 | let mut keynum = [0u8; KEYNUM_BYTES]; 179 | let mut sk = [0u8; SECRETKEY_BYTES]; 180 | let mut chk = [0u8; CHK_BYTES]; 181 | buf.read_exact(&mut sig_alg)?; 182 | buf.read_exact(&mut kdf_alg)?; 183 | buf.read_exact(&mut chk_alg)?; 184 | buf.read_exact(&mut kdf_salt)?; 185 | buf.read_exact(&mut ops_limit)?; 186 | buf.read_exact(&mut mem_limit)?; 187 | buf.read_exact(&mut keynum)?; 188 | buf.read_exact(&mut sk)?; 189 | buf.read_exact(&mut chk)?; 190 | 191 | Ok(SecretKey { 192 | sig_alg, 193 | kdf_alg, 194 | chk_alg, 195 | kdf_salt, 196 | kdf_opslimit_le: ops_limit, 197 | kdf_memlimit_le: mem_limit, 198 | keynum_sk: KeynumSK { keynum, sk, chk }, 199 | }) 200 | } 201 | 202 | /// Serialize a `SecretKey`. 203 | /// 204 | /// For storage, a `SecretKeyBox` is usually what you need instead. 205 | pub fn to_bytes(&self) -> Vec { 206 | let mut iters = Vec::new(); 207 | iters.push(self.sig_alg.iter()); 208 | iters.push(self.kdf_alg.iter()); 209 | iters.push(self.chk_alg.iter()); 210 | iters.push(self.kdf_salt.iter()); 211 | iters.push(self.kdf_opslimit_le.iter()); 212 | iters.push(self.kdf_memlimit_le.iter()); 213 | iters.push(self.keynum_sk.keynum.iter()); 214 | iters.push(self.keynum_sk.sk.iter()); 215 | iters.push(self.keynum_sk.chk.iter()); 216 | let v: Vec = iters.iter().flat_map(|b| b.clone().cloned()).collect(); 217 | v 218 | } 219 | 220 | fn from_box_( 221 | sk_box: SecretKeyBox, 222 | password: Option, 223 | unencrypted_key: bool, 224 | ) -> Result { 225 | let s = sk_box.0; 226 | let mut lines = s.lines(); 227 | lines.next().ok_or_else(|| { 228 | PError::new(ErrorKind::Io, "Missing comment in secret key".to_string()) 229 | })?; 230 | let encoded_sk = lines.next().ok_or_else(|| { 231 | PError::new( 232 | ErrorKind::Io, 233 | "Missing encoded key in secret key".to_string(), 234 | ) 235 | })?; 236 | let mut sk = SecretKey::from_base64(encoded_sk)?; 237 | if unencrypted_key { 238 | if sk.kdf_alg != KDF_NONE { 239 | return Err(PError::new( 240 | ErrorKind::Io, 241 | "Key might be encrypted".to_string(), 242 | )); 243 | } 244 | } else { 245 | match sk.kdf_alg { 246 | KDF_NONE => { 247 | return Err(PError::new( 248 | ErrorKind::Io, 249 | "Key might be encrypted".to_string(), 250 | )) 251 | } 252 | KDF_ALG => {} 253 | _ => { 254 | return Err(PError::new( 255 | ErrorKind::Io, 256 | "Unsupported encryption algorithm".to_string(), 257 | )) 258 | } 259 | } 260 | let interactive = password.is_none(); 261 | let password = match password { 262 | Some(password) => password, 263 | None => { 264 | let password = get_password("Password: ")?; 265 | write!( 266 | io::stdout(), 267 | "Deriving a key from the password and decrypting the secret key... " 268 | ) 269 | .map_err(|e| PError::new(ErrorKind::Io, e))?; 270 | io::stdout().flush()?; 271 | password 272 | } 273 | }; 274 | sk = sk.encrypt(password)?; 275 | if interactive { 276 | writeln!(io::stdout(), "done").map_err(|e| PError::new(ErrorKind::Io, e))? 277 | } 278 | } 279 | let checksum_vec = sk.read_checksum()?; 280 | let mut chk = [0u8; CHK_BYTES]; 281 | chk.copy_from_slice(&checksum_vec[..]); 282 | if chk != sk.keynum_sk.chk { 283 | if unencrypted_key { 284 | Err(PError::new(ErrorKind::Verify, "Corrupted key")) 285 | } else { 286 | Err(PError::new( 287 | ErrorKind::Verify, 288 | "Wrong password for that key", 289 | )) 290 | } 291 | } else { 292 | Ok(sk) 293 | } 294 | } 295 | 296 | /// Convert a `SecretKeyBox` to a `SecretKey`. 297 | /// If `password` is `None`, a password is going to be prompted interactively. 298 | pub fn from_box(sk_box: SecretKeyBox, password: Option) -> Result { 299 | Self::from_box_(sk_box, password, false) 300 | } 301 | 302 | /// Convert an unencrypted `SecretKeyBox` to an unencrypted `SecretKey`. 303 | pub fn from_unencrypted_box(sk_box: SecretKeyBox) -> Result { 304 | Self::from_box_(sk_box, None, true) 305 | } 306 | 307 | /// Convert a `SecretKey` to a `SecretKeyBox`. 308 | pub fn to_box(&self, comment: Option<&str>) -> Result { 309 | let mut s = String::new(); 310 | write!(s, "{COMMENT_PREFIX}")?; 311 | if let Some(comment) = comment { 312 | writeln!(s, "{comment}")?; 313 | } else { 314 | writeln!(s, "{SECRETKEY_DEFAULT_COMMENT}")?; 315 | } 316 | writeln!(s, "{}", self.to_base64())?; 317 | Ok(s.into()) 318 | } 319 | 320 | pub(crate) fn from_base64(s: &str) -> Result { 321 | let bytes = Base64::decode_to_vec(s, None)?; 322 | SecretKey::from_bytes(&bytes[..]) 323 | } 324 | 325 | pub(crate) fn to_base64(&self) -> String { 326 | Base64::encode_to_string(self.to_bytes().as_slice()).unwrap() 327 | } 328 | 329 | /// Load a `SecretKeyBox` from a file, and returns a `SecretKey` from it. 330 | pub fn from_file>(sk_path: P, password: Option) -> Result { 331 | let s = fs::read_to_string(sk_path)?; 332 | SecretKey::from_box(s.into(), password) 333 | } 334 | } 335 | 336 | impl fmt::Debug for SecretKey { 337 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 338 | for byte in self.keynum_sk.sk.iter() { 339 | write!(f, "{byte:x}")? 340 | } 341 | Ok(()) 342 | } 343 | } 344 | 345 | impl cmp::PartialEq for SecretKey { 346 | fn eq(&self, other: &SecretKey) -> bool { 347 | fixed_time_eq(&self.keynum_sk.sk, &other.keynum_sk.sk) 348 | } 349 | } 350 | impl cmp::Eq for SecretKey {} 351 | -------------------------------------------------------------------------------- /src/crypto/curve25519.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_parens)] 2 | #![allow(non_camel_case_types)] 3 | 4 | use std::cmp::{min, Eq, PartialEq}; 5 | use std::ops::{Add, Mul, Sub}; 6 | 7 | pub type fiat_25519_u1 = u8; 8 | pub type fiat_25519_i1 = i8; 9 | pub type fiat_25519_i2 = i8; 10 | 11 | #[inline] 12 | pub fn fiat_25519_addcarryx_u51( 13 | out1: &mut u64, 14 | out2: &mut fiat_25519_u1, 15 | arg1: fiat_25519_u1, 16 | arg2: u64, 17 | arg3: u64, 18 | ) { 19 | let x1: u64 = (((arg1 as u64) + arg2) + arg3); 20 | let x2: u64 = (x1 & 0x7ffffffffffff); 21 | let x3: fiat_25519_u1 = ((x1 >> 51) as fiat_25519_u1); 22 | *out1 = x2; 23 | *out2 = x3; 24 | } 25 | 26 | #[inline] 27 | pub fn fiat_25519_subborrowx_u51( 28 | out1: &mut u64, 29 | out2: &mut fiat_25519_u1, 30 | arg1: fiat_25519_u1, 31 | arg2: u64, 32 | arg3: u64, 33 | ) { 34 | let x1: i64 = ((((((arg2 as i128) - (arg1 as i128)) as i64) as i128) - (arg3 as i128)) as i64); 35 | let x2: fiat_25519_i1 = ((x1 >> 51) as fiat_25519_i1); 36 | let x3: u64 = (((x1 as i128) & 0x7ffffffffffff_i128) as u64); 37 | *out1 = x3; 38 | *out2 = ((0x0_i8 - (x2 as fiat_25519_i2)) as fiat_25519_u1); 39 | } 40 | 41 | #[inline] 42 | pub fn fiat_25519_cmovznz_u64(out1: &mut u64, arg1: fiat_25519_u1, arg2: u64, arg3: u64) { 43 | let x1: fiat_25519_u1 = (!(!arg1)); 44 | let x2: u64 = (((((0x0_i8 - (x1 as fiat_25519_i2)) as fiat_25519_i1) as i128) 45 | & 0xffffffffffffffff_i128) as u64); 46 | let x3: u64 = ((x2 & arg3) | ((!x2) & arg2)); 47 | *out1 = x3; 48 | } 49 | 50 | #[inline] 51 | pub fn fiat_25519_carry_mul(out1: &mut [u64; 5], arg1: &[u64; 5], arg2: &[u64; 5]) { 52 | let x1: u128 = (((arg1[4]) as u128) * (((arg2[4]) * 0x13) as u128)); 53 | let x2: u128 = (((arg1[4]) as u128) * (((arg2[3]) * 0x13) as u128)); 54 | let x3: u128 = (((arg1[4]) as u128) * (((arg2[2]) * 0x13) as u128)); 55 | let x4: u128 = (((arg1[4]) as u128) * (((arg2[1]) * 0x13) as u128)); 56 | let x5: u128 = (((arg1[3]) as u128) * (((arg2[4]) * 0x13) as u128)); 57 | let x6: u128 = (((arg1[3]) as u128) * (((arg2[3]) * 0x13) as u128)); 58 | let x7: u128 = (((arg1[3]) as u128) * (((arg2[2]) * 0x13) as u128)); 59 | let x8: u128 = (((arg1[2]) as u128) * (((arg2[4]) * 0x13) as u128)); 60 | let x9: u128 = (((arg1[2]) as u128) * (((arg2[3]) * 0x13) as u128)); 61 | let x10: u128 = (((arg1[1]) as u128) * (((arg2[4]) * 0x13) as u128)); 62 | let x11: u128 = (((arg1[4]) as u128) * ((arg2[0]) as u128)); 63 | let x12: u128 = (((arg1[3]) as u128) * ((arg2[1]) as u128)); 64 | let x13: u128 = (((arg1[3]) as u128) * ((arg2[0]) as u128)); 65 | let x14: u128 = (((arg1[2]) as u128) * ((arg2[2]) as u128)); 66 | let x15: u128 = (((arg1[2]) as u128) * ((arg2[1]) as u128)); 67 | let x16: u128 = (((arg1[2]) as u128) * ((arg2[0]) as u128)); 68 | let x17: u128 = (((arg1[1]) as u128) * ((arg2[3]) as u128)); 69 | let x18: u128 = (((arg1[1]) as u128) * ((arg2[2]) as u128)); 70 | let x19: u128 = (((arg1[1]) as u128) * ((arg2[1]) as u128)); 71 | let x20: u128 = (((arg1[1]) as u128) * ((arg2[0]) as u128)); 72 | let x21: u128 = (((arg1[0]) as u128) * ((arg2[4]) as u128)); 73 | let x22: u128 = (((arg1[0]) as u128) * ((arg2[3]) as u128)); 74 | let x23: u128 = (((arg1[0]) as u128) * ((arg2[2]) as u128)); 75 | let x24: u128 = (((arg1[0]) as u128) * ((arg2[1]) as u128)); 76 | let x25: u128 = (((arg1[0]) as u128) * ((arg2[0]) as u128)); 77 | let x26: u128 = (x25 + (x10 + (x9 + (x7 + x4)))); 78 | let x27: u64 = ((x26 >> 51) as u64); 79 | let x28: u64 = ((x26 & 0x7ffffffffffff_u128) as u64); 80 | let x29: u128 = (x21 + (x17 + (x14 + (x12 + x11)))); 81 | let x30: u128 = (x22 + (x18 + (x15 + (x13 + x1)))); 82 | let x31: u128 = (x23 + (x19 + (x16 + (x5 + x2)))); 83 | let x32: u128 = (x24 + (x20 + (x8 + (x6 + x3)))); 84 | let x33: u128 = ((x27 as u128) + x32); 85 | let x34: u64 = ((x33 >> 51) as u64); 86 | let x35: u64 = ((x33 & 0x7ffffffffffff_u128) as u64); 87 | let x36: u128 = ((x34 as u128) + x31); 88 | let x37: u64 = ((x36 >> 51) as u64); 89 | let x38: u64 = ((x36 & 0x7ffffffffffff_u128) as u64); 90 | let x39: u128 = ((x37 as u128) + x30); 91 | let x40: u64 = ((x39 >> 51) as u64); 92 | let x41: u64 = ((x39 & 0x7ffffffffffff_u128) as u64); 93 | let x42: u128 = ((x40 as u128) + x29); 94 | let x43: u64 = ((x42 >> 51) as u64); 95 | let x44: u64 = ((x42 & 0x7ffffffffffff_u128) as u64); 96 | let x45: u64 = (x43 * 0x13); 97 | let x46: u64 = (x28 + x45); 98 | let x47: u64 = (x46 >> 51); 99 | let x48: u64 = (x46 & 0x7ffffffffffff); 100 | let x49: u64 = (x47 + x35); 101 | let x50: fiat_25519_u1 = ((x49 >> 51) as fiat_25519_u1); 102 | let x51: u64 = (x49 & 0x7ffffffffffff); 103 | let x52: u64 = ((x50 as u64) + x38); 104 | out1[0] = x48; 105 | out1[1] = x51; 106 | out1[2] = x52; 107 | out1[3] = x41; 108 | out1[4] = x44; 109 | } 110 | 111 | #[inline] 112 | pub fn fiat_25519_carry_square(out1: &mut [u64; 5], arg1: &[u64; 5]) { 113 | let x1: u64 = ((arg1[4]) * 0x13); 114 | let x2: u64 = (x1 * 0x2); 115 | let x3: u64 = ((arg1[4]) * 0x2); 116 | let x4: u64 = ((arg1[3]) * 0x13); 117 | let x5: u64 = (x4 * 0x2); 118 | let x6: u64 = ((arg1[3]) * 0x2); 119 | let x7: u64 = ((arg1[2]) * 0x2); 120 | let x8: u64 = ((arg1[1]) * 0x2); 121 | let x9: u128 = (((arg1[4]) as u128) * (x1 as u128)); 122 | let x10: u128 = (((arg1[3]) as u128) * (x2 as u128)); 123 | let x11: u128 = (((arg1[3]) as u128) * (x4 as u128)); 124 | let x12: u128 = (((arg1[2]) as u128) * (x2 as u128)); 125 | let x13: u128 = (((arg1[2]) as u128) * (x5 as u128)); 126 | let x14: u128 = (((arg1[2]) as u128) * ((arg1[2]) as u128)); 127 | let x15: u128 = (((arg1[1]) as u128) * (x2 as u128)); 128 | let x16: u128 = (((arg1[1]) as u128) * (x6 as u128)); 129 | let x17: u128 = (((arg1[1]) as u128) * (x7 as u128)); 130 | let x18: u128 = (((arg1[1]) as u128) * ((arg1[1]) as u128)); 131 | let x19: u128 = (((arg1[0]) as u128) * (x3 as u128)); 132 | let x20: u128 = (((arg1[0]) as u128) * (x6 as u128)); 133 | let x21: u128 = (((arg1[0]) as u128) * (x7 as u128)); 134 | let x22: u128 = (((arg1[0]) as u128) * (x8 as u128)); 135 | let x23: u128 = (((arg1[0]) as u128) * ((arg1[0]) as u128)); 136 | let x24: u128 = (x23 + (x15 + x13)); 137 | let x25: u64 = ((x24 >> 51) as u64); 138 | let x26: u64 = ((x24 & 0x7ffffffffffff_u128) as u64); 139 | let x27: u128 = (x19 + (x16 + x14)); 140 | let x28: u128 = (x20 + (x17 + x9)); 141 | let x29: u128 = (x21 + (x18 + x10)); 142 | let x30: u128 = (x22 + (x12 + x11)); 143 | let x31: u128 = ((x25 as u128) + x30); 144 | let x32: u64 = ((x31 >> 51) as u64); 145 | let x33: u64 = ((x31 & 0x7ffffffffffff_u128) as u64); 146 | let x34: u128 = ((x32 as u128) + x29); 147 | let x35: u64 = ((x34 >> 51) as u64); 148 | let x36: u64 = ((x34 & 0x7ffffffffffff_u128) as u64); 149 | let x37: u128 = ((x35 as u128) + x28); 150 | let x38: u64 = ((x37 >> 51) as u64); 151 | let x39: u64 = ((x37 & 0x7ffffffffffff_u128) as u64); 152 | let x40: u128 = ((x38 as u128) + x27); 153 | let x41: u64 = ((x40 >> 51) as u64); 154 | let x42: u64 = ((x40 & 0x7ffffffffffff_u128) as u64); 155 | let x43: u64 = (x41 * 0x13); 156 | let x44: u64 = (x26 + x43); 157 | let x45: u64 = (x44 >> 51); 158 | let x46: u64 = (x44 & 0x7ffffffffffff); 159 | let x47: u64 = (x45 + x33); 160 | let x48: fiat_25519_u1 = ((x47 >> 51) as fiat_25519_u1); 161 | let x49: u64 = (x47 & 0x7ffffffffffff); 162 | let x50: u64 = ((x48 as u64) + x36); 163 | out1[0] = x46; 164 | out1[1] = x49; 165 | out1[2] = x50; 166 | out1[3] = x39; 167 | out1[4] = x42; 168 | } 169 | 170 | #[inline] 171 | pub fn fiat_25519_carry(out1: &mut [u64; 5], arg1: &[u64; 5]) { 172 | let x1: u64 = (arg1[0]); 173 | let x2: u64 = ((x1 >> 51) + (arg1[1])); 174 | let x3: u64 = ((x2 >> 51) + (arg1[2])); 175 | let x4: u64 = ((x3 >> 51) + (arg1[3])); 176 | let x5: u64 = ((x4 >> 51) + (arg1[4])); 177 | let x6: u64 = ((x1 & 0x7ffffffffffff) + ((x5 >> 51) * 0x13)); 178 | let x7: u64 = ((((x6 >> 51) as fiat_25519_u1) as u64) + (x2 & 0x7ffffffffffff)); 179 | let x8: u64 = (x6 & 0x7ffffffffffff); 180 | let x9: u64 = (x7 & 0x7ffffffffffff); 181 | let x10: u64 = ((((x7 >> 51) as fiat_25519_u1) as u64) + (x3 & 0x7ffffffffffff)); 182 | let x11: u64 = (x4 & 0x7ffffffffffff); 183 | let x12: u64 = (x5 & 0x7ffffffffffff); 184 | out1[0] = x8; 185 | out1[1] = x9; 186 | out1[2] = x10; 187 | out1[3] = x11; 188 | out1[4] = x12; 189 | } 190 | 191 | #[inline] 192 | pub fn fiat_25519_add(out1: &mut [u64; 5], arg1: &[u64; 5], arg2: &[u64; 5]) { 193 | let x1: u64 = ((arg1[0]) + (arg2[0])); 194 | let x2: u64 = ((arg1[1]) + (arg2[1])); 195 | let x3: u64 = ((arg1[2]) + (arg2[2])); 196 | let x4: u64 = ((arg1[3]) + (arg2[3])); 197 | let x5: u64 = ((arg1[4]) + (arg2[4])); 198 | out1[0] = x1; 199 | out1[1] = x2; 200 | out1[2] = x3; 201 | out1[3] = x4; 202 | out1[4] = x5; 203 | } 204 | 205 | #[inline] 206 | pub fn fiat_25519_sub(out1: &mut [u64; 5], arg1: &[u64; 5], arg2: &[u64; 5]) { 207 | let x1: u64 = ((0xfffffffffffda + (arg1[0])) - (arg2[0])); 208 | let x2: u64 = ((0xffffffffffffe + (arg1[1])) - (arg2[1])); 209 | let x3: u64 = ((0xffffffffffffe + (arg1[2])) - (arg2[2])); 210 | let x4: u64 = ((0xffffffffffffe + (arg1[3])) - (arg2[3])); 211 | let x5: u64 = ((0xffffffffffffe + (arg1[4])) - (arg2[4])); 212 | out1[0] = x1; 213 | out1[1] = x2; 214 | out1[2] = x3; 215 | out1[3] = x4; 216 | out1[4] = x5; 217 | } 218 | 219 | #[inline] 220 | pub fn fiat_25519_opp(out1: &mut [u64; 5], arg1: &[u64; 5]) { 221 | let x1: u64 = (0xfffffffffffda - (arg1[0])); 222 | let x2: u64 = (0xffffffffffffe - (arg1[1])); 223 | let x3: u64 = (0xffffffffffffe - (arg1[2])); 224 | let x4: u64 = (0xffffffffffffe - (arg1[3])); 225 | let x5: u64 = (0xffffffffffffe - (arg1[4])); 226 | out1[0] = x1; 227 | out1[1] = x2; 228 | out1[2] = x3; 229 | out1[3] = x4; 230 | out1[4] = x5; 231 | } 232 | 233 | #[inline] 234 | pub fn fiat_25519_selectznz( 235 | out1: &mut [u64; 5], 236 | arg1: fiat_25519_u1, 237 | arg2: &[u64; 5], 238 | arg3: &[u64; 5], 239 | ) { 240 | let mut x1: u64 = 0; 241 | fiat_25519_cmovznz_u64(&mut x1, arg1, (arg2[0]), (arg3[0])); 242 | let mut x2: u64 = 0; 243 | fiat_25519_cmovznz_u64(&mut x2, arg1, (arg2[1]), (arg3[1])); 244 | let mut x3: u64 = 0; 245 | fiat_25519_cmovznz_u64(&mut x3, arg1, (arg2[2]), (arg3[2])); 246 | let mut x4: u64 = 0; 247 | fiat_25519_cmovznz_u64(&mut x4, arg1, (arg2[3]), (arg3[3])); 248 | let mut x5: u64 = 0; 249 | fiat_25519_cmovznz_u64(&mut x5, arg1, (arg2[4]), (arg3[4])); 250 | out1[0] = x1; 251 | out1[1] = x2; 252 | out1[2] = x3; 253 | out1[3] = x4; 254 | out1[4] = x5; 255 | } 256 | 257 | #[inline] 258 | pub fn fiat_25519_to_bytes(out1: &mut [u8; 32], arg1: &[u64; 5]) { 259 | let mut x1: u64 = 0; 260 | let mut x2: fiat_25519_u1 = 0; 261 | fiat_25519_subborrowx_u51(&mut x1, &mut x2, 0x0, (arg1[0]), 0x7ffffffffffed); 262 | let mut x3: u64 = 0; 263 | let mut x4: fiat_25519_u1 = 0; 264 | fiat_25519_subborrowx_u51(&mut x3, &mut x4, x2, (arg1[1]), 0x7ffffffffffff); 265 | let mut x5: u64 = 0; 266 | let mut x6: fiat_25519_u1 = 0; 267 | fiat_25519_subborrowx_u51(&mut x5, &mut x6, x4, (arg1[2]), 0x7ffffffffffff); 268 | let mut x7: u64 = 0; 269 | let mut x8: fiat_25519_u1 = 0; 270 | fiat_25519_subborrowx_u51(&mut x7, &mut x8, x6, (arg1[3]), 0x7ffffffffffff); 271 | let mut x9: u64 = 0; 272 | let mut x10: fiat_25519_u1 = 0; 273 | fiat_25519_subborrowx_u51(&mut x9, &mut x10, x8, (arg1[4]), 0x7ffffffffffff); 274 | let mut x11: u64 = 0; 275 | fiat_25519_cmovznz_u64(&mut x11, x10, 0x0_u64, 0xffffffffffffffff); 276 | let mut x12: u64 = 0; 277 | let mut x13: fiat_25519_u1 = 0; 278 | fiat_25519_addcarryx_u51(&mut x12, &mut x13, 0x0, x1, (x11 & 0x7ffffffffffed)); 279 | let mut x14: u64 = 0; 280 | let mut x15: fiat_25519_u1 = 0; 281 | fiat_25519_addcarryx_u51(&mut x14, &mut x15, x13, x3, (x11 & 0x7ffffffffffff)); 282 | let mut x16: u64 = 0; 283 | let mut x17: fiat_25519_u1 = 0; 284 | fiat_25519_addcarryx_u51(&mut x16, &mut x17, x15, x5, (x11 & 0x7ffffffffffff)); 285 | let mut x18: u64 = 0; 286 | let mut x19: fiat_25519_u1 = 0; 287 | fiat_25519_addcarryx_u51(&mut x18, &mut x19, x17, x7, (x11 & 0x7ffffffffffff)); 288 | let mut x20: u64 = 0; 289 | let mut x21: fiat_25519_u1 = 0; 290 | fiat_25519_addcarryx_u51(&mut x20, &mut x21, x19, x9, (x11 & 0x7ffffffffffff)); 291 | let x22: u64 = (x20 << 4); 292 | let x23: u64 = (x18 * 0x2_u64); 293 | let x24: u64 = (x16 << 6); 294 | let x25: u64 = (x14 << 3); 295 | let x26: u64 = (x12 >> 8); 296 | let x27: u8 = ((x12 & 0xff_u64) as u8); 297 | let x28: u64 = (x26 >> 8); 298 | let x29: u8 = ((x26 & 0xff_u64) as u8); 299 | let x30: u64 = (x28 >> 8); 300 | let x31: u8 = ((x28 & 0xff_u64) as u8); 301 | let x32: u64 = (x30 >> 8); 302 | let x33: u8 = ((x30 & 0xff_u64) as u8); 303 | let x34: u64 = (x32 >> 8); 304 | let x35: u8 = ((x32 & 0xff_u64) as u8); 305 | let x36: u8 = ((x34 >> 8) as u8); 306 | let x37: u8 = ((x34 & 0xff_u64) as u8); 307 | let x38: u64 = ((x36 as u64) + x25); 308 | let x39: u64 = (x38 >> 8); 309 | let x40: u8 = ((x38 & 0xff_u64) as u8); 310 | let x41: u64 = (x39 >> 8); 311 | let x42: u8 = ((x39 & 0xff_u64) as u8); 312 | let x43: u64 = (x41 >> 8); 313 | let x44: u8 = ((x41 & 0xff_u64) as u8); 314 | let x45: u64 = (x43 >> 8); 315 | let x46: u8 = ((x43 & 0xff_u64) as u8); 316 | let x47: u64 = (x45 >> 8); 317 | let x48: u8 = ((x45 & 0xff_u64) as u8); 318 | let x49: u8 = ((x47 >> 8) as u8); 319 | let x50: u8 = ((x47 & 0xff_u64) as u8); 320 | let x51: u64 = ((x49 as u64) + x24); 321 | let x52: u64 = (x51 >> 8); 322 | let x53: u8 = ((x51 & 0xff_u64) as u8); 323 | let x54: u64 = (x52 >> 8); 324 | let x55: u8 = ((x52 & 0xff_u64) as u8); 325 | let x56: u64 = (x54 >> 8); 326 | let x57: u8 = ((x54 & 0xff_u64) as u8); 327 | let x58: u64 = (x56 >> 8); 328 | let x59: u8 = ((x56 & 0xff_u64) as u8); 329 | let x60: u64 = (x58 >> 8); 330 | let x61: u8 = ((x58 & 0xff_u64) as u8); 331 | let x62: u64 = (x60 >> 8); 332 | let x63: u8 = ((x60 & 0xff_u64) as u8); 333 | let x64: fiat_25519_u1 = ((x62 >> 8) as fiat_25519_u1); 334 | let x65: u8 = ((x62 & 0xff_u64) as u8); 335 | let x66: u64 = ((x64 as u64) + x23); 336 | let x67: u64 = (x66 >> 8); 337 | let x68: u8 = ((x66 & 0xff_u64) as u8); 338 | let x69: u64 = (x67 >> 8); 339 | let x70: u8 = ((x67 & 0xff_u64) as u8); 340 | let x71: u64 = (x69 >> 8); 341 | let x72: u8 = ((x69 & 0xff_u64) as u8); 342 | let x73: u64 = (x71 >> 8); 343 | let x74: u8 = ((x71 & 0xff_u64) as u8); 344 | let x75: u64 = (x73 >> 8); 345 | let x76: u8 = ((x73 & 0xff_u64) as u8); 346 | let x77: u8 = ((x75 >> 8) as u8); 347 | let x78: u8 = ((x75 & 0xff_u64) as u8); 348 | let x79: u64 = ((x77 as u64) + x22); 349 | let x80: u64 = (x79 >> 8); 350 | let x81: u8 = ((x79 & 0xff_u64) as u8); 351 | let x82: u64 = (x80 >> 8); 352 | let x83: u8 = ((x80 & 0xff_u64) as u8); 353 | let x84: u64 = (x82 >> 8); 354 | let x85: u8 = ((x82 & 0xff_u64) as u8); 355 | let x86: u64 = (x84 >> 8); 356 | let x87: u8 = ((x84 & 0xff_u64) as u8); 357 | let x88: u64 = (x86 >> 8); 358 | let x89: u8 = ((x86 & 0xff_u64) as u8); 359 | let x90: u8 = ((x88 >> 8) as u8); 360 | let x91: u8 = ((x88 & 0xff_u64) as u8); 361 | out1[0] = x27; 362 | out1[1] = x29; 363 | out1[2] = x31; 364 | out1[3] = x33; 365 | out1[4] = x35; 366 | out1[5] = x37; 367 | out1[6] = x40; 368 | out1[7] = x42; 369 | out1[8] = x44; 370 | out1[9] = x46; 371 | out1[10] = x48; 372 | out1[11] = x50; 373 | out1[12] = x53; 374 | out1[13] = x55; 375 | out1[14] = x57; 376 | out1[15] = x59; 377 | out1[16] = x61; 378 | out1[17] = x63; 379 | out1[18] = x65; 380 | out1[19] = x68; 381 | out1[20] = x70; 382 | out1[21] = x72; 383 | out1[22] = x74; 384 | out1[23] = x76; 385 | out1[24] = x78; 386 | out1[25] = x81; 387 | out1[26] = x83; 388 | out1[27] = x85; 389 | out1[28] = x87; 390 | out1[29] = x89; 391 | out1[30] = x91; 392 | out1[31] = x90; 393 | } 394 | 395 | #[derive(Clone, Default, Copy)] 396 | pub struct Fe(pub [u64; 5]); 397 | 398 | impl PartialEq for Fe { 399 | fn eq(&self, other: &Fe) -> bool { 400 | let &Fe(self_elems) = self; 401 | let &Fe(other_elems) = other; 402 | self_elems == other_elems 403 | } 404 | } 405 | impl Eq for Fe {} 406 | 407 | static FE_ZERO: Fe = Fe([0, 0, 0, 0, 0]); 408 | static FE_ONE: Fe = Fe([1, 0, 0, 0, 0]); 409 | static FE_SQRTM1: Fe = Fe([ 410 | 1718705420411056, 411 | 234908883556509, 412 | 2233514472574048, 413 | 2117202627021982, 414 | 765476049583133, 415 | ]); 416 | static FE_D: Fe = Fe([ 417 | 929955233495203, 418 | 466365720129213, 419 | 1662059464998953, 420 | 2033849074728123, 421 | 1442794654840575, 422 | ]); 423 | static FE_D2: Fe = Fe([ 424 | 1859910466990425, 425 | 932731440258426, 426 | 1072319116312658, 427 | 1815898335770999, 428 | 633789495995903, 429 | ]); 430 | 431 | #[inline] 432 | fn load_8u(s: &[u8]) -> u64 { 433 | (s[0] as u64) 434 | | ((s[1] as u64) << 8) 435 | | ((s[2] as u64) << 16) 436 | | ((s[3] as u64) << 24) 437 | | ((s[4] as u64) << 32) 438 | | ((s[5] as u64) << 40) 439 | | ((s[6] as u64) << 48) 440 | | ((s[7] as u64) << 56) 441 | } 442 | 443 | #[inline] 444 | fn load_4u(s: &[u8]) -> u64 { 445 | (s[0] as u64) | ((s[1] as u64) << 8) | ((s[2] as u64) << 16) | ((s[3] as u64) << 24) 446 | } 447 | 448 | #[inline] 449 | fn load_4i(s: &[u8]) -> i64 { 450 | load_4u(s) as i64 451 | } 452 | 453 | #[inline] 454 | fn load_3u(s: &[u8]) -> u64 { 455 | (s[0] as u64) | ((s[1] as u64) << 8) | ((s[2] as u64) << 16) 456 | } 457 | 458 | #[inline] 459 | fn load_3i(s: &[u8]) -> i64 { 460 | load_3u(s) as i64 461 | } 462 | 463 | impl Add for Fe { 464 | type Output = Fe; 465 | 466 | fn add(self, _rhs: Fe) -> Fe { 467 | let Fe(f) = self; 468 | let Fe(g) = _rhs; 469 | let mut h = Fe::default(); 470 | fiat_25519_add(&mut h.0, &f, &g); 471 | h 472 | } 473 | } 474 | 475 | impl Sub for Fe { 476 | type Output = Fe; 477 | 478 | fn sub(self, _rhs: Fe) -> Fe { 479 | let Fe(f) = self; 480 | let Fe(g) = _rhs; 481 | let mut h = Fe::default(); 482 | fiat_25519_sub(&mut h.0, &f, &g); 483 | h.carry() 484 | } 485 | } 486 | 487 | impl Mul for Fe { 488 | type Output = Fe; 489 | 490 | fn mul(self, _rhs: Fe) -> Fe { 491 | let Fe(f) = self; 492 | let Fe(g) = _rhs; 493 | let mut h = Fe::default(); 494 | fiat_25519_carry_mul(&mut h.0, &f, &g); 495 | h 496 | } 497 | } 498 | 499 | impl Fe { 500 | pub fn from_bytes(s: &[u8]) -> Fe { 501 | if s.len() != 32 { 502 | panic!("Invalid compressed length") 503 | } 504 | let mut h = Fe::default(); 505 | let mask = 0x7ffffffffffff; 506 | h.0[0] = load_8u(&s[0..]) & mask; 507 | h.0[1] = (load_8u(&s[6..]) >> 3) & mask; 508 | h.0[2] = (load_8u(&s[12..]) >> 6) & mask; 509 | h.0[3] = (load_8u(&s[19..]) >> 1) & mask; 510 | h.0[4] = (load_8u(&s[24..]) >> 12) & mask; 511 | h 512 | } 513 | 514 | pub fn to_bytes(&self) -> [u8; 32] { 515 | let &Fe(es) = &self.carry(); 516 | let mut s_ = [0u8; 32]; 517 | fiat_25519_to_bytes(&mut s_, &es); 518 | s_ 519 | } 520 | 521 | pub fn carry(&self) -> Fe { 522 | let mut h = Fe::default(); 523 | fiat_25519_carry(&mut h.0, &self.0); 524 | h 525 | } 526 | 527 | pub fn maybe_set(&mut self, other: &Fe, do_swap: u8) { 528 | let &mut Fe(f) = self; 529 | let &Fe(g) = other; 530 | let mut t = [0u64; 5]; 531 | fiat_25519_selectznz(&mut t, do_swap, &f, &g); 532 | self.0 = t 533 | } 534 | 535 | fn square(&self) -> Fe { 536 | let &Fe(f) = &self; 537 | let mut h = Fe::default(); 538 | fiat_25519_carry_square(&mut h.0, f); 539 | h 540 | } 541 | 542 | fn square_and_double(&self) -> Fe { 543 | let h = self.square(); 544 | (h + h) 545 | } 546 | 547 | pub fn invert(&self) -> Fe { 548 | let z1 = *self; 549 | let z2 = z1.square(); 550 | let z8 = z2.square().square(); 551 | let z9 = z1 * z8; 552 | let z11 = z2 * z9; 553 | let z22 = z11.square(); 554 | let z_5_0 = z9 * z22; 555 | let z_10_5 = (0..5).fold(z_5_0, |z_5_n, _| z_5_n.square()); 556 | let z_10_0 = z_10_5 * z_5_0; 557 | let z_20_10 = (0..10).fold(z_10_0, |x, _| x.square()); 558 | let z_20_0 = z_20_10 * z_10_0; 559 | let z_40_20 = (0..20).fold(z_20_0, |x, _| x.square()); 560 | let z_40_0 = z_40_20 * z_20_0; 561 | let z_50_10 = (0..10).fold(z_40_0, |x, _| x.square()); 562 | let z_50_0 = z_50_10 * z_10_0; 563 | let z_100_50 = (0..50).fold(z_50_0, |x, _| x.square()); 564 | let z_100_0 = z_100_50 * z_50_0; 565 | let z_200_100 = (0..100).fold(z_100_0, |x, _| x.square()); 566 | let z_200_0 = z_200_100 * z_100_0; 567 | let z_250_50 = (0..50).fold(z_200_0, |x, _| x.square()); 568 | let z_250_0 = z_250_50 * z_50_0; 569 | let z_255_5 = (0..5).fold(z_250_0, |x, _| x.square()); 570 | let z_255_21 = z_255_5 * z11; 571 | z_255_21 572 | } 573 | 574 | fn is_nonzero(&self) -> bool { 575 | self.to_bytes().iter().fold(0, |acc, x| acc | x) != 0 576 | } 577 | 578 | fn is_negative(&self) -> bool { 579 | (self.to_bytes()[0] & 1) != 0 580 | } 581 | 582 | fn neg(&self) -> Fe { 583 | let &Fe(f) = &self; 584 | let mut h = Fe::default(); 585 | fiat_25519_opp(&mut h.0, f); 586 | h 587 | } 588 | 589 | fn pow25523(&self) -> Fe { 590 | let z2 = self.square(); 591 | let z8 = (0..2).fold(z2, |x, _| x.square()); 592 | let z9 = *self * z8; 593 | let z11 = z2 * z9; 594 | let z22 = z11.square(); 595 | let z_5_0 = z9 * z22; 596 | let z_10_5 = (0..5).fold(z_5_0, |x, _| x.square()); 597 | let z_10_0 = z_10_5 * z_5_0; 598 | let z_20_10 = (0..10).fold(z_10_0, |x, _| x.square()); 599 | let z_20_0 = z_20_10 * z_10_0; 600 | let z_40_20 = (0..20).fold(z_20_0, |x, _| x.square()); 601 | let z_40_0 = z_40_20 * z_20_0; 602 | let z_50_10 = (0..10).fold(z_40_0, |x, _| x.square()); 603 | let z_50_0 = z_50_10 * z_10_0; 604 | let z_100_50 = (0..50).fold(z_50_0, |x, _| x.square()); 605 | let z_100_0 = z_100_50 * z_50_0; 606 | let z_200_100 = (0..100).fold(z_100_0, |x, _| x.square()); 607 | let z_200_0 = z_200_100 * z_100_0; 608 | let z_250_50 = (0..50).fold(z_200_0, |x, _| x.square()); 609 | let z_250_0 = z_250_50 * z_50_0; 610 | let z_252_2 = (0..2).fold(z_250_0, |x, _| x.square()); 611 | let z_252_3 = z_252_2 * *self; 612 | 613 | z_252_3 614 | } 615 | } 616 | 617 | #[derive(Clone, Copy)] 618 | pub struct GeP2 { 619 | x: Fe, 620 | y: Fe, 621 | z: Fe, 622 | } 623 | 624 | #[derive(Clone, Copy)] 625 | pub struct GeP3 { 626 | x: Fe, 627 | y: Fe, 628 | z: Fe, 629 | t: Fe, 630 | } 631 | 632 | #[derive(Clone, Copy)] 633 | pub struct GeP1P1 { 634 | x: Fe, 635 | y: Fe, 636 | z: Fe, 637 | t: Fe, 638 | } 639 | 640 | #[derive(Clone, Copy)] 641 | pub struct GePrecomp { 642 | y_plus_x: Fe, 643 | y_minus_x: Fe, 644 | xy2d: Fe, 645 | } 646 | 647 | #[derive(Clone, Copy)] 648 | pub struct GeCached { 649 | y_plus_x: Fe, 650 | y_minus_x: Fe, 651 | z: Fe, 652 | t2d: Fe, 653 | } 654 | 655 | impl GeP1P1 { 656 | fn to_p2(&self) -> GeP2 { 657 | GeP2 { 658 | x: self.x * self.t, 659 | y: self.y * self.z, 660 | z: self.z * self.t, 661 | } 662 | } 663 | 664 | fn to_p3(&self) -> GeP3 { 665 | GeP3 { 666 | x: self.x * self.t, 667 | y: self.y * self.z, 668 | z: self.z * self.t, 669 | t: self.x * self.y, 670 | } 671 | } 672 | } 673 | 674 | impl GeP2 { 675 | fn zero() -> GeP2 { 676 | GeP2 { 677 | x: FE_ZERO, 678 | y: FE_ONE, 679 | z: FE_ONE, 680 | } 681 | } 682 | 683 | pub fn to_bytes(&self) -> [u8; 32] { 684 | let recip = self.z.invert(); 685 | let x = self.x * recip; 686 | let y = self.y * recip; 687 | let mut bs = y.to_bytes(); 688 | bs[31] ^= (if x.is_negative() { 1 } else { 0 }) << 7; 689 | bs 690 | } 691 | 692 | fn dbl(&self) -> GeP1P1 { 693 | let xx = self.x.square(); 694 | let yy = self.y.square(); 695 | let b = self.z.square_and_double(); 696 | let a = self.x + self.y; 697 | let aa = a.square(); 698 | let y3 = yy + xx; 699 | let z3 = yy - xx; 700 | let x3 = aa - y3; 701 | let t3 = b - z3; 702 | 703 | GeP1P1 { 704 | x: x3, 705 | y: y3, 706 | z: z3, 707 | t: t3, 708 | } 709 | } 710 | 711 | fn slide(a: &[u8]) -> [i8; 256] { 712 | let mut r = [0i8; 256]; 713 | for i in 0..256 { 714 | r[i] = (1 & (a[i >> 3] >> (i & 7))) as i8; 715 | } 716 | for i in 0..256 { 717 | if r[i] != 0 { 718 | for b in 1..min(7, 256 - i) { 719 | if r[i + b] != 0 { 720 | if r[i] + (r[i + b] << b) <= 15 { 721 | r[i] += r[i + b] << b; 722 | r[i + b] = 0; 723 | } else if r[i] - (r[i + b] << b) >= -15 { 724 | r[i] -= r[i + b] << b; 725 | for k in i + b..256 { 726 | if r[k] == 0 { 727 | r[k] = 1; 728 | break; 729 | } 730 | r[k] = 0; 731 | } 732 | } else { 733 | break; 734 | } 735 | } 736 | } 737 | } 738 | } 739 | 740 | r 741 | } 742 | 743 | #[allow(clippy::comparison_chain)] 744 | pub fn double_scalarmult_vartime(a_scalar: &[u8], a_point: GeP3, b_scalar: &[u8]) -> GeP2 { 745 | let aslide = GeP2::slide(a_scalar); 746 | let bslide = GeP2::slide(b_scalar); 747 | 748 | let mut ai = [GeCached { 749 | y_plus_x: FE_ZERO, 750 | y_minus_x: FE_ZERO, 751 | z: FE_ZERO, 752 | t2d: FE_ZERO, 753 | }; 8]; // A,3A,5A,7A,9A,11A,13A,15A 754 | ai[0] = a_point.to_cached(); 755 | let a2 = a_point.dbl().to_p3(); 756 | ai[1] = (a2 + ai[0]).to_p3().to_cached(); 757 | ai[2] = (a2 + ai[1]).to_p3().to_cached(); 758 | ai[3] = (a2 + ai[2]).to_p3().to_cached(); 759 | ai[4] = (a2 + ai[3]).to_p3().to_cached(); 760 | ai[5] = (a2 + ai[4]).to_p3().to_cached(); 761 | ai[6] = (a2 + ai[5]).to_p3().to_cached(); 762 | ai[7] = (a2 + ai[6]).to_p3().to_cached(); 763 | 764 | let mut r = GeP2::zero(); 765 | 766 | let mut i: usize = 255; 767 | loop { 768 | if aslide[i] != 0 || bslide[i] != 0 { 769 | break; 770 | } 771 | if i == 0 { 772 | return r; 773 | } 774 | i -= 1; 775 | } 776 | 777 | loop { 778 | let mut t = r.dbl(); 779 | if aslide[i] > 0 { 780 | t = t.to_p3() + ai[(aslide[i] / 2) as usize]; 781 | } else if aslide[i] < 0 { 782 | t = t.to_p3() - ai[(-aslide[i] / 2) as usize]; 783 | } 784 | 785 | if bslide[i] > 0 { 786 | t = t.to_p3() + BI[(bslide[i] / 2) as usize]; 787 | } else if bslide[i] < 0 { 788 | t = t.to_p3() - BI[(-bslide[i] / 2) as usize]; 789 | } 790 | 791 | r = t.to_p2(); 792 | 793 | if i == 0 { 794 | return r; 795 | } 796 | i -= 1; 797 | } 798 | } 799 | } 800 | 801 | impl GeP3 { 802 | pub fn from_bytes_negate_vartime(s: &[u8]) -> Option { 803 | let y = Fe::from_bytes(s); 804 | let z = FE_ONE; 805 | let y_squared = y.square(); 806 | let u = y_squared - FE_ONE; 807 | let v = (y_squared * FE_D) + FE_ONE; 808 | let v_raise_3 = v.square() * v; 809 | let v_raise_7 = v_raise_3.square() * v; 810 | let uv7 = v_raise_7 * u; // Is this commutative? u comes second in the code, but not in the notation... 811 | 812 | let mut x = uv7.pow25523() * v_raise_3 * u; 813 | 814 | let vxx = x.square() * v; 815 | let check = vxx - u; 816 | if check.is_nonzero() { 817 | let check2 = vxx + u; 818 | if check2.is_nonzero() { 819 | return None; 820 | } 821 | x = x * FE_SQRTM1; 822 | } 823 | 824 | if x.is_negative() == ((s[31] >> 7) != 0) { 825 | x = x.neg(); 826 | } 827 | 828 | let t = x * y; 829 | 830 | Some(GeP3 { x, y, z, t }) 831 | } 832 | 833 | fn to_p2(&self) -> GeP2 { 834 | GeP2 { 835 | x: self.x, 836 | y: self.y, 837 | z: self.z, 838 | } 839 | } 840 | 841 | fn to_cached(&self) -> GeCached { 842 | GeCached { 843 | y_plus_x: self.y + self.x, 844 | y_minus_x: self.y - self.x, 845 | z: self.z, 846 | t2d: self.t * FE_D2, 847 | } 848 | } 849 | 850 | fn zero() -> GeP3 { 851 | GeP3 { 852 | x: FE_ZERO, 853 | y: FE_ONE, 854 | z: FE_ONE, 855 | t: FE_ZERO, 856 | } 857 | } 858 | 859 | fn dbl(&self) -> GeP1P1 { 860 | self.to_p2().dbl() 861 | } 862 | 863 | pub fn to_bytes(&self) -> [u8; 32] { 864 | let recip = self.z.invert(); 865 | let x = self.x * recip; 866 | let y = self.y * recip; 867 | let mut bs = y.to_bytes(); 868 | bs[31] ^= (if x.is_negative() { 1 } else { 0 }) << 7; 869 | bs 870 | } 871 | 872 | pub fn maybe_set(&mut self, other: &GeP3, do_swap: u8) { 873 | self.x.maybe_set(&other.x, do_swap); 874 | self.y.maybe_set(&other.y, do_swap); 875 | self.z.maybe_set(&other.z, do_swap); 876 | self.t.maybe_set(&other.t, do_swap); 877 | } 878 | } 879 | 880 | impl Add for GeP3 { 881 | type Output = GeP1P1; 882 | 883 | fn add(self, _rhs: GeCached) -> GeP1P1 { 884 | let y1_plus_x1 = self.y + self.x; 885 | let y1_minus_x1 = self.y - self.x; 886 | let a = y1_plus_x1 * _rhs.y_plus_x; 887 | let b = y1_minus_x1 * _rhs.y_minus_x; 888 | let c = _rhs.t2d * self.t; 889 | let zz = self.z * _rhs.z; 890 | let d = zz + zz; 891 | let x3 = a - b; 892 | let y3 = a + b; 893 | let z3 = d + c; 894 | let t3 = d - c; 895 | 896 | GeP1P1 { 897 | x: x3, 898 | y: y3, 899 | z: z3, 900 | t: t3, 901 | } 902 | } 903 | } 904 | 905 | impl Add for GeP3 { 906 | type Output = GeP1P1; 907 | 908 | fn add(self, _rhs: GePrecomp) -> GeP1P1 { 909 | let y1_plus_x1 = self.y + self.x; 910 | let y1_minus_x1 = self.y - self.x; 911 | let a = y1_plus_x1 * _rhs.y_plus_x; 912 | let b = y1_minus_x1 * _rhs.y_minus_x; 913 | let c = _rhs.xy2d * self.t; 914 | let d = self.z + self.z; 915 | let x3 = a - b; 916 | let y3 = a + b; 917 | let z3 = d + c; 918 | let t3 = d - c; 919 | 920 | GeP1P1 { 921 | x: x3, 922 | y: y3, 923 | z: z3, 924 | t: t3, 925 | } 926 | } 927 | } 928 | 929 | impl Sub for GeP3 { 930 | type Output = GeP1P1; 931 | 932 | fn sub(self, _rhs: GeCached) -> GeP1P1 { 933 | let y1_plus_x1 = self.y + self.x; 934 | let y1_minus_x1 = self.y - self.x; 935 | let a = y1_plus_x1 * _rhs.y_minus_x; 936 | let b = y1_minus_x1 * _rhs.y_plus_x; 937 | let c = _rhs.t2d * self.t; 938 | let zz = self.z * _rhs.z; 939 | let d = zz + zz; 940 | let x3 = a - b; 941 | let y3 = a + b; 942 | let z3 = d - c; 943 | let t3 = d + c; 944 | 945 | GeP1P1 { 946 | x: x3, 947 | y: y3, 948 | z: z3, 949 | t: t3, 950 | } 951 | } 952 | } 953 | 954 | impl Sub for GeP3 { 955 | type Output = GeP1P1; 956 | 957 | fn sub(self, _rhs: GePrecomp) -> GeP1P1 { 958 | let y1_plus_x1 = self.y + self.x; 959 | let y1_minus_x1 = self.y - self.x; 960 | let a = y1_plus_x1 * _rhs.y_minus_x; 961 | let b = y1_minus_x1 * _rhs.y_plus_x; 962 | let c = _rhs.xy2d * self.t; 963 | let d = self.z + self.z; 964 | let x3 = a - b; 965 | let y3 = a + b; 966 | let z3 = d - c; 967 | let t3 = d + c; 968 | 969 | GeP1P1 { 970 | x: x3, 971 | y: y3, 972 | z: z3, 973 | t: t3, 974 | } 975 | } 976 | } 977 | 978 | pub fn ge_scalarmult_base(scalar: &[u8]) -> GeP3 { 979 | const BXP: [u8; 32] = [ 980 | 0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c, 981 | 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36, 982 | 0x69, 0x21, 983 | ]; 984 | const BYP: [u8; 32] = [ 985 | 0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 986 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 987 | 0x66, 0x66, 988 | ]; 989 | let bx = Fe::from_bytes(&BXP); 990 | let by = Fe::from_bytes(&BYP); 991 | let mut q = GeP3 { 992 | x: bx, 993 | y: by, 994 | z: FE_ONE, 995 | t: bx * by, 996 | }; 997 | let mut p = GeP3::zero(); 998 | for i in 0..256 { 999 | let q_cached = q.to_cached(); 1000 | let ps = (p + q_cached).to_p3(); 1001 | q = (q + q_cached).to_p3(); 1002 | let b = ((scalar[(i >> 3)] >> (i as u8 & 7)) & 1); 1003 | p.maybe_set(&ps, b); 1004 | } 1005 | p 1006 | } 1007 | 1008 | pub fn sc_reduce(s: &mut [u8]) { 1009 | let mut s0: i64 = 2097151 & load_3i(s); 1010 | let mut s1: i64 = 2097151 & (load_4i(&s[2..6]) >> 5); 1011 | let mut s2: i64 = 2097151 & (load_3i(&s[5..8]) >> 2); 1012 | let mut s3: i64 = 2097151 & (load_4i(&s[7..11]) >> 7); 1013 | let mut s4: i64 = 2097151 & (load_4i(&s[10..14]) >> 4); 1014 | let mut s5: i64 = 2097151 & (load_3i(&s[13..16]) >> 1); 1015 | let mut s6: i64 = 2097151 & (load_4i(&s[15..19]) >> 6); 1016 | let mut s7: i64 = 2097151 & (load_3i(&s[18..21]) >> 3); 1017 | let mut s8: i64 = 2097151 & load_3i(&s[21..24]); 1018 | let mut s9: i64 = 2097151 & (load_4i(&s[23..27]) >> 5); 1019 | let mut s10: i64 = 2097151 & (load_3i(&s[26..29]) >> 2); 1020 | let mut s11: i64 = 2097151 & (load_4i(&s[28..32]) >> 7); 1021 | let mut s12: i64 = 2097151 & (load_4i(&s[31..35]) >> 4); 1022 | let mut s13: i64 = 2097151 & (load_3i(&s[34..37]) >> 1); 1023 | let mut s14: i64 = 2097151 & (load_4i(&s[36..40]) >> 6); 1024 | let mut s15: i64 = 2097151 & (load_3i(&s[39..42]) >> 3); 1025 | let mut s16: i64 = 2097151 & load_3i(&s[42..45]); 1026 | let mut s17: i64 = 2097151 & (load_4i(&s[44..48]) >> 5); 1027 | let s18: i64 = 2097151 & (load_3i(&s[47..50]) >> 2); 1028 | let s19: i64 = 2097151 & (load_4i(&s[49..53]) >> 7); 1029 | let s20: i64 = 2097151 & (load_4i(&s[52..56]) >> 4); 1030 | let s21: i64 = 2097151 & (load_3i(&s[55..58]) >> 1); 1031 | let s22: i64 = 2097151 & (load_4i(&s[57..61]) >> 6); 1032 | let s23: i64 = load_4i(&s[60..64]) >> 3; 1033 | let mut carry0: i64; 1034 | let mut carry1: i64; 1035 | let mut carry2: i64; 1036 | let mut carry3: i64; 1037 | let mut carry4: i64; 1038 | let mut carry5: i64; 1039 | let mut carry6: i64; 1040 | let mut carry7: i64; 1041 | let mut carry8: i64; 1042 | let mut carry9: i64; 1043 | let mut carry10: i64; 1044 | let mut carry11: i64; 1045 | 1046 | s11 += s23 * 666643; 1047 | s12 += s23 * 470296; 1048 | s13 += s23 * 654183; 1049 | s14 -= s23 * 997805; 1050 | s15 += s23 * 136657; 1051 | s16 -= s23 * 683901; 1052 | 1053 | s10 += s22 * 666643; 1054 | s11 += s22 * 470296; 1055 | s12 += s22 * 654183; 1056 | s13 -= s22 * 997805; 1057 | s14 += s22 * 136657; 1058 | s15 -= s22 * 683901; 1059 | 1060 | s9 += s21 * 666643; 1061 | s10 += s21 * 470296; 1062 | s11 += s21 * 654183; 1063 | s12 -= s21 * 997805; 1064 | s13 += s21 * 136657; 1065 | s14 -= s21 * 683901; 1066 | 1067 | s8 += s20 * 666643; 1068 | s9 += s20 * 470296; 1069 | s10 += s20 * 654183; 1070 | s11 -= s20 * 997805; 1071 | s12 += s20 * 136657; 1072 | s13 -= s20 * 683901; 1073 | 1074 | s7 += s19 * 666643; 1075 | s8 += s19 * 470296; 1076 | s9 += s19 * 654183; 1077 | s10 -= s19 * 997805; 1078 | s11 += s19 * 136657; 1079 | s12 -= s19 * 683901; 1080 | 1081 | s6 += s18 * 666643; 1082 | s7 += s18 * 470296; 1083 | s8 += s18 * 654183; 1084 | s9 -= s18 * 997805; 1085 | s10 += s18 * 136657; 1086 | s11 -= s18 * 683901; 1087 | 1088 | carry6 = (s6 + (1 << 20)) >> 21; 1089 | s7 += carry6; 1090 | s6 -= carry6 << 21; 1091 | carry8 = (s8 + (1 << 20)) >> 21; 1092 | s9 += carry8; 1093 | s8 -= carry8 << 21; 1094 | carry10 = (s10 + (1 << 20)) >> 21; 1095 | s11 += carry10; 1096 | s10 -= carry10 << 21; 1097 | let carry12: i64 = (s12 + (1 << 20)) >> 21; 1098 | s13 += carry12; 1099 | s12 -= carry12 << 21; 1100 | let carry14: i64 = (s14 + (1 << 20)) >> 21; 1101 | s15 += carry14; 1102 | s14 -= carry14 << 21; 1103 | let carry16: i64 = (s16 + (1 << 20)) >> 21; 1104 | s17 += carry16; 1105 | s16 -= carry16 << 21; 1106 | 1107 | carry7 = (s7 + (1 << 20)) >> 21; 1108 | s8 += carry7; 1109 | s7 -= carry7 << 21; 1110 | carry9 = (s9 + (1 << 20)) >> 21; 1111 | s10 += carry9; 1112 | s9 -= carry9 << 21; 1113 | carry11 = (s11 + (1 << 20)) >> 21; 1114 | s12 += carry11; 1115 | s11 -= carry11 << 21; 1116 | let carry13: i64 = (s13 + (1 << 20)) >> 21; 1117 | s14 += carry13; 1118 | s13 -= carry13 << 21; 1119 | let carry15: i64 = (s15 + (1 << 20)) >> 21; 1120 | s16 += carry15; 1121 | s15 -= carry15 << 21; 1122 | 1123 | s5 += s17 * 666643; 1124 | s6 += s17 * 470296; 1125 | s7 += s17 * 654183; 1126 | s8 -= s17 * 997805; 1127 | s9 += s17 * 136657; 1128 | s10 -= s17 * 683901; 1129 | 1130 | s4 += s16 * 666643; 1131 | s5 += s16 * 470296; 1132 | s6 += s16 * 654183; 1133 | s7 -= s16 * 997805; 1134 | s8 += s16 * 136657; 1135 | s9 -= s16 * 683901; 1136 | 1137 | s3 += s15 * 666643; 1138 | s4 += s15 * 470296; 1139 | s5 += s15 * 654183; 1140 | s6 -= s15 * 997805; 1141 | s7 += s15 * 136657; 1142 | s8 -= s15 * 683901; 1143 | 1144 | s2 += s14 * 666643; 1145 | s3 += s14 * 470296; 1146 | s4 += s14 * 654183; 1147 | s5 -= s14 * 997805; 1148 | s6 += s14 * 136657; 1149 | s7 -= s14 * 683901; 1150 | 1151 | s1 += s13 * 666643; 1152 | s2 += s13 * 470296; 1153 | s3 += s13 * 654183; 1154 | s4 -= s13 * 997805; 1155 | s5 += s13 * 136657; 1156 | s6 -= s13 * 683901; 1157 | 1158 | s0 += s12 * 666643; 1159 | s1 += s12 * 470296; 1160 | s2 += s12 * 654183; 1161 | s3 -= s12 * 997805; 1162 | s4 += s12 * 136657; 1163 | s5 -= s12 * 683901; 1164 | s12 = 0; 1165 | 1166 | carry0 = (s0 + (1 << 20)) >> 21; 1167 | s1 += carry0; 1168 | s0 -= carry0 << 21; 1169 | carry2 = (s2 + (1 << 20)) >> 21; 1170 | s3 += carry2; 1171 | s2 -= carry2 << 21; 1172 | carry4 = (s4 + (1 << 20)) >> 21; 1173 | s5 += carry4; 1174 | s4 -= carry4 << 21; 1175 | carry6 = (s6 + (1 << 20)) >> 21; 1176 | s7 += carry6; 1177 | s6 -= carry6 << 21; 1178 | carry8 = (s8 + (1 << 20)) >> 21; 1179 | s9 += carry8; 1180 | s8 -= carry8 << 21; 1181 | carry10 = (s10 + (1 << 20)) >> 21; 1182 | s11 += carry10; 1183 | s10 -= carry10 << 21; 1184 | 1185 | carry1 = (s1 + (1 << 20)) >> 21; 1186 | s2 += carry1; 1187 | s1 -= carry1 << 21; 1188 | carry3 = (s3 + (1 << 20)) >> 21; 1189 | s4 += carry3; 1190 | s3 -= carry3 << 21; 1191 | carry5 = (s5 + (1 << 20)) >> 21; 1192 | s6 += carry5; 1193 | s5 -= carry5 << 21; 1194 | carry7 = (s7 + (1 << 20)) >> 21; 1195 | s8 += carry7; 1196 | s7 -= carry7 << 21; 1197 | carry9 = (s9 + (1 << 20)) >> 21; 1198 | s10 += carry9; 1199 | s9 -= carry9 << 21; 1200 | carry11 = (s11 + (1 << 20)) >> 21; 1201 | s12 += carry11; 1202 | s11 -= carry11 << 21; 1203 | 1204 | s0 += s12 * 666643; 1205 | s1 += s12 * 470296; 1206 | s2 += s12 * 654183; 1207 | s3 -= s12 * 997805; 1208 | s4 += s12 * 136657; 1209 | s5 -= s12 * 683901; 1210 | s12 = 0; 1211 | 1212 | carry0 = s0 >> 21; 1213 | s1 += carry0; 1214 | s0 -= carry0 << 21; 1215 | carry1 = s1 >> 21; 1216 | s2 += carry1; 1217 | s1 -= carry1 << 21; 1218 | carry2 = s2 >> 21; 1219 | s3 += carry2; 1220 | s2 -= carry2 << 21; 1221 | carry3 = s3 >> 21; 1222 | s4 += carry3; 1223 | s3 -= carry3 << 21; 1224 | carry4 = s4 >> 21; 1225 | s5 += carry4; 1226 | s4 -= carry4 << 21; 1227 | carry5 = s5 >> 21; 1228 | s6 += carry5; 1229 | s5 -= carry5 << 21; 1230 | carry6 = s6 >> 21; 1231 | s7 += carry6; 1232 | s6 -= carry6 << 21; 1233 | carry7 = s7 >> 21; 1234 | s8 += carry7; 1235 | s7 -= carry7 << 21; 1236 | carry8 = s8 >> 21; 1237 | s9 += carry8; 1238 | s8 -= carry8 << 21; 1239 | carry9 = s9 >> 21; 1240 | s10 += carry9; 1241 | s9 -= carry9 << 21; 1242 | carry10 = s10 >> 21; 1243 | s11 += carry10; 1244 | s10 -= carry10 << 21; 1245 | carry11 = s11 >> 21; 1246 | s12 += carry11; 1247 | s11 -= carry11 << 21; 1248 | 1249 | s0 += s12 * 666643; 1250 | s1 += s12 * 470296; 1251 | s2 += s12 * 654183; 1252 | s3 -= s12 * 997805; 1253 | s4 += s12 * 136657; 1254 | s5 -= s12 * 683901; 1255 | 1256 | carry0 = s0 >> 21; 1257 | s1 += carry0; 1258 | s0 -= carry0 << 21; 1259 | carry1 = s1 >> 21; 1260 | s2 += carry1; 1261 | s1 -= carry1 << 21; 1262 | carry2 = s2 >> 21; 1263 | s3 += carry2; 1264 | s2 -= carry2 << 21; 1265 | carry3 = s3 >> 21; 1266 | s4 += carry3; 1267 | s3 -= carry3 << 21; 1268 | carry4 = s4 >> 21; 1269 | s5 += carry4; 1270 | s4 -= carry4 << 21; 1271 | carry5 = s5 >> 21; 1272 | s6 += carry5; 1273 | s5 -= carry5 << 21; 1274 | carry6 = s6 >> 21; 1275 | s7 += carry6; 1276 | s6 -= carry6 << 21; 1277 | carry7 = s7 >> 21; 1278 | s8 += carry7; 1279 | s7 -= carry7 << 21; 1280 | carry8 = s8 >> 21; 1281 | s9 += carry8; 1282 | s8 -= carry8 << 21; 1283 | carry9 = s9 >> 21; 1284 | s10 += carry9; 1285 | s9 -= carry9 << 21; 1286 | carry10 = s10 >> 21; 1287 | s11 += carry10; 1288 | s10 -= carry10 << 21; 1289 | 1290 | s[0] = (s0 >> 0) as u8; 1291 | s[1] = (s0 >> 8) as u8; 1292 | s[2] = ((s0 >> 16) | (s1 << 5)) as u8; 1293 | s[3] = (s1 >> 3) as u8; 1294 | s[4] = (s1 >> 11) as u8; 1295 | s[5] = ((s1 >> 19) | (s2 << 2)) as u8; 1296 | s[6] = (s2 >> 6) as u8; 1297 | s[7] = ((s2 >> 14) | (s3 << 7)) as u8; 1298 | s[8] = (s3 >> 1) as u8; 1299 | s[9] = (s3 >> 9) as u8; 1300 | s[10] = ((s3 >> 17) | (s4 << 4)) as u8; 1301 | s[11] = (s4 >> 4) as u8; 1302 | s[12] = (s4 >> 12) as u8; 1303 | s[13] = ((s4 >> 20) | (s5 << 1)) as u8; 1304 | s[14] = (s5 >> 7) as u8; 1305 | s[15] = ((s5 >> 15) | (s6 << 6)) as u8; 1306 | s[16] = (s6 >> 2) as u8; 1307 | s[17] = (s6 >> 10) as u8; 1308 | s[18] = ((s6 >> 18) | (s7 << 3)) as u8; 1309 | s[19] = (s7 >> 5) as u8; 1310 | s[20] = (s7 >> 13) as u8; 1311 | s[21] = (s8 >> 0) as u8; 1312 | s[22] = (s8 >> 8) as u8; 1313 | s[23] = ((s8 >> 16) | (s9 << 5)) as u8; 1314 | s[24] = (s9 >> 3) as u8; 1315 | s[25] = (s9 >> 11) as u8; 1316 | s[26] = ((s9 >> 19) | (s10 << 2)) as u8; 1317 | s[27] = (s10 >> 6) as u8; 1318 | s[28] = ((s10 >> 14) | (s11 << 7)) as u8; 1319 | s[29] = (s11 >> 1) as u8; 1320 | s[30] = (s11 >> 9) as u8; 1321 | s[31] = (s11 >> 17) as u8; 1322 | } 1323 | 1324 | pub fn sc_muladd(s: &mut [u8], a: &[u8], b: &[u8], c: &[u8]) { 1325 | let a0 = 2097151 & load_3i(&a[0..3]); 1326 | let a1 = 2097151 & (load_4i(&a[2..6]) >> 5); 1327 | let a2 = 2097151 & (load_3i(&a[5..8]) >> 2); 1328 | let a3 = 2097151 & (load_4i(&a[7..11]) >> 7); 1329 | let a4 = 2097151 & (load_4i(&a[10..14]) >> 4); 1330 | let a5 = 2097151 & (load_3i(&a[13..16]) >> 1); 1331 | let a6 = 2097151 & (load_4i(&a[15..19]) >> 6); 1332 | let a7 = 2097151 & (load_3i(&a[18..21]) >> 3); 1333 | let a8 = 2097151 & load_3i(&a[21..24]); 1334 | let a9 = 2097151 & (load_4i(&a[23..27]) >> 5); 1335 | let a10 = 2097151 & (load_3i(&a[26..29]) >> 2); 1336 | let a11 = load_4i(&a[28..32]) >> 7; 1337 | let b0 = 2097151 & load_3i(&b[0..3]); 1338 | let b1 = 2097151 & (load_4i(&b[2..6]) >> 5); 1339 | let b2 = 2097151 & (load_3i(&b[5..8]) >> 2); 1340 | let b3 = 2097151 & (load_4i(&b[7..11]) >> 7); 1341 | let b4 = 2097151 & (load_4i(&b[10..14]) >> 4); 1342 | let b5 = 2097151 & (load_3i(&b[13..16]) >> 1); 1343 | let b6 = 2097151 & (load_4i(&b[15..19]) >> 6); 1344 | let b7 = 2097151 & (load_3i(&b[18..21]) >> 3); 1345 | let b8 = 2097151 & load_3i(&b[21..24]); 1346 | let b9 = 2097151 & (load_4i(&b[23..27]) >> 5); 1347 | let b10 = 2097151 & (load_3i(&b[26..29]) >> 2); 1348 | let b11 = load_4i(&b[28..32]) >> 7; 1349 | let c0 = 2097151 & load_3i(&c[0..3]); 1350 | let c1 = 2097151 & (load_4i(&c[2..6]) >> 5); 1351 | let c2 = 2097151 & (load_3i(&c[5..8]) >> 2); 1352 | let c3 = 2097151 & (load_4i(&c[7..11]) >> 7); 1353 | let c4 = 2097151 & (load_4i(&c[10..14]) >> 4); 1354 | let c5 = 2097151 & (load_3i(&c[13..16]) >> 1); 1355 | let c6 = 2097151 & (load_4i(&c[15..19]) >> 6); 1356 | let c7 = 2097151 & (load_3i(&c[18..21]) >> 3); 1357 | let c8 = 2097151 & load_3i(&c[21..24]); 1358 | let c9 = 2097151 & (load_4i(&c[23..27]) >> 5); 1359 | let c10 = 2097151 & (load_3i(&c[26..29]) >> 2); 1360 | let c11 = load_4i(&c[28..32]) >> 7; 1361 | let mut s0: i64; 1362 | let mut s1: i64; 1363 | let mut s2: i64; 1364 | let mut s3: i64; 1365 | let mut s4: i64; 1366 | let mut s5: i64; 1367 | let mut s6: i64; 1368 | let mut s7: i64; 1369 | let mut s8: i64; 1370 | let mut s9: i64; 1371 | let mut s10: i64; 1372 | let mut s11: i64; 1373 | let mut s12: i64; 1374 | let mut s13: i64; 1375 | let mut s14: i64; 1376 | let mut s15: i64; 1377 | let mut s16: i64; 1378 | let mut s17: i64; 1379 | let mut s18: i64; 1380 | let mut s19: i64; 1381 | let mut s20: i64; 1382 | let mut s21: i64; 1383 | let mut s22: i64; 1384 | let mut s23: i64; 1385 | let mut carry0: i64; 1386 | let mut carry1: i64; 1387 | let mut carry2: i64; 1388 | let mut carry3: i64; 1389 | let mut carry4: i64; 1390 | let mut carry5: i64; 1391 | let mut carry6: i64; 1392 | let mut carry7: i64; 1393 | let mut carry8: i64; 1394 | let mut carry9: i64; 1395 | let mut carry10: i64; 1396 | let mut carry11: i64; 1397 | let mut carry12: i64; 1398 | let mut carry13: i64; 1399 | let mut carry14: i64; 1400 | let mut carry15: i64; 1401 | let mut carry16: i64; 1402 | 1403 | s0 = c0 + a0 * b0; 1404 | s1 = c1 + a0 * b1 + a1 * b0; 1405 | s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0; 1406 | s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; 1407 | s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0; 1408 | s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0; 1409 | s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0; 1410 | s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 + a7 * b0; 1411 | s8 = c8 1412 | + a0 * b8 1413 | + a1 * b7 1414 | + a2 * b6 1415 | + a3 * b5 1416 | + a4 * b4 1417 | + a5 * b3 1418 | + a6 * b2 1419 | + a7 * b1 1420 | + a8 * b0; 1421 | s9 = c9 1422 | + a0 * b9 1423 | + a1 * b8 1424 | + a2 * b7 1425 | + a3 * b6 1426 | + a4 * b5 1427 | + a5 * b4 1428 | + a6 * b3 1429 | + a7 * b2 1430 | + a8 * b1 1431 | + a9 * b0; 1432 | s10 = c10 1433 | + a0 * b10 1434 | + a1 * b9 1435 | + a2 * b8 1436 | + a3 * b7 1437 | + a4 * b6 1438 | + a5 * b5 1439 | + a6 * b4 1440 | + a7 * b3 1441 | + a8 * b2 1442 | + a9 * b1 1443 | + a10 * b0; 1444 | s11 = c11 1445 | + a0 * b11 1446 | + a1 * b10 1447 | + a2 * b9 1448 | + a3 * b8 1449 | + a4 * b7 1450 | + a5 * b6 1451 | + a6 * b5 1452 | + a7 * b4 1453 | + a8 * b3 1454 | + a9 * b2 1455 | + a10 * b1 1456 | + a11 * b0; 1457 | s12 = a1 * b11 1458 | + a2 * b10 1459 | + a3 * b9 1460 | + a4 * b8 1461 | + a5 * b7 1462 | + a6 * b6 1463 | + a7 * b5 1464 | + a8 * b4 1465 | + a9 * b3 1466 | + a10 * b2 1467 | + a11 * b1; 1468 | s13 = a2 * b11 1469 | + a3 * b10 1470 | + a4 * b9 1471 | + a5 * b8 1472 | + a6 * b7 1473 | + a7 * b6 1474 | + a8 * b5 1475 | + a9 * b4 1476 | + a10 * b3 1477 | + a11 * b2; 1478 | s14 = 1479 | a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 + a10 * b4 + a11 * b3; 1480 | s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 + a11 * b4; 1481 | s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5; 1482 | s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6; 1483 | s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7; 1484 | s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8; 1485 | s20 = a9 * b11 + a10 * b10 + a11 * b9; 1486 | s21 = a10 * b11 + a11 * b10; 1487 | s22 = a11 * b11; 1488 | s23 = 0; 1489 | 1490 | carry0 = (s0 + (1 << 20)) >> 21; 1491 | s1 += carry0; 1492 | s0 -= carry0 << 21; 1493 | carry2 = (s2 + (1 << 20)) >> 21; 1494 | s3 += carry2; 1495 | s2 -= carry2 << 21; 1496 | carry4 = (s4 + (1 << 20)) >> 21; 1497 | s5 += carry4; 1498 | s4 -= carry4 << 21; 1499 | carry6 = (s6 + (1 << 20)) >> 21; 1500 | s7 += carry6; 1501 | s6 -= carry6 << 21; 1502 | carry8 = (s8 + (1 << 20)) >> 21; 1503 | s9 += carry8; 1504 | s8 -= carry8 << 21; 1505 | carry10 = (s10 + (1 << 20)) >> 21; 1506 | s11 += carry10; 1507 | s10 -= carry10 << 21; 1508 | carry12 = (s12 + (1 << 20)) >> 21; 1509 | s13 += carry12; 1510 | s12 -= carry12 << 21; 1511 | carry14 = (s14 + (1 << 20)) >> 21; 1512 | s15 += carry14; 1513 | s14 -= carry14 << 21; 1514 | carry16 = (s16 + (1 << 20)) >> 21; 1515 | s17 += carry16; 1516 | s16 -= carry16 << 21; 1517 | let carry18: i64 = (s18 + (1 << 20)) >> 21; 1518 | s19 += carry18; 1519 | s18 -= carry18 << 21; 1520 | let carry20: i64 = (s20 + (1 << 20)) >> 21; 1521 | s21 += carry20; 1522 | s20 -= carry20 << 21; 1523 | let carry22: i64 = (s22 + (1 << 20)) >> 21; 1524 | s23 += carry22; 1525 | s22 -= carry22 << 21; 1526 | 1527 | carry1 = (s1 + (1 << 20)) >> 21; 1528 | s2 += carry1; 1529 | s1 -= carry1 << 21; 1530 | carry3 = (s3 + (1 << 20)) >> 21; 1531 | s4 += carry3; 1532 | s3 -= carry3 << 21; 1533 | carry5 = (s5 + (1 << 20)) >> 21; 1534 | s6 += carry5; 1535 | s5 -= carry5 << 21; 1536 | carry7 = (s7 + (1 << 20)) >> 21; 1537 | s8 += carry7; 1538 | s7 -= carry7 << 21; 1539 | carry9 = (s9 + (1 << 20)) >> 21; 1540 | s10 += carry9; 1541 | s9 -= carry9 << 21; 1542 | carry11 = (s11 + (1 << 20)) >> 21; 1543 | s12 += carry11; 1544 | s11 -= carry11 << 21; 1545 | carry13 = (s13 + (1 << 20)) >> 21; 1546 | s14 += carry13; 1547 | s13 -= carry13 << 21; 1548 | carry15 = (s15 + (1 << 20)) >> 21; 1549 | s16 += carry15; 1550 | s15 -= carry15 << 21; 1551 | let carry17: i64 = (s17 + (1 << 20)) >> 21; 1552 | s18 += carry17; 1553 | s17 -= carry17 << 21; 1554 | let carry19: i64 = (s19 + (1 << 20)) >> 21; 1555 | s20 += carry19; 1556 | s19 -= carry19 << 21; 1557 | let carry21: i64 = (s21 + (1 << 20)) >> 21; 1558 | s22 += carry21; 1559 | s21 -= carry21 << 21; 1560 | 1561 | s11 += s23 * 666643; 1562 | s12 += s23 * 470296; 1563 | s13 += s23 * 654183; 1564 | s14 -= s23 * 997805; 1565 | s15 += s23 * 136657; 1566 | s16 -= s23 * 683901; 1567 | 1568 | s10 += s22 * 666643; 1569 | s11 += s22 * 470296; 1570 | s12 += s22 * 654183; 1571 | s13 -= s22 * 997805; 1572 | s14 += s22 * 136657; 1573 | s15 -= s22 * 683901; 1574 | 1575 | s9 += s21 * 666643; 1576 | s10 += s21 * 470296; 1577 | s11 += s21 * 654183; 1578 | s12 -= s21 * 997805; 1579 | s13 += s21 * 136657; 1580 | s14 -= s21 * 683901; 1581 | 1582 | s8 += s20 * 666643; 1583 | s9 += s20 * 470296; 1584 | s10 += s20 * 654183; 1585 | s11 -= s20 * 997805; 1586 | s12 += s20 * 136657; 1587 | s13 -= s20 * 683901; 1588 | 1589 | s7 += s19 * 666643; 1590 | s8 += s19 * 470296; 1591 | s9 += s19 * 654183; 1592 | s10 -= s19 * 997805; 1593 | s11 += s19 * 136657; 1594 | s12 -= s19 * 683901; 1595 | 1596 | s6 += s18 * 666643; 1597 | s7 += s18 * 470296; 1598 | s8 += s18 * 654183; 1599 | s9 -= s18 * 997805; 1600 | s10 += s18 * 136657; 1601 | s11 -= s18 * 683901; 1602 | 1603 | carry6 = (s6 + (1 << 20)) >> 21; 1604 | s7 += carry6; 1605 | s6 -= carry6 << 21; 1606 | carry8 = (s8 + (1 << 20)) >> 21; 1607 | s9 += carry8; 1608 | s8 -= carry8 << 21; 1609 | carry10 = (s10 + (1 << 20)) >> 21; 1610 | s11 += carry10; 1611 | s10 -= carry10 << 21; 1612 | carry12 = (s12 + (1 << 20)) >> 21; 1613 | s13 += carry12; 1614 | s12 -= carry12 << 21; 1615 | carry14 = (s14 + (1 << 20)) >> 21; 1616 | s15 += carry14; 1617 | s14 -= carry14 << 21; 1618 | carry16 = (s16 + (1 << 20)) >> 21; 1619 | s17 += carry16; 1620 | s16 -= carry16 << 21; 1621 | 1622 | carry7 = (s7 + (1 << 20)) >> 21; 1623 | s8 += carry7; 1624 | s7 -= carry7 << 21; 1625 | carry9 = (s9 + (1 << 20)) >> 21; 1626 | s10 += carry9; 1627 | s9 -= carry9 << 21; 1628 | carry11 = (s11 + (1 << 20)) >> 21; 1629 | s12 += carry11; 1630 | s11 -= carry11 << 21; 1631 | carry13 = (s13 + (1 << 20)) >> 21; 1632 | s14 += carry13; 1633 | s13 -= carry13 << 21; 1634 | carry15 = (s15 + (1 << 20)) >> 21; 1635 | s16 += carry15; 1636 | s15 -= carry15 << 21; 1637 | 1638 | s5 += s17 * 666643; 1639 | s6 += s17 * 470296; 1640 | s7 += s17 * 654183; 1641 | s8 -= s17 * 997805; 1642 | s9 += s17 * 136657; 1643 | s10 -= s17 * 683901; 1644 | 1645 | s4 += s16 * 666643; 1646 | s5 += s16 * 470296; 1647 | s6 += s16 * 654183; 1648 | s7 -= s16 * 997805; 1649 | s8 += s16 * 136657; 1650 | s9 -= s16 * 683901; 1651 | 1652 | s3 += s15 * 666643; 1653 | s4 += s15 * 470296; 1654 | s5 += s15 * 654183; 1655 | s6 -= s15 * 997805; 1656 | s7 += s15 * 136657; 1657 | s8 -= s15 * 683901; 1658 | 1659 | s2 += s14 * 666643; 1660 | s3 += s14 * 470296; 1661 | s4 += s14 * 654183; 1662 | s5 -= s14 * 997805; 1663 | s6 += s14 * 136657; 1664 | s7 -= s14 * 683901; 1665 | 1666 | s1 += s13 * 666643; 1667 | s2 += s13 * 470296; 1668 | s3 += s13 * 654183; 1669 | s4 -= s13 * 997805; 1670 | s5 += s13 * 136657; 1671 | s6 -= s13 * 683901; 1672 | 1673 | s0 += s12 * 666643; 1674 | s1 += s12 * 470296; 1675 | s2 += s12 * 654183; 1676 | s3 -= s12 * 997805; 1677 | s4 += s12 * 136657; 1678 | s5 -= s12 * 683901; 1679 | s12 = 0; 1680 | 1681 | carry0 = (s0 + (1 << 20)) >> 21; 1682 | s1 += carry0; 1683 | s0 -= carry0 << 21; 1684 | carry2 = (s2 + (1 << 20)) >> 21; 1685 | s3 += carry2; 1686 | s2 -= carry2 << 21; 1687 | carry4 = (s4 + (1 << 20)) >> 21; 1688 | s5 += carry4; 1689 | s4 -= carry4 << 21; 1690 | carry6 = (s6 + (1 << 20)) >> 21; 1691 | s7 += carry6; 1692 | s6 -= carry6 << 21; 1693 | carry8 = (s8 + (1 << 20)) >> 21; 1694 | s9 += carry8; 1695 | s8 -= carry8 << 21; 1696 | carry10 = (s10 + (1 << 20)) >> 21; 1697 | s11 += carry10; 1698 | s10 -= carry10 << 21; 1699 | 1700 | carry1 = (s1 + (1 << 20)) >> 21; 1701 | s2 += carry1; 1702 | s1 -= carry1 << 21; 1703 | carry3 = (s3 + (1 << 20)) >> 21; 1704 | s4 += carry3; 1705 | s3 -= carry3 << 21; 1706 | carry5 = (s5 + (1 << 20)) >> 21; 1707 | s6 += carry5; 1708 | s5 -= carry5 << 21; 1709 | carry7 = (s7 + (1 << 20)) >> 21; 1710 | s8 += carry7; 1711 | s7 -= carry7 << 21; 1712 | carry9 = (s9 + (1 << 20)) >> 21; 1713 | s10 += carry9; 1714 | s9 -= carry9 << 21; 1715 | carry11 = (s11 + (1 << 20)) >> 21; 1716 | s12 += carry11; 1717 | s11 -= carry11 << 21; 1718 | 1719 | s0 += s12 * 666643; 1720 | s1 += s12 * 470296; 1721 | s2 += s12 * 654183; 1722 | s3 -= s12 * 997805; 1723 | s4 += s12 * 136657; 1724 | s5 -= s12 * 683901; 1725 | s12 = 0; 1726 | 1727 | carry0 = s0 >> 21; 1728 | s1 += carry0; 1729 | s0 -= carry0 << 21; 1730 | carry1 = s1 >> 21; 1731 | s2 += carry1; 1732 | s1 -= carry1 << 21; 1733 | carry2 = s2 >> 21; 1734 | s3 += carry2; 1735 | s2 -= carry2 << 21; 1736 | carry3 = s3 >> 21; 1737 | s4 += carry3; 1738 | s3 -= carry3 << 21; 1739 | carry4 = s4 >> 21; 1740 | s5 += carry4; 1741 | s4 -= carry4 << 21; 1742 | carry5 = s5 >> 21; 1743 | s6 += carry5; 1744 | s5 -= carry5 << 21; 1745 | carry6 = s6 >> 21; 1746 | s7 += carry6; 1747 | s6 -= carry6 << 21; 1748 | carry7 = s7 >> 21; 1749 | s8 += carry7; 1750 | s7 -= carry7 << 21; 1751 | carry8 = s8 >> 21; 1752 | s9 += carry8; 1753 | s8 -= carry8 << 21; 1754 | carry9 = s9 >> 21; 1755 | s10 += carry9; 1756 | s9 -= carry9 << 21; 1757 | carry10 = s10 >> 21; 1758 | s11 += carry10; 1759 | s10 -= carry10 << 21; 1760 | carry11 = s11 >> 21; 1761 | s12 += carry11; 1762 | s11 -= carry11 << 21; 1763 | 1764 | s0 += s12 * 666643; 1765 | s1 += s12 * 470296; 1766 | s2 += s12 * 654183; 1767 | s3 -= s12 * 997805; 1768 | s4 += s12 * 136657; 1769 | s5 -= s12 * 683901; 1770 | 1771 | carry0 = s0 >> 21; 1772 | s1 += carry0; 1773 | s0 -= carry0 << 21; 1774 | carry1 = s1 >> 21; 1775 | s2 += carry1; 1776 | s1 -= carry1 << 21; 1777 | carry2 = s2 >> 21; 1778 | s3 += carry2; 1779 | s2 -= carry2 << 21; 1780 | carry3 = s3 >> 21; 1781 | s4 += carry3; 1782 | s3 -= carry3 << 21; 1783 | carry4 = s4 >> 21; 1784 | s5 += carry4; 1785 | s4 -= carry4 << 21; 1786 | carry5 = s5 >> 21; 1787 | s6 += carry5; 1788 | s5 -= carry5 << 21; 1789 | carry6 = s6 >> 21; 1790 | s7 += carry6; 1791 | s6 -= carry6 << 21; 1792 | carry7 = s7 >> 21; 1793 | s8 += carry7; 1794 | s7 -= carry7 << 21; 1795 | carry8 = s8 >> 21; 1796 | s9 += carry8; 1797 | s8 -= carry8 << 21; 1798 | carry9 = s9 >> 21; 1799 | s10 += carry9; 1800 | s9 -= carry9 << 21; 1801 | carry10 = s10 >> 21; 1802 | s11 += carry10; 1803 | s10 -= carry10 << 21; 1804 | 1805 | s[0] = (s0 >> 0) as u8; 1806 | s[1] = (s0 >> 8) as u8; 1807 | s[2] = ((s0 >> 16) | (s1 << 5)) as u8; 1808 | s[3] = (s1 >> 3) as u8; 1809 | s[4] = (s1 >> 11) as u8; 1810 | s[5] = ((s1 >> 19) | (s2 << 2)) as u8; 1811 | s[6] = (s2 >> 6) as u8; 1812 | s[7] = ((s2 >> 14) | (s3 << 7)) as u8; 1813 | s[8] = (s3 >> 1) as u8; 1814 | s[9] = (s3 >> 9) as u8; 1815 | s[10] = ((s3 >> 17) | (s4 << 4)) as u8; 1816 | s[11] = (s4 >> 4) as u8; 1817 | s[12] = (s4 >> 12) as u8; 1818 | s[13] = ((s4 >> 20) | (s5 << 1)) as u8; 1819 | s[14] = (s5 >> 7) as u8; 1820 | s[15] = ((s5 >> 15) | (s6 << 6)) as u8; 1821 | s[16] = (s6 >> 2) as u8; 1822 | s[17] = (s6 >> 10) as u8; 1823 | s[18] = ((s6 >> 18) | (s7 << 3)) as u8; 1824 | s[19] = (s7 >> 5) as u8; 1825 | s[20] = (s7 >> 13) as u8; 1826 | s[21] = (s8 >> 0) as u8; 1827 | s[22] = (s8 >> 8) as u8; 1828 | s[23] = ((s8 >> 16) | (s9 << 5)) as u8; 1829 | s[24] = (s9 >> 3) as u8; 1830 | s[25] = (s9 >> 11) as u8; 1831 | s[26] = ((s9 >> 19) | (s10 << 2)) as u8; 1832 | s[27] = (s10 >> 6) as u8; 1833 | s[28] = ((s10 >> 14) | (s11 << 7)) as u8; 1834 | s[29] = (s11 >> 1) as u8; 1835 | s[30] = (s11 >> 9) as u8; 1836 | s[31] = (s11 >> 17) as u8; 1837 | } 1838 | 1839 | pub fn is_identity(s: &[u8]) -> bool { 1840 | let mut c = s[0] ^ 0x01; 1841 | for i in 1..31 { 1842 | c |= s[i]; 1843 | } 1844 | c |= s[31] & 0x7f; 1845 | c == 0 1846 | } 1847 | 1848 | static BI: [GePrecomp; 8] = [ 1849 | GePrecomp { 1850 | y_plus_x: Fe([ 1851 | 1288382639258501, 1852 | 245678601348599, 1853 | 269427782077623, 1854 | 1462984067271730, 1855 | 137412439391563, 1856 | ]), 1857 | y_minus_x: Fe([ 1858 | 62697248952638, 1859 | 204681361388450, 1860 | 631292143396476, 1861 | 338455783676468, 1862 | 1213667448819585, 1863 | ]), 1864 | xy2d: Fe([ 1865 | 301289933810280, 1866 | 1259582250014073, 1867 | 1422107436869536, 1868 | 796239922652654, 1869 | 1953934009299142, 1870 | ]), 1871 | }, 1872 | GePrecomp { 1873 | y_plus_x: Fe([ 1874 | 1601611775252272, 1875 | 1720807796594148, 1876 | 1132070835939856, 1877 | 1260455018889551, 1878 | 2147779492816911, 1879 | ]), 1880 | y_minus_x: Fe([ 1881 | 316559037616741, 1882 | 2177824224946892, 1883 | 1459442586438991, 1884 | 1461528397712656, 1885 | 751590696113597, 1886 | ]), 1887 | xy2d: Fe([ 1888 | 1850748884277385, 1889 | 1200145853858453, 1890 | 1068094770532492, 1891 | 672251375690438, 1892 | 1586055907191707, 1893 | ]), 1894 | }, 1895 | GePrecomp { 1896 | y_plus_x: Fe([ 1897 | 769950342298419, 1898 | 132954430919746, 1899 | 844085933195555, 1900 | 974092374476333, 1901 | 726076285546016, 1902 | ]), 1903 | y_minus_x: Fe([ 1904 | 425251763115706, 1905 | 608463272472562, 1906 | 442562545713235, 1907 | 837766094556764, 1908 | 374555092627893, 1909 | ]), 1910 | xy2d: Fe([ 1911 | 1086255230780037, 1912 | 274979815921559, 1913 | 1960002765731872, 1914 | 929474102396301, 1915 | 1190409889297339, 1916 | ]), 1917 | }, 1918 | GePrecomp { 1919 | y_plus_x: Fe([ 1920 | 665000864555967, 1921 | 2065379846933859, 1922 | 370231110385876, 1923 | 350988370788628, 1924 | 1233371373142985, 1925 | ]), 1926 | y_minus_x: Fe([ 1927 | 2019367628972465, 1928 | 676711900706637, 1929 | 110710997811333, 1930 | 1108646842542025, 1931 | 517791959672113, 1932 | ]), 1933 | xy2d: Fe([ 1934 | 965130719900578, 1935 | 247011430587952, 1936 | 526356006571389, 1937 | 91986625355052, 1938 | 2157223321444601, 1939 | ]), 1940 | }, 1941 | GePrecomp { 1942 | y_plus_x: Fe([ 1943 | 1802695059465007, 1944 | 1664899123557221, 1945 | 593559490740857, 1946 | 2160434469266659, 1947 | 927570450755031, 1948 | ]), 1949 | y_minus_x: Fe([ 1950 | 1725674970513508, 1951 | 1933645953859181, 1952 | 1542344539275782, 1953 | 1767788773573747, 1954 | 1297447965928905, 1955 | ]), 1956 | xy2d: Fe([ 1957 | 1381809363726107, 1958 | 1430341051343062, 1959 | 2061843536018959, 1960 | 1551778050872521, 1961 | 2036394857967624, 1962 | ]), 1963 | }, 1964 | GePrecomp { 1965 | y_plus_x: Fe([ 1966 | 1970894096313054, 1967 | 528066325833207, 1968 | 1619374932191227, 1969 | 2207306624415883, 1970 | 1169170329061080, 1971 | ]), 1972 | y_minus_x: Fe([ 1973 | 2070390218572616, 1974 | 1458919061857835, 1975 | 624171843017421, 1976 | 1055332792707765, 1977 | 433987520732508, 1978 | ]), 1979 | xy2d: Fe([ 1980 | 893653801273833, 1981 | 1168026499324677, 1982 | 1242553501121234, 1983 | 1306366254304474, 1984 | 1086752658510815, 1985 | ]), 1986 | }, 1987 | GePrecomp { 1988 | y_plus_x: Fe([ 1989 | 213454002618221, 1990 | 939771523987438, 1991 | 1159882208056014, 1992 | 317388369627517, 1993 | 621213314200687, 1994 | ]), 1995 | y_minus_x: Fe([ 1996 | 1971678598905747, 1997 | 338026507889165, 1998 | 762398079972271, 1999 | 655096486107477, 2000 | 42299032696322, 2001 | ]), 2002 | xy2d: Fe([ 2003 | 177130678690680, 2004 | 1754759263300204, 2005 | 1864311296286618, 2006 | 1180675631479880, 2007 | 1292726903152791, 2008 | ]), 2009 | }, 2010 | GePrecomp { 2011 | y_plus_x: Fe([ 2012 | 1913163449625248, 2013 | 460779200291993, 2014 | 2193883288642314, 2015 | 1008900146920800, 2016 | 1721983679009502, 2017 | ]), 2018 | y_minus_x: Fe([ 2019 | 1070401523076875, 2020 | 1272492007800961, 2021 | 1910153608563310, 2022 | 2075579521696771, 2023 | 1191169788841221, 2024 | ]), 2025 | xy2d: Fe([ 2026 | 692896803108118, 2027 | 500174642072499, 2028 | 2068223309439677, 2029 | 1162190621851337, 2030 | 1426986007309901, 2031 | ]), 2032 | }, 2033 | ]; 2034 | --------------------------------------------------------------------------------