├── .github ├── dependabot.yml └── workflows │ └── issues.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── base64.rs ├── crypto ├── blake2b.rs ├── cryptoutil.rs ├── curve25519.rs ├── ed25519.rs ├── mod.rs └── sha512.rs └── lib.rs /.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/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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "minisign-verify" 3 | version = "0.2.4" 4 | authors = ["Frank Denis "] 5 | description = "A small, zero-dependencies crate to verify Minisign signatures." 6 | readme = "README.md" 7 | keywords = ["minisign", "ed25519", "signatures", "crypto"] 8 | license = "MIT" 9 | homepage = "https://github.com/jedisct1/rust-minisign-verify" 10 | repository = "https://github.com/jedisct1/rust-minisign-verify" 11 | categories = ["cryptography"] 12 | edition = "2018" 13 | 14 | [profile.release] 15 | lto = true 16 | panic = "abort" 17 | opt-level = 3 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019-2025 Frank Denis 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | 27 | -- 28 | 29 | Code in the src/crypto folder is derived from the rust-crypto project: 30 | https://github.com/DaGenix/rust-crypto 31 | 32 | Original ISC license follows: 33 | 34 | Copyright (c) 2006-2009 Graydon Hoare 35 | Copyright (c) 2009-2013 Mozilla Foundation 36 | 37 | Permission is hereby granted, free of charge, to any 38 | person obtaining a copy of this software and associated 39 | documentation files (the "Software"), to deal in the 40 | Software without restriction, including without 41 | limitation the rights to use, copy, modify, merge, 42 | publish, distribute, sublicense, and/or sell copies of 43 | the Software, and to permit persons to whom the Software 44 | is furnished to do so, subject to the following 45 | conditions: 46 | 47 | The above copyright notice and this permission notice 48 | shall be included in all copies or substantial portions 49 | of the Software. 50 | 51 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 52 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 53 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 54 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 55 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 56 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 57 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 58 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 59 | DEALINGS IN THE SOFTWARE. 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # minisign-verify 2 | 3 | A small, zero-dependencies Rust crate to verify [Minisign](https://jedisct1.github.io/minisign/) signatures. 4 | 5 | [API documentation](https://docs.rs/minisign-verify) 6 | 7 | ## Features 8 | 9 | * Verify signatures for both standard and pre-hashed modes 10 | * Streaming verification for large files 11 | * No external dependencies 12 | * Simple, auditable code 13 | * Comprehensive error reporting 14 | 15 | ## Basic Example 16 | 17 | ```rust 18 | let public_key = 19 | PublicKey::from_base64("RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3") 20 | .expect("Unable to decode the public key"); 21 | 22 | let signature = Signature::decode( 23 | "untrusted comment: signature from minisign secret key 24 | RWQf6LRCGA9i59SLOFxz6NxvASXDJeRtuZykwQepbDEGt87ig1BNpWaVWuNrm73YiIiJbq71Wi+dP9eKL8OC351vwIasSSbXxwA= 25 | trusted comment: timestamp:1555779966\tfile:test 26 | QtKMXWyYcwdpZAlPF7tE2ENJkRd1ujvKjlj1m9RtHTBnZPa5WKU5uWRs5GoP5M/VqE81QFuMKI5k/SfNQUaOAA==", 27 | ).expect("Unable to decode the signature"); 28 | 29 | let bin = b"test"; 30 | public_key.verify(&bin[..], &signature, false).expect("Signature didn't verify"); 31 | ``` 32 | 33 | ## Loading from Files 34 | 35 | ```rust 36 | use minisign_verify::{PublicKey, Signature}; 37 | use std::path::Path; 38 | 39 | // Load a public key from a file 40 | let public_key = PublicKey::from_file(Path::new("minisign.pub")) 41 | .expect("Unable to load the public key"); 42 | 43 | // Load a signature from a file 44 | let signature = Signature::from_file(Path::new("file.sig")) 45 | .expect("Unable to load the signature"); 46 | 47 | // Load the file content to verify 48 | let content = std::fs::read("file").expect("Unable to read the file"); 49 | 50 | // Verify the signature 51 | public_key 52 | .verify(&content, &signature, false) 53 | .expect("Signature didn't verify"); 54 | ``` 55 | 56 | ## Streaming Verification (for Large Files) 57 | 58 | ```rust 59 | use minisign_verify::{PublicKey, Signature}; 60 | use std::fs::File; 61 | use std::io::Read; 62 | use std::path::Path; 63 | 64 | // Load a public key and signature 65 | let public_key = PublicKey::from_file(Path::new("minisign.pub")) 66 | .expect("Unable to load the public key"); 67 | 68 | let signature = Signature::from_file(Path::new("large_file.sig")) 69 | .expect("Unable to load the signature"); 70 | 71 | // Create a stream verifier 72 | let mut verifier = public_key.verify_stream(&signature) 73 | .expect("Unable to create stream verifier"); 74 | 75 | // Process the file in chunks 76 | let mut file = File::open("large_file").expect("Unable to open file"); 77 | let mut buffer = [0u8; 8192]; // 8KB buffer 78 | 79 | loop { 80 | let bytes_read = file.read(&mut buffer).expect("Error reading file"); 81 | if bytes_read == 0 { 82 | break; // End of file 83 | } 84 | 85 | verifier.update(&buffer[..bytes_read]); 86 | } 87 | 88 | // Verify the signature 89 | verifier.finalize().expect("Signature verification failed"); 90 | ``` 91 | 92 | Note that the streaming verification mode only works with pre-hashed signatures (the default in newer versions of Minisign). 93 | 94 | ## Running Benchmarks 95 | 96 | To run the benchmarks: 97 | 98 | ```bash 99 | cargo +nightly bench 100 | ``` 101 | 102 | ## License 103 | 104 | MIT 105 | -------------------------------------------------------------------------------- /src/base64.rs: -------------------------------------------------------------------------------- 1 | #![forbid(unsafe_code)] 2 | 3 | use core::fmt::{self, Display}; 4 | 5 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 6 | pub enum Error { 7 | /// The provided output buffer would be too small. 8 | Overflow, 9 | /// The input isn't valid for the given encoding. 10 | InvalidInput, 11 | } 12 | 13 | impl std::error::Error for Error {} 14 | 15 | impl Display for Error { 16 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 17 | match self { 18 | Error::Overflow => write!(f, "Overflow"), 19 | Error::InvalidInput => write!(f, "Invalid input"), 20 | } 21 | } 22 | } 23 | 24 | pub trait Decoder { 25 | /// Decode `encoded` into `bin`. 26 | /// The output buffer can be larger than required; the returned slice is 27 | /// a view of the buffer with the correct length. 28 | fn decode>(bin: &mut [u8], encoded: IN) -> Result<&[u8], Error>; 29 | 30 | /// Decode `encoded` into a `Vec`. 31 | fn decode_to_vec>(encoded: IN) -> Result, Error> { 32 | let mut bin = vec![0u8; encoded.as_ref().len()]; 33 | let bin_len = Self::decode(&mut bin, encoded)?.len(); 34 | bin.truncate(bin_len); 35 | Ok(bin) 36 | } 37 | } 38 | 39 | struct Base64Impl; 40 | 41 | impl Base64Impl { 42 | #[inline] 43 | fn _eq(x: u8, y: u8) -> u8 { 44 | !(((0u16.wrapping_sub((x as u16) ^ (y as u16))) >> 8) as u8) 45 | } 46 | 47 | #[inline] 48 | fn _gt(x: u8, y: u8) -> u8 { 49 | (((y as u16).wrapping_sub(x as u16)) >> 8) as u8 50 | } 51 | 52 | #[inline] 53 | fn _ge(x: u8, y: u8) -> u8 { 54 | !Self::_gt(y, x) 55 | } 56 | 57 | #[inline] 58 | fn _lt(x: u8, y: u8) -> u8 { 59 | Self::_gt(y, x) 60 | } 61 | 62 | #[inline] 63 | fn _le(x: u8, y: u8) -> u8 { 64 | Self::_ge(y, x) 65 | } 66 | 67 | #[inline] 68 | fn b64_char_to_byte(c: u8) -> u8 { 69 | let x = (Self::_ge(c, b'A') & Self::_le(c, b'Z') & (c.wrapping_sub(b'A'))) 70 | | (Self::_ge(c, b'a') & Self::_le(c, b'z') & (c.wrapping_sub(b'a'.wrapping_sub(26)))) 71 | | (Self::_ge(c, b'0') & Self::_le(c, b'9') & (c.wrapping_sub(b'0'.wrapping_sub(52)))) 72 | | (Self::_eq(c, b'+') & 62) 73 | | (Self::_eq(c, b'/') & 63); 74 | x | (Self::_eq(x, 0) & (Self::_eq(c, b'A') ^ 0xff)) 75 | } 76 | 77 | fn skip_padding(b64: &[u8], mut padding_len: usize) -> Result<&[u8], Error> { 78 | let b64_len = b64.len(); 79 | let mut b64_pos = 0usize; 80 | while padding_len > 0 { 81 | if b64_pos >= b64_len { 82 | return Err(Error::InvalidInput); 83 | } 84 | let c = b64[b64_pos]; 85 | if c == b'=' { 86 | padding_len -= 1 87 | } else { 88 | return Err(Error::InvalidInput); 89 | } 90 | b64_pos += 1 91 | } 92 | Ok(&b64[b64_pos..]) 93 | } 94 | 95 | pub fn decode<'t>(bin: &'t mut [u8], b64: &[u8]) -> Result<&'t [u8], Error> { 96 | let bin_maxlen = bin.len(); 97 | let mut acc = 0u16; 98 | let mut acc_len = 0usize; 99 | let mut bin_pos = 0usize; 100 | let mut premature_end = None; 101 | for (b64_pos, &c) in b64.iter().enumerate() { 102 | let d = Self::b64_char_to_byte(c); 103 | if d == 0xff { 104 | premature_end = Some(b64_pos); 105 | break; 106 | } 107 | acc = (acc << 6) + d as u16; 108 | acc_len += 6; 109 | if acc_len >= 8 { 110 | acc_len -= 8; 111 | if bin_pos >= bin_maxlen { 112 | return Err(Error::Overflow); 113 | } 114 | bin[bin_pos] = (acc >> acc_len) as u8; 115 | bin_pos += 1; 116 | } 117 | } 118 | if acc_len > 4 || (acc & ((1u16 << acc_len).wrapping_sub(1))) != 0 { 119 | return Err(Error::InvalidInput); 120 | } 121 | let padding_len = acc_len / 2; 122 | if let Some(premature_end) = premature_end { 123 | let remaining = Self::skip_padding(&b64[premature_end..], padding_len)?; 124 | if !remaining.is_empty() { 125 | return Err(Error::InvalidInput); 126 | } 127 | } else if padding_len != 0 { 128 | return Err(Error::InvalidInput); 129 | } 130 | Ok(&bin[..bin_pos]) 131 | } 132 | } 133 | 134 | pub struct Base64; 135 | 136 | impl Decoder for Base64 { 137 | #[inline] 138 | fn decode>(bin: &mut [u8], b64: IN) -> Result<&[u8], Error> { 139 | Base64Impl::decode(bin, b64.as_ref()) 140 | } 141 | } 142 | 143 | #[test] 144 | fn test_base64_mising_padding() { 145 | let missing_padding = "AA"; 146 | assert!(Base64::decode_to_vec(missing_padding).is_err()); 147 | let missing_padding = "AAA"; 148 | assert!(Base64::decode_to_vec(missing_padding).is_err()); 149 | } 150 | 151 | #[test] 152 | fn test_base64_invalid_padding() { 153 | let valid_padding = "AA=="; 154 | assert_eq!(Base64::decode_to_vec(valid_padding), Ok(vec![0u8; 1])); 155 | let invalid_padding = "AA="; 156 | assert_eq!( 157 | Base64::decode_to_vec(invalid_padding), 158 | Err(Error::InvalidInput) 159 | ); 160 | } 161 | -------------------------------------------------------------------------------- /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 | 0x6a09_e667_f3bc_c908, 11 | 0xbb67_ae85_84ca_a73b, 12 | 0x3c6e_f372_fe94_f82b, 13 | 0xa54f_f53a_5f1d_36f1, 14 | 0x510e_527f_ade6_82d1, 15 | 0x9b05_688c_2b3e_6c1f, 16 | 0x1f83_d9ab_fb41_bd6b, 17 | 0x5be0_cd19_137e_2179, 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] = 0xffff_ffff_ffff_ffff; 103 | } 104 | 105 | fn set_lastblock(&mut self) { 106 | if self.last_node != 0 { 107 | self.set_lastnode(); 108 | } 109 | self.f[0] = 0xffff_ffff_ffff_ffff; 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 | pub fn blake2b(out: &mut [u8], input: &[u8]) { 284 | let mut hasher: Blake2b = Blake2b::new(out.len()); 285 | hasher.update(input); 286 | hasher.finalize(out); 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_to_bytes(out1: &mut [u8; 32], arg1: &[u64; 5]) { 235 | let mut x1: u64 = 0; 236 | let mut x2: fiat_25519_u1 = 0; 237 | fiat_25519_subborrowx_u51(&mut x1, &mut x2, 0x0, (arg1[0]), 0x7ffffffffffed); 238 | let mut x3: u64 = 0; 239 | let mut x4: fiat_25519_u1 = 0; 240 | fiat_25519_subborrowx_u51(&mut x3, &mut x4, x2, (arg1[1]), 0x7ffffffffffff); 241 | let mut x5: u64 = 0; 242 | let mut x6: fiat_25519_u1 = 0; 243 | fiat_25519_subborrowx_u51(&mut x5, &mut x6, x4, (arg1[2]), 0x7ffffffffffff); 244 | let mut x7: u64 = 0; 245 | let mut x8: fiat_25519_u1 = 0; 246 | fiat_25519_subborrowx_u51(&mut x7, &mut x8, x6, (arg1[3]), 0x7ffffffffffff); 247 | let mut x9: u64 = 0; 248 | let mut x10: fiat_25519_u1 = 0; 249 | fiat_25519_subborrowx_u51(&mut x9, &mut x10, x8, (arg1[4]), 0x7ffffffffffff); 250 | let mut x11: u64 = 0; 251 | fiat_25519_cmovznz_u64(&mut x11, x10, 0x0_u64, 0xffffffffffffffff); 252 | let mut x12: u64 = 0; 253 | let mut x13: fiat_25519_u1 = 0; 254 | fiat_25519_addcarryx_u51(&mut x12, &mut x13, 0x0, x1, (x11 & 0x7ffffffffffed)); 255 | let mut x14: u64 = 0; 256 | let mut x15: fiat_25519_u1 = 0; 257 | fiat_25519_addcarryx_u51(&mut x14, &mut x15, x13, x3, (x11 & 0x7ffffffffffff)); 258 | let mut x16: u64 = 0; 259 | let mut x17: fiat_25519_u1 = 0; 260 | fiat_25519_addcarryx_u51(&mut x16, &mut x17, x15, x5, (x11 & 0x7ffffffffffff)); 261 | let mut x18: u64 = 0; 262 | let mut x19: fiat_25519_u1 = 0; 263 | fiat_25519_addcarryx_u51(&mut x18, &mut x19, x17, x7, (x11 & 0x7ffffffffffff)); 264 | let mut x20: u64 = 0; 265 | let mut x21: fiat_25519_u1 = 0; 266 | fiat_25519_addcarryx_u51(&mut x20, &mut x21, x19, x9, (x11 & 0x7ffffffffffff)); 267 | let x22: u64 = (x20 << 4); 268 | let x23: u64 = (x18 * 0x2_u64); 269 | let x24: u64 = (x16 << 6); 270 | let x25: u64 = (x14 << 3); 271 | let x26: u64 = (x12 >> 8); 272 | let x27: u8 = ((x12 & 0xff_u64) as u8); 273 | let x28: u64 = (x26 >> 8); 274 | let x29: u8 = ((x26 & 0xff_u64) as u8); 275 | let x30: u64 = (x28 >> 8); 276 | let x31: u8 = ((x28 & 0xff_u64) as u8); 277 | let x32: u64 = (x30 >> 8); 278 | let x33: u8 = ((x30 & 0xff_u64) as u8); 279 | let x34: u64 = (x32 >> 8); 280 | let x35: u8 = ((x32 & 0xff_u64) as u8); 281 | let x36: u8 = ((x34 >> 8) as u8); 282 | let x37: u8 = ((x34 & 0xff_u64) as u8); 283 | let x38: u64 = ((x36 as u64) + x25); 284 | let x39: u64 = (x38 >> 8); 285 | let x40: u8 = ((x38 & 0xff_u64) as u8); 286 | let x41: u64 = (x39 >> 8); 287 | let x42: u8 = ((x39 & 0xff_u64) as u8); 288 | let x43: u64 = (x41 >> 8); 289 | let x44: u8 = ((x41 & 0xff_u64) as u8); 290 | let x45: u64 = (x43 >> 8); 291 | let x46: u8 = ((x43 & 0xff_u64) as u8); 292 | let x47: u64 = (x45 >> 8); 293 | let x48: u8 = ((x45 & 0xff_u64) as u8); 294 | let x49: u8 = ((x47 >> 8) as u8); 295 | let x50: u8 = ((x47 & 0xff_u64) as u8); 296 | let x51: u64 = ((x49 as u64) + x24); 297 | let x52: u64 = (x51 >> 8); 298 | let x53: u8 = ((x51 & 0xff_u64) as u8); 299 | let x54: u64 = (x52 >> 8); 300 | let x55: u8 = ((x52 & 0xff_u64) as u8); 301 | let x56: u64 = (x54 >> 8); 302 | let x57: u8 = ((x54 & 0xff_u64) as u8); 303 | let x58: u64 = (x56 >> 8); 304 | let x59: u8 = ((x56 & 0xff_u64) as u8); 305 | let x60: u64 = (x58 >> 8); 306 | let x61: u8 = ((x58 & 0xff_u64) as u8); 307 | let x62: u64 = (x60 >> 8); 308 | let x63: u8 = ((x60 & 0xff_u64) as u8); 309 | let x64: fiat_25519_u1 = ((x62 >> 8) as fiat_25519_u1); 310 | let x65: u8 = ((x62 & 0xff_u64) as u8); 311 | let x66: u64 = ((x64 as u64) + x23); 312 | let x67: u64 = (x66 >> 8); 313 | let x68: u8 = ((x66 & 0xff_u64) as u8); 314 | let x69: u64 = (x67 >> 8); 315 | let x70: u8 = ((x67 & 0xff_u64) as u8); 316 | let x71: u64 = (x69 >> 8); 317 | let x72: u8 = ((x69 & 0xff_u64) as u8); 318 | let x73: u64 = (x71 >> 8); 319 | let x74: u8 = ((x71 & 0xff_u64) as u8); 320 | let x75: u64 = (x73 >> 8); 321 | let x76: u8 = ((x73 & 0xff_u64) as u8); 322 | let x77: u8 = ((x75 >> 8) as u8); 323 | let x78: u8 = ((x75 & 0xff_u64) as u8); 324 | let x79: u64 = ((x77 as u64) + x22); 325 | let x80: u64 = (x79 >> 8); 326 | let x81: u8 = ((x79 & 0xff_u64) as u8); 327 | let x82: u64 = (x80 >> 8); 328 | let x83: u8 = ((x80 & 0xff_u64) as u8); 329 | let x84: u64 = (x82 >> 8); 330 | let x85: u8 = ((x82 & 0xff_u64) as u8); 331 | let x86: u64 = (x84 >> 8); 332 | let x87: u8 = ((x84 & 0xff_u64) as u8); 333 | let x88: u64 = (x86 >> 8); 334 | let x89: u8 = ((x86 & 0xff_u64) as u8); 335 | let x90: u8 = ((x88 >> 8) as u8); 336 | let x91: u8 = ((x88 & 0xff_u64) as u8); 337 | out1[0] = x27; 338 | out1[1] = x29; 339 | out1[2] = x31; 340 | out1[3] = x33; 341 | out1[4] = x35; 342 | out1[5] = x37; 343 | out1[6] = x40; 344 | out1[7] = x42; 345 | out1[8] = x44; 346 | out1[9] = x46; 347 | out1[10] = x48; 348 | out1[11] = x50; 349 | out1[12] = x53; 350 | out1[13] = x55; 351 | out1[14] = x57; 352 | out1[15] = x59; 353 | out1[16] = x61; 354 | out1[17] = x63; 355 | out1[18] = x65; 356 | out1[19] = x68; 357 | out1[20] = x70; 358 | out1[21] = x72; 359 | out1[22] = x74; 360 | out1[23] = x76; 361 | out1[24] = x78; 362 | out1[25] = x81; 363 | out1[26] = x83; 364 | out1[27] = x85; 365 | out1[28] = x87; 366 | out1[29] = x89; 367 | out1[30] = x91; 368 | out1[31] = x90; 369 | } 370 | 371 | #[derive(Clone, Default, Copy)] 372 | pub struct Fe(pub [u64; 5]); 373 | 374 | impl PartialEq for Fe { 375 | fn eq(&self, other: &Fe) -> bool { 376 | let &Fe(self_elems) = self; 377 | let &Fe(other_elems) = other; 378 | self_elems == other_elems 379 | } 380 | } 381 | impl Eq for Fe {} 382 | 383 | static FE_ZERO: Fe = Fe([0, 0, 0, 0, 0]); 384 | static FE_ONE: Fe = Fe([1, 0, 0, 0, 0]); 385 | static FE_SQRTM1: Fe = Fe([ 386 | 1718705420411056, 387 | 234908883556509, 388 | 2233514472574048, 389 | 2117202627021982, 390 | 765476049583133, 391 | ]); 392 | static FE_D: Fe = Fe([ 393 | 929955233495203, 394 | 466365720129213, 395 | 1662059464998953, 396 | 2033849074728123, 397 | 1442794654840575, 398 | ]); 399 | static FE_D2: Fe = Fe([ 400 | 1859910466990425, 401 | 932731440258426, 402 | 1072319116312658, 403 | 1815898335770999, 404 | 633789495995903, 405 | ]); 406 | 407 | #[inline] 408 | fn load_8u(s: &[u8]) -> u64 { 409 | (s[0] as u64) 410 | | ((s[1] as u64) << 8) 411 | | ((s[2] as u64) << 16) 412 | | ((s[3] as u64) << 24) 413 | | ((s[4] as u64) << 32) 414 | | ((s[5] as u64) << 40) 415 | | ((s[6] as u64) << 48) 416 | | ((s[7] as u64) << 56) 417 | } 418 | 419 | #[inline] 420 | fn load_4u(s: &[u8]) -> u64 { 421 | (s[0] as u64) | ((s[1] as u64) << 8) | ((s[2] as u64) << 16) | ((s[3] as u64) << 24) 422 | } 423 | 424 | #[inline] 425 | fn load_4i(s: &[u8]) -> i64 { 426 | load_4u(s) as i64 427 | } 428 | 429 | #[inline] 430 | fn load_3u(s: &[u8]) -> u64 { 431 | (s[0] as u64) | ((s[1] as u64) << 8) | ((s[2] as u64) << 16) 432 | } 433 | 434 | #[inline] 435 | fn load_3i(s: &[u8]) -> i64 { 436 | load_3u(s) as i64 437 | } 438 | 439 | impl Add for Fe { 440 | type Output = Fe; 441 | 442 | fn add(self, _rhs: Fe) -> Fe { 443 | let Fe(f) = self; 444 | let Fe(g) = _rhs; 445 | let mut h = Fe::default(); 446 | fiat_25519_add(&mut h.0, &f, &g); 447 | h 448 | } 449 | } 450 | 451 | impl Sub for Fe { 452 | type Output = Fe; 453 | 454 | fn sub(self, _rhs: Fe) -> Fe { 455 | let Fe(f) = self; 456 | let Fe(g) = _rhs; 457 | let mut h = Fe::default(); 458 | fiat_25519_sub(&mut h.0, &f, &g); 459 | h.carry() 460 | } 461 | } 462 | 463 | impl Mul for Fe { 464 | type Output = Fe; 465 | 466 | fn mul(self, _rhs: Fe) -> Fe { 467 | let Fe(f) = self; 468 | let Fe(g) = _rhs; 469 | let mut h = Fe::default(); 470 | fiat_25519_carry_mul(&mut h.0, &f, &g); 471 | h 472 | } 473 | } 474 | 475 | impl Fe { 476 | pub fn from_bytes(s: &[u8]) -> Fe { 477 | if s.len() != 32 { 478 | panic!("Invalid compressed length") 479 | } 480 | let mut h = Fe::default(); 481 | let mask = 0x7ffffffffffff; 482 | h.0[0] = load_8u(&s[0..]) & mask; 483 | h.0[1] = (load_8u(&s[6..]) >> 3) & mask; 484 | h.0[2] = (load_8u(&s[12..]) >> 6) & mask; 485 | h.0[3] = (load_8u(&s[19..]) >> 1) & mask; 486 | h.0[4] = (load_8u(&s[24..]) >> 12) & mask; 487 | h 488 | } 489 | 490 | pub fn to_bytes(&self) -> [u8; 32] { 491 | let &Fe(es) = &self.carry(); 492 | let mut s_ = [0u8; 32]; 493 | fiat_25519_to_bytes(&mut s_, &es); 494 | s_ 495 | } 496 | 497 | pub fn carry(&self) -> Fe { 498 | let mut h = Fe::default(); 499 | fiat_25519_carry(&mut h.0, &self.0); 500 | h 501 | } 502 | 503 | fn square(&self) -> Fe { 504 | let &Fe(f) = &self; 505 | let mut h = Fe::default(); 506 | fiat_25519_carry_square(&mut h.0, f); 507 | h 508 | } 509 | 510 | fn square_and_double(&self) -> Fe { 511 | let h = self.square(); 512 | (h + h) 513 | } 514 | 515 | pub fn invert(&self) -> Fe { 516 | let z1 = *self; 517 | let z2 = z1.square(); 518 | let z8 = z2.square().square(); 519 | let z9 = z1 * z8; 520 | let z11 = z2 * z9; 521 | let z22 = z11.square(); 522 | let z_5_0 = z9 * z22; 523 | let z_10_5 = (0..5).fold(z_5_0, |z_5_n, _| z_5_n.square()); 524 | let z_10_0 = z_10_5 * z_5_0; 525 | let z_20_10 = (0..10).fold(z_10_0, |x, _| x.square()); 526 | let z_20_0 = z_20_10 * z_10_0; 527 | let z_40_20 = (0..20).fold(z_20_0, |x, _| x.square()); 528 | let z_40_0 = z_40_20 * z_20_0; 529 | let z_50_10 = (0..10).fold(z_40_0, |x, _| x.square()); 530 | let z_50_0 = z_50_10 * z_10_0; 531 | let z_100_50 = (0..50).fold(z_50_0, |x, _| x.square()); 532 | let z_100_0 = z_100_50 * z_50_0; 533 | let z_200_100 = (0..100).fold(z_100_0, |x, _| x.square()); 534 | let z_200_0 = z_200_100 * z_100_0; 535 | let z_250_50 = (0..50).fold(z_200_0, |x, _| x.square()); 536 | let z_250_0 = z_250_50 * z_50_0; 537 | let z_255_5 = (0..5).fold(z_250_0, |x, _| x.square()); 538 | 539 | z_255_5 * z11 540 | } 541 | 542 | fn is_nonzero(&self) -> bool { 543 | self.to_bytes().iter().fold(0, |acc, x| acc | x) != 0 544 | } 545 | 546 | fn is_negative(&self) -> bool { 547 | (self.to_bytes()[0] & 1) != 0 548 | } 549 | 550 | fn neg(&self) -> Fe { 551 | let &Fe(f) = &self; 552 | let mut h = Fe::default(); 553 | fiat_25519_opp(&mut h.0, f); 554 | h 555 | } 556 | 557 | fn pow25523(&self) -> Fe { 558 | let z2 = self.square(); 559 | let z8 = (0..2).fold(z2, |x, _| x.square()); 560 | let z9 = *self * z8; 561 | let z11 = z2 * z9; 562 | let z22 = z11.square(); 563 | let z_5_0 = z9 * z22; 564 | let z_10_5 = (0..5).fold(z_5_0, |x, _| x.square()); 565 | let z_10_0 = z_10_5 * z_5_0; 566 | let z_20_10 = (0..10).fold(z_10_0, |x, _| x.square()); 567 | let z_20_0 = z_20_10 * z_10_0; 568 | let z_40_20 = (0..20).fold(z_20_0, |x, _| x.square()); 569 | let z_40_0 = z_40_20 * z_20_0; 570 | let z_50_10 = (0..10).fold(z_40_0, |x, _| x.square()); 571 | let z_50_0 = z_50_10 * z_10_0; 572 | let z_100_50 = (0..50).fold(z_50_0, |x, _| x.square()); 573 | let z_100_0 = z_100_50 * z_50_0; 574 | let z_200_100 = (0..100).fold(z_100_0, |x, _| x.square()); 575 | let z_200_0 = z_200_100 * z_100_0; 576 | let z_250_50 = (0..50).fold(z_200_0, |x, _| x.square()); 577 | let z_250_0 = z_250_50 * z_50_0; 578 | let z_252_2 = (0..2).fold(z_250_0, |x, _| x.square()); 579 | 580 | z_252_2 * *self 581 | } 582 | } 583 | 584 | #[derive(Clone, Copy)] 585 | pub struct GeP2 { 586 | x: Fe, 587 | y: Fe, 588 | z: Fe, 589 | } 590 | 591 | #[derive(Clone, Copy)] 592 | pub struct GeP3 { 593 | x: Fe, 594 | y: Fe, 595 | z: Fe, 596 | t: Fe, 597 | } 598 | 599 | #[derive(Clone, Copy)] 600 | pub struct GeP1P1 { 601 | x: Fe, 602 | y: Fe, 603 | z: Fe, 604 | t: Fe, 605 | } 606 | 607 | #[derive(Clone, Copy)] 608 | pub struct GePrecomp { 609 | y_plus_x: Fe, 610 | y_minus_x: Fe, 611 | xy2d: Fe, 612 | } 613 | 614 | #[derive(Clone, Copy)] 615 | pub struct GeCached { 616 | y_plus_x: Fe, 617 | y_minus_x: Fe, 618 | z: Fe, 619 | t2d: Fe, 620 | } 621 | 622 | impl GeP1P1 { 623 | fn to_p2(&self) -> GeP2 { 624 | GeP2 { 625 | x: self.x * self.t, 626 | y: self.y * self.z, 627 | z: self.z * self.t, 628 | } 629 | } 630 | 631 | fn to_p3(&self) -> GeP3 { 632 | GeP3 { 633 | x: self.x * self.t, 634 | y: self.y * self.z, 635 | z: self.z * self.t, 636 | t: self.x * self.y, 637 | } 638 | } 639 | } 640 | 641 | impl GeP2 { 642 | fn zero() -> GeP2 { 643 | GeP2 { 644 | x: FE_ZERO, 645 | y: FE_ONE, 646 | z: FE_ONE, 647 | } 648 | } 649 | 650 | pub fn to_bytes(&self) -> [u8; 32] { 651 | let recip = self.z.invert(); 652 | let x = self.x * recip; 653 | let y = self.y * recip; 654 | let mut bs = y.to_bytes(); 655 | bs[31] ^= (if x.is_negative() { 1 } else { 0 }) << 7; 656 | bs 657 | } 658 | 659 | fn dbl(&self) -> GeP1P1 { 660 | let xx = self.x.square(); 661 | let yy = self.y.square(); 662 | let b = self.z.square_and_double(); 663 | let a = self.x + self.y; 664 | let aa = a.square(); 665 | let y3 = yy + xx; 666 | let z3 = yy - xx; 667 | let x3 = aa - y3; 668 | let t3 = b - z3; 669 | 670 | GeP1P1 { 671 | x: x3, 672 | y: y3, 673 | z: z3, 674 | t: t3, 675 | } 676 | } 677 | 678 | fn slide(a: &[u8]) -> [i8; 256] { 679 | let mut r = [0i8; 256]; 680 | for i in 0..256 { 681 | r[i] = (1 & (a[i >> 3] >> (i & 7))) as i8; 682 | } 683 | for i in 0..256 { 684 | if r[i] != 0 { 685 | for b in 1..min(7, 256 - i) { 686 | if r[i + b] != 0 { 687 | if r[i] + (r[i + b] << b) <= 15 { 688 | r[i] += r[i + b] << b; 689 | r[i + b] = 0; 690 | } else if r[i] - (r[i + b] << b) >= -15 { 691 | r[i] -= r[i + b] << b; 692 | for k in i + b..256 { 693 | if r[k] == 0 { 694 | r[k] = 1; 695 | break; 696 | } 697 | r[k] = 0; 698 | } 699 | } else { 700 | break; 701 | } 702 | } 703 | } 704 | } 705 | } 706 | 707 | r 708 | } 709 | 710 | pub fn double_scalarmult_vartime(a_scalar: &[u8], a_point: GeP3, b_scalar: &[u8]) -> GeP2 { 711 | let aslide = GeP2::slide(a_scalar); 712 | let bslide = GeP2::slide(b_scalar); 713 | 714 | let mut ai = [GeCached { 715 | y_plus_x: FE_ZERO, 716 | y_minus_x: FE_ZERO, 717 | z: FE_ZERO, 718 | t2d: FE_ZERO, 719 | }; 8]; // A,3A,5A,7A,9A,11A,13A,15A 720 | ai[0] = a_point.to_cached(); 721 | let a2 = a_point.dbl().to_p3(); 722 | ai[1] = (a2 + ai[0]).to_p3().to_cached(); 723 | ai[2] = (a2 + ai[1]).to_p3().to_cached(); 724 | ai[3] = (a2 + ai[2]).to_p3().to_cached(); 725 | ai[4] = (a2 + ai[3]).to_p3().to_cached(); 726 | ai[5] = (a2 + ai[4]).to_p3().to_cached(); 727 | ai[6] = (a2 + ai[5]).to_p3().to_cached(); 728 | ai[7] = (a2 + ai[6]).to_p3().to_cached(); 729 | 730 | let mut r = GeP2::zero(); 731 | 732 | let mut i: usize = 255; 733 | loop { 734 | if aslide[i] != 0 || bslide[i] != 0 { 735 | break; 736 | } 737 | if i == 0 { 738 | return r; 739 | } 740 | i -= 1; 741 | } 742 | 743 | loop { 744 | let mut t = r.dbl(); 745 | if aslide[i] > 0 { 746 | t = t.to_p3() + ai[(aslide[i] / 2) as usize]; 747 | } else if aslide[i] < 0 { 748 | t = t.to_p3() - ai[(-aslide[i] / 2) as usize]; 749 | } 750 | 751 | if bslide[i] > 0 { 752 | t = t.to_p3() + BI[(bslide[i] / 2) as usize]; 753 | } else if bslide[i] < 0 { 754 | t = t.to_p3() - BI[(-bslide[i] / 2) as usize]; 755 | } 756 | 757 | r = t.to_p2(); 758 | 759 | if i == 0 { 760 | return r; 761 | } 762 | i -= 1; 763 | } 764 | } 765 | } 766 | 767 | impl GeP3 { 768 | pub fn from_bytes_negate_vartime(s: &[u8]) -> Option { 769 | let y = Fe::from_bytes(s); 770 | let z = FE_ONE; 771 | let y_squared = y.square(); 772 | let u = y_squared - FE_ONE; 773 | let v = (y_squared * FE_D) + FE_ONE; 774 | let mut x = (u * v).pow25523() * u; 775 | 776 | let vxx = x.square() * v; 777 | let check = vxx - u; 778 | if check.is_nonzero() { 779 | let check2 = vxx + u; 780 | if check2.is_nonzero() { 781 | return None; 782 | } 783 | x = x * FE_SQRTM1; 784 | } 785 | 786 | if x.is_negative() == ((s[31] >> 7) != 0) { 787 | x = x.neg(); 788 | } 789 | 790 | let t = x * y; 791 | 792 | Some(GeP3 { x, y, z, t }) 793 | } 794 | 795 | fn to_p2(&self) -> GeP2 { 796 | GeP2 { 797 | x: self.x, 798 | y: self.y, 799 | z: self.z, 800 | } 801 | } 802 | 803 | fn to_cached(&self) -> GeCached { 804 | GeCached { 805 | y_plus_x: self.y + self.x, 806 | y_minus_x: self.y - self.x, 807 | z: self.z, 808 | t2d: self.t * FE_D2, 809 | } 810 | } 811 | 812 | fn dbl(&self) -> GeP1P1 { 813 | self.to_p2().dbl() 814 | } 815 | } 816 | 817 | impl Add for GeP3 { 818 | type Output = GeP1P1; 819 | 820 | fn add(self, _rhs: GeCached) -> GeP1P1 { 821 | let y1_plus_x1 = self.y + self.x; 822 | let y1_minus_x1 = self.y - self.x; 823 | let a = y1_plus_x1 * _rhs.y_plus_x; 824 | let b = y1_minus_x1 * _rhs.y_minus_x; 825 | let c = _rhs.t2d * self.t; 826 | let zz = self.z * _rhs.z; 827 | let d = zz + zz; 828 | let x3 = a - b; 829 | let y3 = a + b; 830 | let z3 = d + c; 831 | let t3 = d - c; 832 | 833 | GeP1P1 { 834 | x: x3, 835 | y: y3, 836 | z: z3, 837 | t: t3, 838 | } 839 | } 840 | } 841 | 842 | impl Add for GeP3 { 843 | type Output = GeP1P1; 844 | 845 | fn add(self, _rhs: GePrecomp) -> GeP1P1 { 846 | let y1_plus_x1 = self.y + self.x; 847 | let y1_minus_x1 = self.y - self.x; 848 | let a = y1_plus_x1 * _rhs.y_plus_x; 849 | let b = y1_minus_x1 * _rhs.y_minus_x; 850 | let c = _rhs.xy2d * self.t; 851 | let d = self.z + self.z; 852 | let x3 = a - b; 853 | let y3 = a + b; 854 | let z3 = d + c; 855 | let t3 = d - c; 856 | 857 | GeP1P1 { 858 | x: x3, 859 | y: y3, 860 | z: z3, 861 | t: t3, 862 | } 863 | } 864 | } 865 | 866 | impl Sub for GeP3 { 867 | type Output = GeP1P1; 868 | 869 | fn sub(self, _rhs: GeCached) -> GeP1P1 { 870 | let y1_plus_x1 = self.y + self.x; 871 | let y1_minus_x1 = self.y - self.x; 872 | let a = y1_plus_x1 * _rhs.y_minus_x; 873 | let b = y1_minus_x1 * _rhs.y_plus_x; 874 | let c = _rhs.t2d * self.t; 875 | let zz = self.z * _rhs.z; 876 | let d = zz + zz; 877 | let x3 = a - b; 878 | let y3 = a + b; 879 | let z3 = d - c; 880 | let t3 = d + c; 881 | 882 | GeP1P1 { 883 | x: x3, 884 | y: y3, 885 | z: z3, 886 | t: t3, 887 | } 888 | } 889 | } 890 | 891 | impl Sub for GeP3 { 892 | type Output = GeP1P1; 893 | 894 | fn sub(self, _rhs: GePrecomp) -> GeP1P1 { 895 | let y1_plus_x1 = self.y + self.x; 896 | let y1_minus_x1 = self.y - self.x; 897 | let a = y1_plus_x1 * _rhs.y_minus_x; 898 | let b = y1_minus_x1 * _rhs.y_plus_x; 899 | let c = _rhs.xy2d * self.t; 900 | let d = self.z + self.z; 901 | let x3 = a - b; 902 | let y3 = a + b; 903 | let z3 = d - c; 904 | let t3 = d + c; 905 | 906 | GeP1P1 { 907 | x: x3, 908 | y: y3, 909 | z: z3, 910 | t: t3, 911 | } 912 | } 913 | } 914 | 915 | pub fn sc_reduce(s: &mut [u8]) { 916 | let mut s0: i64 = 2097151 & load_3i(s); 917 | let mut s1: i64 = 2097151 & (load_4i(&s[2..6]) >> 5); 918 | let mut s2: i64 = 2097151 & (load_3i(&s[5..8]) >> 2); 919 | let mut s3: i64 = 2097151 & (load_4i(&s[7..11]) >> 7); 920 | let mut s4: i64 = 2097151 & (load_4i(&s[10..14]) >> 4); 921 | let mut s5: i64 = 2097151 & (load_3i(&s[13..16]) >> 1); 922 | let mut s6: i64 = 2097151 & (load_4i(&s[15..19]) >> 6); 923 | let mut s7: i64 = 2097151 & (load_3i(&s[18..21]) >> 3); 924 | let mut s8: i64 = 2097151 & load_3i(&s[21..24]); 925 | let mut s9: i64 = 2097151 & (load_4i(&s[23..27]) >> 5); 926 | let mut s10: i64 = 2097151 & (load_3i(&s[26..29]) >> 2); 927 | let mut s11: i64 = 2097151 & (load_4i(&s[28..32]) >> 7); 928 | let mut s12: i64 = 2097151 & (load_4i(&s[31..35]) >> 4); 929 | let mut s13: i64 = 2097151 & (load_3i(&s[34..37]) >> 1); 930 | let mut s14: i64 = 2097151 & (load_4i(&s[36..40]) >> 6); 931 | let mut s15: i64 = 2097151 & (load_3i(&s[39..42]) >> 3); 932 | let mut s16: i64 = 2097151 & load_3i(&s[42..45]); 933 | let mut s17: i64 = 2097151 & (load_4i(&s[44..48]) >> 5); 934 | let s18: i64 = 2097151 & (load_3i(&s[47..50]) >> 2); 935 | let s19: i64 = 2097151 & (load_4i(&s[49..53]) >> 7); 936 | let s20: i64 = 2097151 & (load_4i(&s[52..56]) >> 4); 937 | let s21: i64 = 2097151 & (load_3i(&s[55..58]) >> 1); 938 | let s22: i64 = 2097151 & (load_4i(&s[57..61]) >> 6); 939 | let s23: i64 = load_4i(&s[60..64]) >> 3; 940 | 941 | s11 += s23 * 666643; 942 | s12 += s23 * 470296; 943 | s13 += s23 * 654183; 944 | s14 -= s23 * 997805; 945 | s15 += s23 * 136657; 946 | s16 -= s23 * 683901; 947 | 948 | s10 += s22 * 666643; 949 | s11 += s22 * 470296; 950 | s12 += s22 * 654183; 951 | s13 -= s22 * 997805; 952 | s14 += s22 * 136657; 953 | s15 -= s22 * 683901; 954 | 955 | s9 += s21 * 666643; 956 | s10 += s21 * 470296; 957 | s11 += s21 * 654183; 958 | s12 -= s21 * 997805; 959 | s13 += s21 * 136657; 960 | s14 -= s21 * 683901; 961 | 962 | s8 += s20 * 666643; 963 | s9 += s20 * 470296; 964 | s10 += s20 * 654183; 965 | s11 -= s20 * 997805; 966 | s12 += s20 * 136657; 967 | s13 -= s20 * 683901; 968 | 969 | s7 += s19 * 666643; 970 | s8 += s19 * 470296; 971 | s9 += s19 * 654183; 972 | s10 -= s19 * 997805; 973 | s11 += s19 * 136657; 974 | s12 -= s19 * 683901; 975 | 976 | s6 += s18 * 666643; 977 | s7 += s18 * 470296; 978 | s8 += s18 * 654183; 979 | s9 -= s18 * 997805; 980 | s10 += s18 * 136657; 981 | s11 -= s18 * 683901; 982 | 983 | let mut carry6: i64 = (s6 + (1 << 20)) >> 21; 984 | s7 += carry6; 985 | s6 -= carry6 << 21; 986 | let mut carry8: i64 = (s8 + (1 << 20)) >> 21; 987 | s9 += carry8; 988 | s8 -= carry8 << 21; 989 | let mut carry10: i64 = (s10 + (1 << 20)) >> 21; 990 | s11 += carry10; 991 | s10 -= carry10 << 21; 992 | let carry12: i64 = (s12 + (1 << 20)) >> 21; 993 | s13 += carry12; 994 | s12 -= carry12 << 21; 995 | let carry14: i64 = (s14 + (1 << 20)) >> 21; 996 | s15 += carry14; 997 | s14 -= carry14 << 21; 998 | let carry16: i64 = (s16 + (1 << 20)) >> 21; 999 | s17 += carry16; 1000 | s16 -= carry16 << 21; 1001 | 1002 | let mut carry7: i64 = (s7 + (1 << 20)) >> 21; 1003 | s8 += carry7; 1004 | s7 -= carry7 << 21; 1005 | let mut carry9: i64 = (s9 + (1 << 20)) >> 21; 1006 | s10 += carry9; 1007 | s9 -= carry9 << 21; 1008 | let mut carry11: i64 = (s11 + (1 << 20)) >> 21; 1009 | s12 += carry11; 1010 | s11 -= carry11 << 21; 1011 | let carry13: i64 = (s13 + (1 << 20)) >> 21; 1012 | s14 += carry13; 1013 | s13 -= carry13 << 21; 1014 | let carry15: i64 = (s15 + (1 << 20)) >> 21; 1015 | s16 += carry15; 1016 | s15 -= carry15 << 21; 1017 | 1018 | s5 += s17 * 666643; 1019 | s6 += s17 * 470296; 1020 | s7 += s17 * 654183; 1021 | s8 -= s17 * 997805; 1022 | s9 += s17 * 136657; 1023 | s10 -= s17 * 683901; 1024 | 1025 | s4 += s16 * 666643; 1026 | s5 += s16 * 470296; 1027 | s6 += s16 * 654183; 1028 | s7 -= s16 * 997805; 1029 | s8 += s16 * 136657; 1030 | s9 -= s16 * 683901; 1031 | 1032 | s3 += s15 * 666643; 1033 | s4 += s15 * 470296; 1034 | s5 += s15 * 654183; 1035 | s6 -= s15 * 997805; 1036 | s7 += s15 * 136657; 1037 | s8 -= s15 * 683901; 1038 | 1039 | s2 += s14 * 666643; 1040 | s3 += s14 * 470296; 1041 | s4 += s14 * 654183; 1042 | s5 -= s14 * 997805; 1043 | s6 += s14 * 136657; 1044 | s7 -= s14 * 683901; 1045 | 1046 | s1 += s13 * 666643; 1047 | s2 += s13 * 470296; 1048 | s3 += s13 * 654183; 1049 | s4 -= s13 * 997805; 1050 | s5 += s13 * 136657; 1051 | s6 -= s13 * 683901; 1052 | 1053 | s0 += s12 * 666643; 1054 | s1 += s12 * 470296; 1055 | s2 += s12 * 654183; 1056 | s3 -= s12 * 997805; 1057 | s4 += s12 * 136657; 1058 | s5 -= s12 * 683901; 1059 | s12 = 0; 1060 | 1061 | let mut carry0: i64 = (s0 + (1 << 20)) >> 21; 1062 | s1 += carry0; 1063 | s0 -= carry0 << 21; 1064 | let mut carry2: i64 = (s2 + (1 << 20)) >> 21; 1065 | s3 += carry2; 1066 | s2 -= carry2 << 21; 1067 | let mut carry4: i64 = (s4 + (1 << 20)) >> 21; 1068 | s5 += carry4; 1069 | s4 -= carry4 << 21; 1070 | carry6 = (s6 + (1 << 20)) >> 21; 1071 | s7 += carry6; 1072 | s6 -= carry6 << 21; 1073 | carry8 = (s8 + (1 << 20)) >> 21; 1074 | s9 += carry8; 1075 | s8 -= carry8 << 21; 1076 | carry10 = (s10 + (1 << 20)) >> 21; 1077 | s11 += carry10; 1078 | s10 -= carry10 << 21; 1079 | 1080 | let mut carry1: i64 = (s1 + (1 << 20)) >> 21; 1081 | s2 += carry1; 1082 | s1 -= carry1 << 21; 1083 | let mut carry3: i64 = (s3 + (1 << 20)) >> 21; 1084 | s4 += carry3; 1085 | s3 -= carry3 << 21; 1086 | let mut carry5: i64 = (s5 + (1 << 20)) >> 21; 1087 | s6 += carry5; 1088 | s5 -= carry5 << 21; 1089 | carry7 = (s7 + (1 << 20)) >> 21; 1090 | s8 += carry7; 1091 | s7 -= carry7 << 21; 1092 | carry9 = (s9 + (1 << 20)) >> 21; 1093 | s10 += carry9; 1094 | s9 -= carry9 << 21; 1095 | carry11 = (s11 + (1 << 20)) >> 21; 1096 | s12 += carry11; 1097 | s11 -= carry11 << 21; 1098 | 1099 | s0 += s12 * 666643; 1100 | s1 += s12 * 470296; 1101 | s2 += s12 * 654183; 1102 | s3 -= s12 * 997805; 1103 | s4 += s12 * 136657; 1104 | s5 -= s12 * 683901; 1105 | s12 = 0; 1106 | 1107 | carry0 = s0 >> 21; 1108 | s1 += carry0; 1109 | s0 -= carry0 << 21; 1110 | carry1 = s1 >> 21; 1111 | s2 += carry1; 1112 | s1 -= carry1 << 21; 1113 | carry2 = s2 >> 21; 1114 | s3 += carry2; 1115 | s2 -= carry2 << 21; 1116 | carry3 = s3 >> 21; 1117 | s4 += carry3; 1118 | s3 -= carry3 << 21; 1119 | carry4 = s4 >> 21; 1120 | s5 += carry4; 1121 | s4 -= carry4 << 21; 1122 | carry5 = s5 >> 21; 1123 | s6 += carry5; 1124 | s5 -= carry5 << 21; 1125 | carry6 = s6 >> 21; 1126 | s7 += carry6; 1127 | s6 -= carry6 << 21; 1128 | carry7 = s7 >> 21; 1129 | s8 += carry7; 1130 | s7 -= carry7 << 21; 1131 | carry8 = s8 >> 21; 1132 | s9 += carry8; 1133 | s8 -= carry8 << 21; 1134 | carry9 = s9 >> 21; 1135 | s10 += carry9; 1136 | s9 -= carry9 << 21; 1137 | carry10 = s10 >> 21; 1138 | s11 += carry10; 1139 | s10 -= carry10 << 21; 1140 | carry11 = s11 >> 21; 1141 | s12 += carry11; 1142 | s11 -= carry11 << 21; 1143 | 1144 | s0 += s12 * 666643; 1145 | s1 += s12 * 470296; 1146 | s2 += s12 * 654183; 1147 | s3 -= s12 * 997805; 1148 | s4 += s12 * 136657; 1149 | s5 -= s12 * 683901; 1150 | 1151 | carry0 = s0 >> 21; 1152 | s1 += carry0; 1153 | s0 -= carry0 << 21; 1154 | carry1 = s1 >> 21; 1155 | s2 += carry1; 1156 | s1 -= carry1 << 21; 1157 | carry2 = s2 >> 21; 1158 | s3 += carry2; 1159 | s2 -= carry2 << 21; 1160 | carry3 = s3 >> 21; 1161 | s4 += carry3; 1162 | s3 -= carry3 << 21; 1163 | carry4 = s4 >> 21; 1164 | s5 += carry4; 1165 | s4 -= carry4 << 21; 1166 | carry5 = s5 >> 21; 1167 | s6 += carry5; 1168 | s5 -= carry5 << 21; 1169 | carry6 = s6 >> 21; 1170 | s7 += carry6; 1171 | s6 -= carry6 << 21; 1172 | carry7 = s7 >> 21; 1173 | s8 += carry7; 1174 | s7 -= carry7 << 21; 1175 | carry8 = s8 >> 21; 1176 | s9 += carry8; 1177 | s8 -= carry8 << 21; 1178 | carry9 = s9 >> 21; 1179 | s10 += carry9; 1180 | s9 -= carry9 << 21; 1181 | carry10 = s10 >> 21; 1182 | s11 += carry10; 1183 | s10 -= carry10 << 21; 1184 | 1185 | s[0] = s0 as u8; 1186 | s[1] = (s0 >> 8) as u8; 1187 | s[2] = ((s0 >> 16) | (s1 << 5)) as u8; 1188 | s[3] = (s1 >> 3) as u8; 1189 | s[4] = (s1 >> 11) as u8; 1190 | s[5] = ((s1 >> 19) | (s2 << 2)) as u8; 1191 | s[6] = (s2 >> 6) as u8; 1192 | s[7] = ((s2 >> 14) | (s3 << 7)) as u8; 1193 | s[8] = (s3 >> 1) as u8; 1194 | s[9] = (s3 >> 9) as u8; 1195 | s[10] = ((s3 >> 17) | (s4 << 4)) as u8; 1196 | s[11] = (s4 >> 4) as u8; 1197 | s[12] = (s4 >> 12) as u8; 1198 | s[13] = ((s4 >> 20) | (s5 << 1)) as u8; 1199 | s[14] = (s5 >> 7) as u8; 1200 | s[15] = ((s5 >> 15) | (s6 << 6)) as u8; 1201 | s[16] = (s6 >> 2) as u8; 1202 | s[17] = (s6 >> 10) as u8; 1203 | s[18] = ((s6 >> 18) | (s7 << 3)) as u8; 1204 | s[19] = (s7 >> 5) as u8; 1205 | s[20] = (s7 >> 13) as u8; 1206 | s[21] = s8 as u8; 1207 | s[22] = (s8 >> 8) as u8; 1208 | s[23] = ((s8 >> 16) | (s9 << 5)) as u8; 1209 | s[24] = (s9 >> 3) as u8; 1210 | s[25] = (s9 >> 11) as u8; 1211 | s[26] = ((s9 >> 19) | (s10 << 2)) as u8; 1212 | s[27] = (s10 >> 6) as u8; 1213 | s[28] = ((s10 >> 14) | (s11 << 7)) as u8; 1214 | s[29] = (s11 >> 1) as u8; 1215 | s[30] = (s11 >> 9) as u8; 1216 | s[31] = (s11 >> 17) as u8; 1217 | } 1218 | 1219 | pub fn is_identity(s: &[u8]) -> bool { 1220 | let mut c = s[0] ^ 0x01; 1221 | for i in 1..31 { 1222 | c |= s[i]; 1223 | } 1224 | c |= s[31] & 0x7f; 1225 | c == 0 1226 | } 1227 | 1228 | static BI: [GePrecomp; 8] = [ 1229 | GePrecomp { 1230 | y_plus_x: Fe([ 1231 | 1288382639258501, 1232 | 245678601348599, 1233 | 269427782077623, 1234 | 1462984067271730, 1235 | 137412439391563, 1236 | ]), 1237 | y_minus_x: Fe([ 1238 | 62697248952638, 1239 | 204681361388450, 1240 | 631292143396476, 1241 | 338455783676468, 1242 | 1213667448819585, 1243 | ]), 1244 | xy2d: Fe([ 1245 | 301289933810280, 1246 | 1259582250014073, 1247 | 1422107436869536, 1248 | 796239922652654, 1249 | 1953934009299142, 1250 | ]), 1251 | }, 1252 | GePrecomp { 1253 | y_plus_x: Fe([ 1254 | 1601611775252272, 1255 | 1720807796594148, 1256 | 1132070835939856, 1257 | 1260455018889551, 1258 | 2147779492816911, 1259 | ]), 1260 | y_minus_x: Fe([ 1261 | 316559037616741, 1262 | 2177824224946892, 1263 | 1459442586438991, 1264 | 1461528397712656, 1265 | 751590696113597, 1266 | ]), 1267 | xy2d: Fe([ 1268 | 1850748884277385, 1269 | 1200145853858453, 1270 | 1068094770532492, 1271 | 672251375690438, 1272 | 1586055907191707, 1273 | ]), 1274 | }, 1275 | GePrecomp { 1276 | y_plus_x: Fe([ 1277 | 769950342298419, 1278 | 132954430919746, 1279 | 844085933195555, 1280 | 974092374476333, 1281 | 726076285546016, 1282 | ]), 1283 | y_minus_x: Fe([ 1284 | 425251763115706, 1285 | 608463272472562, 1286 | 442562545713235, 1287 | 837766094556764, 1288 | 374555092627893, 1289 | ]), 1290 | xy2d: Fe([ 1291 | 1086255230780037, 1292 | 274979815921559, 1293 | 1960002765731872, 1294 | 929474102396301, 1295 | 1190409889297339, 1296 | ]), 1297 | }, 1298 | GePrecomp { 1299 | y_plus_x: Fe([ 1300 | 665000864555967, 1301 | 2065379846933859, 1302 | 370231110385876, 1303 | 350988370788628, 1304 | 1233371373142985, 1305 | ]), 1306 | y_minus_x: Fe([ 1307 | 2019367628972465, 1308 | 676711900706637, 1309 | 110710997811333, 1310 | 1108646842542025, 1311 | 517791959672113, 1312 | ]), 1313 | xy2d: Fe([ 1314 | 965130719900578, 1315 | 247011430587952, 1316 | 526356006571389, 1317 | 91986625355052, 1318 | 2157223321444601, 1319 | ]), 1320 | }, 1321 | GePrecomp { 1322 | y_plus_x: Fe([ 1323 | 1802695059465007, 1324 | 1664899123557221, 1325 | 593559490740857, 1326 | 2160434469266659, 1327 | 927570450755031, 1328 | ]), 1329 | y_minus_x: Fe([ 1330 | 1725674970513508, 1331 | 1933645953859181, 1332 | 1542344539275782, 1333 | 1767788773573747, 1334 | 1297447965928905, 1335 | ]), 1336 | xy2d: Fe([ 1337 | 1381809363726107, 1338 | 1430341051343062, 1339 | 2061843536018959, 1340 | 1551778050872521, 1341 | 2036394857967624, 1342 | ]), 1343 | }, 1344 | GePrecomp { 1345 | y_plus_x: Fe([ 1346 | 1970894096313054, 1347 | 528066325833207, 1348 | 1619374932191227, 1349 | 2207306624415883, 1350 | 1169170329061080, 1351 | ]), 1352 | y_minus_x: Fe([ 1353 | 2070390218572616, 1354 | 1458919061857835, 1355 | 624171843017421, 1356 | 1055332792707765, 1357 | 433987520732508, 1358 | ]), 1359 | xy2d: Fe([ 1360 | 893653801273833, 1361 | 1168026499324677, 1362 | 1242553501121234, 1363 | 1306366254304474, 1364 | 1086752658510815, 1365 | ]), 1366 | }, 1367 | GePrecomp { 1368 | y_plus_x: Fe([ 1369 | 213454002618221, 1370 | 939771523987438, 1371 | 1159882208056014, 1372 | 317388369627517, 1373 | 621213314200687, 1374 | ]), 1375 | y_minus_x: Fe([ 1376 | 1971678598905747, 1377 | 338026507889165, 1378 | 762398079972271, 1379 | 655096486107477, 1380 | 42299032696322, 1381 | ]), 1382 | xy2d: Fe([ 1383 | 177130678690680, 1384 | 1754759263300204, 1385 | 1864311296286618, 1386 | 1180675631479880, 1387 | 1292726903152791, 1388 | ]), 1389 | }, 1390 | GePrecomp { 1391 | y_plus_x: Fe([ 1392 | 1913163449625248, 1393 | 460779200291993, 1394 | 2193883288642314, 1395 | 1008900146920800, 1396 | 1721983679009502, 1397 | ]), 1398 | y_minus_x: Fe([ 1399 | 1070401523076875, 1400 | 1272492007800961, 1401 | 1910153608563310, 1402 | 2075579521696771, 1403 | 1191169788841221, 1404 | ]), 1405 | xy2d: Fe([ 1406 | 692896803108118, 1407 | 500174642072499, 1408 | 2068223309439677, 1409 | 1162190621851337, 1410 | 1426986007309901, 1411 | ]), 1412 | }, 1413 | ]; 1414 | -------------------------------------------------------------------------------- /src/crypto/ed25519.rs: -------------------------------------------------------------------------------- 1 | use super::curve25519::{is_identity, 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 | } else { 20 | i -= 1; 21 | } 22 | } 23 | 24 | c == 0 25 | } 26 | 27 | pub fn verify(message: &[u8], public_key: &[u8], signature: &[u8]) -> bool { 28 | if check_s_lt_l(&signature[32..64]) || is_identity(public_key) { 29 | return false; 30 | } 31 | 32 | let a = match GeP3::from_bytes_negate_vartime(public_key) { 33 | Some(g) => g, 34 | None => { 35 | return false; 36 | } 37 | }; 38 | if public_key.iter().fold(0, |acc, x| acc | x) == 0 { 39 | return false; 40 | } 41 | 42 | let mut hasher = sha512::Hash::new(); 43 | hasher.update(&signature[0..32]); 44 | hasher.update(public_key); 45 | hasher.update(message); 46 | let mut hash = hasher.finalize(); 47 | sc_reduce(&mut hash); 48 | 49 | let r = GeP2::double_scalarmult_vartime(hash.as_ref(), a, &signature[32..64]); 50 | r.to_bytes() 51 | .as_ref() 52 | .iter() 53 | .zip(signature.iter()) 54 | .fold(0, |acc, (x, y)| acc | (x ^ y)) 55 | == 0 56 | } 57 | -------------------------------------------------------------------------------- /src/crypto/mod.rs: -------------------------------------------------------------------------------- 1 | mod cryptoutil; 2 | mod curve25519; 3 | mod sha512; 4 | 5 | pub mod blake2b; 6 | pub mod ed25519; 7 | -------------------------------------------------------------------------------- /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 | 355 | impl Default for Hash { 356 | fn default() -> Self { 357 | Self::new() 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A small crate to verify [Minisign](https://jedisct1.github.io/minisign/) signatures. 2 | //! 3 | //! This library provides zero-dependency verification of Minisign signatures. Minisign is a 4 | //! dead simple tool to sign files and verify signatures, developed by Frank Denis 5 | //! (author of libsodium). 6 | //! 7 | //! ## Features 8 | //! 9 | //! * Verify signatures for both standard and pre-hashed modes 10 | //! * Streaming verification for large files 11 | //! * No external dependencies 12 | //! * Simple, auditable code 13 | //! 14 | //! ## Basic Usage 15 | //! 16 | //! ```rust 17 | //! use minisign_verify::{PublicKey, Signature}; 18 | //! 19 | //! // Create a public key from a base64 string 20 | //! let public_key = 21 | //! PublicKey::from_base64("RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3") 22 | //! .expect("Unable to decode the public key"); 23 | //! 24 | //! // Create a signature from a string 25 | //! let signature = Signature::decode( 26 | //! "untrusted comment: signature from minisign secret key 27 | //! RUQf6LRCGA9i559r3g7V1qNyJDApGip8MfqcadIgT9CuhV3EMhHoN1mGTkUidF/\ 28 | //! z7SrlQgXdy8ofjb7bNJJylDOocrCo8KLzZwo= 29 | //! trusted comment: timestamp:1633700835\tfile:test\tprehashed 30 | //! wLMDjy9FLAuxZ3q4NlEvkgtyhrr0gtTu6KC4KBJdITbbOeAi1zBIYo0v4iTgt8jJpIidRJnp94ABQkJAgAooBQ==", 31 | //! ) 32 | //! .expect("Unable to decode the signature"); 33 | //! 34 | //! // Verify the signature 35 | //! let bin = b"test"; 36 | //! public_key 37 | //! .verify(&bin[..], &signature, false) 38 | //! .expect("Signature didn't verify"); 39 | //! ``` 40 | //! 41 | //! ## Loading from Files 42 | //! 43 | //! ```rust,no_run 44 | //! use minisign_verify::{PublicKey, Signature}; 45 | //! use std::path::Path; 46 | //! 47 | //! // Load a public key from a file 48 | //! let public_key = PublicKey::from_file(Path::new("minisign.pub")) 49 | //! .expect("Unable to load the public key"); 50 | //! 51 | //! // Load a signature from a file 52 | //! let signature = Signature::from_file(Path::new("file.sig")) 53 | //! .expect("Unable to load the signature"); 54 | //! 55 | //! // Load the file content to verify 56 | //! let content = std::fs::read("file").expect("Unable to read the file"); 57 | //! 58 | //! // Verify the signature 59 | //! public_key 60 | //! .verify(&content, &signature, false) 61 | //! .expect("Signature didn't verify"); 62 | //! ``` 63 | //! 64 | //! ## Streaming Verification 65 | //! 66 | //! For large files, you can use streaming verification to avoid loading 67 | //! the entire file into memory at once: 68 | //! 69 | //! ```rust,no_run 70 | //! use minisign_verify::{PublicKey, Signature}; 71 | //! use std::fs::File; 72 | //! use std::io::{self, Read}; 73 | //! use std::path::Path; 74 | //! 75 | //! // Load a public key and signature 76 | //! let public_key = PublicKey::from_file(Path::new("minisign.pub")) 77 | //! .expect("Unable to load the public key"); 78 | //! 79 | //! let signature = Signature::from_file(Path::new("large_file.sig")) 80 | //! .expect("Unable to load the signature"); 81 | //! 82 | //! // Create a stream verifier 83 | //! let mut verifier = public_key.verify_stream(&signature) 84 | //! .expect("Unable to create stream verifier"); 85 | //! 86 | //! // Process the file in chunks 87 | //! let mut file = File::open("large_file").expect("Unable to open file"); 88 | //! let mut buffer = [0u8; 8192]; // 8KB buffer 89 | //! 90 | //! loop { 91 | //! let bytes_read = file.read(&mut buffer).expect("Error reading file"); 92 | //! if bytes_read == 0 { 93 | //! break; // End of file 94 | //! } 95 | //! 96 | //! verifier.update(&buffer[..bytes_read]); 97 | //! } 98 | //! 99 | //! // Verify the signature 100 | //! verifier.finalize().expect("Signature verification failed"); 101 | //! ``` 102 | //! 103 | //! Note that the streaming verification mode only works with pre-hashed signatures 104 | //! (the default in newer versions of Minisign). 105 | 106 | mod base64; 107 | mod crypto; 108 | 109 | use std::path::Path; 110 | use std::{fmt, fs, io}; 111 | 112 | use base64::{Base64, Decoder}; 113 | 114 | use crate::crypto::blake2b::{Blake2b, BLAKE2B_OUTBYTES}; 115 | use crate::crypto::ed25519; 116 | #[derive(Debug)] 117 | pub enum Error { 118 | /// The provided string couldn't be decoded properly 119 | InvalidEncoding, 120 | /// The signature verification failed 121 | InvalidSignature, 122 | /// An I/O error occurred 123 | IoError(io::Error), 124 | /// The algorithm doesn't match what was expected 125 | UnexpectedAlgorithm, 126 | /// The key ID from the signature doesn't match the public key 127 | UnexpectedKeyId, 128 | /// The specified algorithm is not supported by this implementation 129 | UnsupportedAlgorithm, 130 | /// Legacy mode is not supported in streaming verification 131 | UnsupportedLegacyMode, 132 | } 133 | 134 | impl fmt::Display for Error { 135 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 136 | match self { 137 | Error::InvalidEncoding => write!(f, "Invalid encoding in minisign data"), 138 | Error::InvalidSignature => write!(f, "The signature verification failed"), 139 | Error::IoError(e) => write!(f, "I/O error: {}", e), 140 | Error::UnexpectedAlgorithm => write!(f, "Unexpected signature algorithm"), 141 | Error::UnexpectedKeyId => write!( 142 | f, 143 | "The signature was created with a different key than the one provided" 144 | ), 145 | Error::UnsupportedAlgorithm => write!( 146 | f, 147 | "This signature algorithm is not supported by this implementation" 148 | ), 149 | Error::UnsupportedLegacyMode => { 150 | write!(f, "StreamVerifier only supports non-legacy mode signatures") 151 | } 152 | } 153 | } 154 | } 155 | 156 | impl std::error::Error for Error { 157 | // Note: description() is deprecated in favor of Display implementation 158 | 159 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 160 | match self { 161 | Error::IoError(e) => Some(e), 162 | _ => None, 163 | } 164 | } 165 | } 166 | 167 | impl From for Error { 168 | fn from(_e: base64::Error) -> Error { 169 | // We could consider adding a dedicated Base64Error variant that includes the original error 170 | // in the future, but for now we'll just use InvalidEncoding 171 | Error::InvalidEncoding 172 | } 173 | } 174 | 175 | impl From for Error { 176 | fn from(e: io::Error) -> Error { 177 | Error::IoError(e) 178 | } 179 | } 180 | 181 | /// A Minisign public key 182 | /// 183 | /// This struct represents a Minisign public key, which can be used to verify 184 | /// signatures. A public key can be created from a base64 string, read from a file, 185 | /// or parsed from a string in minisign.pub format. 186 | /// 187 | /// The public key contains an Ed25519 key, a key ID for signature matching, 188 | /// and optionally an untrusted comment. 189 | #[derive(Clone, Debug, Eq, PartialEq)] 190 | pub struct PublicKey { 191 | untrusted_comment: Option, 192 | signature_algorithm: [u8; 2], 193 | key_id: [u8; 8], 194 | key: [u8; 32], 195 | } 196 | 197 | /// A StreamVerifier to verify a signature against a data stream 198 | /// 199 | /// This mode of operation allows for verification of large files by processing them 200 | /// in chunks, without having to load the entire file into memory. 201 | /// 202 | /// Note that this mode only works with pre-hashed signatures (not legacy mode). 203 | #[derive(Clone)] 204 | pub struct StreamVerifier<'a> { 205 | public_key: &'a PublicKey, 206 | signature: &'a Signature, 207 | hasher: Blake2b, 208 | } 209 | 210 | /// A Minisign signature 211 | /// 212 | /// This struct represents a Minisign signature, which contains: 213 | /// - An untrusted comment (usually identifies the key that created the signature) 214 | /// - The signature itself (Ed25519 signature of the message or its hash) 215 | /// - A trusted comment (usually contains timestamp and filename) 216 | /// - A global signature (Ed25519 signature of the signature and trusted comment) 217 | /// - A flag indicating if the signature was created in pre-hashed mode 218 | /// 219 | /// Pre-hashed mode is the default in newer versions of Minisign. 220 | #[derive(Clone)] 221 | pub struct Signature { 222 | untrusted_comment: String, 223 | key_id: [u8; 8], 224 | signature: [u8; 64], 225 | trusted_comment: String, 226 | global_signature: [u8; 64], 227 | is_prehashed: bool, 228 | } 229 | 230 | impl Signature { 231 | /// Create a Minisign signature from a string 232 | pub fn decode(lines_str: &str) -> Result { 233 | let mut lines = lines_str.lines(); 234 | let untrusted_comment = lines.next().ok_or(Error::InvalidEncoding)?.to_string(); 235 | let bin1 = Base64::decode_to_vec(lines.next().ok_or(Error::InvalidEncoding)?)?; 236 | if bin1.len() != 74 { 237 | return Err(Error::InvalidEncoding); 238 | } 239 | let trusted_comment = lines.next().ok_or(Error::InvalidEncoding)?.to_string(); 240 | let bin2 = Base64::decode_to_vec(lines.next().ok_or(Error::InvalidEncoding)?)?; 241 | if bin2.len() != 64 { 242 | return Err(Error::InvalidEncoding); 243 | } 244 | if !trusted_comment.starts_with("trusted comment: ") { 245 | return Err(Error::InvalidEncoding); 246 | } 247 | let mut signature_algorithm = [0u8; 2]; 248 | signature_algorithm.copy_from_slice(&bin1[0..2]); 249 | let mut key_id = [0u8; 8]; 250 | key_id.copy_from_slice(&bin1[2..10]); 251 | let mut signature = [0u8; 64]; 252 | signature.copy_from_slice(&bin1[10..74]); 253 | let mut global_signature = [0u8; 64]; 254 | global_signature.copy_from_slice(&bin2); 255 | let is_prehashed = match (signature_algorithm[0], signature_algorithm[1]) { 256 | (0x45, 0x64) => false, 257 | (0x45, 0x44) => true, 258 | _ => return Err(Error::UnsupportedAlgorithm), 259 | }; 260 | Ok(Signature { 261 | untrusted_comment, 262 | key_id, 263 | signature, 264 | trusted_comment, 265 | global_signature, 266 | is_prehashed, 267 | }) 268 | } 269 | 270 | /// Load a Minisign signature from a `.sig` file 271 | pub fn from_file>(path: P) -> Result { 272 | let bin = fs::read_to_string(path)?; 273 | Signature::decode(&bin) 274 | } 275 | 276 | /// Return the trusted comment of the signature 277 | pub fn trusted_comment(&self) -> &str { 278 | &self.trusted_comment[17..] 279 | } 280 | 281 | /// Return the untrusted comment of the signature 282 | pub fn untrusted_comment(&self) -> &str { 283 | &self.untrusted_comment 284 | } 285 | } 286 | 287 | impl PublicKey { 288 | /// Create a Minisign public key from a base64 string 289 | pub fn from_base64(public_key_b64: &str) -> Result { 290 | let bin = Base64::decode_to_vec(public_key_b64)?; 291 | if bin.len() != 42 { 292 | return Err(Error::InvalidEncoding); 293 | } 294 | let mut signature_algorithm = [0u8; 2]; 295 | signature_algorithm.copy_from_slice(&bin[0..2]); 296 | match (signature_algorithm[0], signature_algorithm[1]) { 297 | (0x45, 0x64) | (0x45, 0x44) => {} 298 | _ => return Err(Error::UnsupportedAlgorithm), 299 | }; 300 | let mut key_id = [0u8; 8]; 301 | key_id.copy_from_slice(&bin[2..10]); 302 | let mut key = [0u8; 32]; 303 | key.copy_from_slice(&bin[10..42]); 304 | Ok(PublicKey { 305 | untrusted_comment: None, 306 | signature_algorithm, 307 | key_id, 308 | key, 309 | }) 310 | } 311 | 312 | /// Create a Minisign public key from a string, as in the `minisign.pub` 313 | /// file 314 | pub fn decode(lines_str: &str) -> Result { 315 | let mut lines = lines_str.lines(); 316 | let untrusted_comment = lines.next().ok_or(Error::InvalidEncoding)?; 317 | let public_key_b64 = lines.next().ok_or(Error::InvalidEncoding)?; 318 | let mut public_key = PublicKey::from_base64(public_key_b64)?; 319 | public_key.untrusted_comment = Some(untrusted_comment.to_string()); 320 | Ok(public_key) 321 | } 322 | 323 | /// Load a Minisign key from a file (such as the `minisign.pub` file) 324 | pub fn from_file>(path: P) -> Result { 325 | let bin = fs::read_to_string(path)?; 326 | PublicKey::decode(&bin) 327 | } 328 | 329 | /// Return the untrusted comment, if there is one 330 | pub fn untrusted_comment(&self) -> Option<&str> { 331 | self.untrusted_comment.as_deref() 332 | } 333 | 334 | fn verify_ed25519(&self, bin: &[u8], signature: &Signature) -> Result<(), Error> { 335 | if !ed25519::verify(bin, &self.key, &signature.signature) { 336 | return Err(Error::InvalidSignature); 337 | } 338 | let trusted_comment_bin = signature.trusted_comment().as_bytes(); 339 | let mut global = Vec::with_capacity(signature.signature.len() + trusted_comment_bin.len()); 340 | global.extend_from_slice(&signature.signature[..]); 341 | global.extend_from_slice(trusted_comment_bin); 342 | if !ed25519::verify(&global, &self.key, &signature.global_signature) { 343 | return Err(Error::InvalidSignature); 344 | } 345 | Ok(()) 346 | } 347 | 348 | /// Verify that `signature` is a valid signature for `bin` using this public 349 | /// key `allow_legacy` should only be set to `true` in order to support 350 | /// signatures made by older versions of Minisign. 351 | pub fn verify( 352 | &self, 353 | bin: &[u8], 354 | signature: &Signature, 355 | allow_legacy: bool, 356 | ) -> Result<(), Error> { 357 | if self.key_id != signature.key_id { 358 | return Err(Error::UnexpectedKeyId); 359 | } 360 | let mut h; 361 | let bin = if signature.is_prehashed { 362 | h = vec![0u8; BLAKE2B_OUTBYTES]; 363 | Blake2b::blake2b(&mut h, bin); 364 | &h 365 | } else if !allow_legacy { 366 | return Err(Error::UnexpectedAlgorithm); 367 | } else { 368 | bin 369 | }; 370 | self.verify_ed25519(bin, signature) 371 | } 372 | 373 | /// Sets up a stream verifier that can be use iteratively. 374 | pub fn verify_stream<'a>( 375 | &'a self, 376 | signature: &'a Signature, 377 | ) -> Result, Error> { 378 | if self.key_id != signature.key_id { 379 | return Err(Error::UnexpectedKeyId); 380 | } 381 | if !signature.is_prehashed { 382 | return Err(Error::UnsupportedLegacyMode); 383 | } 384 | let hasher = Blake2b::new(BLAKE2B_OUTBYTES); 385 | Ok(StreamVerifier { 386 | public_key: self, 387 | signature, 388 | hasher, 389 | }) 390 | } 391 | } 392 | 393 | impl StreamVerifier<'_> { 394 | /// Update the verifier with a chunk of data 395 | /// 396 | /// This method can be called multiple times with different chunks of the file 397 | /// to be verified. The chunks will be hashed incrementally. 398 | pub fn update(&mut self, buf: &[u8]) { 399 | self.hasher.update(buf); 400 | } 401 | 402 | /// Finalize the verification process 403 | /// 404 | /// This method must be called after all data has been processed with `update()`. 405 | /// It computes the final hash and verifies the signature. 406 | /// 407 | /// Returns `Ok(())` if the signature is valid, or an error otherwise. 408 | pub fn finalize(&mut self) -> Result<(), Error> { 409 | let mut bin = vec![0u8; BLAKE2B_OUTBYTES]; 410 | self.hasher.finalize(&mut bin); 411 | self.public_key.verify_ed25519(&bin, self.signature) 412 | } 413 | } 414 | 415 | #[cfg(test)] 416 | mod tests { 417 | use super::*; 418 | #[test] 419 | fn verify() { 420 | let public_key = 421 | PublicKey::from_base64("RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3") 422 | .expect("Unable to decode the public key"); 423 | assert_eq!(public_key.untrusted_comment(), None); 424 | let signature = Signature::decode( 425 | "untrusted comment: signature from minisign secret key 426 | RWQf6LRCGA9i59SLOFxz6NxvASXDJeRtuZykwQepbDEGt87ig1BNpWaVWuNrm73YiIiJbq71Wi+dP9eKL8OC351vwIasSSbXxwA= 427 | trusted comment: timestamp:1555779966\tfile:test 428 | QtKMXWyYcwdpZAlPF7tE2ENJkRd1ujvKjlj1m9RtHTBnZPa5WKU5uWRs5GoP5M/VqE81QFuMKI5k/SfNQUaOAA==", 429 | ) 430 | .expect("Unable to decode the signature"); 431 | assert_eq!( 432 | signature.untrusted_comment(), 433 | "untrusted comment: signature from minisign secret key" 434 | ); 435 | assert_eq!( 436 | signature.trusted_comment(), 437 | "timestamp:1555779966\tfile:test" 438 | ); 439 | let bin = b"test"; 440 | public_key 441 | .verify(&bin[..], &signature, true) 442 | .expect("Signature didn't verify"); 443 | let bin = b"Test"; 444 | match public_key.verify(&bin[..], &signature, true) { 445 | Err(Error::InvalidSignature) => {} 446 | _ => panic!("Invalid signature verified"), 447 | }; 448 | 449 | let public_key2 = PublicKey::decode( 450 | "untrusted comment: minisign public key E7620F1842B4E81F 451 | RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3", 452 | ) 453 | .expect("Unable to decode the public key"); 454 | assert_eq!( 455 | public_key2.untrusted_comment(), 456 | Some("untrusted comment: minisign public key E7620F1842B4E81F") 457 | ); 458 | match public_key2.verify(&bin[..], &signature, true) { 459 | Err(Error::InvalidSignature) => {} 460 | _ => panic!("Invalid signature verified"), 461 | }; 462 | } 463 | 464 | #[test] 465 | fn verify_prehashed() { 466 | let public_key = 467 | PublicKey::from_base64("RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3") 468 | .expect("Unable to decode the public key"); 469 | assert_eq!(public_key.untrusted_comment(), None); 470 | let signature = Signature::decode( 471 | "untrusted comment: signature from minisign secret key 472 | RUQf6LRCGA9i559r3g7V1qNyJDApGip8MfqcadIgT9CuhV3EMhHoN1mGTkUidF/\ 473 | z7SrlQgXdy8ofjb7bNJJylDOocrCo8KLzZwo= 474 | trusted comment: timestamp:1556193335\tfile:test 475 | y/rUw2y8/hOUYjZU71eHp/Wo1KZ40fGy2VJEDl34XMJM+TX48Ss/17u3IvIfbVR1FkZZSNCisQbuQY+bHwhEBg==", 476 | ) 477 | .expect("Unable to decode the signature"); 478 | assert_eq!( 479 | signature.untrusted_comment(), 480 | "untrusted comment: signature from minisign secret key" 481 | ); 482 | assert_eq!( 483 | signature.trusted_comment(), 484 | "timestamp:1556193335\tfile:test" 485 | ); 486 | let bin = b"test"; 487 | public_key 488 | .verify(&bin[..], &signature, false) 489 | .expect("Signature didn't verify"); 490 | } 491 | 492 | #[test] 493 | fn verify_stream() { 494 | let public_key = 495 | PublicKey::from_base64("RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3") 496 | .expect("Unable to decode the public key"); 497 | assert_eq!(public_key.untrusted_comment(), None); 498 | let signature = Signature::decode( 499 | "untrusted comment: signature from minisign secret key 500 | RUQf6LRCGA9i559r3g7V1qNyJDApGip8MfqcadIgT9CuhV3EMhHoN1mGTkUidF/\ 501 | z7SrlQgXdy8ofjb7bNJJylDOocrCo8KLzZwo= 502 | trusted comment: timestamp:1556193335\tfile:test 503 | y/rUw2y8/hOUYjZU71eHp/Wo1KZ40fGy2VJEDl34XMJM+TX48Ss/17u3IvIfbVR1FkZZSNCisQbuQY+bHwhEBg==", 504 | ) 505 | .expect("Unable to decode the signature"); 506 | assert_eq!( 507 | signature.untrusted_comment(), 508 | "untrusted comment: signature from minisign secret key" 509 | ); 510 | assert_eq!( 511 | signature.trusted_comment(), 512 | "timestamp:1556193335\tfile:test" 513 | ); 514 | let mut stream_verifier = public_key 515 | .verify_stream(&signature) 516 | .expect("Can't extract StreamerVerifier"); 517 | 518 | let bin: &[u8] = b"te"; 519 | stream_verifier.update(bin); 520 | 521 | let bin: &[u8] = b"st"; 522 | stream_verifier.update(bin); 523 | 524 | stream_verifier.finalize().expect("Signature didn't verify"); 525 | } 526 | } 527 | --------------------------------------------------------------------------------