├── .gitignore ├── .github └── workflows │ └── build_test.yml ├── LICENSE ├── Cargo.toml ├── README.md ├── examples ├── dalek.rs └── chacha20.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ 3 | -------------------------------------------------------------------------------- /.github/workflows/build_test.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test 2 | 3 | on: [push, pull_request, release] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Build 13 | run: | 14 | cargo build --verbose 15 | cargo build --features=subtle --verbose 16 | - name: Run tests 17 | run: | 18 | cargo test --verbose 19 | cargo test --features=subtle --verbose 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Denis Merigoux 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "secret_integers" 3 | version = "0.1.6" 4 | authors = ["Denis Merigoux "] 5 | edition = "2018" 6 | description = "Wrapping around Rust's integers to allow only constant-time operations" 7 | license = "Apache-2.0" 8 | repository = "https://github.com/denismerigoux/rust-secret-integers" 9 | keywords = ["secret", "constant-time"] 10 | readme = "README.md" 11 | 12 | [dependencies] 13 | subtle = { version = "2.2.2", optional = true } 14 | 15 | [[example]] 16 | name = "dalek" 17 | crate-type = ["staticlib"] 18 | 19 | [[example]] 20 | name = "chacha20" 21 | crate-type = ["staticlib"] 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust secret integers 2 | 3 | This simple crate provides integer wrappers that guarantee that they are being used in a constant-time fashion. Hence, division and direct comparison are disallowed. Using Rust's type system, this crate will help the compiler check systematically whether your cryptographic code is constant-time relative to secret inputs. 4 | 5 | To use the crate, just import everything (`use secret_integers::*;`) and replace your integer types with uppercase versions of their names (e.g. `u8` -> `U8`). 6 | 7 | ## Examples 8 | 9 | Two examples show how to use the crate : [Dalek](https://github.com/denismerigoux/rust-secret-integers/tree/master/examples/dalek.rs) 10 | and [Chacha20](https://github.com/denismerigoux/rust-secret-integers/tree/master/examples/chacha20.rs). 11 | To build theses examples, use 12 | 13 | cargo build --example dalek 14 | cargo build --example chacha20 15 | 16 | However, if you try: 17 | 18 | cargo build --example biguint 19 | 20 | You will get the following error message: 21 | 22 | ``` 23 | error[E0599]: no method named `leading_zeros` found for type `&secret_integers::U32` in the current scope 24 | --> examples/biguint.rs:24:46 25 | | 26 | 24 | let zeros = self.data.last().unwrap().leading_zeros(); 27 | | ^^^^^^^^^^^^^ 28 | 29 | error[E0369]: binary operation `!=` cannot be applied to type `secret_integers::U32` 30 | --> examples/biguint.rs:48:11 31 | | 32 | 48 | while r != 0 { 33 | | ^^^^^^ 34 | | 35 | = note: an implementation of `std::cmp::PartialEq` might be missing for `secret_integers::U32` 36 | ``` 37 | -------------------------------------------------------------------------------- /examples/dalek.rs: -------------------------------------------------------------------------------- 1 | /// This code is inspired from Dalek's field multiplication for 64-bits backends contained in the 2 | /// file [`src/backend/u64/field.rs`](https://github.com/dalek-cryptography/curve25519-dalek/blob/master/src/backend/u64/field.rs) 3 | 4 | use secret_integers::*; 5 | use core::ops::Mul; 6 | 7 | /// A `FieldElement64` represents an element of the field 8 | /// \\( \mathbb Z / (2\^{255} - 19)\\). 9 | /// 10 | /// In the 64-bit implementation, a `FieldElement` is represented in 11 | /// radix \\(2\^{51}\\) as five `u64`s; the coefficients are allowed to 12 | /// grow up to \\(2\^{54}\\) between reductions modulo \\(p\\). 13 | /// 14 | /// # Note 15 | /// 16 | /// The `curve25519_dalek::field` module provides a type alias 17 | /// `curve25519_dalek::field::FieldElement` to either `FieldElement64` 18 | /// or `FieldElement32`. 19 | /// 20 | /// The backend-specific type `FieldElement64` should not be used 21 | /// outside of the `curve25519_dalek::field` module. 22 | type Limb = U64; 23 | 24 | #[derive(Copy, Clone)] 25 | pub struct FieldElement64(pub (crate) [Limb; 5]); 26 | 27 | impl<'a, 'b> Mul<&'b FieldElement64> for &'a FieldElement64 { 28 | type Output = FieldElement64; 29 | fn mul(self, _rhs: &'b FieldElement64) -> FieldElement64 { 30 | /// Helper function to multiply two 64-bit integers with 128 31 | /// bits of output. 32 | #[inline(always)] 33 | fn m(x: U64, y: U64) -> U128 { U128::from(x) * y.into() } 34 | 35 | // Alias self, _rhs for more readable formulas 36 | let a: &[Limb; 5] = &self.0; 37 | let b: &[Limb; 5] = &_rhs.0; 38 | 39 | // Precondition: assume input limbs a[i], b[i] are bounded as 40 | // 41 | // a[i], b[i] < 2^(51 + b) 42 | // 43 | // where b is a real parameter measuring the "bit excess" of the limbs. 44 | 45 | // 64-bit precomputations to avoid 128-bit multiplications. 46 | // 47 | // This fits into a u64 whenever 51 + b + lg(19) < 64. 48 | // 49 | // Since 51 + b + lg(19) < 51 + 4.25 + b 50 | // = 55.25 + b, 51 | // this fits if b < 8.75. 52 | let nineteen = 19u64.into(); 53 | let b1_19 = b[1] * nineteen; 54 | let b2_19 = b[2] * nineteen; 55 | let b3_19 = b[3] * nineteen; 56 | let b4_19 = b[4] * nineteen; 57 | 58 | // Multiply to get 128-bit coefficients of output 59 | let c0: U128 = m(a[0],b[0]) + m(a[4],b1_19) + m(a[3],b2_19) + m(a[2],b3_19) + m(a[1],b4_19); 60 | let mut c1: U128 = m(a[1],b[0]) + m(a[0],b[1]) + m(a[4],b2_19) + m(a[3],b3_19) + m(a[2],b4_19); 61 | let mut c2: U128 = m(a[2],b[0]) + m(a[1],b[1]) + m(a[0],b[2]) + m(a[4],b3_19) + m(a[3],b4_19); 62 | let mut c3: U128 = m(a[3],b[0]) + m(a[2],b[1]) + m(a[1],b[2]) + m(a[0],b[3]) + m(a[4],b4_19); 63 | let mut c4: U128 = m(a[4],b[0]) + m(a[3],b[1]) + m(a[2],b[2]) + m(a[1],b[3]) + m(a[0],b[4]); 64 | 65 | // How big are the c[i]? We have 66 | // 67 | // c[i] < 2^(102 + 2*b) * (1+i + (4-i)*19) 68 | // < 2^(102 + lg(1 + 4*19) + 2*b) 69 | // < 2^(108.27 + 2*b) 70 | // 71 | // The carry (c[i] >> 51) fits into a u64 when 72 | // 108.27 + 2*b - 51 < 64 73 | // 2*b < 6.73 74 | // b < 3.365. 75 | // 76 | // So we require b < 3 to ensure this fits. 77 | debug_assert!(U64::declassify(a[0]) < (1 << 54)); 78 | debug_assert!(U64::declassify(b[0]) < (1 << 54)); 79 | debug_assert!(U64::declassify(a[1]) < (1 << 54)); 80 | debug_assert!(U64::declassify(b[1]) < (1 << 54)); 81 | debug_assert!(U64::declassify(a[2]) < (1 << 54)); 82 | debug_assert!(U64::declassify(b[2]) < (1 << 54)); 83 | debug_assert!(U64::declassify(a[3]) < (1 << 54)); 84 | debug_assert!(U64::declassify(b[3]) < (1 << 54)); 85 | debug_assert!(U64::declassify(a[4]) < (1 << 54)); 86 | debug_assert!(U64::declassify(b[4]) < (1 << 54)); 87 | 88 | // Casting to u64 and back tells the compiler that the carry is 89 | // bounded by 2^64, so that the addition is a u128 + u64 rather 90 | // than u128 + u128. 91 | 92 | const LOW_51_BIT_MASK: u64 = (1u64 << 51) - 1; 93 | let mut out = [U64::classify(0u64); 5]; 94 | 95 | c1 += U64::from(c0 >> 51).into(); 96 | out[0] = U64::from(c0) & LOW_51_BIT_MASK.into(); 97 | 98 | c2 += U64::from(c1 >> 51).into(); 99 | out[1] = U64::from(c1) & LOW_51_BIT_MASK.into(); 100 | 101 | c3 += U64::from(c2 >> 51).into(); 102 | out[2] = U64::from(c2) & LOW_51_BIT_MASK.into(); 103 | 104 | c4 += U64::from(c3 >> 51).into(); 105 | out[3] = U64::from(c3) & LOW_51_BIT_MASK.into(); 106 | 107 | let carry: U64 = (c4 >> 51).into(); 108 | out[4] = U64::from(c4) & LOW_51_BIT_MASK.into(); 109 | 110 | // To see that this does not overflow, we need out[0] + carry * 19 < 2^64. 111 | // 112 | // c4 < a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0 + (carry from c3) 113 | // < 5*(2^(51 + b) * 2^(51 + b)) + (carry from c3) 114 | // < 2^(102 + 2*b + lg(5)) + 2^64. 115 | // 116 | // When b < 3 we get 117 | // 118 | // c4 < 2^110.33 so that carry < 2^59.33 119 | // 120 | // so that 121 | // 122 | // out[0] + carry * 19 < 2^51 + 19 * 2^59.33 < 2^63.58 123 | // 124 | // and there is no overflow. 125 | out[0] = out[0] + carry * nineteen; 126 | 127 | // Now out[1] < 2^51 + 2^(64 -51) = 2^51 + 2^13 < 2^(51 + epsilon). 128 | out[1] += out[0] >> 51; 129 | out[0] &= LOW_51_BIT_MASK.into(); 130 | 131 | // Now out[i] < 2^(51 + epsilon) for all i. 132 | FieldElement64(out) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /examples/chacha20.rs: -------------------------------------------------------------------------------- 1 | use secret_integers::*; 2 | 3 | const BLOCK_SIZE : usize = 64; 4 | type State = [U32; 16]; 5 | type Key = Vec; 6 | type Nonce = Vec; 7 | type Block = [U8;64]; 8 | type Constants = [u32;4]; 9 | type Index = usize; 10 | type RotVal = u32; 11 | 12 | pub fn classify_u32s(v: &[u32]) -> Vec { 13 | v.iter().map(|x| U32::classify(*x)).collect() 14 | } 15 | 16 | 17 | fn line(a:Index, b:Index, d:Index, s:RotVal, m: &mut State) { 18 | m[a] = m[a] + m[b]; 19 | m[d] = m[d] ^ m[a]; 20 | m[d] = m[d].rotate_left(s); 21 | } 22 | 23 | 24 | fn quarter_round(a:Index, b:Index, c:Index, d:Index, m: &mut State) { 25 | line(a,b,d,16,m); 26 | line(c,d,b,12,m); 27 | line(a,b,d,8,m); 28 | line(c,d,b,7,m); 29 | } 30 | 31 | fn double_round(m: &mut State) { 32 | quarter_round(0,4,8,12,m); 33 | quarter_round(1,5,9,13,m); 34 | quarter_round(2,6,10,14,m); 35 | quarter_round(3,7,11,15,m); 36 | 37 | quarter_round(0,5,10,15,m); 38 | quarter_round(1,6,11,12,m); 39 | quarter_round(2,7,8,13,m); 40 | quarter_round(3,4,9,14,m); 41 | } 42 | 43 | const CONSTANTS: Constants = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]; 44 | 45 | fn chacha20_init(k:&Key, counter:U32, nonce:&Nonce) -> State { 46 | let mut st = [U32::classify(0u32);16]; 47 | st[0..4].copy_from_slice(&classify_u32s(&CONSTANTS)); 48 | st[4..12].copy_from_slice(U32::from_bytes_le(k).as_slice()); 49 | st[12] = counter; 50 | st[13..16].copy_from_slice(U32::from_bytes_le(nonce).as_slice()); 51 | st 52 | } 53 | 54 | fn chacha20_core(st:&mut State) { 55 | let mut working_state = st.clone(); 56 | for _ in 0..10 { 57 | double_round(&mut working_state); 58 | } 59 | for i in 0..16 { 60 | st[i] += working_state[i]; 61 | } 62 | } 63 | 64 | fn chacha20(k:&Key, counter:U32, nonce:&Nonce) -> State { 65 | let mut st = chacha20_init(k, counter, nonce); 66 | chacha20_core(&mut st); 67 | st 68 | } 69 | 70 | fn chacha20_block(k:&Key, counter:U32, nonce:&Nonce) -> Block { 71 | let st = chacha20(k, counter, nonce); 72 | let mut block = [U8::classify(0u8);BLOCK_SIZE]; 73 | block.copy_from_slice(U32::to_bytes_le(&st).as_slice()); 74 | block 75 | } 76 | 77 | fn xor_block(block: &Block, key_block: &Block) -> Block { 78 | let mut v_out = [Default::default();BLOCK_SIZE]; 79 | for i in 0..BLOCK_SIZE { 80 | v_out[i] = block[i] ^ key_block[i]; 81 | } 82 | let mut out = [Default::default();BLOCK_SIZE]; 83 | out.copy_from_slice(&v_out); 84 | out 85 | } 86 | 87 | fn chacha20_counter_mode(key: &Key, counter:U32, nonce:&Nonce, msg:&Vec) -> Vec { 88 | let mut blocks : Vec<[U8;BLOCK_SIZE]> = msg.chunks(BLOCK_SIZE).map(|block| { 89 | let mut new_block = [U8::zero();BLOCK_SIZE]; 90 | new_block[0..block.len()].copy_from_slice(block); 91 | new_block 92 | }).collect(); 93 | let nb_blocks = blocks.len(); 94 | let mut key_block : [U8; BLOCK_SIZE]; 95 | let mut ctr = counter; 96 | for i in 0..blocks.len() - 1 { 97 | key_block = chacha20_block(key, ctr, nonce); 98 | blocks[i] = xor_block(&blocks[i], &key_block); 99 | ctr += U32::one(); 100 | } 101 | let last = &mut blocks[nb_blocks - 1]; 102 | key_block = chacha20_block(key, ctr, nonce); 103 | *last = xor_block(last, &key_block); 104 | blocks.iter().map(|block| block.to_vec()).flatten().take(msg.len()).collect() 105 | } 106 | 107 | pub fn chacha20_encrypt(key: &Key, counter: U32, nonce:&Nonce, msg: &Vec) -> Vec { 108 | chacha20_counter_mode(key, counter, nonce, msg) 109 | } 110 | 111 | pub fn chacha20_decrypt(key: &Key, counter: U32, nonce:&Nonce, msg: &Vec) -> Vec { 112 | chacha20_counter_mode(key, counter, nonce, msg) 113 | } 114 | 115 | #[test] 116 | fn chacha20_test() { 117 | let plaintext = classify_u8s(&vec![ 118 | 0x4c,0x61,0x64,0x69,0x65,0x73,0x20,0x61, 119 | 0x6e,0x64,0x20,0x47,0x65,0x6e,0x74,0x6c, 120 | 0x65,0x6d,0x65,0x6e,0x20,0x6f,0x66,0x20, 121 | 0x74,0x68,0x65,0x20,0x63,0x6c,0x61,0x73, 122 | 0x73,0x20,0x6f,0x66,0x20,0x27,0x39,0x39, 123 | 0x3a,0x20,0x49,0x66,0x20,0x49,0x20,0x63, 124 | 0x6f,0x75,0x6c,0x64,0x20,0x6f,0x66,0x66, 125 | 0x65,0x72,0x20,0x79,0x6f,0x75,0x20,0x6f, 126 | 0x6e,0x6c,0x79,0x20,0x6f,0x6e,0x65,0x20, 127 | 0x74,0x69,0x70,0x20,0x66,0x6f,0x72,0x20, 128 | 0x74,0x68,0x65,0x20,0x66,0x75,0x74,0x75, 129 | 0x72,0x65,0x2c,0x20,0x73,0x75,0x6e,0x73, 130 | 0x63,0x72,0x65,0x65,0x6e,0x20,0x77,0x6f, 131 | 0x75,0x6c,0x64,0x20,0x62,0x65,0x20,0x69, 132 | 0x74,0x2e]); 133 | let ciphertext = classify_u8s(&vec![ 134 | 0x6e,0x2e,0x35,0x9a,0x25,0x68,0xf9,0x80, 135 | 0x41,0xba,0x07,0x28,0xdd,0x0d,0x69,0x81, 136 | 0xe9,0x7e,0x7a,0xec,0x1d,0x43,0x60,0xc2, 137 | 0x0a,0x27,0xaf,0xcc,0xfd,0x9f,0xae,0x0b, 138 | 0xf9,0x1b,0x65,0xc5,0x52,0x47,0x33,0xab, 139 | 0x8f,0x59,0x3d,0xab,0xcd,0x62,0xb3,0x57, 140 | 0x16,0x39,0xd6,0x24,0xe6,0x51,0x52,0xab, 141 | 0x8f,0x53,0x0c,0x35,0x9f,0x08,0x61,0xd8, 142 | 0x07,0xca,0x0d,0xbf,0x50,0x0d,0x6a,0x61, 143 | 0x56,0xa3,0x8e,0x08,0x8a,0x22,0xb6,0x5e, 144 | 0x52,0xbc,0x51,0x4d,0x16,0xcc,0xf8,0x06, 145 | 0x81,0x8c,0xe9,0x1a,0xb7,0x79,0x37,0x36, 146 | 0x5a,0xf9,0x0b,0xbf,0x74,0xa3,0x5b,0xe6, 147 | 0xb4,0x0b,0x8e,0xed,0xf2,0x78,0x5e,0x42, 148 | 0x87,0x4d 149 | ]); 150 | let key = classify_u8s(&vec![ 151 | 0u8,1u8,2u8,3u8,4u8,5u8,6u8,7u8, 152 | 8u8,9u8,10u8,11u8,12u8,13u8,14u8,15u8, 153 | 16u8,17u8,18u8,19u8,20u8,21u8,22u8,23u8, 154 | 24u8,25u8,26u8,27u8,28u8,29u8,30u8,31u8 155 | ]); 156 | let nonce = classify_u8s(&vec![0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4a,0x0,0x0,0x0,0x0]); 157 | let computed_ciphertext = chacha20_encrypt(&key, 1u32.into(), &nonce, &plaintext); 158 | for (i, (x1,x2)) in ciphertext.iter().zip(computed_ciphertext).enumerate() { 159 | assert_eq!(x1.declassify(), x2.declassify(), "at index {:?}", i); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate defines simple wrappers around Rust's integer type to guarantee they are used in 2 | //! a constant-time fashion. Hence, division and direct comparison of these "secret" integers is 3 | //! disallowed. 4 | //! 5 | //! These integers are intended to be the go-to type to use when implementing cryptographic 6 | //! software, as they provide an extra automated check against use of variable-time operations. 7 | //! 8 | //! To use the crate, just import everything (`use secret_integers::*;`) and replace your integer 9 | //! types with uppercase versions of their names (e.g. `u8` -> `U8`). 10 | //! 11 | //! # Examples 12 | //! 13 | //! In order to print information or test code involving your secret integers, you need first to 14 | //! declassify them. Your crypto code should not contain any `declassify` occurence though to 15 | //! guarantee constant-timedness. Make sure to specify the type of your literals when classifying 16 | //! (e.g. `0x36u16`) or else you'll get a casting error. 17 | //! 18 | //! ``` 19 | //! # use secret_integers::*; 20 | //! let x = U32::classify(1u32); 21 | //! let y : U32 = 2u32.into(); 22 | //! assert_eq!((x + y).declassify(), 3); 23 | //! ``` 24 | //! 25 | //! Using an illegal operation will get you a compile-time error: 26 | //! 27 | //! ```compile_fail 28 | //! # use secret_integers::*; 29 | //! let x = U32::classify(4u32); 30 | //! let y : U32 = 2u32.into(); 31 | //! assert_eq!((x / y).declassify(), 2); 32 | //! ``` 33 | //! 34 | //! Since indexing arrays and vectors is only possible with `usize`, these secret integers also 35 | //! prevent you from using secret values to index memory (which is a breach to constant-timedness 36 | //! due to cache behaviour). 37 | //! 38 | //! ``` 39 | //! # use secret_integers::*; 40 | //! fn xor_block(block1: &mut [U64;16], block2: &[U64;16]) { 41 | //! for i in 0..16 { 42 | //! block1[i] ^= block2[i] 43 | //! } 44 | //! } 45 | //! ``` 46 | //! See the [Dalek](https://github.com/denismerigoux/rust-secret-integers/tree/master/examples/dalek.rs) 47 | //! and [Chacha20](https://github.com/denismerigoux/rust-secret-integers/tree/master/examples/chacha20.rs) 48 | //! examples for more details on how to use this crate. 49 | //! 50 | //! 51 | //! # Const-compatibility 52 | //! 53 | //! Because stable Rust does not allow constant functions for now, it is impossible to use those 54 | //! wrappers in const declarations. Even classifying directly inside the declaration does not work: 55 | //! 56 | //! ```compile_fail 57 | //! const IV : [U32;2] = [U32::classify(0xbe6548u32),U32::classify(0xaec6d48u32)] 58 | //! ``` 59 | //! 60 | //! For now, the solution is to map your const items with `classify` once you're inside a function, 61 | //! or call `into`. 62 | //! 63 | //! ``` 64 | //! # use secret_integers::*; 65 | //! const IV : [u32;2] = [0xbe6548, 0xaec6d48]; 66 | //! 67 | //! fn start_cipher(plain: &mut Vec) { 68 | //! for i in 0..plain.len() { 69 | //! plain[i] = plain[i] | (plain[i] ^ IV[i].into()); 70 | //! } 71 | //! } 72 | //! ``` 73 | //! 74 | 75 | use std::num::Wrapping; 76 | use std::ops::*; 77 | 78 | macro_rules! define_wrapping_op { 79 | ($name:ident, $op:tt, $op_name:ident, $func_op:ident, $assign_name:ident, $assign_func:ident, $checked_func_op:ident) => { 80 | 81 | /// **Warning:** has wrapping semantics. 82 | impl $op_name for $name { 83 | type Output = Self; 84 | #[inline] 85 | fn $func_op(self, rhs: Self) -> Self { 86 | let $name(i1) = self; 87 | let $name(i2) = rhs; 88 | $name((Wrapping(i1) $op Wrapping(i2)).0) 89 | } 90 | } 91 | 92 | impl $name { 93 | /// **Warning:** panics when overflow. 94 | pub fn $checked_func_op(self, rhs: Self) -> Self { 95 | let $name(i1) = self; 96 | let $name(i2) = rhs; 97 | match i1.$checked_func_op(i2) { 98 | None => panic!("Secret integer {} overflow!", stringify!($func_op)), 99 | Some(r) => $name(r) 100 | } 101 | } 102 | } 103 | 104 | /// **Warning:** has wrapping semantics. 105 | impl $assign_name for $name { 106 | #[inline] 107 | fn $assign_func(&mut self, rhs: Self) { 108 | *self = *self $op rhs 109 | } 110 | } 111 | } 112 | } 113 | 114 | macro_rules! define_bitwise_op { 115 | ($name:ident, $op:tt, $op_name:ident, $func_op:ident, $assign_name:ident, $assign_func:ident) => { 116 | impl $op_name for $name { 117 | type Output = Self; 118 | #[inline] 119 | fn $func_op(self, rhs: Self) -> Self { 120 | let $name(i1) = self; 121 | let $name(i2) = rhs; 122 | $name(i1 $op i2) 123 | } 124 | } 125 | 126 | impl $assign_name for $name { 127 | #[inline] 128 | fn $assign_func(&mut self, rhs: Self) { 129 | *self = *self $op rhs 130 | } 131 | } 132 | } 133 | } 134 | 135 | macro_rules! define_unary_op { 136 | ($name:ident, $op:tt, $op_name:ident, $func_op:ident) => { 137 | impl $op_name for $name { 138 | type Output = Self; 139 | #[inline] 140 | fn $func_op(self) -> Self { 141 | let $name(i1) = self; 142 | $name($op i1) 143 | } 144 | } 145 | } 146 | } 147 | 148 | macro_rules! define_shift { 149 | ($name:ident, $op:tt, $op_name:ident, $func_op:ident, $assign_name:ident, $assign_func:ident) => { 150 | impl $op_name for $name { 151 | type Output = Self; 152 | #[inline] 153 | fn $func_op(self, rhs: u32) -> Self { 154 | let $name(i1) = self; 155 | $name(i1 $op rhs) 156 | } 157 | } 158 | 159 | impl $assign_name for $name { 160 | #[inline] 161 | fn $assign_func(&mut self, rhs: u32) { 162 | *self = *self $op rhs 163 | } 164 | } 165 | } 166 | } 167 | 168 | macro_rules! define_secret_integer { 169 | ($name:ident, $repr:ty, $bits:tt) => { 170 | #[derive(Clone, Copy, Default)] 171 | pub struct $name(pub $repr); 172 | 173 | impl $name { 174 | #[inline] 175 | pub fn classify>(x: T) -> Self { 176 | $name(x.into()) 177 | } 178 | 179 | #[inline] 180 | /// **Warning:** use with caution, breaks the constant-time guarantee. 181 | pub fn declassify(self) -> $repr { 182 | self.0 183 | } 184 | 185 | #[inline] 186 | pub fn zero() -> Self { 187 | $name(0) 188 | } 189 | 190 | #[inline] 191 | pub fn one() -> Self { 192 | $name(1) 193 | } 194 | 195 | #[inline] 196 | pub fn ones() -> Self { 197 | !Self::zero() 198 | } 199 | 200 | pub fn from_bytes_le(bytes: &[U8]) -> Vec<$name> { 201 | assert!(bytes.len() % ($bits/8) == 0); 202 | bytes.chunks($bits/8).map(|chunk| { 203 | let mut chunk_raw : [u8; $bits/8] = [0u8; $bits/8]; 204 | for i in 0..$bits/8 { 205 | chunk_raw[i] = U8::declassify(chunk[i]); 206 | } 207 | $name::classify(unsafe { 208 | std::mem::transmute::<[u8;$bits/8], $repr>( 209 | chunk_raw 210 | ).to_le() 211 | }) 212 | }).collect::>() 213 | } 214 | 215 | pub fn to_bytes_le(ints: &[$name]) -> Vec { 216 | ints.iter().map(|int| { 217 | let int = $name::declassify(*int); 218 | let bytes : [u8;$bits/8] = unsafe { 219 | std::mem::transmute::<$repr, [u8;$bits/8]>(int.to_le()) 220 | }; 221 | let secret_bytes : Vec = bytes.iter().map(|x| U8::classify(*x)).collect(); 222 | secret_bytes 223 | }).flatten().collect() 224 | } 225 | 226 | pub fn from_bytes_be(bytes: &[U8]) -> Vec<$name> { 227 | assert!(bytes.len() % ($bits/8) == 0); 228 | bytes.chunks($bits/8).map(|chunk| { 229 | let mut chunk_raw : [u8; $bits/8] = [0u8; $bits/8]; 230 | for i in 0..$bits/8 { 231 | chunk_raw[i] = U8::declassify(chunk[i]); 232 | } 233 | $name::classify(unsafe { 234 | std::mem::transmute::<[u8;$bits/8], $repr>( 235 | chunk_raw 236 | ).to_be() 237 | }) 238 | }).collect::>() 239 | } 240 | 241 | pub fn to_bytes_be(ints: &[$name]) -> Vec { 242 | ints.iter().map(|int| { 243 | let int = $name::declassify(*int); 244 | let bytes : [u8;$bits/8] = unsafe { 245 | std::mem::transmute::<$repr, [u8;$bits/8]>(int.to_be()) 246 | }; 247 | let secret_bytes : Vec = bytes.iter().map(|x| U8::classify(*x)).collect(); 248 | secret_bytes 249 | }).flatten().collect() 250 | } 251 | 252 | pub fn to_be_bytes(&self) -> Vec { 253 | $name::declassify(*self).to_be_bytes().to_vec() 254 | } 255 | } 256 | 257 | impl From<$repr> for $name { 258 | #[inline] 259 | fn from(x:$repr) -> Self { 260 | Self::classify(x) 261 | } 262 | } 263 | 264 | define_wrapping_op!($name, +, Add, add, AddAssign, add_assign, checked_add); 265 | define_wrapping_op!($name, -, Sub, sub, SubAssign, sub_assign, checked_sub); 266 | define_wrapping_op!($name, *, Mul, mul, MulAssign, mul_assign, checked_mul); 267 | 268 | define_shift!($name, <<, Shl, shl, ShlAssign, shl_assign); 269 | define_shift!($name, >>, Shr, shr, ShrAssign, shr_assign); 270 | 271 | impl $name { 272 | #[inline] 273 | pub fn rotate_left(self, rotval:u32) -> Self { 274 | let $name(i) = self; 275 | $name(i.rotate_left(rotval)) 276 | } 277 | 278 | #[inline] 279 | pub fn rotate_right(self, rotval:u32) -> Self { 280 | let $name(i) = self; 281 | $name(i.rotate_right(rotval)) 282 | } 283 | } 284 | 285 | define_bitwise_op!($name, &, BitAnd, bitand, BitAndAssign, bitand_assign); 286 | define_bitwise_op!($name, |, BitOr, bitor, BitOrAssign, bitor_assign); 287 | define_bitwise_op!($name, ^, BitXor, bitxor, BitXorAssign, bitxor_assign); 288 | 289 | /// `Not` has bitwise semantics for integers 290 | define_unary_op!($name, !, Not, not); 291 | 292 | // Printing integers. 293 | impl std::fmt::Display for $name { 294 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 295 | let uint: $repr = self.declassify(); 296 | write!(f, "{}", uint) 297 | } 298 | } 299 | impl std::fmt::Debug for $name { 300 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 301 | let uint: $repr = self.declassify(); 302 | write!(f, "{}", uint) 303 | } 304 | } 305 | impl std::fmt::LowerHex for $name { 306 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 307 | let val: $repr = self.declassify(); 308 | std::fmt::LowerHex::fmt(&val, f) 309 | } 310 | } 311 | } 312 | } 313 | 314 | macro_rules! define_secret_unsigned_integer { 315 | ($name:ident, $repr:ty, $bits:tt) => { 316 | /// Secret unsigned integer. 317 | define_secret_integer!($name, $repr, $bits); 318 | impl Neg for $name { 319 | type Output = Self; 320 | #[inline] 321 | fn neg(self) -> Self { 322 | let $name(i1) = self; 323 | $name((Wrapping(!i1) + Wrapping(1)).0) 324 | } 325 | } 326 | 327 | /// # Constant-time comparison operators 328 | impl $name { 329 | /// Produces a new integer which is all ones if the two arguments are equal and 330 | /// all zeroes otherwise. With inspiration from 331 | /// [Wireguard](https://git.zx2c4.com/WireGuard/commit/src/crypto/curve25519-hacl64.h?id=2e60bb395c1f589a398ec606d611132ef9ef764b). 332 | #[inline] 333 | pub fn comp_eq(self, rhs: Self) -> Self { 334 | let a = self; 335 | let b = rhs; 336 | let x = a ^ b; 337 | let minus_x = -x; 338 | let x_or_minus_x = x | minus_x; 339 | let xnx = x_or_minus_x >> ($bits - 1); 340 | let c = xnx - Self::one(); 341 | c 342 | } 343 | 344 | /// Produces a new integer which is all ones if the first argument is different from 345 | /// the second argument, and all zeroes otherwise. 346 | #[inline] 347 | pub fn comp_ne(self, rhs: Self) -> Self { 348 | !self.comp_eq(rhs) 349 | } 350 | 351 | /// Produces a new integer which is all ones if the first argument is greater than or 352 | /// equal to the second argument, and all zeroes otherwise. With inspiration from 353 | /// [WireGuard](https://git.zx2c4.com/WireGuard/commit/src/crypto/curve25519-hacl64.h?id=0a483a9b431d87eca1b275463c632f8d5551978a). 354 | #[inline] 355 | pub fn comp_gte(self, rhs: Self) -> Self { 356 | let x = self; 357 | let y = rhs; 358 | let x_xor_y = x ^ y; 359 | let x_sub_y = x - y; 360 | let x_sub_y_xor_y = x_sub_y ^ y; 361 | let q = x_xor_y ^ x_sub_y_xor_y; 362 | let x_xor_q = x ^ q; 363 | let x_xor_q_ = x_xor_q >> ($bits - 1); 364 | let c = x_xor_q_ - Self::one(); 365 | c 366 | } 367 | 368 | /// Produces a new integer which is all ones if the first argumentis strictly greater 369 | /// than the second argument, and all zeroes otherwise. 370 | #[inline] 371 | pub fn comp_gt(self, rhs: Self) -> Self { 372 | self.comp_gte(rhs) ^ self.comp_eq(rhs) 373 | } 374 | 375 | /// Produces a new integer which is all ones if the first argumentis less than or 376 | /// equal to the second argument, and all zeroes otherwise. 377 | #[inline] 378 | pub fn comp_lte(self, rhs: Self) -> Self { 379 | !self.comp_gt(rhs) 380 | } 381 | 382 | /// Produces a new integer which is all ones if the first argumentis strictly less than 383 | /// the second argument, and all zeroes otherwise. 384 | #[inline] 385 | pub fn comp_lt(self, rhs: Self) -> Self { 386 | !self.comp_gte(rhs) 387 | } 388 | } 389 | }; 390 | } 391 | 392 | macro_rules! define_secret_signed_integer { 393 | ($name:ident, $repr:ty, $bits:tt) => { 394 | /// Secret signed integer. 395 | define_secret_integer!($name, $repr, $bits); 396 | define_unary_op!($name, -, Neg, neg); 397 | } 398 | } 399 | 400 | define_secret_unsigned_integer!(U8, u8, 8); 401 | define_secret_unsigned_integer!(U16, u16, 16); 402 | define_secret_unsigned_integer!(U32, u32, 32); 403 | define_secret_unsigned_integer!(U64, u64, 64); 404 | define_secret_unsigned_integer!(U128, u128, 128); 405 | define_secret_signed_integer!(I8, i8, 8); 406 | define_secret_signed_integer!(I16, i16, 16); 407 | define_secret_signed_integer!(I32, i32, 32); 408 | define_secret_signed_integer!(I64, i64, 64); 409 | define_secret_signed_integer!(I128, i128, 128); 410 | 411 | macro_rules! define_uU_casting { 412 | ($from:ident, $to:ident, $to_repr:ident) => { 413 | impl From<$from> for $to { 414 | #[inline] 415 | fn from(x: $from) -> $to { 416 | $to(<$to_repr>::from(x)) 417 | } 418 | } 419 | }; 420 | } 421 | 422 | macro_rules! define_usize_casting { 423 | ($from:ident, $to:ident, $to_repr:ident) => { 424 | impl From<$from> for $to { 425 | #[inline] 426 | fn from(x: $from) -> $to { 427 | $to(x as $to_repr) 428 | } 429 | } 430 | }; 431 | } 432 | 433 | macro_rules! define_Uu_casting { 434 | ($from:ident, $to:ident) => { 435 | /// **Warning:** conversion can be lossy! 436 | impl From<$from> for $to { 437 | #[inline] 438 | fn from(x: $from) -> $to { 439 | <$to>::from(x.declassify()) 440 | } 441 | } 442 | }; 443 | } 444 | 445 | macro_rules! define_safe_casting { 446 | ($from:ident, $to:ident, $to_repr:ident) => { 447 | impl From<$from> for $to { 448 | #[inline] 449 | fn from(x: $from) -> $to { 450 | $to(x.0 as $to_repr) 451 | } 452 | } 453 | }; 454 | } 455 | 456 | macro_rules! define_unsafe_casting { 457 | ($from:ident, $to:ident, $to_repr:ident) => { 458 | /// **Warning:** wrapping semantics. 459 | impl From<$from> for $to { 460 | #[inline] 461 | fn from(x: $from) -> $to { 462 | $to(x.0 as $to_repr) 463 | } 464 | } 465 | }; 466 | } 467 | 468 | macro_rules! define_signed_unsigned_casting { 469 | ($unsigned:ident, $unsiged_repr:ident, $signed:ident, $signed_repr:ident) => { 470 | /// **Warning:** wrapping semantics. 471 | impl From<$unsigned> for $signed { 472 | #[inline] 473 | fn from(x: $unsigned) -> $signed { 474 | $signed(x.0 as $signed_repr) 475 | } 476 | } 477 | }; 478 | } 479 | 480 | // Casting 481 | 482 | // U128 <-> Un{n < 128} 483 | define_safe_casting!(U8, U128, u128); 484 | define_unsafe_casting!(U128, U8, u8); 485 | define_safe_casting!(U16, U128, u128); 486 | define_unsafe_casting!(U128, U16, u16); 487 | define_safe_casting!(U32, U128, u128); 488 | define_unsafe_casting!(U128, U32, u32); 489 | define_safe_casting!(U64, U128, u128); 490 | define_unsafe_casting!(U128, U64, u64); 491 | 492 | // U64 <-> Un{n < 64} 493 | define_safe_casting!(U8, U64, u64); 494 | define_unsafe_casting!(U64, U8, u8); 495 | define_safe_casting!(U16, U64, u64); 496 | define_unsafe_casting!(U64, U16, u16); 497 | define_safe_casting!(U32, U64, u64); 498 | define_unsafe_casting!(U64, U32, u32); 499 | 500 | // U32 <-> Un{n < 32} 501 | define_safe_casting!(U8, U32, u32); 502 | define_unsafe_casting!(U32, U8, u8); 503 | define_safe_casting!(U16, U32, u32); 504 | define_unsafe_casting!(U32, U16, u16); 505 | 506 | // U8 <-> u 507 | define_Uu_casting!(U8, u8); 508 | define_Uu_casting!(U8, u16); 509 | define_Uu_casting!(U8, u32); 510 | define_Uu_casting!(U8, u64); 511 | define_Uu_casting!(U8, u128); 512 | define_Uu_casting!(U8, usize); 513 | define_usize_casting!(usize, U8, u8); 514 | 515 | // U16 <-> u 516 | define_Uu_casting!(U16, u16); 517 | define_Uu_casting!(U16, u32); 518 | define_Uu_casting!(U16, u64); 519 | define_Uu_casting!(U16, u128); 520 | 521 | // U32 <-> u 522 | define_Uu_casting!(U32, u32); 523 | define_Uu_casting!(U32, u64); 524 | define_Uu_casting!(U32, u128); 525 | 526 | // U64 <-> u 527 | define_Uu_casting!(U64, u64); 528 | define_Uu_casting!(U64, u128); 529 | 530 | // U128 <-> u 531 | define_Uu_casting!(U128, u128); 532 | 533 | // u16 <-> U 534 | define_uU_casting!(u8, U16, u16); 535 | 536 | // u32 <-> U 537 | define_uU_casting!(u8, U32, u32); 538 | define_uU_casting!(u16, U32, u32); 539 | 540 | // u64 <-> U 541 | define_uU_casting!(u8, U64, u64); 542 | define_uU_casting!(u16, U64, u64); 543 | define_uU_casting!(u32, U64, u64); 544 | define_usize_casting!(usize, U64, u64); 545 | 546 | // u128 <-> U 547 | define_uU_casting!(u8, U128, u128); 548 | define_uU_casting!(u16, U128, u128); 549 | define_uU_casting!(u32, U128, u128); 550 | define_uU_casting!(u64, U128, u128); 551 | define_usize_casting!(usize, U128, u128); 552 | 553 | // U16 <-> Un{n < 16} 554 | define_safe_casting!(U8, U16, u16); 555 | define_unsafe_casting!(U16, U8, u8); 556 | 557 | // I128 <-> In{n < 128} 558 | define_safe_casting!(I8, I128, i128); 559 | define_unsafe_casting!(I128, I8, i8); 560 | define_safe_casting!(I16, I128, i128); 561 | define_unsafe_casting!(I128, I16, i16); 562 | define_safe_casting!(I32, I128, i128); 563 | define_unsafe_casting!(I128, I32, i32); 564 | define_safe_casting!(I64, I128, i128); 565 | define_unsafe_casting!(I128, I64, i64); 566 | 567 | // I64 <-> In{n < 64} 568 | define_safe_casting!(I8, I64, i64); 569 | define_unsafe_casting!(I64, I8, i8); 570 | define_safe_casting!(I16, I64, i64); 571 | define_unsafe_casting!(I64, I16, i16); 572 | define_safe_casting!(I32, I64, i64); 573 | define_unsafe_casting!(I64, I32, i32); 574 | 575 | // I32 <-> In{n < 32} 576 | define_safe_casting!(I8, I32, i32); 577 | define_unsafe_casting!(I32, I8, i8); 578 | define_safe_casting!(I16, I32, i32); 579 | define_unsafe_casting!(I32, I16, i16); 580 | 581 | // I16 <-> In{n < 16} 582 | define_safe_casting!(I8, I16, i16); 583 | define_unsafe_casting!(I16, I8, i8); 584 | 585 | // Unsigned <-> signed 586 | define_signed_unsigned_casting!(U128, u128, I128, i128); 587 | define_signed_unsigned_casting!(U64, u64, I64, i64); 588 | define_signed_unsigned_casting!(U32, u32, I32, i32); 589 | define_signed_unsigned_casting!(U16, u16, I16, i16); 590 | define_signed_unsigned_casting!(U8, u8, I8, i8); 591 | 592 | macro_rules! define_tests { 593 | // Note that the u8 here is necessary because the integers get interpreted 594 | // as i32 on Linux otherwise. 595 | ($modname:ident, $type:ident) => { 596 | #[cfg(test)] 597 | mod $modname { 598 | use crate::*; 599 | 600 | #[test] 601 | fn test_comp_eq_ok() { 602 | let a = $type::from(3u8); 603 | let b = $type::from(3u8); 604 | let eq = $type::comp_eq(a, b); 605 | assert_eq!(eq.declassify(), $type::ones().declassify()); 606 | } 607 | 608 | #[test] 609 | fn test_comp_eq_fail() { 610 | let a = $type::from(3u8); 611 | let b = $type::from(42u8); 612 | let eq = $type::comp_eq(a, b); 613 | assert_eq!(eq.declassify(), $type::zero().declassify()); 614 | } 615 | 616 | #[test] 617 | fn test_comp_neq_ok() { 618 | let a = $type::from(3u8); 619 | let b = $type::from(42u8); 620 | let eq = $type::comp_ne(a, b); 621 | assert_eq!(eq.declassify(), $type::ones().declassify()); 622 | } 623 | 624 | #[test] 625 | fn test_comp_neq_fail() { 626 | let a = $type::from(3u8); 627 | let b = $type::from(3u8); 628 | let eq = $type::comp_ne(a, b); 629 | assert_eq!(eq.declassify(), $type::zero().declassify()); 630 | } 631 | 632 | #[test] 633 | fn test_comp_gte_ok() { 634 | let a = $type::from(42u8); 635 | let b = $type::from(3u8); 636 | let eq = $type::comp_gte(a, b); 637 | assert_eq!(eq.declassify(), $type::ones().declassify()); 638 | } 639 | 640 | #[test] 641 | fn test_comp_gte_fail() { 642 | let a = $type::from(3u8); 643 | let b = $type::from(42u8); 644 | let eq = $type::comp_gte(a, b); 645 | assert_eq!(eq.declassify(), $type::zero().declassify()); 646 | } 647 | } 648 | }; 649 | } 650 | 651 | define_tests!(tests_u8, U8); 652 | define_tests!(tests_u32, U32); 653 | define_tests!(tests_u64, U64); 654 | 655 | // Optional conversions for `subtle::Choice`, enabled via the "subtle" feature flag 656 | #[cfg(feature = "subtle")] 657 | impl Into for U8 { 658 | #[inline] 659 | fn into(self) -> subtle::Choice { 660 | let mask = 0b00000001u8; 661 | subtle::Choice::from(self.0 & mask) 662 | } 663 | } 664 | 665 | #[cfg(feature = "subtle")] 666 | impl From for U8 { 667 | #[inline] 668 | fn from(choice: subtle::Choice) -> Self { 669 | U8((choice).unwrap_u8()).comp_eq(U8::one()) 670 | } 671 | } 672 | 673 | #[cfg(all(test, feature = "subtle"))] 674 | mod subtle_test { 675 | use super::U8; 676 | #[test] 677 | fn test_subtle_u8() { 678 | let forty_two = U8::classify(42); 679 | let forty_one = U8::classify(41); 680 | 681 | let equal: subtle::Choice = forty_two.comp_eq(forty_two).into(); 682 | assert!(bool::from(equal)); 683 | assert_eq!(equal.unwrap_u8(), 1); 684 | 685 | let not_equal: subtle::Choice = forty_two.comp_eq(forty_one).into(); 686 | assert!(!bool::from(not_equal)); 687 | assert_eq!(not_equal.unwrap_u8(), 0); 688 | 689 | // Test round trips 690 | let subtle_true: subtle::Choice = 1u8.into(); 691 | let secret_true: U8 = subtle_true.into(); 692 | let subtle_true: subtle::Choice = secret_true.into(); 693 | assert!(bool::from(subtle_true)); 694 | assert_eq!(subtle_true.unwrap_u8(), 1); 695 | 696 | let subtle_false: subtle::Choice = 0u8.into(); 697 | let secret_false: U8 = subtle_false.into(); 698 | let subtle_false: subtle::Choice = secret_false.into(); 699 | assert!(!bool::from(subtle_false)); 700 | assert_eq!(subtle_false.unwrap_u8(), 0); 701 | } 702 | } 703 | --------------------------------------------------------------------------------