Compare commits
140 Commits
explicit_s
...
aes
| Author | SHA1 | Date | |
|---|---|---|---|
| 6072e959b0 | |||
| 5b122b8c0c | |||
| e84f6a865e | |||
| 52a9a56d38 | |||
| 57b4e37147 | |||
| 12599b9a63 | |||
| f15ae3e323 | |||
| c32fa6c2b6 | |||
| c07fccd57c | |||
| 08be0bf9e4 | |||
| ecc29f06fa | |||
| 82bb499be3 | |||
| 20c65b93bf | |||
| 7c45f898ab | |||
| c675aaa5f6 | |||
| 6d4c9c4f50 | |||
| a34d8dc88b | |||
| b59653de57 | |||
| 3eee154fe1 | |||
| 0b4d84b038 | |||
| 54687cb602 | |||
| 1bac2010b1 | |||
| 818b006521 | |||
| bae1c93c54 | |||
| ef3174f224 | |||
| 89c8705779 | |||
| 080c8f18e2 | |||
| 060b82b351 | |||
| ba2ceee725 | |||
| 4d2e43620a | |||
| c5850b4d01 | |||
| a19c1ee124 | |||
| 2145fb47fa | |||
| 2912c72a07 | |||
| 23a79300c8 | |||
| 0e6664f232 | |||
| 4ce8797da2 | |||
| 7f2b509640 | |||
| b0885722a8 | |||
| 83cdd8ef4c | |||
| 2f395721bc | |||
| 2b63dfa376 | |||
| fc09ff48a2 | |||
| c9f418feff | |||
| ac380d08af | |||
| 25746af626 | |||
| 16cf6172ce | |||
| d2bdbd37fe | |||
| e6e3789127 | |||
| 8bca480e47 | |||
| b42902e6ab | |||
| 44618c2e2f | |||
| 4c03ab6648 | |||
| 1b2d7db1e0 | |||
| 9cf0b587b2 | |||
| d459850c54 | |||
| 6c61e1c56c | |||
| aaa8dc3497 | |||
| ad484877cf | |||
| 1eba2d1709 | |||
| 29872fa47a | |||
| 031b4be14e | |||
| 8c87f945a1 | |||
| 6d2c803f2b | |||
| 8a7e604fbd | |||
| cfc06c3b56 | |||
| 40a5793089 | |||
| 026b321f7c | |||
| 887c90202a | |||
| 6613f85ff3 | |||
| 22b4fcbd94 | |||
| e4f67f0918 | |||
| 68ddc7096b | |||
| 54c5244bc5 | |||
| 06d3391748 | |||
| 3a0d08d572 | |||
| 1d1ca3d817 | |||
| 69cef498f2 | |||
| 47fae77a4f | |||
| 06400cc7a5 | |||
| 3a6ae61707 | |||
| 5a69795fdf | |||
| 40254014d3 | |||
| 9d2e56ad06 | |||
| 95c3dc94df | |||
| cc83b239cc | |||
| b3276ce2f6 | |||
| 91d595ee4b | |||
| f1f39f8b5f | |||
| 038db66c2f | |||
| bf13f4a1e6 | |||
| 15ec3c5c9b | |||
| 8d8351e833 | |||
| 4559b80d2f | |||
| 293a93c6d4 | |||
| a3b0ef8d98 | |||
| 9ce976bb6e | |||
| 94defc4e77 | |||
| a426ca3901 | |||
| 3716dba87c | |||
| a390a7bb53 | |||
| 71cb38ca30 | |||
| 322701ad6c | |||
| 8a771c05a4 | |||
| a8174ac47e | |||
| 00b944e30a | |||
| 63bfda9073 | |||
| 4529562cb8 | |||
| 4af5446e80 | |||
| 04b4c79f7a | |||
| 89deea0337 | |||
| f60a492a0b | |||
| c5e9d4be25 | |||
| f4e47154c2 | |||
| cdcfd9a3a3 | |||
| f3494d8524 | |||
| eb82edea7e | |||
| 62cb276888 | |||
| 160618cdd7 | |||
| 9d87916cc5 | |||
| ef54ed4cda | |||
| 43b73139cd | |||
| b30fe6a75f | |||
| 011ebc0c99 | |||
| a6def22bd1 | |||
| c49cd29c43 | |||
| 65d7b7e93f | |||
| b93286fe60 | |||
| 5a5b48569b | |||
| b5afa8fdf9 | |||
| 26eb05ceeb | |||
| fee68cca18 | |||
| 11c951d29b | |||
| 72a5c4568e | |||
| eae2ea49a9 | |||
| 69596c83ec | |||
| bebb5b2861 | |||
| a5f0179d77 | |||
| 041f824caf | |||
| f088f0f9a5 |
2
.cargo/config
Normal file
2
.cargo/config
Normal file
@@ -0,0 +1,2 @@
|
||||
[target.'cfg(any(target_arch="x86",target_arch="x86_64"))']
|
||||
rustflags = ["-C","target-feature=+aes,+ssse3"]
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -11,3 +11,16 @@ Cargo.lock
|
||||
|
||||
# And these are just annoying
|
||||
.DS_Store
|
||||
.vscode
|
||||
|
||||
# Ignore testing files
|
||||
**/*.o
|
||||
**/*.hi
|
||||
**/gen
|
||||
**/.cabal-sandbox
|
||||
**/cabal.sandbox.config
|
||||
FlameGraph
|
||||
*.user_stacks
|
||||
**/.ghc.environment.*
|
||||
|
||||
test.ed25519
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "cryptonum"]
|
||||
path = cryptonum
|
||||
url = https://github.com/acw/cryptonum.git
|
||||
@@ -1,3 +1,9 @@
|
||||
language: rust
|
||||
rust:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
matrix:
|
||||
allow_failures:
|
||||
- nightly
|
||||
fast_finish: true
|
||||
|
||||
22
Cargo.toml
22
Cargo.toml
@@ -9,19 +9,23 @@ license-file = "LICENSE"
|
||||
repository = "https://github.com/acw/simple_crypto"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "^1.2.1"
|
||||
digest = "^0.7.1"
|
||||
num = "^0.1.39"
|
||||
rand = "^0.3"
|
||||
sha-1 = "^0.7.0"
|
||||
sha2 = "^0.7.0"
|
||||
simple_asn1 = "^0.1.0"
|
||||
base64 = "^0.10.1"
|
||||
byteorder = "^1.3.1"
|
||||
chrono = "^0.4.6"
|
||||
cryptonum = { path = "cryptonum" }
|
||||
num = "^0.2.0"
|
||||
rand = "^0.6.5"
|
||||
simple_asn1 = "^0.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = "^0.4.1"
|
||||
quickcheck = "^0.7.2"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
overflow-checks = false
|
||||
|
||||
[profile.test]
|
||||
opt-level = 2
|
||||
debug = true
|
||||
debug-assertions = true
|
||||
|
||||
overflow-checks = false
|
||||
|
||||
11
TECHNICAL_DEBT
Normal file
11
TECHNICAL_DEBT
Normal file
@@ -0,0 +1,11 @@
|
||||
- Add negative test cases (RSA, DSA, ECDSA)
|
||||
- Make Point::double_scalar_mult() not truly awful
|
||||
- Use std::Default instead of the bespoke default() in Point?
|
||||
- Run rustfmt on this stuff
|
||||
- Run clippy on this stuff
|
||||
- De-macro. Surely some of this stuff could be turned into trait invocations?
|
||||
- Test cases for key generation
|
||||
- Better, timing-resistant ECC point math
|
||||
- Make the x.509 library not terrible
|
||||
- Ability to generate a SSH public key line / file
|
||||
- Extend SSH examples with public key reading/writing
|
||||
1
cryptonum
Submodule
1
cryptonum
Submodule
Submodule cryptonum added at 666378b14b
412
src/aes/aesni.rs
Normal file
412
src/aes/aesni.rs
Normal file
@@ -0,0 +1,412 @@
|
||||
#[cfg(target_arch="x86")]
|
||||
use std::arch::x86::*;
|
||||
#[cfg(target_arch="x86_64")]
|
||||
use std::arch::x86_64::*;
|
||||
#[cfg(test)]
|
||||
use std::mem::transmute;
|
||||
use std::mem::uninitialized;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 128-Bit Support
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct AES128 {
|
||||
expanded_enc: [__m128i; 11],
|
||||
expanded_dec: [__m128i; 11],
|
||||
}
|
||||
|
||||
macro_rules! expand128 {
|
||||
($v: expr, $r: expr) => {{
|
||||
let gen0 = _mm_aeskeygenassist_si128($v, $r);
|
||||
let gen1 = _mm_shuffle_epi32(gen0, 0xff);
|
||||
let key0 = $v;
|
||||
let key1 = _mm_xor_si128(key0, _mm_slli_si128(key0, 4));
|
||||
let key2 = _mm_xor_si128(key1, _mm_slli_si128(key1, 4));
|
||||
let key3 = _mm_xor_si128(key2, _mm_slli_si128(key2, 4));
|
||||
_mm_xor_si128(gen1, key3)
|
||||
}};
|
||||
}
|
||||
|
||||
impl AES128 {
|
||||
pub fn new(base_key: &[u8]) -> AES128 {
|
||||
assert_eq!(base_key.len(), 16);
|
||||
unsafe {
|
||||
let mut expanded_enc: [__m128i; 11] = uninitialized();
|
||||
let mut expanded_dec: [__m128i; 11] = uninitialized();
|
||||
|
||||
let initial_m128 = _mm_loadu_si128(base_key.as_ptr() as *const __m128i);
|
||||
_mm_store_si128(expanded_enc.as_mut_ptr(), initial_m128);
|
||||
|
||||
expanded_enc[1] = expand128!(expanded_enc[0], 0x01);
|
||||
expanded_enc[2] = expand128!(expanded_enc[1], 0x02);
|
||||
expanded_enc[3] = expand128!(expanded_enc[2], 0x04);
|
||||
expanded_enc[4] = expand128!(expanded_enc[3], 0x08);
|
||||
expanded_enc[5] = expand128!(expanded_enc[4], 0x10);
|
||||
expanded_enc[6] = expand128!(expanded_enc[5], 0x20);
|
||||
expanded_enc[7] = expand128!(expanded_enc[6], 0x40);
|
||||
expanded_enc[8] = expand128!(expanded_enc[7], 0x80);
|
||||
expanded_enc[9] = expand128!(expanded_enc[8], 0x1B);
|
||||
expanded_enc[10] = expand128!(expanded_enc[9], 0x36);
|
||||
|
||||
expanded_dec[0] = expanded_enc[10];
|
||||
expanded_dec[1] = _mm_aesimc_si128(expanded_enc[9]);
|
||||
expanded_dec[2] = _mm_aesimc_si128(expanded_enc[8]);
|
||||
expanded_dec[3] = _mm_aesimc_si128(expanded_enc[7]);
|
||||
expanded_dec[4] = _mm_aesimc_si128(expanded_enc[6]);
|
||||
expanded_dec[5] = _mm_aesimc_si128(expanded_enc[5]);
|
||||
expanded_dec[6] = _mm_aesimc_si128(expanded_enc[4]);
|
||||
expanded_dec[7] = _mm_aesimc_si128(expanded_enc[3]);
|
||||
expanded_dec[8] = _mm_aesimc_si128(expanded_enc[2]);
|
||||
expanded_dec[9] = _mm_aesimc_si128(expanded_enc[1]);
|
||||
expanded_dec[10] = expanded_enc[0];
|
||||
|
||||
AES128 { expanded_enc, expanded_dec }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encrypt(&self, block: &[u8]) -> Vec<u8> {
|
||||
assert_eq!(block.len(), 16);
|
||||
let mut result = Vec::with_capacity(16);
|
||||
result.resize(16, 0);
|
||||
unsafe {
|
||||
let mut val = _mm_loadu_si128(block.as_ptr() as *const __m128i);
|
||||
val = _mm_xor_si128(val, self.expanded_enc[0]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[1]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[2]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[3]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[4]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[5]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[6]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[7]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[8]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[9]);
|
||||
val = _mm_aesenclast_si128(val, self.expanded_enc[10]);
|
||||
_mm_storeu_si128(result.as_mut_ptr() as *mut __m128i, val);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn decrypt(&self, block: &[u8]) -> Vec<u8> {
|
||||
assert_eq!(block.len(), 16);
|
||||
let mut result = Vec::with_capacity(16);
|
||||
result.resize(16, 0);
|
||||
unsafe {
|
||||
let mut val = _mm_loadu_si128(block.as_ptr() as *const __m128i);
|
||||
val = _mm_xor_si128(val, self.expanded_dec[0]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[1]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[2]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[3]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[4]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[5]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[6]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[7]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[8]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[9]);
|
||||
val = _mm_aesdeclast_si128(val, self.expanded_dec[10]);
|
||||
_mm_storeu_si128(result.as_mut_ptr() as *mut __m128i, val);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn unpack_m128(b: __m128i) -> (u64, u64)
|
||||
{
|
||||
unsafe {
|
||||
let data: [u64; 2] = transmute(b);
|
||||
(data[0].to_be(), data[1].to_be())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod aes128 {
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
|
||||
#[test]
|
||||
fn expansion() {
|
||||
let zero_key = AES128::new(&[0x00; 16]);
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[0]), (0x0000000000000000, 0x0000000000000000));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[1]), (0x6263636362636363, 0x6263636362636363));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[2]), (0x9b9898c9f9fbfbaa, 0x9b9898c9f9fbfbaa));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[3]), (0x90973450696ccffa, 0xf2f457330b0fac99));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[4]), (0xee06da7b876a1581, 0x759e42b27e91ee2b));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[5]), (0x7f2e2b88f8443e09, 0x8dda7cbbf34b9290));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[6]), (0xec614b851425758c, 0x99ff09376ab49ba7));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[7]), (0x217517873550620b, 0xacaf6b3cc61bf09b));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[8]), (0x0ef903333ba96138, 0x97060a04511dfa9f));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[9]), (0xb1d4d8e28a7db9da, 0x1d7bb3de4c664941));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[10]), (0xb4ef5bcb3e92e211, 0x23e951cf6f8f188e));
|
||||
let ff_key = AES128::new(&[0xff; 16]);
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[0]), (0xffffffffffffffff, 0xffffffffffffffff));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[1]), (0xe8e9e9e917161616, 0xe8e9e9e917161616));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[2]), (0xadaeae19bab8b80f, 0x525151e6454747f0));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[3]), (0x090e2277b3b69a78, 0xe1e7cb9ea4a08c6e));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[4]), (0xe16abd3e52dc2746, 0xb33becd8179b60b6));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[5]), (0xe5baf3ceb766d488, 0x045d385013c658e6));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[6]), (0x71d07db3c6b6a93b, 0xc2eb916bd12dc98d));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[7]), (0xe90d208d2fbb89b6, 0xed5018dd3c7dd150));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[8]), (0x96337366b988fad0, 0x54d8e20d68a5335d));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[9]), (0x8bf03f233278c5f3, 0x66a027fe0e0514a3));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[10]), (0xd60a3588e472f07b, 0x82d2d7858cd7c326));
|
||||
let nist_key = AES128::new(&[0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c]);
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[0]), (0x2b7e151628aed2a6, 0xabf7158809cf4f3c));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[1]), (0xa0fafe1788542cb1, 0x23a339392a6c7605));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[2]), (0xf2c295f27a96b943, 0x5935807a7359f67f));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[3]), (0x3d80477d4716fe3e, 0x1e237e446d7a883b));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[4]), (0xef44a541a8525b7f, 0xb671253bdb0bad00));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[5]), (0xd4d1c6f87c839d87, 0xcaf2b8bc11f915bc));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[6]), (0x6d88a37a110b3efd, 0xdbf98641ca0093fd));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[7]), (0x4e54f70e5f5fc9f3, 0x84a64fb24ea6dc4f));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[8]), (0xead27321b58dbad2, 0x312bf5607f8d292f));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[9]), (0xac7766f319fadc21, 0x28d12941575c006e));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[10]), (0xd014f9a8c9ee2589, 0xe13f0cc8b6630ca6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fips197_examples() {
|
||||
let input1 = [0x32,0x43,0xf6,0xa8,0x88,0x5a,0x30,0x8d,0x31,0x31,0x98,0xa2,0xe0,0x37,0x07,0x34];
|
||||
let key1 = [0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c];
|
||||
let aeskey1 = AES128::new(&key1);
|
||||
let cipher1 = aeskey1.encrypt(&input1);
|
||||
assert_eq!(cipher1, vec![0x39,0x25,0x84,0x1d,0x02,0xdc,0x09,0xfb,
|
||||
0xdc,0x11,0x85,0x97,0x19,0x6a,0x0b,0x32]);
|
||||
assert_eq!(input1.to_vec(), aeskey1.decrypt(&cipher1));
|
||||
let input2 = [0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff];
|
||||
let key2 = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f];
|
||||
let aeskey2 = AES128::new(&key2);
|
||||
let cipher2 = aeskey2.encrypt(&input2);
|
||||
assert_eq!(cipher2, vec![0x69,0xc4,0xe0,0xd8,0x6a,0x7b,0x04,0x30,
|
||||
0xd8,0xcd,0xb7,0x80,0x70,0xb4,0xc5,0x5a]);
|
||||
assert_eq!(input2.to_vec(), aeskey2.decrypt(&cipher2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nist_test_vectors() {
|
||||
let fname = "testdata/aes/aes128.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negk, kbytes) = case.get("k").unwrap();
|
||||
let (negp, pbytes) = case.get("p").unwrap();
|
||||
let (negc, cbytes) = case.get("c").unwrap();
|
||||
|
||||
assert!(!negk && !negp && !negc);
|
||||
let key = AES128::new(&kbytes);
|
||||
let cipher = key.encrypt(&pbytes);
|
||||
let plain = key.decrypt(&cipher);
|
||||
assert_eq!(&cipher, cbytes);
|
||||
assert_eq!(&plain, pbytes);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 256-Bit Support
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct AES256 {
|
||||
expanded_enc: [__m128i; 15],
|
||||
expanded_dec: [__m128i; 15],
|
||||
}
|
||||
|
||||
macro_rules! expand256 {
|
||||
($prevprev: expr, $prev: expr, $shuffle: expr, $round: expr) => {{
|
||||
let gen0 = _mm_aeskeygenassist_si128($prev, $round);
|
||||
let gen1 = _mm_shuffle_epi32(gen0, $shuffle);
|
||||
let key0 = $prevprev;
|
||||
let key1 = _mm_xor_si128(key0, _mm_slli_si128(key0, 4));
|
||||
let key2 = _mm_xor_si128(key1, _mm_slli_si128(key1, 4));
|
||||
let key3 = _mm_xor_si128(key2, _mm_slli_si128(key2, 4));
|
||||
_mm_xor_si128(gen1, key3)
|
||||
}};
|
||||
}
|
||||
|
||||
impl AES256 {
|
||||
pub fn new(base_key: &[u8]) -> AES256 {
|
||||
assert_eq!(base_key.len(), 32);
|
||||
unsafe {
|
||||
let mut expanded_enc: [__m128i; 15] = uninitialized();
|
||||
let mut expanded_dec: [__m128i; 15] = uninitialized();
|
||||
|
||||
let keyptr = base_key.as_ptr() as *const __m128i;
|
||||
expanded_enc[00] = _mm_loadu_si128(keyptr.offset(0));
|
||||
expanded_enc[01] = _mm_loadu_si128(keyptr.offset(1));
|
||||
expanded_enc[02] = expand256!(expanded_enc[00], expanded_enc[01], 0xff, 0x01);
|
||||
expanded_enc[03] = expand256!(expanded_enc[01], expanded_enc[02], 0xaa, 0x00);
|
||||
expanded_enc[04] = expand256!(expanded_enc[02], expanded_enc[03], 0xff, 0x02);
|
||||
expanded_enc[05] = expand256!(expanded_enc[03], expanded_enc[04], 0xaa, 0x00);
|
||||
expanded_enc[06] = expand256!(expanded_enc[04], expanded_enc[05], 0xff, 0x04);
|
||||
expanded_enc[07] = expand256!(expanded_enc[05], expanded_enc[06], 0xaa, 0x00);
|
||||
expanded_enc[08] = expand256!(expanded_enc[06], expanded_enc[07], 0xff, 0x08);
|
||||
expanded_enc[09] = expand256!(expanded_enc[07], expanded_enc[08], 0xaa, 0x00);
|
||||
expanded_enc[10] = expand256!(expanded_enc[08], expanded_enc[09], 0xff, 0x10);
|
||||
expanded_enc[11] = expand256!(expanded_enc[09], expanded_enc[10], 0xaa, 0x00);
|
||||
expanded_enc[12] = expand256!(expanded_enc[10], expanded_enc[11], 0xff, 0x20);
|
||||
expanded_enc[13] = expand256!(expanded_enc[11], expanded_enc[12], 0xaa, 0x00);
|
||||
expanded_enc[14] = expand256!(expanded_enc[12], expanded_enc[13], 0xff, 0x40);
|
||||
|
||||
expanded_dec[00] = expanded_enc[14];
|
||||
expanded_dec[01] = _mm_aesimc_si128(expanded_enc[13]);
|
||||
expanded_dec[02] = _mm_aesimc_si128(expanded_enc[12]);
|
||||
expanded_dec[03] = _mm_aesimc_si128(expanded_enc[11]);
|
||||
expanded_dec[04] = _mm_aesimc_si128(expanded_enc[10]);
|
||||
expanded_dec[05] = _mm_aesimc_si128(expanded_enc[09]);
|
||||
expanded_dec[06] = _mm_aesimc_si128(expanded_enc[08]);
|
||||
expanded_dec[07] = _mm_aesimc_si128(expanded_enc[07]);
|
||||
expanded_dec[08] = _mm_aesimc_si128(expanded_enc[06]);
|
||||
expanded_dec[09] = _mm_aesimc_si128(expanded_enc[05]);
|
||||
expanded_dec[10] = _mm_aesimc_si128(expanded_enc[04]);
|
||||
expanded_dec[11] = _mm_aesimc_si128(expanded_enc[03]);
|
||||
expanded_dec[12] = _mm_aesimc_si128(expanded_enc[02]);
|
||||
expanded_dec[13] = _mm_aesimc_si128(expanded_enc[01]);
|
||||
expanded_dec[14] = expanded_enc[0];
|
||||
|
||||
AES256{ expanded_enc, expanded_dec }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encrypt(&self, block: &[u8]) -> Vec<u8> {
|
||||
assert_eq!(block.len(), 16);
|
||||
let mut result = Vec::with_capacity(16);
|
||||
result.resize(16, 0);
|
||||
unsafe {
|
||||
let mut val = _mm_loadu_si128(block.as_ptr() as *const __m128i);
|
||||
val = _mm_xor_si128(val, self.expanded_enc[0]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[01]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[02]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[03]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[04]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[05]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[06]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[07]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[08]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[09]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[10]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[11]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[12]);
|
||||
val = _mm_aesenc_si128(val, self.expanded_enc[13]);
|
||||
val = _mm_aesenclast_si128(val, self.expanded_enc[14]);
|
||||
_mm_storeu_si128(result.as_mut_ptr() as *mut __m128i, val);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn decrypt(&self, block: &[u8]) -> Vec<u8> {
|
||||
assert_eq!(block.len(), 16);
|
||||
let mut result = Vec::with_capacity(16);
|
||||
result.resize(16, 0);
|
||||
unsafe {
|
||||
let mut val = _mm_loadu_si128(block.as_ptr() as *const __m128i);
|
||||
val = _mm_xor_si128(val, self.expanded_dec[00]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[01]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[02]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[03]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[04]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[05]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[06]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[07]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[08]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[09]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[10]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[11]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[12]);
|
||||
val = _mm_aesdec_si128(val, self.expanded_dec[13]);
|
||||
val = _mm_aesdeclast_si128(val, self.expanded_dec[14]);
|
||||
_mm_storeu_si128(result.as_mut_ptr() as *mut __m128i, val);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod aes256 {
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
|
||||
#[test]
|
||||
fn expansion() {
|
||||
let zero_key = AES256::new(&[0x00; 32]);
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[00]), (0x0000000000000000, 0x0000000000000000));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[01]), (0x0000000000000000, 0x0000000000000000));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[02]), (0x6263636362636363, 0x6263636362636363));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[03]), (0xaafbfbfbaafbfbfb, 0xaafbfbfbaafbfbfb));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[04]), (0x6f6c6ccf0d0f0fac, 0x6f6c6ccf0d0f0fac));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[05]), (0x7d8d8d6ad7767691, 0x7d8d8d6ad7767691));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[06]), (0x5354edc15e5be26d, 0x31378ea23c38810e));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[07]), (0x968a81c141fcf750, 0x3c717a3aeb070cab));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[08]), (0x9eaa8f28c0f16d45, 0xf1c6e3e7cdfe62e9));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[09]), (0x2b312bdf6acddc8f, 0x56bca6b5bdbbaa1e));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[10]), (0x6406fd52a4f79017, 0x553173f098cf1119));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[11]), (0x6dbba90b07767584, 0x51cad331ec71792f));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[12]), (0xe7b0e89c4347788b, 0x16760b7b8eb91a62));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[13]), (0x74ed0ba1739b7e25, 0x2251ad14ce20d43b));
|
||||
assert_eq!(unpack_m128(zero_key.expanded_enc[14]), (0x10f80a1753bf729c, 0x45c979e7cb706385));
|
||||
let ff_key = AES256::new(&[0xff; 32]);
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[00]), (0xffffffffffffffff, 0xffffffffffffffff));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[01]), (0xffffffffffffffff, 0xffffffffffffffff));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[02]), (0xe8e9e9e917161616, 0xe8e9e9e917161616));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[03]), (0x0fb8b8b8f0474747, 0x0fb8b8b8f0474747));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[04]), (0x4a4949655d5f5f73, 0xb5b6b69aa2a0a08c));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[05]), (0x355858dcc51f1f9b, 0xcaa7a7233ae0e064));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[06]), (0xafa80ae5f2f75596, 0x4741e30ce5e14380));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[07]), (0xeca0421129bf5d8a, 0xe318faa9d9f81acd));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[08]), (0xe60ab7d014fde246, 0x53bc014ab65d42ca));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[09]), (0xa2ec6e658b5333ef, 0x684bc946b1b3d38b));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[10]), (0x9b6c8a188f91685e, 0xdc2d69146a702bde));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[11]), (0xa0bd9f782beeac97, 0x43a565d1f216b65a));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[12]), (0xfc22349173b35ccf, 0xaf9e35dbc5ee1e05));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[13]), (0x0695ed132d7b4184, 0x6ede24559cc8920f));
|
||||
assert_eq!(unpack_m128(ff_key.expanded_enc[14]), (0x546d424f27de1e80, 0x88402b5b4dae355e));
|
||||
let nist_key = AES256::new(&[0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
|
||||
0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
|
||||
0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
|
||||
0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4]);
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[00]), (0x603deb1015ca71be, 0x2b73aef0857d7781));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[01]), (0x1f352c073b6108d7, 0x2d9810a30914dff4));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[02]), (0x9ba354118e6925af, 0xa51a8b5f2067fcde));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[03]), (0xa8b09c1a93d194cd, 0xbe49846eb75d5b9a));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[04]), (0xd59aecb85bf3c917, 0xfee94248de8ebe96));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[05]), (0xb5a9328a2678a647, 0x983122292f6c79b3));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[06]), (0x812c81addadf48ba, 0x24360af2fab8b464));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[07]), (0x98c5bfc9bebd198e, 0x268c3ba709e04214));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[08]), (0x68007bacb2df3316, 0x96e939e46c518d80));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[09]), (0xc814e20476a9fb8a, 0x5025c02d59c58239));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[10]), (0xde1369676ccc5a71, 0xfa2563959674ee15));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[11]), (0x5886ca5d2e2f31d7, 0x7e0af1fa27cf73c3));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[12]), (0x749c47ab18501dda, 0xe2757e4f7401905a));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[13]), (0xcafaaae3e4d59b34, 0x9adf6acebd10190d));
|
||||
assert_eq!(unpack_m128(nist_key.expanded_enc[14]), (0xfe4890d1e6188d0b, 0x046df344706c631e));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fips197_example() {
|
||||
let input = [0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff];
|
||||
let key = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
|
||||
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f];
|
||||
let aeskey = AES256::new(&key);
|
||||
let cipher = aeskey.encrypt(&input);
|
||||
assert_eq!(cipher, vec![0x8e,0xa2,0xb7,0xca,0x51,0x67,0x45,0xbf,
|
||||
0xea,0xfc,0x49,0x90,0x4b,0x49,0x60,0x89]);
|
||||
assert_eq!(input.to_vec(), aeskey.decrypt(&cipher));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nist_test_vectors() {
|
||||
let fname = "testdata/aes/aes256.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negk, kbytes) = case.get("k").unwrap();
|
||||
let (negp, pbytes) = case.get("p").unwrap();
|
||||
let (negc, cbytes) = case.get("c").unwrap();
|
||||
|
||||
assert!(!negk && !negp && !negc);
|
||||
let key = AES256::new(&kbytes);
|
||||
let cipher = key.encrypt(&pbytes);
|
||||
let plain = key.decrypt(&cipher);
|
||||
assert_eq!(&cipher, cbytes);
|
||||
assert_eq!(&plain, pbytes);
|
||||
});
|
||||
}
|
||||
}
|
||||
205
src/aes/mod.rs
Normal file
205
src/aes/mod.rs
Normal file
@@ -0,0 +1,205 @@
|
||||
#[cfg(all(any(target_arch="x86", target_arch="x86_64"),
|
||||
target_feature = "aes"))]
|
||||
pub mod aesni;
|
||||
pub mod portable;
|
||||
|
||||
#[cfg(all(target_arch="x86", target_feature = "aes"))]
|
||||
use std::arch::x86::__cpuid;
|
||||
#[cfg(all(target_arch="x86_64", target_feature = "aes"))]
|
||||
use std::arch::x86_64::__cpuid;
|
||||
|
||||
/// This is the type to use for an AES128 key. The new() routine will select
|
||||
/// an accelerated routine, at runtime, if one is available. Otherwise, it
|
||||
/// will use a slower, portable routine.
|
||||
pub enum AES128 {
|
||||
Portable(portable::AES128),
|
||||
#[cfg(all(any(target_arch="x86", target_arch="x86_64"),
|
||||
target_feature = "aes"))]
|
||||
Accelerated(aesni::AES128),
|
||||
}
|
||||
|
||||
#[cfg(all(any(target_arch="x86", target_arch="x86_64"),
|
||||
target_feature = "aes"))]
|
||||
fn test_aesni() -> bool {
|
||||
let result = unsafe { __cpuid(1) }; // 1 == processor features
|
||||
(result.edx & 0b00000010000000000000000000000000) != 0
|
||||
}
|
||||
|
||||
impl AES128 {
|
||||
/// Returns true iff this platform has an acceleration system we support.
|
||||
pub fn accelerated() -> bool {
|
||||
#[cfg(all(any(target_arch="x86", target_arch="x86_64"),
|
||||
target_feature = "aes"))]
|
||||
return test_aesni();
|
||||
#[allow(unreachable_code)]
|
||||
false
|
||||
}
|
||||
|
||||
/// Generate a new AES128 object from the given key. This routine does a
|
||||
/// dynamic check of `AES128::accelerated()` to see if it can be
|
||||
/// accelerated; this means that you'll get acceleration where you can,
|
||||
/// and a safe default where you can't.
|
||||
pub fn new(key: [u8; 16]) -> AES128 {
|
||||
#[cfg(all(any(target_arch="x86", target_arch="x86_64"),
|
||||
target_feature = "aes"))]
|
||||
return if AES128::accelerated() {
|
||||
AES128::Accelerated(aesni::AES128::new(&key))
|
||||
} else {
|
||||
AES128::Portable(portable::AES128::new(&key))
|
||||
};
|
||||
#[allow(unreachable_code)]
|
||||
AES128::Portable(portable::AES128::new(&key))
|
||||
}
|
||||
|
||||
/// Encrypt the given block. This *must* be exactly 16 bytes long.
|
||||
pub fn encrypt(&self, block: &[u8]) -> Vec<u8> {
|
||||
match self {
|
||||
AES128::Portable(ref key) => key.encrypt(block),
|
||||
#[cfg(all(any(target_arch="x86", target_arch="x86_64"),
|
||||
target_feature = "aes"))]
|
||||
AES128::Accelerated(ref key) => key.encrypt(block),
|
||||
}
|
||||
}
|
||||
|
||||
/// Decrypt the given block. This *must* be exactly 16 bytes long.
|
||||
pub fn decrypt(&self, block: &[u8]) -> Vec<u8> {
|
||||
match self {
|
||||
AES128::Portable(ref key) => key.decrypt(block),
|
||||
#[cfg(all(any(target_arch="x86", target_arch="x86_64"),
|
||||
target_feature = "aes"))]
|
||||
AES128::Accelerated(ref key) => key.decrypt(block),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the type to use for an AES128 key. The new() routine will select
|
||||
/// an accelerated routine, at runtime, if one is available. Otherwise, it
|
||||
/// will use a slower, portable routine.
|
||||
pub enum AES256 {
|
||||
Portable(portable::AES256),
|
||||
#[cfg(all(any(target_arch="x86", target_arch="x86_64"),
|
||||
target_feature = "aes"))]
|
||||
Accelerated(aesni::AES256),
|
||||
}
|
||||
|
||||
impl AES256 {
|
||||
/// Returns true iff this platform has an acceleration system we support.
|
||||
pub fn accelerated() -> bool {
|
||||
#[cfg(all(any(target_arch="x86", target_arch="x86_64"),
|
||||
target_feature = "aes"))]
|
||||
return test_aesni();
|
||||
#[allow(unreachable_code)]
|
||||
false
|
||||
}
|
||||
|
||||
/// Generate a new AES256 object from the given key. This routine does a
|
||||
/// dynamic check of `AES256::accelerated()` to see if it can be
|
||||
/// accelerated; this means that you'll get acceleration where you can,
|
||||
/// and a safe default where you can't.
|
||||
pub fn new(key: [u8; 32]) -> AES256 {
|
||||
#[cfg(all(any(target_arch="x86", target_arch="x86_64"),
|
||||
target_feature = "aes"))]
|
||||
return if AES256::accelerated() {
|
||||
AES256::Accelerated(aesni::AES256::new(&key))
|
||||
} else {
|
||||
AES256::Portable(portable::AES256::new(&key))
|
||||
};
|
||||
#[allow(unreachable_code)]
|
||||
AES256::Portable(portable::AES256::new(&key))
|
||||
}
|
||||
|
||||
/// Encrypt the given block. This *must* be exactly 16 bytes long.
|
||||
pub fn encrypt(&self, block: &[u8]) -> Vec<u8> {
|
||||
match self {
|
||||
AES256::Portable(ref key) => key.encrypt(block),
|
||||
#[cfg(all(any(target_arch="x86", target_arch="x86_64"),
|
||||
target_feature = "aes"))]
|
||||
AES256::Accelerated(ref key) => key.encrypt(block),
|
||||
}
|
||||
}
|
||||
|
||||
/// Decrypt the given block. This *must* be exactly 16 bytes long.
|
||||
pub fn decrypt(&self, block: &[u8]) -> Vec<u8> {
|
||||
match self {
|
||||
AES256::Portable(ref key) => key.decrypt(block),
|
||||
#[cfg(all(any(target_arch="x86", target_arch="x86_64"),
|
||||
target_feature = "aes"))]
|
||||
AES256::Accelerated(ref key) => key.decrypt(block),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(any(target_arch="x86", target_arch="x86_64"),
|
||||
target_feature = "aes",
|
||||
test))]
|
||||
mod flexible {
|
||||
use super::{AES128,AES256};
|
||||
use super::aesni;
|
||||
use super::portable;
|
||||
use super::portable::aes256::{RandomBlock,RandomKey};
|
||||
use testing::run_test;
|
||||
|
||||
quickcheck! {
|
||||
fn aes128_implementations_match(key: RandomBlock, block: RandomBlock) -> bool {
|
||||
let aesni_key = aesni::AES128::new(&key.block);
|
||||
let portable_key = portable::AES128::new(&key.block);
|
||||
let aesni_cipher = aesni_key.encrypt(&block.block);
|
||||
let portable_cipher = portable_key.encrypt(&block.block);
|
||||
aesni_cipher == portable_cipher
|
||||
}
|
||||
|
||||
fn aes256_implementations_match(key: RandomKey, block: RandomBlock) -> bool {
|
||||
let aesni_key = aesni::AES256::new(&key.key);
|
||||
let portable_key = portable::AES256::new(&key.key);
|
||||
let aesni_cipher = aesni_key.encrypt(&block.block);
|
||||
let portable_cipher = portable_key.encrypt(&block.block);
|
||||
aesni_cipher == portable_cipher
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aes128_nist_test_vectors() {
|
||||
let fname = "testdata/aes/aes128.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negk, kbytes) = case.get("k").unwrap();
|
||||
let (negp, pbytes) = case.get("p").unwrap();
|
||||
let (negc, cbytes) = case.get("c").unwrap();
|
||||
|
||||
assert!(!negk && !negp && !negc);
|
||||
let keyval = [kbytes[00], kbytes[01], kbytes[02], kbytes[03],
|
||||
kbytes[04], kbytes[05], kbytes[06], kbytes[07],
|
||||
kbytes[08], kbytes[09], kbytes[10], kbytes[11],
|
||||
kbytes[12], kbytes[13], kbytes[14], kbytes[15]];
|
||||
let key = AES128::new(keyval);
|
||||
let cipher = key.encrypt(&pbytes);
|
||||
let plain = key.decrypt(&cipher);
|
||||
assert_eq!(&cipher, cbytes);
|
||||
assert_eq!(&plain, pbytes);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aes256_nist_test_vectors() {
|
||||
let fname = "testdata/aes/aes256.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negk, kbytes) = case.get("k").unwrap();
|
||||
let (negp, pbytes) = case.get("p").unwrap();
|
||||
let (negc, cbytes) = case.get("c").unwrap();
|
||||
|
||||
assert!(!negk && !negp && !negc);
|
||||
let keyval = [kbytes[00], kbytes[01], kbytes[02], kbytes[03],
|
||||
kbytes[04], kbytes[05], kbytes[06], kbytes[07],
|
||||
kbytes[08], kbytes[09], kbytes[10], kbytes[11],
|
||||
kbytes[12], kbytes[13], kbytes[14], kbytes[15],
|
||||
kbytes[16], kbytes[17], kbytes[18], kbytes[19],
|
||||
kbytes[20], kbytes[21], kbytes[22], kbytes[23],
|
||||
kbytes[24], kbytes[25], kbytes[26], kbytes[27],
|
||||
kbytes[28], kbytes[29], kbytes[30], kbytes[31]];
|
||||
let key = AES256::new(keyval);
|
||||
let cipher = key.encrypt(&pbytes);
|
||||
let plain = key.decrypt(&cipher);
|
||||
assert_eq!(&cipher, cbytes);
|
||||
assert_eq!(&plain, pbytes);
|
||||
});
|
||||
}
|
||||
}
|
||||
835
src/aes/portable.rs
Normal file
835
src/aes/portable.rs
Normal file
@@ -0,0 +1,835 @@
|
||||
const RIJNDAEL_KEY_SCHEDULE: [u32; 11] = [
|
||||
0x00000000, 0x01000000, 0x02000000, 0x04000000,
|
||||
0x08000000, 0x10000000, 0x20000000, 0x40000000,
|
||||
0x80000000, 0x1b000000, 0x36000000,
|
||||
];
|
||||
|
||||
const SUB_BYTES_SBOX: [u8; 256] = [
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
||||
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
||||
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
||||
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
||||
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
|
||||
];
|
||||
|
||||
const INVSUB_BYTES_SBOX: [u8; 256] = [
|
||||
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
|
||||
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
|
||||
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
||||
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
|
||||
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
|
||||
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
||||
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
|
||||
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
|
||||
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
||||
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
|
||||
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
|
||||
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
||||
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
|
||||
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
|
||||
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
||||
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
|
||||
];
|
||||
|
||||
fn word(a: u8, b: u8, c: u8, d: u8) -> u32 {
|
||||
((a as u32) << 24) | ((b as u32) << 16) |
|
||||
((c as u32) << 08) | ((d as u32) << 00)
|
||||
}
|
||||
|
||||
fn rot_word(x: u32) -> u32 {
|
||||
x.rotate_left(8)
|
||||
}
|
||||
|
||||
fn sub_word(x: u32) -> u32 {
|
||||
(((SUB_BYTES_SBOX[((x >> 24) & 0xff) as usize]) as u32) << 24) |
|
||||
(((SUB_BYTES_SBOX[((x >> 16) & 0xff) as usize]) as u32) << 16) |
|
||||
(((SUB_BYTES_SBOX[((x >> 08) & 0xff) as usize]) as u32) << 08) |
|
||||
(((SUB_BYTES_SBOX[((x >> 00) & 0xff) as usize]) as u32) << 00)
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
/* */
|
||||
/* AES State */
|
||||
/* */
|
||||
/**************************************************************************************************/
|
||||
|
||||
struct AESState {
|
||||
state: [[u8; 4]; 4]
|
||||
}
|
||||
|
||||
macro_rules! xtime {
|
||||
($e: expr) => {{
|
||||
let base: u8 = $e;
|
||||
let dbl = base << 1;
|
||||
let high = base & 0x80;
|
||||
let xorval = if high == 0x80 { 0x1b } else { 0x00 };
|
||||
dbl ^ xorval
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! field_09 {
|
||||
($e: expr) => {{
|
||||
let base = $e;
|
||||
base ^ xtime!(xtime!(xtime!(base)))
|
||||
}}
|
||||
}
|
||||
|
||||
macro_rules! field_0b {
|
||||
($e: expr) => {{
|
||||
let base = $e;
|
||||
// 1 + 8 + 2 = 11 = 0xb
|
||||
base ^ xtime!(xtime!(xtime!(base))) ^ xtime!(base)
|
||||
}}
|
||||
}
|
||||
|
||||
macro_rules! field_0d {
|
||||
($e: expr) => {{
|
||||
let base = $e;
|
||||
// 1 + 8 + 4 = 13 = 0xd
|
||||
base ^ xtime!(xtime!(xtime!(base))) ^ xtime!(xtime!(base))
|
||||
}}
|
||||
}
|
||||
|
||||
macro_rules! field_0e {
|
||||
($e: expr) => {{
|
||||
let base = $e;
|
||||
// 2 + 8 + 4 = 14 = 0xe
|
||||
xtime!(base) ^ xtime!(xtime!(xtime!(base))) ^ xtime!(xtime!(base))
|
||||
}}
|
||||
}
|
||||
|
||||
impl AESState {
|
||||
fn new(inkey: &[u8]) -> AESState {
|
||||
assert_eq!(inkey.len(), 16);
|
||||
AESState {
|
||||
state: [[inkey[00], inkey[04], inkey[08], inkey[12]],
|
||||
[inkey[01], inkey[05], inkey[09], inkey[13]],
|
||||
[inkey[02], inkey[06], inkey[10], inkey[14]],
|
||||
[inkey[03], inkey[07], inkey[11], inkey[15]]]
|
||||
}
|
||||
}
|
||||
|
||||
// fn print_state(&self) {
|
||||
// println!("{:02x} {:02x} {:02x} {:02x}", self.state[0][0], self.state[0][1], self.state[0][2], self.state[0][3]);
|
||||
// println!("{:02x} {:02x} {:02x} {:02x}", self.state[1][0], self.state[1][1], self.state[1][2], self.state[1][3]);
|
||||
// println!("{:02x} {:02x} {:02x} {:02x}", self.state[2][0], self.state[2][1], self.state[2][2], self.state[2][3]);
|
||||
// println!("{:02x} {:02x} {:02x} {:02x}", self.state[3][0], self.state[3][1], self.state[3][2], self.state[3][3]);
|
||||
// println!("-----------");
|
||||
// }
|
||||
|
||||
fn add_round_key(&mut self, w: &[u32]) {
|
||||
assert_eq!(w.len(), 4);
|
||||
for i in 0..4 {
|
||||
self.state[0][i] ^= (w[i] >> 24) as u8;
|
||||
self.state[1][i] ^= (w[i] >> 16) as u8;
|
||||
self.state[2][i] ^= (w[i] >> 08) as u8;
|
||||
self.state[3][i] ^= (w[i] >> 00) as u8;
|
||||
}
|
||||
}
|
||||
|
||||
fn sub_bytes(&mut self) {
|
||||
for i in 0..4 {
|
||||
for j in 0..4 {
|
||||
self.state[i][j] = SUB_BYTES_SBOX[self.state[i][j] as usize];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn inv_sub_bytes(&mut self) {
|
||||
for i in 0..4 {
|
||||
for j in 0..4 {
|
||||
self.state[i][j] = INVSUB_BYTES_SBOX[self.state[i][j] as usize];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shift_rows(&mut self) {
|
||||
let temp1 = self.state[1][0];
|
||||
self.state[1][0] = self.state[1][1];
|
||||
self.state[1][1] = self.state[1][2];
|
||||
self.state[1][2] = self.state[1][3];
|
||||
self.state[1][3] = temp1;
|
||||
let temp2a = self.state[2][0];
|
||||
let temp2b = self.state[2][1];
|
||||
self.state[2][0] = self.state[2][2];
|
||||
self.state[2][1] = self.state[2][3];
|
||||
self.state[2][2] = temp2a;
|
||||
self.state[2][3] = temp2b;
|
||||
let temp3 = self.state[3][3];
|
||||
self.state[3][3] = self.state[3][2];
|
||||
self.state[3][2] = self.state[3][1];
|
||||
self.state[3][1] = self.state[3][0];
|
||||
self.state[3][0] = temp3;
|
||||
}
|
||||
|
||||
fn inv_shift_rows(&mut self) {
|
||||
let temp1 = self.state[1][3];
|
||||
self.state[1][3] = self.state[1][2];
|
||||
self.state[1][2] = self.state[1][1];
|
||||
self.state[1][1] = self.state[1][0];
|
||||
self.state[1][0] = temp1;
|
||||
let temp2a = self.state[2][0];
|
||||
let temp2b = self.state[2][1];
|
||||
self.state[2][0] = self.state[2][2];
|
||||
self.state[2][1] = self.state[2][3];
|
||||
self.state[2][2] = temp2a;
|
||||
self.state[2][3] = temp2b;
|
||||
let temp3 = self.state[3][0];
|
||||
self.state[3][0] = self.state[3][1];
|
||||
self.state[3][1] = self.state[3][2];
|
||||
self.state[3][2] = self.state[3][3];
|
||||
self.state[3][3] = temp3;
|
||||
}
|
||||
|
||||
fn mix_columns(&mut self) {
|
||||
for c in 0..4 {
|
||||
// get the base values
|
||||
let s0c = self.state[0][c];
|
||||
let s1c = self.state[1][c];
|
||||
let s2c = self.state[2][c];
|
||||
let s3c = self.state[3][c];
|
||||
|
||||
// get the doubled values, forced to be within the field
|
||||
let d0c = xtime!(s0c);
|
||||
let d1c = xtime!(s1c);
|
||||
let d2c = xtime!(s2c);
|
||||
let d3c = xtime!(s3c);
|
||||
|
||||
self.state[0][c] = d0c ^ d1c ^ s2c ^ s3c ^ s1c;
|
||||
self.state[1][c] = s0c ^ d1c ^ d2c ^ s3c ^ s2c;
|
||||
self.state[2][c] = s0c ^ s1c ^ d2c ^ d3c ^ s3c;
|
||||
self.state[3][c] = d0c ^ s1c ^ s2c ^ d3c ^ s0c;
|
||||
}
|
||||
}
|
||||
|
||||
fn inv_mix_columns(&mut self) {
|
||||
for c in 0..4 {
|
||||
// get the base values
|
||||
let s0c = self.state[0][c];
|
||||
let s1c = self.state[1][c];
|
||||
let s2c = self.state[2][c];
|
||||
let s3c = self.state[3][c];
|
||||
|
||||
self.state[0][c] = field_0e!(s0c) ^ field_0b!(s1c) ^ field_0d!(s2c) ^ field_09!(s3c);
|
||||
self.state[1][c] = field_09!(s0c) ^ field_0e!(s1c) ^ field_0b!(s2c) ^ field_0d!(s3c);
|
||||
self.state[2][c] = field_0d!(s0c) ^ field_09!(s1c) ^ field_0e!(s2c) ^ field_0b!(s3c);
|
||||
self.state[3][c] = field_0b!(s0c) ^ field_0d!(s1c) ^ field_09!(s2c) ^ field_0e!(s3c);
|
||||
}
|
||||
}
|
||||
|
||||
fn decant(&self) -> Vec<u8> {
|
||||
vec![self.state[0][0], self.state[1][0], self.state[2][0], self.state[3][0],
|
||||
self.state[0][1], self.state[1][1], self.state[2][1], self.state[3][1],
|
||||
self.state[0][2], self.state[1][2], self.state[2][2], self.state[3][2],
|
||||
self.state[0][3], self.state[1][3], self.state[2][3], self.state[3][3],
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod state {
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use std::fmt;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn xtime_works() {
|
||||
assert_eq!(xtime!(0x57), 0xae);
|
||||
assert_eq!(xtime!(0xae), 0x47);
|
||||
assert_eq!(xtime!(0x47), 0x8e);
|
||||
assert_eq!(xtime!(0x8e), 0x07);
|
||||
assert_eq!(xtime!(xtime!(0x57)), 0x47);
|
||||
}
|
||||
|
||||
impl PartialEq for AESState {
|
||||
fn eq(&self, other: &AESState) -> bool {
|
||||
for i in 0..4 {
|
||||
for j in 0..4 {
|
||||
if self.state[i][j] != other.state[i][j] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AESState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "[{:02x} {:02x} {:02x} {:02x}", self.state[0][0], self.state[0][1], self.state[0][2], self.state[0][3])?;
|
||||
write!(f, " {:02x} {:02x} {:02x} {:02x}", self.state[1][0], self.state[1][1], self.state[1][2], self.state[1][3])?;
|
||||
write!(f, " {:02x} {:02x} {:02x} {:02x}", self.state[2][0], self.state[2][1], self.state[2][2], self.state[2][3])?;
|
||||
write!(f, " {:02x} {:02x} {:02x} {:02x}]", self.state[3][0], self.state[3][1], self.state[3][2], self.state[3][3])
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fips197_add_round_example() {
|
||||
let mut input = AESState{ state: [[0x32, 0x88, 0x31, 0xe0],
|
||||
[0x43, 0x5a, 0x31, 0x37],
|
||||
[0xf6, 0x30, 0x98, 0x07],
|
||||
[0xa8, 0x8d, 0xa2, 0x34]] };
|
||||
let key = [0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c];
|
||||
let output = AESState{ state: [[0x19, 0xa0, 0x9a, 0xe9],
|
||||
[0x3d, 0xf4, 0xc6, 0xf8],
|
||||
[0xe3, 0xe2, 0x8d, 0x48],
|
||||
[0xbe, 0x2b, 0x2a, 0x08]] };
|
||||
input.add_round_key(&key);
|
||||
assert_eq!(input, output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fips197_sub_bytes_example() {
|
||||
let mut input = AESState{ state: [[0x19, 0xa0, 0x9a, 0xe9],
|
||||
[0x3d, 0xf4, 0xc6, 0xf8],
|
||||
[0xe3, 0xe2, 0x8d, 0x48],
|
||||
[0xbe, 0x2b, 0x2a, 0x08]] };
|
||||
let output = AESState{ state: [[0xd4, 0xe0, 0xb8, 0x1e],
|
||||
[0x27, 0xbf, 0xb4, 0x41],
|
||||
[0x11, 0x98, 0x5d, 0x52],
|
||||
[0xae, 0xf1, 0xe5, 0x30]] };
|
||||
input.sub_bytes();
|
||||
assert_eq!(input, output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fips197_shift_rows_example() {
|
||||
let mut input = AESState{ state: [[0xd4, 0xe0, 0xb8, 0x1e],
|
||||
[0x27, 0xbf, 0xb4, 0x41],
|
||||
[0x11, 0x98, 0x5d, 0x52],
|
||||
[0xae, 0xf1, 0xe5, 0x30]] };
|
||||
let output = AESState{ state: [[0xd4, 0xe0, 0xb8, 0x1e],
|
||||
[0xbf, 0xb4, 0x41, 0x27],
|
||||
[0x5d, 0x52, 0x11, 0x98],
|
||||
[0x30, 0xae, 0xf1, 0xe5]] };
|
||||
input.shift_rows();
|
||||
assert_eq!(input, output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fips197_mix_columns_example() {
|
||||
let mut input = AESState{ state: [[0xd4, 0xe0, 0xb8, 0x1e],
|
||||
[0xbf, 0xb4, 0x41, 0x27],
|
||||
[0x5d, 0x52, 0x11, 0x98],
|
||||
[0x30, 0xae, 0xf1, 0xe5]] };
|
||||
let output = AESState{ state: [[0x04, 0xe0, 0x48, 0x28],
|
||||
[0x66, 0xcb, 0xf8, 0x06],
|
||||
[0x81, 0x19, 0xd3, 0x26],
|
||||
[0xe5, 0x9a, 0x7a, 0x4c]] };
|
||||
input.mix_columns();
|
||||
assert_eq!(input, output);
|
||||
}
|
||||
|
||||
impl Clone for AESState {
|
||||
fn clone(&self) -> AESState {
|
||||
AESState{ state: self.state.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for AESState {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> AESState {
|
||||
let mut base = [0; 16];
|
||||
g.fill_bytes(&mut base);
|
||||
AESState::new(&base)
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn check_sub_bytes_inverse(input: AESState) -> bool {
|
||||
let mut output = input.clone();
|
||||
output.sub_bytes();
|
||||
output.inv_sub_bytes();
|
||||
input == output
|
||||
}
|
||||
|
||||
fn check_shift_rows_inverse(input: AESState) -> bool {
|
||||
let mut output = input.clone();
|
||||
output.shift_rows();
|
||||
output.inv_shift_rows();
|
||||
input == output
|
||||
}
|
||||
|
||||
fn check_mix_columns_inverse(input: AESState) -> bool {
|
||||
let mut output = input.clone();
|
||||
output.mix_columns();
|
||||
output.inv_mix_columns();
|
||||
input == output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
/* */
|
||||
/* AES128 Implementation */
|
||||
/* */
|
||||
/**************************************************************************************************/
|
||||
|
||||
const AES128_KEY_LENGTH: usize = 4; // Nk
|
||||
const AES128_BLOCK_SIZE: usize = 4; // Nb
|
||||
const AES128_NUM_ROUNDS: usize = 10; // Nr
|
||||
const AES128_STATE_WORDS: usize = AES128_BLOCK_SIZE * (AES128_NUM_ROUNDS + 1);
|
||||
|
||||
pub struct AES128 {
|
||||
expanded: [u32; AES128_STATE_WORDS]
|
||||
}
|
||||
|
||||
impl AES128 {
|
||||
pub fn new(base_key: &[u8]) -> AES128 {
|
||||
let mut expanded = [0; AES128_STATE_WORDS];
|
||||
let mut i = 0;
|
||||
|
||||
assert_eq!(base_key.len(), 16);
|
||||
// while (i < Nk)
|
||||
// w[i] = word(key[4*i],key[4*i+1],key[4*i+2],key[4*i+3])
|
||||
// i = i+1
|
||||
// end while
|
||||
while i < AES128_KEY_LENGTH {
|
||||
expanded[i] = word(base_key[(4*i)+0], base_key[(4*i)+1],
|
||||
base_key[(4*i)+2], base_key[(4*i)+3]);
|
||||
//println!("{:02}: expanded[{}] = {:08x}", i, i, expanded[i]);
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
// i = Nk
|
||||
assert_eq!(i, AES128_KEY_LENGTH);
|
||||
|
||||
// while (i < Nb * (Nr + 1))
|
||||
while i < AES128_BLOCK_SIZE * (AES128_NUM_ROUNDS+1) {
|
||||
// temp = w[i-1]
|
||||
let mut temp = expanded[i-1];
|
||||
//println!("{:02}: temp = {:08x}", i, temp);
|
||||
// if (i mod Nk = 0)
|
||||
// temp = sub_word(rot_word(temp)) xor Rcon[i/Nk]
|
||||
// else
|
||||
// temp = sub_word(temp)
|
||||
// end if
|
||||
if i % AES128_KEY_LENGTH == 0 {
|
||||
temp = rot_word(temp);
|
||||
//println!("{:02}: after rotword = {:08x}", i, temp);
|
||||
temp = sub_word(temp);
|
||||
//println!("{:02}: after subword = {:08x}", i, temp);
|
||||
temp ^= RIJNDAEL_KEY_SCHEDULE[i/AES128_KEY_LENGTH];
|
||||
//println!("{:02}: after rcon xor = {:08x}", i, temp);
|
||||
}
|
||||
// w[i] = w[i-Nk] ^ temp;
|
||||
//println!("{:02}: w[{}-{}] = {:08x}", i, i, AES128_KEY_LENGTH, expanded[i-AES128_KEY_LENGTH]);
|
||||
expanded[i] = expanded[i-AES128_KEY_LENGTH] ^ temp;
|
||||
//println!("{:02}: expanded[{:02}] = {:08x}", i, i, expanded[i]);
|
||||
// i = i + 1
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
AES128{ expanded }
|
||||
}
|
||||
|
||||
pub fn encrypt(&self, block: &[u8]) -> Vec<u8> {
|
||||
let mut state = AESState::new(block);
|
||||
|
||||
state.add_round_key(&self.expanded[0..4]);
|
||||
for round in 1..AES128_NUM_ROUNDS {
|
||||
state.sub_bytes();
|
||||
state.shift_rows();
|
||||
state.mix_columns();
|
||||
let start = round * AES128_BLOCK_SIZE;
|
||||
let end = (round + 1) * AES128_BLOCK_SIZE;
|
||||
state.add_round_key(&self.expanded[start..end]);
|
||||
}
|
||||
|
||||
state.sub_bytes();
|
||||
state.shift_rows();
|
||||
let start = AES128_NUM_ROUNDS * AES128_BLOCK_SIZE;
|
||||
state.add_round_key(&self.expanded[start..]);
|
||||
|
||||
state.decant()
|
||||
}
|
||||
|
||||
pub fn decrypt(&self, block: &[u8]) -> Vec<u8> {
|
||||
let mut state = AESState::new(block);
|
||||
|
||||
let last_chunk_start = AES128_NUM_ROUNDS * AES128_BLOCK_SIZE;
|
||||
state.add_round_key(&self.expanded[last_chunk_start..]);
|
||||
|
||||
let mut round = AES128_NUM_ROUNDS - 1;
|
||||
while round > 0 {
|
||||
state.inv_shift_rows();
|
||||
state.inv_sub_bytes();
|
||||
let start = round * AES128_BLOCK_SIZE;
|
||||
let end = start + AES128_BLOCK_SIZE;
|
||||
state.add_round_key(&self.expanded[start..end]);
|
||||
state.inv_mix_columns();
|
||||
round -= 1;
|
||||
}
|
||||
state.inv_shift_rows();
|
||||
state.inv_sub_bytes();
|
||||
state.add_round_key(&self.expanded[0..4]);
|
||||
|
||||
state.decant()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod aes128 {
|
||||
use super::*;
|
||||
use super::aes256::RandomBlock;
|
||||
use testing::run_test;
|
||||
|
||||
#[test]
|
||||
fn fips197_key_expansion_example() {
|
||||
let cipher_key = [0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c];
|
||||
let expanded = AES128::new(&cipher_key);
|
||||
assert_eq!(expanded.expanded[00], 0x2b7e1516);
|
||||
assert_eq!(expanded.expanded[01], 0x28aed2a6);
|
||||
assert_eq!(expanded.expanded[02], 0xabf71588);
|
||||
assert_eq!(expanded.expanded[03], 0x09cf4f3c);
|
||||
assert_eq!(expanded.expanded[04], 0xa0fafe17);
|
||||
assert_eq!(expanded.expanded[05], 0x88542cb1);
|
||||
assert_eq!(expanded.expanded[06], 0x23a33939);
|
||||
assert_eq!(expanded.expanded[07], 0x2a6c7605);
|
||||
assert_eq!(expanded.expanded[08], 0xf2c295f2);
|
||||
assert_eq!(expanded.expanded[09], 0x7a96b943);
|
||||
assert_eq!(expanded.expanded[10], 0x5935807a);
|
||||
assert_eq!(expanded.expanded[11], 0x7359f67f);
|
||||
assert_eq!(expanded.expanded[12], 0x3d80477d);
|
||||
assert_eq!(expanded.expanded[13], 0x4716fe3e);
|
||||
assert_eq!(expanded.expanded[14], 0x1e237e44);
|
||||
assert_eq!(expanded.expanded[15], 0x6d7a883b);
|
||||
assert_eq!(expanded.expanded[16], 0xef44a541);
|
||||
assert_eq!(expanded.expanded[17], 0xa8525b7f);
|
||||
assert_eq!(expanded.expanded[18], 0xb671253b);
|
||||
assert_eq!(expanded.expanded[19], 0xdb0bad00);
|
||||
assert_eq!(expanded.expanded[20], 0xd4d1c6f8);
|
||||
assert_eq!(expanded.expanded[21], 0x7c839d87);
|
||||
assert_eq!(expanded.expanded[22], 0xcaf2b8bc);
|
||||
assert_eq!(expanded.expanded[23], 0x11f915bc);
|
||||
assert_eq!(expanded.expanded[24], 0x6d88a37a);
|
||||
assert_eq!(expanded.expanded[25], 0x110b3efd);
|
||||
assert_eq!(expanded.expanded[26], 0xdbf98641);
|
||||
assert_eq!(expanded.expanded[27], 0xca0093fd);
|
||||
assert_eq!(expanded.expanded[28], 0x4e54f70e);
|
||||
assert_eq!(expanded.expanded[29], 0x5f5fc9f3);
|
||||
assert_eq!(expanded.expanded[30], 0x84a64fb2);
|
||||
assert_eq!(expanded.expanded[31], 0x4ea6dc4f);
|
||||
assert_eq!(expanded.expanded[32], 0xead27321);
|
||||
assert_eq!(expanded.expanded[33], 0xb58dbad2);
|
||||
assert_eq!(expanded.expanded[34], 0x312bf560);
|
||||
assert_eq!(expanded.expanded[35], 0x7f8d292f);
|
||||
assert_eq!(expanded.expanded[36], 0xac7766f3);
|
||||
assert_eq!(expanded.expanded[37], 0x19fadc21);
|
||||
assert_eq!(expanded.expanded[38], 0x28d12941);
|
||||
assert_eq!(expanded.expanded[39], 0x575c006e);
|
||||
assert_eq!(expanded.expanded[40], 0xd014f9a8);
|
||||
assert_eq!(expanded.expanded[41], 0xc9ee2589);
|
||||
assert_eq!(expanded.expanded[42], 0xe13f0cc8);
|
||||
assert_eq!(expanded.expanded[43], 0xb6630ca6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fips197_encrypt_examples() {
|
||||
let input1 = [0x32,0x43,0xf6,0xa8,0x88,0x5a,0x30,0x8d,0x31,0x31,0x98,0xa2,0xe0,0x37,0x07,0x34];
|
||||
let cipher_key1 = [0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6,0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c];
|
||||
let expanded1 = AES128::new(&cipher_key1);
|
||||
let ciphertext1 = expanded1.encrypt(&input1);
|
||||
assert_eq!(ciphertext1, vec![0x39,0x25,0x84,0x1d,0x02,0xdc,0x09,0xfb,
|
||||
0xdc,0x11,0x85,0x97,0x19,0x6a,0x0b,0x32]);
|
||||
assert_eq!(input1.to_vec(), expanded1.decrypt(&ciphertext1));
|
||||
//
|
||||
let input2 = [0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff];
|
||||
let cipher_key2 = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f];
|
||||
let expanded2 = AES128::new(&cipher_key2);
|
||||
let ciphertext2 = expanded2.encrypt(&input2);
|
||||
assert_eq!(ciphertext2, vec![0x69,0xc4,0xe0,0xd8,0x6a,0x7b,0x04,0x30,
|
||||
0xd8,0xcd,0xb7,0x80,0x70,0xb4,0xc5,0x5a]);
|
||||
assert_eq!(input2.to_vec(), expanded2.decrypt(&ciphertext2));
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn encrypt_decrypt_is_identity(key: RandomBlock, block: RandomBlock) -> bool {
|
||||
let key = AES128::new(&key.block);
|
||||
let cipher = key.encrypt(&block.block);
|
||||
let block2 = key.decrypt(&cipher);
|
||||
block2 == block.block.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nist_test_vectors() {
|
||||
let fname = "testdata/aes/aes128.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negk, kbytes) = case.get("k").unwrap();
|
||||
let (negp, pbytes) = case.get("p").unwrap();
|
||||
let (negc, cbytes) = case.get("c").unwrap();
|
||||
|
||||
assert!(!negk && !negp && !negc);
|
||||
let key = AES128::new(&kbytes);
|
||||
let cipher = key.encrypt(&pbytes);
|
||||
let plain = key.decrypt(&cipher);
|
||||
assert_eq!(&cipher, cbytes);
|
||||
assert_eq!(&plain, pbytes);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
/* */
|
||||
/* AES256 Implementation */
|
||||
/* */
|
||||
/**************************************************************************************************/
|
||||
|
||||
const AES256_KEY_LENGTH: usize = 8; // Nk
|
||||
const AES256_BLOCK_SIZE: usize = 4; // Nb
|
||||
const AES256_NUM_ROUNDS: usize = 14; // Nr
|
||||
const AES256_STATE_WORDS: usize = AES256_BLOCK_SIZE * (AES256_NUM_ROUNDS + 1);
|
||||
|
||||
pub struct AES256 {
|
||||
expanded: [u32; AES256_STATE_WORDS]
|
||||
}
|
||||
|
||||
impl AES256 {
|
||||
pub fn new(base_key: &[u8]) -> AES256 {
|
||||
let mut expanded = [0; AES256_STATE_WORDS];
|
||||
let mut i = 0;
|
||||
|
||||
assert_eq!(base_key.len(), 32);
|
||||
// while (i < Nk)
|
||||
// w[i] = word(key[4*i],key[4*i+1],key[4*i+2],key[4*i+3])
|
||||
// i = i+1
|
||||
// end while
|
||||
while i < AES256_KEY_LENGTH {
|
||||
expanded[i] = word(base_key[(4*i)+0], base_key[(4*i)+1],
|
||||
base_key[(4*i)+2], base_key[(4*i)+3]);
|
||||
//println!("{:02}: expanded[{}] = {:08x}", i, i, expanded[i]);
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
// i = Nk
|
||||
assert_eq!(i, AES256_KEY_LENGTH);
|
||||
|
||||
// while (i < Nb * (Nr + 1))
|
||||
while i < AES256_BLOCK_SIZE * (AES256_NUM_ROUNDS+1) {
|
||||
// temp = w[i-1]
|
||||
let mut temp = expanded[i-1];
|
||||
//println!("{:02}: temp = {:08x}", i, temp);
|
||||
// if (i mod Nk = 0)
|
||||
// temp = sub_word(rot_word(temp)) xor Rcon[i/Nk]
|
||||
// else
|
||||
// temp = sub_word(temp)
|
||||
// end if
|
||||
if i % AES256_KEY_LENGTH == 0 {
|
||||
temp = rot_word(temp);
|
||||
//println!("{:02}: after rotword = {:08x}", i, temp);
|
||||
temp = sub_word(temp);
|
||||
//println!("{:02}: after subword = {:08x}", i, temp);
|
||||
temp ^= RIJNDAEL_KEY_SCHEDULE[i/AES256_KEY_LENGTH];
|
||||
//println!("{:02}: after rcon xor = {:08x}", i, temp);
|
||||
} else if i % 4 == 0 {
|
||||
temp = sub_word(temp);
|
||||
//println!("{:02}: after subword' = {:08x}", i, temp);
|
||||
}
|
||||
// w[i] = w[i-Nk] ^ temp;
|
||||
//println!("{:02}: w[{}-{}] = {:08x}", i, i, AES256_KEY_LENGTH, expanded[i-AES256_KEY_LENGTH]);
|
||||
expanded[i] = expanded[i-AES256_KEY_LENGTH] ^ temp;
|
||||
//println!("{:02}: expanded[{:02}] = {:08x}", i, i, expanded[i]);
|
||||
// i = i + 1
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
AES256{ expanded }
|
||||
}
|
||||
|
||||
pub fn encrypt(&self, block: &[u8]) -> Vec<u8> {
|
||||
let mut state = AESState::new(block);
|
||||
|
||||
assert_eq!(block.len(), 16);
|
||||
state.add_round_key(&self.expanded[0..4]);
|
||||
for round in 1..AES256_NUM_ROUNDS {
|
||||
state.sub_bytes();
|
||||
state.shift_rows();
|
||||
state.mix_columns();
|
||||
let start = round * AES256_BLOCK_SIZE;
|
||||
let end = (round + 1) * AES256_BLOCK_SIZE;
|
||||
state.add_round_key(&self.expanded[start..end]);
|
||||
}
|
||||
|
||||
state.sub_bytes();
|
||||
state.shift_rows();
|
||||
let start = AES256_NUM_ROUNDS * AES256_BLOCK_SIZE;
|
||||
state.add_round_key(&self.expanded[start..]);
|
||||
|
||||
state.decant()
|
||||
}
|
||||
|
||||
pub fn decrypt(&self, block: &[u8]) -> Vec<u8> {
|
||||
let mut state = AESState::new(block);
|
||||
|
||||
let last_chunk_start = AES256_NUM_ROUNDS * AES256_BLOCK_SIZE;
|
||||
state.add_round_key(&self.expanded[last_chunk_start..]);
|
||||
|
||||
let mut round = AES256_NUM_ROUNDS - 1;
|
||||
while round > 0 {
|
||||
state.inv_shift_rows();
|
||||
state.inv_sub_bytes();
|
||||
let start = round * AES256_BLOCK_SIZE;
|
||||
let end = start + AES256_BLOCK_SIZE;
|
||||
state.add_round_key(&self.expanded[start..end]);
|
||||
state.inv_mix_columns();
|
||||
round -= 1;
|
||||
}
|
||||
state.inv_shift_rows();
|
||||
state.inv_sub_bytes();
|
||||
state.add_round_key(&self.expanded[0..4]);
|
||||
|
||||
state.decant()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod aes256 {
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
|
||||
#[test]
|
||||
fn fips197_key_expansion_example() {
|
||||
let cipher_key = [0x60,0x3d,0xeb,0x10,0x15,0xca,0x71,0xbe,
|
||||
0x2b,0x73,0xae,0xf0,0x85,0x7d,0x77,0x81,
|
||||
0x1f,0x35,0x2c,0x07,0x3b,0x61,0x08,0xd7,
|
||||
0x2d,0x98,0x10,0xa3,0x09,0x14,0xdf,0xf4];
|
||||
let expanded = AES256::new(&cipher_key);
|
||||
assert_eq!(expanded.expanded[00], 0x603deb10);
|
||||
assert_eq!(expanded.expanded[01], 0x15ca71be);
|
||||
assert_eq!(expanded.expanded[02], 0x2b73aef0);
|
||||
assert_eq!(expanded.expanded[03], 0x857d7781);
|
||||
assert_eq!(expanded.expanded[04], 0x1f352c07);
|
||||
assert_eq!(expanded.expanded[05], 0x3b6108d7);
|
||||
assert_eq!(expanded.expanded[06], 0x2d9810a3);
|
||||
assert_eq!(expanded.expanded[07], 0x0914dff4);
|
||||
assert_eq!(expanded.expanded[08], 0x9ba35411);
|
||||
assert_eq!(expanded.expanded[09], 0x8e6925af);
|
||||
assert_eq!(expanded.expanded[10], 0xa51a8b5f);
|
||||
assert_eq!(expanded.expanded[11], 0x2067fcde);
|
||||
assert_eq!(expanded.expanded[12], 0xa8b09c1a);
|
||||
assert_eq!(expanded.expanded[13], 0x93d194cd);
|
||||
assert_eq!(expanded.expanded[14], 0xbe49846e);
|
||||
assert_eq!(expanded.expanded[15], 0xb75d5b9a);
|
||||
assert_eq!(expanded.expanded[16], 0xd59aecb8);
|
||||
assert_eq!(expanded.expanded[17], 0x5bf3c917);
|
||||
assert_eq!(expanded.expanded[18], 0xfee94248);
|
||||
assert_eq!(expanded.expanded[19], 0xde8ebe96);
|
||||
assert_eq!(expanded.expanded[20], 0xb5a9328a);
|
||||
assert_eq!(expanded.expanded[21], 0x2678a647);
|
||||
assert_eq!(expanded.expanded[22], 0x98312229);
|
||||
assert_eq!(expanded.expanded[23], 0x2f6c79b3);
|
||||
assert_eq!(expanded.expanded[24], 0x812c81ad);
|
||||
assert_eq!(expanded.expanded[25], 0xdadf48ba);
|
||||
assert_eq!(expanded.expanded[26], 0x24360af2);
|
||||
assert_eq!(expanded.expanded[27], 0xfab8b464);
|
||||
assert_eq!(expanded.expanded[28], 0x98c5bfc9);
|
||||
assert_eq!(expanded.expanded[29], 0xbebd198e);
|
||||
assert_eq!(expanded.expanded[30], 0x268c3ba7);
|
||||
assert_eq!(expanded.expanded[31], 0x09e04214);
|
||||
assert_eq!(expanded.expanded[32], 0x68007bac);
|
||||
assert_eq!(expanded.expanded[33], 0xb2df3316);
|
||||
assert_eq!(expanded.expanded[34], 0x96e939e4);
|
||||
assert_eq!(expanded.expanded[35], 0x6c518d80);
|
||||
assert_eq!(expanded.expanded[36], 0xc814e204);
|
||||
assert_eq!(expanded.expanded[37], 0x76a9fb8a);
|
||||
assert_eq!(expanded.expanded[38], 0x5025c02d);
|
||||
assert_eq!(expanded.expanded[39], 0x59c58239);
|
||||
assert_eq!(expanded.expanded[40], 0xde136967);
|
||||
assert_eq!(expanded.expanded[41], 0x6ccc5a71);
|
||||
assert_eq!(expanded.expanded[42], 0xfa256395);
|
||||
assert_eq!(expanded.expanded[43], 0x9674ee15);
|
||||
assert_eq!(expanded.expanded[44], 0x5886ca5d);
|
||||
assert_eq!(expanded.expanded[45], 0x2e2f31d7);
|
||||
assert_eq!(expanded.expanded[46], 0x7e0af1fa);
|
||||
assert_eq!(expanded.expanded[47], 0x27cf73c3);
|
||||
assert_eq!(expanded.expanded[48], 0x749c47ab);
|
||||
assert_eq!(expanded.expanded[49], 0x18501dda);
|
||||
assert_eq!(expanded.expanded[50], 0xe2757e4f);
|
||||
assert_eq!(expanded.expanded[51], 0x7401905a);
|
||||
assert_eq!(expanded.expanded[52], 0xcafaaae3);
|
||||
assert_eq!(expanded.expanded[53], 0xe4d59b34);
|
||||
assert_eq!(expanded.expanded[54], 0x9adf6ace);
|
||||
assert_eq!(expanded.expanded[55], 0xbd10190d);
|
||||
assert_eq!(expanded.expanded[56], 0xfe4890d1);
|
||||
assert_eq!(expanded.expanded[57], 0xe6188d0b);
|
||||
assert_eq!(expanded.expanded[58], 0x046df344);
|
||||
assert_eq!(expanded.expanded[59], 0x706c631e);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fips197_example() {
|
||||
let input = [0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff];
|
||||
let key = [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
|
||||
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f];
|
||||
let aeskey = AES256::new(&key);
|
||||
let cipher = aeskey.encrypt(&input);
|
||||
assert_eq!(cipher, vec![0x8e,0xa2,0xb7,0xca,0x51,0x67,0x45,0xbf,
|
||||
0xea,0xfc,0x49,0x90,0x4b,0x49,0x60,0x89]);
|
||||
assert_eq!(input.to_vec(), aeskey.decrypt(&cipher));
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub(crate) struct RandomKey {
|
||||
pub(crate) key: [u8; 32]
|
||||
}
|
||||
|
||||
impl Arbitrary for RandomKey {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> RandomKey {
|
||||
let mut res = RandomKey{ key: [0; 32] };
|
||||
g.fill_bytes(&mut res.key);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub(crate) struct RandomBlock {
|
||||
pub(crate) block: [u8; 16]
|
||||
}
|
||||
|
||||
impl Arbitrary for RandomBlock {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> RandomBlock {
|
||||
let mut res = RandomBlock{ block: [0; 16] };
|
||||
g.fill_bytes(&mut res.block);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn encrypt_decrypt_is_identity(key: RandomKey, block: RandomBlock) -> bool {
|
||||
let key = AES256::new(&key.key);
|
||||
let cipher = key.encrypt(&block.block);
|
||||
let block2 = key.decrypt(&cipher);
|
||||
block2 == block.block.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nist_test_vectors() {
|
||||
let fname = "testdata/aes/aes256.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negk, kbytes) = case.get("k").unwrap();
|
||||
let (negp, pbytes) = case.get("p").unwrap();
|
||||
let (negc, cbytes) = case.get("c").unwrap();
|
||||
|
||||
assert!(!negk && !negp && !negc);
|
||||
let key = AES256::new(&kbytes);
|
||||
let cipher = key.encrypt(&pbytes);
|
||||
let plain = key.decrypt(&cipher);
|
||||
assert_eq!(&cipher, cbytes);
|
||||
assert_eq!(&plain, pbytes);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
macro_rules! define_arithmetic {
|
||||
($type: ident, $asncl: ident, $asnfn: ident,
|
||||
$cl: ident, $clfn: ident,
|
||||
$self: ident, $o: ident, $body: block) =>
|
||||
{
|
||||
build_assign_operator!($type, $asncl, $asnfn, $self, $o, $body);
|
||||
derive_arithmetic_operators!($type, $cl, $clfn, $asncl, $asnfn);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! build_assign_operator {
|
||||
($type: ident, $asncl: ident, $asnfn: ident, $self: ident,
|
||||
$o: ident, $body: block) =>
|
||||
{
|
||||
impl<'a> $asncl<&'a $type> for $type {
|
||||
fn $asnfn(&mut $self, $o: &$type) $body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! derive_arithmetic_operators
|
||||
{
|
||||
($type: ident, $cl: ident, $fn: ident, $asncl: ident, $asnfn: ident) => {
|
||||
impl $asncl for $type {
|
||||
fn $asnfn(&mut self, other: $type) {
|
||||
self.$asnfn(&other)
|
||||
}
|
||||
}
|
||||
|
||||
impl $cl for $type {
|
||||
type Output = $type;
|
||||
|
||||
fn $fn(self, other: $type) -> $type {
|
||||
let mut res = self.clone();
|
||||
res.$asnfn(&other);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> $cl<&'a $type> for $type {
|
||||
type Output = $type;
|
||||
|
||||
fn $fn(self, other: &$type) -> $type {
|
||||
let mut res = self.clone();
|
||||
res.$asnfn(other);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> $cl<$type> for &'a $type {
|
||||
type Output = $type;
|
||||
|
||||
fn $fn(self, other: $type) -> $type {
|
||||
let mut res = self.clone();
|
||||
res.$asnfn(&other);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> $cl<&'a $type> for &'b $type {
|
||||
type Output = $type;
|
||||
|
||||
fn $fn(self, other: &$type) -> $type {
|
||||
let mut res = self.clone();
|
||||
res.$asnfn(other);
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
macro_rules! derive_barrett
|
||||
{
|
||||
($type: ident, $barrett: ident, $count: expr) => {
|
||||
impl CryptoNumFastMod for $type {
|
||||
type BarrettMu = $barrett;
|
||||
|
||||
fn barrett_mu(&self) -> Option<$barrett> {
|
||||
// Step #0: Don't divide by 0.
|
||||
if self.is_zero() {
|
||||
return None
|
||||
}
|
||||
// Step #1: Compute k.
|
||||
let mut k = $count;
|
||||
while self.contents[k - 1] == 0 { k -= 1 };
|
||||
// Step #2: The algorithm below only works if x has at most 2k
|
||||
// digits, so if k*2 < count, abort this whole process.
|
||||
if (k * 2) < $count {
|
||||
return None
|
||||
}
|
||||
// Step #2: Compute floor(b^2k / m), where m is this value.
|
||||
let mut widebody_b2k = [0; ($count * 2) + 1];
|
||||
let mut widebody_self = [0; ($count * 2) + 1];
|
||||
let mut quotient = [0; ($count * 2) + 1];
|
||||
let mut remainder = [0; ($count * 2) + 1];
|
||||
widebody_b2k[$count * 2] = 1;
|
||||
for i in 0..k {
|
||||
widebody_self[i] = self.contents[i];
|
||||
}
|
||||
generic_div(&widebody_b2k, &widebody_self,
|
||||
&mut quotient, &mut remainder);
|
||||
let mut result = [0; $count + 1];
|
||||
for (idx, val) in quotient.iter().enumerate() {
|
||||
if idx < ($count + 1) {
|
||||
result[idx] = *val;
|
||||
} else {
|
||||
if quotient[idx] != 0 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some($barrett{k: k, progenitor: self.clone(), contents: result})
|
||||
}
|
||||
|
||||
fn fastmod(&self, mu: &$barrett) -> $type {
|
||||
// This algorithm is from our friends at the Handbook of
|
||||
// Applied Cryptography, Chapter 14, Algorithm 14.42.
|
||||
// Step #0:
|
||||
// Expand x so that it has the same size as the Barrett
|
||||
// value.
|
||||
let mut x = [0; $count + 1];
|
||||
for i in 0..$count {
|
||||
x[i] = self.contents[i];
|
||||
}
|
||||
// Step #1:
|
||||
// q1 <- floor(x / b^(k-1))
|
||||
let mut q1 = x.clone();
|
||||
generic_shr(&mut q1, &x, 64 * (mu.k - 1));
|
||||
// q2 <- q1 * mu
|
||||
let q2 = expanding_mul(&q1, &mu.contents);
|
||||
// q3 <- floor(q2 / b^(k+1))
|
||||
let mut q3big = q2.clone();
|
||||
generic_shr(&mut q3big, &q2, 64 * (mu.k + 1));
|
||||
let mut q3 = [0; $count + 1];
|
||||
for (idx, val) in q3big.iter().enumerate() {
|
||||
if idx <= $count {
|
||||
q3[idx] = *val;
|
||||
} else {
|
||||
assert_eq!(*val, 0);
|
||||
}
|
||||
}
|
||||
// Step #2:
|
||||
// r1 <- x mod b^(k+1)
|
||||
let mut r1 = x.clone();
|
||||
for i in mu.k..($count+1) {
|
||||
r1[i] = 0;
|
||||
}
|
||||
// r2 <- q3 * m mod b^(k+1)
|
||||
let mut moddedm = [0; $count + 1];
|
||||
for i in 0..mu.k {
|
||||
moddedm[i] = mu.progenitor.contents[i];
|
||||
}
|
||||
let mut r2 = q3.clone();
|
||||
generic_mul(&mut r2, &q3, &moddedm);
|
||||
// r <- r1 - r2
|
||||
let mut r = r1.clone();
|
||||
generic_sub(&mut r, &r2);
|
||||
let is_negative = !ge(&r1, &r2);
|
||||
// Step #3:
|
||||
// if r < 0 then r <- r + b^(k + 1)
|
||||
if is_negative {
|
||||
let mut bk1 = [0; $count + 1];
|
||||
bk1[mu.k] = 1;
|
||||
generic_add(&mut r, &bk1);
|
||||
}
|
||||
// Step #4:
|
||||
// while r >= m do: r <- r - m.
|
||||
while ge(&r, &moddedm) {
|
||||
generic_sub(&mut r, &moddedm);
|
||||
}
|
||||
// Step #5:
|
||||
// return r
|
||||
let mut retval = [0; $count];
|
||||
for i in 0..$count {
|
||||
retval[i] = r[i];
|
||||
}
|
||||
assert_eq!(r[$count], 0);
|
||||
$type{ contents: retval }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
macro_rules! generate_unsigned_conversions
|
||||
{
|
||||
($type: ident, $count: expr) => {
|
||||
generate_unsigned_primtype_conversions!($type, u8, $count);
|
||||
generate_unsigned_primtype_conversions!($type, u16, $count);
|
||||
generate_unsigned_primtype_conversions!($type, u32, $count);
|
||||
generate_unsigned_primtype_conversions!($type, u64, $count);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! generate_signed_conversions
|
||||
{
|
||||
($type: ident, $base: ident) => {
|
||||
generate_signed_primtype_conversions!($type, $base, i8, u8);
|
||||
generate_signed_primtype_conversions!($type, $base, i16, u16);
|
||||
generate_signed_primtype_conversions!($type, $base, i32, u32);
|
||||
generate_signed_primtype_conversions!($type, $base, i64, u64);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! generate_unsigned_primtype_conversions
|
||||
{
|
||||
($type: ident, $base: ty, $count: expr) => {
|
||||
generate_from!($type, $base, x, {
|
||||
let mut res = $type{ contents: [0; $count] };
|
||||
res.contents[0] = x as u64;
|
||||
res
|
||||
});
|
||||
generate_into!($type, $base, self, {
|
||||
self.contents[0] as $base
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! generate_signed_primtype_conversions
|
||||
{
|
||||
($type: ident, $untype: ident, $base: ident, $unbase: ident) => {
|
||||
generate_from!($type, $unbase, x, {
|
||||
$type{ negative: false, value: $untype::from(x) }
|
||||
});
|
||||
generate_into!($type, $unbase, self, {
|
||||
self.value.contents[0] as $unbase
|
||||
});
|
||||
generate_from!($type, $base, x, {
|
||||
let neg = x < 0;
|
||||
$type{negative: neg, value: $untype::from(x.abs() as $unbase)}
|
||||
});
|
||||
generate_into!($type, $base, self, {
|
||||
if self.negative {
|
||||
let start = self.value.contents[0] as $unbase;
|
||||
let mask = ($unbase::max_value() - 1) >> 1;
|
||||
let res = (start & mask) as $base;
|
||||
-res
|
||||
} else {
|
||||
self.value.contents[0] as $base
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! generate_from
|
||||
{
|
||||
($type: ident, $base: ty, $x: ident, $body: block) => {
|
||||
impl From<$base> for $type {
|
||||
fn from($x: $base) -> $type $body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! generate_into
|
||||
{
|
||||
($type: ident, $base: ty, $self: ident, $body: block) => {
|
||||
impl Into<$base> for $type {
|
||||
fn into($self) -> $base $body
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn generic_cmp(a: &[u64], b: &[u64]) -> Ordering {
|
||||
let mut i = a.len() - 1;
|
||||
|
||||
assert!(a.len() == b.len());
|
||||
loop {
|
||||
match a[i].cmp(&b[i]) {
|
||||
Ordering::Equal if i == 0 =>
|
||||
return Ordering::Equal,
|
||||
Ordering::Equal =>
|
||||
i -= 1,
|
||||
res =>
|
||||
return res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn le(a: &[u64], b: &[u64]) -> bool {
|
||||
generic_cmp(a, b) != Ordering::Greater
|
||||
}
|
||||
|
||||
pub fn ge(a: &[u64], b: &[u64]) -> bool {
|
||||
generic_cmp(a, b) != Ordering::Less
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn generic_bitand(a: &mut [u64], b: &[u64]) {
|
||||
let mut i = 0;
|
||||
|
||||
assert!(a.len() == b.len());
|
||||
while i < a.len() {
|
||||
a[i] &= b[i];
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn generic_bitor(a: &mut [u64], b: &[u64]) {
|
||||
let mut i = 0;
|
||||
|
||||
assert!(a.len() == b.len());
|
||||
while i < a.len() {
|
||||
a[i] |= b[i];
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn generic_bitxor(a: &mut [u64], b: &[u64]) {
|
||||
let mut i = 0;
|
||||
|
||||
assert!(a.len() == b.len());
|
||||
while i < a.len() {
|
||||
a[i] ^= b[i];
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn generic_not(a: &mut [u64]) {
|
||||
for x in a.iter_mut() {
|
||||
*x = !*x;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn generic_shl(a: &mut [u64], orig: &[u64], amount: usize) {
|
||||
let digits = amount / 64;
|
||||
let bits = amount % 64;
|
||||
|
||||
assert!(a.len() == orig.len());
|
||||
for i in 0..a.len() {
|
||||
if i < digits {
|
||||
a[i] = 0;
|
||||
} else {
|
||||
let origidx = i - digits;
|
||||
let prev = if origidx == 0 { 0 } else { orig[origidx - 1] };
|
||||
let (carry,_) = if bits == 0 { (0, false) }
|
||||
else { prev.overflowing_shr(64 - bits as u32) };
|
||||
a[i] = (orig[origidx] << bits) | carry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn generic_shr(a: &mut [u64], orig: &[u64], amount: usize) {
|
||||
let digits = amount / 64;
|
||||
let bits = amount % 64;
|
||||
|
||||
assert!(a.len() == orig.len());
|
||||
for i in 0..a.len() {
|
||||
let oldidx = i + digits;
|
||||
let caridx = i + digits + 1;
|
||||
let old = if oldidx >= a.len() { 0 } else { orig[oldidx] };
|
||||
let carry = if caridx >= a.len() { 0 } else { orig[caridx] };
|
||||
let cb = if bits == 0 { 0 } else { carry << (64 - bits) };
|
||||
a[i] = (old >> bits) | cb;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn generic_add(a: &mut [u64], b: &[u64]) {
|
||||
let mut carry = 0;
|
||||
|
||||
assert!(a.len() == b.len());
|
||||
for i in 0..a.len() {
|
||||
let x = a[i] as u128;
|
||||
let y = b[i] as u128;
|
||||
let total = x + y + carry;
|
||||
a[i] = total as u64;
|
||||
carry = total >> 64;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn generic_sub(a: &mut [u64], b: &[u64]) {
|
||||
let mut negated_rhs = b.to_vec();
|
||||
generic_not(&mut negated_rhs);
|
||||
let mut one = Vec::with_capacity(a.len());
|
||||
one.resize(a.len(), 0);
|
||||
one[0] = 1;
|
||||
generic_add(&mut negated_rhs, &one);
|
||||
generic_add(a, &negated_rhs);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn generic_mul(a: &mut [u64], orig: &[u64], b: &[u64]) {
|
||||
assert!(a.len() == orig.len());
|
||||
assert!(a.len() == b.len());
|
||||
assert!(a == orig);
|
||||
|
||||
// Build the output table. This is a little bit awkward because we don't
|
||||
// know how big we're running, but hopefully the compiler is smart enough
|
||||
// to work all this out.
|
||||
let mut table = Vec::with_capacity(a.len());
|
||||
for _ in 0..a.len() {
|
||||
let mut row = Vec::with_capacity(a.len());
|
||||
row.resize(a.len(), 0);
|
||||
table.push(row);
|
||||
}
|
||||
// This uses "simple" grade school techniques to work things out. But,
|
||||
// for reference, consider two 4 digit numbers:
|
||||
//
|
||||
// l0c3 l0c2 l0c1 l0c0 [orig]
|
||||
// x l1c3 l1c2 l1c1 l1c0 [b]
|
||||
// ------------------------------------------------------------
|
||||
// (l0c3*l1c0) (l0c2*l1c0) (l0c1*l1c0) (l0c0*l1c0)
|
||||
// (l0c2*l1c1) (l0c1*l1c1) (l0c0*l1c1)
|
||||
// (l0c1*l1c2) (l0c0*l1c2)
|
||||
// (l0c0*l1c3)
|
||||
// ------------------------------------------------------------
|
||||
// AAAAA BBBBB CCCCC DDDDD
|
||||
for line in 0..a.len() {
|
||||
let maxcol = a.len() - line;
|
||||
for col in 0..maxcol {
|
||||
let left = orig[col] as u128;
|
||||
let right = b[line] as u128;
|
||||
table[line][col + line] = left * right;
|
||||
}
|
||||
}
|
||||
// ripple the carry across each line, ensuring that each entry in the
|
||||
// table is 64-bits
|
||||
for line in 0..a.len() {
|
||||
let mut carry = 0;
|
||||
for col in 0..a.len() {
|
||||
table[line][col] = table[line][col] + carry;
|
||||
carry = table[line][col] >> 64;
|
||||
table[line][col] &= 0xFFFFFFFFFFFFFFFF;
|
||||
}
|
||||
}
|
||||
// now do the final addition across the lines, rippling the carry as
|
||||
// normal
|
||||
let mut carry = 0;
|
||||
for col in 0..a.len() {
|
||||
let mut total = carry;
|
||||
for line in 0..a.len() {
|
||||
total += table[line][col];
|
||||
}
|
||||
a[col] = total as u64;
|
||||
carry = total >> 64;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn expanding_mul(a: &[u64], b: &[u64]) -> Vec<u64> {
|
||||
assert!(a.len() == b.len());
|
||||
// The maximum size of an n x n digit multiplication is 2n digits, so
|
||||
// here's our output array.
|
||||
let mut result = Vec::with_capacity(a.len() * 2);
|
||||
result.resize(a.len() * 2, 0);
|
||||
|
||||
for (base_idx, digit) in b.iter().enumerate() {
|
||||
let mut myrow = Vec::with_capacity(a.len() * 2);
|
||||
let mut carry = 0;
|
||||
|
||||
myrow.resize(a.len() * 2, 0);
|
||||
for (col_idx, digit2) in a.iter().enumerate() {
|
||||
let left = *digit2 as u128;
|
||||
let right = *digit as u128;
|
||||
let combo = (left * right) + carry;
|
||||
|
||||
myrow[base_idx + col_idx] = combo as u64;
|
||||
carry = combo >> 64;
|
||||
}
|
||||
generic_add(&mut result, &myrow);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn generic_div(inx: &[u64], iny: &[u64],
|
||||
outq: &mut [u64], outr: &mut [u64])
|
||||
{
|
||||
assert!(inx.len() == inx.len());
|
||||
assert!(inx.len() == iny.len());
|
||||
assert!(inx.len() == outq.len());
|
||||
assert!(inx.len() == outr.len());
|
||||
// This algorithm is from the Handbook of Applied Cryptography, Chapter 14,
|
||||
// algorithm 14.20. It has a couple assumptions about the inputs, namely that
|
||||
// n >= t >= 1 and y[t] != 0, where n and t refer to the number of digits in
|
||||
// the numbers. Which means that if we used the inputs unmodified, we can't
|
||||
// divide by single-digit numbers.
|
||||
//
|
||||
// To deal with this, we multiply inx and iny by 2^64, so that we push out
|
||||
// t by one.
|
||||
//
|
||||
// In addition, this algorithm starts to go badly when y[t] is very small
|
||||
// and x[n] is very large. Really, really badly. This can be fixed by
|
||||
// insuring that the top bit is set in y[t], which we can achieve by
|
||||
// shifting everyone over a maxiumum of 63 bits.
|
||||
//
|
||||
// What this means is, just for safety, we add a 0 at the beginning and
|
||||
// end of each number.
|
||||
let mut y = iny.to_vec();
|
||||
let mut x = inx.to_vec();
|
||||
y.insert(0,0); y.push(0);
|
||||
x.insert(0,0); x.push(0);
|
||||
// 0. Compute 'n' and 't'
|
||||
let n = x.len() - 1;
|
||||
let mut t = y.len() - 1;
|
||||
while (t > 0) && (y[t] == 0) { t -= 1 }
|
||||
assert!(y[t] != 0); // this is where division by zero will fire
|
||||
// 0.5. Figure out a shift we can do such that the high bit of y[t] is
|
||||
// set, and then shift x and y left by that much.
|
||||
let additional_shift: usize = y[t].leading_zeros() as usize;
|
||||
let origx = x.clone();
|
||||
let origy = y.clone();
|
||||
generic_shl(&mut x, &origx, additional_shift);
|
||||
generic_shl(&mut y, &origy, additional_shift);
|
||||
// 1. For j from 0 to (n - 1) do: q_j <- 0
|
||||
let mut q = Vec::with_capacity(y.len());
|
||||
q.resize(y.len(), 0);
|
||||
for qj in q.iter_mut() { *qj = 0 }
|
||||
// 2. While (x >= yb^(n-t)) do the following:
|
||||
// q_(n-t) <- q_(n-t) + 1
|
||||
// x <- x - yb^(n-t)
|
||||
let mut ybnt = y.clone();
|
||||
generic_shl(&mut ybnt, &y, 64 * (n - t));
|
||||
while ge(&x, &ybnt) {
|
||||
q[n-t] = q[n-t] + 1;
|
||||
generic_sub(&mut x, &ybnt);
|
||||
}
|
||||
// 3. For i from n down to (t + 1) do the following:
|
||||
let mut i = n;
|
||||
while i >= (t + 1) {
|
||||
// 3.1. if x_i = y_t, then set q_(i-t-1) <- b - 1; otherwise set
|
||||
// q_(i-t-1) <- floor((x_i * b + x_(i-1)) / y_t).
|
||||
if x[i] == y[t] {
|
||||
q[i-t-1] = 0xFFFFFFFFFFFFFFFF;
|
||||
} else {
|
||||
let top = ((x[i] as u128) << 64) + (x[i-1] as u128);
|
||||
let bot = y[t] as u128;
|
||||
let solution = top / bot;
|
||||
q[i-t-1] = solution as u64;
|
||||
}
|
||||
// 3.2. While (q_(i-t-1)(y_t * b + y_(t-1)) > x_i(b2) + x_(i-1)b +
|
||||
// x_(i-2)) do:
|
||||
// q_(i - t - 1) <- q_(i - t 1) - 1.
|
||||
loop {
|
||||
let mut left = Vec::with_capacity(x.len());
|
||||
left.resize(x.len(), 0);
|
||||
left[0] = q[i - t - 1];
|
||||
let mut leftright = Vec::with_capacity(x.len());
|
||||
leftright.resize(x.len(), 0);
|
||||
leftright[0] = y[t-1];
|
||||
|
||||
let copy = left.clone();
|
||||
generic_mul(&mut left, ©, &leftright);
|
||||
let mut right = Vec::with_capacity(x.len());
|
||||
right.resize(x.len(), 0);
|
||||
right[0] = x[i-2];
|
||||
right[1] = x[i-1];
|
||||
right[2] = x[i];
|
||||
|
||||
if le(&left, &right) {
|
||||
break
|
||||
}
|
||||
|
||||
q[i - t - 1] -= 1;
|
||||
}
|
||||
// 3.3. x <- x - q_(i - t - 1) * y * b^(i-t-1)
|
||||
let mut right = Vec::with_capacity(y.len());
|
||||
right.resize(y.len(), 0);
|
||||
right[i - t - 1] = q[i - t - 1];
|
||||
let rightclone = right.clone();
|
||||
generic_mul(&mut right, &rightclone, &y);
|
||||
let wentnegative = generic_cmp(&x, &right) == Ordering::Less;
|
||||
generic_sub(&mut x, &right);
|
||||
// 3.4. if x < 0 then set x <- x + yb^(i-t-1) and
|
||||
// q_(i-t-1) <- q_(i-t-1) - 1
|
||||
if wentnegative {
|
||||
let mut ybit1 = y.to_vec();
|
||||
generic_shl(&mut ybit1, &y, 64 * (i - t - 1));
|
||||
generic_add(&mut x, &ybit1);
|
||||
q[i - t - 1] -= 1;
|
||||
}
|
||||
i -= 1;
|
||||
}
|
||||
// 4. r <- x
|
||||
let finalx = x.clone();
|
||||
generic_shr(&mut x, &finalx, additional_shift);
|
||||
for i in 0..outr.len() {
|
||||
outr[i] = x[i + 1]; // note that for the remainder, we're dividing by
|
||||
// our normalization value.
|
||||
}
|
||||
// 5. return (q,r)
|
||||
for i in 0..outq.len() {
|
||||
outq[i] = q[i];
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
//! # Simple-Crypto CryptoNum
|
||||
//!
|
||||
//! This module is designed to provide large, fixed-width number support for
|
||||
//! the rest of the Simple-Crypto libraries. Feel free to use it other places,
|
||||
//! of course, but that's its origin.
|
||||
//!
|
||||
//! Key generation is supported, using either the native `OsRng` or a random
|
||||
//! number generator of your choice. Obviously, you should be careful to use
|
||||
//! a cryptographically-sound random number generator sufficient for the
|
||||
//! security level you're going for.
|
||||
//!
|
||||
//! Signing and verification are via standard PKCS1 padding, but can be
|
||||
//! adjusted based on the exact hash you want. This library also supports
|
||||
//! somewhat arbitrary signing mechanisms used by your weirder network
|
||||
//! protocols. (I'm looking at you, Tor.)
|
||||
//!
|
||||
//! Encryption and decryption are via the OAEP mechanism, as described in
|
||||
//! NIST documents.
|
||||
//!
|
||||
#[macro_use]
|
||||
mod arithmetic_traits;
|
||||
#[macro_use]
|
||||
mod barrett;
|
||||
#[macro_use]
|
||||
mod conversions;
|
||||
mod core;
|
||||
#[macro_use]
|
||||
mod modops;
|
||||
#[macro_use]
|
||||
mod primes;
|
||||
#[macro_use]
|
||||
mod signed;
|
||||
#[macro_use]
|
||||
mod unsigned;
|
||||
mod traits;
|
||||
|
||||
use cryptonum::core::*;
|
||||
use num::{BigUint,BigInt};
|
||||
use rand::Rng;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt::{Debug,Error,Formatter};
|
||||
use std::ops::*;
|
||||
pub use self::traits::*;
|
||||
use self::primes::SMALL_PRIMES;
|
||||
|
||||
construct_unsigned!(U512, BarretMu512, u512, 8);
|
||||
construct_unsigned!(U1024, BarretMu1024, u1024, 16);
|
||||
construct_unsigned!(U2048, BarretMu2048, u2048, 32);
|
||||
construct_unsigned!(U3072, BarretMu3072, u3072, 48);
|
||||
construct_unsigned!(U4096, BarretMu4096, u4096, 64);
|
||||
construct_unsigned!(U7680, BarretMu7680, u7680, 120);
|
||||
construct_unsigned!(U8192, BarretMu8192, u8192, 128);
|
||||
construct_unsigned!(U15360, BarretMu15360, u15360, 240);
|
||||
|
||||
construct_signed!(I512, U512, i512);
|
||||
construct_signed!(I1024, U1024, i1024);
|
||||
construct_signed!(I2048, U2048, i2048);
|
||||
construct_signed!(I3072, U3072, i3072);
|
||||
construct_signed!(I4096, U4096, i4096);
|
||||
construct_signed!(I7680, U7680, i7680);
|
||||
construct_signed!(I8192, U8192, i8192);
|
||||
construct_signed!(I15360, U15360, i15360);
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
macro_rules! derive_modulo_operations
|
||||
{
|
||||
($type: ident) => {
|
||||
impl CryptoNumModOps for $type {
|
||||
fn modinv(&self, _b: &Self) -> Self {
|
||||
panic!("modinv");
|
||||
}
|
||||
fn modexp(&self, _a: &Self, _b: &Self) -> Self {
|
||||
panic!("modexp");
|
||||
}
|
||||
fn modsq(&self, _v: &Self) -> Self {
|
||||
panic!("modsq");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
pub static SMALL_PRIMES: [u32; 310] = [
|
||||
2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
|
||||
31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
|
||||
73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
|
||||
127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
|
||||
179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
|
||||
233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
|
||||
283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
|
||||
353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
|
||||
419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
|
||||
467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
|
||||
547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
|
||||
607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
|
||||
661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
|
||||
739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
|
||||
811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
|
||||
877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
|
||||
947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013,
|
||||
1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069,
|
||||
1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
|
||||
1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223,
|
||||
1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291,
|
||||
1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373,
|
||||
1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451,
|
||||
1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511,
|
||||
1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583,
|
||||
1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657,
|
||||
1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
|
||||
1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811,
|
||||
1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889,
|
||||
1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987,
|
||||
1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053];
|
||||
|
||||
macro_rules! derive_prime_operations
|
||||
{
|
||||
($type: ident, $count: expr) => {
|
||||
impl $type {
|
||||
fn miller_rabin<G: Rng>(&self, g: &mut G, iters: usize)
|
||||
-> bool
|
||||
{
|
||||
let nm1 = self - $type::from(1 as u8);
|
||||
// Quoth Wikipedia:
|
||||
// write n - 1 as 2^r*d with d odd by factoring powers of 2 from n - 1
|
||||
let mut d = nm1.clone();
|
||||
let mut r = 0;
|
||||
while d.is_even() {
|
||||
d >>= 1;
|
||||
r += 1;
|
||||
assert!(r < ($count * 64));
|
||||
}
|
||||
// WitnessLoop: repeat k times
|
||||
'WitnessLoop: for _k in 0..iters {
|
||||
// pick a random integer a in the range [2, n - 2]
|
||||
let a = $type::random_in_range(g, &$type::from(2 as u8), &nm1);
|
||||
// x <- a^d mod n
|
||||
let mut x = a.modexp(&d, &self);
|
||||
// if x = 1 or x = n - 1 then
|
||||
if (&x == &$type::from(1 as u8)) || (&x == &nm1) {
|
||||
// continue WitnessLoop
|
||||
continue 'WitnessLoop;
|
||||
}
|
||||
// repeat r - 1 times:
|
||||
for _i in 0..r {
|
||||
// x <- x^2 mod n
|
||||
x = x.modexp(&$type::from(2 as u8), &self);
|
||||
// if x = 1 then
|
||||
if &x == &$type::from(1 as u8) {
|
||||
// return composite
|
||||
return false;
|
||||
}
|
||||
// if x = n - 1 then
|
||||
if &x == &nm1 {
|
||||
// continue WitnessLoop
|
||||
continue 'WitnessLoop;
|
||||
}
|
||||
}
|
||||
// return composite
|
||||
return false;
|
||||
}
|
||||
// return probably prime
|
||||
true
|
||||
}
|
||||
|
||||
fn random_in_range<G: Rng>(rng: &mut G, min: &$type, max: &$type)
|
||||
-> $type
|
||||
{
|
||||
loop {
|
||||
let candidate = $type::random_number(rng);
|
||||
if (&candidate >= min) && (&candidate < max) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn random_number<G: Rng>(rng: &mut G) -> $type {
|
||||
let mut components = [0; $count];
|
||||
for i in 0..$count {
|
||||
components[i] = rng.gen();
|
||||
}
|
||||
$type{ contents: components }
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptoNumPrimes for $type {
|
||||
fn probably_prime<G: Rng>(&self, g: &mut G, iters: usize) -> bool {
|
||||
for tester in SMALL_PRIMES.iter() {
|
||||
let testvalue = $type::from(*tester);
|
||||
if (self % testvalue).is_zero() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
self.miller_rabin(g, iters)
|
||||
}
|
||||
fn generate_prime<G: Rng>(_g: &mut G, _iters: usize, _e: &Self, _min: &Self) -> Self {
|
||||
panic!("generate_prime");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
macro_rules! construct_signed {
|
||||
($type: ident, $base: ident, $modname: ident) => {
|
||||
#[derive(Clone,PartialEq,Eq)]
|
||||
pub struct $type {
|
||||
negative: bool,
|
||||
value: $base
|
||||
}
|
||||
|
||||
impl Debug for $type {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(),Error> {
|
||||
if self.negative {
|
||||
f.write_str("-")?;
|
||||
} else {
|
||||
f.write_str("+")?;
|
||||
}
|
||||
self.value.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a $type> for $type {
|
||||
fn eq(&self, other: &&$type) -> bool {
|
||||
(self.negative == other.negative) &&
|
||||
(self.value == other.value)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for $type {
|
||||
fn partial_cmp(&self, other: &$type) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for $type {
|
||||
fn cmp(&self, other: &$type) -> Ordering {
|
||||
match (self.negative, other.negative) {
|
||||
(true, true) =>
|
||||
self.value.cmp(&other.value).reverse(),
|
||||
(true, false) => Ordering::Greater,
|
||||
(false, true) => Ordering::Less,
|
||||
(false, false) =>
|
||||
self.value.cmp(&other.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptoNumBase for $type {
|
||||
fn zero() -> $type {
|
||||
$type{ negative: false, value: $base::zero() }
|
||||
}
|
||||
fn max_value() -> $type {
|
||||
$type{ negative: false, value: $base::max_value() }
|
||||
}
|
||||
fn is_zero(&self) -> bool {
|
||||
self.value.is_zero()
|
||||
}
|
||||
fn is_odd(&self) -> bool {
|
||||
self.value.is_odd()
|
||||
}
|
||||
fn is_even(&self) -> bool {
|
||||
self.value.is_even()
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptoNumFastMod for $type {
|
||||
type BarrettMu = <$base as CryptoNumFastMod>::BarrettMu;
|
||||
|
||||
fn barrett_mu(&self) -> Option<Self::BarrettMu> {
|
||||
if self.negative {
|
||||
None
|
||||
} else {
|
||||
self.value.barrett_mu()
|
||||
}
|
||||
}
|
||||
|
||||
fn fastmod(&self, mu: &Self::BarrettMu) -> $type {
|
||||
let res = self.value.fastmod(mu);
|
||||
$type{ negative: self.negative, value: res }
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptoNumSigned for $type {
|
||||
type Unsigned = $base;
|
||||
|
||||
fn new(v: $base) -> $type {
|
||||
$type{ negative: false, value: v.clone() }
|
||||
}
|
||||
fn abs(&self) -> $base {
|
||||
self.value.clone()
|
||||
}
|
||||
fn is_positive(&self) -> bool {
|
||||
!self.negative
|
||||
}
|
||||
fn is_negative(&self) -> bool {
|
||||
self.negative
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for $type {
|
||||
type Output = $type;
|
||||
|
||||
fn neg(self) -> $type {
|
||||
(&self).neg()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Neg for &'a $type {
|
||||
type Output = $type;
|
||||
|
||||
fn neg(self) -> $type {
|
||||
if self.value.is_zero() {
|
||||
$type{ negative: false, value: self.value.clone() }
|
||||
} else {
|
||||
$type{ negative: !self.negative, value: self.value.clone() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_arithmetic!($type,AddAssign,add_assign,Add,add,self,other,{
|
||||
let signs_match = self.negative == other.negative;
|
||||
let ordering = self.value.cmp(&other.value);
|
||||
|
||||
match (signs_match, ordering) {
|
||||
(true, _) =>
|
||||
// if the signs are the same, we maintain the sign and
|
||||
// just increase the magnitude
|
||||
self.value.add_assign(&other.value),
|
||||
(false, Ordering::Equal) => {
|
||||
// if the signs are different and the numbers are equal,
|
||||
// we just set this to zero. However, we actually do the
|
||||
// subtraction to make the timing roughly similar.
|
||||
self.negative = false;
|
||||
self.value.sub_assign(&other.value)
|
||||
}
|
||||
(false, Ordering::Less) => {
|
||||
// if the signs are different and the first one is less
|
||||
// than the second, then we flip the sign and subtract.
|
||||
self.negative = !self.negative;
|
||||
let mut other_copy = other.value.clone();
|
||||
other_copy.sub_assign(&self.value);
|
||||
self.value = other_copy;
|
||||
}
|
||||
(false, Ordering::Greater) => {
|
||||
// if the signs are different and the first one is
|
||||
// greater than the second, then we leave the sign and
|
||||
// subtract.
|
||||
self.value.sub_assign(&other.value)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
define_arithmetic!($type,SubAssign,sub_assign,Sub,sub,self,other,{
|
||||
// this is a bit inefficient, but a heck of a lot easier.
|
||||
let mut other2 = other.clone();
|
||||
other2.negative = !other2.negative;
|
||||
self.add_assign(&other2)
|
||||
});
|
||||
|
||||
define_arithmetic!($type,MulAssign,mul_assign,Mul,mul,self,other,{
|
||||
self.negative = self.negative ^ other.negative;
|
||||
self.value.mul_assign(&other.value);
|
||||
});
|
||||
|
||||
define_arithmetic!($type,DivAssign,div_assign,Div,div,self,other,{
|
||||
self.negative = self.negative ^ other.negative;
|
||||
self.value.div_assign(&other.value);
|
||||
});
|
||||
|
||||
define_arithmetic!($type,RemAssign,rem_assign,Rem,rem,self,other,{
|
||||
self.value.rem_assign(&other.value);
|
||||
});
|
||||
|
||||
generate_signed_conversions!($type, $base);
|
||||
|
||||
#[cfg(test)]
|
||||
mod $modname {
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use super::*;
|
||||
|
||||
impl Arbitrary for $type {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> $type {
|
||||
let value = $base::arbitrary(g);
|
||||
if value.is_zero() {
|
||||
$type{ negative: false, value: value }
|
||||
} else {
|
||||
$type{ negative: g.gen_weighted_bool(2), value: value }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn double_negation(x: $type) -> bool {
|
||||
(- (- &x)) == &x
|
||||
}
|
||||
fn add_identity(x: $type) -> bool {
|
||||
(&x + $type::zero()) == &x
|
||||
}
|
||||
fn add_commutivity(x: $type, y: $type) -> bool {
|
||||
(&x + &y) == (&y + &x)
|
||||
}
|
||||
fn add_associativity(a: $type, b: $type, c: $type) -> bool {
|
||||
// we shift these to get away from rollover
|
||||
let x = $type{ negative: a.negative, value: a.value >> 2 };
|
||||
let y = $type{ negative: b.negative, value: b.value >> 2 };
|
||||
let z = $type{ negative: c.negative, value: c.value >> 2 };
|
||||
(&x + (&y + &z)) == ((&x + &y) + &z)
|
||||
}
|
||||
fn sub_is_add_negation(x: $type, y: $type) -> bool {
|
||||
(&x - &y) == (&x + (- &y))
|
||||
}
|
||||
fn sub_destroys(x: $type) -> bool {
|
||||
(&x - &x) == $type::zero()
|
||||
}
|
||||
fn mul_identity(x: $type) -> bool {
|
||||
(&x * $type::from(1)) == &x
|
||||
}
|
||||
fn mul_commutivity(x: $type, y: $type) -> bool {
|
||||
(&x * &y) == (&y * &x)
|
||||
}
|
||||
fn mul_associativity(a: $type, b: $type, c: $type) -> bool {
|
||||
// we shift these to get away from rollover
|
||||
let s = (a.value.bit_size() / 2) - 2;
|
||||
let x = $type{ negative: a.negative, value: a.value >> s };
|
||||
let y = $type{ negative: b.negative, value: b.value >> s };
|
||||
let z = $type{ negative: c.negative, value: c.value >> s };
|
||||
(&x * (&y * &z)) == ((&x * &y) * &z)
|
||||
}
|
||||
#[ignore]
|
||||
fn div_identity(a: $type) -> bool {
|
||||
&a / $type::from(1) == a
|
||||
}
|
||||
fn div_self_is_one(a: $type) -> bool {
|
||||
(&a / &a) == $type::from(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
use rand::Rng;
|
||||
|
||||
pub trait CryptoNumBase {
|
||||
/// Generate the zero value for this type.
|
||||
fn zero() -> Self;
|
||||
/// Generate the maximum possible value for this type.
|
||||
fn max_value() -> Self;
|
||||
/// Test if the number is zero.
|
||||
fn is_zero(&self) -> bool;
|
||||
/// Test if the number is odd (a.k.a., the low bit is set)
|
||||
fn is_odd(&self) -> bool;
|
||||
/// Test if the number is even (a.k.a., the low bit is clear)
|
||||
fn is_even(&self) -> bool;
|
||||
}
|
||||
|
||||
pub trait CryptoNumSerialization {
|
||||
/// The number of bits used when this number is serialized.
|
||||
fn bit_size(&self) -> usize;
|
||||
/// The number of bytes used when this number is serialized.
|
||||
fn byte_size(&self) -> usize;
|
||||
/// Convert a number to a series of bytes, in standard order (most to
|
||||
/// least significant)
|
||||
fn to_bytes(&self) -> Vec<u8>;
|
||||
/// Convert a series of bytes into the number. The size of the given slice
|
||||
/// must be greater than or equal to the size of the number, and must be
|
||||
/// a multiple of 8 bytes long. Unused bytes should be ignored.
|
||||
fn from_bytes(&[u8]) -> Self;
|
||||
}
|
||||
|
||||
pub trait CryptoNumFastMod {
|
||||
/// A related type that can hold the constant required for Barrett
|
||||
/// reduction.
|
||||
type BarrettMu;
|
||||
|
||||
/// Compute the Barett constant mu, using this as a modulus, which we can
|
||||
/// use later to perform faster mod operations.
|
||||
fn barrett_mu(&self) -> Option<Self::BarrettMu>;
|
||||
/// Faster modulo through the use of the Barrett constant, above.
|
||||
fn fastmod(&self, &Self::BarrettMu) -> Self;
|
||||
}
|
||||
|
||||
pub trait CryptoNumSigned {
|
||||
/// The unsigned type that this type is related to.
|
||||
type Unsigned;
|
||||
|
||||
/// Generate a new signed number based on the given unsigned number.
|
||||
fn new(x: Self::Unsigned) -> Self;
|
||||
/// Get the absolute value of the signed number, turning it back into an
|
||||
/// unsigned number.
|
||||
fn abs(&self) -> Self::Unsigned;
|
||||
/// Test if the number is negative.
|
||||
fn is_negative(&self) -> bool;
|
||||
/// Test if the number is positive.
|
||||
fn is_positive(&self) -> bool;
|
||||
}
|
||||
|
||||
pub trait CryptoNumModOps: Sized
|
||||
{
|
||||
/// Compute the modular inverse of the number.
|
||||
fn modinv(&self, b: &Self) -> Self;
|
||||
/// Raise the number to the power of the first value, mod the second.
|
||||
fn modexp(&self, a: &Self, b: &Self) -> Self;
|
||||
/// Square the number, mod the given value.
|
||||
fn modsq(&self, v: &Self) -> Self;
|
||||
}
|
||||
|
||||
pub trait CryptoNumPrimes
|
||||
{
|
||||
/// Determine if the given number is probably prime using a quick spot
|
||||
/// check and Miller-Rabin, using the given random number generator
|
||||
/// and number of iterations.
|
||||
fn probably_prime<G: Rng>(&self, g: &mut G, iters: usize) -> bool;
|
||||
/// Generate a prime using the given random number generator, using
|
||||
/// the given number of rounds to determine if the number is probably
|
||||
/// prime. The other two numbers are a number for which the generator
|
||||
/// should have a GCD of 1, and the minimum value for the number.
|
||||
fn generate_prime<G: Rng>(g: &mut G, iters: usize, e: &Self, min: &Self)
|
||||
-> Self;
|
||||
}
|
||||
@@ -1,685 +0,0 @@
|
||||
macro_rules! construct_unsigned {
|
||||
($type: ident, $barrett: ident, $modname: ident, $count: expr) => {
|
||||
#[derive(Clone)]
|
||||
pub struct $type {
|
||||
contents: [u64; $count]
|
||||
}
|
||||
|
||||
pub struct $barrett {
|
||||
k: usize,
|
||||
progenitor: $type,
|
||||
contents: [u64; $count + 1]
|
||||
}
|
||||
|
||||
impl PartialEq for $type {
|
||||
fn eq(&self, other: &$type) -> bool {
|
||||
for i in 0..$count {
|
||||
if self.contents[i] != other.contents[i] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for $type {}
|
||||
|
||||
impl Debug for $type {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(),Error> {
|
||||
f.write_str("CryptoNum{{ ")?;
|
||||
f.debug_list().entries(self.contents.iter()).finish()?;
|
||||
f.write_str(" }}")
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for $barrett {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(),Error> {
|
||||
f.write_str("BarrettMu{{ ")?;
|
||||
f.write_fmt(format_args!("k = {}, ", self.k))?;
|
||||
f.write_fmt(format_args!("progen = {:?}, ",self.progenitor))?;
|
||||
f.write_str("contents: ")?;
|
||||
f.debug_list().entries(self.contents.iter()).finish()?;
|
||||
f.write_str(" }}")
|
||||
}
|
||||
}
|
||||
|
||||
generate_unsigned_conversions!($type, $count);
|
||||
|
||||
impl PartialOrd for $type {
|
||||
fn partial_cmp(&self, other: &$type) -> Option<Ordering> {
|
||||
Some(generic_cmp(&self.contents, &other.contents))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for $type {
|
||||
fn cmp(&self, other: &$type) -> Ordering {
|
||||
generic_cmp(&self.contents, &other.contents)
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for $type {
|
||||
type Output = $type;
|
||||
|
||||
fn not(self) -> $type {
|
||||
let mut output = self.clone();
|
||||
generic_not(&mut output.contents);
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Not for &'a $type {
|
||||
type Output = $type;
|
||||
|
||||
fn not(self) -> $type {
|
||||
let mut output = self.clone();
|
||||
generic_not(&mut output.contents);
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
define_arithmetic!($type,BitOrAssign,bitor_assign,BitOr,bitor,self,other,{
|
||||
generic_bitor(&mut self.contents, &other.contents);
|
||||
});
|
||||
define_arithmetic!($type,BitAndAssign,bitand_assign,BitAnd,bitand,self,other,{
|
||||
generic_bitand(&mut self.contents, &other.contents);
|
||||
});
|
||||
define_arithmetic!($type,BitXorAssign,bitxor_assign,BitXor,bitxor,self,other,{
|
||||
generic_bitxor(&mut self.contents, &other.contents);
|
||||
});
|
||||
define_arithmetic!($type,AddAssign,add_assign,Add,add,self,other,{
|
||||
generic_add(&mut self.contents, &other.contents);
|
||||
});
|
||||
define_arithmetic!($type,SubAssign,sub_assign,Sub,sub,self,other,{
|
||||
generic_sub(&mut self.contents, &other.contents);
|
||||
});
|
||||
define_arithmetic!($type,MulAssign,mul_assign,Mul,mul,self,other,{
|
||||
let copy = self.contents.clone();
|
||||
generic_mul(&mut self.contents, ©, &other.contents);
|
||||
});
|
||||
define_arithmetic!($type,DivAssign,div_assign,Div,div,self,other,{
|
||||
let mut dead = [0; $count];
|
||||
let copy = self.contents.clone();
|
||||
generic_div(©, &other.contents,
|
||||
&mut self.contents, &mut dead);
|
||||
});
|
||||
define_arithmetic!($type,RemAssign,rem_assign,Rem,rem,self,other,{
|
||||
let mut dead = [0; $count];
|
||||
let copy = self.contents.clone();
|
||||
generic_div(©, &other.contents,
|
||||
&mut dead, &mut self.contents);
|
||||
});
|
||||
|
||||
shifts!($type, usize);
|
||||
shifts!($type, u64);
|
||||
shifts!($type, i64);
|
||||
shifts!($type, u32);
|
||||
shifts!($type, i32);
|
||||
shifts!($type, u16);
|
||||
shifts!($type, i16);
|
||||
shifts!($type, u8);
|
||||
shifts!($type, i8);
|
||||
|
||||
impl CryptoNumBase for $type {
|
||||
fn zero() -> $type {
|
||||
$type { contents: [0; $count] }
|
||||
}
|
||||
|
||||
fn max_value() -> $type {
|
||||
$type { contents: [0xFFFFFFFFFFFFFFFF; $count] }
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
for x in self.contents.iter() {
|
||||
if *x != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn is_odd(&self) -> bool {
|
||||
(self.contents[0] & 1) == 1
|
||||
}
|
||||
|
||||
fn is_even(&self) -> bool {
|
||||
(self.contents[0] & 1) == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptoNumSerialization for $type {
|
||||
fn bit_size(&self) -> usize {
|
||||
$count * 64
|
||||
}
|
||||
|
||||
fn byte_size(&self) -> usize {
|
||||
$count * 8
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut res = Vec::with_capacity($count * 8);
|
||||
for x in self.contents.iter() {
|
||||
res.push( (x >> 56) as u8 );
|
||||
res.push( (x >> 48) as u8 );
|
||||
res.push( (x >> 40) as u8 );
|
||||
res.push( (x >> 32) as u8 );
|
||||
res.push( (x >> 24) as u8 );
|
||||
res.push( (x >> 16) as u8 );
|
||||
res.push( (x >> 8) as u8 );
|
||||
res.push( (x >> 0) as u8 );
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn from_bytes(x: &[u8]) -> $type {
|
||||
let mut res = $type::zero();
|
||||
let mut i = 0;
|
||||
|
||||
assert!(x.len() >= ($count * 8));
|
||||
for chunk in x.chunks(8) {
|
||||
assert!(chunk.len() == 8);
|
||||
res.contents[i] = ((chunk[0] as u64) << 56) |
|
||||
((chunk[1] as u64) << 48) |
|
||||
((chunk[2] as u64) << 40) |
|
||||
((chunk[3] as u64) << 32) |
|
||||
((chunk[4] as u64) << 24) |
|
||||
((chunk[5] as u64) << 16) |
|
||||
((chunk[6] as u64) << 8) |
|
||||
((chunk[7] as u64) << 0);
|
||||
i += 1;
|
||||
}
|
||||
assert!(i == $count);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
derive_barrett!($type, $barrett, $count);
|
||||
derive_modulo_operations!($type);
|
||||
derive_prime_operations!($type, $count);
|
||||
|
||||
impl Into<BigInt> for $type {
|
||||
fn into(self) -> BigInt {
|
||||
panic!("into bigint")
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<BigUint> for $type {
|
||||
fn into(self) -> BigUint {
|
||||
panic!("into big uint")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BigInt> for $type {
|
||||
fn from(_x: BigInt) -> Self {
|
||||
panic!("from bigint")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BigUint> for $type {
|
||||
fn from(_x: BigUint) -> Self {
|
||||
panic!("from biguint")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod $modname {
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use super::*;
|
||||
|
||||
impl Arbitrary for $type {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> $type {
|
||||
let mut res = [0; $count];
|
||||
|
||||
for i in 0..$count {
|
||||
res[i] = g.next_u64();
|
||||
}
|
||||
$type{ contents: res }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builders() {
|
||||
let mut buffer = [0; $count];
|
||||
assert_eq!($type{ contents: buffer }, $type::from(0 as u8));
|
||||
buffer[0] = 0x7F;
|
||||
assert_eq!($type{ contents: buffer }, $type::from(0x7F as u8));
|
||||
buffer[0] = 0x7F7F;
|
||||
assert_eq!($type{ contents: buffer }, $type::from(0x7F7F as u16));
|
||||
buffer[0] = 0xCA5CADE5;
|
||||
assert_eq!($type{ contents: buffer },
|
||||
$type::from(0xCA5CADE5 as u32));
|
||||
assert_eq!($type{ contents: buffer },
|
||||
$type::from(0xCA5CADE5 as u32));
|
||||
buffer[0] = 0xFFFFFFFFFFFFFFFF;
|
||||
assert_eq!($type{ contents: buffer },
|
||||
$type::from(0xFFFFFFFFFFFFFFFF as u64));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max() {
|
||||
let max64: u64 = $type::from(u64::max_value()).into();
|
||||
assert_eq!(max64, u64::max_value());
|
||||
let max64v: u64 = $type::max_value().into();
|
||||
assert_eq!(max64v, u64::max_value());
|
||||
assert_eq!($type::max_value() + $type::from(1 as u8), $type::zero());
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn builder_u8_upgrade_u16(x: u8) -> bool {
|
||||
$type::from(x) == $type::from(x as u16)
|
||||
}
|
||||
fn builder_u16_upgrade_u32(x: u16) -> bool {
|
||||
$type::from(x) == $type::from(x as u32)
|
||||
}
|
||||
fn builder_u32_upgrade_u64(x: u32) -> bool {
|
||||
$type::from(x) == $type::from(x as u64)
|
||||
}
|
||||
fn builder_u8_roundtrips(x: u8) -> bool {
|
||||
let thereback: u8 = $type::from(x).into();
|
||||
x == thereback
|
||||
}
|
||||
fn builder_u16_roundtrips(x: u16) -> bool {
|
||||
let thereback: u16 = $type::from(x).into();
|
||||
x == thereback
|
||||
}
|
||||
fn builder_u32_roundtrips(x: u32) -> bool {
|
||||
let thereback: u32 = $type::from(x).into();
|
||||
x == thereback
|
||||
}
|
||||
fn builder_u64_roundtrips(x: u64) -> bool {
|
||||
let thereback: u64 = $type::from(x).into();
|
||||
x == thereback
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn partial_ord64_works(x: u64, y: u64) -> bool {
|
||||
let xbig = $type::from(x);
|
||||
let ybig = $type::from(y);
|
||||
xbig.partial_cmp(&ybig) == x.partial_cmp(&y)
|
||||
}
|
||||
fn ord64_works(x: u64, y: u64) -> bool {
|
||||
let xbig = $type::from(x);
|
||||
let ybig = $type::from(y);
|
||||
xbig.cmp(&ybig) == x.cmp(&y)
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn and_annulment(x: $type) -> bool {
|
||||
(x & $type::zero()) == $type::zero()
|
||||
}
|
||||
fn or_annulment(x: $type) -> bool {
|
||||
(x | $type::max_value()) == $type::max_value()
|
||||
}
|
||||
fn and_identity(x: $type) -> bool {
|
||||
(&x & $type::max_value()) == x
|
||||
}
|
||||
fn or_identity(x: $type) -> bool {
|
||||
(&x | $type::zero()) == x
|
||||
}
|
||||
fn and_idempotent(x: $type) -> bool {
|
||||
(&x & &x) == x
|
||||
}
|
||||
fn or_idempotent(x: $type) -> bool {
|
||||
(&x | &x) == x
|
||||
}
|
||||
fn and_complement(x: $type) -> bool {
|
||||
(&x & &x) == x
|
||||
}
|
||||
fn or_complement(x: $type) -> bool {
|
||||
(&x | !&x) == $type::max_value()
|
||||
}
|
||||
fn and_commutative(x: $type, y: $type) -> bool {
|
||||
(&x & &y) == (&y & &x)
|
||||
}
|
||||
fn or_commutative(x: $type, y: $type) -> bool {
|
||||
(&x | &y) == (&y | &x)
|
||||
}
|
||||
fn double_negation(x: $type) -> bool {
|
||||
!!&x == x
|
||||
}
|
||||
fn or_distributive(a: $type, b: $type, c: $type) -> bool {
|
||||
(&a & (&b | &c)) == ((&a & &b) | (&a & &c))
|
||||
}
|
||||
fn and_distributive(a: $type, b: $type, c: $type) -> bool {
|
||||
(&a | (&b & &c)) == ((&a | &b) & (&a | &c))
|
||||
}
|
||||
fn or_absorption(a: $type, b: $type) -> bool {
|
||||
(&a | (&a & &b)) == a
|
||||
}
|
||||
fn and_absorption(a: $type, b: $type) -> bool {
|
||||
(&a & (&a | &b)) == a
|
||||
}
|
||||
fn or_associative(a: $type, b: $type, c: $type) -> bool {
|
||||
(&a | (&b | &c)) == ((&a | &b) | &c)
|
||||
}
|
||||
fn and_associative(a: $type, b: $type, c: $type) -> bool {
|
||||
(&a & (&b & &c)) == ((&a & &b) & &c)
|
||||
}
|
||||
fn xor_as_defined(a: $type, b: $type) -> bool {
|
||||
(&a ^ &b) == ((&a | &b) & !(&a & &b))
|
||||
}
|
||||
fn small_or_check(x: u64, y: u64) -> bool {
|
||||
let x512 = $type::from(x);
|
||||
let y512 = $type::from(y);
|
||||
let z512 = x512 | y512;
|
||||
let res: u64 = z512.into();
|
||||
res == (x | y)
|
||||
}
|
||||
fn small_and_check(x: u64, y: u64) -> bool {
|
||||
let x512 = $type::from(x);
|
||||
let y512 = $type::from(y);
|
||||
let z512 = x512 & y512;
|
||||
let res: u64 = z512.into();
|
||||
res == (x & y)
|
||||
}
|
||||
fn small_xor_check(x: u64, y: u64) -> bool {
|
||||
let x512 = $type::from(x);
|
||||
let y512 = $type::from(y);
|
||||
let z512 = x512 ^ y512;
|
||||
let res: u64 = z512.into();
|
||||
res == (x ^ y)
|
||||
}
|
||||
fn small_neg_check(x: u64) -> bool {
|
||||
let x512 = $type::from(x);
|
||||
let z512 = !x512;
|
||||
let res: u64 = z512.into();
|
||||
res == !x
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shl_tests() {
|
||||
let ones = [1; $count];
|
||||
assert_eq!($type{ contents: ones.clone() } << 0,
|
||||
$type{ contents: ones.clone() });
|
||||
let mut notones = [0; $count];
|
||||
for i in 0..$count {
|
||||
notones[i] = (i + 1) as u64;
|
||||
}
|
||||
assert_eq!($type{ contents: notones.clone() } << 0,
|
||||
$type{ contents: notones.clone() });
|
||||
assert_eq!($type{ contents: ones.clone() } << ($count * 64),
|
||||
$type::zero());
|
||||
assert_eq!($type::from(2 as u8) << 1, $type::from(4 as u8));
|
||||
let mut buffer = [0; $count];
|
||||
buffer[1] = 1;
|
||||
assert_eq!($type::from(1 as u8) << 64,
|
||||
$type{ contents: buffer.clone() });
|
||||
buffer[0] = 0xFFFFFFFFFFFFFFFE;
|
||||
assert_eq!($type::from(0xFFFFFFFFFFFFFFFF as u64) << 1,
|
||||
$type{ contents: buffer.clone() });
|
||||
buffer[0] = 0;
|
||||
buffer[1] = 4;
|
||||
assert_eq!($type::from(1 as u8) << 66,
|
||||
$type{ contents: buffer.clone() });
|
||||
assert_eq!($type::from(1 as u8) << 1, $type::from(2 as u8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shr_tests() {
|
||||
let ones = [1; $count];
|
||||
assert_eq!($type{ contents: ones.clone() } >> 0,
|
||||
$type{ contents: ones.clone() });
|
||||
let mut notones = [0; $count];
|
||||
for i in 0..$count {
|
||||
notones[i] = (i + 1) as u64;
|
||||
}
|
||||
assert_eq!($type{ contents: ones.clone() } >> 0,
|
||||
$type{ contents: ones.clone() });
|
||||
assert_eq!($type{ contents: ones.clone() } >> ($count * 64),
|
||||
$type::zero());
|
||||
assert_eq!($type::from(2 as u8) >> 1,
|
||||
$type::from(1 as u8));
|
||||
let mut oneleft = [0; $count];
|
||||
oneleft[1] = 1;
|
||||
assert_eq!($type{ contents: oneleft.clone() } >> 1,
|
||||
$type::from(0x8000000000000000 as u64));
|
||||
assert_eq!($type{ contents: oneleft.clone() } >> 64,
|
||||
$type::from(1 as u64));
|
||||
oneleft[1] = 4;
|
||||
assert_eq!($type{ contents: oneleft.clone() } >> 66,
|
||||
$type::from(1 as u64));
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn shift_mask_equivr(x: $type, in_shift: usize) -> bool {
|
||||
let shift = in_shift % ($count * 64);
|
||||
let mask = $type::max_value() << shift;
|
||||
let masked_x = &x & mask;
|
||||
let shift_maskr = (x >> shift) << shift;
|
||||
shift_maskr == masked_x
|
||||
}
|
||||
fn shift_mask_equivl(x: $type, in_shift: usize) -> bool {
|
||||
let shift = in_shift % ($count * 64);
|
||||
let mask = $type::max_value() >> shift;
|
||||
let masked_x = &x & mask;
|
||||
let shift_maskl = (x << shift) >> shift;
|
||||
shift_maskl == masked_x
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_tests() {
|
||||
let ones = [1; $count];
|
||||
let twos = [2; $count];
|
||||
assert_eq!($type{ contents: ones.clone() } +
|
||||
$type{ contents: ones.clone() },
|
||||
$type{ contents: twos.clone() });
|
||||
let mut buffer = [0; $count];
|
||||
buffer[1] = 1;
|
||||
assert_eq!($type::from(1 as u64) +
|
||||
$type::from(0xFFFFFFFFFFFFFFFF as u64),
|
||||
$type{ contents: buffer.clone() });
|
||||
let mut high = [0; $count];
|
||||
high[$count - 1] = 0xFFFFFFFFFFFFFFFF;
|
||||
buffer[1] = 0;
|
||||
buffer[$count - 1] = 1;
|
||||
assert_eq!($type{ contents: buffer } + $type{ contents: high },
|
||||
$type{ contents: [0; $count] });
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn add_symmetry(a: $type, b: $type) -> bool {
|
||||
(&a + &b) == (&b + &a)
|
||||
}
|
||||
fn add_commutivity(a: $type, b: $type, c: $type) -> bool {
|
||||
(&a + (&b + &c)) == ((&a + &b) + &c)
|
||||
}
|
||||
fn add_identity(a: $type) -> bool {
|
||||
(&a + $type::zero()) == a
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sub_tests() {
|
||||
let ones = [1; $count];
|
||||
assert_eq!($type{ contents: ones.clone() } -
|
||||
$type{ contents: ones.clone() },
|
||||
$type::zero());
|
||||
let mut buffer = [0; $count];
|
||||
buffer[1] = 1;
|
||||
assert_eq!($type{contents:buffer.clone()} - $type::from(1 as u8),
|
||||
$type::from(0xFFFFFFFFFFFFFFFF as u64));
|
||||
assert_eq!($type::zero() - $type::from(1 as u8),
|
||||
$type::max_value());
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn sub_destroys(a: $type) -> bool {
|
||||
(&a - &a) == $type::zero()
|
||||
}
|
||||
fn sub_add_ident(a: $type, b: $type) -> bool {
|
||||
((&a - &b) + &b) == a
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mul_tests() {
|
||||
assert_eq!($type::from(1 as u8) * $type::from(1 as u8),
|
||||
$type::from(1 as u8));
|
||||
assert_eq!($type::from(1 as u8) * $type::from(0 as u8),
|
||||
$type::from(0 as u8));
|
||||
assert_eq!($type::from(1 as u8) * $type::from(2 as u8),
|
||||
$type::from(2 as u8));
|
||||
let mut temp = $type::zero();
|
||||
temp.contents[0] = 1;
|
||||
temp.contents[1] = 0xFFFFFFFFFFFFFFFE;
|
||||
assert_eq!($type::from(0xFFFFFFFFFFFFFFFF as u64) *
|
||||
$type::from(0xFFFFFFFFFFFFFFFF as u64),
|
||||
temp);
|
||||
let effs = $type{ contents: [0xFFFFFFFFFFFFFFFF; $count] };
|
||||
assert_eq!($type::from(1 as u8) * &effs, effs);
|
||||
temp = effs.clone();
|
||||
temp.contents[0] = temp.contents[0] - 1;
|
||||
assert_eq!($type::from(2 as u8) * &effs, temp);
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn mul_symmetry(a: $type, b: $type) -> bool {
|
||||
(&a * &b) == (&b * &a)
|
||||
}
|
||||
fn mul_commutivity(a: $type, b: $type, c: $type) -> bool {
|
||||
(&a * (&b * &c)) == ((&a * &b) * &c)
|
||||
}
|
||||
fn mul_identity(a: $type) -> bool {
|
||||
(&a * $type::from(1 as u8)) == a
|
||||
}
|
||||
fn mul_zero(a: $type) -> bool {
|
||||
(&a * $type::zero()) == $type::zero()
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn addmul_distribution(a: $type, b: $type, c: $type) -> bool {
|
||||
(&a * (&b + &c)) == ((&a * &b) + (&a * &c))
|
||||
}
|
||||
fn submul_distribution(a: $type, b: $type, c: $type) -> bool {
|
||||
(&a * (&b - &c)) == ((&a * &b) - (&a * &c))
|
||||
}
|
||||
fn mul2shift1_equiv(a: $type) -> bool {
|
||||
(&a << 1) == (&a * $type::from(2 as u8))
|
||||
}
|
||||
fn mul16shift4_equiv(a: $type) -> bool {
|
||||
(&a << 4) == (&a * $type::from(16 as u8))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn div_tests() {
|
||||
assert_eq!($type::from(2 as u8) / $type::from(2 as u8),
|
||||
$type::from(1 as u8));
|
||||
assert_eq!($type::from(2 as u8) / $type::from(1 as u8),
|
||||
$type::from(2 as u8));
|
||||
assert_eq!($type::from(4 as u8) / $type::from(3 as u8),
|
||||
$type::from(1 as u8));
|
||||
assert_eq!($type::from(4 as u8) / $type::from(5 as u8),
|
||||
$type::from(0 as u8));
|
||||
assert_eq!($type::from(4 as u8) / $type::from(4 as u8),
|
||||
$type::from(1 as u8));
|
||||
let mut temp1 = $type::zero();
|
||||
let mut temp2 = $type::zero();
|
||||
temp1.contents[$count - 1] = 4;
|
||||
temp2.contents[$count - 1] = 4;
|
||||
assert_eq!(&temp1 / temp2, $type::from(1 as u8));
|
||||
assert_eq!(&temp1 / $type::from(1 as u8), temp1);
|
||||
temp1.contents[$count - 1] = u64::max_value();
|
||||
assert_eq!(&temp1 / $type::from(1 as u8), temp1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn div0_fails() {
|
||||
$type::from(0xabcd as u16) / $type::zero();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mod_tests() {
|
||||
assert_eq!($type::from(4 as u16) % $type::from(5 as u16),
|
||||
$type::from(4 as u16));
|
||||
assert_eq!($type::from(5 as u16) % $type::from(4 as u16),
|
||||
$type::from(1 as u16));
|
||||
let fives = $type{ contents: [5; $count] };
|
||||
let fours = $type{ contents: [4; $count] };
|
||||
let ones = $type{ contents: [1; $count] };
|
||||
assert_eq!(fives % fours, ones);
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
#[ignore]
|
||||
fn div_identity(a: $type) -> bool {
|
||||
&a / $type::from(1 as u16) == a
|
||||
}
|
||||
fn div_self_is_one(a: $type) -> bool {
|
||||
if a == $type::zero() {
|
||||
return true;
|
||||
}
|
||||
&a / &a == $type::from(1 as u16)
|
||||
}
|
||||
fn euclid_is_alive(a: $type, b: $type) -> bool {
|
||||
let q = &a / &b;
|
||||
let r = &a % &b;
|
||||
a == ((b * q) + r)
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn serialization_inverts(a: $type) -> bool {
|
||||
let bytes = a.to_bytes();
|
||||
let b = $type::from_bytes(&bytes);
|
||||
a == b
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn fastmod_works(a: $type, b: $type) -> bool {
|
||||
assert!(b != $type::zero());
|
||||
match b.barrett_mu() {
|
||||
None =>
|
||||
true,
|
||||
Some(barrett) => {
|
||||
a.fastmod(&barrett) == (&a % &b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! shifts {
|
||||
($type: ident, $shtype: ty) => {
|
||||
shifts!($type, $shtype, ShlAssign, shl_assign, Shl, shl, generic_shl);
|
||||
shifts!($type, $shtype, ShrAssign, shr_assign, Shr, shr, generic_shr);
|
||||
};
|
||||
|
||||
($type: ident, $shtype: ty, $asncl: ident, $asnfn: ident,
|
||||
$cl: ident, $fn: ident, $impl: ident) => {
|
||||
impl $asncl<$shtype> for $type {
|
||||
fn $asnfn(&mut self, amount: $shtype) {
|
||||
let copy = self.contents.clone();
|
||||
$impl(&mut self.contents, ©, amount as usize);
|
||||
}
|
||||
}
|
||||
|
||||
impl $cl<$shtype> for $type {
|
||||
type Output = $type;
|
||||
|
||||
fn $fn(self, rhs: $shtype) -> $type {
|
||||
let mut res = self.clone();
|
||||
$impl(&mut res.contents, &self.contents, rhs as usize);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> $cl<$shtype> for &'a $type {
|
||||
type Output = $type;
|
||||
|
||||
fn $fn(self, rhs: $shtype) -> $type {
|
||||
let mut res = self.clone();
|
||||
$impl(&mut res.contents, &self.contents, rhs as usize);
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/dsa/errors.rs
Normal file
20
src/dsa/errors.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use simple_asn1::ASN1DecodeErr;
|
||||
use rand;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DSAError {
|
||||
RandomGenError(rand::Error),
|
||||
ASN1DecodeErr(ASN1DecodeErr)
|
||||
}
|
||||
|
||||
impl From<rand::Error> for DSAError {
|
||||
fn from(e: rand::Error) -> DSAError {
|
||||
DSAError::RandomGenError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ASN1DecodeErr> for DSAError {
|
||||
fn from(e: ASN1DecodeErr) -> DSAError {
|
||||
DSAError::ASN1DecodeErr(e)
|
||||
}
|
||||
}
|
||||
118
src/dsa/mod.rs
Normal file
118
src/dsa/mod.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
//! If you want to use this module to generate keys, which you really
|
||||
//! really shouldn't, there are two ways to do so, depending on whether
|
||||
//! you've previously agreed on a set of DSA parameters for this key
|
||||
//! pair. If you haven't, you can generate the parameters using a good
|
||||
//! random number generator.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use simple_crypto::dsa::{DSAKeyPair,DSAParameters,L2048N256};
|
||||
//! use simple_crypto::sha::SHA224;
|
||||
//!
|
||||
//! // Generate a set of DSA parameters, assuming you don't have
|
||||
//! // them already
|
||||
//! let mut rng = rand::rngs::OsRng::new().unwrap();
|
||||
//! let params = L2048N256::generate(&mut rng);
|
||||
//!
|
||||
//! // Given those parameters, you can generate a key pair like so:
|
||||
//! let kp = DSAKeyPair::<L2048N256>::generate(¶ms, &mut rng);
|
||||
//! // Keeping in mind that you can re-use the parameters across multiple
|
||||
//! // keys, and that their secrecy isn't paramout for the security of the
|
||||
//! // algorithm.
|
||||
//!
|
||||
//! // Now that you have this key pair, you can sign and verify messages
|
||||
//! // using it. For example, to sign the vector [0,1,2,3,4] with SHA224
|
||||
//! // and then verify that signature, we would write:
|
||||
//! let msg = vec![0,1,2,3,4];
|
||||
//! let sig = kp.private.sign::<SHA224>(&msg);
|
||||
//! assert!( kp.public.verify::<SHA224>(&msg, &sig) );
|
||||
//! ```
|
||||
|
||||
|
||||
mod errors;
|
||||
mod params;
|
||||
mod private;
|
||||
mod public;
|
||||
/// Support for RFC6979 signing, which provides a secure way to generate
|
||||
/// signatures without the use of a random number generator. This is used
|
||||
/// in DSA signing as well as in ECDSA signing, but appears here because
|
||||
/// ... well, because it was written for DSA first, both historically
|
||||
/// (I think) and by me.
|
||||
pub mod rfc6979;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use self::params::*;
|
||||
pub use self::private::*;
|
||||
pub use self::public::*;
|
||||
|
||||
use cryptonum::unsigned::*;
|
||||
use rand::Rng;
|
||||
use rand::distributions::Standard;
|
||||
use super::KeyPair;
|
||||
|
||||
/// A DSA key pair, for use in signing and signature verification. Note
|
||||
/// that you probably shouldn't be using DSA any more; ECDSA or ED25519
|
||||
/// are probably better options.
|
||||
///
|
||||
/// DSA key pairs are parameterized by their DSA parameters, so that
|
||||
/// you can't accidentally use them in the wrong place.
|
||||
pub struct DSAKeyPair<P: DSAParameters>
|
||||
{
|
||||
pub private: DSAPrivateKey<P>,
|
||||
pub public: DSAPublicKey<P>
|
||||
}
|
||||
|
||||
impl<P: DSAParameters> KeyPair for DSAKeyPair<P>
|
||||
{
|
||||
type Private = DSAPrivateKey<P>;
|
||||
type Public = DSAPublicKey<P>;
|
||||
|
||||
fn new(public: DSAPublicKey<P>, private: DSAPrivateKey<P>) -> DSAKeyPair<P>
|
||||
{
|
||||
DSAKeyPair{ private, public }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! generate_dsa_pair {
|
||||
($ptype: ident, $ltype: ident, $ntype: ident, $nbig: ident) => {
|
||||
impl DSAKeyPair<$ptype>
|
||||
{
|
||||
/// Generate a DSA key pair using the given parameters and random
|
||||
/// number generator. Please make sure that the RNG you're using
|
||||
/// is suitable for key generators (look for the term "cryptographic"
|
||||
/// or "crypto strong" in its documentation, or see if it matches
|
||||
/// any of the NIST-suggested RNG algorithms).
|
||||
pub fn generate<G: Rng>(params: &$ptype, rng: &mut G) -> Self
|
||||
{
|
||||
// 1. N = len(q); L = len(p);
|
||||
let n = $ptype::n_size();
|
||||
// 2. If the (L,N) pair is invalid, then return an ERROR indicator,
|
||||
// Invalid_x, and Invalid_y.
|
||||
// 3. requested_security_strength = the security strength associated
|
||||
// with the (L, N) pair; see SP 800-57.
|
||||
// 4. Obtain a string of N+64 returned_bits from an RBG with a security
|
||||
// strength of requested_security_strength or more. If an ERROR
|
||||
// indication is returned, then return an ERROR indication,
|
||||
// Invalid_x, and Invalid_y.
|
||||
let returned_bits: Vec<u8> = rng.sample_iter(&Standard).take( (n + 64) / 8 ).collect();
|
||||
// 5. Convert returned_bits to the (non-negative) integer c.
|
||||
let c = $nbig::from_bytes(&returned_bits);
|
||||
// 6. x = (c mod (q-1)) + 1.
|
||||
let one = $nbig::from(1 as u64);
|
||||
let qbig = $nbig::from(¶ms.q);
|
||||
let x = $ntype::from( (&c % (&qbig - &one)) + &one );
|
||||
// 7. y = g^x mod p
|
||||
let y = params.g.modexp(&$ltype::from(&x), ¶ms.p);
|
||||
// 8. Return SUCCESS, x, and y.
|
||||
let private = DSAPrivateKey::<$ptype>::new(params.clone(), x);
|
||||
let public = DSAPublicKey::<$ptype>::new(params.clone(), y);
|
||||
DSAKeyPair { private, public }
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
generate_dsa_pair!(L1024N160, U1024, U192, U256);
|
||||
generate_dsa_pair!(L2048N224, U2048, U256, U384);
|
||||
generate_dsa_pair!(L2048N256, U2048, U256, U384);
|
||||
generate_dsa_pair!(L3072N256, U3072, U256, U384);
|
||||
225
src/dsa/params.rs
Normal file
225
src/dsa/params.rs
Normal file
@@ -0,0 +1,225 @@
|
||||
use cryptonum::unsigned::{CryptoNum,Decoder,Encoder,ModExp,PrimeGen};
|
||||
use cryptonum::unsigned::{U192,U256,U1024,U2048,U3072};
|
||||
use sha::{Hash,SHA256};
|
||||
use simple_asn1::{ToASN1,ASN1Block,ASN1Class,ASN1EncodeErr};
|
||||
use rand::Rng;
|
||||
use utils::TranslateNums;
|
||||
|
||||
/// A trait that describes what a set of DSA parameters must support in
|
||||
/// order to be used by the rest of the system.
|
||||
pub trait DSAParameters : ToASN1
|
||||
{
|
||||
/// The fixed-width, unsigned type of values in L.
|
||||
type L;
|
||||
/// The fixed-width, unsigned type of values in N.
|
||||
type N;
|
||||
|
||||
/// Given a `p`, `g`, and `q`, generate a new structure that includes
|
||||
/// this information. Optionally, do any cross-checks needed.
|
||||
fn new(p: Self::L, g: Self::L, q: Self::N) -> Self;
|
||||
/// Generate a new set of DSA parameters given the provided random
|
||||
/// number generator. Just as with key generation, this should be a
|
||||
/// cryptographically-strong random number generator. If it's not,
|
||||
/// you may be writing compromisable code.
|
||||
fn generate<G: Rng>(rng: &mut G) -> Self;
|
||||
/// Return the size of values of N in bits.
|
||||
fn n_size() -> usize;
|
||||
/// Return the size of values of L in bits.
|
||||
fn l_size() -> usize;
|
||||
/// Return the size of `q` in this particular instance of the parameters.
|
||||
/// (Should be the same as `n_size()`, and the default implementation
|
||||
/// simply uses `n_size(), but included for convenience)
|
||||
fn n_bits(&self) -> usize {
|
||||
Self::n_size()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! generate_parameters {
|
||||
($name: ident, $ltype: ident, $ntype: ident, $l: expr, $n: expr) => {
|
||||
/// DSA parameters to the given L and N, with the values given in bits.
|
||||
#[derive(Clone,PartialEq)]
|
||||
pub struct $name {
|
||||
pub p: $ltype,
|
||||
pub g: $ltype,
|
||||
pub q: $ntype
|
||||
}
|
||||
|
||||
impl ToASN1 for $name {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
|
||||
{
|
||||
let p = ASN1Block::Integer(c, 0, self.p.to_num());
|
||||
let q = ASN1Block::Integer(c, 0, self.q.to_num());
|
||||
let g = ASN1Block::Integer(c, 0, self.g.to_num());
|
||||
Ok(vec![ASN1Block::Sequence(c, 0, vec![p, q, g])])
|
||||
}
|
||||
}
|
||||
|
||||
impl DSAParameters for $name
|
||||
{
|
||||
type L = $ltype;
|
||||
type N = $ntype;
|
||||
|
||||
fn new(p: $ltype, g: $ltype, q: $ntype) -> $name
|
||||
{
|
||||
$name{ p: p, g: g, q: q }
|
||||
}
|
||||
|
||||
fn generate<G: Rng>(rng: &mut G) -> $name
|
||||
{
|
||||
let (p, q, _, _) = $name::generate_primes(rng);
|
||||
let g = $name::generate_g(rng, &p, &q);
|
||||
$name{ p: p, g: g, q: q }
|
||||
}
|
||||
|
||||
fn l_size() -> usize {
|
||||
$l
|
||||
}
|
||||
|
||||
fn n_size() -> usize {
|
||||
$n
|
||||
}
|
||||
}
|
||||
|
||||
impl $name
|
||||
{
|
||||
pub fn generate_primes<G: Rng>(rng: &mut G) -> ($ltype,$ntype,U256,usize)
|
||||
{
|
||||
// This is A.1.1.2 from FIPS 186-4, with seedlen hardcoded to 256
|
||||
// (since that's guaranteed to be >= N), and with the hash
|
||||
// hardcoded as SHA-256.
|
||||
#[allow(non_snake_case)]
|
||||
let L = $ltype::bit_length();
|
||||
#[allow(non_snake_case)]
|
||||
let N = $ntype::bit_length();
|
||||
let seedlen = 256;
|
||||
let outlen = 256;
|
||||
//
|
||||
// 1. Check that the (L,N) pair is in the list of acceptable
|
||||
// (L,N) pairs (see Section 4.2). If the pair is not in the
|
||||
// list, then return INVALID.
|
||||
// [This is always true.]
|
||||
//
|
||||
// 2. If (seedlen < N), then return INVALID.
|
||||
// [This is always true.]
|
||||
//
|
||||
// 3. n = L/outlen – 1.
|
||||
let n = ((L + 255) / 256) - 1;
|
||||
// 4. b = L – 1 – (n ∗ outlen).
|
||||
let b = L - 1 - (n * outlen);
|
||||
loop {
|
||||
// 5. Get an arbitrary sequence of seedlen bits as the
|
||||
// domain_parameter_seed.
|
||||
let domain_parameter_seed: U256 = rng.gen();
|
||||
// 6. U = Hash (domain_parameter_seed) mod 2^(N–1).
|
||||
let mut ubytes = hash(&domain_parameter_seed, 32);
|
||||
while ubytes.len() > (N / 8) { ubytes.remove(0); }
|
||||
#[allow(non_snake_case)]
|
||||
let U = $ntype::from_bytes(&ubytes);
|
||||
// 7. q = 2^(N–1) + U + 1 – (U mod 2).
|
||||
let highbit = $ntype::from(1u64) << (N - 1);
|
||||
let lowbit = $ntype::from(1u64);
|
||||
let q = U | highbit | lowbit;
|
||||
// 8. Test whether or not q is prime as specified in Appendix C.3.
|
||||
let q_is_prime = q.probably_prime(rng, 40);
|
||||
// 9. If q is not a prime, then go to step 5.
|
||||
if !q_is_prime {
|
||||
continue;
|
||||
}
|
||||
// 10. offset = 1.
|
||||
let mut offset = 1;
|
||||
// 11. For counter = 0 to (4L – 1) do
|
||||
for counter in 0..(4*L)-1 {
|
||||
// 11.1 For j = 0 to n do
|
||||
// Vj = Hash ((domain_parameter_seed + offset + j) mod 2^seedlen).
|
||||
#[allow(non_snake_case)]
|
||||
let mut V = Vec::new();
|
||||
for j in 0..n {
|
||||
let val = &domain_parameter_seed + U256::from(offset + j);
|
||||
let bytes = hash(&val, 32);
|
||||
assert_eq!(seedlen, bytes.len() * 8);
|
||||
V.push(bytes);
|
||||
}
|
||||
// 11.2 W = V_0 + ( V_1 ∗ 2^outlen) + ... + ( V_(n–1) ∗ 2^(n –1) ∗ outlen) + ((V_n mod 2^b) ∗ 2^(n ∗ outlen).
|
||||
#[allow(non_snake_case)]
|
||||
let mut W = $ltype::zero();
|
||||
for (idx, val) in V.iter().enumerate() {
|
||||
if idx < n {
|
||||
let mut base = val.clone();
|
||||
let baselen = base.len();
|
||||
base.resize(baselen + (idx * (outlen / 8)), 0);
|
||||
W += $ltype::from_bytes(&base);
|
||||
} else {
|
||||
let base = $ltype::from_bytes(val);
|
||||
let twob = $ltype::from(1u64) << b;
|
||||
let val = base % twob;
|
||||
W += val << (n * outlen);
|
||||
}
|
||||
}
|
||||
// 11.3 X = W + 2^(L – 1).
|
||||
// Comment: 0 ≤ W < 2 L – 1 ; hence, 2 L – 1 ≤ X < 2 L .
|
||||
#[allow(non_snake_case)]
|
||||
let mut X = $ltype::from(1u64) << (L - 1);
|
||||
X += W;
|
||||
// 11.4 c = X mod 2q.
|
||||
let c = &X % ($ltype::from(&q) << 1);
|
||||
// 11.5 p = X – ( c – 1).
|
||||
// Comment: p ≡ 1 ( mod 2 q) .
|
||||
let p = &X - (c - $ltype::from(1u64));
|
||||
// 11.6 If ( p < 2L – 1), then go to step 11.9.
|
||||
if p >= $ltype::from((2*L) - 1) {
|
||||
// 11.7 Test whether or not p is prime as specified in Appendix C .3.
|
||||
if p.probably_prime(rng, 40) {
|
||||
// 11.8 If p is determined to be prime, then return VALID and the values of p , q and (optionally) the values of domain_parameter_seed and counter .
|
||||
return (p, q, domain_parameter_seed, counter);
|
||||
}
|
||||
}
|
||||
// 11.9 offset = offset + n + 1.
|
||||
offset = offset + n + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_g<G: Rng>(rng: &mut G, p: &$ltype, q: &$ntype) -> $ltype
|
||||
{
|
||||
let bigq = $ltype::from(q);
|
||||
let p_minus_1 = p - $ltype::from(1u64);
|
||||
// This is A.2.1 (Unverifiable Generation of g) from FIPS 186-4.
|
||||
// 1. e = (p – 1) / q.
|
||||
let e = (p - $ltype::from(1u64)) / bigq;
|
||||
loop {
|
||||
// 2. Set h = any integer satisfying 1 < h < ( p – 1), such that
|
||||
// h differs from any value previously tried. Note that h could
|
||||
// be obtained from a random number generator or from a counter
|
||||
// that changes after each use.
|
||||
let h = rng.gen_range($ltype::from(2u64), &p_minus_1);
|
||||
// 3. g = h^e mod p.
|
||||
let g = h.modexp(&e, p);
|
||||
// 4. If ( g = 1), then go to step 2.
|
||||
if g != $ltype::from(1u64) {
|
||||
// 5. Return g
|
||||
return g;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
generate_parameters!(L1024N160, U1024, U192, 1024, 160);
|
||||
generate_parameters!(L2048N224, U2048, U256, 2048, 224);
|
||||
generate_parameters!(L2048N256, U2048, U256, 2048, 256);
|
||||
generate_parameters!(L3072N256, U3072, U256, 3072, 256);
|
||||
|
||||
fn hash<T>(x: &T, len: usize) -> Vec<u8>
|
||||
where T: Encoder
|
||||
{
|
||||
let mut base = x.to_bytes();
|
||||
let bytelen = len / 8;
|
||||
while base.len() < bytelen {
|
||||
base.insert(0,0);
|
||||
}
|
||||
SHA256::hash(&base)
|
||||
}
|
||||
159
src/dsa/private.rs
Normal file
159
src/dsa/private.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
use cryptonum::unsigned::*;
|
||||
use cryptonum::signed::ModInv;
|
||||
use dsa::params::*;
|
||||
use dsa::rfc6979::*;
|
||||
use sha::Hash;
|
||||
|
||||
/// A DSA private key, parameterized by its DSA parameters (so that you don't
|
||||
/// accidentally pass the wrong key to the wrong routine).
|
||||
pub struct DSAPrivateKey<Params: DSAParameters>
|
||||
{
|
||||
pub(crate) params: Params,
|
||||
pub(crate) x: Params::N
|
||||
}
|
||||
|
||||
/// A generic DSA private key enum, which masks which kind of DSA key (1024,
|
||||
/// 2048 with the small `n`, 2048 with the normal `n`, or 3072) you're using.
|
||||
pub enum DSAPrivate {
|
||||
DSA1024Private(DSAPrivateKey<L1024N160>),
|
||||
DSA2048SmallPrivate(DSAPrivateKey<L2048N224>),
|
||||
DSA2048Private(DSAPrivateKey<L2048N256>),
|
||||
DSA3072Private(DSAPrivateKey<L3072N256>)
|
||||
}
|
||||
|
||||
macro_rules! privkey_impls {
|
||||
($ptype: ident, $ltype: ident, $ntype: ident, $big: ident, $bigger: ident, $biggest: ident) => {
|
||||
impl DSAPrivateKey<$ptype>
|
||||
{
|
||||
pub fn new(params: $ptype, x: $ntype) -> DSAPrivateKey<$ptype>
|
||||
{
|
||||
DSAPrivateKey{ params, x }
|
||||
}
|
||||
|
||||
pub fn sign<H: Hash + Clone>(&self, m: &[u8]) -> DSASignature<$ntype>
|
||||
{
|
||||
// This algorithm is per RFC 6979, which has a nice, relatively
|
||||
// straightforward description of how to do DSA signing.
|
||||
//
|
||||
// 1. H(m) is transformed into an integer modulo q using the bits2int
|
||||
// transform and an extra modular reduction:
|
||||
//
|
||||
// h = bits2int(H(m)) mod q
|
||||
//
|
||||
// As was noted in the description of bits2octets, the extra
|
||||
// modular reduction is no more than a conditional subtraction.
|
||||
//
|
||||
let h1 = <H>::hash(m);
|
||||
let n = $ptype::n_size();
|
||||
let h0: $ntype = bits2int(&h1, $ptype::n_size());
|
||||
let q = &self.params.q;
|
||||
let h = h0 % q;
|
||||
|
||||
// 2. A random value modulo q, dubbed k, is generated. That value
|
||||
// shall not be 0; hence, it lies in the [1, q-1] range. Most
|
||||
// of the remainder of this document will revolve around the
|
||||
// process used to generate k. In plain DSA or ECDSA, k should
|
||||
// be selected through a random selection that chooses a value
|
||||
// among the q-1 possible values with uniform probability.
|
||||
for k in KIterator::<H,$ntype>::new(&h1, n, q, &self.x) {
|
||||
// 3. A value r (modulo q) is computed from k and the key
|
||||
// parameters:
|
||||
// * For DSA:
|
||||
// r = g^k mod p mod q
|
||||
//
|
||||
// (The exponentiation is performed modulo p, yielding a
|
||||
// number between 0 and p-1, which is then further reduced
|
||||
// modulo q.)
|
||||
// * For ECDSA ...
|
||||
//
|
||||
// If r turns out to be zero, a new k should be selected and r
|
||||
// computed again (this is an utterly improbable occurrence).
|
||||
let bigk = $ltype::from(&k);
|
||||
let bigr = self.params.g.modexp(&bigk, &self.params.p) % $ltype::from(q);
|
||||
if bigr.is_zero() {
|
||||
continue;
|
||||
}
|
||||
let r = $ntype::from(bigr);
|
||||
// 4. The value s (modulo q) is computed:
|
||||
//
|
||||
// s = (h+x*r)/k mod q
|
||||
//
|
||||
// The pair (r, s) is the signature.
|
||||
if let Some(kinv) = k.modinv(&q) {
|
||||
let xr = &self.x * &r;
|
||||
let top = xr + $big::from(&h);
|
||||
let left = top * $bigger::from(kinv);
|
||||
let bigs = left % $biggest::from(q);
|
||||
return DSASignature::new(r, $ntype::from(bigs));
|
||||
}
|
||||
}
|
||||
panic!("The world is broken; couldn't find a k in sign().");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
privkey_impls!(L1024N160, U1024, U192, U384, U448, U896);
|
||||
privkey_impls!(L2048N224, U2048, U256, U512, U576, U1152);
|
||||
privkey_impls!(L2048N256, U2048, U256, U512, U576, U1152);
|
||||
privkey_impls!(L3072N256, U3072, U256, U512, U576, U1152);
|
||||
|
||||
macro_rules! generate_tests {
|
||||
( $( ($mod: ident, $params: ident, $lt: ident, $nt: ident) ),* ) => {
|
||||
$(
|
||||
#[cfg(test)]
|
||||
#[allow(non_snake_case)]
|
||||
mod $mod {
|
||||
use cryptonum::unsigned::Decoder;
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
use sha::{SHA224,SHA256,SHA384,SHA512};
|
||||
|
||||
#[test]
|
||||
fn verify() {
|
||||
let fname = format!("testdata/dsa/sign{}.test", stringify!($params));
|
||||
run_test(fname.to_string(), 9, |case| {
|
||||
let (neg0, pbytes) = case.get("p").unwrap();
|
||||
let (neg1, qbytes) = case.get("q").unwrap();
|
||||
let (neg2, gbytes) = case.get("g").unwrap();
|
||||
let (neg3, _bytes) = case.get("y").unwrap();
|
||||
let (neg4, xbytes) = case.get("x").unwrap();
|
||||
let (neg5, mbytes) = case.get("m").unwrap();
|
||||
let (neg6, hbytes) = case.get("h").unwrap();
|
||||
let (neg7, rbytes) = case.get("r").unwrap();
|
||||
let (neg8, sbytes) = case.get("s").unwrap();
|
||||
|
||||
assert!(!neg0 && !neg1 && !neg2 && !neg3 && !neg4 &&
|
||||
!neg5 && !neg6 && !neg7 && !neg8);
|
||||
let p = $lt::from_bytes(pbytes);
|
||||
let q = $nt::from_bytes(qbytes);
|
||||
let g = $lt::from_bytes(gbytes);
|
||||
let x = $nt::from_bytes(xbytes);
|
||||
//let y = $lt::from_bytes(ybytes);
|
||||
let h = usize::from($nt::from_bytes(hbytes));
|
||||
let r = $nt::from_bytes(rbytes);
|
||||
let s = $nt::from_bytes(sbytes);
|
||||
|
||||
let params = $params::new(p,g,q);
|
||||
let private = DSAPrivateKey::<$params>::new(params, x);
|
||||
let sig = match h {
|
||||
224 => private.sign::<SHA224>(mbytes),
|
||||
256 => private.sign::<SHA256>(mbytes),
|
||||
384 => private.sign::<SHA384>(mbytes),
|
||||
512 => private.sign::<SHA512>(mbytes),
|
||||
_ => panic!("Unexpected hash {}", h)
|
||||
};
|
||||
assert_eq!(r, sig.r);
|
||||
assert_eq!(s, sig.s);
|
||||
});
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
generate_tests!( (DSA1024N160, L1024N160, U1024, U192),
|
||||
(DSA2048N224, L2048N224, U2048, U256),
|
||||
(DSA2048N256, L2048N256, U2048, U256),
|
||||
(DSA3072N256, L3072N256, U3072, U256)
|
||||
);
|
||||
145
src/dsa/public.rs
Normal file
145
src/dsa/public.rs
Normal file
@@ -0,0 +1,145 @@
|
||||
use cryptonum::unsigned::*;
|
||||
use cryptonum::signed::ModInv;
|
||||
use dsa::params::*;
|
||||
use dsa::rfc6979::DSASignature;
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,ToASN1};
|
||||
use sha::Hash;
|
||||
use std::cmp::min;
|
||||
use utils::TranslateNums;
|
||||
|
||||
/// A DSA public key, parameterized by its DSA parameters (so that you don't
|
||||
/// accidentally pass the wrong thing to the wrong function).
|
||||
pub struct DSAPublicKey<Params: DSAParameters> {
|
||||
pub(crate) params: Params,
|
||||
pub(crate) y: Params::L
|
||||
}
|
||||
|
||||
/// An enumeration that hides exactly which parameters you're using. Use at
|
||||
/// your own risk, as the types won't save you.
|
||||
pub enum DSAPublic {
|
||||
DSAPublicL1024N160(DSAPublicKey<L1024N160>),
|
||||
DSAPublicL2048N224(DSAPublicKey<L2048N224>),
|
||||
DSAPublicL2048N256(DSAPublicKey<L2048N256>),
|
||||
DSAPublicL3072N256(DSAPublicKey<L3072N256>)
|
||||
}
|
||||
|
||||
macro_rules! pubkey_impls {
|
||||
($ptype: ident, $ltype: ident, $ntype: ident, $dbl: ident, $bdbl: ident) => {
|
||||
impl DSAPublicKey<$ptype>
|
||||
{
|
||||
pub fn new(params: $ptype, y: $ltype) -> DSAPublicKey<$ptype>
|
||||
{
|
||||
DSAPublicKey{ params, y }
|
||||
}
|
||||
|
||||
pub fn verify<H: Hash>(&self, m: &[u8], sig: &DSASignature<$ntype>) -> bool
|
||||
{
|
||||
if sig.r >= self.params.q {
|
||||
return false;
|
||||
}
|
||||
if sig.s >= self.params.q {
|
||||
return false;
|
||||
}
|
||||
// w = (s')^-1 mod q;
|
||||
if let Some(w) = sig.s.modinv(&self.params.q) {
|
||||
// z = the leftmost min(N, outlen) bits of Hash(M').
|
||||
let mut digest_bytes = <H>::hash(m);
|
||||
let len = min(digest_bytes.len(), $ptype::n_size() / 8);
|
||||
digest_bytes.truncate(len);
|
||||
let z = $ntype::from_bytes(&digest_bytes);
|
||||
// u1 = (zw) mod q
|
||||
let qdbl = $dbl::from(&self.params.q);
|
||||
let u1 = $ltype::from( (&z * &w) % &qdbl );
|
||||
// u2 = (rw) mod q
|
||||
let u2 = $ltype::from( (&sig.r * &w) % &qdbl );
|
||||
// v = (((g)^u1(y)^u2) mod p) mod q
|
||||
let v_1 = self.params.g.modexp(&u1, &self.params.p);
|
||||
let v_2 = self.y.modexp(&u2, &self.params.p);
|
||||
let bigp = $bdbl::from(&self.params.p);
|
||||
let v_first_mod = (v_1 * v_2) % bigp;
|
||||
let v = $ltype::from(v_first_mod) % $ltype::from(&self.params.q);
|
||||
// if v = r, then the signature is verified
|
||||
return $ntype::from(v) == sig.r
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl ToASN1 for DSAPublicKey<$ptype> {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
|
||||
{
|
||||
let inty = self.y.to_num();
|
||||
let yblock = ASN1Block::Integer(c, 0, inty);
|
||||
Ok(vec![yblock])
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pubkey_impls!(L1024N160, U1024, U192, U384, U2048);
|
||||
pubkey_impls!(L2048N224, U2048, U256, U512, U4096);
|
||||
pubkey_impls!(L2048N256, U2048, U256, U512, U4096);
|
||||
pubkey_impls!(L3072N256, U3072, U256, U512, U6144);
|
||||
|
||||
macro_rules! generate_tests {
|
||||
( $( ($mod: ident, $params: ident, $lt: ident, $nt: ident) ),* ) => {
|
||||
$(
|
||||
#[cfg(test)]
|
||||
#[allow(non_snake_case)]
|
||||
mod $mod {
|
||||
use cryptonum::unsigned::Decoder;
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
use sha::{SHA224,SHA256,SHA384,SHA512};
|
||||
|
||||
#[test]
|
||||
fn verify() {
|
||||
let fname = format!("testdata/dsa/sign{}.test", stringify!($params));
|
||||
run_test(fname.to_string(), 9, |case| {
|
||||
let (neg0, pbytes) = case.get("p").unwrap();
|
||||
let (neg1, qbytes) = case.get("q").unwrap();
|
||||
let (neg2, gbytes) = case.get("g").unwrap();
|
||||
let (neg3, ybytes) = case.get("y").unwrap();
|
||||
let (neg4, _bytes) = case.get("x").unwrap();
|
||||
let (neg5, mbytes) = case.get("m").unwrap();
|
||||
let (neg6, hbytes) = case.get("h").unwrap();
|
||||
let (neg7, rbytes) = case.get("r").unwrap();
|
||||
let (neg8, sbytes) = case.get("s").unwrap();
|
||||
|
||||
assert!(!neg0 && !neg1 && !neg2 && !neg3 && !neg4 &&
|
||||
!neg5 && !neg6 && !neg7 && !neg8);
|
||||
let p = $lt::from_bytes(pbytes);
|
||||
let q = $nt::from_bytes(qbytes);
|
||||
let g = $lt::from_bytes(gbytes);
|
||||
//let x = $lt::from_bytes(xbytes);
|
||||
let y = $lt::from_bytes(ybytes);
|
||||
let h = usize::from($nt::from_bytes(hbytes));
|
||||
let r = $nt::from_bytes(rbytes);
|
||||
let s = $nt::from_bytes(sbytes);
|
||||
|
||||
let params = $params::new(p,g,q);
|
||||
let public = DSAPublicKey::<$params>::new(params, y);
|
||||
let sig = DSASignature::<$nt>::new(r, s);
|
||||
match h {
|
||||
224 => assert!(public.verify::<SHA224>(mbytes, &sig)),
|
||||
256 => assert!(public.verify::<SHA256>(mbytes, &sig)),
|
||||
384 => assert!(public.verify::<SHA384>(mbytes, &sig)),
|
||||
512 => assert!(public.verify::<SHA512>(mbytes, &sig)),
|
||||
_ => panic!("Unexpected hash {}", h)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
generate_tests!( (DSA1024N160, L1024N160, U1024, U192),
|
||||
(DSA2048N224, L2048N224, U2048, U256),
|
||||
(DSA2048N256, L2048N256, U2048, U256),
|
||||
(DSA3072N256, L3072N256, U3072, U256)
|
||||
);
|
||||
417
src/dsa/rfc6979.rs
Normal file
417
src/dsa/rfc6979.rs
Normal file
@@ -0,0 +1,417 @@
|
||||
use cryptonum::unsigned::{CryptoNum,Decoder,Encoder};
|
||||
use hmac::HMAC;
|
||||
use sha::Hash;
|
||||
use num::BigInt;
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1DecodeErr,ASN1EncodeErr};
|
||||
use simple_asn1::{FromASN1,ToASN1};
|
||||
use utils::TranslateNums;
|
||||
use std::ops::{Shr,Sub};
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct DSASignature<N>
|
||||
{
|
||||
pub r: N,
|
||||
pub s: N
|
||||
}
|
||||
|
||||
impl<N> DSASignature<N>
|
||||
{
|
||||
pub fn new(r: N, s: N) -> DSASignature<N>
|
||||
{
|
||||
DSASignature{ r, s }
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub struct KIterator<H,N>
|
||||
where
|
||||
H: Hash + Clone,
|
||||
N: Clone + Decoder + Encoder + PartialOrd + Shr<usize,Output=N>,
|
||||
{
|
||||
hmac_k: HMAC<H>,
|
||||
V: Vec<u8>,
|
||||
q: N,
|
||||
qlen: usize
|
||||
}
|
||||
|
||||
impl<H,N> KIterator<H,N>
|
||||
where
|
||||
H: Hash + Clone,
|
||||
N: Clone + Decoder + Encoder + PartialOrd + Shr<usize,Output=N> + Sub<Output=N>,
|
||||
{
|
||||
pub fn new(h1: &[u8], qlen: usize, q: &N, x: &N) -> KIterator<H,N>
|
||||
{
|
||||
// Given the input message m, the following process is applied:
|
||||
//
|
||||
// a. Process m through the hash function H, yielding:
|
||||
//
|
||||
// h1 = H(m)
|
||||
//
|
||||
// (h1 is a sequence of hlen bits).
|
||||
//
|
||||
let hlen = h1.len();
|
||||
// b. Set:
|
||||
//
|
||||
// V = 0x01 0x01 0x01 ... 0x01
|
||||
//
|
||||
// such that the length of V, in bits, is equal to 8*ceil(hlen/8).
|
||||
// For instance, on an octet-based system, if H is SHA-256, then
|
||||
// V is set to a sequence of 32 octets of value 1. Note that in
|
||||
// this step and all subsequent steps, we use the same H function
|
||||
// as the one used in step 'a' to process the input message; this
|
||||
// choice will be discussed in more detail in Section 3.6.
|
||||
//
|
||||
#[allow(non_snake_case)]
|
||||
let mut V = Vec::new();
|
||||
V.resize(hlen, 0x01);
|
||||
// c. Set:
|
||||
//
|
||||
// K = 0x00 0x00 0x00 ... 0x00
|
||||
//
|
||||
// such that the length of K, in bits, is equal to 8*ceil(hlen/8).
|
||||
#[allow(non_snake_case)]
|
||||
let mut K = Vec::new();
|
||||
K.resize(hlen, 0x00);
|
||||
// d. Set:
|
||||
//
|
||||
// K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1))
|
||||
//
|
||||
// where '||' denotes concatenation. In other words, we compute
|
||||
// HMAC with key K, over the concatenation of the following, in
|
||||
// order: the current value of V, a sequence of eight bits of value
|
||||
// 0, the encoding of the (EC)DSA private key x, and the hashed
|
||||
// message (possibly truncated and extended as specified by the
|
||||
// bits2octets transform). The HMAC result is the new value of K.
|
||||
// Note that the private key x is in the [1, q-1] range, hence a
|
||||
// proper input for int2octets, yielding rlen bits of output, i.e.,
|
||||
// an integral number of octets (rlen is a multiple of 8).
|
||||
let xbytes = int2octets(x, qlen);
|
||||
let h1bytes = bits2octets(h1, q, qlen);
|
||||
let mut input = Vec::new();
|
||||
input.extend_from_slice(&V);
|
||||
input.push(0x00);
|
||||
input.extend_from_slice(&xbytes);
|
||||
input.extend_from_slice(&h1bytes);
|
||||
K = HMAC::<H>::hmac(&K, &input);
|
||||
// e. Set:
|
||||
//
|
||||
// V = HMAC_K(V)
|
||||
V = HMAC::<H>::hmac(&K, &V);
|
||||
// f. Set:
|
||||
//
|
||||
// K = HMAC_K(V || 0x01 || int2octets(x) || bits2octets(h1))
|
||||
//
|
||||
// Note that the "internal octet" is 0x01 this time.
|
||||
input = Vec::new();
|
||||
input.extend_from_slice(&V);
|
||||
input.push(0x01);
|
||||
input.extend_from_slice(&xbytes);
|
||||
input.extend_from_slice(&h1bytes);
|
||||
K = HMAC::<H>::hmac(&K, &input);
|
||||
// g. Set:
|
||||
//
|
||||
// V = HMAC_K(V)
|
||||
V = HMAC::<H>::hmac(&K, &V);
|
||||
// h is for later ...
|
||||
KIterator {
|
||||
hmac_k: HMAC::<H>::new(&K),
|
||||
V: V,
|
||||
q: q.clone(),
|
||||
qlen: qlen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<H,N> Iterator for KIterator<H,N>
|
||||
where
|
||||
H: Hash + Clone,
|
||||
N: Clone + CryptoNum + Decoder + Encoder + PartialOrd + Shr<usize,Output=N>,
|
||||
{
|
||||
type Item = N;
|
||||
|
||||
fn next(&mut self) -> Option<N>
|
||||
{
|
||||
loop {
|
||||
// h. Apply the following algorithm until a proper value is found
|
||||
// for k:
|
||||
//
|
||||
// 1. Set T to the empty sequence. The length of T (in bits) is
|
||||
// denoted tlen; thus, at that point, tlen = 0.
|
||||
let mut t = Vec::new();
|
||||
//
|
||||
// 2. While tlen < qlen, do the following:
|
||||
//
|
||||
// V = HMAC_K(V)
|
||||
// T = T || V
|
||||
let target = (self.qlen + 7) / 8;
|
||||
while t.len() < target {
|
||||
self.V = runhmac(&self.hmac_k, &self.V);
|
||||
t.extend_from_slice(&self.V);
|
||||
}
|
||||
//
|
||||
// 3. Compute:
|
||||
//
|
||||
// k = bits2int(T)
|
||||
let resk: N = bits2int(&t, self.qlen);
|
||||
//
|
||||
// If that value of k is within the [1,q-1] range, and is
|
||||
// suitable for DSA or ECDSA (i.e., it results in an r value
|
||||
// that is not 0; see Section 3.4), then the generation of k
|
||||
// is finished. The obtained value of k is used in DSA or
|
||||
// ECDSA. Otherwise, compute:
|
||||
//
|
||||
// K = HMAC_K(V || 0x00)
|
||||
let mut input = self.V.clone();
|
||||
input.push(0x00);
|
||||
#[allow(non_snake_case)]
|
||||
let K = runhmac(&self.hmac_k, &input);
|
||||
// V = HMAC_K(V)
|
||||
self.hmac_k = HMAC::<H>::new(&K);
|
||||
self.V = runhmac(&self.hmac_k, &self.V);
|
||||
//
|
||||
// and loop (try to generate a new T, and so on).
|
||||
//
|
||||
if !resk.is_zero() && (resk < self.q) {
|
||||
return Some(resk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bits2int<X>(x: &[u8], qlen: usize) -> X
|
||||
where
|
||||
X: Decoder + Shr<usize,Output=X>
|
||||
{
|
||||
|
||||
if qlen < (x.len() * 8) {
|
||||
let mut fixed_x = Vec::from(x);
|
||||
let qlen_bytes = (qlen + 7) / 8;
|
||||
let rounded_qlen = qlen_bytes * 8;
|
||||
fixed_x.resize(qlen_bytes, 0);
|
||||
X::from_bytes(&fixed_x) >> (rounded_qlen - qlen)
|
||||
} else {
|
||||
X::from_bytes(x)
|
||||
}
|
||||
}
|
||||
|
||||
fn bits2octets<X>(x: &[u8], q: &X, qlen: usize) -> Vec<u8>
|
||||
where
|
||||
X: Clone + Decoder + Encoder + PartialOrd + Sub<Output=X> + Shr<usize,Output=X>
|
||||
{
|
||||
let z1: X = bits2int(x, qlen);
|
||||
let res = if &z1 > q { z1 - q.clone() } else { z1 };
|
||||
int2octets(&res, qlen)
|
||||
}
|
||||
|
||||
fn int2octets<X>(x: &X, qlen_bits: usize) -> Vec<u8>
|
||||
where X: Encoder
|
||||
{
|
||||
let qlen_bytes = (qlen_bits + 7) / 8;
|
||||
let mut base = x.to_bytes();
|
||||
|
||||
while base.len() < qlen_bytes {
|
||||
base.insert(0,0);
|
||||
}
|
||||
|
||||
while base.len() > qlen_bytes {
|
||||
base.remove(0);
|
||||
}
|
||||
|
||||
base
|
||||
}
|
||||
|
||||
fn runhmac<H: Hash + Clone>(base: &HMAC<H>, m: &[u8]) -> Vec<u8>
|
||||
{
|
||||
let mut runner = base.clone();
|
||||
runner.update(&m);
|
||||
runner.finalize()
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub enum DSADecodeError {
|
||||
ASN1Error(ASN1DecodeErr),
|
||||
NoSignatureFound,
|
||||
InvalidRValue,
|
||||
InvalidSValue
|
||||
}
|
||||
|
||||
impl From<ASN1DecodeErr> for DSADecodeError {
|
||||
fn from(a: ASN1DecodeErr) -> DSADecodeError {
|
||||
DSADecodeError::ASN1Error(a)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N> FromASN1 for DSASignature<N>
|
||||
where N: TranslateNums<BigInt>
|
||||
{
|
||||
type Error = DSADecodeError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(DSASignature<N>,&[ASN1Block]),DSADecodeError>
|
||||
{
|
||||
match v.split_first() {
|
||||
Some((&ASN1Block::Sequence(_,_,ref info), rest))
|
||||
if info.len() == 2 =>
|
||||
{
|
||||
match (&info[0], &info[1]) {
|
||||
(&ASN1Block::Integer(_,_,ref rint),
|
||||
&ASN1Block::Integer(_,_,ref sint)) => {
|
||||
let r = N::from_num(rint).ok_or(DSADecodeError::InvalidRValue)?;
|
||||
let s = N::from_num(sint).ok_or(DSADecodeError::InvalidSValue)?;
|
||||
Ok((DSASignature{ r, s }, rest))
|
||||
}
|
||||
_ => Err(DSADecodeError::NoSignatureFound)
|
||||
}
|
||||
}
|
||||
_ => Err(DSADecodeError::NoSignatureFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N> ToASN1 for DSASignature<N>
|
||||
where N: TranslateNums<BigInt>
|
||||
{
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
|
||||
{
|
||||
let rb = ASN1Block::Integer(c, 0, self.r.to_num());
|
||||
let sb = ASN1Block::Integer(c, 0, self.s.to_num());
|
||||
Ok(vec![ASN1Block::Sequence(c, 0, vec![rb,sb])])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use cryptonum::unsigned::U192;
|
||||
use sha::{SHA224,SHA256,SHA384,SHA512};
|
||||
use super::*;
|
||||
use testing::*;
|
||||
|
||||
const QBYTES: [u8; 21] = [0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x02, 0x01, 0x08, 0xA2, 0xE0, 0xCC,
|
||||
0x0D, 0x99, 0xF8, 0xA5, 0xEF];
|
||||
const XBYTES: [u8; 21] = [0x00, 0x9A, 0x4D, 0x67, 0x92, 0x29, 0x5A, 0x7F,
|
||||
0x73, 0x0F, 0xC3, 0xF2, 0xB4, 0x9C, 0xBC, 0x0F,
|
||||
0x62, 0xE8, 0x62, 0x27, 0x2F];
|
||||
const H1: [u8; 32] = [0xAF, 0x2B, 0xDB, 0xE1, 0xAA, 0x9B, 0x6E, 0xC1,
|
||||
0xE2, 0xAD, 0xE1, 0xD6, 0x94, 0xF4, 0x1F, 0xC7,
|
||||
0x1A, 0x83, 0x1D, 0x02, 0x68, 0xE9, 0x89, 0x15,
|
||||
0x62, 0x11, 0x3D, 0x8A, 0x62, 0xAD, 0xD1, 0xBF];
|
||||
|
||||
#[test]
|
||||
fn int2octets_example() {
|
||||
let x = U192::from_bytes(&XBYTES);
|
||||
let octets = int2octets(&x, 163);
|
||||
let target = vec![0x00, 0x9A, 0x4D, 0x67, 0x92, 0x29, 0x5A, 0x7F,
|
||||
0x73, 0x0F, 0xC3, 0xF2, 0xB4, 0x9C, 0xBC, 0x0F,
|
||||
0x62, 0xE8, 0x62, 0x27, 0x2F];
|
||||
assert_eq!(octets, target);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bits2octets_example() {
|
||||
let q = U192::from_bytes(&QBYTES);
|
||||
let octets = bits2octets(&H1, &q, 163);
|
||||
let target = vec![0x01, 0x79, 0x5E, 0xDF, 0x0D, 0x54, 0xDB, 0x76,
|
||||
0x0F, 0x15, 0x6D, 0x0D, 0xAC, 0x04, 0xC0, 0x32,
|
||||
0x2B, 0x3A, 0x20, 0x42, 0x24];
|
||||
assert_eq!(octets, target);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn k_gen_example() {
|
||||
let q = U192::from_bytes(&QBYTES);
|
||||
let x = U192::from_bytes(&XBYTES);
|
||||
let mut iter = KIterator::<SHA256,U192>::new(&H1, 163, &q, &x);
|
||||
match iter.next() {
|
||||
None =>
|
||||
assert!(false),
|
||||
Some(x) => {
|
||||
let target = vec![0x02, 0x3A, 0xF4, 0x07, 0x4C, 0x90, 0xA0,
|
||||
0x2B, 0x3F, 0xE6, 0x1D, 0x28, 0x6D, 0x5C,
|
||||
0x87, 0xF4, 0x25, 0xE6, 0xBD, 0xD8, 0x1B];
|
||||
let x2 = U192::from_bytes(&target);
|
||||
assert_eq!(x, x2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use cryptonum::unsigned::*;
|
||||
|
||||
macro_rules! k_generator_tests {
|
||||
($testname: ident, $hash: ident, $fname: expr) => {
|
||||
#[test]
|
||||
fn $testname() {
|
||||
let fname = build_test_path("rfc6979", $fname);
|
||||
run_test(fname.to_string(), 7, |case| {
|
||||
let (negq, qbytes) = case.get("q").unwrap();
|
||||
let (negl, lbytes) = case.get("l").unwrap();
|
||||
let (negx, xbytes) = case.get("x").unwrap();
|
||||
let (negh, h1) = case.get("h").unwrap();
|
||||
let (negk, kbytes) = case.get("k").unwrap();
|
||||
let (negy, ybytes) = case.get("y").unwrap();
|
||||
let (negz, zbytes) = case.get("z").unwrap();
|
||||
|
||||
assert!(!negq && !negl && !negx && !negh &&
|
||||
!negk && !negy && !negz);
|
||||
let qlen = usize::from(U192::from_bytes(lbytes));
|
||||
assert!(qlen >= 160); assert!(qlen <= 521);
|
||||
|
||||
if qlen < 192 {
|
||||
let q = U192::from_bytes(qbytes);
|
||||
let x = U192::from_bytes(xbytes);
|
||||
let k = U192::from_bytes(kbytes);
|
||||
let y = U192::from_bytes(ybytes);
|
||||
let z = U192::from_bytes(zbytes);
|
||||
|
||||
let mut kiter = KIterator::<$hash,U192>::new(h1,qlen,&q,&x);
|
||||
assert_eq!(Some(k), kiter.next(), "first value test");
|
||||
assert_eq!(Some(y), kiter.next(), "second value test");
|
||||
assert_eq!(Some(z), kiter.next(), "third value test");
|
||||
} else if qlen < 256 {
|
||||
let q = U256::from_bytes(qbytes);
|
||||
let x = U256::from_bytes(xbytes);
|
||||
let k = U256::from_bytes(kbytes);
|
||||
let y = U256::from_bytes(ybytes);
|
||||
let z = U256::from_bytes(zbytes);
|
||||
|
||||
let mut kiter = KIterator::<$hash,U256>::new(h1,qlen,&q,&x);
|
||||
assert_eq!(Some(k), kiter.next(), "first value test");
|
||||
assert_eq!(Some(y), kiter.next(), "second value test");
|
||||
assert_eq!(Some(z), kiter.next(), "third value test");
|
||||
} else if qlen < 512 {
|
||||
let q = U512::from_bytes(qbytes);
|
||||
let x = U512::from_bytes(xbytes);
|
||||
let k = U512::from_bytes(kbytes);
|
||||
let y = U512::from_bytes(ybytes);
|
||||
let z = U512::from_bytes(zbytes);
|
||||
|
||||
let mut kiter = KIterator::<$hash,U512>::new(h1,qlen,&q,&x);
|
||||
assert_eq!(Some(k), kiter.next(), "first value test");
|
||||
assert_eq!(Some(y), kiter.next(), "second value test");
|
||||
assert_eq!(Some(z), kiter.next(), "third value test");
|
||||
} else {
|
||||
let q = U576::from_bytes(qbytes);
|
||||
let x = U576::from_bytes(xbytes);
|
||||
let k = U576::from_bytes(kbytes);
|
||||
let y = U576::from_bytes(ybytes);
|
||||
let z = U576::from_bytes(zbytes);
|
||||
|
||||
let mut kiter = KIterator::<$hash,U576>::new(h1,qlen,&q,&x);
|
||||
assert_eq!(Some(k), kiter.next(), "first value test");
|
||||
assert_eq!(Some(y), kiter.next(), "second value test");
|
||||
assert_eq!(Some(z), kiter.next(), "third value test");
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
k_generator_tests!(kgen_sha224, SHA224, "SHA224");
|
||||
k_generator_tests!(kgen_sha256, SHA256, "SHA256");
|
||||
k_generator_tests!(kgen_sha384, SHA384, "SHA384");
|
||||
k_generator_tests!(kgen_sha512, SHA512, "SHA512");
|
||||
|
||||
}
|
||||
535
src/dsa/tests.rs
Normal file
535
src/dsa/tests.rs
Normal file
@@ -0,0 +1,535 @@
|
||||
use cryptonum::unsigned::*;
|
||||
use sha::{Hash,SHA1,SHA224,SHA256,SHA384,SHA512};
|
||||
use simple_asn1::{der_decode,der_encode};
|
||||
use dsa::params::{DSAParameters,L1024N160,L2048N256};
|
||||
use dsa::private::DSAPrivateKey;
|
||||
use dsa::public::DSAPublicKey;
|
||||
use dsa::rfc6979::KIterator;
|
||||
|
||||
macro_rules! run_rfc6979_test {
|
||||
($hash: ty, $ntype: ident, $val: ident, $params: ident, $public: ident, $private: ident,
|
||||
k $k: expr,
|
||||
r $r: expr,
|
||||
s $s: expr) => ({
|
||||
let h1 = <$hash>::hash(&$val);
|
||||
let rbytes = $r;
|
||||
let sbytes = $s;
|
||||
let r = $ntype::from_bytes(&rbytes);
|
||||
let s = $ntype::from_bytes(&sbytes);
|
||||
let mut iter = KIterator::<$hash,$ntype>::new(&h1, $params.n_bits(), &$params.q, &$private.x);
|
||||
let mut k1 = iter.next().unwrap().to_bytes().to_vec();
|
||||
while k1.len() > $k.len() {
|
||||
assert_eq!(k1[0], 0);
|
||||
k1.remove(0);
|
||||
}
|
||||
assert_eq!($k, k1);
|
||||
let sig = $private.sign::<$hash>(&$val);
|
||||
assert_eq!(sig.r, r);
|
||||
assert_eq!(sig.s, s);
|
||||
assert!($public.verify::<$hash>(&$val, &sig));
|
||||
let blocks = der_encode(&sig).unwrap();
|
||||
let sig2 = der_decode(&blocks).unwrap();
|
||||
assert_eq!(sig, sig2);
|
||||
})
|
||||
}
|
||||
|
||||
// these appendix_* tests are out of RFC6979
|
||||
#[test]
|
||||
fn appendix_a21() {
|
||||
let pbytes = vec![0x86, 0xF5, 0xCA, 0x03, 0xDC, 0xFE, 0xB2, 0x25,
|
||||
0x06, 0x3F, 0xF8, 0x30, 0xA0, 0xC7, 0x69, 0xB9,
|
||||
0xDD, 0x9D, 0x61, 0x53, 0xAD, 0x91, 0xD7, 0xCE,
|
||||
0x27, 0xF7, 0x87, 0xC4, 0x32, 0x78, 0xB4, 0x47,
|
||||
0xE6, 0x53, 0x3B, 0x86, 0xB1, 0x8B, 0xED, 0x6E,
|
||||
0x8A, 0x48, 0xB7, 0x84, 0xA1, 0x4C, 0x25, 0x2C,
|
||||
0x5B, 0xE0, 0xDB, 0xF6, 0x0B, 0x86, 0xD6, 0x38,
|
||||
0x5B, 0xD2, 0xF1, 0x2F, 0xB7, 0x63, 0xED, 0x88,
|
||||
0x73, 0xAB, 0xFD, 0x3F, 0x5B, 0xA2, 0xE0, 0xA8,
|
||||
0xC0, 0xA5, 0x90, 0x82, 0xEA, 0xC0, 0x56, 0x93,
|
||||
0x5E, 0x52, 0x9D, 0xAF, 0x7C, 0x61, 0x04, 0x67,
|
||||
0x89, 0x9C, 0x77, 0xAD, 0xED, 0xFC, 0x84, 0x6C,
|
||||
0x88, 0x18, 0x70, 0xB7, 0xB1, 0x9B, 0x2B, 0x58,
|
||||
0xF9, 0xBE, 0x05, 0x21, 0xA1, 0x70, 0x02, 0xE3,
|
||||
0xBD, 0xD6, 0xB8, 0x66, 0x85, 0xEE, 0x90, 0xB3,
|
||||
0xD9, 0xA1, 0xB0, 0x2B, 0x78, 0x2B, 0x17, 0x79];
|
||||
let qbytes = vec![0x99, 0x6F, 0x96, 0x7F, 0x6C, 0x8E, 0x38, 0x8D,
|
||||
0x9E, 0x28, 0xD0, 0x1E, 0x20, 0x5F, 0xBA, 0x95,
|
||||
0x7A, 0x56, 0x98, 0xB1];
|
||||
let gbytes = vec![0x07, 0xB0, 0xF9, 0x25, 0x46, 0x15, 0x0B, 0x62,
|
||||
0x51, 0x4B, 0xB7, 0x71, 0xE2, 0xA0, 0xC0, 0xCE,
|
||||
0x38, 0x7F, 0x03, 0xBD, 0xA6, 0xC5, 0x6B, 0x50,
|
||||
0x52, 0x09, 0xFF, 0x25, 0xFD, 0x3C, 0x13, 0x3D,
|
||||
0x89, 0xBB, 0xCD, 0x97, 0xE9, 0x04, 0xE0, 0x91,
|
||||
0x14, 0xD9, 0xA7, 0xDE, 0xFD, 0xEA, 0xDF, 0xC9,
|
||||
0x07, 0x8E, 0xA5, 0x44, 0xD2, 0xE4, 0x01, 0xAE,
|
||||
0xEC, 0xC4, 0x0B, 0xB9, 0xFB, 0xBF, 0x78, 0xFD,
|
||||
0x87, 0x99, 0x5A, 0x10, 0xA1, 0xC2, 0x7C, 0xB7,
|
||||
0x78, 0x9B, 0x59, 0x4B, 0xA7, 0xEF, 0xB5, 0xC4,
|
||||
0x32, 0x6A, 0x9F, 0xE5, 0x9A, 0x07, 0x0E, 0x13,
|
||||
0x6D, 0xB7, 0x71, 0x75, 0x46, 0x4A, 0xDC, 0xA4,
|
||||
0x17, 0xBE, 0x5D, 0xCE, 0x2F, 0x40, 0xD1, 0x0A,
|
||||
0x46, 0xA3, 0xA3, 0x94, 0x3F, 0x26, 0xAB, 0x7F,
|
||||
0xD9, 0xC0, 0x39, 0x8F, 0xF8, 0xC7, 0x6E, 0xE0,
|
||||
0xA5, 0x68, 0x26, 0xA8, 0xA8, 0x8F, 0x1D, 0xBD];
|
||||
let xbytes = vec![0x41, 0x16, 0x02, 0xCB, 0x19, 0xA6, 0xCC, 0xC3,
|
||||
0x44, 0x94, 0xD7, 0x9D, 0x98, 0xEF, 0x1E, 0x7E,
|
||||
0xD5, 0xAF, 0x25, 0xF7];
|
||||
let ybytes = vec![0x5D, 0xF5, 0xE0, 0x1D, 0xED, 0x31, 0xD0, 0x29,
|
||||
0x7E, 0x27, 0x4E, 0x16, 0x91, 0xC1, 0x92, 0xFE,
|
||||
0x58, 0x68, 0xFE, 0xF9, 0xE1, 0x9A, 0x84, 0x77,
|
||||
0x64, 0x54, 0xB1, 0x00, 0xCF, 0x16, 0xF6, 0x53,
|
||||
0x92, 0x19, 0x5A, 0x38, 0xB9, 0x05, 0x23, 0xE2,
|
||||
0x54, 0x2E, 0xE6, 0x18, 0x71, 0xC0, 0x44, 0x0C,
|
||||
0xB8, 0x7C, 0x32, 0x2F, 0xC4, 0xB4, 0xD2, 0xEC,
|
||||
0x5E, 0x1E, 0x7E, 0xC7, 0x66, 0xE1, 0xBE, 0x8D,
|
||||
0x4C, 0xE9, 0x35, 0x43, 0x7D, 0xC1, 0x1C, 0x3C,
|
||||
0x8F, 0xD4, 0x26, 0x33, 0x89, 0x33, 0xEB, 0xFE,
|
||||
0x73, 0x9C, 0xB3, 0x46, 0x5F, 0x4D, 0x36, 0x68,
|
||||
0xC5, 0xE4, 0x73, 0x50, 0x82, 0x53, 0xB1, 0xE6,
|
||||
0x82, 0xF6, 0x5C, 0xBD, 0xC4, 0xFA, 0xE9, 0x3C,
|
||||
0x2E, 0xA2, 0x12, 0x39, 0x0E, 0x54, 0x90, 0x5A,
|
||||
0x86, 0xE2, 0x22, 0x31, 0x70, 0xB4, 0x4E, 0xAA,
|
||||
0x7D, 0xA5, 0xDD, 0x9F, 0xFC, 0xFB, 0x7F, 0x3B];
|
||||
//
|
||||
let p = U1024::from_bytes(&pbytes);
|
||||
let q = U192::from_bytes(&qbytes);
|
||||
let g = U1024::from_bytes(&gbytes);
|
||||
let params = L1024N160::new(p, g, q);
|
||||
let x = U192::from_bytes(&xbytes);
|
||||
let y = U1024::from_bytes(&ybytes);
|
||||
let private = DSAPrivateKey::<L1024N160>::new(params.clone(), x);
|
||||
let public = DSAPublicKey::<L1024N160>::new(params.clone(), y);
|
||||
//
|
||||
let sample: [u8; 6] = [115, 97, 109, 112, 108, 101]; // "sample", ASCII
|
||||
let test: [u8; 4] = [116, 101, 115, 116]; // "test", ASCII
|
||||
// With SHA-1, message = "sample":
|
||||
// k = 7BDB6B0FF756E1BB5D53583EF979082F9AD5BD5B
|
||||
// r = 2E1A0C2562B2912CAAF89186FB0F42001585DA55
|
||||
// s = 29EFB6B0AFF2D7A68EB70CA313022253B9A88DF5
|
||||
run_rfc6979_test!(SHA1, U192, sample, params, public, private,
|
||||
k vec![0x7B, 0xDB, 0x6B, 0x0F, 0xF7, 0x56, 0xE1, 0xBB,
|
||||
0x5D, 0x53, 0x58, 0x3E, 0xF9, 0x79, 0x08, 0x2F,
|
||||
0x9A, 0xD5, 0xBD, 0x5B],
|
||||
r vec![0x2E, 0x1A, 0x0C, 0x25, 0x62, 0xB2, 0x91, 0x2C,
|
||||
0xAA, 0xF8, 0x91, 0x86, 0xFB, 0x0F, 0x42, 0x00,
|
||||
0x15, 0x85, 0xDA, 0x55],
|
||||
s vec![0x29, 0xEF, 0xB6, 0xB0, 0xAF, 0xF2, 0xD7, 0xA6,
|
||||
0x8E, 0xB7, 0x0C, 0xA3, 0x13, 0x02, 0x22, 0x53,
|
||||
0xB9, 0xA8, 0x8D, 0xF5]);
|
||||
// With SHA-224, message = "sample":
|
||||
// k = 562097C06782D60C3037BA7BE104774344687649
|
||||
// r = 4BC3B686AEA70145856814A6F1BB53346F02101E
|
||||
// s = 410697B92295D994D21EDD2F4ADA85566F6F94C1
|
||||
run_rfc6979_test!(SHA224, U192, sample, params, public, private,
|
||||
k vec![0x56, 0x20, 0x97, 0xC0, 0x67, 0x82, 0xD6, 0x0C,
|
||||
0x30, 0x37, 0xBA, 0x7B, 0xE1, 0x04, 0x77, 0x43,
|
||||
0x44, 0x68, 0x76, 0x49],
|
||||
r vec![0x4B, 0xC3, 0xB6, 0x86, 0xAE, 0xA7, 0x01, 0x45,
|
||||
0x85, 0x68, 0x14, 0xA6, 0xF1, 0xBB, 0x53, 0x34,
|
||||
0x6F, 0x02, 0x10, 0x1E],
|
||||
s vec![0x41, 0x06, 0x97, 0xB9, 0x22, 0x95, 0xD9, 0x94,
|
||||
0xD2, 0x1E, 0xDD, 0x2F, 0x4A, 0xDA, 0x85, 0x56,
|
||||
0x6F, 0x6F, 0x94, 0xC1]);
|
||||
// With SHA-256, message = "sample":
|
||||
// k = 519BA0546D0C39202A7D34D7DFA5E760B318BCFB
|
||||
// r = 81F2F5850BE5BC123C43F71A3033E9384611C545
|
||||
// s = 4CDD914B65EB6C66A8AAAD27299BEE6B035F5E89
|
||||
run_rfc6979_test!(SHA256, U192, sample, params, public, private,
|
||||
k vec![0x51, 0x9B, 0xA0, 0x54, 0x6D, 0x0C, 0x39, 0x20,
|
||||
0x2A, 0x7D, 0x34, 0xD7, 0xDF, 0xA5, 0xE7, 0x60,
|
||||
0xB3, 0x18, 0xBC, 0xFB],
|
||||
r vec![0x81, 0xF2, 0xF5, 0x85, 0x0B, 0xE5, 0xBC, 0x12,
|
||||
0x3C, 0x43, 0xF7, 0x1A, 0x30, 0x33, 0xE9, 0x38,
|
||||
0x46, 0x11, 0xC5, 0x45],
|
||||
s vec![0x4C, 0xDD, 0x91, 0x4B, 0x65, 0xEB, 0x6C, 0x66,
|
||||
0xA8, 0xAA, 0xAD, 0x27, 0x29, 0x9B, 0xEE, 0x6B,
|
||||
0x03, 0x5F, 0x5E, 0x89]);
|
||||
// With SHA-384, message = "sample":
|
||||
// k = 95897CD7BBB944AA932DBC579C1C09EB6FCFC595
|
||||
// r = 07F2108557EE0E3921BC1774F1CA9B410B4CE65A
|
||||
// s = 54DF70456C86FAC10FAB47C1949AB83F2C6F7595
|
||||
run_rfc6979_test!(SHA384, U192, sample, params, public, private,
|
||||
k vec![0x95, 0x89, 0x7C, 0xD7, 0xBB, 0xB9, 0x44, 0xAA,
|
||||
0x93, 0x2D, 0xBC, 0x57, 0x9C, 0x1C, 0x09, 0xEB,
|
||||
0x6F, 0xCF, 0xC5, 0x95],
|
||||
r vec![0x07, 0xF2, 0x10, 0x85, 0x57, 0xEE, 0x0E, 0x39,
|
||||
0x21, 0xBC, 0x17, 0x74, 0xF1, 0xCA, 0x9B, 0x41,
|
||||
0x0B, 0x4C, 0xE6, 0x5A],
|
||||
s vec![0x54, 0xDF, 0x70, 0x45, 0x6C, 0x86, 0xFA, 0xC1,
|
||||
0x0F, 0xAB, 0x47, 0xC1, 0x94, 0x9A, 0xB8, 0x3F,
|
||||
0x2C, 0x6F, 0x75, 0x95]);
|
||||
// With SHA-512, message = "sample":
|
||||
// k = 09ECE7CA27D0F5A4DD4E556C9DF1D21D28104F8B
|
||||
// r = 16C3491F9B8C3FBBDD5E7A7B667057F0D8EE8E1B
|
||||
// s = 02C36A127A7B89EDBB72E4FFBC71DABC7D4FC69C
|
||||
run_rfc6979_test!(SHA512, U192, sample, params, public, private,
|
||||
k vec![0x09, 0xEC, 0xE7, 0xCA, 0x27, 0xD0, 0xF5, 0xA4,
|
||||
0xDD, 0x4E, 0x55, 0x6C, 0x9D, 0xF1, 0xD2, 0x1D,
|
||||
0x28, 0x10, 0x4F, 0x8B],
|
||||
r vec![0x16, 0xC3, 0x49, 0x1F, 0x9B, 0x8C, 0x3F, 0xBB,
|
||||
0xDD, 0x5E, 0x7A, 0x7B, 0x66, 0x70, 0x57, 0xF0,
|
||||
0xD8, 0xEE, 0x8E, 0x1B],
|
||||
s vec![0x02, 0xC3, 0x6A, 0x12, 0x7A, 0x7B, 0x89, 0xED,
|
||||
0xBB, 0x72, 0xE4, 0xFF, 0xBC, 0x71, 0xDA, 0xBC,
|
||||
0x7D, 0x4F, 0xC6, 0x9C]);
|
||||
// With SHA-1, message = "test":
|
||||
// k = 5C842DF4F9E344EE09F056838B42C7A17F4A6433
|
||||
// r = 42AB2052FD43E123F0607F115052A67DCD9C5C77
|
||||
// s = 183916B0230D45B9931491D4C6B0BD2FB4AAF088
|
||||
run_rfc6979_test!(SHA1, U192, test, params, public, private,
|
||||
k vec![0x5C, 0x84, 0x2D, 0xF4, 0xF9, 0xE3, 0x44, 0xEE,
|
||||
0x09, 0xF0, 0x56, 0x83, 0x8B, 0x42, 0xC7, 0xA1,
|
||||
0x7F, 0x4A, 0x64, 0x33],
|
||||
r vec![0x42, 0xAB, 0x20, 0x52, 0xFD, 0x43, 0xE1, 0x23,
|
||||
0xF0, 0x60, 0x7F, 0x11, 0x50, 0x52, 0xA6, 0x7D,
|
||||
0xCD, 0x9C, 0x5C, 0x77],
|
||||
s vec![0x18, 0x39, 0x16, 0xB0, 0x23, 0x0D, 0x45, 0xB9,
|
||||
0x93, 0x14, 0x91, 0xD4, 0xC6, 0xB0, 0xBD, 0x2F,
|
||||
0xB4, 0xAA, 0xF0, 0x88]);
|
||||
// With SHA-224, message = "test":
|
||||
// k = 4598B8EFC1A53BC8AECD58D1ABBB0C0C71E67297
|
||||
// r = 6868E9964E36C1689F6037F91F28D5F2C30610F2
|
||||
// s = 49CEC3ACDC83018C5BD2674ECAAD35B8CD22940F
|
||||
run_rfc6979_test!(SHA224, U192, test, params, public, private,
|
||||
k vec![0x45, 0x98, 0xB8, 0xEF, 0xC1, 0xA5, 0x3B, 0xC8,
|
||||
0xAE, 0xCD, 0x58, 0xD1, 0xAB, 0xBB, 0x0C, 0x0C,
|
||||
0x71, 0xE6, 0x72, 0x97],
|
||||
r vec![0x68, 0x68, 0xE9, 0x96, 0x4E, 0x36, 0xC1, 0x68,
|
||||
0x9F, 0x60, 0x37, 0xF9, 0x1F, 0x28, 0xD5, 0xF2,
|
||||
0xC3, 0x06, 0x10, 0xF2],
|
||||
s vec![0x49, 0xCE, 0xC3, 0xAC, 0xDC, 0x83, 0x01, 0x8C,
|
||||
0x5B, 0xD2, 0x67, 0x4E, 0xCA, 0xAD, 0x35, 0xB8,
|
||||
0xCD, 0x22, 0x94, 0x0F]);
|
||||
// With SHA-256, message = "test":
|
||||
// k = 5A67592E8128E03A417B0484410FB72C0B630E1A
|
||||
// r = 22518C127299B0F6FDC9872B282B9E70D0790812
|
||||
// s = 6837EC18F150D55DE95B5E29BE7AF5D01E4FE160
|
||||
run_rfc6979_test!(SHA256, U192, test, params, public, private,
|
||||
k vec![0x5A, 0x67, 0x59, 0x2E, 0x81, 0x28, 0xE0, 0x3A,
|
||||
0x41, 0x7B, 0x04, 0x84, 0x41, 0x0F, 0xB7, 0x2C,
|
||||
0x0B, 0x63, 0x0E, 0x1A],
|
||||
r vec![0x22, 0x51, 0x8C, 0x12, 0x72, 0x99, 0xB0, 0xF6,
|
||||
0xFD, 0xC9, 0x87, 0x2B, 0x28, 0x2B, 0x9E, 0x70,
|
||||
0xD0, 0x79, 0x08, 0x12],
|
||||
s vec![0x68, 0x37, 0xEC, 0x18, 0xF1, 0x50, 0xD5, 0x5D,
|
||||
0xE9, 0x5B, 0x5E, 0x29, 0xBE, 0x7A, 0xF5, 0xD0,
|
||||
0x1E, 0x4F, 0xE1, 0x60]);
|
||||
// With SHA-384, message = "test":
|
||||
// k = 220156B761F6CA5E6C9F1B9CF9C24BE25F98CD89
|
||||
// r = 854CF929B58D73C3CBFDC421E8D5430CD6DB5E66
|
||||
// s = 91D0E0F53E22F898D158380676A871A157CDA622
|
||||
run_rfc6979_test!(SHA384, U192, test, params, public, private,
|
||||
k vec![0x22, 0x01, 0x56, 0xB7, 0x61, 0xF6, 0xCA, 0x5E,
|
||||
0x6C, 0x9F, 0x1B, 0x9C, 0xF9, 0xC2, 0x4B, 0xE2,
|
||||
0x5F, 0x98, 0xCD, 0x89],
|
||||
r vec![0x85, 0x4C, 0xF9, 0x29, 0xB5, 0x8D, 0x73, 0xC3,
|
||||
0xCB, 0xFD, 0xC4, 0x21, 0xE8, 0xD5, 0x43, 0x0C,
|
||||
0xD6, 0xDB, 0x5E, 0x66],
|
||||
s vec![0x91, 0xD0, 0xE0, 0xF5, 0x3E, 0x22, 0xF8, 0x98,
|
||||
0xD1, 0x58, 0x38, 0x06, 0x76, 0xA8, 0x71, 0xA1,
|
||||
0x57, 0xCD, 0xA6, 0x22]);
|
||||
// With SHA-512, message = "test":
|
||||
// k = 65D2C2EEB175E370F28C75BFCDC028D22C7DBE9C
|
||||
// r = 8EA47E475BA8AC6F2D821DA3BD212D11A3DEB9A0
|
||||
// s = 7C670C7AD72B6C050C109E1790008097125433E8
|
||||
run_rfc6979_test!(SHA512, U192, test, params, public, private,
|
||||
k vec![0x65, 0xD2, 0xC2, 0xEE, 0xB1, 0x75, 0xE3, 0x70,
|
||||
0xF2, 0x8C, 0x75, 0xBF, 0xCD, 0xC0, 0x28, 0xD2,
|
||||
0x2C, 0x7D, 0xBE, 0x9C],
|
||||
r vec![0x8E, 0xA4, 0x7E, 0x47, 0x5B, 0xA8, 0xAC, 0x6F,
|
||||
0x2D, 0x82, 0x1D, 0xA3, 0xBD, 0x21, 0x2D, 0x11,
|
||||
0xA3, 0xDE, 0xB9, 0xA0],
|
||||
s vec![0x7C, 0x67, 0x0C, 0x7A, 0xD7, 0x2B, 0x6C, 0x05,
|
||||
0x0C, 0x10, 0x9E, 0x17, 0x90, 0x00, 0x80, 0x97,
|
||||
0x12, 0x54, 0x33, 0xE8]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn appendix_a22() {
|
||||
let pbytes = vec![0x9D,0xB6,0xFB,0x59,0x51,0xB6,0x6B,0xB6,
|
||||
0xFE,0x1E,0x14,0x0F,0x1D,0x2C,0xE5,0x50,
|
||||
0x23,0x74,0x16,0x1F,0xD6,0x53,0x8D,0xF1,
|
||||
0x64,0x82,0x18,0x64,0x2F,0x0B,0x5C,0x48,
|
||||
0xC8,0xF7,0xA4,0x1A,0xAD,0xFA,0x18,0x73,
|
||||
0x24,0xB8,0x76,0x74,0xFA,0x18,0x22,0xB0,
|
||||
0x0F,0x1E,0xCF,0x81,0x36,0x94,0x3D,0x7C,
|
||||
0x55,0x75,0x72,0x64,0xE5,0xA1,0xA4,0x4F,
|
||||
0xFE,0x01,0x2E,0x99,0x36,0xE0,0x0C,0x1D,
|
||||
0x3E,0x93,0x10,0xB0,0x1C,0x7D,0x17,0x98,
|
||||
0x05,0xD3,0x05,0x8B,0x2A,0x9F,0x4B,0xB6,
|
||||
0xF9,0x71,0x6B,0xFE,0x61,0x17,0xC6,0xB5,
|
||||
0xB3,0xCC,0x4D,0x9B,0xE3,0x41,0x10,0x4A,
|
||||
0xD4,0xA8,0x0A,0xD6,0xC9,0x4E,0x00,0x5F,
|
||||
0x4B,0x99,0x3E,0x14,0xF0,0x91,0xEB,0x51,
|
||||
0x74,0x3B,0xF3,0x30,0x50,0xC3,0x8D,0xE2,
|
||||
0x35,0x56,0x7E,0x1B,0x34,0xC3,0xD6,0xA5,
|
||||
0xC0,0xCE,0xAA,0x1A,0x0F,0x36,0x82,0x13,
|
||||
0xC3,0xD1,0x98,0x43,0xD0,0xB4,0xB0,0x9D,
|
||||
0xCB,0x9F,0xC7,0x2D,0x39,0xC8,0xDE,0x41,
|
||||
0xF1,0xBF,0x14,0xD4,0xBB,0x45,0x63,0xCA,
|
||||
0x28,0x37,0x16,0x21,0xCA,0xD3,0x32,0x4B,
|
||||
0x6A,0x2D,0x39,0x21,0x45,0xBE,0xBF,0xAC,
|
||||
0x74,0x88,0x05,0x23,0x6F,0x5C,0xA2,0xFE,
|
||||
0x92,0xB8,0x71,0xCD,0x8F,0x9C,0x36,0xD3,
|
||||
0x29,0x2B,0x55,0x09,0xCA,0x8C,0xAA,0x77,
|
||||
0xA2,0xAD,0xFC,0x7B,0xFD,0x77,0xDD,0xA6,
|
||||
0xF7,0x11,0x25,0xA7,0x45,0x6F,0xEA,0x15,
|
||||
0x3E,0x43,0x32,0x56,0xA2,0x26,0x1C,0x6A,
|
||||
0x06,0xED,0x36,0x93,0x79,0x7E,0x79,0x95,
|
||||
0xFA,0xD5,0xAA,0xBB,0xCF,0xBE,0x3E,0xDA,
|
||||
0x27,0x41,0xE3,0x75,0x40,0x4A,0xE2,0x5B];
|
||||
let qbytes = vec![0xF2,0xC3,0x11,0x93,0x74,0xCE,0x76,0xC9,
|
||||
0x35,0x69,0x90,0xB4,0x65,0x37,0x4A,0x17,
|
||||
0xF2,0x3F,0x9E,0xD3,0x50,0x89,0xBD,0x96,
|
||||
0x9F,0x61,0xC6,0xDD,0xE9,0x99,0x8C,0x1F];
|
||||
let gbytes = vec![0x5C,0x7F,0xF6,0xB0,0x6F,0x8F,0x14,0x3F,
|
||||
0xE8,0x28,0x84,0x33,0x49,0x3E,0x47,0x69,
|
||||
0xC4,0xD9,0x88,0xAC,0xE5,0xBE,0x25,0xA0,
|
||||
0xE2,0x48,0x09,0x67,0x07,0x16,0xC6,0x13,
|
||||
0xD7,0xB0,0xCE,0xE6,0x93,0x2F,0x8F,0xAA,
|
||||
0x7C,0x44,0xD2,0xCB,0x24,0x52,0x3D,0xA5,
|
||||
0x3F,0xBE,0x4F,0x6E,0xC3,0x59,0x58,0x92,
|
||||
0xD1,0xAA,0x58,0xC4,0x32,0x8A,0x06,0xC4,
|
||||
0x6A,0x15,0x66,0x2E,0x7E,0xAA,0x70,0x3A,
|
||||
0x1D,0xEC,0xF8,0xBB,0xB2,0xD0,0x5D,0xBE,
|
||||
0x2E,0xB9,0x56,0xC1,0x42,0xA3,0x38,0x66,
|
||||
0x1D,0x10,0x46,0x1C,0x0D,0x13,0x54,0x72,
|
||||
0x08,0x50,0x57,0xF3,0x49,0x43,0x09,0xFF,
|
||||
0xA7,0x3C,0x61,0x1F,0x78,0xB3,0x2A,0xDB,
|
||||
0xB5,0x74,0x0C,0x36,0x1C,0x9F,0x35,0xBE,
|
||||
0x90,0x99,0x7D,0xB2,0x01,0x4E,0x2E,0xF5,
|
||||
0xAA,0x61,0x78,0x2F,0x52,0xAB,0xEB,0x8B,
|
||||
0xD6,0x43,0x2C,0x4D,0xD0,0x97,0xBC,0x54,
|
||||
0x23,0xB2,0x85,0xDA,0xFB,0x60,0xDC,0x36,
|
||||
0x4E,0x81,0x61,0xF4,0xA2,0xA3,0x5A,0xCA,
|
||||
0x3A,0x10,0xB1,0xC4,0xD2,0x03,0xCC,0x76,
|
||||
0xA4,0x70,0xA3,0x3A,0xFD,0xCB,0xDD,0x92,
|
||||
0x95,0x98,0x59,0xAB,0xD8,0xB5,0x6E,0x17,
|
||||
0x25,0x25,0x2D,0x78,0xEA,0xC6,0x6E,0x71,
|
||||
0xBA,0x9A,0xE3,0xF1,0xDD,0x24,0x87,0x19,
|
||||
0x98,0x74,0x39,0x3C,0xD4,0xD8,0x32,0x18,
|
||||
0x68,0x00,0x65,0x47,0x60,0xE1,0xE3,0x4C,
|
||||
0x09,0xE4,0xD1,0x55,0x17,0x9F,0x9E,0xC0,
|
||||
0xDC,0x44,0x73,0xF9,0x96,0xBD,0xCE,0x6E,
|
||||
0xED,0x1C,0xAB,0xED,0x8B,0x6F,0x11,0x6F,
|
||||
0x7A,0xD9,0xCF,0x50,0x5D,0xF0,0xF9,0x98,
|
||||
0xE3,0x4A,0xB2,0x75,0x14,0xB0,0xFF,0xE7];
|
||||
let xbytes = vec![0x69,0xC7,0x54,0x8C,0x21,0xD0,0xDF,0xEA,
|
||||
0x6B,0x9A,0x51,0xC9,0xEA,0xD4,0xE2,0x7C,
|
||||
0x33,0xD3,0xB3,0xF1,0x80,0x31,0x6E,0x5B,
|
||||
0xCA,0xB9,0x2C,0x93,0x3F,0x0E,0x4D,0xBC];
|
||||
let ybytes = vec![0x66,0x70,0x98,0xC6,0x54,0x42,0x6C,0x78,
|
||||
0xD7,0xF8,0x20,0x1E,0xAC,0x6C,0x20,0x3E,
|
||||
0xF0,0x30,0xD4,0x36,0x05,0x03,0x2C,0x2F,
|
||||
0x1F,0xA9,0x37,0xE5,0x23,0x7D,0xBD,0x94,
|
||||
0x9F,0x34,0xA0,0xA2,0x56,0x4F,0xE1,0x26,
|
||||
0xDC,0x8B,0x71,0x5C,0x51,0x41,0x80,0x2C,
|
||||
0xE0,0x97,0x9C,0x82,0x46,0x46,0x3C,0x40,
|
||||
0xE6,0xB6,0xBD,0xAA,0x25,0x13,0xFA,0x61,
|
||||
0x17,0x28,0x71,0x6C,0x2E,0x4F,0xD5,0x3B,
|
||||
0xC9,0x5B,0x89,0xE6,0x99,0x49,0xD9,0x65,
|
||||
0x12,0xE8,0x73,0xB9,0xC8,0xF8,0xDF,0xD4,
|
||||
0x99,0xCC,0x31,0x28,0x82,0x56,0x1A,0xDE,
|
||||
0xCB,0x31,0xF6,0x58,0xE9,0x34,0xC0,0xC1,
|
||||
0x97,0xF2,0xC4,0xD9,0x6B,0x05,0xCB,0xAD,
|
||||
0x67,0x38,0x1E,0x7B,0x76,0x88,0x91,0xE4,
|
||||
0xDA,0x38,0x43,0xD2,0x4D,0x94,0xCD,0xFB,
|
||||
0x51,0x26,0xE9,0xB8,0xBF,0x21,0xE8,0x35,
|
||||
0x8E,0xE0,0xE0,0xA3,0x0E,0xF1,0x3F,0xD6,
|
||||
0xA6,0x64,0xC0,0xDC,0xE3,0x73,0x1F,0x7F,
|
||||
0xB4,0x9A,0x48,0x45,0xA4,0xFD,0x82,0x54,
|
||||
0x68,0x79,0x72,0xA2,0xD3,0x82,0x59,0x9C,
|
||||
0x9B,0xAC,0x4E,0x0E,0xD7,0x99,0x81,0x93,
|
||||
0x07,0x89,0x13,0x03,0x25,0x58,0x13,0x49,
|
||||
0x76,0x41,0x0B,0x89,0xD2,0xC1,0x71,0xD1,
|
||||
0x23,0xAC,0x35,0xFD,0x97,0x72,0x19,0x59,
|
||||
0x7A,0xA7,0xD1,0x5C,0x1A,0x9A,0x42,0x8E,
|
||||
0x59,0x19,0x4F,0x75,0xC7,0x21,0xEB,0xCB,
|
||||
0xCF,0xAE,0x44,0x69,0x6A,0x49,0x9A,0xFA,
|
||||
0x74,0xE0,0x42,0x99,0xF1,0x32,0x02,0x66,
|
||||
0x01,0x63,0x8C,0xB8,0x7A,0xB7,0x91,0x90,
|
||||
0xD4,0xA0,0x98,0x63,0x15,0xDA,0x8E,0xEC,
|
||||
0x65,0x61,0xC9,0x38,0x99,0x6B,0xEA,0xDF];
|
||||
//
|
||||
let p = U2048::from_bytes(&pbytes);
|
||||
let q = U256::from_bytes(&qbytes);
|
||||
let g = U2048::from_bytes(&gbytes);
|
||||
let params = L2048N256::new(p, g, q);
|
||||
let x = U256::from_bytes(&xbytes);
|
||||
let y = U2048::from_bytes(&ybytes);
|
||||
let private = DSAPrivateKey::<L2048N256>::new(params.clone(), x);
|
||||
let public = DSAPublicKey::<L2048N256>::new(params.clone(), y);
|
||||
//
|
||||
let sample: [u8; 6] = [115, 97, 109, 112, 108, 101]; // "sample", ASCII
|
||||
let test: [u8; 4] = [116, 101, 115, 116]; // "test", ASCII
|
||||
// With SHA-1, message = "sample":
|
||||
// k = 888FA6F7738A41BDC9846466ABDB8174C0338250AE50CE955CA16230F9CBD53E
|
||||
// r = 3A1B2DBD7489D6ED7E608FD036C83AF396E290DBD602408E8677DAABD6E7445A
|
||||
// s = D26FCBA19FA3E3058FFC02CA1596CDBB6E0D20CB37B06054F7E36DED0CDBBCCF
|
||||
run_rfc6979_test!(SHA1, U256, sample, params, public, private,
|
||||
k vec![0x88,0x8F,0xA6,0xF7,0x73,0x8A,0x41,0xBD,
|
||||
0xC9,0x84,0x64,0x66,0xAB,0xDB,0x81,0x74,
|
||||
0xC0,0x33,0x82,0x50,0xAE,0x50,0xCE,0x95,
|
||||
0x5C,0xA1,0x62,0x30,0xF9,0xCB,0xD5,0x3E],
|
||||
r vec![0x3A,0x1B,0x2D,0xBD,0x74,0x89,0xD6,0xED,
|
||||
0x7E,0x60,0x8F,0xD0,0x36,0xC8,0x3A,0xF3,
|
||||
0x96,0xE2,0x90,0xDB,0xD6,0x02,0x40,0x8E,
|
||||
0x86,0x77,0xDA,0xAB,0xD6,0xE7,0x44,0x5A],
|
||||
s vec![0xD2,0x6F,0xCB,0xA1,0x9F,0xA3,0xE3,0x05,
|
||||
0x8F,0xFC,0x02,0xCA,0x15,0x96,0xCD,0xBB,
|
||||
0x6E,0x0D,0x20,0xCB,0x37,0xB0,0x60,0x54,
|
||||
0xF7,0xE3,0x6D,0xED,0x0C,0xDB,0xBC,0xCF]);
|
||||
// With SHA-224, message = "sample":
|
||||
// k = BC372967702082E1AA4FCE892209F71AE4AD25A6DFD869334E6F153BD0C4D806
|
||||
// r = DC9F4DEADA8D8FF588E98FED0AB690FFCE858DC8C79376450EB6B76C24537E2C
|
||||
// s = A65A9C3BC7BABE286B195D5DA68616DA8D47FA0097F36DD19F517327DC848CEC
|
||||
run_rfc6979_test!(SHA224, U256, sample, params, public, private,
|
||||
k vec![0xBC,0x37,0x29,0x67,0x70,0x20,0x82,0xE1,
|
||||
0xAA,0x4F,0xCE,0x89,0x22,0x09,0xF7,0x1A,
|
||||
0xE4,0xAD,0x25,0xA6,0xDF,0xD8,0x69,0x33,
|
||||
0x4E,0x6F,0x15,0x3B,0xD0,0xC4,0xD8,0x06],
|
||||
r vec![0xDC,0x9F,0x4D,0xEA,0xDA,0x8D,0x8F,0xF5,
|
||||
0x88,0xE9,0x8F,0xED,0x0A,0xB6,0x90,0xFF,
|
||||
0xCE,0x85,0x8D,0xC8,0xC7,0x93,0x76,0x45,
|
||||
0x0E,0xB6,0xB7,0x6C,0x24,0x53,0x7E,0x2C],
|
||||
s vec![0xA6,0x5A,0x9C,0x3B,0xC7,0xBA,0xBE,0x28,
|
||||
0x6B,0x19,0x5D,0x5D,0xA6,0x86,0x16,0xDA,
|
||||
0x8D,0x47,0xFA,0x00,0x97,0xF3,0x6D,0xD1,
|
||||
0x9F,0x51,0x73,0x27,0xDC,0x84,0x8C,0xEC]);
|
||||
// With SHA-256, message = "sample":
|
||||
// k = 8926A27C40484216F052F4427CFD5647338B7B3939BC6573AF4333569D597C52
|
||||
// r = EACE8BDBBE353C432A795D9EC556C6D021F7A03F42C36E9BC87E4AC7932CC809
|
||||
// s = 7081E175455F9247B812B74583E9E94F9EA79BD640DC962533B0680793A38D53
|
||||
run_rfc6979_test!(SHA256, U256, sample, params, public, private,
|
||||
k vec![0x89,0x26,0xA2,0x7C,0x40,0x48,0x42,0x16,
|
||||
0xF0,0x52,0xF4,0x42,0x7C,0xFD,0x56,0x47,
|
||||
0x33,0x8B,0x7B,0x39,0x39,0xBC,0x65,0x73,
|
||||
0xAF,0x43,0x33,0x56,0x9D,0x59,0x7C,0x52],
|
||||
r vec![0xEA,0xCE,0x8B,0xDB,0xBE,0x35,0x3C,0x43,
|
||||
0x2A,0x79,0x5D,0x9E,0xC5,0x56,0xC6,0xD0,
|
||||
0x21,0xF7,0xA0,0x3F,0x42,0xC3,0x6E,0x9B,
|
||||
0xC8,0x7E,0x4A,0xC7,0x93,0x2C,0xC8,0x09],
|
||||
s vec![0x70,0x81,0xE1,0x75,0x45,0x5F,0x92,0x47,
|
||||
0xB8,0x12,0xB7,0x45,0x83,0xE9,0xE9,0x4F,
|
||||
0x9E,0xA7,0x9B,0xD6,0x40,0xDC,0x96,0x25,
|
||||
0x33,0xB0,0x68,0x07,0x93,0xA3,0x8D,0x53]);
|
||||
// With SHA-384, message = "sample":
|
||||
// k = C345D5AB3DA0A5BCB7EC8F8FB7A7E96069E03B206371EF7D83E39068EC564920
|
||||
// r = B2DA945E91858834FD9BF616EBAC151EDBC4B45D27D0DD4A7F6A22739F45C00B
|
||||
// s = 19048B63D9FD6BCA1D9BAE3664E1BCB97F7276C306130969F63F38FA8319021B
|
||||
run_rfc6979_test!(SHA384, U256, sample, params, public, private,
|
||||
k vec![0xC3,0x45,0xD5,0xAB,0x3D,0xA0,0xA5,0xBC,
|
||||
0xB7,0xEC,0x8F,0x8F,0xB7,0xA7,0xE9,0x60,
|
||||
0x69,0xE0,0x3B,0x20,0x63,0x71,0xEF,0x7D,
|
||||
0x83,0xE3,0x90,0x68,0xEC,0x56,0x49,0x20],
|
||||
r vec![0xB2,0xDA,0x94,0x5E,0x91,0x85,0x88,0x34,
|
||||
0xFD,0x9B,0xF6,0x16,0xEB,0xAC,0x15,0x1E,
|
||||
0xDB,0xC4,0xB4,0x5D,0x27,0xD0,0xDD,0x4A,
|
||||
0x7F,0x6A,0x22,0x73,0x9F,0x45,0xC0,0x0B],
|
||||
s vec![0x19,0x04,0x8B,0x63,0xD9,0xFD,0x6B,0xCA,
|
||||
0x1D,0x9B,0xAE,0x36,0x64,0xE1,0xBC,0xB9,
|
||||
0x7F,0x72,0x76,0xC3,0x06,0x13,0x09,0x69,
|
||||
0xF6,0x3F,0x38,0xFA,0x83,0x19,0x02,0x1B]);
|
||||
// With SHA-512, message = "sample":
|
||||
// k = 5A12994431785485B3F5F067221517791B85A597B7A9436995C89ED0374668FC
|
||||
// r = 2016ED092DC5FB669B8EFB3D1F31A91EECB199879BE0CF78F02BA062CB4C942E
|
||||
// s = D0C76F84B5F091E141572A639A4FB8C230807EEA7D55C8A154A224400AFF2351
|
||||
run_rfc6979_test!(SHA512, U256, sample, params, public, private,
|
||||
k vec![0x5A,0x12,0x99,0x44,0x31,0x78,0x54,0x85,
|
||||
0xB3,0xF5,0xF0,0x67,0x22,0x15,0x17,0x79,
|
||||
0x1B,0x85,0xA5,0x97,0xB7,0xA9,0x43,0x69,
|
||||
0x95,0xC8,0x9E,0xD0,0x37,0x46,0x68,0xFC],
|
||||
r vec![0x20,0x16,0xED,0x09,0x2D,0xC5,0xFB,0x66,
|
||||
0x9B,0x8E,0xFB,0x3D,0x1F,0x31,0xA9,0x1E,
|
||||
0xEC,0xB1,0x99,0x87,0x9B,0xE0,0xCF,0x78,
|
||||
0xF0,0x2B,0xA0,0x62,0xCB,0x4C,0x94,0x2E],
|
||||
s vec![0xD0,0xC7,0x6F,0x84,0xB5,0xF0,0x91,0xE1,
|
||||
0x41,0x57,0x2A,0x63,0x9A,0x4F,0xB8,0xC2,
|
||||
0x30,0x80,0x7E,0xEA,0x7D,0x55,0xC8,0xA1,
|
||||
0x54,0xA2,0x24,0x40,0x0A,0xFF,0x23,0x51]);
|
||||
// With SHA-1, message = "test":
|
||||
// k = 6EEA486F9D41A037B2C640BC5645694FF8FF4B98D066A25F76BE641CCB24BA4F
|
||||
// r = C18270A93CFC6063F57A4DFA86024F700D980E4CF4E2CB65A504397273D98EA0
|
||||
// s = 414F22E5F31A8B6D33295C7539C1C1BA3A6160D7D68D50AC0D3A5BEAC2884FAA
|
||||
run_rfc6979_test!(SHA1, U256, test, params, public, private,
|
||||
k vec![0x6E,0xEA,0x48,0x6F,0x9D,0x41,0xA0,0x37,
|
||||
0xB2,0xC6,0x40,0xBC,0x56,0x45,0x69,0x4F,
|
||||
0xF8,0xFF,0x4B,0x98,0xD0,0x66,0xA2,0x5F,
|
||||
0x76,0xBE,0x64,0x1C,0xCB,0x24,0xBA,0x4F],
|
||||
r vec![0xC1,0x82,0x70,0xA9,0x3C,0xFC,0x60,0x63,
|
||||
0xF5,0x7A,0x4D,0xFA,0x86,0x02,0x4F,0x70,
|
||||
0x0D,0x98,0x0E,0x4C,0xF4,0xE2,0xCB,0x65,
|
||||
0xA5,0x04,0x39,0x72,0x73,0xD9,0x8E,0xA0],
|
||||
s vec![0x41,0x4F,0x22,0xE5,0xF3,0x1A,0x8B,0x6D,
|
||||
0x33,0x29,0x5C,0x75,0x39,0xC1,0xC1,0xBA,
|
||||
0x3A,0x61,0x60,0xD7,0xD6,0x8D,0x50,0xAC,
|
||||
0x0D,0x3A,0x5B,0xEA,0xC2,0x88,0x4F,0xAA]);
|
||||
// With SHA-224, message = "test":
|
||||
// k = 06BD4C05ED74719106223BE33F2D95DA6B3B541DAD7BFBD7AC508213B6DA6670
|
||||
// r = 272ABA31572F6CC55E30BF616B7A265312018DD325BE031BE0CC82AA17870EA3
|
||||
// s = E9CC286A52CCE201586722D36D1E917EB96A4EBDB47932F9576AC645B3A60806
|
||||
run_rfc6979_test!(SHA224, U256, test, params, public, private,
|
||||
k vec![0x06,0xBD,0x4C,0x05,0xED,0x74,0x71,0x91,
|
||||
0x06,0x22,0x3B,0xE3,0x3F,0x2D,0x95,0xDA,
|
||||
0x6B,0x3B,0x54,0x1D,0xAD,0x7B,0xFB,0xD7,
|
||||
0xAC,0x50,0x82,0x13,0xB6,0xDA,0x66,0x70],
|
||||
r vec![0x27,0x2A,0xBA,0x31,0x57,0x2F,0x6C,0xC5,
|
||||
0x5E,0x30,0xBF,0x61,0x6B,0x7A,0x26,0x53,
|
||||
0x12,0x01,0x8D,0xD3,0x25,0xBE,0x03,0x1B,
|
||||
0xE0,0xCC,0x82,0xAA,0x17,0x87,0x0E,0xA3],
|
||||
s vec![0xE9,0xCC,0x28,0x6A,0x52,0xCC,0xE2,0x01,
|
||||
0x58,0x67,0x22,0xD3,0x6D,0x1E,0x91,0x7E,
|
||||
0xB9,0x6A,0x4E,0xBD,0xB4,0x79,0x32,0xF9,
|
||||
0x57,0x6A,0xC6,0x45,0xB3,0xA6,0x08,0x06]);
|
||||
// With SHA-256, message = "test":
|
||||
// k = 1D6CE6DDA1C5D37307839CD03AB0A5CBB18E60D800937D67DFB4479AAC8DEAD7
|
||||
// r = 8190012A1969F9957D56FCCAAD223186F423398D58EF5B3CEFD5A4146A4476F0
|
||||
// s = 7452A53F7075D417B4B013B278D1BB8BBD21863F5E7B1CEE679CF2188E1AB19E
|
||||
run_rfc6979_test!(SHA256, U256, test, params, public, private,
|
||||
k vec![0x1D,0x6C,0xE6,0xDD,0xA1,0xC5,0xD3,0x73,
|
||||
0x07,0x83,0x9C,0xD0,0x3A,0xB0,0xA5,0xCB,
|
||||
0xB1,0x8E,0x60,0xD8,0x00,0x93,0x7D,0x67,
|
||||
0xDF,0xB4,0x47,0x9A,0xAC,0x8D,0xEA,0xD7],
|
||||
r vec![0x81,0x90,0x01,0x2A,0x19,0x69,0xF9,0x95,
|
||||
0x7D,0x56,0xFC,0xCA,0xAD,0x22,0x31,0x86,
|
||||
0xF4,0x23,0x39,0x8D,0x58,0xEF,0x5B,0x3C,
|
||||
0xEF,0xD5,0xA4,0x14,0x6A,0x44,0x76,0xF0],
|
||||
s vec![0x74,0x52,0xA5,0x3F,0x70,0x75,0xD4,0x17,
|
||||
0xB4,0xB0,0x13,0xB2,0x78,0xD1,0xBB,0x8B,
|
||||
0xBD,0x21,0x86,0x3F,0x5E,0x7B,0x1C,0xEE,
|
||||
0x67,0x9C,0xF2,0x18,0x8E,0x1A,0xB1,0x9E]);
|
||||
// With SHA-384, message = "test":
|
||||
// k = 206E61F73DBE1B2DC8BE736B22B079E9DACD974DB00EEBBC5B64CAD39CF9F91C
|
||||
// r = 239E66DDBE8F8C230A3D071D601B6FFBDFB5901F94D444C6AF56F732BEB954BE
|
||||
// s = 6BD737513D5E72FE85D1C750E0F73921FE299B945AAD1C802F15C26A43D34961
|
||||
run_rfc6979_test!(SHA384, U256, test, params, public, private,
|
||||
k vec![0x20,0x6E,0x61,0xF7,0x3D,0xBE,0x1B,0x2D,
|
||||
0xC8,0xBE,0x73,0x6B,0x22,0xB0,0x79,0xE9,
|
||||
0xDA,0xCD,0x97,0x4D,0xB0,0x0E,0xEB,0xBC,
|
||||
0x5B,0x64,0xCA,0xD3,0x9C,0xF9,0xF9,0x1C],
|
||||
r vec![0x23,0x9E,0x66,0xDD,0xBE,0x8F,0x8C,0x23,
|
||||
0x0A,0x3D,0x07,0x1D,0x60,0x1B,0x6F,0xFB,
|
||||
0xDF,0xB5,0x90,0x1F,0x94,0xD4,0x44,0xC6,
|
||||
0xAF,0x56,0xF7,0x32,0xBE,0xB9,0x54,0xBE],
|
||||
s vec![0x6B,0xD7,0x37,0x51,0x3D,0x5E,0x72,0xFE,
|
||||
0x85,0xD1,0xC7,0x50,0xE0,0xF7,0x39,0x21,
|
||||
0xFE,0x29,0x9B,0x94,0x5A,0xAD,0x1C,0x80,
|
||||
0x2F,0x15,0xC2,0x6A,0x43,0xD3,0x49,0x61]);
|
||||
// With SHA-512, message = "test":
|
||||
// k = AFF1651E4CD6036D57AA8B2A05CCF1A9D5A40166340ECBBDC55BE10B568AA0AA
|
||||
// r = 89EC4BB1400ECCFF8E7D9AA515CD1DE7803F2DAFF09693EE7FD1353E90A68307
|
||||
// s = C9F0BDABCC0D880BB137A994CC7F3980CE91CC10FAF529FC46565B15CEA854E1
|
||||
run_rfc6979_test!(SHA512, U256, test, params, public, private,
|
||||
k vec![0xAF,0xF1,0x65,0x1E,0x4C,0xD6,0x03,0x6D,
|
||||
0x57,0xAA,0x8B,0x2A,0x05,0xCC,0xF1,0xA9,
|
||||
0xD5,0xA4,0x01,0x66,0x34,0x0E,0xCB,0xBD,
|
||||
0xC5,0x5B,0xE1,0x0B,0x56,0x8A,0xA0,0xAA],
|
||||
r vec![0x89,0xEC,0x4B,0xB1,0x40,0x0E,0xCC,0xFF,
|
||||
0x8E,0x7D,0x9A,0xA5,0x15,0xCD,0x1D,0xE7,
|
||||
0x80,0x3F,0x2D,0xAF,0xF0,0x96,0x93,0xEE,
|
||||
0x7F,0xD1,0x35,0x3E,0x90,0xA6,0x83,0x07],
|
||||
s vec![0xC9,0xF0,0xBD,0xAB,0xCC,0x0D,0x88,0x0B,
|
||||
0xB1,0x37,0xA9,0x94,0xCC,0x7F,0x39,0x80,
|
||||
0xCE,0x91,0xCC,0x10,0xFA,0xF5,0x29,0xFC,
|
||||
0x46,0x56,0x5B,0x15,0xCE,0xA8,0x54,0xE1]);
|
||||
}
|
||||
498
src/ecdsa/curve.rs
Normal file
498
src/ecdsa/curve.rs
Normal file
@@ -0,0 +1,498 @@
|
||||
use cryptonum::signed::{I192,I256,I384,I576};
|
||||
use cryptonum::unsigned::{Decoder};
|
||||
use cryptonum::unsigned::{U192,U256,U384,U576};
|
||||
use ecdsa::point::Point;
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Elliptic curves must implement this trait in order to work with the rest
|
||||
/// of the ECDSA system. I've included instances for the core NIST curves
|
||||
/// used in most systems, but this could be extended without issues.
|
||||
/// (Eventually the curves defined here should actually be extended in
|
||||
/// interesting ways to make the math faster, but we haven't gotten there
|
||||
/// yet.)
|
||||
#[allow(non_snake_case)]
|
||||
pub trait EllipticCurve {
|
||||
/// The unsigned numeric type that fits constants for this curve.
|
||||
type Unsigned : Clone;
|
||||
/// The signed numeric type that fits constants for this curve.
|
||||
type Signed : Clone + Debug + PartialEq;
|
||||
/// The type of a point on the curve
|
||||
type Point;
|
||||
|
||||
/// The size of the curve in bits.
|
||||
fn size() -> usize;
|
||||
/// The `p` value for the curve.
|
||||
fn p() -> Self::Unsigned;
|
||||
/// The `p` value for the curve.
|
||||
fn n() -> Self::Unsigned;
|
||||
/// The seed value for the curve.
|
||||
fn SEED() -> Self::Unsigned;
|
||||
/// The `c` value for the curve.
|
||||
fn c() -> Self::Unsigned;
|
||||
/// The `a` value for the curve.
|
||||
fn a() -> Self::Unsigned;
|
||||
/// The `b` value for the curve.
|
||||
fn b() -> Self::Unsigned;
|
||||
/// The `x` coordinate of the base point for the curve.
|
||||
fn Gx() -> Self::Signed;
|
||||
/// The `y` coordinate of the base point for the curve.
|
||||
fn Gy() -> Self::Signed;
|
||||
/// Generate a point for the curve given the provided values.
|
||||
fn new_point(x: Self::Unsigned, y: Self::Unsigned) -> Self::Point;
|
||||
}
|
||||
|
||||
/// NIST curve P-192 (FIPS 186-4, page 101-102), a.k.a. secp192r1 from RFC5480
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct P192 {}
|
||||
|
||||
impl EllipticCurve for P192 {
|
||||
type Unsigned = U192;
|
||||
type Signed = I192;
|
||||
type Point = Point<P192>;
|
||||
|
||||
fn size() -> usize {
|
||||
192
|
||||
}
|
||||
|
||||
fn p() -> U192 {
|
||||
U192::from([0xffffffffffffffff, 0xfffffffffffffffe, 0xffffffffffffffff])
|
||||
}
|
||||
|
||||
fn n() -> U192 {
|
||||
U192::from([0x146bc9b1b4d22831, 0xffffffff99def836, 0xffffffffffffffff])
|
||||
}
|
||||
|
||||
fn SEED() -> U192 {
|
||||
U192::from([0xd38020eae12196d5, 0xc8422f64ed579528, 0x3045ae6f])
|
||||
}
|
||||
|
||||
fn c() -> U192 {
|
||||
U192::from([0x5f3d6fe2c745de65, 0x542dcd5fb078b6ef, 0x3099d2bbbfcb2538])
|
||||
}
|
||||
|
||||
fn a() -> U192 {
|
||||
U192::from([0xfffffffffffffffc, 0xfffffffffffffffe, 0xffffffffffffffff])
|
||||
}
|
||||
|
||||
fn b() -> U192 {
|
||||
U192::from([0xfeb8deecc146b9b1, 0x0fa7e9ab72243049, 0x64210519e59c80e7])
|
||||
}
|
||||
|
||||
fn Gx() -> I192 {
|
||||
I192::from(U192::from([0xf4ff0afd82ff1012, 0x7cbf20eb43a18800, 0x188da80eb03090f6]))
|
||||
}
|
||||
|
||||
fn Gy() -> I192 {
|
||||
I192::from(U192::from([0x73f977a11e794811, 0x631011ed6b24cdd5, 0x07192b95ffc8da78]))
|
||||
}
|
||||
|
||||
fn new_point(x: Self::Unsigned, y: Self::Unsigned) -> Self::Point {
|
||||
Point::<P192>{ x: I192::from(x), y: I192::from(y) }
|
||||
}
|
||||
}
|
||||
|
||||
/// NIST curve P-224 (FIPS 186-4, page 102), a.k.a. secp224r1 from RFC5480
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct P224 {}
|
||||
|
||||
impl EllipticCurve for P224 {
|
||||
type Unsigned = U256;
|
||||
type Signed = I256;
|
||||
type Point = Point<P224>;
|
||||
|
||||
fn size() -> usize {
|
||||
224
|
||||
}
|
||||
|
||||
fn p() -> U256 {
|
||||
U256::from_bytes(&[
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01
|
||||
])
|
||||
}
|
||||
|
||||
fn n() -> U256 {
|
||||
U256::from_bytes(&[
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x16, 0xa2,
|
||||
0xe0, 0xb8, 0xf0, 0x3e, 0x13, 0xdd, 0x29, 0x45,
|
||||
0x5c, 0x5c, 0x2a, 0x3d
|
||||
])
|
||||
}
|
||||
|
||||
fn SEED() -> U256 {
|
||||
U256::from_bytes(&[
|
||||
0xbd, 0x71, 0x34, 0x47, 0x99, 0xd5, 0xc7, 0xfc,
|
||||
0xdc, 0x45, 0xb5, 0x9f, 0xa3, 0xb9, 0xab, 0x8f,
|
||||
0x6a, 0x94, 0x8b, 0xc5
|
||||
])
|
||||
}
|
||||
|
||||
fn c() -> U256 {
|
||||
U256::from_bytes(&[
|
||||
0x5b, 0x05, 0x6c, 0x7e, 0x11, 0xdd, 0x68, 0xf4,
|
||||
0x04, 0x69, 0xee, 0x7f, 0x3c, 0x7a, 0x7d, 0x74,
|
||||
0xf7, 0xd1, 0x21, 0x11, 0x65, 0x06, 0xd0, 0x31,
|
||||
0x21, 0x82, 0x91, 0xfb
|
||||
])
|
||||
}
|
||||
|
||||
fn a() -> U256 {
|
||||
U256::from_bytes(&[
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xfe
|
||||
])
|
||||
}
|
||||
|
||||
fn b() -> U256 {
|
||||
U256::from_bytes(&[
|
||||
0xb4, 0x05, 0x0a, 0x85, 0x0c, 0x04, 0xb3, 0xab,
|
||||
0xf5, 0x41, 0x32, 0x56, 0x50, 0x44, 0xb0, 0xb7,
|
||||
0xd7, 0xbf, 0xd8, 0xba, 0x27, 0x0b, 0x39, 0x43,
|
||||
0x23, 0x55, 0xff, 0xb4
|
||||
])
|
||||
}
|
||||
|
||||
fn Gx() -> I256 {
|
||||
I256::from(U256::from_bytes(&[
|
||||
0xb7, 0x0e, 0x0c, 0xbd, 0x6b, 0xb4, 0xbf, 0x7f,
|
||||
0x32, 0x13, 0x90, 0xb9, 0x4a, 0x03, 0xc1, 0xd3,
|
||||
0x56, 0xc2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xd6,
|
||||
0x11, 0x5c, 0x1d, 0x21
|
||||
]))
|
||||
}
|
||||
|
||||
fn Gy() -> I256 {
|
||||
I256::from(U256::from_bytes(&[
|
||||
0xbd, 0x37, 0x63, 0x88, 0xb5, 0xf7, 0x23, 0xfb,
|
||||
0x4c, 0x22, 0xdf, 0xe6, 0xcd, 0x43, 0x75, 0xa0,
|
||||
0x5a, 0x07, 0x47, 0x64, 0x44, 0xd5, 0x81, 0x99,
|
||||
0x85, 0x00, 0x7e, 0x34
|
||||
]))
|
||||
}
|
||||
|
||||
fn new_point(x: Self::Unsigned, y: Self::Unsigned) -> Self::Point {
|
||||
Point::<P224>{ x: I256::from(x), y: I256::from(y) }
|
||||
}
|
||||
}
|
||||
|
||||
/// NIST curve P-256 (FIPS 186-4, page 102-103), a.k.a. secp256r1 from RFC5480
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct P256 {}
|
||||
|
||||
impl EllipticCurve for P256 {
|
||||
type Signed = I256;
|
||||
type Unsigned = U256;
|
||||
type Point = Point<P256>;
|
||||
|
||||
fn size() -> usize {
|
||||
256
|
||||
}
|
||||
|
||||
fn p() -> U256 {
|
||||
U256::from_bytes(&[
|
||||
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
])
|
||||
}
|
||||
|
||||
fn n() -> U256 {
|
||||
U256::from_bytes(&[
|
||||
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84,
|
||||
0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51
|
||||
])
|
||||
}
|
||||
|
||||
fn SEED() -> U256 {
|
||||
U256::from_bytes(&[
|
||||
0xc4, 0x9d, 0x36, 0x08, 0x86, 0xe7, 0x04, 0x93,
|
||||
0x6a, 0x66, 0x78, 0xe1, 0x13, 0x9d, 0x26, 0xb7,
|
||||
0x81, 0x9f, 0x7e, 0x90
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
fn c() -> U256 {
|
||||
U256::from_bytes(&[
|
||||
0x7e, 0xfb, 0xa1, 0x66, 0x29, 0x85, 0xbe, 0x94,
|
||||
0x03, 0xcb, 0x05, 0x5c, 0x75, 0xd4, 0xf7, 0xe0,
|
||||
0xce, 0x8d, 0x84, 0xa9, 0xc5, 0x11, 0x4a, 0xbc,
|
||||
0xaf, 0x31, 0x77, 0x68, 0x01, 0x04, 0xfa, 0x0d
|
||||
])
|
||||
}
|
||||
|
||||
fn a() -> U256 {
|
||||
U256::from_bytes(&[
|
||||
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc
|
||||
])
|
||||
}
|
||||
|
||||
fn b() -> U256 {
|
||||
U256::from_bytes(&[
|
||||
0x5a, 0xc6, 0x35, 0xd8, 0xaa, 0x3a, 0x93, 0xe7,
|
||||
0xb3, 0xeb, 0xbd, 0x55, 0x76, 0x98, 0x86, 0xbc,
|
||||
0x65, 0x1d, 0x06, 0xb0, 0xcc, 0x53, 0xb0, 0xf6,
|
||||
0x3b, 0xce, 0x3c, 0x3e, 0x27, 0xd2, 0x60, 0x4b
|
||||
])
|
||||
}
|
||||
|
||||
fn Gx() -> I256 {
|
||||
I256::from(U256::from_bytes(&[
|
||||
0x6b, 0x17, 0xd1, 0xf2, 0xe1, 0x2c, 0x42, 0x47,
|
||||
0xf8, 0xbc, 0xe6, 0xe5, 0x63, 0xa4, 0x40, 0xf2,
|
||||
0x77, 0x03, 0x7d, 0x81, 0x2d, 0xeb, 0x33, 0xa0,
|
||||
0xf4, 0xa1, 0x39, 0x45, 0xd8, 0x98, 0xc2, 0x96
|
||||
]))
|
||||
}
|
||||
|
||||
fn Gy() -> I256 {
|
||||
I256::from(U256::from_bytes(&[
|
||||
0x4f, 0xe3, 0x42, 0xe2, 0xfe, 0x1a, 0x7f, 0x9b,
|
||||
0x8e, 0xe7, 0xeb, 0x4a, 0x7c, 0x0f, 0x9e, 0x16,
|
||||
0x2b, 0xce, 0x33, 0x57, 0x6b, 0x31, 0x5e, 0xce,
|
||||
0xcb, 0xb6, 0x40, 0x68, 0x37, 0xbf, 0x51, 0xf5
|
||||
]))
|
||||
}
|
||||
|
||||
fn new_point(x: Self::Unsigned, y: Self::Unsigned) -> Self::Point {
|
||||
Point::<P256>{ x: I256::from(x), y: I256::from(y) }
|
||||
}
|
||||
}
|
||||
|
||||
/// NIST curve P-384 (FIPS 186-4, page 103-104), a.k.a. secp384r1 from RFC5480
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct P384 {}
|
||||
|
||||
impl EllipticCurve for P384 {
|
||||
type Signed = I384;
|
||||
type Unsigned = U384;
|
||||
type Point = Point<P384>;
|
||||
|
||||
fn size() -> usize {
|
||||
384
|
||||
}
|
||||
|
||||
fn p() -> U384 {
|
||||
U384::from_bytes(&[
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
|
||||
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
|
||||
])
|
||||
}
|
||||
|
||||
fn n() -> U384 {
|
||||
U384::from_bytes(&[
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf,
|
||||
0x58, 0x1a, 0x0d, 0xb2, 0x48, 0xb0, 0xa7, 0x7a,
|
||||
0xec, 0xec, 0x19, 0x6a, 0xcc, 0xc5, 0x29, 0x73
|
||||
])
|
||||
}
|
||||
|
||||
fn SEED() -> U384 {
|
||||
U384::from_bytes(&[
|
||||
0xa3, 0x35, 0x92, 0x6a, 0xa3, 0x19, 0xa2, 0x7a,
|
||||
0x1d, 0x00, 0x89, 0x6a, 0x67, 0x73, 0xa4, 0x82,
|
||||
0x7a, 0xcd, 0xac, 0x73
|
||||
])
|
||||
}
|
||||
|
||||
fn c() -> U384 {
|
||||
U384::from_bytes(&[
|
||||
0x79, 0xd1, 0xe6, 0x55, 0xf8, 0x68, 0xf0, 0x2f,
|
||||
0xff, 0x48, 0xdc, 0xde, 0xe1, 0x41, 0x51, 0xdd,
|
||||
0xb8, 0x06, 0x43, 0xc1, 0x40, 0x6d, 0x0c, 0xa1,
|
||||
0x0d, 0xfe, 0x6f, 0xc5, 0x20, 0x09, 0x54, 0x0a,
|
||||
0x49, 0x5e, 0x80, 0x42, 0xea, 0x5f, 0x74, 0x4f,
|
||||
0x6e, 0x18, 0x46, 0x67, 0xcc, 0x72, 0x24, 0x83
|
||||
])
|
||||
}
|
||||
|
||||
fn a() -> U384 {
|
||||
U384::from_bytes(&[
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
|
||||
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfc
|
||||
])
|
||||
}
|
||||
|
||||
fn b() -> U384 {
|
||||
U384::from_bytes(&[
|
||||
0xb3, 0x31, 0x2f, 0xa7, 0xe2, 0x3e, 0xe7, 0xe4,
|
||||
0x98, 0x8e, 0x05, 0x6b, 0xe3, 0xf8, 0x2d, 0x19,
|
||||
0x18, 0x1d, 0x9c, 0x6e, 0xfe, 0x81, 0x41, 0x12,
|
||||
0x03, 0x14, 0x08, 0x8f, 0x50, 0x13, 0x87, 0x5a,
|
||||
0xc6, 0x56, 0x39, 0x8d, 0x8a, 0x2e, 0xd1, 0x9d,
|
||||
0x2a, 0x85, 0xc8, 0xed, 0xd3, 0xec, 0x2a, 0xef
|
||||
])
|
||||
}
|
||||
|
||||
fn Gx() -> I384 {
|
||||
I384::from(U384::from_bytes(&[
|
||||
0xaa, 0x87, 0xca, 0x22, 0xbe, 0x8b, 0x05, 0x37,
|
||||
0x8e, 0xb1, 0xc7, 0x1e, 0xf3, 0x20, 0xad, 0x74,
|
||||
0x6e, 0x1d, 0x3b, 0x62, 0x8b, 0xa7, 0x9b, 0x98,
|
||||
0x59, 0xf7, 0x41, 0xe0, 0x82, 0x54, 0x2a, 0x38,
|
||||
0x55, 0x02, 0xf2, 0x5d, 0xbf, 0x55, 0x29, 0x6c,
|
||||
0x3a, 0x54, 0x5e, 0x38, 0x72, 0x76, 0x0a, 0xb7
|
||||
]))
|
||||
}
|
||||
|
||||
fn Gy() -> I384 {
|
||||
I384::from(U384::from_bytes(&[
|
||||
0x36, 0x17, 0xde, 0x4a, 0x96, 0x26, 0x2c, 0x6f,
|
||||
0x5d, 0x9e, 0x98, 0xbf, 0x92, 0x92, 0xdc, 0x29,
|
||||
0xf8, 0xf4, 0x1d, 0xbd, 0x28, 0x9a, 0x14, 0x7c,
|
||||
0xe9, 0xda, 0x31, 0x13, 0xb5, 0xf0, 0xb8, 0xc0,
|
||||
0x0a, 0x60, 0xb1, 0xce, 0x1d, 0x7e, 0x81, 0x9d,
|
||||
0x7a, 0x43, 0x1d, 0x7c, 0x90, 0xea, 0x0e, 0x5f
|
||||
]))
|
||||
}
|
||||
|
||||
fn new_point(x: Self::Unsigned, y: Self::Unsigned) -> Self::Point {
|
||||
Point::<P384>{ x: I384::from(x), y: I384::from(y) }
|
||||
}
|
||||
}
|
||||
|
||||
/// NIST curve P-521 (FIPS 186-4, page 104), a.k.a. secp521r1 from RFC5480
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct P521 {}
|
||||
|
||||
impl EllipticCurve for P521 {
|
||||
type Signed = I576;
|
||||
type Unsigned = U576;
|
||||
type Point = Point<P521>;
|
||||
|
||||
fn size() -> usize {
|
||||
521
|
||||
}
|
||||
|
||||
fn p() -> U576 {
|
||||
U576::from_bytes(&[
|
||||
0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff
|
||||
])
|
||||
}
|
||||
|
||||
fn n() -> U576 {
|
||||
U576::from_bytes(&[
|
||||
0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xfa, 0x51, 0x86, 0x87, 0x83, 0xbf, 0x2f,
|
||||
0x96, 0x6b, 0x7f, 0xcc, 0x01, 0x48, 0xf7, 0x09,
|
||||
0xa5, 0xd0, 0x3b, 0xb5, 0xc9, 0xb8, 0x89, 0x9c,
|
||||
0x47, 0xae, 0xbb, 0x6f, 0xb7, 0x1e, 0x91, 0x38,
|
||||
0x64, 0x09
|
||||
])
|
||||
}
|
||||
|
||||
fn SEED() -> U576 {
|
||||
U576::from_bytes(&[
|
||||
0xd0, 0x9e, 0x88, 0x00, 0x29, 0x1c, 0xb8, 0x53,
|
||||
0x96, 0xcc, 0x67, 0x17, 0x39, 0x32, 0x84, 0xaa,
|
||||
0xa0, 0xda, 0x64, 0xba
|
||||
])
|
||||
}
|
||||
|
||||
fn c() -> U576 {
|
||||
U576::from_bytes(&[
|
||||
0xb4, 0x8b, 0xfa, 0x5f, 0x42, 0x0a, 0x34, 0x94,
|
||||
0x95, 0x39, 0xd2, 0xbd, 0xfc, 0x26, 0x4e, 0xee,
|
||||
0xeb, 0x07, 0x76, 0x88, 0xe4, 0x4f, 0xbf, 0x0a,
|
||||
0xd8, 0xf6, 0xd0, 0xed, 0xb3, 0x7b, 0xd6, 0xb5,
|
||||
0x33, 0x28, 0x10, 0x00, 0x51, 0x8e, 0x19, 0xf1,
|
||||
0xb9, 0xff, 0xbe, 0x0f, 0xe9, 0xed, 0x8a, 0x3c,
|
||||
0x22, 0x00, 0xb8, 0xf8, 0x75, 0xe5, 0x23, 0x86,
|
||||
0x8c, 0x70, 0xc1, 0xe5, 0xbf, 0x55, 0xba, 0xd6,
|
||||
0x37
|
||||
])
|
||||
}
|
||||
|
||||
fn a() -> U576 {
|
||||
U576::from_bytes(&[
|
||||
0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xfc
|
||||
])
|
||||
}
|
||||
|
||||
fn b() -> U576 {
|
||||
U576::from_bytes(&[
|
||||
0x51, 0x95, 0x3e, 0xb9, 0x61, 0x8e, 0x1c, 0x9a,
|
||||
0x1f, 0x92, 0x9a, 0x21, 0xa0, 0xb6, 0x85, 0x40,
|
||||
0xee, 0xa2, 0xda, 0x72, 0x5b, 0x99, 0xb3, 0x15,
|
||||
0xf3, 0xb8, 0xb4, 0x89, 0x91, 0x8e, 0xf1, 0x09,
|
||||
0xe1, 0x56, 0x19, 0x39, 0x51, 0xec, 0x7e, 0x93,
|
||||
0x7b, 0x16, 0x52, 0xc0, 0xbd, 0x3b, 0xb1, 0xbf,
|
||||
0x07, 0x35, 0x73, 0xdf, 0x88, 0x3d, 0x2c, 0x34,
|
||||
0xf1, 0xef, 0x45, 0x1f, 0xd4, 0x6b, 0x50, 0x3f,
|
||||
0x00
|
||||
])
|
||||
}
|
||||
|
||||
fn Gx() -> I576 {
|
||||
I576::from(U576::from_bytes(&[
|
||||
0x00, 0xc6, 0x85, 0x8e, 0x06, 0xb7, 0x04, 0x04,
|
||||
0xe9, 0xcd, 0x9e, 0x3e, 0xcb, 0x66, 0x23, 0x95,
|
||||
0xb4, 0x42, 0x9c, 0x64, 0x81, 0x39, 0x05, 0x3f,
|
||||
0xb5, 0x21, 0xf8, 0x28, 0xaf, 0x60, 0x6b, 0x4d,
|
||||
0x3d, 0xba, 0xa1, 0x4b, 0x5e, 0x77, 0xef, 0xe7,
|
||||
0x59, 0x28, 0xfe, 0x1d, 0xc1, 0x27, 0xa2, 0xff,
|
||||
0xa8, 0xde, 0x33, 0x48, 0xb3, 0xc1, 0x85, 0x6a,
|
||||
0x42, 0x9b, 0xf9, 0x7e, 0x7e, 0x31, 0xc2, 0xe5,
|
||||
0xbd, 0x66
|
||||
]))
|
||||
}
|
||||
|
||||
fn Gy() -> I576 {
|
||||
I576::from(U576::from_bytes(&[
|
||||
0x01, 0x18, 0x39, 0x29, 0x6a, 0x78, 0x9a, 0x3b,
|
||||
0xc0, 0x04, 0x5c, 0x8a, 0x5f, 0xb4, 0x2c, 0x7d,
|
||||
0x1b, 0xd9, 0x98, 0xf5, 0x44, 0x49, 0x57, 0x9b,
|
||||
0x44, 0x68, 0x17, 0xaf, 0xbd, 0x17, 0x27, 0x3e,
|
||||
0x66, 0x2c, 0x97, 0xee, 0x72, 0x99, 0x5e, 0xf4,
|
||||
0x26, 0x40, 0xc5, 0x50, 0xb9, 0x01, 0x3f, 0xad,
|
||||
0x07, 0x61, 0x35, 0x3c, 0x70, 0x86, 0xa2, 0x72,
|
||||
0xc2, 0x40, 0x88, 0xbe, 0x94, 0x76, 0x9f, 0xd1,
|
||||
0x66, 0x50
|
||||
]))
|
||||
}
|
||||
|
||||
fn new_point(x: Self::Unsigned, y: Self::Unsigned) -> Self::Point {
|
||||
Point::<P521>{ x: I576::from(x), y: I576::from(y) }
|
||||
}
|
||||
}
|
||||
121
src/ecdsa/mod.rs
Normal file
121
src/ecdsa/mod.rs
Normal file
@@ -0,0 +1,121 @@
|
||||
//! The generation and use of ECDSA keys is pretty straightforward, compared
|
||||
//! to DSA and RSA. You should be able to find what you want to do in the
|
||||
//! following code snippet, as an example:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use simple_crypto::ecdsa::{ECDSAKeyPair,P384};
|
||||
//! use simple_crypto::sha::SHA256;
|
||||
//!
|
||||
//! // Generate a new ECDSA key for curve P384 (this is a good choice, by
|
||||
//! // the way, if you're wondering which curve to use).
|
||||
//! let mut rng = rand::rngs::OsRng::new().unwrap();
|
||||
//! let kp = ECDSAKeyPair::<P384>::generate(&mut rng);
|
||||
//!
|
||||
//! // Now that you have this key pair, you can sign and verify messages
|
||||
//! // using it. For example, to sign the vector [0,1,2,3,4] with SHA256
|
||||
//! // and then verify that signature, we would write:
|
||||
//! let msg = vec![0,1,2,3,4];
|
||||
//! let sig = kp.private.sign::<SHA256>(&msg);
|
||||
//! assert!( kp.public.verify::<SHA256>(&msg, &sig) );
|
||||
//! ```
|
||||
|
||||
mod curve;
|
||||
pub(crate) mod point;
|
||||
mod private;
|
||||
mod public;
|
||||
|
||||
use cryptonum::signed::{I192,I256,I384,I576};
|
||||
use cryptonum::unsigned::{CryptoNum,Decoder};
|
||||
use cryptonum::unsigned::{U192,U256,U384,U576};
|
||||
use rand::Rng;
|
||||
use rand::distributions::Standard;
|
||||
pub use self::curve::{EllipticCurve,P192,P224,P256,P384,P521};
|
||||
use self::point::{ECCPoint,Point};
|
||||
pub use self::private::{ECDSAPrivate,ECCPrivateKey};
|
||||
pub use self::public::{ECDSAPublic,ECCPublicKey};
|
||||
pub use self::public::{ECDSADecodeErr,ECDSAEncodeErr};
|
||||
use super::KeyPair;
|
||||
|
||||
/// An ECDSA key pair for the given curve.
|
||||
pub struct ECDSAKeyPair<Curve: EllipticCurve> {
|
||||
pub public: ECCPublicKey<Curve>,
|
||||
pub private: ECCPrivateKey<Curve>
|
||||
}
|
||||
|
||||
/// A generic ECDSA key pair that implements one of our known curves, for cases
|
||||
/// when you're not sure which one you're going to have.
|
||||
pub enum ECDSAPair {
|
||||
P192(ECCPublicKey<P192>,ECCPrivateKey<P192>),
|
||||
P224(ECCPublicKey<P224>,ECCPrivateKey<P224>),
|
||||
P256(ECCPublicKey<P256>,ECCPrivateKey<P256>),
|
||||
P384(ECCPublicKey<P384>,ECCPrivateKey<P384>),
|
||||
P521(ECCPublicKey<P521>,ECCPrivateKey<P521>),
|
||||
}
|
||||
|
||||
impl KeyPair for ECDSAPair {
|
||||
type Public = ECDSAPublic;
|
||||
type Private = ECDSAPrivate;
|
||||
|
||||
fn new(pu: ECDSAPublic, pr: ECDSAPrivate) -> ECDSAPair
|
||||
{
|
||||
match (pu, pr) {
|
||||
(ECDSAPublic::P192(pbl),ECDSAPrivate::P192(prv)) => ECDSAPair::P192(pbl,prv),
|
||||
(ECDSAPublic::P224(pbl),ECDSAPrivate::P224(prv)) => ECDSAPair::P224(pbl,prv),
|
||||
(ECDSAPublic::P256(pbl),ECDSAPrivate::P256(prv)) => ECDSAPair::P256(pbl,prv),
|
||||
(ECDSAPublic::P384(pbl),ECDSAPrivate::P384(prv)) => ECDSAPair::P384(pbl,prv),
|
||||
(ECDSAPublic::P521(pbl),ECDSAPrivate::P521(prv)) => ECDSAPair::P521(pbl,prv),
|
||||
_ =>
|
||||
panic!("Non-matching public/private pairs in ECDSAPair::new()")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! generate_impl {
|
||||
($curve: ident, $un: ident, $si: ident) => {
|
||||
impl KeyPair for ECDSAKeyPair<$curve> {
|
||||
type Public = ECCPublicKey<$curve>;
|
||||
type Private = ECCPrivateKey<$curve>;
|
||||
|
||||
fn new(public: ECCPublicKey<$curve>, private: ECCPrivateKey<$curve>) -> ECDSAKeyPair<$curve>
|
||||
{
|
||||
ECDSAKeyPair{ public, private }
|
||||
}
|
||||
}
|
||||
|
||||
impl ECDSAKeyPair<$curve> {
|
||||
/// Generate a fresh ECDSA key pair for this curve, given the
|
||||
/// provided random number generator. THIS MUST BE A CRYPTO
|
||||
/// STRONG RNG. If it's not, then you're going to generate weak
|
||||
/// keys and the crypto gremlins will get you.
|
||||
pub fn generate<G: Rng>(rng: &mut G) -> ECDSAKeyPair<$curve>
|
||||
{
|
||||
loop {
|
||||
let size = ($curve::size() + 7) / 8;
|
||||
let random_bytes: Vec<u8> = rng.sample_iter(&Standard).take(size).collect();
|
||||
let proposed_d = $un::from_bytes(&random_bytes);
|
||||
|
||||
if proposed_d.is_zero() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if proposed_d >= $curve::n() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let d = $si::from(&proposed_d);
|
||||
let public_point = Point::<$curve>::default().scale(&d);
|
||||
let public = ECCPublicKey::<$curve>::new(public_point);
|
||||
let private = ECCPrivateKey::<$curve>::new(proposed_d);
|
||||
|
||||
return ECDSAKeyPair{ public, private };
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
generate_impl!(P192, U192, I192);
|
||||
generate_impl!(P224, U256, I256);
|
||||
generate_impl!(P256, U256, I256);
|
||||
generate_impl!(P384, U384, I384);
|
||||
generate_impl!(P521, U576, I576);
|
||||
329
src/ecdsa/point.rs
Normal file
329
src/ecdsa/point.rs
Normal file
@@ -0,0 +1,329 @@
|
||||
use cryptonum::signed::*;
|
||||
use cryptonum::unsigned::*;
|
||||
use ecdsa::curve::*;
|
||||
|
||||
pub trait ECCPoint : Sized {
|
||||
type Curve: EllipticCurve;
|
||||
type Scale;
|
||||
|
||||
fn default() -> Self;
|
||||
fn negate(&self) -> Self;
|
||||
fn double(&self) -> Self;
|
||||
fn add(&self, other: &Self) -> Self;
|
||||
fn scale(&self, amt: &Self::Scale) -> Self;
|
||||
fn double_scalar_mult(x1: &Self::Scale, p1: &Self, x2: &Self::Scale, p2: &Self) -> Self
|
||||
{
|
||||
// FIXME: Replace this with something not stupid.
|
||||
let big1 = p1.scale(x1);
|
||||
let big2 = p2.scale(x2);
|
||||
big1.add(&big2)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Point<T: EllipticCurve>
|
||||
{
|
||||
pub x: T::Signed,
|
||||
pub y: T::Signed
|
||||
}
|
||||
|
||||
macro_rules! point_impl
|
||||
{
|
||||
($curve: ident, $base: ident,
|
||||
$s2: ident, $u2: ident,
|
||||
$s2p1: ident, $u2p1: ident) =>
|
||||
{
|
||||
impl Clone for Point<$curve> {
|
||||
fn clone(&self) -> Point<$curve> {
|
||||
Point {
|
||||
x: self.x.clone(),
|
||||
y: self.y.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ECCPoint for Point<$curve> {
|
||||
type Curve = $curve;
|
||||
type Scale = $base;
|
||||
|
||||
fn default() -> Point<$curve>
|
||||
{
|
||||
Point {
|
||||
x: $curve::Gx(),
|
||||
y: $curve::Gy()
|
||||
}
|
||||
}
|
||||
|
||||
fn negate(&self) -> Point<$curve>
|
||||
{
|
||||
let mut newy = $base::new(false, $curve::p());
|
||||
newy -= &self.y;
|
||||
Point{ x: self.x.clone(), y: newy }
|
||||
}
|
||||
|
||||
fn double(&self) -> Point<$curve>
|
||||
{
|
||||
let up = $curve::p();
|
||||
let bigp = $s2::new(false, $u2::from(&up));
|
||||
// lambda = (3 * xp ^ 2 + a) / 2 yp
|
||||
let xsquared = self.x.square();
|
||||
let mut lambda_top = &xsquared * 3u64;
|
||||
lambda_top += $s2p1::new(false, $u2p1::from($curve::a()));
|
||||
let mut lambda_bot = $s2p1::from(&self.y);
|
||||
lambda_bot <<= 1;
|
||||
let lambda = $base::from(lambda_top.moddiv(&lambda_bot, &$s2p1::from(&bigp)));
|
||||
// xr = lambda^2 - 2 xp
|
||||
let mut xr = lambda.square();
|
||||
let mut xr_right = $s2::from(&self.x);
|
||||
xr_right <<= 1;
|
||||
xr -= xr_right;
|
||||
xr %= &bigp;
|
||||
let x = $base::from(xr);
|
||||
// yr = lambda (xp - xr) - yp
|
||||
let xdiff = $base::from(&self.x - &x);
|
||||
let mut yr = &lambda * &xdiff;
|
||||
yr -= $s2::from(&self.y);
|
||||
let y = $base::from(&yr % &bigp);
|
||||
//
|
||||
Point{ x, y }
|
||||
}
|
||||
|
||||
fn add(&self, other: &Point<$curve>) -> Point<$curve>
|
||||
{
|
||||
let mut xdiff = self.x.clone(); xdiff -= &other.x;
|
||||
let mut ydiff = self.y.clone(); ydiff -= &other.y;
|
||||
let signedp = $base::new(false, $curve::p());
|
||||
let s = ydiff.moddiv(&xdiff, &signedp);
|
||||
let mut xr = &s * &s;
|
||||
xr -= $s2::from(&self.x);
|
||||
xr -= $s2::from(&other.x);
|
||||
let bigsignedp = $s2::from(&signedp);
|
||||
xr %= &bigsignedp;
|
||||
let mut yr = $s2::from(&self.x);
|
||||
yr -= &xr;
|
||||
yr *= $s2::from(&s);
|
||||
yr -= $s2::from(&self.y);
|
||||
yr %= &bigsignedp;
|
||||
Point{ x: $base::from(xr), y: $base::from(yr) }
|
||||
}
|
||||
|
||||
fn scale(&self, d: &$base) -> Point<$curve>
|
||||
{
|
||||
assert!(!d.is_zero());
|
||||
#[allow(non_snake_case)]
|
||||
let mut Q: Point<$curve> = self.clone();
|
||||
let mut bit = ($base::bit_length() - 1) as isize;
|
||||
|
||||
// Skip down until we hit a set bit
|
||||
while !d.testbit(bit as usize) {
|
||||
bit -= 1;
|
||||
}
|
||||
// drop one
|
||||
bit -= 1;
|
||||
// do the double and add algorithm
|
||||
while bit >= 0 {
|
||||
Q = Q.double();
|
||||
|
||||
let test = d.testbit(bit as usize);
|
||||
if test {
|
||||
Q = Q.add(&self);
|
||||
}
|
||||
|
||||
bit -= 1;
|
||||
}
|
||||
|
||||
if d.is_negative() {
|
||||
Q.negate()
|
||||
} else {
|
||||
Q
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
point_impl!(P192, I192, I384, U384, I448, U448);
|
||||
point_impl!(P224, I256, I512, U512, I576, U576);
|
||||
point_impl!(P256, I256, I512, U512, I576, U576);
|
||||
point_impl!(P384, I384, I768, U768, I832, U832);
|
||||
point_impl!(P521, I576, I1152, U1152, I1216, U1216);
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! negate_test_body
|
||||
{
|
||||
($curve: ident, $lcurve: ident, $stype: ident, $utype: ident) => {
|
||||
let fname = build_test_path("ecc/negate",stringify!($curve));
|
||||
run_test(fname.to_string(), 4, |case| {
|
||||
let (negx, xbytes) = case.get("x").unwrap();
|
||||
let (negy, ybytes) = case.get("y").unwrap();
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
|
||||
let x = $stype::new(*negx, $utype::from_bytes(xbytes));
|
||||
let y = $stype::new(*negy, $utype::from_bytes(ybytes));
|
||||
let a = $stype::new(*nega, $utype::from_bytes(abytes));
|
||||
let b = $stype::new(*negb, $utype::from_bytes(bbytes));
|
||||
let point = Point::<$curve>{ x, y };
|
||||
let dbl = point.negate();
|
||||
assert_eq!(a, dbl.x, "x equivalence");
|
||||
assert_eq!(b, dbl.y, "y equivalence");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! double_test_body
|
||||
{
|
||||
($curve: ident, $lcurve: ident, $stype: ident, $utype: ident) => {
|
||||
let fname = build_test_path("ecc/double",stringify!($curve));
|
||||
run_test(fname.to_string(), 4, |case| {
|
||||
let (negx, xbytes) = case.get("x").unwrap();
|
||||
let (negy, ybytes) = case.get("y").unwrap();
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
|
||||
let x = $stype::new(*negx, $utype::from_bytes(xbytes));
|
||||
let y = $stype::new(*negy, $utype::from_bytes(ybytes));
|
||||
let a = $stype::new(*nega, $utype::from_bytes(abytes));
|
||||
let b = $stype::new(*negb, $utype::from_bytes(bbytes));
|
||||
let point = Point::<$curve>{ x, y };
|
||||
let dbl = point.double();
|
||||
assert_eq!(a, dbl.x, "x equivalence");
|
||||
assert_eq!(b, dbl.y, "y equivalence");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! add_test_body
|
||||
{
|
||||
($curve: ident, $lcurve: ident, $stype: ident, $utype: ident) => {
|
||||
let fname = build_test_path("ecc/add",stringify!($curve));
|
||||
run_test(fname.to_string(), 6, move |case| {
|
||||
let (negx, xbytes) = case.get("x").unwrap();
|
||||
let (negy, ybytes) = case.get("y").unwrap();
|
||||
let (negu, ubytes) = case.get("u").unwrap();
|
||||
let (negv, vbytes) = case.get("v").unwrap();
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
|
||||
let x = $stype::new(*negx, $utype::from_bytes(xbytes));
|
||||
let y = $stype::new(*negy, $utype::from_bytes(ybytes));
|
||||
let u = $stype::new(*negu, $utype::from_bytes(ubytes));
|
||||
let v = $stype::new(*negv, $utype::from_bytes(vbytes));
|
||||
let a = $stype::new(*nega, $utype::from_bytes(abytes));
|
||||
let b = $stype::new(*negb, $utype::from_bytes(bbytes));
|
||||
let point1 = Point::<$curve>{ x: x, y: y };
|
||||
let point2 = Point::<$curve>{ x: u, y: v };
|
||||
let res = point1.add(&point2);
|
||||
assert_eq!(a, res.x, "x equivalence");
|
||||
assert_eq!(b, res.y, "y equivalence");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! scale_test_body
|
||||
{
|
||||
($curve: ident, $lcurve: ident, $stype: ident, $utype: ident) => {
|
||||
let fname = build_test_path("ecc/scale",stringify!($curve));
|
||||
run_test(fname.to_string(), 5, |case| {
|
||||
let (negx, xbytes) = case.get("x").unwrap();
|
||||
let (negy, ybytes) = case.get("y").unwrap();
|
||||
let (negk, kbytes) = case.get("k").unwrap();
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
|
||||
let x = $stype::new(*negx, $utype::from_bytes(xbytes));
|
||||
let y = $stype::new(*negy, $utype::from_bytes(ybytes));
|
||||
let k = $stype::new(*negk, $utype::from_bytes(kbytes));
|
||||
let a = $stype::new(*nega, $utype::from_bytes(abytes));
|
||||
let b = $stype::new(*negb, $utype::from_bytes(bbytes));
|
||||
let point = Point::<$curve>{ x: x, y: y };
|
||||
let res = point.scale(&k);
|
||||
assert_eq!(a, res.x, "x equivalence");
|
||||
assert_eq!(b, res.y, "y equivalence");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! add_scale2_test_body
|
||||
{
|
||||
($curve: ident, $lcurve: ident, $stype: ident, $utype: ident) => {
|
||||
let fname = build_test_path("ecc/add_scale2",stringify!($curve));
|
||||
run_test(fname.to_string(), 8, |case| {
|
||||
let (negx, xbytes) = case.get("x").unwrap();
|
||||
let (negy, ybytes) = case.get("y").unwrap();
|
||||
let (negp, pbytes) = case.get("p").unwrap();
|
||||
let (negq, qbytes) = case.get("q").unwrap();
|
||||
let (negn, nbytes) = case.get("n").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negr, rbytes) = case.get("r").unwrap();
|
||||
let (negs, sbytes) = case.get("s").unwrap();
|
||||
|
||||
let x = $stype::new(*negx, $utype::from_bytes(xbytes));
|
||||
let y = $stype::new(*negy, $utype::from_bytes(ybytes));
|
||||
let p = $stype::new(*negp, $utype::from_bytes(pbytes));
|
||||
let q = $stype::new(*negq, $utype::from_bytes(qbytes));
|
||||
let n = $stype::new(*negn, $utype::from_bytes(nbytes));
|
||||
let m = $stype::new(*negm, $utype::from_bytes(mbytes));
|
||||
let r = $stype::new(*negr, $utype::from_bytes(rbytes));
|
||||
let s = $stype::new(*negs, $utype::from_bytes(sbytes));
|
||||
let p1 = Point::<$curve>{ x: x, y: y };
|
||||
let p2 = Point::<$curve>{ x: p, y: q };
|
||||
let res = Point::<$curve>::double_scalar_mult(&n, &p1, &m, &p2);
|
||||
assert_eq!(r, res.x, "x equivalence");
|
||||
assert_eq!(s, res.y, "y equivalence");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! point_tests
|
||||
{
|
||||
($curve: ident, $lcurve: ident, $stype: ident, $utype: ident) => {
|
||||
#[cfg(test)]
|
||||
mod $lcurve {
|
||||
use super::*;
|
||||
use testing::*;
|
||||
|
||||
#[test]
|
||||
fn negate() { negate_test_body!($curve, $lcurve, $stype, $utype); }
|
||||
#[test]
|
||||
fn double() { double_test_body!($curve, $lcurve, $stype, $utype); }
|
||||
#[test]
|
||||
fn add() { add_test_body!($curve, $lcurve, $stype, $utype); }
|
||||
#[test]
|
||||
fn scale() { scale_test_body!($curve, $lcurve, $stype, $utype); }
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn add_scale2() { add_scale2_test_body!($curve, $lcurve, $stype, $utype); }
|
||||
}
|
||||
};
|
||||
(ignore_expensive $curve: ident, $lcurve: ident, $stype: ident, $utype: ident) => {
|
||||
#[cfg(test)]
|
||||
mod $lcurve {
|
||||
use super::*;
|
||||
use testing::*;
|
||||
|
||||
#[test]
|
||||
fn negate() { negate_test_body!($curve, $lcurve, $stype, $utype); }
|
||||
#[test]
|
||||
fn double() { double_test_body!($curve, $lcurve, $stype, $utype); }
|
||||
#[test]
|
||||
fn add() { add_test_body!($curve, $lcurve, $stype, $utype); }
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn scale() { scale_test_body!($curve, $lcurve, $stype, $utype); }
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn add_scale2() { add_scale2_test_body!($curve, $lcurve, $stype, $utype); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
point_tests!(P192, p192, I192, U192);
|
||||
point_tests!(ignore_expensive P224, p224, I256, U256);
|
||||
point_tests!(ignore_expensive P256, p256, I256, U256);
|
||||
point_tests!(ignore_expensive P384, p384, I384, U384);
|
||||
point_tests!(ignore_expensive P521, p521, I576, U576);
|
||||
179
src/ecdsa/private.rs
Normal file
179
src/ecdsa/private.rs
Normal file
@@ -0,0 +1,179 @@
|
||||
use cryptonum::signed::*;
|
||||
use cryptonum::unsigned::*;
|
||||
use dsa::rfc6979::{DSASignature,KIterator,bits2int};
|
||||
use ecdsa::curve::{EllipticCurve,P192,P224,P256,P384,P521};
|
||||
use ecdsa::point::{ECCPoint,Point};
|
||||
use sha::Hash;
|
||||
use std::fmt;
|
||||
|
||||
/// A private key for the given curve.
|
||||
#[derive(PartialEq)]
|
||||
pub struct ECCPrivateKey<Curve: EllipticCurve> {
|
||||
pub(crate) d: Curve::Unsigned
|
||||
}
|
||||
|
||||
impl<Curve: EllipticCurve> fmt::Debug for ECCPrivateKey<Curve> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(),fmt::Error>
|
||||
{
|
||||
f.write_str("<ECCPrivateKey>")
|
||||
}
|
||||
}
|
||||
|
||||
/// A generic private key.
|
||||
pub enum ECDSAPrivate {
|
||||
P192(ECCPrivateKey<P192>),
|
||||
P224(ECCPrivateKey<P224>),
|
||||
P256(ECCPrivateKey<P256>),
|
||||
P384(ECCPrivateKey<P384>),
|
||||
P521(ECCPrivateKey<P521>),
|
||||
}
|
||||
|
||||
macro_rules! generate_privates
|
||||
{
|
||||
($curve: ident, $base: ident, $sig: ident, $dbl: ident, $quad: ident) => {
|
||||
impl ECCPrivateKey<$curve>
|
||||
{
|
||||
/// Generate a new private key using the given private scalar.
|
||||
pub fn new(d: $base) -> ECCPrivateKey<$curve>
|
||||
{
|
||||
ECCPrivateKey{ d }
|
||||
}
|
||||
|
||||
/// Sign the given message with the current key, using the hash provided
|
||||
/// in the type.
|
||||
pub fn sign<H: Hash + Clone>(&self, m: &[u8]) -> DSASignature<$base>
|
||||
{
|
||||
// This algorithm is per RFC 6979, which has a nice, relatively
|
||||
// straightforward description of how to do DSA signing.
|
||||
//
|
||||
// 1. H(m) is transformed into an integer modulo q using the bits2int
|
||||
// transform and an extra modular reduction:
|
||||
//
|
||||
// h = bits2int(H(m)) mod q
|
||||
//
|
||||
// As was noted in the description of bits2octets, the extra
|
||||
// modular reduction is no more than a conditional subtraction.
|
||||
//
|
||||
let h1 = <H>::hash(m);
|
||||
let size = <$curve>::size();
|
||||
let h0: $base = bits2int(&h1, size);
|
||||
let n = <$curve>::n();
|
||||
let h = h0 % &n;
|
||||
|
||||
// 2. A random value modulo q, dubbed k, is generated. That value
|
||||
// shall not be 0; hence, it lies in the [1, q-1] range. Most
|
||||
// of the remainder of this document will revolve around the
|
||||
// process used to generate k. In plain DSA or ECDSA, k should
|
||||
// be selected through a random selection that chooses a value
|
||||
// among the q-1 possible values with uniform probability.
|
||||
for k in KIterator::<H,$base>::new(&h1, size, &n, &self.d) {
|
||||
// 3. A value r (modulo q) is computed from k and the key
|
||||
// parameters:
|
||||
// * For DSA ...
|
||||
// * For ECDSA ...
|
||||
//
|
||||
// If r turns out to be zero, a new k should be selected and r
|
||||
// computed again (this is an utterly improbable occurrence).
|
||||
let g = Point::<$curve>::default();
|
||||
let ki = $sig::new(false, k.clone());
|
||||
let kg = g.scale(&ki);
|
||||
let ni = $sig::from(&n);
|
||||
let ri = &kg.x % ∋
|
||||
if ri.is_zero() {
|
||||
continue;
|
||||
}
|
||||
if ri.is_negative() {
|
||||
continue;
|
||||
}
|
||||
let r = $base::from(ri);
|
||||
// 4. The value s (modulo q) is computed:
|
||||
//
|
||||
// s = (h+x*r)/k mod q
|
||||
//
|
||||
// The pair (r, s) is the signature.
|
||||
if let Some(kinv) = k.modinv(&n) {
|
||||
let mut hxr = &self.d * &r;
|
||||
hxr += $dbl::from(&h);
|
||||
let base = hxr * $dbl::from(kinv);
|
||||
let s = $base::from(base % $quad::from(n));
|
||||
return DSASignature{ r, s };
|
||||
}
|
||||
}
|
||||
panic!("The world is broken; couldn't find a k in sign().");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_privates!(P192, U192, I192, U384, U768);
|
||||
generate_privates!(P224, U256, I256, U512, U1024);
|
||||
generate_privates!(P256, U256, I256, U512, U1024);
|
||||
generate_privates!(P384, U384, I384, U768, U1536);
|
||||
generate_privates!(P521, U576, I576, U1152, U2304);
|
||||
|
||||
/************* TESTING ********************************************************/
|
||||
|
||||
#[cfg(test)]
|
||||
use sha::{SHA224,SHA256,SHA384,SHA512};
|
||||
#[cfg(test)]
|
||||
use testing::*;
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! sign_test_body
|
||||
{
|
||||
($name: ident, $curve: ident, $base: ident) => {
|
||||
let fname = build_test_path("ecc/sign",stringify!($curve));
|
||||
run_test(fname.to_string(), 9, |case| {
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
let (negk, _bytes) = case.get("k").unwrap();
|
||||
let (negx, xbytes) = case.get("x").unwrap();
|
||||
let (negy, ybytes) = case.get("y").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negh, hbytes) = case.get("h").unwrap();
|
||||
let (negr, rbytes) = case.get("r").unwrap();
|
||||
let (negs, sbytes) = case.get("s").unwrap();
|
||||
|
||||
assert!(!negd && !negk && !negx && !negy &&
|
||||
!negm && !negh && !negr && !negs);
|
||||
let d = $base::from_bytes(dbytes);
|
||||
let _ = $base::from_bytes(xbytes);
|
||||
let _ = $base::from_bytes(ybytes);
|
||||
let h = $base::from_bytes(hbytes);
|
||||
let r = $base::from_bytes(rbytes);
|
||||
let s = $base::from_bytes(sbytes);
|
||||
|
||||
let private = ECCPrivateKey::<$curve>::new(d);
|
||||
let sig = match usize::from(h) {
|
||||
224 => private.sign::<SHA224>(mbytes),
|
||||
256 => private.sign::<SHA256>(mbytes),
|
||||
384 => private.sign::<SHA384>(mbytes),
|
||||
512 => private.sign::<SHA512>(mbytes),
|
||||
x => panic!("Unknown hash algorithm {}", x)
|
||||
};
|
||||
assert_eq!(r, sig.r, "r signature check");
|
||||
assert_eq!(s, sig.s, "s signature check");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! generate_tests {
|
||||
($name: ident, $curve: ident, $base: ident) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
sign_test_body!($name, $curve, $base);
|
||||
}
|
||||
};
|
||||
(ignore $name: ident, $curve: ident, $base: ident) => {
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn $name() {
|
||||
sign_test_body!($name, $curve, $base);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
generate_tests!(p192_sign, P192, U192);
|
||||
generate_tests!(ignore p224_sign, P224, U256);
|
||||
generate_tests!(ignore p256_sign, P256, U256);
|
||||
generate_tests!(ignore p384_sign, P384, U384);
|
||||
generate_tests!(ignore p521_sign, P521, U576);
|
||||
230
src/ecdsa/public.rs
Normal file
230
src/ecdsa/public.rs
Normal file
@@ -0,0 +1,230 @@
|
||||
use cryptonum::signed::*;
|
||||
use cryptonum::unsigned::*;
|
||||
use dsa::rfc6979::DSASignature;
|
||||
use ecdsa::curve::{EllipticCurve,P192,P224,P256,P384,P521};
|
||||
use ecdsa::point::{ECCPoint,Point};
|
||||
use sha::Hash;
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1DecodeErr,ASN1EncodeErr,FromASN1,ToASN1};
|
||||
use std::cmp::min;
|
||||
|
||||
/// An ECDSA public key for the given curve.
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct ECCPublicKey<Curve: EllipticCurve> {
|
||||
pub(crate) q: Point<Curve>
|
||||
}
|
||||
|
||||
/// A generic ECDSA public key, when you're not sure which curve you're
|
||||
/// going to get.
|
||||
pub enum ECDSAPublic {
|
||||
P192(ECCPublicKey<P192>),
|
||||
P224(ECCPublicKey<P224>),
|
||||
P256(ECCPublicKey<P256>),
|
||||
P384(ECCPublicKey<P384>),
|
||||
P521(ECCPublicKey<P521>),
|
||||
}
|
||||
|
||||
/// An error that can occur when encoding an ECDSA public key as an ASN.1
|
||||
/// object.
|
||||
pub enum ECDSAEncodeErr {
|
||||
ASN1EncodeErr(ASN1EncodeErr),
|
||||
XValueNegative, YValueNegative
|
||||
}
|
||||
|
||||
impl From<ASN1EncodeErr> for ECDSAEncodeErr {
|
||||
fn from(x: ASN1EncodeErr) -> ECDSAEncodeErr {
|
||||
ECDSAEncodeErr::ASN1EncodeErr(x)
|
||||
}
|
||||
}
|
||||
|
||||
/// An error that can occur when decoding an ECDSA public key from an
|
||||
/// ASN.1 blob.
|
||||
#[derive(Debug)]
|
||||
pub enum ECDSADecodeErr {
|
||||
ASN1DecodeErr(ASN1DecodeErr),
|
||||
NoKeyFound,
|
||||
InvalidKeyFormat,
|
||||
InvalidKeyBlockSize
|
||||
}
|
||||
|
||||
impl From<ASN1DecodeErr> for ECDSADecodeErr {
|
||||
fn from(x: ASN1DecodeErr) -> ECDSADecodeErr {
|
||||
ECDSADecodeErr::ASN1DecodeErr(x)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! public_impl {
|
||||
($curve: ident, $un: ident, $si: ident) => {
|
||||
impl ECCPublicKey<$curve>
|
||||
{
|
||||
/// Generate a new public key object from the given public point.
|
||||
pub fn new(q: Point<$curve>) -> ECCPublicKey<$curve>
|
||||
{
|
||||
ECCPublicKey{ q }
|
||||
}
|
||||
|
||||
/// Returns true if the given message matches the given signature,
|
||||
/// assuming the provided hash function.
|
||||
pub fn verify<H: Hash>(&self, m: &[u8], sig: &DSASignature<$un>) -> bool
|
||||
{
|
||||
let n = <$curve>::n();
|
||||
|
||||
if sig.r.is_zero() || (sig.r >= n) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if sig.s.is_zero() || (sig.s >= n) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// e = the leftmost min(N, outlen) bits of Hash(M').
|
||||
let mut digest_bytes = <H>::hash(m);
|
||||
let len = min(digest_bytes.len(), $curve::size() / 8);
|
||||
digest_bytes.truncate(len);
|
||||
|
||||
if let Some(c) = sig.s.modinv(&n) {
|
||||
let e = $un::from_bytes(&digest_bytes);
|
||||
let u1 = e.modmul(&c, &n);
|
||||
let u2 = sig.r.modmul(&c, &n);
|
||||
let g = Point::<$curve>::default();
|
||||
let u1i = $si::from(u1);
|
||||
let u2i = $si::from(u2);
|
||||
let point = Point::<$curve>::double_scalar_mult(&u1i, &g, &u2i, &self.q);
|
||||
!point.x.is_negative() && (sig.r == $un::from(point.x))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToASN1 for ECCPublicKey<$curve> {
|
||||
type Error = ECDSAEncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class) -> Result<Vec<ASN1Block>,ECDSAEncodeErr>
|
||||
{
|
||||
if self.q.x.is_negative() {
|
||||
return Err(ECDSAEncodeErr::XValueNegative);
|
||||
}
|
||||
if self.q.y.is_negative() {
|
||||
return Err(ECDSAEncodeErr::YValueNegative);
|
||||
}
|
||||
|
||||
let xval = $un::from(&self.q.x);
|
||||
let yval = $un::from(&self.q.y);
|
||||
let mut xbytes = xval.to_bytes();
|
||||
let mut ybytes = yval.to_bytes();
|
||||
let goalsize = ($curve::size() + 7) / 8;
|
||||
let mut target = Vec::with_capacity(1 + (goalsize * 2));
|
||||
|
||||
while xbytes.len() > goalsize { xbytes.remove(0); };
|
||||
while xbytes.len() < goalsize { xbytes.insert(0,0) };
|
||||
while ybytes.len() > goalsize { ybytes.remove(0); };
|
||||
while ybytes.len() < goalsize { ybytes.insert(0,0) };
|
||||
|
||||
target.push(4);
|
||||
target.append(&mut xbytes);
|
||||
target.append(&mut ybytes);
|
||||
|
||||
let result = ASN1Block::BitString(c, 0, target.len() * 8, target);
|
||||
Ok(vec![result])
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for ECCPublicKey<$curve> {
|
||||
type Error = ECDSADecodeErr;
|
||||
|
||||
fn from_asn1(bs: &[ASN1Block]) -> Result<(ECCPublicKey<$curve>,&[ASN1Block]),ECDSADecodeErr>
|
||||
{
|
||||
let (x, rest) = bs.split_first().ok_or(ECDSADecodeErr::NoKeyFound)?;
|
||||
if let ASN1Block::BitString(_, _, _, target) = x {
|
||||
let (hdr, xy_bstr) = target.split_first().ok_or(ECDSADecodeErr::InvalidKeyFormat)?;
|
||||
if *hdr != 4 {
|
||||
return Err(ECDSADecodeErr::InvalidKeyFormat);
|
||||
}
|
||||
let goalsize = ($curve::size() + 7) / 8;
|
||||
if xy_bstr.len() != (2 * goalsize) {
|
||||
return Err(ECDSADecodeErr::InvalidKeyBlockSize);
|
||||
}
|
||||
let (xbstr, ybstr) = xy_bstr.split_at(goalsize);
|
||||
let x = $un::from_bytes(xbstr);
|
||||
let y = $un::from_bytes(ybstr);
|
||||
let point = Point::<$curve>{ x: $si::from(x), y: $si::from(y) };
|
||||
let res = ECCPublicKey::<$curve>::new(point);
|
||||
Ok((res, rest))
|
||||
} else {
|
||||
Err(ECDSADecodeErr::InvalidKeyFormat)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public_impl!(P192, U192, I192);
|
||||
public_impl!(P224, U256, I256);
|
||||
public_impl!(P256, U256, I256);
|
||||
public_impl!(P384, U384, I384);
|
||||
public_impl!(P521, U576, I576);
|
||||
|
||||
#[cfg(test)]
|
||||
use sha::{SHA224,SHA256,SHA384,SHA512};
|
||||
#[cfg(test)]
|
||||
use testing::*;
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! verify_test_body
|
||||
{
|
||||
($name: ident, $curve: ident, $un: ident, $si: ident) => {
|
||||
let fname = build_test_path("ecc/sign",stringify!($curve));
|
||||
run_test(fname.to_string(), 9, |case| {
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
let (negk, _bytes) = case.get("k").unwrap();
|
||||
let (negx, xbytes) = case.get("x").unwrap();
|
||||
let (negy, ybytes) = case.get("y").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negh, hbytes) = case.get("h").unwrap();
|
||||
let (negr, rbytes) = case.get("r").unwrap();
|
||||
let (negs, sbytes) = case.get("s").unwrap();
|
||||
|
||||
assert!(!negd && !negk && !negx && !negy &&
|
||||
!negm && !negh && !negr && !negs);
|
||||
let _ = $un::from_bytes(dbytes);
|
||||
let x = $un::from_bytes(xbytes);
|
||||
let y = $un::from_bytes(ybytes);
|
||||
let h = $un::from_bytes(hbytes);
|
||||
let r = $un::from_bytes(rbytes);
|
||||
let s = $un::from_bytes(sbytes);
|
||||
|
||||
let point = Point::<$curve>{ x: $si::from(x), y: $si::from(y) };
|
||||
let public = ECCPublicKey::<$curve>::new(point);
|
||||
let sig = DSASignature::new(r, s);
|
||||
match usize::from(h) {
|
||||
224 => assert!(public.verify::<SHA224>(mbytes, &sig)),
|
||||
256 => assert!(public.verify::<SHA256>(mbytes, &sig)),
|
||||
384 => assert!(public.verify::<SHA384>(mbytes, &sig)),
|
||||
512 => assert!(public.verify::<SHA512>(mbytes, &sig)),
|
||||
x => panic!("Unknown hash algorithm {}", x)
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! test_impl {
|
||||
($name: ident, $curve: ident, $un: ident, $si: ident) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
verify_test_body!($name, $curve, $un, $si);
|
||||
}
|
||||
};
|
||||
(ignore $name: ident, $curve: ident, $un: ident, $si: ident) => {
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn $name() {
|
||||
verify_test_body!($name, $curve, $un, $si);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_impl!(p192,P192,U192,I192);
|
||||
test_impl!(p224,P224,U256,I256);
|
||||
test_impl!(ignore p256,P256,U256,I256);
|
||||
test_impl!(ignore p384,P384,U384,I384);
|
||||
test_impl!(ignore p521,P521,U576,I576);
|
||||
1500
src/ed25519/constants.rs
Normal file
1500
src/ed25519/constants.rs
Normal file
File diff suppressed because it is too large
Load Diff
1090
src/ed25519/fe.rs
Normal file
1090
src/ed25519/fe.rs
Normal file
File diff suppressed because it is too large
Load Diff
33
src/ed25519/loads.rs
Normal file
33
src/ed25519/loads.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
#[cfg(test)]
|
||||
use cryptonum::unsigned::{Decoder,U192};
|
||||
#[cfg(test)]
|
||||
use testing::run_test;
|
||||
|
||||
pub fn load3(x: &[u8]) -> u64
|
||||
{
|
||||
(x[0] as u64) | ((x[1] as u64) << 8) | ((x[2] as u64) << 16)
|
||||
}
|
||||
|
||||
pub fn load4(x: &[u8]) -> u64
|
||||
{
|
||||
(x[0] as u64) | ((x[1] as u64) << 8) |
|
||||
((x[2] as u64) << 16) | ((x[3] as u64) << 24)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn loads() {
|
||||
let fname = "testdata/ed25519/load.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negx, xbytes) = case.get("x").unwrap();
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
|
||||
assert!(!negx && !nega && !negb);
|
||||
let res3 = u64::from(U192::from_bytes(abytes));
|
||||
let res4 = u64::from(U192::from_bytes(bbytes));
|
||||
assert_eq!(res3, load3(&xbytes), "load3");
|
||||
assert_eq!(res4, load4(&xbytes), "load4");
|
||||
});
|
||||
}
|
||||
|
||||
267
src/ed25519/mod.rs
Normal file
267
src/ed25519/mod.rs
Normal file
@@ -0,0 +1,267 @@
|
||||
//! The generation and use of ED25519 keys is the most straightforward
|
||||
//! of all the asymmetric crypto schemes, because you basically get no
|
||||
//! choices. There's just one key size, and you're going to use the
|
||||
//! built-in hash (which is a good one, if you were worried). So if
|
||||
//! you're not sure, this is a pretty good choice.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use simple_crypto::ed25519::ED25519KeyPair;
|
||||
//!
|
||||
//! // Generate a new ED25519 key
|
||||
//! let mut rng = rand::rngs::OsRng::new().unwrap();
|
||||
//! let kp = ED25519KeyPair::generate(&mut rng);
|
||||
//!
|
||||
//! // Now that you have this key pair, you can sign and verify messages
|
||||
//! // using it. For example, to sign the vector [0,1,2,3,4] and then
|
||||
//! // verify that signature, we would write:
|
||||
//! let msg = vec![0,1,2,3,4];
|
||||
//! let sig = kp.private.sign(&msg);
|
||||
//! assert!( kp.public.verify(&msg, &sig) );
|
||||
//! ```
|
||||
|
||||
mod constants;
|
||||
mod fe;
|
||||
mod loads;
|
||||
mod point;
|
||||
mod scalars;
|
||||
|
||||
use rand::Rng;
|
||||
use sha::{Hash,SHA512};
|
||||
use self::scalars::{curve25519_scalar_mask,x25519_sc_muladd,x25519_sc_reduce};
|
||||
use self::point::{Point,Point2};
|
||||
#[cfg(test)]
|
||||
use testing::run_test;
|
||||
#[cfg(test)]
|
||||
use std::collections::HashMap;
|
||||
use super::KeyPair;
|
||||
|
||||
/// An ED25519 key pair
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct ED25519KeyPair
|
||||
{
|
||||
pub public: ED25519Public,
|
||||
pub private: ED25519Private
|
||||
}
|
||||
|
||||
impl KeyPair for ED25519KeyPair
|
||||
{
|
||||
type Public = ED25519Public;
|
||||
type Private = ED25519Private;
|
||||
|
||||
fn new(pbl: ED25519Public, prv: ED25519Private) -> ED25519KeyPair
|
||||
{
|
||||
ED25519KeyPair {
|
||||
public: pbl,
|
||||
private: prv
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ED25519KeyPair
|
||||
{
|
||||
/// Generate a random ED25519 key pair, using the given random number
|
||||
/// generator. You really need to use a good, cryptographically-strong
|
||||
/// RNG if you want good keys.
|
||||
pub fn generate<G: Rng>(rng: &mut G) -> ED25519KeyPair
|
||||
{
|
||||
let mut seed = [0; 32];
|
||||
rng.fill_bytes(&mut seed);
|
||||
let private = ED25519Private::from_seed(&seed);
|
||||
let public = ED25519Public::from(&private);
|
||||
ED25519KeyPair::new(public, private)
|
||||
}
|
||||
|
||||
/// Generate the ED25519 key pair defined by the given seed value.
|
||||
/// This should be a block of 32 bytes.
|
||||
pub fn from_seed(seed: &[u8]) -> ED25519KeyPair
|
||||
{
|
||||
let private = ED25519Private::from_seed(seed);
|
||||
let public = ED25519Public::from(&private);
|
||||
ED25519KeyPair{ public, private }
|
||||
}
|
||||
}
|
||||
|
||||
/// An ED25519 private key.
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct ED25519Private
|
||||
{
|
||||
seed: [u8; 32],
|
||||
private: [u8; 32],
|
||||
prefix: [u8; 32],
|
||||
public: [u8; 32]
|
||||
}
|
||||
|
||||
impl ED25519Private {
|
||||
/// Generate the ED25519 private key defined by the given 32 byte seed
|
||||
/// value.
|
||||
pub fn from_seed(seed: &[u8]) -> ED25519Private {
|
||||
let mut result = ED25519Private {
|
||||
seed: [0; 32],
|
||||
private: [0; 32],
|
||||
prefix: [0; 32],
|
||||
public: [0; 32]
|
||||
};
|
||||
result.seed.copy_from_slice(seed);
|
||||
let mut expanded = SHA512::hash(seed);
|
||||
let (private, prefix) = expanded.split_at_mut(32);
|
||||
result.private.copy_from_slice(private);
|
||||
result.prefix.copy_from_slice(prefix);
|
||||
curve25519_scalar_mask(&mut result.private);
|
||||
let a = Point::scalarmult_base(&result.private);
|
||||
result.public.copy_from_slice(&a.encode());
|
||||
result
|
||||
}
|
||||
|
||||
/// Sign the given message, returning the signature. Unlike most other
|
||||
/// public/private schemes, you don't get a choice on the hash used to
|
||||
/// compute this signature. (On the bright side, it's SHA2-512.)
|
||||
pub fn sign(&self, msg: &[u8]) -> Vec<u8>
|
||||
{
|
||||
let mut signature_s = [0u8; 32];
|
||||
|
||||
let mut ctx = SHA512::new();
|
||||
ctx.update(&self.prefix);
|
||||
ctx.update(&msg);
|
||||
let nonce = digest_scalar(&ctx.finalize());
|
||||
let r = Point::scalarmult_base(&nonce);
|
||||
let signature_r = r.encode();
|
||||
let hram_digest = eddsa_digest(&signature_r, &self.public, &msg);
|
||||
let hram = digest_scalar(&hram_digest);
|
||||
x25519_sc_muladd(&mut signature_s, &hram, &self.private, &nonce);
|
||||
let mut result = Vec::with_capacity(64);
|
||||
result.extend_from_slice(&signature_r);
|
||||
result.extend_from_slice(&signature_s);
|
||||
result
|
||||
}
|
||||
|
||||
/// Covert the given private key into its byte representation. This is
|
||||
/// guaranteed to be exactly 32 bytes.
|
||||
pub fn to_bytes(&self) -> Vec<u8>
|
||||
{
|
||||
self.seed.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
/// An ED25519 Public key
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct ED25519Public
|
||||
{
|
||||
bytes: [u8; 32],
|
||||
point: Point
|
||||
}
|
||||
|
||||
impl<'a> From<&'a ED25519Private> for ED25519Public
|
||||
{
|
||||
fn from(x: &ED25519Private) -> ED25519Public
|
||||
{
|
||||
ED25519Public::new(&x.public).expect("Broke converting private ED25519 to public. (?!)")
|
||||
}
|
||||
}
|
||||
|
||||
/// The kinds of errors you can get when you try to generate a public key from,
|
||||
/// for example, an unknown block of bytes.
|
||||
#[derive(Debug)]
|
||||
pub enum ED25519PublicImportError
|
||||
{
|
||||
WrongNumberOfBytes(usize),
|
||||
InvalidPublicPoint
|
||||
}
|
||||
|
||||
impl ED25519Public {
|
||||
/// Generate an ED25519 public key given the provided (32 byte) bytes. This
|
||||
/// can return errors if the value isn't a reasonable representation of an
|
||||
/// ED25519 point.
|
||||
pub fn new(bytes: &[u8]) -> Result<ED25519Public,ED25519PublicImportError>
|
||||
{
|
||||
if bytes.len() != 32 {
|
||||
return Err(ED25519PublicImportError::WrongNumberOfBytes(bytes.len()));
|
||||
}
|
||||
match Point::from_bytes(&bytes) {
|
||||
None =>
|
||||
Err(ED25519PublicImportError::InvalidPublicPoint),
|
||||
Some(a) => {
|
||||
let mut res = ED25519Public{ bytes: [0; 32], point: a };
|
||||
res.bytes.copy_from_slice(&bytes);
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify that the given signature matches the given message.
|
||||
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool
|
||||
{
|
||||
assert_eq!(sig.len(), 64);
|
||||
|
||||
let signature_r = &sig[..32];
|
||||
let signature_s = &sig[32..];
|
||||
|
||||
if signature_s[31] & 0b11100000 != 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let ainv = self.point.invert();
|
||||
let h_digest = eddsa_digest(signature_r, &self.bytes, msg);
|
||||
let h = digest_scalar(&h_digest);
|
||||
let r = Point2::double_scalarmult_vartime(&h, &ainv, &signature_s);
|
||||
let r_check = r.encode();
|
||||
signature_r.to_vec() == r_check
|
||||
}
|
||||
|
||||
/// Turn the ED25519 into its byte representation. This will always be a
|
||||
/// 32 byte block.
|
||||
pub fn to_bytes(&self) -> Vec<u8>
|
||||
{
|
||||
self.bytes.to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
fn eddsa_digest(signature_r: &[u8], public_key: &[u8], msg: &[u8]) -> Vec<u8>
|
||||
{
|
||||
let mut ctx = SHA512::new();
|
||||
ctx.update(signature_r);
|
||||
ctx.update(public_key);
|
||||
ctx.update(msg);
|
||||
ctx.finalize()
|
||||
}
|
||||
|
||||
fn digest_scalar(digest: &[u8]) -> Vec<u8> {
|
||||
assert_eq!(digest.len(), 512/8);
|
||||
let mut copy = [0; 512/8];
|
||||
copy.copy_from_slice(digest);
|
||||
x25519_sc_reduce(&mut copy);
|
||||
copy[..32].to_vec()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn run_signing_testcase(case: HashMap<String,(bool,Vec<u8>)>)
|
||||
{
|
||||
let (negr, rbytes) = case.get("r").unwrap();
|
||||
let (negu, ubytes) = case.get("u").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negs, sbytes) = case.get("s").unwrap();
|
||||
|
||||
assert!(!negr && !negu && !negm && !negs);
|
||||
let keypair = ED25519KeyPair::from_seed(rbytes);
|
||||
assert_eq!(ubytes, &keypair.public.bytes.to_vec());
|
||||
let mut privpub = Vec::new();
|
||||
privpub.append(&mut rbytes.clone());
|
||||
privpub.append(&mut ubytes.clone());
|
||||
let sig = keypair.private.sign(&mbytes);
|
||||
assert_eq!(sig.len(), sbytes.len());
|
||||
assert!(sig.iter().eq(sbytes.iter()));
|
||||
assert!(keypair.public.verify(&mbytes, &sig));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn rfc8072() {
|
||||
let fname = "testdata/ed25519/rfc8032.test";
|
||||
run_test(fname.to_string(), 4, run_signing_testcase);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn signing() {
|
||||
let fname = "testdata/ed25519/sign.test";
|
||||
run_test(fname.to_string(), 4, run_signing_testcase);
|
||||
}
|
||||
733
src/ed25519/point.rs
Normal file
733
src/ed25519/point.rs
Normal file
@@ -0,0 +1,733 @@
|
||||
#[cfg(test)]
|
||||
use testing::run_test;
|
||||
|
||||
use ed25519::fe::*;
|
||||
use ed25519::constants::*;
|
||||
use std::ops::*;
|
||||
|
||||
// This is ge_p3 in the original source code
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct Point {
|
||||
pub x: FieldElement,
|
||||
pub y: FieldElement,
|
||||
pub z: FieldElement,
|
||||
pub t: FieldElement
|
||||
}
|
||||
|
||||
impl Point {
|
||||
fn zero() -> Point
|
||||
{
|
||||
Point {
|
||||
x: FieldElement::zero(),
|
||||
y: FieldElement::one(),
|
||||
z: FieldElement::one(),
|
||||
t: FieldElement::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn load_test_value(xs: &[u8]) -> Point {
|
||||
assert!(xs.len() == 160);
|
||||
Point {
|
||||
x: test_from_bytes(&xs[0..40]),
|
||||
y: test_from_bytes(&xs[40..80]),
|
||||
z: test_from_bytes(&xs[80..120]),
|
||||
t: test_from_bytes(&xs[120..])
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert 32 bytes into an ED25519 point. This routine is not
|
||||
/// statically timed, so don't use it if that's important to you.
|
||||
pub fn from_bytes(s: &[u8]) -> Option<Point>
|
||||
{
|
||||
let hy = FieldElement::from_bytes(s);
|
||||
let hz = FieldElement::one();
|
||||
let mut u = hy.square();
|
||||
let mut v = &u * &D;
|
||||
u = &u - &hz; /* u = y^2-1 */
|
||||
v += &hz;
|
||||
|
||||
let mut v3 = v.square();
|
||||
v3 *= &v; /* v3 = v^3 */
|
||||
let mut hx = v3.square();
|
||||
hx *= &v;
|
||||
hx *= &u; /* x = uv^7 */
|
||||
hx = hx.pow22523(); /* x = (uv^7)^((q-5)/8) */
|
||||
hx *= &v3;
|
||||
hx *= &u; /* x = uv^3(uv^7)^((q-5)/8) */
|
||||
|
||||
let mut vxx = hx.square();
|
||||
vxx *= &v;
|
||||
let mut check = &vxx - &u; /* vx^2-u */
|
||||
if check.isnonzero() {
|
||||
check = &vxx + &u;
|
||||
if check.isnonzero() {
|
||||
return None;
|
||||
}
|
||||
hx *= &SQRTM1;
|
||||
}
|
||||
|
||||
if hx.isnegative() != ((s[31] >> 7) == 1) {
|
||||
hx = -&hx;
|
||||
}
|
||||
|
||||
let ht = &hx * &hy;
|
||||
return Some(Point{ x: hx, y: hy, z: hz, t: ht });
|
||||
}
|
||||
|
||||
pub fn encode(&self) -> Vec<u8>
|
||||
{
|
||||
into_encoded_point(&self.x, &self.y, &self.z)
|
||||
}
|
||||
|
||||
pub fn invert(&self) -> Point
|
||||
{
|
||||
Point {
|
||||
x: -&self.x,
|
||||
y: self.y.clone(),
|
||||
z: self.z.clone(),
|
||||
t: -&self.t,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const D: FieldElement = FieldElement {
|
||||
value: [-10913610, 13857413, -15372611, 6949391, 114729,
|
||||
-8787816, -6275908, -3247719, -18696448, -12055116]
|
||||
};
|
||||
|
||||
const SQRTM1: FieldElement = FieldElement {
|
||||
value: [-32595792, -7943725, 9377950, 3500415, 12389472,
|
||||
-272473, -25146209, -2005654, 326686, 11406482]
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn from_bytes_vartime() {
|
||||
let fname = "testdata/ed25519/fbv.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
let (negc, cbytes) = case.get("c").unwrap();
|
||||
|
||||
assert!(!nega && !negb && !negc);
|
||||
let target = Point::load_test_value(&cbytes);
|
||||
let mine = Point::from_bytes(&abytes);
|
||||
if bbytes.len() < cbytes.len() {
|
||||
assert!(mine.is_none());
|
||||
} else {
|
||||
assert_eq!(target, mine.unwrap());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct Point2 {
|
||||
pub x: FieldElement,
|
||||
pub y: FieldElement,
|
||||
pub z: FieldElement,
|
||||
}
|
||||
|
||||
impl Point2 {
|
||||
pub fn zero() -> Point2
|
||||
{
|
||||
Point2 {
|
||||
x: FieldElement::zero(),
|
||||
y: FieldElement::one(),
|
||||
z: FieldElement::one()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn load_test_value(xs: &[u8]) -> Point2 {
|
||||
assert!(xs.len() == 120);
|
||||
Point2 {
|
||||
x: test_from_bytes(&xs[0..40]),
|
||||
y: test_from_bytes(&xs[40..80]),
|
||||
z: test_from_bytes(&xs[80..120]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode(&self) -> Vec<u8>
|
||||
{
|
||||
into_encoded_point(&self.x, &self.y, &self.z)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Point> for Point2 {
|
||||
fn from(p: &Point) -> Point2 {
|
||||
Point2 {
|
||||
x: p.x.clone(),
|
||||
y: p.y.clone(),
|
||||
z: p.z.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub struct PointP1P1 {
|
||||
x: FieldElement,
|
||||
y: FieldElement,
|
||||
z: FieldElement,
|
||||
t: FieldElement
|
||||
}
|
||||
|
||||
impl PointP1P1 {
|
||||
#[cfg(test)]
|
||||
fn load_test_value(xs: &[u8]) -> PointP1P1 {
|
||||
assert!(xs.len() == 160);
|
||||
PointP1P1 {
|
||||
x: test_from_bytes(&xs[0..40]),
|
||||
y: test_from_bytes(&xs[40..80]),
|
||||
z: test_from_bytes(&xs[80..120]),
|
||||
t: test_from_bytes(&xs[120..])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
struct Cached {
|
||||
yplusx: FieldElement,
|
||||
yminusx: FieldElement,
|
||||
z: FieldElement,
|
||||
t2d: FieldElement
|
||||
}
|
||||
|
||||
impl Cached
|
||||
{
|
||||
fn new() -> Cached
|
||||
{
|
||||
Cached {
|
||||
yplusx: FieldElement::new(),
|
||||
yminusx: FieldElement::new(),
|
||||
z: FieldElement::new(),
|
||||
t2d: FieldElement::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn load_test_value(xs: &[u8]) -> Cached {
|
||||
assert!(xs.len() == 160);
|
||||
Cached {
|
||||
yplusx: test_from_bytes(&xs[0..40]),
|
||||
yminusx: test_from_bytes(&xs[40..80]),
|
||||
z: test_from_bytes(&xs[80..120]),
|
||||
t2d: test_from_bytes(&xs[120..])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const D2: FieldElement = FieldElement {
|
||||
value: [-21827239, -5839606, -30745221, 13898782, 229458,
|
||||
15978800, -12551817, -6495438, 29715968, 9444199]
|
||||
};
|
||||
|
||||
impl<'a> From<&'a Point> for Cached
|
||||
{
|
||||
fn from(p: &Point) -> Cached
|
||||
{
|
||||
Cached {
|
||||
yplusx: &p.y + &p.x,
|
||||
yminusx: &p.y - &p.x,
|
||||
z: p.z.clone(),
|
||||
t2d: &p.t * &D2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a PointP1P1> for Point2
|
||||
{
|
||||
fn from(p: &PointP1P1) -> Point2
|
||||
{
|
||||
Point2 {
|
||||
x: &p.x * &p.t,
|
||||
y: &p.y * &p.z,
|
||||
z: &p.z * &p.t,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a PointP1P1> for Point
|
||||
{
|
||||
fn from(p: &PointP1P1) -> Point
|
||||
{
|
||||
Point {
|
||||
x: &p.x * &p.t,
|
||||
y: &p.y * &p.z,
|
||||
z: &p.z * &p.t,
|
||||
t: &p.x * &p.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn conversion() {
|
||||
let fname = "testdata/ed25519/conversion.test";
|
||||
run_test(fname.to_string(), 6, |case| {
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negc, cbytes) = case.get("c").unwrap();
|
||||
let (negt, tbytes) = case.get("t").unwrap();
|
||||
let (nego, obytes) = case.get("o").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
|
||||
let a = Point::load_test_value(&abytes);
|
||||
let c = Cached::load_test_value(&cbytes);
|
||||
let t = Point2::load_test_value(&tbytes);
|
||||
let o = PointP1P1::load_test_value(&obytes);
|
||||
let d = Point2::load_test_value(&dbytes);
|
||||
let b = Point::load_test_value(&bbytes);
|
||||
|
||||
assert!(!nega && !negc && !negt && !nego && !negd && !negb);
|
||||
|
||||
let myc = Cached::from(&a);
|
||||
assert_eq!(myc, c);
|
||||
|
||||
let myt = Point2::from(&a);
|
||||
assert_eq!(myt, t);
|
||||
|
||||
let myo = a.double();
|
||||
assert_eq!(myo, o);
|
||||
|
||||
let myd = Point2::from(&o);
|
||||
assert_eq!(myd, d);
|
||||
|
||||
let myb = Point::from(&o);
|
||||
assert_eq!(myb, b);
|
||||
});
|
||||
}
|
||||
|
||||
/* r = 2 * p */
|
||||
impl Point2 {
|
||||
fn double(&self) -> PointP1P1
|
||||
{
|
||||
let x0 = self.x.square();
|
||||
let z0 = self.y.square();
|
||||
let t0 = self.z.sq2();
|
||||
let y0 = &self.x + &self.y;
|
||||
let ry = &z0 + &x0;
|
||||
let rz = &z0 - &x0;
|
||||
let rx = &y0.square() - &ry;
|
||||
let rt = &t0 - &rz;
|
||||
PointP1P1 { x: rx, y: ry, z: rz, t: rt }
|
||||
}
|
||||
}
|
||||
|
||||
/* r = 2 * p */
|
||||
impl Point {
|
||||
fn double(&self) -> PointP1P1
|
||||
{
|
||||
Point2::from(self).double()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn double() {
|
||||
let fname = "testdata/ed25519/pt_double.test";
|
||||
run_test(fname.to_string(), 4, |case| {
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
let (negc, cbytes) = case.get("c").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!nega && !negb && !negc && !negd);
|
||||
let a = Point::load_test_value(abytes);
|
||||
let b = PointP1P1::load_test_value(bbytes);
|
||||
let c = Point2::load_test_value(cbytes);
|
||||
let d = PointP1P1::load_test_value(dbytes);
|
||||
|
||||
let myb = a.double();
|
||||
assert_eq!(myb, b);
|
||||
let myd = c.double();
|
||||
assert_eq!(myd, d);
|
||||
});
|
||||
}
|
||||
|
||||
impl<'a,'b> Add<&'a Precomp> for &'b Point
|
||||
{
|
||||
type Output = PointP1P1;
|
||||
|
||||
fn add(self, q: &Precomp) -> PointP1P1
|
||||
{
|
||||
let mut rx;
|
||||
let mut ry;
|
||||
let mut rz;
|
||||
let mut rt;
|
||||
|
||||
rx = &self.y + &self.x;
|
||||
ry = &self.y - &self.x;
|
||||
rz = &rx * &q.yplusx;
|
||||
ry *= &q.yminusx;
|
||||
rt = &q.xy2d * &self.t;
|
||||
let t0 = &self.z + &self.z;
|
||||
rx = &rz - &ry;
|
||||
ry += &rz;
|
||||
rz = &t0 + &rt;
|
||||
rt = &t0 - &rt;
|
||||
|
||||
PointP1P1 { x: rx, y: ry, z: rz, t: rt }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> Sub<&'a Precomp> for &'b Point
|
||||
{
|
||||
type Output = PointP1P1;
|
||||
|
||||
/* r = p - q */
|
||||
fn sub(self, q: &Precomp) -> PointP1P1
|
||||
{
|
||||
let mut rx = &self.y + &self.x;
|
||||
let mut ry = &self.y - &self.x;
|
||||
let mut rz = &rx * &q.yminusx;
|
||||
ry *= &q.yplusx;
|
||||
let mut rt = &q.xy2d * &self.t;
|
||||
let t0 = &self.z + &self.z;
|
||||
rx = &rz - &ry;
|
||||
ry += &rz;
|
||||
rz = &t0 - &rt;
|
||||
rt += &t0;
|
||||
PointP1P1{ x: rx, y: ry, z: rz, t: rt }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn maddsub() {
|
||||
let fname = "testdata/ed25519/maddsub.test";
|
||||
run_test(fname.to_string(), 4, |case| {
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
let (negc, cbytes) = case.get("c").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!nega && !negb && !negc && !negd);
|
||||
let a = Point::load_test_value(abytes);
|
||||
let b = PointP1P1::load_test_value(bbytes);
|
||||
let c = Precomp::load_test_value(cbytes);
|
||||
let d = PointP1P1::load_test_value(dbytes);
|
||||
|
||||
let myb = &a + &c;
|
||||
assert_eq!(myb, b);
|
||||
let myd = &a - &c;
|
||||
assert_eq!(myd, d);
|
||||
});
|
||||
}
|
||||
|
||||
impl<'a,'b> Add<&'a Cached> for &'b Point
|
||||
{
|
||||
type Output = PointP1P1;
|
||||
|
||||
fn add(self, q: &Cached) -> PointP1P1
|
||||
{
|
||||
let mut rx;
|
||||
let mut ry;
|
||||
let mut rz;
|
||||
let mut rt;
|
||||
|
||||
rx = &self.y + &self.x;
|
||||
ry = &self.y - &self.x;
|
||||
rz = &rx * &q.yplusx;
|
||||
ry *= &q.yminusx;
|
||||
rt = &q.t2d * &self.t;
|
||||
rx = &self.z * &q.z;
|
||||
let t0 = &rx + ℞
|
||||
rx = &rz - &ry;
|
||||
ry += &rz;
|
||||
rz = &t0 + &rt;
|
||||
rt = &t0 - &rt;
|
||||
|
||||
PointP1P1{ x: rx, y: ry, z: rz, t: rt }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> Sub<&'a Cached> for &'b Point
|
||||
{
|
||||
type Output = PointP1P1;
|
||||
|
||||
fn sub(self, q: &Cached) -> PointP1P1
|
||||
{
|
||||
let mut rx;
|
||||
let mut ry;
|
||||
let mut rz;
|
||||
let mut rt;
|
||||
|
||||
rx = &self.y + &self.x;
|
||||
ry = &self.y - &self.x;
|
||||
rz = &rx * &q.yminusx;
|
||||
ry *= &q.yplusx;
|
||||
rt = &q.t2d * &self.t;
|
||||
rx = &self.z * &q.z;
|
||||
let t0 = &rx + ℞
|
||||
rx = &rz - &ry;
|
||||
ry += &rz;
|
||||
rz = &t0 - &rt;
|
||||
rt += &t0;
|
||||
|
||||
PointP1P1{ x: rx, y: ry, z: rz, t: rt }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn addsub() {
|
||||
let fname = "testdata/ed25519/ptaddsub.test";
|
||||
run_test(fname.to_string(), 4, |case| {
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
let (negc, cbytes) = case.get("c").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!nega && !negb && !negc && !negd);
|
||||
let a = Point::load_test_value(abytes);
|
||||
let b = PointP1P1::load_test_value(bbytes);
|
||||
let c = Cached::load_test_value(cbytes);
|
||||
let d = PointP1P1::load_test_value(dbytes);
|
||||
|
||||
let myb = &a + &c;
|
||||
assert_eq!(myb, b);
|
||||
let myd = &a - &c;
|
||||
assert_eq!(myd, d);
|
||||
});
|
||||
}
|
||||
|
||||
impl Point {
|
||||
/* h = a * B
|
||||
* where a = a[0]+256*a[1]+...+256^31 a[31]
|
||||
* B is the Ed25519 base point (x,4/5) with x positive.
|
||||
*
|
||||
* Preconditions:
|
||||
* a[31] <= 127 */
|
||||
pub fn scalarmult_base(a: &[u8]) -> Point
|
||||
{
|
||||
let mut e: [i8; 64] = [0; 64];
|
||||
for i in 0..32 {
|
||||
e[2 * i + 0] = ((a[i] >> 0) & 15) as i8;
|
||||
e[2 * i + 1] = ((a[i] >> 4) & 15) as i8;
|
||||
}
|
||||
/* each e[i] is between 0 and 15 */
|
||||
/* e[63] is between 0 and 7 */
|
||||
|
||||
let mut carry = 0;
|
||||
for i in 0..63 {
|
||||
e[i] += carry;
|
||||
carry = e[i] + 8;
|
||||
carry >>= 4;
|
||||
e[i] -= carry << 4;
|
||||
}
|
||||
e[63] += carry;
|
||||
/* each e[i] is between -8 and 8 */
|
||||
|
||||
let mut r;
|
||||
let mut t;
|
||||
|
||||
let mut h = Point::zero();
|
||||
for i in &[1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63] {
|
||||
t = Precomp::table_select(*i / 2, e[*i as usize]);
|
||||
r = &h + &t;
|
||||
h = Point::from(&r);
|
||||
}
|
||||
|
||||
r = h.double();
|
||||
let mut s = Point2::from(&r);
|
||||
r = s.double();
|
||||
s = Point2::from(&r);
|
||||
r = s.double();
|
||||
s = Point2::from(&r);
|
||||
r = s.double();
|
||||
h = Point::from(&r);
|
||||
|
||||
for i in &[0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62] {
|
||||
t = Precomp::table_select(*i / 2, e[*i as usize]);
|
||||
r = &h + &t;
|
||||
h = Point::from(&r);
|
||||
}
|
||||
|
||||
h
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn scalarmult_base() {
|
||||
let fname = "testdata/ed25519/scalar_mult.test";
|
||||
run_test(fname.to_string(), 2, |case| {
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
|
||||
assert!(!nega && !negb);
|
||||
let b = Point::load_test_value(bbytes);
|
||||
let mine = Point::scalarmult_base(&abytes);
|
||||
assert_eq!(mine, b);
|
||||
});
|
||||
}
|
||||
|
||||
fn slide(r: &mut [i8], a: &[u8])
|
||||
{
|
||||
for i in 0..256 {
|
||||
r[i] = (1 & (a[i >> 3] >> (i & 7))) as i8;
|
||||
}
|
||||
|
||||
for i in 0..256 {
|
||||
if r[i] != 0 {
|
||||
let mut b = 1;
|
||||
while (b <= 6) && ((i + b) < 256) {
|
||||
if r[i + b] != 0 {
|
||||
if r[i] + (r[i + b] << b) <= 15 {
|
||||
r[i] += r[i + b] << b;
|
||||
r[i + b] = 0;
|
||||
} else if r[i] - (r[i + b] << b) >= -15 {
|
||||
r[i] -= r[i + b] << b;
|
||||
for k in (i+b)..256 {
|
||||
if r[k] == 0 {
|
||||
r[k] = 1;
|
||||
break;
|
||||
}
|
||||
r[k] = 0;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
b += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn helper_slide() {
|
||||
let fname = "testdata/ed25519/slide.test";
|
||||
run_test(fname.to_string(), 2, |case| {
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
|
||||
assert!(!nega && !negb);
|
||||
let mut mine = [0; 256];
|
||||
slide(&mut mine, &abytes);
|
||||
for i in 0..256 {
|
||||
assert_eq!(mine[i], bbytes[i] as i8);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl Point2
|
||||
{
|
||||
/* r = a * A + b * B
|
||||
* where a = a[0]+256*a[1]+...+256^31 a[31].
|
||||
* and b = b[0]+256*b[1]+...+256^31 b[31].
|
||||
* B is the Ed25519 base point (x,4/5) with x positive. */
|
||||
#[allow(non_snake_case)]
|
||||
pub fn double_scalarmult_vartime(a: &[u8], A: &Point, b: &[u8]) -> Point2
|
||||
{
|
||||
let mut aslide: [i8; 256] = [0; 256];
|
||||
let mut bslide: [i8; 256] = [0; 256];
|
||||
#[allow(non_snake_case)]
|
||||
let mut Ai: [Cached; 8] = [Cached::new(), Cached::new(), Cached::new(), Cached::new(),
|
||||
Cached::new(), Cached::new(), Cached::new(), Cached::new()];
|
||||
#[allow(non_snake_case)]
|
||||
|
||||
slide(&mut aslide, &a);
|
||||
slide(&mut bslide, &b);
|
||||
|
||||
Ai[0] = Cached::from(A);
|
||||
let mut t = A.double();
|
||||
let A2 = Point::from(&t);
|
||||
t = &A2 + &Ai[0];
|
||||
let mut u = Point::from(&t);
|
||||
Ai[1] = Cached::from(&u);
|
||||
t = &A2 + &Ai[1];
|
||||
u = Point::from(&t);
|
||||
Ai[2] = Cached::from(&u);
|
||||
t = &A2 + &Ai[2];
|
||||
u = Point::from(&t);
|
||||
Ai[3] = Cached::from(&u);
|
||||
t = &A2 + &Ai[3];
|
||||
u = Point::from(&t);
|
||||
Ai[4] = Cached::from(&u);
|
||||
t = &A2 + &Ai[4];
|
||||
u = Point::from(&t);
|
||||
Ai[5] = Cached::from(&u);
|
||||
t = &A2 + &Ai[5];
|
||||
u = Point::from(&t);
|
||||
Ai[6] = Cached::from(&u);
|
||||
t = &A2 + &Ai[6];
|
||||
u = Point::from(&t);
|
||||
Ai[7] = Cached::from(&u);
|
||||
|
||||
let mut r = Point2::zero();
|
||||
|
||||
let mut i: i32 = 255;
|
||||
loop {
|
||||
if (aslide[i as usize] != 0) || (bslide[i as usize] != 0) {
|
||||
break;
|
||||
}
|
||||
i -= 1;
|
||||
if i < 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while i >= 0 {
|
||||
t = r.double();
|
||||
|
||||
if aslide[i as usize] > 0 {
|
||||
u = Point::from(&t);
|
||||
let idx = (aslide[i as usize] / 2) as usize;
|
||||
t = &u + &Ai[idx]
|
||||
} else if aslide[i as usize] < 0 {
|
||||
u = Point::from(&t);
|
||||
let idx = ((-aslide[i as usize]) / 2) as usize;
|
||||
t = &u - &Ai[idx];
|
||||
}
|
||||
|
||||
if bslide[i as usize] > 0 {
|
||||
u = Point::from(&t);
|
||||
let idx = (bslide[i as usize] / 2) as usize;
|
||||
t = &u + &BI[idx];
|
||||
} else if bslide[i as usize] < 0 {
|
||||
u = Point::from(&t);
|
||||
let idx = ((-bslide[i as usize]) / 2) as usize;
|
||||
t = &u - &BI[idx];
|
||||
}
|
||||
|
||||
r = Point2::from(&t);
|
||||
i -= 1;
|
||||
}
|
||||
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn double_scalarmult() {
|
||||
let fname = "testdata/ed25519/scalar_mult_gen.test";
|
||||
run_test(fname.to_string(), 4, |case| {
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
let (negc, cbytes) = case.get("c").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!nega && !negb && !negc && !negd);
|
||||
let b = Point::load_test_value(bbytes);
|
||||
let d = Point2::load_test_value(dbytes);
|
||||
let mine = Point2::double_scalarmult_vartime(&abytes, &b, &cbytes);
|
||||
assert_eq!(mine, d);
|
||||
});
|
||||
}
|
||||
|
||||
fn into_encoded_point(x: &FieldElement, y: &FieldElement, z: &FieldElement) -> Vec<u8>
|
||||
{
|
||||
let recip = z.invert();
|
||||
let x_over_z = x * &recip;
|
||||
let y_over_z = y * &recip;
|
||||
let mut bytes = y_over_z.to_bytes();
|
||||
let sign_bit = if x_over_z.isnegative() { 1 } else { 0 };
|
||||
// The preceding computations must execute in constant time, but this
|
||||
// doesn't need to.
|
||||
bytes[31] ^= sign_bit << 7;
|
||||
bytes
|
||||
}
|
||||
3363
src/ed25519/rfc8032.txt
Normal file
3363
src/ed25519/rfc8032.txt
Normal file
File diff suppressed because it is too large
Load Diff
881
src/ed25519/scalars.rs
Normal file
881
src/ed25519/scalars.rs
Normal file
@@ -0,0 +1,881 @@
|
||||
use ed25519::loads::{load3,load4};
|
||||
#[cfg(test)]
|
||||
use testing::run_test;
|
||||
|
||||
/* The set of scalars is \Z/l
|
||||
* where l = 2^252 + 27742317777372353535851937790883648493. */
|
||||
|
||||
/* Input:
|
||||
* s[0]+256*s[1]+...+256^63*s[63] = s
|
||||
*
|
||||
* Output:
|
||||
* s[0]+256*s[1]+...+256^31*s[31] = s mod l
|
||||
* where l = 2^252 + 27742317777372353535851937790883648493.
|
||||
* Overwrites s in place. */
|
||||
pub fn x25519_sc_reduce(s: &mut [u8])
|
||||
{
|
||||
let mut s0 : i64 = 2097151 & load3(s) as i64;
|
||||
let mut s1 : i64 = 2097151 & (load4(&s[2..]) >> 5) as i64;
|
||||
let mut s2 : i64 = 2097151 & (load3(&s[5..]) >> 2) as i64;
|
||||
let mut s3 : i64 = 2097151 & (load4(&s[7..]) >> 7) as i64;
|
||||
let mut s4 : i64 = 2097151 & (load4(&s[10..]) >> 4) as i64;
|
||||
let mut s5 : i64 = 2097151 & (load3(&s[13..]) >> 1) as i64;
|
||||
let mut s6 : i64 = 2097151 & (load4(&s[15..]) >> 6) as i64;
|
||||
let mut s7 : i64 = 2097151 & (load3(&s[18..]) >> 3) as i64;
|
||||
let mut s8 : i64 = 2097151 & load3(&s[21..]) as i64;
|
||||
let mut s9 : i64 = 2097151 & (load4(&s[23..]) >> 5) as i64;
|
||||
let mut s10 : i64 = 2097151 & (load3(&s[26..]) >> 2) as i64;
|
||||
let mut s11 : i64 = 2097151 & (load4(&s[28..]) >> 7) as i64;
|
||||
let mut s12 : i64 = 2097151 & (load4(&s[31..]) >> 4) as i64;
|
||||
let mut s13 : i64 = 2097151 & (load3(&s[34..]) >> 1) as i64;
|
||||
let mut s14 : i64 = 2097151 & (load4(&s[36..]) >> 6) as i64;
|
||||
let mut s15 : i64 = 2097151 & (load3(&s[39..]) >> 3) as i64;
|
||||
let mut s16 : i64 = 2097151 & load3(&s[42..]) as i64;
|
||||
let mut s17 : i64 = 2097151 & (load4(&s[44..]) >> 5) as i64;
|
||||
let s18 : i64 = 2097151 & (load3(&s[47..]) >> 2) as i64;
|
||||
let s19 : i64 = 2097151 & (load4(&s[49..]) >> 7) as i64;
|
||||
let s20 : i64 = 2097151 & (load4(&s[52..]) >> 4) as i64;
|
||||
let s21 : i64 = 2097151 & (load3(&s[55..]) >> 1) as i64;
|
||||
let s22 : i64 = 2097151 & (load4(&s[57..]) >> 6) as i64;
|
||||
let s23 : i64 = (load4(&s[60..]) >> 3) as i64 as i64;
|
||||
let mut carry0 : i64;
|
||||
let mut carry1 : i64;
|
||||
let mut carry2 : i64;
|
||||
let mut carry3 : i64;
|
||||
let mut carry4 : i64;
|
||||
let mut carry5 : i64;
|
||||
let mut carry6 : i64;
|
||||
let mut carry7 : i64;
|
||||
let mut carry8 : i64;
|
||||
let mut carry9 : i64;
|
||||
let mut carry10 : i64;
|
||||
let mut carry11 : i64;
|
||||
let carry12 : i64;
|
||||
let carry13 : i64;
|
||||
let carry14 : i64;
|
||||
let carry15 : i64;
|
||||
let carry16 : i64;
|
||||
|
||||
s11 += s23 * 666643;
|
||||
s12 += s23 * 470296;
|
||||
s13 += s23 * 654183;
|
||||
s14 -= s23 * 997805;
|
||||
s15 += s23 * 136657;
|
||||
s16 -= s23 * 683901;
|
||||
//s23 = 0;
|
||||
|
||||
s10 += s22 * 666643;
|
||||
s11 += s22 * 470296;
|
||||
s12 += s22 * 654183;
|
||||
s13 -= s22 * 997805;
|
||||
s14 += s22 * 136657;
|
||||
s15 -= s22 * 683901;
|
||||
//s22 = 0;
|
||||
|
||||
s9 += s21 * 666643;
|
||||
s10 += s21 * 470296;
|
||||
s11 += s21 * 654183;
|
||||
s12 -= s21 * 997805;
|
||||
s13 += s21 * 136657;
|
||||
s14 -= s21 * 683901;
|
||||
//s21 = 0;
|
||||
|
||||
s8 += s20 * 666643;
|
||||
s9 += s20 * 470296;
|
||||
s10 += s20 * 654183;
|
||||
s11 -= s20 * 997805;
|
||||
s12 += s20 * 136657;
|
||||
s13 -= s20 * 683901;
|
||||
//s20 = 0;
|
||||
|
||||
s7 += s19 * 666643;
|
||||
s8 += s19 * 470296;
|
||||
s9 += s19 * 654183;
|
||||
s10 -= s19 * 997805;
|
||||
s11 += s19 * 136657;
|
||||
s12 -= s19 * 683901;
|
||||
//s19 = 0;
|
||||
|
||||
s6 += s18 * 666643;
|
||||
s7 += s18 * 470296;
|
||||
s8 += s18 * 654183;
|
||||
s9 -= s18 * 997805;
|
||||
s10 += s18 * 136657;
|
||||
s11 -= s18 * 683901;
|
||||
//s18 = 0;
|
||||
|
||||
carry6 = (s6 + (1 << 20)) >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry8 = (s8 + (1 << 20)) >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry10 = (s10 + (1 << 20)) >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
carry12 = (s12 + (1 << 20)) >> 21;
|
||||
s13 += carry12;
|
||||
s12 -= carry12 << 21;
|
||||
carry14 = (s14 + (1 << 20)) >> 21;
|
||||
s15 += carry14;
|
||||
s14 -= carry14 << 21;
|
||||
carry16 = (s16 + (1 << 20)) >> 21;
|
||||
s17 += carry16;
|
||||
s16 -= carry16 << 21;
|
||||
|
||||
carry7 = (s7 + (1 << 20)) >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry9 = (s9 + (1 << 20)) >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry11 = (s11 + (1 << 20)) >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
carry13 = (s13 + (1 << 20)) >> 21;
|
||||
s14 += carry13;
|
||||
s13 -= carry13 << 21;
|
||||
carry15 = (s15 + (1 << 20)) >> 21;
|
||||
s16 += carry15;
|
||||
s15 -= carry15 << 21;
|
||||
|
||||
s5 += s17 * 666643;
|
||||
s6 += s17 * 470296;
|
||||
s7 += s17 * 654183;
|
||||
s8 -= s17 * 997805;
|
||||
s9 += s17 * 136657;
|
||||
s10 -= s17 * 683901;
|
||||
//s17 = 0;
|
||||
|
||||
s4 += s16 * 666643;
|
||||
s5 += s16 * 470296;
|
||||
s6 += s16 * 654183;
|
||||
s7 -= s16 * 997805;
|
||||
s8 += s16 * 136657;
|
||||
s9 -= s16 * 683901;
|
||||
//s16 = 0;
|
||||
|
||||
s3 += s15 * 666643;
|
||||
s4 += s15 * 470296;
|
||||
s5 += s15 * 654183;
|
||||
s6 -= s15 * 997805;
|
||||
s7 += s15 * 136657;
|
||||
s8 -= s15 * 683901;
|
||||
//s15 = 0;
|
||||
|
||||
s2 += s14 * 666643;
|
||||
s3 += s14 * 470296;
|
||||
s4 += s14 * 654183;
|
||||
s5 -= s14 * 997805;
|
||||
s6 += s14 * 136657;
|
||||
s7 -= s14 * 683901;
|
||||
//s14 = 0;
|
||||
|
||||
s1 += s13 * 666643;
|
||||
s2 += s13 * 470296;
|
||||
s3 += s13 * 654183;
|
||||
s4 -= s13 * 997805;
|
||||
s5 += s13 * 136657;
|
||||
s6 -= s13 * 683901;
|
||||
//s13 = 0;
|
||||
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
s12 = 0;
|
||||
|
||||
carry0 = (s0 + (1 << 20)) >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry2 = (s2 + (1 << 20)) >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry4 = (s4 + (1 << 20)) >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry6 = (s6 + (1 << 20)) >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry8 = (s8 + (1 << 20)) >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry10 = (s10 + (1 << 20)) >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
|
||||
carry1 = (s1 + (1 << 20)) >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry3 = (s3 + (1 << 20)) >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry5 = (s5 + (1 << 20)) >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry7 = (s7 + (1 << 20)) >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry9 = (s9 + (1 << 20)) >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry11 = (s11 + (1 << 20)) >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
s12 = 0;
|
||||
|
||||
carry0 = s0 >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry1 = s1 >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry2 = s2 >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry3 = s3 >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry4 = s4 >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry5 = s5 >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry6 = s6 >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry7 = s7 >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry8 = s8 >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry9 = s9 >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry10 = s10 >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
carry11 = s11 >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
//s12 = 0;
|
||||
|
||||
carry0 = s0 >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry1 = s1 >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry2 = s2 >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry3 = s3 >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry4 = s4 >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry5 = s5 >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry6 = s6 >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry7 = s7 >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry8 = s8 >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry9 = s9 >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry10 = s10 >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
|
||||
s[0] = (s0 >> 0) as u8;
|
||||
s[1] = (s0 >> 8) as u8;
|
||||
s[2] = ((s0 >> 16) | (s1 << 5)) as u8;
|
||||
s[3] = (s1 >> 3) as u8;
|
||||
s[4] = (s1 >> 11) as u8;
|
||||
s[5] = ((s1 >> 19) | (s2 << 2)) as u8;
|
||||
s[6] = (s2 >> 6) as u8;
|
||||
s[7] = ((s2 >> 14) | (s3 << 7)) as u8;
|
||||
s[8] = (s3 >> 1) as u8;
|
||||
s[9] = (s3 >> 9) as u8;
|
||||
s[10] = ((s3 >> 17) | (s4 << 4)) as u8;
|
||||
s[11] = (s4 >> 4) as u8;
|
||||
s[12] = (s4 >> 12) as u8;
|
||||
s[13] = ((s4 >> 20) | (s5 << 1)) as u8;
|
||||
s[14] = (s5 >> 7) as u8;
|
||||
s[15] = ((s5 >> 15) | (s6 << 6)) as u8;
|
||||
s[16] = (s6 >> 2) as u8;
|
||||
s[17] = (s6 >> 10) as u8;
|
||||
s[18] = ((s6 >> 18) | (s7 << 3)) as u8;
|
||||
s[19] = (s7 >> 5) as u8;
|
||||
s[20] = (s7 >> 13) as u8;
|
||||
s[21] = (s8 >> 0) as u8;
|
||||
s[22] = (s8 >> 8) as u8;
|
||||
s[23] = ((s8 >> 16) | (s9 << 5)) as u8;
|
||||
s[24] = (s9 >> 3) as u8;
|
||||
s[25] = (s9 >> 11) as u8;
|
||||
s[26] = ((s9 >> 19) | (s10 << 2)) as u8;
|
||||
s[27] = (s10 >> 6) as u8;
|
||||
s[28] = ((s10 >> 14) | (s11 << 7)) as u8;
|
||||
s[29] = (s11 >> 1) as u8;
|
||||
s[30] = (s11 >> 9) as u8;
|
||||
s[31] = (s11 >> 17) as u8;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn reduce() {
|
||||
let fname = "testdata/ed25519/reduce.test";
|
||||
run_test(fname.to_string(), 2, |case| {
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
|
||||
assert!(!nega && !negb);
|
||||
assert_eq!(abytes.len(), 64);
|
||||
assert_eq!(bbytes.len(), 32);
|
||||
let mut copy = abytes.clone();
|
||||
x25519_sc_reduce(&mut copy);
|
||||
assert_eq!(©[0..32], &bbytes[0..]);
|
||||
});
|
||||
}
|
||||
|
||||
/* Input:
|
||||
* a[0]+256*a[1]+...+256^31*a[31] = a
|
||||
* b[0]+256*b[1]+...+256^31*b[31] = b
|
||||
* c[0]+256*c[1]+...+256^31*c[31] = c
|
||||
*
|
||||
* Output:
|
||||
* s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
|
||||
* where l = 2^252 + 27742317777372353535851937790883648493. */
|
||||
pub fn x25519_sc_muladd(s: &mut [u8], a: &[u8], b: &[u8], c: &[u8])
|
||||
{
|
||||
let a0 : i64 = 2097151 & load3(a) as i64;
|
||||
let a1 : i64 = 2097151 & (load4(&a[2..]) >> 5) as i64;
|
||||
let a2 : i64 = 2097151 & (load3(&a[5..]) >> 2) as i64;
|
||||
let a3 : i64 = 2097151 & (load4(&a[7..]) >> 7) as i64;
|
||||
let a4 : i64 = 2097151 & (load4(&a[10..]) >> 4) as i64;
|
||||
let a5 : i64 = 2097151 & (load3(&a[13..]) >> 1) as i64;
|
||||
let a6 : i64 = 2097151 & (load4(&a[15..]) >> 6) as i64;
|
||||
let a7 : i64 = 2097151 & (load3(&a[18..]) >> 3) as i64;
|
||||
let a8 : i64 = 2097151 & load3(&a[21..]) as i64;
|
||||
let a9 : i64 = 2097151 & (load4(&a[23..]) >> 5) as i64;
|
||||
let a10 : i64 = 2097151 & (load3(&a[26..]) >> 2) as i64;
|
||||
let a11 : i64 = (load4(&a[28..]) >> 7) as i64;
|
||||
let b0 : i64 = 2097151 & load3(b) as i64;
|
||||
let b1 : i64 = 2097151 & (load4(&b[2..]) >> 5) as i64;
|
||||
let b2 : i64 = 2097151 & (load3(&b[5..]) >> 2) as i64;
|
||||
let b3 : i64 = 2097151 & (load4(&b[7..]) >> 7) as i64;
|
||||
let b4 : i64 = 2097151 & (load4(&b[10..]) >> 4) as i64;
|
||||
let b5 : i64 = 2097151 & (load3(&b[13..]) >> 1) as i64;
|
||||
let b6 : i64 = 2097151 & (load4(&b[15..]) >> 6) as i64;
|
||||
let b7 : i64 = 2097151 & (load3(&b[18..]) >> 3) as i64;
|
||||
let b8 : i64 = 2097151 & load3(&b[21..]) as i64;
|
||||
let b9 : i64 = 2097151 & (load4(&b[23..]) >> 5) as i64;
|
||||
let b10 : i64 = 2097151 & (load3(&b[26..]) >> 2) as i64;
|
||||
let b11 : i64 = (load4(&b[28..]) >> 7) as i64;
|
||||
let c0 : i64 = 2097151 & load3(c) as i64;
|
||||
let c1 : i64 = 2097151 & (load4(&c[2..]) >> 5) as i64;
|
||||
let c2 : i64 = 2097151 & (load3(&c[5..]) >> 2) as i64;
|
||||
let c3 : i64 = 2097151 & (load4(&c[7..]) >> 7) as i64;
|
||||
let c4 : i64 = 2097151 & (load4(&c[10..]) >> 4) as i64;
|
||||
let c5 : i64 = 2097151 & (load3(&c[13..]) >> 1) as i64;
|
||||
let c6 : i64 = 2097151 & (load4(&c[15..]) >> 6) as i64;
|
||||
let c7 : i64 = 2097151 & (load3(&c[18..]) >> 3) as i64;
|
||||
let c8 : i64 = 2097151 & load3(&c[21..]) as i64;
|
||||
let c9 : i64 = 2097151 & (load4(&c[23..]) >> 5) as i64;
|
||||
let c10 : i64 = 2097151 & (load3(&c[26..]) >> 2) as i64;
|
||||
let c11 : i64 = (load4(&c[28..]) >> 7) as i64;
|
||||
let mut s0 : i64;
|
||||
let mut s1 : i64;
|
||||
let mut s2 : i64;
|
||||
let mut s3 : i64;
|
||||
let mut s4 : i64;
|
||||
let mut s5 : i64;
|
||||
let mut s6 : i64;
|
||||
let mut s7 : i64;
|
||||
let mut s8 : i64;
|
||||
let mut s9 : i64;
|
||||
let mut s10 : i64;
|
||||
let mut s11 : i64;
|
||||
let mut s12 : i64;
|
||||
let mut s13 : i64;
|
||||
let mut s14 : i64;
|
||||
let mut s15 : i64;
|
||||
let mut s16 : i64;
|
||||
let mut s17 : i64;
|
||||
let mut s18 : i64;
|
||||
let mut s19 : i64;
|
||||
let mut s20 : i64;
|
||||
let mut s21 : i64;
|
||||
let mut s22 : i64;
|
||||
let mut s23 : i64;
|
||||
let mut carry0 : i64;
|
||||
let mut carry1 : i64;
|
||||
let mut carry2 : i64;
|
||||
let mut carry3 : i64;
|
||||
let mut carry4 : i64;
|
||||
let mut carry5 : i64;
|
||||
let mut carry6 : i64;
|
||||
let mut carry7 : i64;
|
||||
let mut carry8 : i64;
|
||||
let mut carry9 : i64;
|
||||
let mut carry10 : i64;
|
||||
let mut carry11 : i64;
|
||||
let mut carry12 : i64;
|
||||
let mut carry13 : i64;
|
||||
let mut carry14 : i64;
|
||||
let mut carry15 : i64;
|
||||
let mut carry16 : i64;
|
||||
let carry17 : i64;
|
||||
let carry18 : i64;
|
||||
let carry19 : i64;
|
||||
let carry20 : i64;
|
||||
let carry21 : i64;
|
||||
let carry22 : i64;
|
||||
|
||||
s0 = c0 + a0 * b0;
|
||||
s1 = c1 + a0 * b1 + a1 * b0;
|
||||
s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0;
|
||||
s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0;
|
||||
s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0;
|
||||
s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0;
|
||||
s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + a6 * b0;
|
||||
s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 +
|
||||
a6 * b1 + a7 * b0;
|
||||
s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 +
|
||||
a6 * b2 + a7 * b1 + a8 * b0;
|
||||
s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 +
|
||||
a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0;
|
||||
s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 +
|
||||
a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0;
|
||||
s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 +
|
||||
a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0;
|
||||
s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + a7 * b5 +
|
||||
a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1;
|
||||
s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + a8 * b5 +
|
||||
a9 * b4 + a10 * b3 + a11 * b2;
|
||||
s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + a9 * b5 +
|
||||
a10 * b4 + a11 * b3;
|
||||
s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + a10 * b5 +
|
||||
a11 * b4;
|
||||
s16 = a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5;
|
||||
s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6;
|
||||
s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7;
|
||||
s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8;
|
||||
s20 = a9 * b11 + a10 * b10 + a11 * b9;
|
||||
s21 = a10 * b11 + a11 * b10;
|
||||
s22 = a11 * b11;
|
||||
s23 = 0;
|
||||
|
||||
carry0 = (s0 + (1 << 20)) >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry2 = (s2 + (1 << 20)) >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry4 = (s4 + (1 << 20)) >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry6 = (s6 + (1 << 20)) >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry8 = (s8 + (1 << 20)) >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry10 = (s10 + (1 << 20)) >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
carry12 = (s12 + (1 << 20)) >> 21;
|
||||
s13 += carry12;
|
||||
s12 -= carry12 << 21;
|
||||
carry14 = (s14 + (1 << 20)) >> 21;
|
||||
s15 += carry14;
|
||||
s14 -= carry14 << 21;
|
||||
carry16 = (s16 + (1 << 20)) >> 21;
|
||||
s17 += carry16;
|
||||
s16 -= carry16 << 21;
|
||||
carry18 = (s18 + (1 << 20)) >> 21;
|
||||
s19 += carry18;
|
||||
s18 -= carry18 << 21;
|
||||
carry20 = (s20 + (1 << 20)) >> 21;
|
||||
s21 += carry20;
|
||||
s20 -= carry20 << 21;
|
||||
carry22 = (s22 + (1 << 20)) >> 21;
|
||||
s23 += carry22;
|
||||
s22 -= carry22 << 21;
|
||||
|
||||
carry1 = (s1 + (1 << 20)) >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry3 = (s3 + (1 << 20)) >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry5 = (s5 + (1 << 20)) >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry7 = (s7 + (1 << 20)) >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry9 = (s9 + (1 << 20)) >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry11 = (s11 + (1 << 20)) >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
carry13 = (s13 + (1 << 20)) >> 21;
|
||||
s14 += carry13;
|
||||
s13 -= carry13 << 21;
|
||||
carry15 = (s15 + (1 << 20)) >> 21;
|
||||
s16 += carry15;
|
||||
s15 -= carry15 << 21;
|
||||
carry17 = (s17 + (1 << 20)) >> 21;
|
||||
s18 += carry17;
|
||||
s17 -= carry17 << 21;
|
||||
carry19 = (s19 + (1 << 20)) >> 21;
|
||||
s20 += carry19;
|
||||
s19 -= carry19 << 21;
|
||||
carry21 = (s21 + (1 << 20)) >> 21;
|
||||
s22 += carry21;
|
||||
s21 -= carry21 << 21;
|
||||
|
||||
s11 += s23 * 666643;
|
||||
s12 += s23 * 470296;
|
||||
s13 += s23 * 654183;
|
||||
s14 -= s23 * 997805;
|
||||
s15 += s23 * 136657;
|
||||
s16 -= s23 * 683901;
|
||||
//s23 = 0;
|
||||
|
||||
s10 += s22 * 666643;
|
||||
s11 += s22 * 470296;
|
||||
s12 += s22 * 654183;
|
||||
s13 -= s22 * 997805;
|
||||
s14 += s22 * 136657;
|
||||
s15 -= s22 * 683901;
|
||||
//s22 = 0;
|
||||
|
||||
s9 += s21 * 666643;
|
||||
s10 += s21 * 470296;
|
||||
s11 += s21 * 654183;
|
||||
s12 -= s21 * 997805;
|
||||
s13 += s21 * 136657;
|
||||
s14 -= s21 * 683901;
|
||||
//s21 = 0;
|
||||
|
||||
s8 += s20 * 666643;
|
||||
s9 += s20 * 470296;
|
||||
s10 += s20 * 654183;
|
||||
s11 -= s20 * 997805;
|
||||
s12 += s20 * 136657;
|
||||
s13 -= s20 * 683901;
|
||||
//s20 = 0;
|
||||
|
||||
s7 += s19 * 666643;
|
||||
s8 += s19 * 470296;
|
||||
s9 += s19 * 654183;
|
||||
s10 -= s19 * 997805;
|
||||
s11 += s19 * 136657;
|
||||
s12 -= s19 * 683901;
|
||||
//s19 = 0;
|
||||
|
||||
s6 += s18 * 666643;
|
||||
s7 += s18 * 470296;
|
||||
s8 += s18 * 654183;
|
||||
s9 -= s18 * 997805;
|
||||
s10 += s18 * 136657;
|
||||
s11 -= s18 * 683901;
|
||||
//s18 = 0;
|
||||
|
||||
carry6 = (s6 + (1 << 20)) >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry8 = (s8 + (1 << 20)) >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry10 = (s10 + (1 << 20)) >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
carry12 = (s12 + (1 << 20)) >> 21;
|
||||
s13 += carry12;
|
||||
s12 -= carry12 << 21;
|
||||
carry14 = (s14 + (1 << 20)) >> 21;
|
||||
s15 += carry14;
|
||||
s14 -= carry14 << 21;
|
||||
carry16 = (s16 + (1 << 20)) >> 21;
|
||||
s17 += carry16;
|
||||
s16 -= carry16 << 21;
|
||||
|
||||
carry7 = (s7 + (1 << 20)) >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry9 = (s9 + (1 << 20)) >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry11 = (s11 + (1 << 20)) >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
carry13 = (s13 + (1 << 20)) >> 21;
|
||||
s14 += carry13;
|
||||
s13 -= carry13 << 21;
|
||||
carry15 = (s15 + (1 << 20)) >> 21;
|
||||
s16 += carry15;
|
||||
s15 -= carry15 << 21;
|
||||
|
||||
s5 += s17 * 666643;
|
||||
s6 += s17 * 470296;
|
||||
s7 += s17 * 654183;
|
||||
s8 -= s17 * 997805;
|
||||
s9 += s17 * 136657;
|
||||
s10 -= s17 * 683901;
|
||||
//s17 = 0;
|
||||
|
||||
s4 += s16 * 666643;
|
||||
s5 += s16 * 470296;
|
||||
s6 += s16 * 654183;
|
||||
s7 -= s16 * 997805;
|
||||
s8 += s16 * 136657;
|
||||
s9 -= s16 * 683901;
|
||||
//s16 = 0;
|
||||
|
||||
s3 += s15 * 666643;
|
||||
s4 += s15 * 470296;
|
||||
s5 += s15 * 654183;
|
||||
s6 -= s15 * 997805;
|
||||
s7 += s15 * 136657;
|
||||
s8 -= s15 * 683901;
|
||||
//s15 = 0;
|
||||
|
||||
s2 += s14 * 666643;
|
||||
s3 += s14 * 470296;
|
||||
s4 += s14 * 654183;
|
||||
s5 -= s14 * 997805;
|
||||
s6 += s14 * 136657;
|
||||
s7 -= s14 * 683901;
|
||||
//s14 = 0;
|
||||
|
||||
s1 += s13 * 666643;
|
||||
s2 += s13 * 470296;
|
||||
s3 += s13 * 654183;
|
||||
s4 -= s13 * 997805;
|
||||
s5 += s13 * 136657;
|
||||
s6 -= s13 * 683901;
|
||||
//s13 = 0;
|
||||
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
s12 = 0;
|
||||
|
||||
carry0 = (s0 + (1 << 20)) >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry2 = (s2 + (1 << 20)) >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry4 = (s4 + (1 << 20)) >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry6 = (s6 + (1 << 20)) >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry8 = (s8 + (1 << 20)) >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry10 = (s10 + (1 << 20)) >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
|
||||
carry1 = (s1 + (1 << 20)) >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry3 = (s3 + (1 << 20)) >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry5 = (s5 + (1 << 20)) >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry7 = (s7 + (1 << 20)) >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry9 = (s9 + (1 << 20)) >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry11 = (s11 + (1 << 20)) >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
s12 = 0;
|
||||
|
||||
carry0 = s0 >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry1 = s1 >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry2 = s2 >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry3 = s3 >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry4 = s4 >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry5 = s5 >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry6 = s6 >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry7 = s7 >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry8 = s8 >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry9 = s9 >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry10 = s10 >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
carry11 = s11 >> 21;
|
||||
s12 += carry11;
|
||||
s11 -= carry11 << 21;
|
||||
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
//s12 = 0;
|
||||
|
||||
carry0 = s0 >> 21;
|
||||
s1 += carry0;
|
||||
s0 -= carry0 << 21;
|
||||
carry1 = s1 >> 21;
|
||||
s2 += carry1;
|
||||
s1 -= carry1 << 21;
|
||||
carry2 = s2 >> 21;
|
||||
s3 += carry2;
|
||||
s2 -= carry2 << 21;
|
||||
carry3 = s3 >> 21;
|
||||
s4 += carry3;
|
||||
s3 -= carry3 << 21;
|
||||
carry4 = s4 >> 21;
|
||||
s5 += carry4;
|
||||
s4 -= carry4 << 21;
|
||||
carry5 = s5 >> 21;
|
||||
s6 += carry5;
|
||||
s5 -= carry5 << 21;
|
||||
carry6 = s6 >> 21;
|
||||
s7 += carry6;
|
||||
s6 -= carry6 << 21;
|
||||
carry7 = s7 >> 21;
|
||||
s8 += carry7;
|
||||
s7 -= carry7 << 21;
|
||||
carry8 = s8 >> 21;
|
||||
s9 += carry8;
|
||||
s8 -= carry8 << 21;
|
||||
carry9 = s9 >> 21;
|
||||
s10 += carry9;
|
||||
s9 -= carry9 << 21;
|
||||
carry10 = s10 >> 21;
|
||||
s11 += carry10;
|
||||
s10 -= carry10 << 21;
|
||||
|
||||
s[0] = (s0 >> 0) as u8;
|
||||
s[1] = (s0 >> 8) as u8;
|
||||
s[2] = ((s0 >> 16) | (s1 << 5)) as u8;
|
||||
s[3] = (s1 >> 3) as u8;
|
||||
s[4] = (s1 >> 11) as u8;
|
||||
s[5] = ((s1 >> 19) | (s2 << 2)) as u8;
|
||||
s[6] = (s2 >> 6) as u8;
|
||||
s[7] = ((s2 >> 14) | (s3 << 7)) as u8;
|
||||
s[8] = (s3 >> 1) as u8;
|
||||
s[9] = (s3 >> 9) as u8;
|
||||
s[10] = ((s3 >> 17) | (s4 << 4)) as u8;
|
||||
s[11] = (s4 >> 4) as u8;
|
||||
s[12] = (s4 >> 12) as u8;
|
||||
s[13] = ((s4 >> 20) | (s5 << 1)) as u8;
|
||||
s[14] = (s5 >> 7) as u8;
|
||||
s[15] = ((s5 >> 15) | (s6 << 6)) as u8;
|
||||
s[16] = (s6 >> 2) as u8;
|
||||
s[17] = (s6 >> 10) as u8;
|
||||
s[18] = ((s6 >> 18) | (s7 << 3)) as u8;
|
||||
s[19] = (s7 >> 5) as u8;
|
||||
s[20] = (s7 >> 13) as u8;
|
||||
s[21] = (s8 >> 0) as u8;
|
||||
s[22] = (s8 >> 8) as u8;
|
||||
s[23] = ((s8 >> 16) | (s9 << 5)) as u8;
|
||||
s[24] = (s9 >> 3) as u8;
|
||||
s[25] = (s9 >> 11) as u8;
|
||||
s[26] = ((s9 >> 19) | (s10 << 2)) as u8;
|
||||
s[27] = (s10 >> 6) as u8;
|
||||
s[28] = ((s10 >> 14) | (s11 << 7)) as u8;
|
||||
s[29] = (s11 >> 1) as u8;
|
||||
s[30] = (s11 >> 9) as u8;
|
||||
s[31] = (s11 >> 17) as u8;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn muladd() {
|
||||
let fname = "testdata/ed25519/muladd.test";
|
||||
run_test(fname.to_string(), 4, |case| {
|
||||
let (nega, abytes) = case.get("a").unwrap();
|
||||
let (negb, bbytes) = case.get("b").unwrap();
|
||||
let (negc, cbytes) = case.get("c").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!nega && !negb && !negc && !negd);
|
||||
let mut mine = [0; 32];
|
||||
x25519_sc_muladd(&mut mine, abytes, bbytes, cbytes);
|
||||
for i in 0..32 {
|
||||
assert_eq!(&mine[i], &dbytes[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn curve25519_scalar_mask(a: &mut [u8])
|
||||
{
|
||||
assert_eq!(a.len(), 32);
|
||||
a[0] &= 248;
|
||||
a[31] &= 127;
|
||||
a[31] |= 64;
|
||||
}
|
||||
|
||||
196
src/hmac.rs
Normal file
196
src/hmac.rs
Normal file
@@ -0,0 +1,196 @@
|
||||
//! This module implements the Keyed-Hash Message Authentication Code, or HMAC,
|
||||
//! as defined by NIST 198-1. Now, you might have questions, like:
|
||||
//! * *Where did the 'K' go in the acronym?* I don't know. Maybe we should
|
||||
//! always be saying Keyed-HMAC? It's a mystery.
|
||||
//! * *What is this good for?* I do know the answer to that! HMACs are
|
||||
//! useful when you want to extend the ability of a hash to tell you if
|
||||
//! a message has been modified with the ability to determine if the
|
||||
//! person that sent it had hold of a key. It's thus a version of the
|
||||
//! message signing capability used in asymmetric crypto (`DSA`, `RSA`,
|
||||
//! `ECDSA`, and `ED25519`, as implemented in this crate), but with a
|
||||
//! symmetric key, instead.
|
||||
//!
|
||||
//! Because HMAC can be used with a variety of hash functions, this module
|
||||
//! implements it as a generic structure that takes the associated hash as
|
||||
//! a type argument. This should provide a reasonable level of flexibility,
|
||||
//! while allowing the type system from preventing us from making any number
|
||||
//! of really annoying mistakes. You can specify which of the hash functions
|
||||
//! you want to use by using your standard turbofish:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use simple_crypto::hmac::HMAC;
|
||||
//! use simple_crypto::sha::SHA256;
|
||||
//!
|
||||
//! let key = [0,1,2,3,4]; // very secure
|
||||
//! let msg = [5,6,7,8];
|
||||
//! let hmac = HMAC::<SHA256>::hmac(&key, &msg);
|
||||
//! ```
|
||||
//!
|
||||
//! Much like with `SHAKE128` and `SHAKE256` the interface for HMAC is
|
||||
//! similar to, but not quite, the interface for `Hash`. We thus try to
|
||||
//! copy as much of the standard `Hash` interface as we can, but extend
|
||||
//! `new` with a key, rename `hash` to `hmac`, and extend `hmac` with a
|
||||
//! key as well. This provides a similar ability to use HMACs both in an
|
||||
//! incremental mode as well as just do it all at once, as follows:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use simple_crypto::hmac::HMAC;
|
||||
//! use simple_crypto::sha::SHA256;
|
||||
//!
|
||||
//! let key = [0,1,2,3,4]; // like my suitcase
|
||||
//! let msg = [5,6,7,8];
|
||||
//!
|
||||
//! // Compute the HMAC incrementally
|
||||
//! let mut hmacinc = HMAC::<SHA256>::new(&key);
|
||||
//! hmacinc.update(&[5,6]);
|
||||
//! hmacinc.update(&[7,8]);
|
||||
//! let hmac_incremental = hmacinc.finalize();
|
||||
//!
|
||||
//! // Compute the HMAC all at once
|
||||
//! let hmac_once = HMAC::<SHA256>::hmac(&key, &msg);
|
||||
//!
|
||||
//! // ... which should be the same thing
|
||||
//! assert_eq!(hmac_incremental, hmac_once);
|
||||
//! ```
|
||||
|
||||
/// The HMAC structure, parameterized by its hash function.
|
||||
///
|
||||
/// Much like with `SHAKE128` and `SHAKE256` the interface for HMAC is
|
||||
/// similar to, but not quite, the interface for `Hash`. We thus try to
|
||||
/// copy as much of the standard `Hash` interface as we can, but extend
|
||||
/// `new` with a key, rename `hash` to `hmac`, and extend `hmac` with a
|
||||
/// key as well. This provides a similar ability to use HMACs both in an
|
||||
/// incremental mode as well as just do it all at once, as follows:
|
||||
///
|
||||
/// ```rust
|
||||
/// use simple_crypto::hmac::HMAC;
|
||||
/// use simple_crypto::sha::SHA256;
|
||||
///
|
||||
/// let key = [0,1,2,3,4]; // like my suitcase
|
||||
/// let msg = [5,6,7,8];
|
||||
///
|
||||
/// // Compute the HMAC incrementally
|
||||
/// let mut hmacinc = HMAC::<SHA256>::new(&key);
|
||||
/// hmacinc.update(&[5,6]);
|
||||
/// hmacinc.update(&[7,8]);
|
||||
/// let hmac_incremental = hmacinc.finalize();
|
||||
///
|
||||
/// // Compute the HMAC all at once
|
||||
/// let hmac_once = HMAC::<SHA256>::hmac(&key, &msg);
|
||||
///
|
||||
/// // ... which should be the same thing
|
||||
/// assert_eq!(hmac_incremental, hmac_once);
|
||||
/// ```
|
||||
use super::Hash;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HMAC<H: Hash + Clone> {
|
||||
ipad_hash: H,
|
||||
opad_hash: H,
|
||||
result: Option<Vec<u8>>
|
||||
}
|
||||
|
||||
impl<H: Hash + Clone> HMAC<H> {
|
||||
/// Generate a new HMAC construction for the provide underlying hash
|
||||
/// function, and prep it to start taking input via the `update`
|
||||
/// method.
|
||||
pub fn new(inkey: &[u8]) -> Self {
|
||||
let hash_blocklen_bytes = H::block_size() / 8;
|
||||
|
||||
// If the input key is longer than the hash block length, then we
|
||||
// immediately hash it down to be the block length. Otherwise, we
|
||||
// leave it be.
|
||||
let mut key = if inkey.len() > hash_blocklen_bytes { H::hash(inkey) }
|
||||
else { inkey.to_vec() };
|
||||
// It may now be too small, or have started too small, in which case
|
||||
// we pad it out with zeros.
|
||||
key.resize(hash_blocklen_bytes, 0);
|
||||
// Generate the inner and outer key pad from this key.
|
||||
let o_key_pad: Vec<u8> = key.iter().map(|x| *x ^ 0x5c).collect();
|
||||
let i_key_pad: Vec<u8> = key.iter().map(|x| *x ^ 0x36).collect();
|
||||
// Now we can start the hashes; obviously we'll have to wait
|
||||
// until we get the rest of the message to complete them.
|
||||
let mut ipad_hash = H::new();
|
||||
ipad_hash.update(&i_key_pad);
|
||||
let mut opad_hash = H::new();
|
||||
opad_hash.update(&o_key_pad);
|
||||
let result = None;
|
||||
HMAC { ipad_hash, opad_hash, result }
|
||||
}
|
||||
|
||||
/// Add more data as part of the HMAC computation. This can be called
|
||||
/// zero or more times over the lifetime of the HMAC structure. That
|
||||
/// being said, once you call `finalize`, this structure is done, and
|
||||
/// it will ignore further calls to `update`.
|
||||
pub fn update(&mut self, buffer: &[u8])
|
||||
{
|
||||
if self.result.is_none() {
|
||||
self.ipad_hash.update(&buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide the final HMAC value for the bitrstream as read. This shifts
|
||||
/// this structure into a final mode, in which it will ignore any more
|
||||
/// data provided to it from `update`. You can, however, call `finalize`
|
||||
/// more than once; the HMAC structure caches the return value and will
|
||||
/// return it as many times as you like.
|
||||
pub fn finalize(&mut self) -> Vec<u8>
|
||||
{
|
||||
if let Some(ref res) = self.result {
|
||||
res.clone()
|
||||
} else {
|
||||
self.opad_hash.update(&self.ipad_hash.finalize());
|
||||
let res = self.opad_hash.finalize();
|
||||
self.result = Some(res.clone());
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// A useful method for those situations in which you have only one block
|
||||
/// of data to generate an HMAC for. Runs `new`, `update`, and `finalize`
|
||||
/// for you, in order.
|
||||
pub fn hmac(key: &[u8], val: &[u8]) -> Vec<u8>
|
||||
{
|
||||
let mut h = Self::new(key);
|
||||
h.update(val);
|
||||
h.finalize()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use sha::{SHA1,SHA224,SHA256,SHA384,SHA512};
|
||||
#[cfg(test)]
|
||||
use testing::run_test;
|
||||
#[cfg(test)]
|
||||
use cryptonum::unsigned::{Decoder,U192};
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn nist_vectors() {
|
||||
let fname = "testdata/sha/hmac.test";
|
||||
run_test(fname.to_string(), 6, |case| {
|
||||
let (negh, hbytes) = case.get("h").unwrap();
|
||||
let (negr, rbytes) = case.get("r").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negk, kbytes) = case.get("k").unwrap();
|
||||
let (negl, lbytes) = case.get("l").unwrap();
|
||||
let (negt, tbytes) = case.get("t").unwrap();
|
||||
|
||||
assert!(!negh && !negr && !negm && !negk && !negl && !negt);
|
||||
let l = usize::from(U192::from_bytes(lbytes));
|
||||
let h = usize::from(U192::from_bytes(hbytes));
|
||||
assert_eq!(l, kbytes.len());
|
||||
let mut res = match h {
|
||||
160 => HMAC::<SHA1>::hmac(&kbytes, &mbytes),
|
||||
224 => HMAC::<SHA224>::hmac(&kbytes, &mbytes),
|
||||
256 => HMAC::<SHA256>::hmac(&kbytes, &mbytes),
|
||||
384 => HMAC::<SHA384>::hmac(&kbytes, &mbytes),
|
||||
512 => HMAC::<SHA512>::hmac(&kbytes, &mbytes),
|
||||
_ => panic!("Weird hash size in HMAC test file")
|
||||
};
|
||||
let t = usize::from(U192::from_bytes(tbytes));
|
||||
res.resize(t, 0);
|
||||
assert_eq!(rbytes, &res);
|
||||
});
|
||||
}
|
||||
|
||||
94
src/lib.rs
94
src/lib.rs
@@ -1,4 +1,3 @@
|
||||
#![feature(i128_type)]
|
||||
//! # Simple Crypto: A quaint little crypto library for rust.
|
||||
//!
|
||||
//! This is the simple_crypto library. Its goal is to provide straightforward
|
||||
@@ -10,31 +9,94 @@
|
||||
//! that a new user should use, along with documentation regarding how and
|
||||
//! when they should use it, and examples. For now, it mostly just fowards
|
||||
//! off to more detailed modules. Help requested!
|
||||
|
||||
extern crate base64;
|
||||
extern crate byteorder;
|
||||
extern crate digest;
|
||||
extern crate chrono;
|
||||
extern crate cryptonum;
|
||||
extern crate num;
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate quickcheck;
|
||||
extern crate rand;
|
||||
extern crate sha1;
|
||||
extern crate sha2;
|
||||
#[macro_use]
|
||||
extern crate simple_asn1;
|
||||
|
||||
/// The cryptonum module provides support for large numbers at fixed,
|
||||
/// cryptographically-relevant sizes.
|
||||
pub mod cryptonum;
|
||||
/// The RSA module performs the basic operations for RSA, and should
|
||||
/// be used directly only if you're fairly confident about what you're
|
||||
/// doing.
|
||||
/// The `aes` module provides bare-bones AES support.
|
||||
pub mod aes;
|
||||
/// The `rsa` module provides bare-bones support for RSA signing, verification,
|
||||
/// encryption, decryption, and key generation.
|
||||
pub mod rsa;
|
||||
/// The `dsa` module provides bare-bones support for DSA signing, verification,
|
||||
/// and key generation. You shouldn't need to use these if you're building a
|
||||
/// new system, but might need to use them to interact with legacy systems or
|
||||
/// protocols.
|
||||
pub mod dsa;
|
||||
/// The `ecdsa` module provides bare-bones support for ECDSA signing,
|
||||
/// verification, and key generation.
|
||||
pub mod ecdsa;
|
||||
/// The `ed25519` provides signing and verification using ED25519.
|
||||
pub mod ed25519;
|
||||
/// The `ssh` module provides support for parsing OpenSSH-formatted SSH keys,
|
||||
/// both public and private.
|
||||
pub mod ssh;
|
||||
/// The `shake` module provides support for SHAKE128 and SHAKE256, two
|
||||
/// variable-length hash functions that derive from the same core hash
|
||||
/// as SHA3.
|
||||
pub mod shake;
|
||||
/// The `hmac` module provides support for keyed-hash message authentication,
|
||||
/// or HMAC, based on any of the hash functions defined in this module.
|
||||
pub mod hmac;
|
||||
/// The `x509` module supports parsing and generating x.509 certificates, as
|
||||
/// used by TLS and others.
|
||||
pub mod x509;
|
||||
/// An implementation of the SHA family of hashes, including the relatively
|
||||
/// weak SHA1 and a bunch of hashes you should use, like the SHA2 and SHA3
|
||||
/// hashes.
|
||||
pub mod sha;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
/// A generic trait for defining what a key pair looks like. This is useful
|
||||
/// in a couple places in which we want to define code regardless of the
|
||||
/// kind of key it is, but is unlikely to be hugely useful to users of the
|
||||
/// library.
|
||||
pub trait KeyPair {
|
||||
/// The type of the public key of this pair.
|
||||
type Public;
|
||||
/// The type of the private key of this pair.
|
||||
type Private;
|
||||
|
||||
#[test]
|
||||
fn testing_works() {
|
||||
assert!(true);
|
||||
/// Generate a key pair given the provided public and private keys.
|
||||
fn new(pbl: Self::Public, prv: Self::Private) -> Self;
|
||||
}
|
||||
|
||||
/// A generic trait for defining a hash function.
|
||||
pub trait Hash: Sized
|
||||
{
|
||||
/// Generate a fresh instance of this hash function, set to the
|
||||
/// appropriate initial state.
|
||||
fn new() -> Self;
|
||||
/// Update the hash function with some more data for it to chew on.
|
||||
/// Nom nom nom. If you give it more information after calling
|
||||
/// `finalize`, the implementation is welcome to do anything it
|
||||
/// wants; mostly they will just ignore additional data, but
|
||||
/// maybe just don't do that.
|
||||
fn update(&mut self, data: &[u8]);
|
||||
/// Finalize the hash function, returning the hash value.
|
||||
fn finalize(&mut self) -> Vec<u8>;
|
||||
/// Return the block size of the underlying hash function, in
|
||||
/// bits. This is mostly useful internally to this crate.
|
||||
fn block_size() -> usize;
|
||||
|
||||
/// This is a convenience routine that runs new(), update(), and
|
||||
/// finalize() on a piece of data all at once. Because that's
|
||||
/// mostly what people want to do.
|
||||
fn hash(data: &[u8]) -> Vec<u8>
|
||||
{
|
||||
let mut x = Self::new();
|
||||
x.update(&data);
|
||||
x.finalize()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod testing;
|
||||
mod utils;
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
use cryptonum::{CryptoNumModOps};
|
||||
use num::BigUint;
|
||||
use cryptonum::unsigned::*;
|
||||
use num::bigint::BigUint;
|
||||
use rsa::errors::RSAError;
|
||||
use simple_asn1::{ASN1DecodeErr,ASN1Block};
|
||||
use simple_asn1::{ASN1Block,ASN1DecodeErr};
|
||||
|
||||
// encoding PKCS1 stuff
|
||||
pub fn pkcs1_pad(ident: &[u8], hash: &[u8], keylen: usize) -> Vec<u8> {
|
||||
/// A valid key size for RSA keys, basically, and a (slightly annoying)
|
||||
/// trait that is used to tie these types to their Barrett value types.
|
||||
pub trait RSAMode {
|
||||
type Barrett;
|
||||
}
|
||||
|
||||
impl RSAMode for U512 { type Barrett = BarrettU512; }
|
||||
impl RSAMode for U1024 { type Barrett = BarrettU1024; }
|
||||
impl RSAMode for U2048 { type Barrett = BarrettU2048; }
|
||||
impl RSAMode for U3072 { type Barrett = BarrettU3072; }
|
||||
impl RSAMode for U4096 { type Barrett = BarrettU4096; }
|
||||
impl RSAMode for U8192 { type Barrett = BarrettU8192; }
|
||||
impl RSAMode for U15360 { type Barrett = BarrettU15360; }
|
||||
|
||||
|
||||
pub fn pkcs1_pad(ident: &[u8], hash: &[u8], keylen: usize) -> Vec<u8>
|
||||
{
|
||||
let mut idhash = Vec::new();
|
||||
idhash.extend_from_slice(ident);
|
||||
idhash.extend_from_slice(hash);
|
||||
@@ -12,26 +27,25 @@ pub fn pkcs1_pad(ident: &[u8], hash: &[u8], keylen: usize) -> Vec<u8> {
|
||||
assert!(keylen > (tlen + 3));
|
||||
let mut padding = Vec::new();
|
||||
padding.resize(keylen - tlen - 3, 0xFF);
|
||||
let mut result = vec![0x00, 0x01];
|
||||
let mut result = vec![0x00,0x01];
|
||||
result.append(&mut padding);
|
||||
result.push(0x00);
|
||||
result.append(&mut idhash);
|
||||
result
|
||||
}
|
||||
|
||||
// the RSA encryption function
|
||||
pub fn ep<U: CryptoNumModOps>(n: &U, e: &U, m: &U) -> U {
|
||||
m.modexp(e, n)
|
||||
pub fn drop0s(a: &[u8]) -> &[u8] {
|
||||
let mut idx = 0;
|
||||
|
||||
while (idx < a.len()) && (a[idx] == 0) {
|
||||
idx = idx + 1;
|
||||
}
|
||||
|
||||
&a[idx..]
|
||||
}
|
||||
|
||||
// the RSA decryption function
|
||||
pub fn dp<U: CryptoNumModOps>(n: &U, d: &U, c: &U) -> U {
|
||||
c.modexp(d, n)
|
||||
}
|
||||
|
||||
// the RSA signature generation function
|
||||
pub fn sp1<U: CryptoNumModOps>(n: &U, d: &U, m: &U) -> U {
|
||||
m.modexp(d, n)
|
||||
pub fn xor_vecs(a: &Vec<u8>, b: &Vec<u8>) -> Vec<u8> {
|
||||
a.iter().zip(b.iter()).map(|(a,b)| a^b).collect()
|
||||
}
|
||||
|
||||
pub fn decode_biguint(b: &ASN1Block) -> Result<BigUint,RSAError> {
|
||||
@@ -48,12 +62,3 @@ pub fn decode_biguint(b: &ASN1Block) -> Result<BigUint,RSAError> {
|
||||
}
|
||||
|
||||
|
||||
// the RSA signature verification function
|
||||
pub fn vp1<U: CryptoNumModOps>(n: &U, e: &U, s: &U) -> U {
|
||||
s.modexp(e, n)
|
||||
}
|
||||
|
||||
pub fn xor_vecs(a: &Vec<u8>, b: &Vec<u8>) -> Vec<u8> {
|
||||
a.iter().zip(b.iter()).map(|(a,b)| a^b).collect()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
use simple_asn1::{ASN1DecodeErr};
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RSAKeyGenError {
|
||||
InvalidKeySize(usize), RngFailure(io::Error)
|
||||
}
|
||||
|
||||
impl From<io::Error> for RSAKeyGenError {
|
||||
fn from(e: io::Error) -> RSAKeyGenError {
|
||||
RSAKeyGenError::RngFailure(e)
|
||||
}
|
||||
}
|
||||
use simple_asn1::ASN1DecodeErr;
|
||||
use rand;
|
||||
|
||||
/// A bunch of errors that you can get generating, reading, or
|
||||
/// writing RSA keys.
|
||||
#[derive(Debug)]
|
||||
pub enum RSAError {
|
||||
BadMessageSize,
|
||||
@@ -19,13 +10,12 @@ pub enum RSAError {
|
||||
DecryptionError,
|
||||
DecryptHashMismatch,
|
||||
InvalidKey,
|
||||
KeySizeMismatch,
|
||||
RandomGenError(io::Error),
|
||||
RandomGenError(rand::Error),
|
||||
ASN1DecodeErr(ASN1DecodeErr)
|
||||
}
|
||||
|
||||
impl From<io::Error> for RSAError {
|
||||
fn from(e: io::Error) -> RSAError {
|
||||
impl From<rand::Error> for RSAError {
|
||||
fn from(e: rand::Error) -> RSAError {
|
||||
RSAError::RandomGenError(e)
|
||||
}
|
||||
}
|
||||
|
||||
523
src/rsa/mod.rs
523
src/rsa/mod.rs
@@ -1,10 +1,54 @@
|
||||
//! # An implementation of RSA.
|
||||
//! A simple RSA library.
|
||||
//!
|
||||
//! This module is designed to provide implementations of the core routines
|
||||
//! used for asymmetric cryptography using RSA. It probably provides a bit
|
||||
//! more flexibility than beginners should play with, and definitely includes
|
||||
//! some capabilities largely targeted at legacy systems. New users should
|
||||
//! probably stick with the stuff in the root of this crate.
|
||||
//! This library performs all the standard bits and pieces that you'd expect
|
||||
//! from an RSA library, and does so using only Rust. It's a bit slow at the
|
||||
//! moment, but it gets the job done.
|
||||
//!
|
||||
//! Key generation is supported, using either the native `OsRng` or a random
|
||||
//! number generator of your choice. Obviously, you should be careful to use
|
||||
//! a cryptographically-sound random number generator sufficient for the
|
||||
//! security level you're going for.
|
||||
//!
|
||||
//! Signing and verification are via standard PKCS1 padding, but can be
|
||||
//! adjusted based on the exact hash you want. This library also supports
|
||||
//! somewhat arbitrary signing mechanisms used by your weirder network
|
||||
//! protocols. (I'm looking at you, Tor.)
|
||||
//!
|
||||
//! Encryption and decryption are via the OAEP mechanism, as described in
|
||||
//! NIST documents.
|
||||
//!
|
||||
//! The following is an example of generating an RSA2048 key pair, then using
|
||||
//! it to sign, verify, encrypt, and decrypt some data.
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate cryptonum;
|
||||
//!
|
||||
//! use simple_crypto::rsa::RSAKeyPair;
|
||||
//! use simple_crypto::rsa::SIGNING_HASH_SHA256;
|
||||
//! use simple_crypto::rsa::OAEPParams;
|
||||
//! use simple_crypto::sha::SHA256;
|
||||
//! use cryptonum::unsigned::U2048;
|
||||
//!
|
||||
//! // Generate a new RSA with key size 2048. (This is an acceptable but
|
||||
//! // not great key size, but is a nice compromise given that this little
|
||||
//! // example runs as part of the test suite.)
|
||||
//! let mut rng = rand::rngs::OsRng::new().unwrap();
|
||||
//! let kp = RSAKeyPair::<U2048>::generate(&mut rng);
|
||||
//!
|
||||
//! // Now that you have this key pair, you can sign and verify messages
|
||||
//! // using it. For example, to sign the vector [0,1,2,3,4] with SHA256
|
||||
//! // and then verify that signature, we would write:
|
||||
//! let msg = vec![0,1,2,3,4];
|
||||
//! let sig = kp.private.sign(&SIGNING_HASH_SHA256, &msg);
|
||||
//! assert!( kp.public.verify(&SIGNING_HASH_SHA256, &msg, &sig) );
|
||||
//!
|
||||
//! // We can also use RSA public keys to encrypt data, which can then be
|
||||
//! // decrypted by the private key.
|
||||
//! let params = OAEPParams::<SHA256>::new(String::from("example!"));
|
||||
//! let cipher = kp.public.encrypt(¶ms, &msg).expect("Encryption error");
|
||||
//! let msg2 = kp.private.decrypt(¶ms, &cipher).expect("Decryption error");
|
||||
//! assert_eq!(msg, msg2);
|
||||
//! ```
|
||||
mod core;
|
||||
mod errors;
|
||||
mod oaep;
|
||||
@@ -12,217 +56,284 @@ mod private;
|
||||
mod public;
|
||||
mod signing_hashes;
|
||||
|
||||
use cryptonum::*;
|
||||
use rand::{OsRng,Rng};
|
||||
use std::cmp::PartialOrd;
|
||||
use std::ops::*;
|
||||
|
||||
pub use self::errors::{RSAKeyGenError,RSAError};
|
||||
pub use self::oaep::{OAEPParams};
|
||||
pub use self::private::RSAPrivateKey;
|
||||
pub use self::public::RSAPublicKey;
|
||||
pub use self::core::RSAMode;
|
||||
pub use self::errors::RSAError;
|
||||
pub use self::signing_hashes::{SigningHash,
|
||||
SIGNING_HASH_NULL, SIGNING_HASH_SHA1,
|
||||
SIGNING_HASH_SHA224, SIGNING_HASH_SHA256,
|
||||
SIGNING_HASH_SHA384, SIGNING_HASH_SHA512};
|
||||
SIGNING_HASH_NULL,
|
||||
SIGNING_HASH_SHA1,
|
||||
SIGNING_HASH_SHA224,
|
||||
SIGNING_HASH_SHA256,
|
||||
SIGNING_HASH_SHA384,
|
||||
SIGNING_HASH_SHA512};
|
||||
pub use self::oaep::OAEPParams;
|
||||
pub use self::private::{RSAPrivate, RSAPrivateKey};
|
||||
pub use self::public::{RSAPublic, RSAPublicKey};
|
||||
use cryptonum::signed::{EGCD,ModInv};
|
||||
use cryptonum::unsigned::{CryptoNum,PrimeGen};
|
||||
use cryptonum::unsigned::{U256,U512,U1024,U1536,U2048,U3072,U4096,U7680,U8192,U15360};
|
||||
use rand::RngCore;
|
||||
#[cfg(test)]
|
||||
use std::fmt;
|
||||
use std::ops::Sub;
|
||||
use super::KeyPair;
|
||||
|
||||
/// An RSA public and private key.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct RSAKeyPair<Size>
|
||||
fn diff<T>(a: &T, b: &T) -> T
|
||||
where
|
||||
Size: CryptoNumBase + CryptoNumSerialization
|
||||
{
|
||||
pub private: RSAPrivateKey<Size>,
|
||||
pub public: RSAPublicKey<Size>
|
||||
}
|
||||
|
||||
impl<T> RSAKeyPair<T>
|
||||
where
|
||||
T: Clone + Sized + PartialOrd + From<u64>,
|
||||
T: CryptoNumBase + CryptoNumModOps + CryptoNumPrimes + CryptoNumSerialization,
|
||||
T: Sub<Output=T> + Mul<Output=T> + Shl<usize,Output=T>
|
||||
{
|
||||
/// Generates a fresh RSA key pair. If you actually want to protect data,
|
||||
/// use a value greater than or equal to 2048. If you don't want to spend
|
||||
/// all day waiting for RSA computations to finish, choose a value less
|
||||
/// than or equal to 4096.
|
||||
///
|
||||
/// This routine will use `OsRng` for entropy. If you want to use your
|
||||
/// own random number generator, use `generate_w_rng`.
|
||||
pub fn generate() -> Result<RSAKeyPair<T>,RSAKeyGenError> {
|
||||
let mut rng = OsRng::new()?;
|
||||
RSAKeyPair::<T>::generate_w_rng(&mut rng)
|
||||
}
|
||||
|
||||
/// Generates a fresh RSA key pair of the given bit size. Valid bit sizes
|
||||
/// are 512, 1024, 2048, 3072, 4096, 7680, 8192, and 15360. If you
|
||||
/// actually want to protect data, use a value greater than or equal to
|
||||
/// 2048. If you don't want to spend all day waiting for RSA computations
|
||||
/// to finish, choose a value less than or equal to 4096.
|
||||
///
|
||||
/// If you provide your own random number generator that is not `OsRng`,
|
||||
/// you should know what you're doing, and be using a cryptographically-
|
||||
/// strong RNG of your own choosing. We've warned you. Use a good one.
|
||||
/// So now it's on you.
|
||||
pub fn generate_w_rng<G: Rng>(rng: &mut G)
|
||||
-> Result<RSAKeyPair<T>,RSAKeyGenError>
|
||||
{
|
||||
let e = T::from(65537);
|
||||
let len_bits = e.bit_size();
|
||||
match generate_pq(rng, &e) {
|
||||
None =>
|
||||
return Err(RSAKeyGenError::InvalidKeySize(len_bits)),
|
||||
Some((p, q)) => {
|
||||
let n = p.clone() * q.clone();
|
||||
let phi = (p - T::from(1)) * (q - T::from(1));
|
||||
let d = e.modinv(&phi);
|
||||
let public_key = RSAPublicKey::new(n.clone(), e);
|
||||
let private_key = RSAPrivateKey::new(n, d);
|
||||
return Ok(RSAKeyPair{ private: private_key, public: public_key })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_pq<'a,G,T>(rng: &mut G, e: &T) -> Option<(T,T)>
|
||||
where
|
||||
G: Rng,
|
||||
T: Clone + PartialOrd + Shl<usize,Output=T> + Sub<Output=T> + From<u64>,
|
||||
T: CryptoNumBase + CryptoNumPrimes + CryptoNumSerialization
|
||||
{
|
||||
let bitlen = T::zero().bit_size();
|
||||
let mindiff = T::from(1) << ((bitlen/2)-101);
|
||||
let minval = T::from(6074001000) << ((mindiff.bit_size()/2) - 33);
|
||||
let p = T::generate_prime(rng, 7, e, &minval);
|
||||
|
||||
loop {
|
||||
let q = T::generate_prime(rng, 7, e, &minval);
|
||||
|
||||
if diff(p.clone(), q.clone()) >= mindiff {
|
||||
return Some((p, q));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn diff<T: PartialOrd + Sub<Output=T>>(a: T, b: T) -> T
|
||||
T: Clone + PartialOrd,
|
||||
T: Sub<T,Output=T>
|
||||
{
|
||||
if a > b {
|
||||
a - b
|
||||
a.clone() - b.clone()
|
||||
} else {
|
||||
b - a
|
||||
b.clone() - a.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// An RSA key pair containing keys of the given size; keeping them in the
|
||||
/// type means you'll never forget which one you have.
|
||||
///
|
||||
/// As an aside:
|
||||
/// * `U512` should only be used for testing
|
||||
/// * `U1024` should only be used to support old protocols or devices
|
||||
/// * `U2048` is probably your bare minimum
|
||||
/// * `U3072` is a very reasonable choice
|
||||
/// * **`U4096` is what you should use**
|
||||
/// * `U8192` is starting to get a bit silly (and slow)
|
||||
/// * `U15360` is for when you're using encryption to heat your house or server room
|
||||
pub struct RSAKeyPair<R: RSAMode> {
|
||||
pub public: RSAPublicKey<R>,
|
||||
pub private: RSAPrivateKey<R>
|
||||
}
|
||||
|
||||
/// A generic RSA key pair that is agnostic about its key size. It's not
|
||||
/// totally clear why this is useful, at this point.
|
||||
#[derive(PartialEq)]
|
||||
pub enum RSAPair {
|
||||
R512(RSAPublicKey<U512>, RSAPrivateKey<U512>),
|
||||
R1024(RSAPublicKey<U1024>, RSAPrivateKey<U1024>),
|
||||
R2048(RSAPublicKey<U2048>, RSAPrivateKey<U2048>),
|
||||
R3072(RSAPublicKey<U3072>, RSAPrivateKey<U3072>),
|
||||
R4096(RSAPublicKey<U4096>, RSAPrivateKey<U4096>),
|
||||
R8192(RSAPublicKey<U8192>, RSAPrivateKey<U8192>),
|
||||
R15360(RSAPublicKey<U15360>, RSAPrivateKey<U15360>),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use rsa::core::{dp,ep,sp1,vp1};
|
||||
use sha2::Sha224;
|
||||
use simple_asn1::{der_decode,der_encode};
|
||||
use super::*;
|
||||
|
||||
impl Arbitrary for RSAKeyPair<U512> {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> RSAKeyPair<U512> {
|
||||
RSAKeyPair::generate_w_rng(g).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// Core primitive checks
|
||||
quickcheck! {
|
||||
fn ep_dp_inversion(kp: RSAKeyPair<U512>, m: U512) -> bool {
|
||||
let realm = &m % &kp.public.n;
|
||||
let ciphertext = ep(&kp.public.n, &kp.public.e, &realm);
|
||||
let mprime = dp(&kp.private.n, &kp.private.d, &ciphertext);
|
||||
mprime == m
|
||||
}
|
||||
fn sp_vp_inversion(kp: RSAKeyPair<U512>, m: U512) -> bool {
|
||||
let realm = &m % &kp.public.n;
|
||||
let sig = sp1(&kp.private.n, &kp.private.d, &realm);
|
||||
let mprime = vp1(&kp.public.n, &kp.public.e, &sig);
|
||||
mprime == m
|
||||
}
|
||||
}
|
||||
|
||||
// Public key serialization
|
||||
quickcheck! {
|
||||
fn asn1_encoding_inverts(kp: RSAKeyPair<U512>) -> bool {
|
||||
let bytes = der_encode(&kp.public).unwrap();
|
||||
let pubkey: RSAPublicKey<U512> = der_decode(&bytes).unwrap();
|
||||
(pubkey.n == kp.public.n) && (pubkey.e == kp.public.e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
struct Message {
|
||||
m: Vec<u8>
|
||||
}
|
||||
|
||||
impl Arbitrary for Message {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> Message {
|
||||
let len = 1 + (g.gen::<u8>() % 3);
|
||||
let mut storage = Vec::new();
|
||||
for _ in 0..len {
|
||||
storage.push(g.gen::<u8>());
|
||||
}
|
||||
Message{ m: storage }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
struct KeyPairAndSigHash<T>
|
||||
where
|
||||
T: CryptoNumSerialization + CryptoNumBase
|
||||
impl fmt::Debug for RSAPair {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>
|
||||
{
|
||||
kp: RSAKeyPair<T>,
|
||||
sh: &'static SigningHash
|
||||
}
|
||||
|
||||
impl<T> Arbitrary for KeyPairAndSigHash<T>
|
||||
where
|
||||
T: Clone + Sized + PartialOrd + From<u64>,
|
||||
T: CryptoNumBase + CryptoNumModOps,
|
||||
T: CryptoNumPrimes + CryptoNumSerialization,
|
||||
T: Sub<Output=T> + Mul<Output=T> + Shl<usize,Output=T>,
|
||||
RSAKeyPair<T>: Arbitrary
|
||||
{
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> KeyPairAndSigHash<T> {
|
||||
let kp = RSAKeyPair::generate_w_rng(g).unwrap();
|
||||
let size = kp.public.n.bit_size();
|
||||
let mut hashes = vec![&SIGNING_HASH_SHA1];
|
||||
|
||||
if size >= 1024 {
|
||||
hashes.push(&SIGNING_HASH_SHA224);
|
||||
}
|
||||
|
||||
if size >= 2048 {
|
||||
hashes.push(&SIGNING_HASH_SHA256);
|
||||
}
|
||||
|
||||
if size >= 4096 {
|
||||
hashes.push(&SIGNING_HASH_SHA384);
|
||||
hashes.push(&SIGNING_HASH_SHA512);
|
||||
}
|
||||
|
||||
let hash = g.choose(&hashes).unwrap().clone();
|
||||
KeyPairAndSigHash{ kp: kp, sh: hash }
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn sign_verifies(kpsh: KeyPairAndSigHash<U512>, m: Message) -> bool {
|
||||
let sig = kpsh.kp.private.sign(kpsh.sh, &m.m);
|
||||
kpsh.kp.public.verify(kpsh.sh, &m.m, &sig)
|
||||
}
|
||||
|
||||
fn enc_dec_roundtrips(kp: RSAKeyPair<U512>, m: Message) -> bool {
|
||||
let oaep = OAEPParams {
|
||||
hash: Sha224::default(),
|
||||
label: "test".to_string()
|
||||
};
|
||||
let c = kp.public.encrypt(&oaep, &m.m).unwrap();
|
||||
let mp = kp.private.decrypt(&oaep, &c).unwrap();
|
||||
|
||||
mp == m.m
|
||||
match self {
|
||||
RSAPair::R512(_,_) => f.write_str("512-bit RSA key pair"),
|
||||
RSAPair::R1024(_,_) => f.write_str("1024-bit RSA key pair"),
|
||||
RSAPair::R2048(_,_) => f.write_str("2048-bit RSA key pair"),
|
||||
RSAPair::R3072(_,_) => f.write_str("3072-bit RSA key pair"),
|
||||
RSAPair::R4096(_,_) => f.write_str("4096-bit RSA key pair"),
|
||||
RSAPair::R8192(_,_) => f.write_str("8192-bit RSA key pair"),
|
||||
RSAPair::R15360(_,_) => f.write_str("15360-bit RSA key pair"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyPair for RSAPair {
|
||||
type Public = RSAPublic;
|
||||
type Private = RSAPrivate;
|
||||
|
||||
fn new(pu: RSAPublic, pr: RSAPrivate) -> RSAPair
|
||||
{
|
||||
match (pu, pr) {
|
||||
(RSAPublic::Key512(pbl), RSAPrivate::Key512(prv)) =>
|
||||
RSAPair::R512(pbl, prv),
|
||||
(RSAPublic::Key1024(pbl), RSAPrivate::Key1024(prv)) =>
|
||||
RSAPair::R1024(pbl, prv),
|
||||
(RSAPublic::Key2048(pbl), RSAPrivate::Key2048(prv)) =>
|
||||
RSAPair::R2048(pbl, prv),
|
||||
(RSAPublic::Key3072(pbl), RSAPrivate::Key3072(prv)) =>
|
||||
RSAPair::R3072(pbl, prv),
|
||||
(RSAPublic::Key4096(pbl), RSAPrivate::Key4096(prv)) =>
|
||||
RSAPair::R4096(pbl, prv),
|
||||
(RSAPublic::Key8192(pbl), RSAPrivate::Key8192(prv)) =>
|
||||
RSAPair::R8192(pbl, prv),
|
||||
(RSAPublic::Key15360(pbl), RSAPrivate::Key15360(prv)) =>
|
||||
RSAPair::R15360(pbl, prv),
|
||||
_ =>
|
||||
panic!("Unmatched public/private arguments to RSAPair::new()")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RSAPair {
|
||||
pub fn sign(&self, signhash: &SigningHash, msg: &[u8]) -> Vec<u8>
|
||||
{
|
||||
match self {
|
||||
RSAPair::R512(_,prv) => prv.sign(signhash, msg),
|
||||
RSAPair::R1024(_,prv) => prv.sign(signhash, msg),
|
||||
RSAPair::R2048(_,prv) => prv.sign(signhash, msg),
|
||||
RSAPair::R3072(_,prv) => prv.sign(signhash, msg),
|
||||
RSAPair::R4096(_,prv) => prv.sign(signhash, msg),
|
||||
RSAPair::R8192(_,prv) => prv.sign(signhash, msg),
|
||||
RSAPair::R15360(_,prv) => prv.sign(signhash, msg),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify(&self, signhash: &SigningHash, msg: &[u8], sig: &[u8]) -> bool
|
||||
{
|
||||
match self {
|
||||
RSAPair::R512(pbl,_) => pbl.verify(signhash, msg, sig),
|
||||
RSAPair::R1024(pbl,_) => pbl.verify(signhash, msg, sig),
|
||||
RSAPair::R2048(pbl,_) => pbl.verify(signhash, msg, sig),
|
||||
RSAPair::R3072(pbl,_) => pbl.verify(signhash, msg, sig),
|
||||
RSAPair::R4096(pbl,_) => pbl.verify(signhash, msg, sig),
|
||||
RSAPair::R8192(pbl,_) => pbl.verify(signhash, msg, sig),
|
||||
RSAPair::R15360(pbl,_) => pbl.verify(signhash, msg, sig),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn public(&self) -> RSAPublic
|
||||
{
|
||||
match self {
|
||||
&RSAPair::R512(ref pbl,_) => RSAPublic::Key512(pbl.clone()),
|
||||
&RSAPair::R1024(ref pbl,_) => RSAPublic::Key1024(pbl.clone()),
|
||||
&RSAPair::R2048(ref pbl,_) => RSAPublic::Key2048(pbl.clone()),
|
||||
&RSAPair::R3072(ref pbl,_) => RSAPublic::Key3072(pbl.clone()),
|
||||
&RSAPair::R4096(ref pbl,_) => RSAPublic::Key4096(pbl.clone()),
|
||||
&RSAPair::R8192(ref pbl,_) => RSAPublic::Key8192(pbl.clone()),
|
||||
&RSAPair::R15360(ref pbl,_) => RSAPublic::Key15360(pbl.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn private(&self) -> RSAPrivate
|
||||
{
|
||||
match self {
|
||||
&RSAPair::R512(_,ref prv) => RSAPrivate::Key512(prv.clone()),
|
||||
&RSAPair::R1024(_,ref prv) => RSAPrivate::Key1024(prv.clone()),
|
||||
&RSAPair::R2048(_,ref prv) => RSAPrivate::Key2048(prv.clone()),
|
||||
&RSAPair::R3072(_,ref prv) => RSAPrivate::Key3072(prv.clone()),
|
||||
&RSAPair::R4096(_,ref prv) => RSAPrivate::Key4096(prv.clone()),
|
||||
&RSAPair::R8192(_,ref prv) => RSAPrivate::Key8192(prv.clone()),
|
||||
&RSAPair::R15360(_,ref prv) => RSAPrivate::Key15360(prv.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! generate_rsa_pair
|
||||
{
|
||||
($uint: ident, $half: ident, $iterations: expr) => {
|
||||
impl KeyPair for RSAKeyPair<$uint> {
|
||||
type Public = RSAPublicKey<$uint>;
|
||||
type Private = RSAPrivateKey<$uint>;
|
||||
|
||||
fn new(pu: RSAPublicKey<$uint>, pr: RSAPrivateKey<$uint>) -> RSAKeyPair<$uint> {
|
||||
RSAKeyPair {
|
||||
public: pu,
|
||||
private: pr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RSAKeyPair<$uint> {
|
||||
pub fn generate<G>(rng: &mut G) -> RSAKeyPair<$uint>
|
||||
where G: RngCore
|
||||
{
|
||||
loop {
|
||||
let ebase = 65537u32;
|
||||
let e = $uint::from(ebase);
|
||||
let (p, q) = RSAKeyPair::<$uint>::generate_pq(rng, &$half::from(ebase));
|
||||
let one = $half::from(1u32);
|
||||
let pminus1 = &p - &one;
|
||||
let qminus1 = &q - &one;
|
||||
let phi = pminus1 * qminus1;
|
||||
let n = &p * &q;
|
||||
if let Some(d) = e.modinv(&phi) {
|
||||
let public = RSAPublicKey::<$uint>::new(n.clone(), e);
|
||||
let private = RSAPrivateKey::<$uint>::new(n, d);
|
||||
return RSAKeyPair::<$uint>::new(public, private);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_pq<G>(rng: &mut G, e: &$half) -> ($half, $half)
|
||||
where G: RngCore
|
||||
{
|
||||
let sqrt2_32 = 6074001000u64;
|
||||
let half_bitlen = $half::bit_length();
|
||||
let minval = $half::from(sqrt2_32) << (half_bitlen - 33);
|
||||
let mindiff = $half::from(1u64) << (half_bitlen - 101);
|
||||
let p = $half::random_primef(rng, $iterations, |x| {
|
||||
if (x >= minval) && x.gcd_is_one(e) {
|
||||
Some(x)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
loop {
|
||||
let q = $half::random_primef(rng, $iterations, |x| {
|
||||
if (x >= minval) && x.gcd_is_one(e) {
|
||||
Some(x)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
if diff(&p, &q) >= mindiff {
|
||||
return (p, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_rsa_pair!(U512, U256, 7);
|
||||
generate_rsa_pair!(U1024, U512, 7);
|
||||
generate_rsa_pair!(U2048, U1024, 4);
|
||||
generate_rsa_pair!(U3072, U1536, 3);
|
||||
generate_rsa_pair!(U4096, U2048, 3);
|
||||
generate_rsa_pair!(U8192, U4096, 3);
|
||||
generate_rsa_pair!(U15360, U7680, 3);
|
||||
|
||||
#[cfg(test)]
|
||||
mod generation {
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use std::fmt;
|
||||
use super::*;
|
||||
|
||||
impl Clone for RSAKeyPair<U512> {
|
||||
fn clone(&self) -> RSAKeyPair<U512> {
|
||||
RSAKeyPair {
|
||||
public: RSAPublicKey {
|
||||
n: self.public.n.clone(),
|
||||
nu: self.public.nu.clone(),
|
||||
e: self.public.e.clone(),
|
||||
},
|
||||
private: RSAPrivateKey {
|
||||
nu: self.private.nu.clone(),
|
||||
d: self.private.d.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for RSAKeyPair<U512> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("RSA512KeyPair")
|
||||
.field("n", &self.public.n)
|
||||
.field("e", &self.public.e)
|
||||
.field("d", &self.private.d)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for RSAKeyPair<U512> {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> RSAKeyPair<U512> {
|
||||
RSAKeyPair::<U512>::generate(g)
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn generate_and_sign(keypair: RSAKeyPair<U512>, msg: Vec<u8>) -> bool {
|
||||
let sig = keypair.private.sign(&SIGNING_HASH_SHA256, &msg);
|
||||
keypair.public.verify(&SIGNING_HASH_SHA256, &msg, &sig)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,43 @@
|
||||
use byteorder::{BigEndian,ByteOrder};
|
||||
use digest::{FixedOutput,Input};
|
||||
use sha::Hash;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// Parameters for OAEP encryption and decryption: a hash function to use
|
||||
/// as part of the message generation function (MGF1, if you're curious),
|
||||
/// Parameters for OAEP encryption and decryption: a hash function to use as
|
||||
/// part of the message generation function (MGF1, if you're curious),
|
||||
/// and any labels you want to include as part of the encryption.
|
||||
pub struct OAEPParams<H: Clone + Input + FixedOutput> {
|
||||
pub hash: H,
|
||||
pub label: String
|
||||
pub struct OAEPParams<H: Hash> {
|
||||
pub label: String,
|
||||
phantom: PhantomData<H>
|
||||
}
|
||||
|
||||
impl<H: Clone + Input + FixedOutput> OAEPParams<H> {
|
||||
pub fn new(hash: H, label: String)
|
||||
impl<H: Hash> OAEPParams<H> {
|
||||
pub fn new(label: String)
|
||||
-> OAEPParams<H>
|
||||
{
|
||||
OAEPParams { hash: hash, label: label }
|
||||
OAEPParams { label: label, phantom: PhantomData }
|
||||
}
|
||||
|
||||
pub fn hash_len(&self) -> usize {
|
||||
self.hash.clone().fixed_result().as_slice().len()
|
||||
H::hash(&[]).len()
|
||||
}
|
||||
|
||||
pub fn hash(&self, input: &[u8]) -> Vec<u8> {
|
||||
let mut digest = self.hash.clone();
|
||||
digest.process(input);
|
||||
digest.fixed_result().as_slice().to_vec()
|
||||
H::hash(input)
|
||||
}
|
||||
|
||||
pub fn mgf1(&self, input: &[u8], len: usize) -> Vec<u8> {
|
||||
let mut res = Vec::with_capacity(len);
|
||||
let mut counter: u32 = 0;
|
||||
let mut counter = 0u32;
|
||||
|
||||
while res.len() < len {
|
||||
let mut c: [u8; 4] = [0; 4];
|
||||
BigEndian::write_u32(&mut c, counter);
|
||||
let mut digest = self.hash.clone();
|
||||
digest.process(input);
|
||||
digest.process(&c);
|
||||
let chunk = digest.fixed_result();
|
||||
let mut buffer = [0; 4];
|
||||
BigEndian::write_u32(&mut buffer, counter);
|
||||
let mut digest = H::new();
|
||||
digest.update(input);
|
||||
digest.update(&buffer);
|
||||
let chunk = digest.finalize();
|
||||
res.extend_from_slice(chunk.as_slice());
|
||||
counter += 1;
|
||||
counter = counter + 1;
|
||||
}
|
||||
|
||||
res.truncate(len);
|
||||
|
||||
@@ -1,117 +1,269 @@
|
||||
use cryptonum::{CryptoNumBase,CryptoNumModOps,CryptoNumSerialization};
|
||||
use digest::{FixedOutput,Input};
|
||||
use rsa::core::{dp,sp1,pkcs1_pad,xor_vecs};
|
||||
use rsa::oaep::{OAEPParams};
|
||||
use rsa::errors::{RSAError};
|
||||
use cryptonum::unsigned::*;
|
||||
use rsa::core::{RSAMode,drop0s,pkcs1_pad,xor_vecs};
|
||||
use rsa::errors::RSAError;
|
||||
use rsa::oaep::OAEPParams;
|
||||
use rsa::signing_hashes::SigningHash;
|
||||
use sha::Hash;
|
||||
|
||||
/// A RSA private key. As with public keys, I've left the size as a
|
||||
/// parameter: 2048-4096 is standard practice, 512-1024 is weak, and
|
||||
/// >4096 is going to be slow.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct RSAPrivateKey<Size>
|
||||
where
|
||||
Size: CryptoNumBase + CryptoNumSerialization
|
||||
/// An RSA private key. Useful for signing messages and decrypting encrypted
|
||||
/// content.
|
||||
#[derive(Clone,PartialEq)]
|
||||
pub struct RSAPrivateKey<R: RSAMode>
|
||||
{
|
||||
pub(crate) n: Size,
|
||||
pub(crate) d: Size
|
||||
pub(crate) nu: R::Barrett,
|
||||
pub(crate) d: R
|
||||
}
|
||||
|
||||
impl<U> RSAPrivateKey<U>
|
||||
where
|
||||
U: CryptoNumBase + CryptoNumModOps + CryptoNumSerialization
|
||||
/// A generic RSA private key which is agnostic to its key size.
|
||||
#[derive(Clone,PartialEq)]
|
||||
pub enum RSAPrivate {
|
||||
Key512(RSAPrivateKey<U512>),
|
||||
Key1024(RSAPrivateKey<U1024>),
|
||||
Key2048(RSAPrivateKey<U2048>),
|
||||
Key3072(RSAPrivateKey<U3072>),
|
||||
Key4096(RSAPrivateKey<U4096>),
|
||||
Key8192(RSAPrivateKey<U8192>),
|
||||
Key15360(RSAPrivateKey<U15360>)
|
||||
}
|
||||
|
||||
macro_rules! generate_rsa_private
|
||||
{
|
||||
/// Generate a private key, using the given `n` and `d` parameters
|
||||
/// gathered from some other source. The length should be given in
|
||||
/// bits.
|
||||
pub fn new(n: U, d: U) -> RSAPrivateKey<U> {
|
||||
RSAPrivateKey {
|
||||
n: n,
|
||||
d: d
|
||||
}
|
||||
}
|
||||
($num: ident, $bar: ident, $size: expr) => {
|
||||
impl RSAPrivateKey<$num> {
|
||||
/// Generate a new private key with the given modulus and private
|
||||
/// number (`d`). This operation actually does a bit of computation
|
||||
/// under the hood, in order to speed up future ones, so you might
|
||||
/// want to strongly consider sharing rather than multiple
|
||||
/// instantiation. But you do you.
|
||||
pub fn new(n: $num, d: $num) -> RSAPrivateKey<$num> {
|
||||
let nu = $bar::new(n.clone());
|
||||
RSAPrivateKey{ nu: nu, d: d }
|
||||
}
|
||||
|
||||
/// Sign a message using the given hash.
|
||||
pub fn sign(&self, sighash: &SigningHash, msg: &[u8]) -> Vec<u8> {
|
||||
let hash = (sighash.run)(msg);
|
||||
let em = pkcs1_pad(&sighash.ident, &hash, self.d.byte_size());
|
||||
let m = U::from_bytes(&em);
|
||||
let s = sp1(&self.n, &self.d, &m);
|
||||
let sig = s.to_bytes();
|
||||
sig
|
||||
}
|
||||
/// Sign the given message with the given SigningHash, returning
|
||||
/// the signature. This uses a deterministic PKCS1 method for
|
||||
/// signing messages, so no RNG required.
|
||||
pub fn sign(&self, signhash: &SigningHash, msg: &[u8])
|
||||
-> Vec<u8>
|
||||
{
|
||||
let hash = (signhash.run)(msg);
|
||||
let em = pkcs1_pad(&signhash.ident, &hash, $size/8);
|
||||
let m = $num::from_bytes(&em);
|
||||
let s = self.sp1(&m);
|
||||
let sig = s.to_bytes();
|
||||
sig
|
||||
}
|
||||
|
||||
/// Decrypt a message with the given parameters.
|
||||
pub fn decrypt<H: Clone + Input + FixedOutput>(&self, oaep: &OAEPParams<H>, msg: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
{
|
||||
let mut res = Vec::new();
|
||||
let byte_len = self.d.byte_size();
|
||||
/// Decrypted the provided encrypted blob using the given
|
||||
/// parameters. This does standard RSA OAEP decryption, which is
|
||||
/// rather slow. If you have a choice, you should probably do
|
||||
/// something clever, like only use this encryption/decryption
|
||||
/// method to encrypt/decrypt a shared symmetric key, like an
|
||||
/// AES key. That way, you only do this operation (which is
|
||||
/// SO SLOW) for a relatively small amount of data.
|
||||
pub fn decrypt<H: Hash>(&self, oaep: &OAEPParams<H>, msg: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
{
|
||||
let mut res = Vec::new();
|
||||
|
||||
for chunk in msg.chunks(byte_len) {
|
||||
let mut dchunk = self.oaep_decrypt(oaep, chunk)?;
|
||||
res.append(&mut dchunk);
|
||||
}
|
||||
for chunk in msg.chunks($size/8) {
|
||||
let mut dchunk = self.oaep_decrypt(oaep, chunk)?;
|
||||
res.append(&mut dchunk);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn oaep_decrypt<H: Clone + Input + FixedOutput>(&self, oaep: &OAEPParams<H>, c: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
{
|
||||
let byte_len = self.d.byte_size();
|
||||
// Step 1b
|
||||
if c.len() != byte_len {
|
||||
return Err(RSAError::DecryptionError);
|
||||
}
|
||||
// Step 1c
|
||||
if byte_len < ((2 * oaep.hash_len()) + 2) {
|
||||
return Err(RSAError::DecryptHashMismatch);
|
||||
}
|
||||
// Step 2a
|
||||
let c_ip = U::from_bytes(&c);
|
||||
// Step 2b
|
||||
let m_ip = dp(&self.n, &self.d, &c_ip);
|
||||
// Step 2c
|
||||
let em = m_ip.to_bytes();
|
||||
// Step 3a
|
||||
let l_hash = oaep.hash(oaep.label.as_bytes());
|
||||
// Step 3b
|
||||
let (y, rest) = em.split_at(1);
|
||||
let (masked_seed, masked_db) = rest.split_at(oaep.hash_len());
|
||||
// Step 3c
|
||||
let seed_mask = oaep.mgf1(masked_db, oaep.hash_len());
|
||||
// Step 3d
|
||||
let seed = xor_vecs(&masked_seed.to_vec(), &seed_mask);
|
||||
// Step 3e
|
||||
let db_mask = oaep.mgf1(&seed, byte_len - oaep.hash_len() - 1);
|
||||
// Step 3f
|
||||
let db = xor_vecs(&masked_db.to_vec(), &db_mask);
|
||||
// Step 3g
|
||||
let (l_hash2, ps_o_m) = db.split_at(oaep.hash_len());
|
||||
let o_m = drop0s(ps_o_m);
|
||||
let (o, m) = o_m.split_at(1);
|
||||
// Checks!
|
||||
if o != [1] {
|
||||
return Err(RSAError::DecryptionError);
|
||||
}
|
||||
if l_hash != l_hash2 {
|
||||
return Err(RSAError::DecryptionError);
|
||||
}
|
||||
if y != [0] {
|
||||
return Err(RSAError::DecryptionError);
|
||||
}
|
||||
fn sp1(&self, m: &$num) -> $num {
|
||||
m.modexp(&self.d, &self.nu)
|
||||
}
|
||||
|
||||
Ok(m.to_vec())
|
||||
fn dp(&self, c: &$num) -> $num {
|
||||
c.modexp(&self.d, &self.nu)
|
||||
}
|
||||
|
||||
fn oaep_decrypt<H: Hash>(&self, oaep: &OAEPParams<H>, c: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
{
|
||||
let byte_len = $size / 8;
|
||||
// Step 1b
|
||||
if c.len() != byte_len {
|
||||
return Err(RSAError::DecryptionError);
|
||||
}
|
||||
// Step 1c
|
||||
if byte_len < ((2 * oaep.hash_len()) + 2) {
|
||||
return Err(RSAError::DecryptHashMismatch);
|
||||
}
|
||||
// Step 2a
|
||||
let c_ip = $num::from_bytes(&c);
|
||||
// Step 2b
|
||||
let m_ip = self.dp(&c_ip);
|
||||
// Step 2c
|
||||
let em = m_ip.to_bytes();
|
||||
// Step 3a
|
||||
let l_hash = oaep.hash(oaep.label.as_bytes());
|
||||
// Step 3b
|
||||
let (y, rest) = em.split_at(1);
|
||||
let (masked_seed, masked_db) = rest.split_at(oaep.hash_len());
|
||||
// Step 3c
|
||||
let seed_mask = oaep.mgf1(masked_db, oaep.hash_len());
|
||||
// Step 3d
|
||||
let seed = xor_vecs(&masked_seed.to_vec(), &seed_mask);
|
||||
// Step 3e
|
||||
let db_mask = oaep.mgf1(&seed, byte_len - oaep.hash_len() - 1);
|
||||
// Step 3f
|
||||
let db = xor_vecs(&masked_db.to_vec(), &db_mask);
|
||||
// Step 3g
|
||||
let (l_hash2, ps_o_m) = db.split_at(oaep.hash_len());
|
||||
let o_m = drop0s(ps_o_m);
|
||||
let (o, m) = o_m.split_at(1);
|
||||
// Checks!
|
||||
if o != [1] {
|
||||
return Err(RSAError::DecryptionError);
|
||||
}
|
||||
if l_hash != l_hash2 {
|
||||
return Err(RSAError::DecryptionError);
|
||||
}
|
||||
if y != [0] {
|
||||
return Err(RSAError::DecryptionError);
|
||||
}
|
||||
|
||||
Ok(m.to_vec())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn drop0s(a: &[u8]) -> &[u8] {
|
||||
let mut idx = 0;
|
||||
generate_rsa_private!(U512, BarrettU512, 512);
|
||||
generate_rsa_private!(U1024, BarrettU1024, 1024);
|
||||
generate_rsa_private!(U2048, BarrettU2048, 2048);
|
||||
generate_rsa_private!(U3072, BarrettU3072, 3072);
|
||||
generate_rsa_private!(U4096, BarrettU4096, 4096);
|
||||
generate_rsa_private!(U8192, BarrettU8192, 8192);
|
||||
generate_rsa_private!(U15360, BarrettU15360, 15360);
|
||||
|
||||
while (idx < a.len()) && (a[idx] == 0) {
|
||||
idx = idx + 1;
|
||||
}
|
||||
#[cfg(test)]
|
||||
macro_rules! sign_test_body {
|
||||
($mod: ident, $num: ident, $bar: ident, $num64: ident, $size: expr) => {
|
||||
let fname = format!("testdata/rsa/sign{}.test", $size);
|
||||
run_test(fname.to_string(), 7, |case| {
|
||||
let (neg0, dbytes) = case.get("d").unwrap();
|
||||
let (neg1, nbytes) = case.get("n").unwrap();
|
||||
let (neg2, hbytes) = case.get("h").unwrap();
|
||||
let (neg3, mbytes) = case.get("m").unwrap();
|
||||
let (neg4, sbytes) = case.get("s").unwrap();
|
||||
let (neg5, ubytes) = case.get("u").unwrap();
|
||||
let (neg6, kbytes) = case.get("k").unwrap();
|
||||
|
||||
&a[idx..]
|
||||
assert!(!neg0&&!neg1&&!neg2&&!neg3&&!neg4&&!neg5&&!neg6);
|
||||
let n = $num64::from_bytes(nbytes);
|
||||
let nu = $num64::from_bytes(ubytes);
|
||||
let bigk = $num::from_bytes(kbytes);
|
||||
let k = usize::from(bigk);
|
||||
let d = $num::from_bytes(dbytes);
|
||||
let sighash = match usize::from($num::from_bytes(hbytes)) {
|
||||
224 => &SIGNING_HASH_SHA224,
|
||||
256 => &SIGNING_HASH_SHA256,
|
||||
384 => &SIGNING_HASH_SHA384,
|
||||
512 => &SIGNING_HASH_SHA512,
|
||||
x => panic!("Bad signing hash: {}", x)
|
||||
};
|
||||
let privkey = RSAPrivateKey{ nu: $bar::from_components(k, n.clone(), nu), d: d };
|
||||
let sig = privkey.sign(sighash, &mbytes);
|
||||
assert_eq!(*sbytes, sig);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! decrypt_test_body {
|
||||
($mod: ident, $num: ident, $bar: ident, $num64: ident, $size: expr) => {
|
||||
let fname = format!("testdata/rsa/encrypt{}.test", $size);
|
||||
run_test(fname.to_string(), 9, |case| {
|
||||
let (neg0, nbytes) = case.get("n").unwrap();
|
||||
let (neg1, hbytes) = case.get("h").unwrap();
|
||||
let (neg2, mbytes) = case.get("m").unwrap();
|
||||
let (neg3, _bytes) = case.get("e").unwrap();
|
||||
let (neg4, ubytes) = case.get("u").unwrap();
|
||||
let (neg5, kbytes) = case.get("k").unwrap();
|
||||
let (neg6, dbytes) = case.get("d").unwrap();
|
||||
let (neg7, lbytes) = case.get("l").unwrap();
|
||||
let (neg8, cbytes) = case.get("c").unwrap();
|
||||
|
||||
assert!(!neg0 && !neg1 && !neg2 && !neg3 && !neg4 && !neg5 && !neg6 && !neg7 && !neg8);
|
||||
let n = $num::from_bytes(nbytes);
|
||||
let n64 = $num64::from(&n);
|
||||
let nu = $num64::from_bytes(ubytes);
|
||||
let bigk = $num::from_bytes(kbytes);
|
||||
let k = usize::from(bigk);
|
||||
let d = $num::from_bytes(dbytes);
|
||||
let nu = $bar::from_components(k, n64, nu);
|
||||
let privkey = RSAPrivateKey{ nu: nu, d: d };
|
||||
let lstr = String::from_utf8(lbytes.clone()).unwrap();
|
||||
let message = match usize::from($num::from_bytes(hbytes)) {
|
||||
224 => privkey.decrypt(&OAEPParams::<SHA224>::new(lstr), &cbytes),
|
||||
256 => privkey.decrypt(&OAEPParams::<SHA256>::new(lstr), &cbytes),
|
||||
384 => privkey.decrypt(&OAEPParams::<SHA384>::new(lstr), &cbytes),
|
||||
512 => privkey.decrypt(&OAEPParams::<SHA512>::new(lstr), &cbytes),
|
||||
x => panic!("Unknown hash number: {}", x)
|
||||
};
|
||||
assert!(message.is_ok());
|
||||
assert_eq!(mbytes, &message.unwrap());
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! generate_tests {
|
||||
($mod: ident, $num: ident, $bar: ident, $num64: ident, $size: expr) => {
|
||||
#[cfg(test)]
|
||||
#[allow(non_snake_case)]
|
||||
mod $mod {
|
||||
use cryptonum::unsigned::Decoder;
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
use rsa::signing_hashes::*;
|
||||
use sha::{SHA224,SHA256,SHA384,SHA512};
|
||||
|
||||
#[test]
|
||||
fn sign() {
|
||||
sign_test_body!($mod, $num, $bar, $num64, $size);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decrypt() {
|
||||
decrypt_test_body!($mod, $num, $bar, $num64, $size);
|
||||
}
|
||||
}
|
||||
};
|
||||
(ignore $mod: ident, $num: ident, $bar: ident, $num64: ident, $size: expr) => {
|
||||
#[cfg(test)]
|
||||
#[allow(non_snake_case)]
|
||||
mod $mod {
|
||||
use cryptonum::unsigned::Decoder;
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
use rsa::signing_hashes::*;
|
||||
use sha::{SHA224,SHA256,SHA384,SHA512};
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn sign() {
|
||||
sign_test_body!($mod, $num, $bar, $num64, $size);
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn decrypt() {
|
||||
decrypt_test_body!($mod, $num, $bar, $num64, $size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_tests!( RSA512, U512, BarrettU512, U576, 512);
|
||||
generate_tests!( RSA1024, U1024, BarrettU1024, U1088, 1024);
|
||||
generate_tests!( RSA2048, U2048, BarrettU2048, U2112, 2048);
|
||||
generate_tests!( RSA3072, U3072, BarrettU3072, U3136, 3072);
|
||||
generate_tests!( RSA4096, U4096, BarrettU4096, U4160, 4096);
|
||||
generate_tests!(ignore RSA8192, U8192, BarrettU8192, U8256, 8192);
|
||||
generate_tests!(ignore RSA15360, U15360, BarrettU15360, U15424, 15360);
|
||||
@@ -1,147 +1,61 @@
|
||||
use cryptonum::{CryptoNumBase,CryptoNumModOps,CryptoNumSerialization};
|
||||
use digest::{FixedOutput,Input};
|
||||
use num::{BigInt,BigUint};
|
||||
use rand::{OsRng,Rng};
|
||||
use rsa::core::{ep,vp1,pkcs1_pad,xor_vecs,decode_biguint};
|
||||
use rsa::oaep::{OAEPParams};
|
||||
use rsa::errors::{RSAError};
|
||||
use cryptonum::unsigned::*;
|
||||
use rand::Rng;
|
||||
use rand::rngs::OsRng;
|
||||
use rsa::core::{RSAMode,decode_biguint,pkcs1_pad,xor_vecs};
|
||||
use rsa::errors::RSAError;
|
||||
use rsa::oaep::OAEPParams;
|
||||
use rsa::signing_hashes::SigningHash;
|
||||
use simple_asn1::{FromASN1,ToASN1,ASN1DecodeErr,ASN1EncodeErr};
|
||||
use simple_asn1::{ASN1Block,ASN1Class};
|
||||
use sha::Hash;
|
||||
use simple_asn1::{ASN1Block,ASN1DecodeErr,ASN1EncodeErr,
|
||||
ASN1Class,FromASN1,ToASN1};
|
||||
#[cfg(test)]
|
||||
use std::fmt;
|
||||
use utils::TranslateNums;
|
||||
|
||||
/// An RSA public key with the given modulus size. I've left the size as a
|
||||
/// parameter, instead of hardcoding particular values. That being said,
|
||||
/// you almost certainly want one of `U2048`, `U3072`, or `U4096` if you're
|
||||
/// being pretty standard; `U512` or `U1024` if you're interfacing with
|
||||
/// legacy code or want to build intentionally weak systems; or `U7680`,
|
||||
/// `U8192`, or `U15360` if you like things running very slowly.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct RSAPublicKey<Size>
|
||||
where
|
||||
Size: CryptoNumBase + CryptoNumSerialization
|
||||
{
|
||||
pub(crate) n: Size,
|
||||
pub(crate) e: Size
|
||||
/// An RSA public key. Useful for verifying signatures or encrypting data to
|
||||
/// send to the private key holder.
|
||||
#[derive(Clone,PartialEq)]
|
||||
pub struct RSAPublicKey<R: RSAMode> {
|
||||
pub(crate) n: R,
|
||||
pub(crate) nu: R::Barrett,
|
||||
pub(crate) e: R
|
||||
}
|
||||
|
||||
impl<U> RSAPublicKey<U>
|
||||
where
|
||||
U: CryptoNumBase + CryptoNumModOps + CryptoNumSerialization
|
||||
{
|
||||
/// Create a new RSA public key from the given components, which you found
|
||||
/// via some other mechanism.
|
||||
pub fn new(n: U, e: U) -> RSAPublicKey<U> {
|
||||
RSAPublicKey{ n: n, e: e }
|
||||
}
|
||||
/// A generic private key that is agnostic to the key size.
|
||||
#[derive(Clone,PartialEq)]
|
||||
pub enum RSAPublic {
|
||||
Key512( RSAPublicKey<U512>),
|
||||
Key1024( RSAPublicKey<U1024>),
|
||||
Key2048( RSAPublicKey<U2048>),
|
||||
Key3072( RSAPublicKey<U3072>),
|
||||
Key4096( RSAPublicKey<U4096>),
|
||||
Key8192( RSAPublicKey<U8192>),
|
||||
Key15360(RSAPublicKey<U15360>)
|
||||
}
|
||||
|
||||
/// Verify the signature for a given message, using the given signing hash,
|
||||
/// return true iff the signature validates.
|
||||
pub fn verify(&self, sighash: &SigningHash, msg: &[u8], sig: &[u8]) -> bool
|
||||
impl RSAPublic {
|
||||
/// Verify that the given signature is for the given message, passing
|
||||
/// in the same signing arguments used to sign the message in the
|
||||
/// first place.
|
||||
pub fn verify(&self, signhash: &SigningHash, msg: &[u8], sig: &[u8]) -> bool
|
||||
{
|
||||
let hash = (sighash.run)(msg);
|
||||
let s = U::from_bytes(sig);
|
||||
let m = vp1(&self.n, &self.e, &s);
|
||||
let em = s.to_bytes();
|
||||
let em_ = pkcs1_pad(&sighash.ident, &hash, m.byte_size());
|
||||
em == em_
|
||||
}
|
||||
|
||||
/// Encrypt the given data with the public key and parameters, returning
|
||||
/// the encrypted blob or an error encountered during encryption.
|
||||
///
|
||||
/// OAEP encoding is used for this process, which requires a random number
|
||||
/// generator. This version of the function uses `OsRng`. If you want to
|
||||
/// use your own RNG, use `encrypt_w_rng`.
|
||||
pub fn encrypt<H:Clone + Input + FixedOutput>(&self, oaep: &OAEPParams<H>,
|
||||
msg: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
{
|
||||
let mut g = OsRng::new()?;
|
||||
self.encrypt_with_rng(&mut g, oaep, msg)
|
||||
}
|
||||
|
||||
/// Encrypt the given data with the public key and parameters, returning
|
||||
/// the encrypted blob or an error encountered during encryption. This
|
||||
/// version also allows you to provide your own RNG, if you really feel
|
||||
/// like shooting yourself in the foot.
|
||||
pub fn encrypt_with_rng<G,H>(&self, g: &mut G, oaep: &OAEPParams<H>,
|
||||
msg: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
where G: Rng, H: Clone + Input + FixedOutput
|
||||
{
|
||||
let mylen = self.e.byte_size();
|
||||
|
||||
if mylen <= ((2 * oaep.hash_len()) + 2) {
|
||||
return Err(RSAError::KeyTooSmallForHash);
|
||||
match self {
|
||||
RSAPublic::Key512(x) => x.verify(signhash, msg, sig),
|
||||
RSAPublic::Key1024(x) => x.verify(signhash, msg, sig),
|
||||
RSAPublic::Key2048(x) => x.verify(signhash, msg, sig),
|
||||
RSAPublic::Key3072(x) => x.verify(signhash, msg, sig),
|
||||
RSAPublic::Key4096(x) => x.verify(signhash, msg, sig),
|
||||
RSAPublic::Key8192(x) => x.verify(signhash, msg, sig),
|
||||
RSAPublic::Key15360(x) => x.verify(signhash, msg, sig)
|
||||
}
|
||||
|
||||
let mut res = Vec::new();
|
||||
|
||||
for chunk in msg.chunks(mylen - (2 * oaep.hash_len()) - 2) {
|
||||
let mut newchunk = self.oaep_encrypt(g, oaep, chunk)?;
|
||||
res.append(&mut newchunk)
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn oaep_encrypt<G,H>(&self, g: &mut G, oaep: &OAEPParams<H>, msg: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
where
|
||||
G: Rng, H:Clone + Input + FixedOutput
|
||||
{
|
||||
let mylen = self.e.byte_size();
|
||||
|
||||
// Step 1b
|
||||
if msg.len() > (mylen - (2 * oaep.hash_len()) - 2) {
|
||||
return Err(RSAError::BadMessageSize)
|
||||
}
|
||||
// Step 2a
|
||||
let mut lhash = oaep.hash(oaep.label.as_bytes());
|
||||
// Step 2b
|
||||
let num0s = mylen - msg.len() - (2 * oaep.hash_len()) - 2;
|
||||
let mut ps = Vec::new();
|
||||
ps.resize(num0s, 0);
|
||||
// Step 2c
|
||||
let mut db = Vec::new();
|
||||
db.append(&mut lhash);
|
||||
db.append(&mut ps);
|
||||
db.push(1);
|
||||
db.extend_from_slice(msg);
|
||||
// Step 2d
|
||||
let seed : Vec<u8> = g.gen_iter().take(oaep.hash_len()).collect();
|
||||
// Step 2e
|
||||
let db_mask = oaep.mgf1(&seed, mylen - oaep.hash_len() - 1);
|
||||
// Step 2f
|
||||
let mut masked_db = xor_vecs(&db, &db_mask);
|
||||
// Step 2g
|
||||
let seed_mask = oaep.mgf1(&masked_db, oaep.hash_len());
|
||||
// Step 2h
|
||||
let mut masked_seed = xor_vecs(&seed, &seed_mask);
|
||||
// Step 2i
|
||||
let mut em = Vec::new();
|
||||
em.push(0);
|
||||
em.append(&mut masked_seed);
|
||||
em.append(&mut masked_db);
|
||||
// Step 3a
|
||||
let m_i = U::from_bytes(&em);
|
||||
// Step 3b
|
||||
let c_i = ep(&self.n, &self.e, &m_i);
|
||||
// Step 3c
|
||||
let c = c_i.to_bytes();
|
||||
Ok(c)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromASN1 for RSAPublicKey<T>
|
||||
where
|
||||
T: CryptoNumBase + CryptoNumSerialization,
|
||||
T: From<BigUint>
|
||||
{
|
||||
impl FromASN1 for RSAPublic {
|
||||
type Error = RSAError;
|
||||
|
||||
fn from_asn1(bs: &[ASN1Block])
|
||||
-> Result<(RSAPublicKey<T>,&[ASN1Block]),RSAError>
|
||||
-> Result<(RSAPublic,&[ASN1Block]),RSAError>
|
||||
{
|
||||
match bs.split_first() {
|
||||
None =>
|
||||
@@ -149,26 +63,60 @@ impl<T> FromASN1 for RSAPublicKey<T>
|
||||
Some((&ASN1Block::Sequence(_, _, ref items), rest))
|
||||
if items.len() == 2 =>
|
||||
{
|
||||
let numn = decode_biguint(&items[0])?;
|
||||
let nume = decode_biguint(&items[1])?;
|
||||
let nsize = numn.bits();
|
||||
let n = decode_biguint(&items[0])?;
|
||||
let e = decode_biguint(&items[1])?;
|
||||
let nsize = n.bits();
|
||||
let mut rsa_size = 512;
|
||||
|
||||
while rsa_size < nsize {
|
||||
rsa_size = rsa_size + 256;
|
||||
}
|
||||
rsa_size /= 8;
|
||||
|
||||
if rsa_size != (T::zero()).bit_size() {
|
||||
return Err(RSAError::KeySizeMismatch);
|
||||
match rsa_size {
|
||||
512 => {
|
||||
let n2 = U512::from_num(&n).ok_or(RSAError::InvalidKey)?;
|
||||
let e2 = U512::from_num(&e).ok_or(RSAError::InvalidKey)?;
|
||||
let res = RSAPublicKey::<U512>::new(n2, e2);
|
||||
Ok((RSAPublic::Key512(res), rest))
|
||||
}
|
||||
1024 => {
|
||||
let n2 = U1024::from_num(&n).ok_or(RSAError::InvalidKey)?;
|
||||
let e2 = U1024::from_num(&e).ok_or(RSAError::InvalidKey)?;
|
||||
let res = RSAPublicKey::<U1024>::new(n2, e2);
|
||||
Ok((RSAPublic::Key1024(res), rest))
|
||||
}
|
||||
2048 => {
|
||||
let n2 = U2048::from_num(&n).ok_or(RSAError::InvalidKey)?;
|
||||
let e2 = U2048::from_num(&e).ok_or(RSAError::InvalidKey)?;
|
||||
let res = RSAPublicKey::<U2048>::new(n2, e2);
|
||||
Ok((RSAPublic::Key2048(res), rest))
|
||||
}
|
||||
3072 => {
|
||||
let n2 = U3072::from_num(&n).ok_or(RSAError::InvalidKey)?;
|
||||
let e2 = U3072::from_num(&e).ok_or(RSAError::InvalidKey)?;
|
||||
let res = RSAPublicKey::<U3072>::new(n2, e2);
|
||||
Ok((RSAPublic::Key3072(res), rest))
|
||||
}
|
||||
4096 => {
|
||||
let n2 = U4096::from_num(&n).ok_or(RSAError::InvalidKey)?;
|
||||
let e2 = U4096::from_num(&e).ok_or(RSAError::InvalidKey)?;
|
||||
let res = RSAPublicKey::<U4096>::new(n2, e2);
|
||||
Ok((RSAPublic::Key4096(res), rest))
|
||||
}
|
||||
8192 => {
|
||||
let n2 = U8192::from_num(&n).ok_or(RSAError::InvalidKey)?;
|
||||
let e2 = U8192::from_num(&e).ok_or(RSAError::InvalidKey)?;
|
||||
let res = RSAPublicKey::<U8192>::new(n2, e2);
|
||||
Ok((RSAPublic::Key8192(res), rest))
|
||||
}
|
||||
15360 => {
|
||||
let n2 = U15360::from_num(&n).ok_or(RSAError::InvalidKey)?;
|
||||
let e2 = U15360::from_num(&e).ok_or(RSAError::InvalidKey)?;
|
||||
let res = RSAPublicKey::<U15360>::new(n2, e2);
|
||||
Ok((RSAPublic::Key15360(res), rest))
|
||||
}
|
||||
_ =>
|
||||
Err(RSAError::InvalidKey)
|
||||
}
|
||||
|
||||
let n = T::from(numn);
|
||||
let e = T::from(nume);
|
||||
|
||||
let res = RSAPublicKey{ n: n, e: e };
|
||||
|
||||
Ok((res, rest))
|
||||
}
|
||||
Some(_) =>
|
||||
Err(RSAError::InvalidKey)
|
||||
@@ -176,21 +124,403 @@ impl<T> FromASN1 for RSAPublicKey<T>
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToASN1 for RSAPublicKey<T>
|
||||
where
|
||||
T: Clone + Into<BigInt>,
|
||||
T: CryptoNumBase + CryptoNumSerialization
|
||||
{
|
||||
impl ToASN1 for RSAPublic {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,Self::Error>
|
||||
{
|
||||
let enc_n = ASN1Block::Integer(c, 0, self.n.clone().into());
|
||||
let enc_e = ASN1Block::Integer(c, 0, self.e.clone().into());
|
||||
let seq = ASN1Block::Sequence(c, 0, vec![enc_n, enc_e]);
|
||||
Ok(vec![seq])
|
||||
match self {
|
||||
RSAPublic::Key512(x) => x.to_asn1_class(c),
|
||||
RSAPublic::Key1024(x) => x.to_asn1_class(c),
|
||||
RSAPublic::Key2048(x) => x.to_asn1_class(c),
|
||||
RSAPublic::Key3072(x) => x.to_asn1_class(c),
|
||||
RSAPublic::Key4096(x) => x.to_asn1_class(c),
|
||||
RSAPublic::Key8192(x) => x.to_asn1_class(c),
|
||||
RSAPublic::Key15360(x) => x.to_asn1_class(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl fmt::Debug for RSAPublic {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
RSAPublic::Key512(x) => write!(fmt, "RSA:{:?}", x),
|
||||
RSAPublic::Key1024(x) => write!(fmt, "RSA:{:?}", x),
|
||||
RSAPublic::Key2048(x) => write!(fmt, "RSA:{:?}", x),
|
||||
RSAPublic::Key3072(x) => write!(fmt, "RSA:{:?}", x),
|
||||
RSAPublic::Key4096(x) => write!(fmt, "RSA:{:?}", x),
|
||||
RSAPublic::Key8192(x) => write!(fmt, "RSA:{:?}", x),
|
||||
RSAPublic::Key15360(x) => write!(fmt, "RSA:{:?}", x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! generate_rsa_public
|
||||
{
|
||||
($num: ident, $bar: ident, $var: ident, $size: expr) => {
|
||||
impl RSAPublicKey<$num> {
|
||||
/// Generate a new public key pair for the given modulus and
|
||||
/// exponent. You should probably not call this directly unless
|
||||
/// you're writing a key generation function or writing your own
|
||||
/// public key parser.
|
||||
pub fn new(n: $num, e: $num) -> RSAPublicKey<$num> {
|
||||
let nu = $bar::new(n.clone());
|
||||
RSAPublicKey{ n: n, nu: nu, e: e }
|
||||
}
|
||||
|
||||
/// Verify that the provided signature is valid; that the private
|
||||
/// key associated with this public key sent exactly this message.
|
||||
/// The hash used here must exactly match the hash used to sign
|
||||
/// the message, including its ASN.1 metadata.
|
||||
pub fn verify(&self, signhash: &SigningHash, msg: &[u8], sig: &[u8])
|
||||
-> bool
|
||||
{
|
||||
let hash: Vec<u8> = (signhash.run)(msg);
|
||||
let s = $num::from_bytes(&sig);
|
||||
let m = self.vp1(&s);
|
||||
let em = m.to_bytes();
|
||||
let em_ = pkcs1_pad(signhash.ident, &hash, $size/8);
|
||||
em == em_
|
||||
}
|
||||
|
||||
/// Encrypt the message with a hash function, given the appropriate
|
||||
/// label. Please note that RSA encryption is not particularly fast,
|
||||
/// and decryption is very slow indeed. Thus, most crypto systems that
|
||||
/// need asymmetric encryption should generate a symmetric key, encrypt
|
||||
/// that key with RSA encryption, and then encrypt the actual message
|
||||
/// with that symmetric key.
|
||||
///
|
||||
/// In this variant of the function, we use an explicit random number
|
||||
/// generator, just in case you have one you really like. It better be
|
||||
/// cryptographically strong, though, as some of the padding protections
|
||||
/// are relying on it.
|
||||
pub fn encrypt_rng<G,H>(&self,g: &mut G,oaep: &OAEPParams<H>,msg: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
where
|
||||
G: Rng,
|
||||
H: Hash
|
||||
{
|
||||
let byte_len = $size / 8;
|
||||
let mut res = Vec::new();
|
||||
|
||||
if byte_len <= ((2 * oaep.hash_len()) + 2) {
|
||||
return Err(RSAError::KeyTooSmallForHash);
|
||||
}
|
||||
for chunk in msg.chunks(byte_len - (2 * oaep.hash_len()) - 2) {
|
||||
let mut newchunk = self.oaep_encrypt(g, oaep, chunk)?;
|
||||
res.append(&mut newchunk);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Encrypt the message with a hash function, given the appropriate
|
||||
/// label. Please note that RSA encryption is not particularly fast,
|
||||
/// and decryption is very slow indeed. Thus, most crypto systems that
|
||||
/// need asymmetric encryption should generate a symmetric key, encrypt
|
||||
/// that key with RSA encryption, and then encrypt the actual message
|
||||
/// with that symmetric key.
|
||||
///
|
||||
/// This variant will just use the system RNG for its randomness.
|
||||
pub fn encrypt<H: Hash>(&self,oaep:&OAEPParams<H>,msg:&[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
{
|
||||
let mut g = OsRng::new()?;
|
||||
self.encrypt_rng(&mut g, oaep, msg)
|
||||
}
|
||||
|
||||
fn vp1(&self, s: &$num) -> $num {
|
||||
s.modexp(&self.e, &self.nu)
|
||||
}
|
||||
|
||||
fn ep(&self, m: &$num) -> $num {
|
||||
m.modexp(&self.e, &self.nu)
|
||||
}
|
||||
|
||||
fn oaep_encrypt<G,H>(&self,g: &mut G,oaep: &OAEPParams<H>,m: &[u8])
|
||||
-> Result<Vec<u8>,RSAError>
|
||||
where
|
||||
G: Rng,
|
||||
H: Hash
|
||||
{
|
||||
let byte_len = $size / 8;
|
||||
// Step 1b
|
||||
if m.len() > (byte_len - (2 * oaep.hash_len()) - 2) {
|
||||
return Err(RSAError::BadMessageSize)
|
||||
}
|
||||
// Step 2a
|
||||
let mut lhash = oaep.hash(oaep.label.as_bytes());
|
||||
// Step 2b
|
||||
let num0s = byte_len - m.len() - (2 * oaep.hash_len()) - 2;
|
||||
let mut ps = Vec::new();
|
||||
ps.resize(num0s, 0);
|
||||
// Step 2c
|
||||
let mut db = Vec::new();
|
||||
db.append(&mut lhash);
|
||||
db.append(&mut ps);
|
||||
db.push(1);
|
||||
db.extend_from_slice(m);
|
||||
// Step 2d
|
||||
let mut seed: Vec<u8> = Vec::with_capacity(oaep.hash_len());
|
||||
seed.resize(oaep.hash_len(), 0);
|
||||
g.fill(seed.as_mut_slice());
|
||||
// Step 2e
|
||||
let db_mask = oaep.mgf1(&seed, byte_len - oaep.hash_len() - 1);
|
||||
// Step 2f
|
||||
let mut masked_db = xor_vecs(&db, &db_mask);
|
||||
// Step 2g
|
||||
let seed_mask = oaep.mgf1(&masked_db, oaep.hash_len());
|
||||
// Step 2h
|
||||
let mut masked_seed = xor_vecs(&seed, &seed_mask);
|
||||
// Step 2i
|
||||
let mut em = Vec::new();
|
||||
em.push(0);
|
||||
em.append(&mut masked_seed);
|
||||
em.append(&mut masked_db);
|
||||
// Step 3a
|
||||
let m_i = $num::from_bytes(&em);
|
||||
// Step 3b
|
||||
let c_i = self.ep(&m_i);
|
||||
// Step 3c
|
||||
let c = c_i.to_bytes();
|
||||
Ok(c)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for RSAPublicKey<$num> {
|
||||
type Error = RSAError;
|
||||
|
||||
fn from_asn1(bs: &[ASN1Block])
|
||||
-> Result<(RSAPublicKey<$num>,&[ASN1Block]),RSAError>
|
||||
{
|
||||
let (core, rest) = RSAPublic::from_asn1(bs)?;
|
||||
|
||||
match core {
|
||||
RSAPublic::$var(x) => Ok((x, rest)),
|
||||
_ => Err(RSAError::InvalidKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToASN1 for RSAPublicKey<$num> {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,Self::Error>
|
||||
{
|
||||
let n = self.n.to_num();
|
||||
let e = self.e.to_num();
|
||||
let enc_n = ASN1Block::Integer(c, 0, n);
|
||||
let enc_e = ASN1Block::Integer(c, 0, e);
|
||||
let seq = ASN1Block::Sequence(c, 0, vec![enc_n, enc_e]);
|
||||
Ok(vec![seq])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl fmt::Debug for RSAPublicKey<$num> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct(stringify!($rsa))
|
||||
.field("n", &self.n)
|
||||
.field("nu", &self.nu)
|
||||
.field("e", &self.e)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
generate_rsa_public!(U512, BarrettU512, Key512, 512);
|
||||
generate_rsa_public!(U1024, BarrettU1024, Key1024, 1024);
|
||||
generate_rsa_public!(U2048, BarrettU2048, Key2048, 2048);
|
||||
generate_rsa_public!(U3072, BarrettU3072, Key3072, 3072);
|
||||
generate_rsa_public!(U4096, BarrettU4096, Key4096, 4096);
|
||||
generate_rsa_public!(U8192, BarrettU8192, Key8192, 8192);
|
||||
generate_rsa_public!(U15360, BarrettU15360, Key15360, 15360);
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! new_test_body {
|
||||
($mod: ident, $num: ident, $bar: ident, $num64: ident, $size: expr) => {
|
||||
let fname = format!("testdata/rsa/sign{}.test", $size);
|
||||
run_test(fname.to_string(), 7, |case| {
|
||||
let (neg0, nbytes) = case.get("n").unwrap();
|
||||
let (neg1, ubytes) = case.get("u").unwrap();
|
||||
let (neg2, kbytes) = case.get("k").unwrap();
|
||||
|
||||
assert!(!neg0&&!neg1&&!neg2);
|
||||
let n = $num::from_bytes(nbytes);
|
||||
let n64 = $num64::from(&n);
|
||||
let nu = $num64::from_bytes(ubytes);
|
||||
let bigk = $num::from_bytes(kbytes);
|
||||
let k = usize::from(bigk);
|
||||
let e = $num::from(65537u64);
|
||||
let pubkey2 = RSAPublicKey::<$num>::new(n.clone(), e.clone());
|
||||
let pubkey1 = RSAPublicKey{ n: n, nu: $bar::from_components(k, n64, nu), e: e };
|
||||
assert_eq!(pubkey1, pubkey2);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! encode_test_body {
|
||||
($mod: ident, $num: ident, $bar: ident, $num64: ident, $size: expr) => {
|
||||
let fname = format!("testdata/rsa/sign{}.test", $size);
|
||||
run_test(fname.to_string(), 7, |case| {
|
||||
let (neg0, nbytes) = case.get("n").unwrap();
|
||||
let (neg1, ubytes) = case.get("u").unwrap();
|
||||
let (neg2, kbytes) = case.get("k").unwrap();
|
||||
|
||||
assert!(!neg0&&!neg1&&!neg2);
|
||||
let n = $num::from_bytes(nbytes);
|
||||
let n64 = $num64::from(&n);
|
||||
let nu = $num64::from_bytes(ubytes);
|
||||
let bigk = $num::from_bytes(kbytes);
|
||||
let k = usize::from(bigk);
|
||||
let e = $num::from(65537u64);
|
||||
let pubkey = RSAPublicKey{ n: n, nu: $bar::from_components(k, n64, nu), e: e };
|
||||
let asn1 = pubkey.to_asn1().unwrap();
|
||||
let (pubkey2, _) = RSAPublicKey::from_asn1(&asn1).unwrap();
|
||||
assert_eq!(pubkey, pubkey2);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! verify_test_body {
|
||||
($mod: ident, $num: ident, $bar: ident, $num64: ident, $size: expr) => {
|
||||
let fname = format!("testdata/rsa/sign{}.test", $size);
|
||||
run_test(fname.to_string(), 7, |case| {
|
||||
let (neg0, nbytes) = case.get("n").unwrap();
|
||||
let (neg1, hbytes) = case.get("h").unwrap();
|
||||
let (neg2, mbytes) = case.get("m").unwrap();
|
||||
let (neg3, sbytes) = case.get("s").unwrap();
|
||||
let (neg4, ubytes) = case.get("u").unwrap();
|
||||
let (neg5, kbytes) = case.get("k").unwrap();
|
||||
|
||||
assert!(!neg0 && !neg1 && !neg2 && !neg3 && !neg4 && !neg5);
|
||||
let n = $num::from_bytes(nbytes);
|
||||
let n64 = $num64::from(&n);
|
||||
let nu = $num64::from_bytes(ubytes);
|
||||
let bigk = $num::from_bytes(kbytes);
|
||||
let k = usize::from(bigk);
|
||||
let e = $num::from(65537u64);
|
||||
let pubkey = RSAPublicKey{ n: n, nu: $bar::from_components(k, n64, nu), e: e };
|
||||
let hashnum = u64::from($num::from_bytes(hbytes));
|
||||
let sighash = match hashnum {
|
||||
160 => &SIGNING_HASH_SHA1,
|
||||
224 => &SIGNING_HASH_SHA224,
|
||||
256 => &SIGNING_HASH_SHA256,
|
||||
384 => &SIGNING_HASH_SHA384,
|
||||
512 => &SIGNING_HASH_SHA512,
|
||||
_ => panic!("Bad signing hash: {}", hashnum)
|
||||
};
|
||||
assert!(pubkey.verify(sighash, &mbytes, &sbytes));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! encrypt_test_body {
|
||||
($mod: ident, $num: ident, $bar: ident, $num64: ident, $size: expr) => {
|
||||
let fname = format!("testdata/rsa/encrypt{}.test", $size);
|
||||
run_test(fname.to_string(), 9, |case| {
|
||||
let (neg0, nbytes) = case.get("n").unwrap();
|
||||
let (neg1, hbytes) = case.get("h").unwrap();
|
||||
let (neg2, mbytes) = case.get("m").unwrap();
|
||||
let (neg3, _bytes) = case.get("e").unwrap();
|
||||
let (neg4, ubytes) = case.get("u").unwrap();
|
||||
let (neg5, kbytes) = case.get("k").unwrap();
|
||||
let (neg6, dbytes) = case.get("d").unwrap();
|
||||
let (neg7, lbytes) = case.get("l").unwrap();
|
||||
|
||||
assert!(!neg0 && !neg1 && !neg2 && !neg3 && !neg4 && !neg5 && !neg6 && !neg7);
|
||||
let n = $num::from_bytes(nbytes);
|
||||
let n64 = $num64::from(&n);
|
||||
let nu = $num64::from_bytes(ubytes);
|
||||
let bigk = $num::from_bytes(kbytes);
|
||||
let k = usize::from(bigk);
|
||||
let e = $num::from(65537u64);
|
||||
let d = $num::from_bytes(dbytes);
|
||||
let nu = $bar::from_components(k, n64, nu);
|
||||
let pubkey = RSAPublicKey{ n: n.clone(), nu: nu.clone(), e: e };
|
||||
let privkey = RSAPrivateKey{ nu: nu, d: d };
|
||||
let lstr = String::from_utf8(lbytes.clone()).unwrap();
|
||||
let cipher = match usize::from($num::from_bytes(hbytes)) {
|
||||
224 => pubkey.encrypt(&OAEPParams::<SHA224>::new(lstr.clone()), mbytes),
|
||||
256 => pubkey.encrypt(&OAEPParams::<SHA256>::new(lstr.clone()), mbytes),
|
||||
384 => pubkey.encrypt(&OAEPParams::<SHA384>::new(lstr.clone()), mbytes),
|
||||
512 => pubkey.encrypt(&OAEPParams::<SHA512>::new(lstr.clone()), mbytes),
|
||||
x => panic!("Unknown hash number: {}", x)
|
||||
};
|
||||
assert!(cipher.is_ok());
|
||||
let message = match usize::from($num::from_bytes(hbytes)) {
|
||||
224 => privkey.decrypt(&OAEPParams::<SHA224>::new(lstr), &cipher.unwrap()),
|
||||
256 => privkey.decrypt(&OAEPParams::<SHA256>::new(lstr), &cipher.unwrap()),
|
||||
384 => privkey.decrypt(&OAEPParams::<SHA384>::new(lstr), &cipher.unwrap()),
|
||||
512 => privkey.decrypt(&OAEPParams::<SHA512>::new(lstr), &cipher.unwrap()),
|
||||
x => panic!("Unknown hash number: {}", x)
|
||||
};
|
||||
assert!(message.is_ok());
|
||||
assert_eq!(mbytes, &message.unwrap());
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! generate_tests {
|
||||
($mod: ident, $num: ident, $bar: ident, $num64: ident, $size: expr) => {
|
||||
#[cfg(test)]
|
||||
#[allow(non_snake_case)]
|
||||
mod $mod {
|
||||
use cryptonum::unsigned::Decoder;
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
use rsa::private::*;
|
||||
use rsa::signing_hashes::*;
|
||||
use sha::{SHA224,SHA256,SHA384,SHA512};
|
||||
|
||||
#[test]
|
||||
fn new() { new_test_body!($mod, $num, $bar, $num64, $size); }
|
||||
#[test]
|
||||
fn encode() { encode_test_body!($mod, $num, $bar, $num64, $size); }
|
||||
#[test]
|
||||
fn verify() { verify_test_body!($mod, $num, $bar, $num64, $size); }
|
||||
#[test]
|
||||
fn encrypt() { encrypt_test_body!($mod, $num, $bar, $num64, $size); }
|
||||
}
|
||||
};
|
||||
(ignore $mod: ident, $num: ident, $bar: ident, $num64: ident, $size: expr) => {
|
||||
#[cfg(test)]
|
||||
#[allow(non_snake_case)]
|
||||
mod $mod {
|
||||
use cryptonum::unsigned::Decoder;
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
use rsa::private::*;
|
||||
use rsa::signing_hashes::*;
|
||||
use sha::{SHA224,SHA256,SHA384,SHA512};
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn new() { new_test_body!($mod, $num, $bar, $num64, $size); }
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn encode() { encode_test_body!($mod, $num, $bar, $num64, $size); }
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn verify() { verify_test_body!($mod, $num, $bar, $num64, $size); }
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn encrypt() { encrypt_test_body!($mod, $num, $bar, $num64, $size); }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
generate_tests!( RSA512, U512, BarrettU512, U576, 512);
|
||||
generate_tests!( RSA1024, U1024, BarrettU1024, U1088, 1024);
|
||||
generate_tests!( RSA2048, U2048, BarrettU2048, U2112, 2048);
|
||||
generate_tests!( RSA3072, U3072, BarrettU3072, U3136, 3072);
|
||||
generate_tests!( RSA4096, U4096, BarrettU4096, U4160, 4096);
|
||||
generate_tests!(ignore RSA8192, U8192, BarrettU8192, U8256, 8192);
|
||||
generate_tests!(ignore RSA15360, U15360, BarrettU15360, U15424, 15360);
|
||||
@@ -1,6 +1,4 @@
|
||||
use digest::{FixedOutput,Input};
|
||||
use sha1::Sha1;
|
||||
use sha2::{Sha224,Sha256,Sha384,Sha512};
|
||||
use sha::{Hash,SHA1,SHA224,SHA256,SHA384,SHA512};
|
||||
use std::fmt;
|
||||
|
||||
/// A hash that can be used to sign a message.
|
||||
@@ -28,13 +26,9 @@ impl fmt::Debug for SigningHash {
|
||||
pub static SIGNING_HASH_NULL: SigningHash = SigningHash {
|
||||
name: "NULL",
|
||||
ident: &[],
|
||||
run: nohash
|
||||
run: |x| { x.to_vec() }
|
||||
};
|
||||
|
||||
fn nohash(i: &[u8]) -> Vec<u8> {
|
||||
i.to_vec()
|
||||
}
|
||||
|
||||
/// Sign a hash based on SHA1. You shouldn't use this unless you're using
|
||||
/// very small keys, and this is the only one available to you. Even then,
|
||||
/// why are you using such small keys?!
|
||||
@@ -42,15 +36,9 @@ pub static SIGNING_HASH_SHA1: SigningHash = SigningHash {
|
||||
name: "SHA1",
|
||||
ident: &[0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,
|
||||
0x02,0x1a,0x05,0x00,0x04,0x14],
|
||||
run: runsha1
|
||||
run: |x| { SHA1::hash(x) }
|
||||
};
|
||||
|
||||
fn runsha1(i: &[u8]) -> Vec<u8> {
|
||||
let mut d = Sha1::default();
|
||||
d.process(i);
|
||||
d.fixed_result().as_slice().to_vec()
|
||||
}
|
||||
|
||||
/// Sign a hash based on SHA2-224. This is the first reasonable choice
|
||||
/// we've come across, and is useful when you have smaller RSA key sizes.
|
||||
/// I wouldn't recommend it, though.
|
||||
@@ -59,30 +47,18 @@ pub static SIGNING_HASH_SHA224: SigningHash = SigningHash {
|
||||
ident: &[0x30,0x2d,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,
|
||||
0x01,0x65,0x03,0x04,0x02,0x04,0x05,0x00,0x04,
|
||||
0x1c],
|
||||
run: runsha224
|
||||
run: |x| { SHA224::hash(x) }
|
||||
};
|
||||
|
||||
fn runsha224(i: &[u8]) -> Vec<u8> {
|
||||
let mut d = Sha224::default();
|
||||
d.process(i);
|
||||
d.fixed_result().as_slice().to_vec()
|
||||
}
|
||||
|
||||
/// Sign a hash based on SHA2-256. The first one I'd recommend!
|
||||
pub static SIGNING_HASH_SHA256: SigningHash = SigningHash {
|
||||
name: "SHA256",
|
||||
ident: &[0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,
|
||||
0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,
|
||||
0x20],
|
||||
run: runsha256
|
||||
run: |x| { SHA256::hash(x) }
|
||||
};
|
||||
|
||||
fn runsha256(i: &[u8]) -> Vec<u8> {
|
||||
let mut d = Sha256::default();
|
||||
d.process(i);
|
||||
d.fixed_result().as_slice().to_vec()
|
||||
}
|
||||
|
||||
/// Sign a hash based on SHA2-384. Approximately 50% better than
|
||||
/// SHA-256.
|
||||
pub static SIGNING_HASH_SHA384: SigningHash = SigningHash {
|
||||
@@ -90,15 +66,9 @@ pub static SIGNING_HASH_SHA384: SigningHash = SigningHash {
|
||||
ident: &[0x30,0x41,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,
|
||||
0x01,0x65,0x03,0x04,0x02,0x02,0x05,0x00,0x04,
|
||||
0x30],
|
||||
run: runsha384
|
||||
run: |x| { SHA384::hash(x) }
|
||||
};
|
||||
|
||||
fn runsha384(i: &[u8]) -> Vec<u8> {
|
||||
let mut d = Sha384::default();
|
||||
d.process(i);
|
||||
d.fixed_result().as_slice().to_vec()
|
||||
}
|
||||
|
||||
/// Sign a hash based on SHA2-512. At this point, you're getting a bit
|
||||
/// silly. But if you want to through 8kbit RSA keys with a 512 bit SHA2
|
||||
/// signing hash, we're totally behind you.
|
||||
@@ -107,13 +77,5 @@ pub static SIGNING_HASH_SHA512: SigningHash = SigningHash {
|
||||
ident: &[0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,
|
||||
0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,
|
||||
0x40],
|
||||
run: runsha512
|
||||
};
|
||||
|
||||
fn runsha512(i: &[u8]) -> Vec<u8> {
|
||||
let mut d = Sha512::default();
|
||||
d.process(i);
|
||||
d.fixed_result().as_slice().to_vec()
|
||||
}
|
||||
|
||||
|
||||
run: |x| { SHA512::hash(x) }
|
||||
};
|
||||
64
src/sha/mod.rs
Normal file
64
src/sha/mod.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
//! The SHA family of hash functions, as defined by NIST; specifically, from
|
||||
//! NIST 180-4 (for SHA1 and SHA2) and NIST 202 (for SHA3).
|
||||
//!
|
||||
//! These hash functions are used through their instantiation of the `Hash`
|
||||
//! trait, located in the parent `simple_crypto` module and re-exported
|
||||
//! here for convenience. Thus, you're not going to see a lot of functions
|
||||
//! or macros, here, just the type declarations.
|
||||
//!
|
||||
//! To use SHA2-384, as an example, you could run the following code:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use simple_crypto::sha::{Hash,SHA384};
|
||||
//!
|
||||
//! let empty: [u8; 0] = [0; 0];
|
||||
//! let mut digest_incremental = SHA384::new();
|
||||
//! digest_incremental.update(&empty);
|
||||
//! digest_incremental.update(&empty);
|
||||
//! digest_incremental.update(&empty);
|
||||
//! let result = digest_incremental.finalize();
|
||||
//!
|
||||
//! assert_eq!(result, vec![0x38,0xb0,0x60,0xa7,0x51,0xac,0x96,0x38,
|
||||
//! 0x4c,0xd9,0x32,0x7e,0xb1,0xb1,0xe3,0x6a,
|
||||
//! 0x21,0xfd,0xb7,0x11,0x14,0xbe,0x07,0x43,
|
||||
//! 0x4c,0x0c,0xc7,0xbf,0x63,0xf6,0xe1,0xda,
|
||||
//! 0x27,0x4e,0xde,0xbf,0xe7,0x6f,0x65,0xfb,
|
||||
//! 0xd5,0x1a,0xd2,0xf1,0x48,0x98,0xb9,0x5b]);
|
||||
//! ```
|
||||
//!
|
||||
//! For other hashes, just substitute the appropriate hash structure for
|
||||
//! `SHA384`. The `Hash` trait also includes a do-it-all-at-once built-in
|
||||
//! function for those cases when you just have a single blob of data
|
||||
//! you want to hash, rather than an incremental set of data:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use simple_crypto::sha::{Hash,SHA3_256};
|
||||
//!
|
||||
//! let empty: [u8; 0] = [0; 0];
|
||||
//! let result = SHA3_256::hash(&empty);
|
||||
//!
|
||||
//! assert_eq!(result, vec![0xa7,0xff,0xc6,0xf8,0xbf,0x1e,0xd7,0x66,
|
||||
//! 0x51,0xc1,0x47,0x56,0xa0,0x61,0xd6,0x62,
|
||||
//! 0xf5,0x80,0xff,0x4d,0xe4,0x3b,0x49,0xfa,
|
||||
//! 0x82,0xd8,0x0a,0x4b,0x80,0xf8,0x43,0x4a]);
|
||||
//! ```
|
||||
//!
|
||||
//! In general, you should not use SHA1 for anything but supporting legacy
|
||||
//! systems. We recommend either SHA2 or SHA3 at their 256-, 384-, or 512-bit
|
||||
//! sizes. NIST claims (in FIPS 202, page 23-24) that SHA2 and SHA3 are
|
||||
//! approximately equivalent in terms of security for collision, preimate,
|
||||
//! and second preimage attacks, but that SHA3 improves upon SHA2 against
|
||||
//! length-extension and other attacks. On the other hand, SHA2 has been
|
||||
//! banged on for a little longer, and there's some claims that it's more
|
||||
//! resistant to quantum attacks. So ... make your own decisions.
|
||||
#[macro_use]
|
||||
mod shared;
|
||||
mod sha1;
|
||||
mod sha2;
|
||||
mod sha3;
|
||||
|
||||
pub use super::Hash;
|
||||
pub use self::sha1::SHA1;
|
||||
pub use self::sha2::{SHA224,SHA256,SHA384,SHA512};
|
||||
pub use self::sha3::{SHA3_224,SHA3_256,SHA3_384,SHA3_512};
|
||||
pub(crate) use self::sha3::Keccak;
|
||||
306
src/sha/sha1.rs
Normal file
306
src/sha/sha1.rs
Normal file
@@ -0,0 +1,306 @@
|
||||
use byteorder::{BigEndian,WriteBytesExt};
|
||||
use super::super::Hash;
|
||||
use sha::shared::calculate_k;
|
||||
|
||||
/// The SHA1 hash. Don't use this except to support legacy systems.
|
||||
///
|
||||
/// To use, you can run it in incremental mode -- by calling new(),
|
||||
/// update() zero or more times, and then finalize() -- or you can
|
||||
/// just invoke the hash directly. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use simple_crypto::sha::{Hash,SHA1};
|
||||
///
|
||||
/// let empty = [0; 0];
|
||||
/// // Do the hash using the incremental API
|
||||
/// let mut hashf = SHA1::new();
|
||||
/// hashf.update(&empty);
|
||||
/// let result_incremental = hashf.finalize();
|
||||
/// // Do the hash using the direct API
|
||||
/// let result_direct = SHA1::hash(&empty);
|
||||
/// // ... and they should be the same
|
||||
/// assert_eq!(result_incremental,result_direct);
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct SHA1 {
|
||||
state: [u32; 5],
|
||||
buffer: Vec<u8>,
|
||||
done: bool,
|
||||
l: usize
|
||||
}
|
||||
|
||||
macro_rules! sha1_step {
|
||||
($op: ident, $out: ident, $ins: expr, $k: expr, $w: ident) => {
|
||||
let $out = {
|
||||
let [a,b,c,d,e] = $ins;
|
||||
let ap = a.rotate_left(5) + $op!(b,c,d) + e + $k + $w;
|
||||
let bp = a;
|
||||
let cp = b.rotate_left(30);
|
||||
let dp = c;
|
||||
let ep = d;
|
||||
[ap, bp, cp, dp, ep]
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
impl SHA1 {
|
||||
fn process(&mut self, w00: u32, w01: u32, w02: u32, w03: u32,
|
||||
w04: u32, w05: u32, w06: u32, w07: u32,
|
||||
w08: u32, w09: u32, w10: u32, w11: u32,
|
||||
w12: u32, w13: u32, w14: u32, w15: u32)
|
||||
{
|
||||
let w16 = (w13 ^ w08 ^ w02 ^ w00).rotate_left(1);
|
||||
let w17 = (w14 ^ w09 ^ w03 ^ w01).rotate_left(1);
|
||||
let w18 = (w15 ^ w10 ^ w04 ^ w02).rotate_left(1);
|
||||
let w19 = (w16 ^ w11 ^ w05 ^ w03).rotate_left(1);
|
||||
let w20 = (w17 ^ w12 ^ w06 ^ w04).rotate_left(1);
|
||||
let w21 = (w18 ^ w13 ^ w07 ^ w05).rotate_left(1);
|
||||
let w22 = (w19 ^ w14 ^ w08 ^ w06).rotate_left(1);
|
||||
let w23 = (w20 ^ w15 ^ w09 ^ w07).rotate_left(1);
|
||||
let w24 = (w21 ^ w16 ^ w10 ^ w08).rotate_left(1);
|
||||
let w25 = (w22 ^ w17 ^ w11 ^ w09).rotate_left(1);
|
||||
let w26 = (w23 ^ w18 ^ w12 ^ w10).rotate_left(1);
|
||||
let w27 = (w24 ^ w19 ^ w13 ^ w11).rotate_left(1);
|
||||
let w28 = (w25 ^ w20 ^ w14 ^ w12).rotate_left(1);
|
||||
let w29 = (w26 ^ w21 ^ w15 ^ w13).rotate_left(1);
|
||||
let w30 = (w27 ^ w22 ^ w16 ^ w14).rotate_left(1);
|
||||
let w31 = (w28 ^ w23 ^ w17 ^ w15).rotate_left(1);
|
||||
let w32 = (w29 ^ w24 ^ w18 ^ w16).rotate_left(1);
|
||||
let w33 = (w30 ^ w25 ^ w19 ^ w17).rotate_left(1);
|
||||
let w34 = (w31 ^ w26 ^ w20 ^ w18).rotate_left(1);
|
||||
let w35 = (w32 ^ w27 ^ w21 ^ w19).rotate_left(1);
|
||||
let w36 = (w33 ^ w28 ^ w22 ^ w20).rotate_left(1);
|
||||
let w37 = (w34 ^ w29 ^ w23 ^ w21).rotate_left(1);
|
||||
let w38 = (w35 ^ w30 ^ w24 ^ w22).rotate_left(1);
|
||||
let w39 = (w36 ^ w31 ^ w25 ^ w23).rotate_left(1);
|
||||
let w40 = (w37 ^ w32 ^ w26 ^ w24).rotate_left(1);
|
||||
let w41 = (w38 ^ w33 ^ w27 ^ w25).rotate_left(1);
|
||||
let w42 = (w39 ^ w34 ^ w28 ^ w26).rotate_left(1);
|
||||
let w43 = (w40 ^ w35 ^ w29 ^ w27).rotate_left(1);
|
||||
let w44 = (w41 ^ w36 ^ w30 ^ w28).rotate_left(1);
|
||||
let w45 = (w42 ^ w37 ^ w31 ^ w29).rotate_left(1);
|
||||
let w46 = (w43 ^ w38 ^ w32 ^ w30).rotate_left(1);
|
||||
let w47 = (w44 ^ w39 ^ w33 ^ w31).rotate_left(1);
|
||||
let w48 = (w45 ^ w40 ^ w34 ^ w32).rotate_left(1);
|
||||
let w49 = (w46 ^ w41 ^ w35 ^ w33).rotate_left(1);
|
||||
let w50 = (w47 ^ w42 ^ w36 ^ w34).rotate_left(1);
|
||||
let w51 = (w48 ^ w43 ^ w37 ^ w35).rotate_left(1);
|
||||
let w52 = (w49 ^ w44 ^ w38 ^ w36).rotate_left(1);
|
||||
let w53 = (w50 ^ w45 ^ w39 ^ w37).rotate_left(1);
|
||||
let w54 = (w51 ^ w46 ^ w40 ^ w38).rotate_left(1);
|
||||
let w55 = (w52 ^ w47 ^ w41 ^ w39).rotate_left(1);
|
||||
let w56 = (w53 ^ w48 ^ w42 ^ w40).rotate_left(1);
|
||||
let w57 = (w54 ^ w49 ^ w43 ^ w41).rotate_left(1);
|
||||
let w58 = (w55 ^ w50 ^ w44 ^ w42).rotate_left(1);
|
||||
let w59 = (w56 ^ w51 ^ w45 ^ w43).rotate_left(1);
|
||||
let w60 = (w57 ^ w52 ^ w46 ^ w44).rotate_left(1);
|
||||
let w61 = (w58 ^ w53 ^ w47 ^ w45).rotate_left(1);
|
||||
let w62 = (w59 ^ w54 ^ w48 ^ w46).rotate_left(1);
|
||||
let w63 = (w60 ^ w55 ^ w49 ^ w47).rotate_left(1);
|
||||
let w64 = (w61 ^ w56 ^ w50 ^ w48).rotate_left(1);
|
||||
let w65 = (w62 ^ w57 ^ w51 ^ w49).rotate_left(1);
|
||||
let w66 = (w63 ^ w58 ^ w52 ^ w50).rotate_left(1);
|
||||
let w67 = (w64 ^ w59 ^ w53 ^ w51).rotate_left(1);
|
||||
let w68 = (w65 ^ w60 ^ w54 ^ w52).rotate_left(1);
|
||||
let w69 = (w66 ^ w61 ^ w55 ^ w53).rotate_left(1);
|
||||
let w70 = (w67 ^ w62 ^ w56 ^ w54).rotate_left(1);
|
||||
let w71 = (w68 ^ w63 ^ w57 ^ w55).rotate_left(1);
|
||||
let w72 = (w69 ^ w64 ^ w58 ^ w56).rotate_left(1);
|
||||
let w73 = (w70 ^ w65 ^ w59 ^ w57).rotate_left(1);
|
||||
let w74 = (w71 ^ w66 ^ w60 ^ w58).rotate_left(1);
|
||||
let w75 = (w72 ^ w67 ^ w61 ^ w59).rotate_left(1);
|
||||
let w76 = (w73 ^ w68 ^ w62 ^ w60).rotate_left(1);
|
||||
let w77 = (w74 ^ w69 ^ w63 ^ w61).rotate_left(1);
|
||||
let w78 = (w75 ^ w70 ^ w64 ^ w62).rotate_left(1);
|
||||
let w79 = (w76 ^ w71 ^ w65 ^ w63).rotate_left(1);
|
||||
sha1_step!(ch, s01, self.state, 0x5a827999, w00);
|
||||
sha1_step!(ch, s02, s01, 0x5a827999, w01);
|
||||
sha1_step!(ch, s03, s02, 0x5a827999, w02);
|
||||
sha1_step!(ch, s04, s03, 0x5a827999, w03);
|
||||
sha1_step!(ch, s05, s04, 0x5a827999, w04);
|
||||
sha1_step!(ch, s06, s05, 0x5a827999, w05);
|
||||
sha1_step!(ch, s07, s06, 0x5a827999, w06);
|
||||
sha1_step!(ch, s08, s07, 0x5a827999, w07);
|
||||
sha1_step!(ch, s09, s08, 0x5a827999, w08);
|
||||
sha1_step!(ch, s10, s09, 0x5a827999, w09);
|
||||
sha1_step!(ch, s11, s10, 0x5a827999, w10);
|
||||
sha1_step!(ch, s12, s11, 0x5a827999, w11);
|
||||
sha1_step!(ch, s13, s12, 0x5a827999, w12);
|
||||
sha1_step!(ch, s14, s13, 0x5a827999, w13);
|
||||
sha1_step!(ch, s15, s14, 0x5a827999, w14);
|
||||
sha1_step!(ch, s16, s15, 0x5a827999, w15);
|
||||
sha1_step!(ch, s17, s16, 0x5a827999, w16);
|
||||
sha1_step!(ch, s18, s17, 0x5a827999, w17);
|
||||
sha1_step!(ch, s19, s18, 0x5a827999, w18);
|
||||
sha1_step!(ch, s20, s19, 0x5a827999, w19);
|
||||
sha1_step!(parity, s21, s20, 0x6ed9eba1, w20);
|
||||
sha1_step!(parity, s22, s21, 0x6ed9eba1, w21);
|
||||
sha1_step!(parity, s23, s22, 0x6ed9eba1, w22);
|
||||
sha1_step!(parity, s24, s23, 0x6ed9eba1, w23);
|
||||
sha1_step!(parity, s25, s24, 0x6ed9eba1, w24);
|
||||
sha1_step!(parity, s26, s25, 0x6ed9eba1, w25);
|
||||
sha1_step!(parity, s27, s26, 0x6ed9eba1, w26);
|
||||
sha1_step!(parity, s28, s27, 0x6ed9eba1, w27);
|
||||
sha1_step!(parity, s29, s28, 0x6ed9eba1, w28);
|
||||
sha1_step!(parity, s30, s29, 0x6ed9eba1, w29);
|
||||
sha1_step!(parity, s31, s30, 0x6ed9eba1, w30);
|
||||
sha1_step!(parity, s32, s31, 0x6ed9eba1, w31);
|
||||
sha1_step!(parity, s33, s32, 0x6ed9eba1, w32);
|
||||
sha1_step!(parity, s34, s33, 0x6ed9eba1, w33);
|
||||
sha1_step!(parity, s35, s34, 0x6ed9eba1, w34);
|
||||
sha1_step!(parity, s36, s35, 0x6ed9eba1, w35);
|
||||
sha1_step!(parity, s37, s36, 0x6ed9eba1, w36);
|
||||
sha1_step!(parity, s38, s37, 0x6ed9eba1, w37);
|
||||
sha1_step!(parity, s39, s38, 0x6ed9eba1, w38);
|
||||
sha1_step!(parity, s40, s39, 0x6ed9eba1, w39);
|
||||
sha1_step!(maj, s41, s40, 0x8f1bbcdc, w40);
|
||||
sha1_step!(maj, s42, s41, 0x8f1bbcdc, w41);
|
||||
sha1_step!(maj, s43, s42, 0x8f1bbcdc, w42);
|
||||
sha1_step!(maj, s44, s43, 0x8f1bbcdc, w43);
|
||||
sha1_step!(maj, s45, s44, 0x8f1bbcdc, w44);
|
||||
sha1_step!(maj, s46, s45, 0x8f1bbcdc, w45);
|
||||
sha1_step!(maj, s47, s46, 0x8f1bbcdc, w46);
|
||||
sha1_step!(maj, s48, s47, 0x8f1bbcdc, w47);
|
||||
sha1_step!(maj, s49, s48, 0x8f1bbcdc, w48);
|
||||
sha1_step!(maj, s50, s49, 0x8f1bbcdc, w49);
|
||||
sha1_step!(maj, s51, s50, 0x8f1bbcdc, w50);
|
||||
sha1_step!(maj, s52, s51, 0x8f1bbcdc, w51);
|
||||
sha1_step!(maj, s53, s52, 0x8f1bbcdc, w52);
|
||||
sha1_step!(maj, s54, s53, 0x8f1bbcdc, w53);
|
||||
sha1_step!(maj, s55, s54, 0x8f1bbcdc, w54);
|
||||
sha1_step!(maj, s56, s55, 0x8f1bbcdc, w55);
|
||||
sha1_step!(maj, s57, s56, 0x8f1bbcdc, w56);
|
||||
sha1_step!(maj, s58, s57, 0x8f1bbcdc, w57);
|
||||
sha1_step!(maj, s59, s58, 0x8f1bbcdc, w58);
|
||||
sha1_step!(maj, s60, s59, 0x8f1bbcdc, w59);
|
||||
sha1_step!(parity, s61, s60, 0xca62c1d6, w60);
|
||||
sha1_step!(parity, s62, s61, 0xca62c1d6, w61);
|
||||
sha1_step!(parity, s63, s62, 0xca62c1d6, w62);
|
||||
sha1_step!(parity, s64, s63, 0xca62c1d6, w63);
|
||||
sha1_step!(parity, s65, s64, 0xca62c1d6, w64);
|
||||
sha1_step!(parity, s66, s65, 0xca62c1d6, w65);
|
||||
sha1_step!(parity, s67, s66, 0xca62c1d6, w66);
|
||||
sha1_step!(parity, s68, s67, 0xca62c1d6, w67);
|
||||
sha1_step!(parity, s69, s68, 0xca62c1d6, w68);
|
||||
sha1_step!(parity, s70, s69, 0xca62c1d6, w69);
|
||||
sha1_step!(parity, s71, s70, 0xca62c1d6, w70);
|
||||
sha1_step!(parity, s72, s71, 0xca62c1d6, w71);
|
||||
sha1_step!(parity, s73, s72, 0xca62c1d6, w72);
|
||||
sha1_step!(parity, s74, s73, 0xca62c1d6, w73);
|
||||
sha1_step!(parity, s75, s74, 0xca62c1d6, w74);
|
||||
sha1_step!(parity, s76, s75, 0xca62c1d6, w75);
|
||||
sha1_step!(parity, s77, s76, 0xca62c1d6, w76);
|
||||
sha1_step!(parity, s78, s77, 0xca62c1d6, w77);
|
||||
sha1_step!(parity, s79, s78, 0xca62c1d6, w78);
|
||||
sha1_step!(parity, s80, s79, 0xca62c1d6, w79);
|
||||
self.state[0] += s80[0];
|
||||
self.state[1] += s80[1];
|
||||
self.state[2] += s80[2];
|
||||
self.state[3] += s80[3];
|
||||
self.state[4] += s80[4];
|
||||
}
|
||||
|
||||
fn finish(&mut self)
|
||||
{
|
||||
let bitlen = self.l * 8;
|
||||
let k = calculate_k(448, 512, bitlen);
|
||||
// INVARIANT: k is necessarily > 0, and (k + 1) is a multiple of 8
|
||||
let bytes_to_add = (k + 1) / 8;
|
||||
let mut padvec = Vec::with_capacity(bytes_to_add + 8);
|
||||
padvec.push(0x80); // Set the high bit, since the first bit after the data
|
||||
// should be set
|
||||
padvec.resize(bytes_to_add, 0);
|
||||
padvec.write_u64::<BigEndian>(bitlen as u64).expect("Broken writing value to pre-allocated Vec?");
|
||||
self.update(&padvec);
|
||||
self.done = true;
|
||||
assert_eq!(self.buffer.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for SHA1
|
||||
{
|
||||
fn new() -> SHA1
|
||||
{
|
||||
SHA1 {
|
||||
state: [ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ],
|
||||
buffer: Vec::with_capacity(64),
|
||||
done: false,
|
||||
l: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, block: &[u8])
|
||||
{
|
||||
if !self.done {
|
||||
let mut offset = 0;
|
||||
|
||||
self.l += block.len();
|
||||
|
||||
if self.buffer.len() + block.len() < 64 {
|
||||
self.buffer.extend_from_slice(block);
|
||||
return;
|
||||
}
|
||||
|
||||
if self.buffer.len() > 0 {
|
||||
// We must be able to build up a 64 byte chunk, at this point, otherwise
|
||||
// the math above would've been wrong.
|
||||
while self.buffer.len() < 64 {
|
||||
self.buffer.push(block[offset]);
|
||||
offset += 1;
|
||||
}
|
||||
process_u32_block!(self.buffer, 0, self);
|
||||
// Reset the buffer now, we're done with that nonsense for the moment
|
||||
self.buffer.resize(0,0);
|
||||
}
|
||||
|
||||
while (offset + 64) <= block.len() {
|
||||
process_u32_block!(block, offset, self);
|
||||
offset += 64;
|
||||
}
|
||||
|
||||
if offset < block.len() {
|
||||
self.buffer.extend_from_slice(&block[offset..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn finalize(&mut self) -> Vec<u8>
|
||||
{
|
||||
if !self.done {
|
||||
self.finish();
|
||||
}
|
||||
|
||||
let mut output = Vec::with_capacity(20);
|
||||
output.write_u32::<BigEndian>(self.state[0]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state[1]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state[2]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state[3]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state[4]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output
|
||||
}
|
||||
|
||||
fn block_size() -> usize
|
||||
{
|
||||
512
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use testing::run_test;
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn nist() {
|
||||
let fname = "testdata/sha/nist_sha1.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negl, lbytes) = case.get("l").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!negl && !negm && !negd);
|
||||
let msg = if lbytes[0] == 0 { Vec::new() } else { mbytes.clone() };
|
||||
let digest = SHA1::hash(&msg);
|
||||
assert_eq!(dbytes, &digest);
|
||||
});
|
||||
}
|
||||
|
||||
842
src/sha/sha2.rs
Normal file
842
src/sha/sha2.rs
Normal file
@@ -0,0 +1,842 @@
|
||||
use byteorder::{BigEndian,ByteOrder,WriteBytesExt};
|
||||
use sha::shared::calculate_k;
|
||||
use super::super::Hash;
|
||||
|
||||
/// The SHA2-224 hash.
|
||||
///
|
||||
/// To use, you can run it in incremental mode -- by calling new(),
|
||||
/// update() zero or more times, and then finalize() -- or you can
|
||||
/// just invoke the hash directly. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use simple_crypto::sha::{Hash,SHA224};
|
||||
///
|
||||
/// let empty = [0; 0];
|
||||
/// // Do the hash using the incremental API
|
||||
/// let mut hashf = SHA224::new();
|
||||
/// hashf.update(&empty);
|
||||
/// let result_incremental = hashf.finalize();
|
||||
/// // Do the hash using the direct API
|
||||
/// let result_direct = SHA224::hash(&empty);
|
||||
/// // ... and they should be the same
|
||||
/// assert_eq!(result_incremental,result_direct);
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct SHA224 {
|
||||
state: SHA256State
|
||||
}
|
||||
|
||||
impl Hash for SHA224 {
|
||||
fn new() -> Self
|
||||
{
|
||||
let state = SHA256State::new([0xc1059ed8,0x367cd507,0x3070dd17,0xf70e5939,
|
||||
0xffc00b31,0x68581511,0x64f98fa7,0xbefa4fa4]);
|
||||
SHA224{ state }
|
||||
}
|
||||
|
||||
fn update(&mut self, data: &[u8])
|
||||
{
|
||||
self.state.update(data);
|
||||
}
|
||||
|
||||
fn finalize(&mut self) -> Vec<u8>
|
||||
{
|
||||
if !self.state.done {
|
||||
self.state.finish();
|
||||
}
|
||||
|
||||
let mut output = Vec::with_capacity(28);
|
||||
output.write_u32::<BigEndian>(self.state.state[0]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state.state[1]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state.state[2]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state.state[3]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state.state[4]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state.state[5]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state.state[6]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output
|
||||
}
|
||||
|
||||
fn block_size() -> usize
|
||||
{
|
||||
512
|
||||
}
|
||||
}
|
||||
|
||||
/// The SHA2-256 hash. [GOOD]
|
||||
///
|
||||
/// To use, you can run it in incremental mode -- by calling new(),
|
||||
/// update() zero or more times, and then finalize() -- or you can
|
||||
/// just invoke the hash directly. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use simple_crypto::sha::{Hash,SHA256};
|
||||
///
|
||||
/// let empty = [0; 0];
|
||||
/// // Do the hash using the incremental API
|
||||
/// let mut hashf = SHA256::new();
|
||||
/// hashf.update(&empty);
|
||||
/// let result_incremental = hashf.finalize();
|
||||
/// // Do the hash using the direct API
|
||||
/// let result_direct = SHA256::hash(&empty);
|
||||
/// // ... and they should be the same
|
||||
/// assert_eq!(result_incremental,result_direct);
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct SHA256 {
|
||||
state: SHA256State
|
||||
}
|
||||
|
||||
impl Hash for SHA256 {
|
||||
fn new() -> Self
|
||||
{
|
||||
let state = SHA256State::new([0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,
|
||||
0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19]);
|
||||
SHA256{ state }
|
||||
}
|
||||
|
||||
fn update(&mut self, data: &[u8])
|
||||
{
|
||||
self.state.update(data);
|
||||
}
|
||||
|
||||
fn finalize(&mut self) -> Vec<u8>
|
||||
{
|
||||
if !self.state.done {
|
||||
self.state.finish();
|
||||
}
|
||||
|
||||
let mut output = Vec::with_capacity(28);
|
||||
output.write_u32::<BigEndian>(self.state.state[0]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state.state[1]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state.state[2]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state.state[3]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state.state[4]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state.state[5]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state.state[6]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u32::<BigEndian>(self.state.state[7]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output
|
||||
}
|
||||
|
||||
fn block_size() -> usize
|
||||
{
|
||||
512
|
||||
}
|
||||
}
|
||||
|
||||
/// The SHA2-384 hash. [BETTER]
|
||||
///
|
||||
/// To use, you can run it in incremental mode -- by calling new(),
|
||||
/// update() zero or more times, and then finalize() -- or you can
|
||||
/// just invoke the hash directly. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use simple_crypto::sha::{Hash,SHA384};
|
||||
///
|
||||
/// let empty = [0; 0];
|
||||
/// // Do the hash using the incremental API
|
||||
/// let mut hashf = SHA384::new();
|
||||
/// hashf.update(&empty);
|
||||
/// let result_incremental = hashf.finalize();
|
||||
/// // Do the hash using the direct API
|
||||
/// let result_direct = SHA384::hash(&empty);
|
||||
/// // ... and they should be the same
|
||||
/// assert_eq!(result_incremental,result_direct);
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct SHA384 {
|
||||
state: SHA512State
|
||||
}
|
||||
|
||||
impl Hash for SHA384 {
|
||||
fn new() -> Self
|
||||
{
|
||||
let state = SHA512State::new([0xcbbb9d5dc1059ed8,0x629a292a367cd507,
|
||||
0x9159015a3070dd17,0x152fecd8f70e5939,
|
||||
0x67332667ffc00b31,0x8eb44a8768581511,
|
||||
0xdb0c2e0d64f98fa7,0x47b5481dbefa4fa4]);
|
||||
SHA384{ state }
|
||||
}
|
||||
|
||||
fn update(&mut self, data: &[u8])
|
||||
{
|
||||
self.state.update(data);
|
||||
}
|
||||
|
||||
fn finalize(&mut self) -> Vec<u8>
|
||||
{
|
||||
if !self.state.done {
|
||||
self.state.finish();
|
||||
}
|
||||
|
||||
let mut output = Vec::with_capacity(64);
|
||||
output.write_u64::<BigEndian>(self.state.state[0]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u64::<BigEndian>(self.state.state[1]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u64::<BigEndian>(self.state.state[2]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u64::<BigEndian>(self.state.state[3]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u64::<BigEndian>(self.state.state[4]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u64::<BigEndian>(self.state.state[5]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output
|
||||
}
|
||||
|
||||
fn block_size() -> usize
|
||||
{
|
||||
1024
|
||||
}
|
||||
}
|
||||
|
||||
/// The SHA2-512 hash. [BEST]
|
||||
///
|
||||
/// To use, you can run it in incremental mode -- by calling new(),
|
||||
/// update() zero or more times, and then finalize() -- or you can
|
||||
/// just invoke the hash directly. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use simple_crypto::sha::{Hash,SHA512};
|
||||
///
|
||||
/// let empty = [0; 0];
|
||||
/// // Do the hash using the incremental API
|
||||
/// let mut hashf = SHA512::new();
|
||||
/// hashf.update(&empty);
|
||||
/// let result_incremental = hashf.finalize();
|
||||
/// // Do the hash using the direct API
|
||||
/// let result_direct = SHA512::hash(&empty);
|
||||
/// // ... and they should be the same
|
||||
/// assert_eq!(result_incremental,result_direct);
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct SHA512 {
|
||||
state: SHA512State
|
||||
}
|
||||
|
||||
impl Hash for SHA512 {
|
||||
fn new() -> Self
|
||||
{
|
||||
let state = SHA512State::new([0x6a09e667f3bcc908,0xbb67ae8584caa73b,
|
||||
0x3c6ef372fe94f82b,0xa54ff53a5f1d36f1,
|
||||
0x510e527fade682d1,0x9b05688c2b3e6c1f,
|
||||
0x1f83d9abfb41bd6b,0x5be0cd19137e2179]);
|
||||
SHA512{ state }
|
||||
}
|
||||
|
||||
fn update(&mut self, data: &[u8])
|
||||
{
|
||||
self.state.update(data);
|
||||
}
|
||||
|
||||
fn finalize(&mut self) -> Vec<u8>
|
||||
{
|
||||
if !self.state.done {
|
||||
self.state.finish();
|
||||
}
|
||||
|
||||
let mut output = Vec::with_capacity(64);
|
||||
output.write_u64::<BigEndian>(self.state.state[0]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u64::<BigEndian>(self.state.state[1]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u64::<BigEndian>(self.state.state[2]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u64::<BigEndian>(self.state.state[3]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u64::<BigEndian>(self.state.state[4]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u64::<BigEndian>(self.state.state[5]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u64::<BigEndian>(self.state.state[6]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output.write_u64::<BigEndian>(self.state.state[7]).expect("Broken writing value to pre-allocated Vec?");
|
||||
output
|
||||
}
|
||||
|
||||
fn block_size() -> usize
|
||||
{
|
||||
1024
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! bsig256_0 {
|
||||
($x: ident) => {
|
||||
$x.rotate_right(2) ^ $x.rotate_right(13) ^ $x.rotate_right(22)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! bsig256_1 {
|
||||
($x: ident) => {
|
||||
$x.rotate_right(6) ^ $x.rotate_right(11) ^ $x.rotate_right(25)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! lsig256_0 {
|
||||
($x: ident) => {
|
||||
$x.rotate_right(7) ^ $x.rotate_right(18) ^ ($x >> 3)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! lsig256_1 {
|
||||
($x: ident) => {
|
||||
$x.rotate_right(17) ^ $x.rotate_right(19) ^ ($x >> 10)
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SHA256State {
|
||||
state: [u32; 8],
|
||||
buffer: Vec<u8>,
|
||||
done: bool,
|
||||
l: usize
|
||||
}
|
||||
|
||||
impl SHA256State {
|
||||
fn new(values: [u32; 8]) -> Self {
|
||||
SHA256State {
|
||||
state: values,
|
||||
buffer: Vec::with_capacity(64),
|
||||
done: false,
|
||||
l: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn process(&mut self, w00: u32, w01: u32, w02: u32, w03: u32,
|
||||
w04: u32, w05: u32, w06: u32, w07: u32,
|
||||
w08: u32, w09: u32, w10: u32, w11: u32,
|
||||
w12: u32, w13: u32, w14: u32, w15: u32)
|
||||
{
|
||||
let w16 = lsig256_1!(w14) + w09 + lsig256_0!(w01) + w00;
|
||||
let w17 = lsig256_1!(w15) + w10 + lsig256_0!(w02) + w01;
|
||||
let w18 = lsig256_1!(w16) + w11 + lsig256_0!(w03) + w02;
|
||||
let w19 = lsig256_1!(w17) + w12 + lsig256_0!(w04) + w03;
|
||||
let w20 = lsig256_1!(w18) + w13 + lsig256_0!(w05) + w04;
|
||||
let w21 = lsig256_1!(w19) + w14 + lsig256_0!(w06) + w05;
|
||||
let w22 = lsig256_1!(w20) + w15 + lsig256_0!(w07) + w06;
|
||||
let w23 = lsig256_1!(w21) + w16 + lsig256_0!(w08) + w07;
|
||||
let w24 = lsig256_1!(w22) + w17 + lsig256_0!(w09) + w08;
|
||||
let w25 = lsig256_1!(w23) + w18 + lsig256_0!(w10) + w09;
|
||||
let w26 = lsig256_1!(w24) + w19 + lsig256_0!(w11) + w10;
|
||||
let w27 = lsig256_1!(w25) + w20 + lsig256_0!(w12) + w11;
|
||||
let w28 = lsig256_1!(w26) + w21 + lsig256_0!(w13) + w12;
|
||||
let w29 = lsig256_1!(w27) + w22 + lsig256_0!(w14) + w13;
|
||||
let w30 = lsig256_1!(w28) + w23 + lsig256_0!(w15) + w14;
|
||||
let w31 = lsig256_1!(w29) + w24 + lsig256_0!(w16) + w15;
|
||||
let w32 = lsig256_1!(w30) + w25 + lsig256_0!(w17) + w16;
|
||||
let w33 = lsig256_1!(w31) + w26 + lsig256_0!(w18) + w17;
|
||||
let w34 = lsig256_1!(w32) + w27 + lsig256_0!(w19) + w18;
|
||||
let w35 = lsig256_1!(w33) + w28 + lsig256_0!(w20) + w19;
|
||||
let w36 = lsig256_1!(w34) + w29 + lsig256_0!(w21) + w20;
|
||||
let w37 = lsig256_1!(w35) + w30 + lsig256_0!(w22) + w21;
|
||||
let w38 = lsig256_1!(w36) + w31 + lsig256_0!(w23) + w22;
|
||||
let w39 = lsig256_1!(w37) + w32 + lsig256_0!(w24) + w23;
|
||||
let w40 = lsig256_1!(w38) + w33 + lsig256_0!(w25) + w24;
|
||||
let w41 = lsig256_1!(w39) + w34 + lsig256_0!(w26) + w25;
|
||||
let w42 = lsig256_1!(w40) + w35 + lsig256_0!(w27) + w26;
|
||||
let w43 = lsig256_1!(w41) + w36 + lsig256_0!(w28) + w27;
|
||||
let w44 = lsig256_1!(w42) + w37 + lsig256_0!(w29) + w28;
|
||||
let w45 = lsig256_1!(w43) + w38 + lsig256_0!(w30) + w29;
|
||||
let w46 = lsig256_1!(w44) + w39 + lsig256_0!(w31) + w30;
|
||||
let w47 = lsig256_1!(w45) + w40 + lsig256_0!(w32) + w31;
|
||||
let w48 = lsig256_1!(w46) + w41 + lsig256_0!(w33) + w32;
|
||||
let w49 = lsig256_1!(w47) + w42 + lsig256_0!(w34) + w33;
|
||||
let w50 = lsig256_1!(w48) + w43 + lsig256_0!(w35) + w34;
|
||||
let w51 = lsig256_1!(w49) + w44 + lsig256_0!(w36) + w35;
|
||||
let w52 = lsig256_1!(w50) + w45 + lsig256_0!(w37) + w36;
|
||||
let w53 = lsig256_1!(w51) + w46 + lsig256_0!(w38) + w37;
|
||||
let w54 = lsig256_1!(w52) + w47 + lsig256_0!(w39) + w38;
|
||||
let w55 = lsig256_1!(w53) + w48 + lsig256_0!(w40) + w39;
|
||||
let w56 = lsig256_1!(w54) + w49 + lsig256_0!(w41) + w40;
|
||||
let w57 = lsig256_1!(w55) + w50 + lsig256_0!(w42) + w41;
|
||||
let w58 = lsig256_1!(w56) + w51 + lsig256_0!(w43) + w42;
|
||||
let w59 = lsig256_1!(w57) + w52 + lsig256_0!(w44) + w43;
|
||||
let w60 = lsig256_1!(w58) + w53 + lsig256_0!(w45) + w44;
|
||||
let w61 = lsig256_1!(w59) + w54 + lsig256_0!(w46) + w45;
|
||||
let w62 = lsig256_1!(w60) + w55 + lsig256_0!(w47) + w46;
|
||||
let w63 = lsig256_1!(w61) + w56 + lsig256_0!(w48) + w47;
|
||||
let s01 = step256(self.state,0x428a2f98,w00);
|
||||
let s02 = step256(s01,0x71374491,w01);
|
||||
let s03 = step256(s02,0xb5c0fbcf,w02);
|
||||
let s04 = step256(s03,0xe9b5dba5,w03);
|
||||
let s05 = step256(s04,0x3956c25b,w04);
|
||||
let s06 = step256(s05,0x59f111f1,w05);
|
||||
let s07 = step256(s06,0x923f82a4,w06);
|
||||
let s08 = step256(s07,0xab1c5ed5,w07);
|
||||
let s09 = step256(s08,0xd807aa98,w08);
|
||||
let s10 = step256(s09,0x12835b01,w09);
|
||||
let s11 = step256(s10,0x243185be,w10);
|
||||
let s12 = step256(s11,0x550c7dc3,w11);
|
||||
let s13 = step256(s12,0x72be5d74,w12);
|
||||
let s14 = step256(s13,0x80deb1fe,w13);
|
||||
let s15 = step256(s14,0x9bdc06a7,w14);
|
||||
let s16 = step256(s15,0xc19bf174,w15);
|
||||
let s17 = step256(s16,0xe49b69c1,w16);
|
||||
let s18 = step256(s17,0xefbe4786,w17);
|
||||
let s19 = step256(s18,0x0fc19dc6,w18);
|
||||
let s20 = step256(s19,0x240ca1cc,w19);
|
||||
let s21 = step256(s20,0x2de92c6f,w20);
|
||||
let s22 = step256(s21,0x4a7484aa,w21);
|
||||
let s23 = step256(s22,0x5cb0a9dc,w22);
|
||||
let s24 = step256(s23,0x76f988da,w23);
|
||||
let s25 = step256(s24,0x983e5152,w24);
|
||||
let s26 = step256(s25,0xa831c66d,w25);
|
||||
let s27 = step256(s26,0xb00327c8,w26);
|
||||
let s28 = step256(s27,0xbf597fc7,w27);
|
||||
let s29 = step256(s28,0xc6e00bf3,w28);
|
||||
let s30 = step256(s29,0xd5a79147,w29);
|
||||
let s31 = step256(s30,0x06ca6351,w30);
|
||||
let s32 = step256(s31,0x14292967,w31);
|
||||
let s33 = step256(s32,0x27b70a85,w32);
|
||||
let s34 = step256(s33,0x2e1b2138,w33);
|
||||
let s35 = step256(s34,0x4d2c6dfc,w34);
|
||||
let s36 = step256(s35,0x53380d13,w35);
|
||||
let s37 = step256(s36,0x650a7354,w36);
|
||||
let s38 = step256(s37,0x766a0abb,w37);
|
||||
let s39 = step256(s38,0x81c2c92e,w38);
|
||||
let s40 = step256(s39,0x92722c85,w39);
|
||||
let s41 = step256(s40,0xa2bfe8a1,w40);
|
||||
let s42 = step256(s41,0xa81a664b,w41);
|
||||
let s43 = step256(s42,0xc24b8b70,w42);
|
||||
let s44 = step256(s43,0xc76c51a3,w43);
|
||||
let s45 = step256(s44,0xd192e819,w44);
|
||||
let s46 = step256(s45,0xd6990624,w45);
|
||||
let s47 = step256(s46,0xf40e3585,w46);
|
||||
let s48 = step256(s47,0x106aa070,w47);
|
||||
let s49 = step256(s48,0x19a4c116,w48);
|
||||
let s50 = step256(s49,0x1e376c08,w49);
|
||||
let s51 = step256(s50,0x2748774c,w50);
|
||||
let s52 = step256(s51,0x34b0bcb5,w51);
|
||||
let s53 = step256(s52,0x391c0cb3,w52);
|
||||
let s54 = step256(s53,0x4ed8aa4a,w53);
|
||||
let s55 = step256(s54,0x5b9cca4f,w54);
|
||||
let s56 = step256(s55,0x682e6ff3,w55);
|
||||
let s57 = step256(s56,0x748f82ee,w56);
|
||||
let s58 = step256(s57,0x78a5636f,w57);
|
||||
let s59 = step256(s58,0x84c87814,w58);
|
||||
let s60 = step256(s59,0x8cc70208,w59);
|
||||
let s61 = step256(s60,0x90befffa,w60);
|
||||
let s62 = step256(s61,0xa4506ceb,w61);
|
||||
let s63 = step256(s62,0xbef9a3f7,w62);
|
||||
let s64 = step256(s63,0xc67178f2,w63);
|
||||
self.state[0] += s64[0];
|
||||
self.state[1] += s64[1];
|
||||
self.state[2] += s64[2];
|
||||
self.state[3] += s64[3];
|
||||
self.state[4] += s64[4];
|
||||
self.state[5] += s64[5];
|
||||
self.state[6] += s64[6];
|
||||
self.state[7] += s64[7];
|
||||
}
|
||||
|
||||
fn update(&mut self, block: &[u8]) {
|
||||
if !self.done {
|
||||
let mut offset = 0;
|
||||
|
||||
self.l += block.len();
|
||||
|
||||
if self.buffer.len() + block.len() < 64 {
|
||||
self.buffer.extend_from_slice(block);
|
||||
return;
|
||||
}
|
||||
|
||||
if self.buffer.len() > 0 {
|
||||
// We must be able to build up a 64 byte chunk, at this point, otherwise
|
||||
// the math above would've been wrong.
|
||||
while self.buffer.len() < 64 {
|
||||
self.buffer.push(block[offset]);
|
||||
offset += 1;
|
||||
}
|
||||
process_u32_block!(self.buffer, 0, self);
|
||||
// Reset the buffer now, we're done with that nonsense for the moment
|
||||
self.buffer.resize(0,0);
|
||||
}
|
||||
|
||||
while (offset + 64) <= block.len() {
|
||||
process_u32_block!(block, offset, self);
|
||||
offset += 64;
|
||||
}
|
||||
|
||||
if offset < block.len() {
|
||||
self.buffer.extend_from_slice(&block[offset..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(&mut self) {
|
||||
let bitlen = self.l * 8;
|
||||
let k = calculate_k(448, 512, bitlen);
|
||||
// INVARIANT: k is necessarily > 0, and (k + 1) is a multiple of 8
|
||||
let bytes_to_add = (k + 1) / 8;
|
||||
let mut padvec = Vec::with_capacity(bytes_to_add + 8);
|
||||
padvec.push(0x80); // Set the high bit, since the first bit after the data
|
||||
// should be set
|
||||
padvec.resize(bytes_to_add, 0);
|
||||
padvec.write_u64::<BigEndian>(bitlen as u64).expect("Broken writing value to pre-allocated Vec?");
|
||||
self.update(&padvec);
|
||||
self.done = true;
|
||||
assert_eq!(self.buffer.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn step256(state0: [u32; 8], k: u32, w: u32) -> [u32; 8]
|
||||
{
|
||||
let [a,b,c,d,e,f,g,h] = state0;
|
||||
let t1 = h + bsig256_1!(e) + ch!(e,f,g) + k + w;
|
||||
let t2 = bsig256_0!(a) + maj!(a,b,c);
|
||||
let hp = g;
|
||||
let gp = f;
|
||||
let fp = e;
|
||||
let ep = d + t1;
|
||||
let dp = c;
|
||||
let cp = b;
|
||||
let bp = a;
|
||||
let ap = t1 + t2;
|
||||
[ap,bp,cp,dp,ep,fp,gp,hp]
|
||||
}
|
||||
|
||||
macro_rules! bsig512_0 {
|
||||
($x: ident) => {
|
||||
$x.rotate_right(28) ^ $x.rotate_right(34) ^ $x.rotate_right(39)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! bsig512_1 {
|
||||
($x: ident) => {
|
||||
$x.rotate_right(14) ^ $x.rotate_right(18) ^ $x.rotate_right(41)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! lsig512_0 {
|
||||
($x: ident) => {
|
||||
$x.rotate_right(1) ^ $x.rotate_right(8) ^ ($x >> 7)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! lsig512_1 {
|
||||
($x: ident) => {
|
||||
$x.rotate_right(19) ^ $x.rotate_right(61) ^ ($x >> 6)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! process_u64_block {
|
||||
($buf: expr, $off: expr, $self: ident) => {{
|
||||
let w00 = BigEndian::read_u64(&$buf[$off+0..]);
|
||||
let w01 = BigEndian::read_u64(&$buf[$off+8..]);
|
||||
let w02 = BigEndian::read_u64(&$buf[$off+16..]);
|
||||
let w03 = BigEndian::read_u64(&$buf[$off+24..]);
|
||||
let w04 = BigEndian::read_u64(&$buf[$off+32..]);
|
||||
let w05 = BigEndian::read_u64(&$buf[$off+40..]);
|
||||
let w06 = BigEndian::read_u64(&$buf[$off+48..]);
|
||||
let w07 = BigEndian::read_u64(&$buf[$off+56..]);
|
||||
let w08 = BigEndian::read_u64(&$buf[$off+64..]);
|
||||
let w09 = BigEndian::read_u64(&$buf[$off+72..]);
|
||||
let w10 = BigEndian::read_u64(&$buf[$off+80..]);
|
||||
let w11 = BigEndian::read_u64(&$buf[$off+88..]);
|
||||
let w12 = BigEndian::read_u64(&$buf[$off+96..]);
|
||||
let w13 = BigEndian::read_u64(&$buf[$off+104..]);
|
||||
let w14 = BigEndian::read_u64(&$buf[$off+112..]);
|
||||
let w15 = BigEndian::read_u64(&$buf[$off+120..]);
|
||||
$self.process(w00, w01, w02, w03, w04, w05, w06, w07,
|
||||
w08, w09, w10, w11, w12, w13, w14, w15);
|
||||
}};
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SHA512State {
|
||||
state: [u64; 8],
|
||||
buffer: Vec<u8>,
|
||||
done: bool,
|
||||
l: usize
|
||||
}
|
||||
|
||||
impl SHA512State {
|
||||
fn new(values: [u64; 8]) -> Self {
|
||||
SHA512State {
|
||||
state: values,
|
||||
buffer: Vec::with_capacity(128),
|
||||
done: false,
|
||||
l: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn process(&mut self, w00: u64, w01: u64, w02: u64, w03: u64,
|
||||
w04: u64, w05: u64, w06: u64, w07: u64,
|
||||
w08: u64, w09: u64, w10: u64, w11: u64,
|
||||
w12: u64, w13: u64, w14: u64, w15: u64)
|
||||
{
|
||||
let w16 = lsig512_1!(w14) + w09 + lsig512_0!(w01) + w00;
|
||||
let w17 = lsig512_1!(w15) + w10 + lsig512_0!(w02) + w01;
|
||||
let w18 = lsig512_1!(w16) + w11 + lsig512_0!(w03) + w02;
|
||||
let w19 = lsig512_1!(w17) + w12 + lsig512_0!(w04) + w03;
|
||||
let w20 = lsig512_1!(w18) + w13 + lsig512_0!(w05) + w04;
|
||||
let w21 = lsig512_1!(w19) + w14 + lsig512_0!(w06) + w05;
|
||||
let w22 = lsig512_1!(w20) + w15 + lsig512_0!(w07) + w06;
|
||||
let w23 = lsig512_1!(w21) + w16 + lsig512_0!(w08) + w07;
|
||||
let w24 = lsig512_1!(w22) + w17 + lsig512_0!(w09) + w08;
|
||||
let w25 = lsig512_1!(w23) + w18 + lsig512_0!(w10) + w09;
|
||||
let w26 = lsig512_1!(w24) + w19 + lsig512_0!(w11) + w10;
|
||||
let w27 = lsig512_1!(w25) + w20 + lsig512_0!(w12) + w11;
|
||||
let w28 = lsig512_1!(w26) + w21 + lsig512_0!(w13) + w12;
|
||||
let w29 = lsig512_1!(w27) + w22 + lsig512_0!(w14) + w13;
|
||||
let w30 = lsig512_1!(w28) + w23 + lsig512_0!(w15) + w14;
|
||||
let w31 = lsig512_1!(w29) + w24 + lsig512_0!(w16) + w15;
|
||||
let w32 = lsig512_1!(w30) + w25 + lsig512_0!(w17) + w16;
|
||||
let w33 = lsig512_1!(w31) + w26 + lsig512_0!(w18) + w17;
|
||||
let w34 = lsig512_1!(w32) + w27 + lsig512_0!(w19) + w18;
|
||||
let w35 = lsig512_1!(w33) + w28 + lsig512_0!(w20) + w19;
|
||||
let w36 = lsig512_1!(w34) + w29 + lsig512_0!(w21) + w20;
|
||||
let w37 = lsig512_1!(w35) + w30 + lsig512_0!(w22) + w21;
|
||||
let w38 = lsig512_1!(w36) + w31 + lsig512_0!(w23) + w22;
|
||||
let w39 = lsig512_1!(w37) + w32 + lsig512_0!(w24) + w23;
|
||||
let w40 = lsig512_1!(w38) + w33 + lsig512_0!(w25) + w24;
|
||||
let w41 = lsig512_1!(w39) + w34 + lsig512_0!(w26) + w25;
|
||||
let w42 = lsig512_1!(w40) + w35 + lsig512_0!(w27) + w26;
|
||||
let w43 = lsig512_1!(w41) + w36 + lsig512_0!(w28) + w27;
|
||||
let w44 = lsig512_1!(w42) + w37 + lsig512_0!(w29) + w28;
|
||||
let w45 = lsig512_1!(w43) + w38 + lsig512_0!(w30) + w29;
|
||||
let w46 = lsig512_1!(w44) + w39 + lsig512_0!(w31) + w30;
|
||||
let w47 = lsig512_1!(w45) + w40 + lsig512_0!(w32) + w31;
|
||||
let w48 = lsig512_1!(w46) + w41 + lsig512_0!(w33) + w32;
|
||||
let w49 = lsig512_1!(w47) + w42 + lsig512_0!(w34) + w33;
|
||||
let w50 = lsig512_1!(w48) + w43 + lsig512_0!(w35) + w34;
|
||||
let w51 = lsig512_1!(w49) + w44 + lsig512_0!(w36) + w35;
|
||||
let w52 = lsig512_1!(w50) + w45 + lsig512_0!(w37) + w36;
|
||||
let w53 = lsig512_1!(w51) + w46 + lsig512_0!(w38) + w37;
|
||||
let w54 = lsig512_1!(w52) + w47 + lsig512_0!(w39) + w38;
|
||||
let w55 = lsig512_1!(w53) + w48 + lsig512_0!(w40) + w39;
|
||||
let w56 = lsig512_1!(w54) + w49 + lsig512_0!(w41) + w40;
|
||||
let w57 = lsig512_1!(w55) + w50 + lsig512_0!(w42) + w41;
|
||||
let w58 = lsig512_1!(w56) + w51 + lsig512_0!(w43) + w42;
|
||||
let w59 = lsig512_1!(w57) + w52 + lsig512_0!(w44) + w43;
|
||||
let w60 = lsig512_1!(w58) + w53 + lsig512_0!(w45) + w44;
|
||||
let w61 = lsig512_1!(w59) + w54 + lsig512_0!(w46) + w45;
|
||||
let w62 = lsig512_1!(w60) + w55 + lsig512_0!(w47) + w46;
|
||||
let w63 = lsig512_1!(w61) + w56 + lsig512_0!(w48) + w47;
|
||||
let w64 = lsig512_1!(w62) + w57 + lsig512_0!(w49) + w48;
|
||||
let w65 = lsig512_1!(w63) + w58 + lsig512_0!(w50) + w49;
|
||||
let w66 = lsig512_1!(w64) + w59 + lsig512_0!(w51) + w50;
|
||||
let w67 = lsig512_1!(w65) + w60 + lsig512_0!(w52) + w51;
|
||||
let w68 = lsig512_1!(w66) + w61 + lsig512_0!(w53) + w52;
|
||||
let w69 = lsig512_1!(w67) + w62 + lsig512_0!(w54) + w53;
|
||||
let w70 = lsig512_1!(w68) + w63 + lsig512_0!(w55) + w54;
|
||||
let w71 = lsig512_1!(w69) + w64 + lsig512_0!(w56) + w55;
|
||||
let w72 = lsig512_1!(w70) + w65 + lsig512_0!(w57) + w56;
|
||||
let w73 = lsig512_1!(w71) + w66 + lsig512_0!(w58) + w57;
|
||||
let w74 = lsig512_1!(w72) + w67 + lsig512_0!(w59) + w58;
|
||||
let w75 = lsig512_1!(w73) + w68 + lsig512_0!(w60) + w59;
|
||||
let w76 = lsig512_1!(w74) + w69 + lsig512_0!(w61) + w60;
|
||||
let w77 = lsig512_1!(w75) + w70 + lsig512_0!(w62) + w61;
|
||||
let w78 = lsig512_1!(w76) + w71 + lsig512_0!(w63) + w62;
|
||||
let w79 = lsig512_1!(w77) + w72 + lsig512_0!(w64) + w63;
|
||||
let s01 = step512(self.state,0x428a2f98d728ae22,w00);
|
||||
let s02 = step512(s01,0x7137449123ef65cd,w01);
|
||||
let s03 = step512(s02,0xb5c0fbcfec4d3b2f,w02);
|
||||
let s04 = step512(s03,0xe9b5dba58189dbbc,w03);
|
||||
let s05 = step512(s04,0x3956c25bf348b538,w04);
|
||||
let s06 = step512(s05,0x59f111f1b605d019,w05);
|
||||
let s07 = step512(s06,0x923f82a4af194f9b,w06);
|
||||
let s08 = step512(s07,0xab1c5ed5da6d8118,w07);
|
||||
let s09 = step512(s08,0xd807aa98a3030242,w08);
|
||||
let s10 = step512(s09,0x12835b0145706fbe,w09);
|
||||
let s11 = step512(s10,0x243185be4ee4b28c,w10);
|
||||
let s12 = step512(s11,0x550c7dc3d5ffb4e2,w11);
|
||||
let s13 = step512(s12,0x72be5d74f27b896f,w12);
|
||||
let s14 = step512(s13,0x80deb1fe3b1696b1,w13);
|
||||
let s15 = step512(s14,0x9bdc06a725c71235,w14);
|
||||
let s16 = step512(s15,0xc19bf174cf692694,w15);
|
||||
let s17 = step512(s16,0xe49b69c19ef14ad2,w16);
|
||||
let s18 = step512(s17,0xefbe4786384f25e3,w17);
|
||||
let s19 = step512(s18,0x0fc19dc68b8cd5b5,w18);
|
||||
let s20 = step512(s19,0x240ca1cc77ac9c65,w19);
|
||||
let s21 = step512(s20,0x2de92c6f592b0275,w20);
|
||||
let s22 = step512(s21,0x4a7484aa6ea6e483,w21);
|
||||
let s23 = step512(s22,0x5cb0a9dcbd41fbd4,w22);
|
||||
let s24 = step512(s23,0x76f988da831153b5,w23);
|
||||
let s25 = step512(s24,0x983e5152ee66dfab,w24);
|
||||
let s26 = step512(s25,0xa831c66d2db43210,w25);
|
||||
let s27 = step512(s26,0xb00327c898fb213f,w26);
|
||||
let s28 = step512(s27,0xbf597fc7beef0ee4,w27);
|
||||
let s29 = step512(s28,0xc6e00bf33da88fc2,w28);
|
||||
let s30 = step512(s29,0xd5a79147930aa725,w29);
|
||||
let s31 = step512(s30,0x06ca6351e003826f,w30);
|
||||
let s32 = step512(s31,0x142929670a0e6e70,w31);
|
||||
let s33 = step512(s32,0x27b70a8546d22ffc,w32);
|
||||
let s34 = step512(s33,0x2e1b21385c26c926,w33);
|
||||
let s35 = step512(s34,0x4d2c6dfc5ac42aed,w34);
|
||||
let s36 = step512(s35,0x53380d139d95b3df,w35);
|
||||
let s37 = step512(s36,0x650a73548baf63de,w36);
|
||||
let s38 = step512(s37,0x766a0abb3c77b2a8,w37);
|
||||
let s39 = step512(s38,0x81c2c92e47edaee6,w38);
|
||||
let s40 = step512(s39,0x92722c851482353b,w39);
|
||||
let s41 = step512(s40,0xa2bfe8a14cf10364,w40);
|
||||
let s42 = step512(s41,0xa81a664bbc423001,w41);
|
||||
let s43 = step512(s42,0xc24b8b70d0f89791,w42);
|
||||
let s44 = step512(s43,0xc76c51a30654be30,w43);
|
||||
let s45 = step512(s44,0xd192e819d6ef5218,w44);
|
||||
let s46 = step512(s45,0xd69906245565a910,w45);
|
||||
let s47 = step512(s46,0xf40e35855771202a,w46);
|
||||
let s48 = step512(s47,0x106aa07032bbd1b8,w47);
|
||||
let s49 = step512(s48,0x19a4c116b8d2d0c8,w48);
|
||||
let s50 = step512(s49,0x1e376c085141ab53,w49);
|
||||
let s51 = step512(s50,0x2748774cdf8eeb99,w50);
|
||||
let s52 = step512(s51,0x34b0bcb5e19b48a8,w51);
|
||||
let s53 = step512(s52,0x391c0cb3c5c95a63,w52);
|
||||
let s54 = step512(s53,0x4ed8aa4ae3418acb,w53);
|
||||
let s55 = step512(s54,0x5b9cca4f7763e373,w54);
|
||||
let s56 = step512(s55,0x682e6ff3d6b2b8a3,w55);
|
||||
let s57 = step512(s56,0x748f82ee5defb2fc,w56);
|
||||
let s58 = step512(s57,0x78a5636f43172f60,w57);
|
||||
let s59 = step512(s58,0x84c87814a1f0ab72,w58);
|
||||
let s60 = step512(s59,0x8cc702081a6439ec,w59);
|
||||
let s61 = step512(s60,0x90befffa23631e28,w60);
|
||||
let s62 = step512(s61,0xa4506cebde82bde9,w61);
|
||||
let s63 = step512(s62,0xbef9a3f7b2c67915,w62);
|
||||
let s64 = step512(s63,0xc67178f2e372532b,w63);
|
||||
let s65 = step512(s64,0xca273eceea26619c,w64);
|
||||
let s66 = step512(s65,0xd186b8c721c0c207,w65);
|
||||
let s67 = step512(s66,0xeada7dd6cde0eb1e,w66);
|
||||
let s68 = step512(s67,0xf57d4f7fee6ed178,w67);
|
||||
let s69 = step512(s68,0x06f067aa72176fba,w68);
|
||||
let s70 = step512(s69,0x0a637dc5a2c898a6,w69);
|
||||
let s71 = step512(s70,0x113f9804bef90dae,w70);
|
||||
let s72 = step512(s71,0x1b710b35131c471b,w71);
|
||||
let s73 = step512(s72,0x28db77f523047d84,w72);
|
||||
let s74 = step512(s73,0x32caab7b40c72493,w73);
|
||||
let s75 = step512(s74,0x3c9ebe0a15c9bebc,w74);
|
||||
let s76 = step512(s75,0x431d67c49c100d4c,w75);
|
||||
let s77 = step512(s76,0x4cc5d4becb3e42b6,w76);
|
||||
let s78 = step512(s77,0x597f299cfc657e2a,w77);
|
||||
let s79 = step512(s78,0x5fcb6fab3ad6faec,w78);
|
||||
let s80 = step512(s79,0x6c44198c4a475817,w79);
|
||||
self.state[0] += s80[0];
|
||||
self.state[1] += s80[1];
|
||||
self.state[2] += s80[2];
|
||||
self.state[3] += s80[3];
|
||||
self.state[4] += s80[4];
|
||||
self.state[5] += s80[5];
|
||||
self.state[6] += s80[6];
|
||||
self.state[7] += s80[7];
|
||||
}
|
||||
|
||||
fn update(&mut self, block: &[u8]) {
|
||||
if !self.done {
|
||||
let mut offset = 0;
|
||||
|
||||
self.l += block.len();
|
||||
|
||||
if self.buffer.len() + block.len() < 128 {
|
||||
self.buffer.extend_from_slice(block);
|
||||
return;
|
||||
}
|
||||
|
||||
if self.buffer.len() > 0 {
|
||||
// We must be able to build up a 128 byte chunk, at this point, otherwise
|
||||
// the math above would've been wrong.
|
||||
while self.buffer.len() < 128 {
|
||||
self.buffer.push(block[offset]);
|
||||
offset += 1;
|
||||
}
|
||||
process_u64_block!(self.buffer, 0, self);
|
||||
// Reset the buffer now, we're done with that nonsense for the moment
|
||||
self.buffer.resize(0,0);
|
||||
}
|
||||
|
||||
while (offset + 128) <= block.len() {
|
||||
process_u64_block!(block, offset, self);
|
||||
offset += 128;
|
||||
}
|
||||
|
||||
if offset < block.len() {
|
||||
self.buffer.extend_from_slice(&block[offset..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(&mut self) {
|
||||
let bitlen = self.l * 8;
|
||||
let k = calculate_k(896, 1024, bitlen);
|
||||
// INVARIANT: k is necessarily > 0, and (k + 1) is a multiple of 8
|
||||
let bytes_to_add = (k + 1) / 8;
|
||||
let mut padvec = Vec::with_capacity(bytes_to_add + 16);
|
||||
padvec.push(0x80); // Set the high bit, since the first bit after the data
|
||||
// should be set
|
||||
padvec.resize(bytes_to_add, 0);
|
||||
padvec.write_u128::<BigEndian>(bitlen as u128).expect("Broken writing value to pre-allocated Vec?");
|
||||
self.update(&padvec);
|
||||
self.done = true;
|
||||
assert_eq!(self.buffer.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn step512(state0: [u64; 8], k: u64, w: u64) -> [u64; 8]
|
||||
{
|
||||
let [a,b,c,d,e,f,g,h] = state0;
|
||||
let t1 = h + bsig512_1!(e) + ch!(e,f,g) + k + w;
|
||||
let t2 = bsig512_0!(a) + maj!(a,b,c);
|
||||
let hp = g;
|
||||
let gp = f;
|
||||
let fp = e;
|
||||
let ep = d + t1;
|
||||
let dp = c;
|
||||
let cp = b;
|
||||
let bp = a;
|
||||
let ap = t1 + t2;
|
||||
[ap,bp,cp,dp,ep,fp,gp,hp]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use testing::run_test;
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn nist_sha224() {
|
||||
let fname = "testdata/sha/nist_sha224.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negl, lbytes) = case.get("l").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!negl && !negm && !negd);
|
||||
let msg = if lbytes[0] == 0 { Vec::new() } else { mbytes.clone() };
|
||||
let digest = SHA224::hash(&msg);
|
||||
assert_eq!(dbytes, &digest);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn nist_sha256() {
|
||||
let fname = "testdata/sha/nist_sha256.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negl, lbytes) = case.get("l").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!negl && !negm && !negd);
|
||||
let msg = if lbytes[0] == 0 { Vec::new() } else { mbytes.clone() };
|
||||
let digest = SHA256::hash(&msg);
|
||||
assert_eq!(dbytes, &digest);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn nist_sha384() {
|
||||
let fname = "testdata/sha/nist_sha384.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negl, lbytes) = case.get("l").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!negl && !negm && !negd);
|
||||
let msg = if lbytes[0] == 0 { Vec::new() } else { mbytes.clone() };
|
||||
let digest = SHA384::hash(&msg);
|
||||
assert_eq!(dbytes, &digest);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn nist_sha512() {
|
||||
let fname = "testdata/sha/nist_sha512.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negl, lbytes) = case.get("l").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!negl && !negm && !negd);
|
||||
let msg = if lbytes[0] == 0 { Vec::new() } else { mbytes.clone() };
|
||||
let digest = SHA512::hash(&msg);
|
||||
assert_eq!(dbytes, &digest);
|
||||
});
|
||||
}
|
||||
610
src/sha/sha3.rs
Normal file
610
src/sha/sha3.rs
Normal file
@@ -0,0 +1,610 @@
|
||||
use super::super::Hash;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Keccak {
|
||||
rate_in_bytes: usize,
|
||||
rate_in_longs: usize,
|
||||
buffer: Vec<u8>,
|
||||
state: [u64; 25], // This is Keccak-f[1600]
|
||||
output: Option<Vec<u8>>
|
||||
}
|
||||
|
||||
const KECCAK_ROUND_CONSTANTS: [u64; 24] =
|
||||
[ 0x0000000000000001u64, 0x0000000000008082u64,
|
||||
0x800000000000808au64, 0x8000000080008000u64,
|
||||
0x000000000000808bu64, 0x0000000080000001u64,
|
||||
0x8000000080008081u64, 0x8000000000008009u64,
|
||||
0x000000000000008au64, 0x0000000000000088u64,
|
||||
0x0000000080008009u64, 0x000000008000000au64,
|
||||
0x000000008000808bu64, 0x800000000000008bu64,
|
||||
0x8000000000008089u64, 0x8000000000008003u64,
|
||||
0x8000000000008002u64, 0x8000000000000080u64,
|
||||
0x000000000000800au64, 0x800000008000000au64,
|
||||
0x8000000080008081u64, 0x8000000000008080u64,
|
||||
0x0000000080000001u64, 0x8000000080008008u64,
|
||||
];
|
||||
|
||||
macro_rules! absorb {
|
||||
($self: ident, $block: expr, $sidx: expr) => {{
|
||||
let mut i = 0;
|
||||
let mut off = 0;
|
||||
|
||||
while i < $self.rate_in_longs {
|
||||
let word = ($block[$sidx+off+0] as u64) << 00 |
|
||||
($block[$sidx+off+1] as u64) << 08 |
|
||||
($block[$sidx+off+2] as u64) << 16 |
|
||||
($block[$sidx+off+3] as u64) << 24 |
|
||||
($block[$sidx+off+4] as u64) << 32 |
|
||||
($block[$sidx+off+5] as u64) << 40 |
|
||||
($block[$sidx+off+6] as u64) << 48 |
|
||||
($block[$sidx+off+7] as u64) << 56;
|
||||
$self.state[i] ^= word;
|
||||
off += 8;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
$self.permute();
|
||||
}};
|
||||
}
|
||||
|
||||
impl Keccak {
|
||||
pub fn new(rate: usize) -> Self
|
||||
{
|
||||
assert_eq!(rate % 64, 0);
|
||||
Keccak {
|
||||
rate_in_bytes: rate / 8,
|
||||
rate_in_longs: rate / 64,
|
||||
buffer: Vec::with_capacity(rate),
|
||||
state: [0; 25],
|
||||
output: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn permute(&mut self)
|
||||
{
|
||||
// This is a translation of the very easy-to-read implementation in BouncyCastle
|
||||
for i in 0..24 {
|
||||
// Theta!
|
||||
let c0 = self.state[0] ^ self.state[5] ^ self.state[10] ^ self.state[15] ^ self.state[20];
|
||||
let c1 = self.state[1] ^ self.state[6] ^ self.state[11] ^ self.state[16] ^ self.state[21];
|
||||
let c2 = self.state[2] ^ self.state[7] ^ self.state[12] ^ self.state[17] ^ self.state[22];
|
||||
let c3 = self.state[3] ^ self.state[8] ^ self.state[13] ^ self.state[18] ^ self.state[23];
|
||||
let c4 = self.state[4] ^ self.state[9] ^ self.state[14] ^ self.state[19] ^ self.state[24];
|
||||
let d0 = c0.rotate_left(1) ^ c3;
|
||||
let d1 = c1.rotate_left(1) ^ c4;
|
||||
let d2 = c2.rotate_left(1) ^ c0;
|
||||
let d3 = c3.rotate_left(1) ^ c1;
|
||||
let d4 = c4.rotate_left(1) ^ c2;
|
||||
self.state[0] ^= d1; self.state[5] ^= d1; self.state[10] ^= d1; self.state[15] ^= d1; self.state[20] ^= d1;
|
||||
self.state[1] ^= d2; self.state[6] ^= d2; self.state[11] ^= d2; self.state[16] ^= d2; self.state[21] ^= d2;
|
||||
self.state[2] ^= d3; self.state[7] ^= d3; self.state[12] ^= d3; self.state[17] ^= d3; self.state[22] ^= d3;
|
||||
self.state[3] ^= d4; self.state[8] ^= d4; self.state[13] ^= d4; self.state[18] ^= d4; self.state[23] ^= d4;
|
||||
self.state[4] ^= d0; self.state[9] ^= d0; self.state[14] ^= d0; self.state[19] ^= d0; self.state[24] ^= d0;
|
||||
// Rho & Pi!
|
||||
let t1 = self.state[01].rotate_left(1);
|
||||
self.state[01] = self.state[06].rotate_left(44);
|
||||
self.state[06] = self.state[09].rotate_left(20);
|
||||
self.state[09] = self.state[22].rotate_left(61);
|
||||
self.state[22] = self.state[14].rotate_left(39);
|
||||
self.state[14] = self.state[20].rotate_left(18);
|
||||
self.state[20] = self.state[02].rotate_left(62);
|
||||
self.state[02] = self.state[12].rotate_left(43);
|
||||
self.state[12] = self.state[13].rotate_left(25);
|
||||
self.state[13] = self.state[19].rotate_left(8);
|
||||
self.state[19] = self.state[23].rotate_left(56);
|
||||
self.state[23] = self.state[15].rotate_left(41);
|
||||
self.state[15] = self.state[04].rotate_left(27);
|
||||
self.state[04] = self.state[24].rotate_left(14);
|
||||
self.state[24] = self.state[21].rotate_left(2);
|
||||
self.state[21] = self.state[08].rotate_left(55);
|
||||
self.state[08] = self.state[16].rotate_left(45);
|
||||
self.state[16] = self.state[05].rotate_left(36);
|
||||
self.state[05] = self.state[03].rotate_left(28);
|
||||
self.state[03] = self.state[18].rotate_left(21);
|
||||
self.state[18] = self.state[17].rotate_left(15);
|
||||
self.state[17] = self.state[11].rotate_left(10);
|
||||
self.state[11] = self.state[07].rotate_left(6);
|
||||
self.state[07] = self.state[10].rotate_left(3);
|
||||
self.state[10] = t1;
|
||||
// Chi!
|
||||
let t2 = self.state[00] ^ (!self.state[01] & self.state[02]);
|
||||
let t3 = self.state[01] ^ (!self.state[02] & self.state[03]);
|
||||
self.state[02] ^= !self.state[03] & self.state[04];
|
||||
self.state[03] ^= !self.state[04] & self.state[00];
|
||||
self.state[04] ^= !self.state[00] & self.state[01];
|
||||
self.state[00] = t2;
|
||||
self.state[01] = t3;
|
||||
|
||||
let t4 = self.state[05] ^ (!self.state[06] & self.state[07]);
|
||||
let t5 = self.state[06] ^ (!self.state[07] & self.state[08]);
|
||||
self.state[07] ^= !self.state[08] & self.state[09];
|
||||
self.state[08] ^= !self.state[09] & self.state[05];
|
||||
self.state[09] ^= !self.state[05] & self.state[06];
|
||||
self.state[05] = t4;
|
||||
self.state[06] = t5;
|
||||
|
||||
let t6 = self.state[10] ^ (!self.state[11] & self.state[12]);
|
||||
let t7 = self.state[11] ^ (!self.state[12] & self.state[13]);
|
||||
self.state[12] ^= !self.state[13] & self.state[14];
|
||||
self.state[13] ^= !self.state[14] & self.state[10];
|
||||
self.state[14] ^= !self.state[10] & self.state[11];
|
||||
self.state[10] = t6;
|
||||
self.state[11] = t7;
|
||||
|
||||
let t8 = self.state[15] ^ (!self.state[16] & self.state[17]);
|
||||
let t9 = self.state[16] ^ (!self.state[17] & self.state[18]);
|
||||
self.state[17] ^= !self.state[18] & self.state[19];
|
||||
self.state[18] ^= !self.state[19] & self.state[15];
|
||||
self.state[19] ^= !self.state[15] & self.state[16];
|
||||
self.state[15] = t8;
|
||||
self.state[16] = t9;
|
||||
|
||||
let ta = self.state[20] ^ (!self.state[21] & self.state[22]);
|
||||
let tb = self.state[21] ^ (!self.state[22] & self.state[23]);
|
||||
self.state[22] ^= !self.state[23] & self.state[24];
|
||||
self.state[23] ^= !self.state[24] & self.state[20];
|
||||
self.state[24] ^= !self.state[20] & self.state[21];
|
||||
self.state[20] = ta;
|
||||
self.state[21] = tb;
|
||||
|
||||
// iota
|
||||
self.state[00] ^= KECCAK_ROUND_CONSTANTS[i];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process(&mut self, bytes: &[u8])
|
||||
{
|
||||
if self.output.is_none() {
|
||||
let mut offset = 0;
|
||||
|
||||
if self.buffer.len() + bytes.len() < self.rate_in_bytes {
|
||||
self.buffer.extend_from_slice(bytes);
|
||||
return;
|
||||
}
|
||||
|
||||
if self.buffer.len() > 0 {
|
||||
// We must be able to build up a chunk at our absorbtion rate, at this
|
||||
// point, otherwise the math above would've been wrong.
|
||||
while self.buffer.len() < self.rate_in_bytes {
|
||||
self.buffer.push(bytes[offset]);
|
||||
offset += 1;
|
||||
}
|
||||
absorb!(self, self.buffer, 0);
|
||||
// Reset the buffer now, we're done with that nonsense for the moment
|
||||
self.buffer.resize(0,0);
|
||||
}
|
||||
|
||||
while (offset + self.rate_in_bytes) <= bytes.len() {
|
||||
absorb!(self, bytes, offset);
|
||||
offset += self.rate_in_bytes;
|
||||
}
|
||||
|
||||
if offset < bytes.len() {
|
||||
self.buffer.extend_from_slice(&bytes[offset..]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tag_and_pad(&mut self, tag_byte: u8)
|
||||
{
|
||||
if self.output.is_none() {
|
||||
assert!(self.buffer.len() < self.rate_in_bytes);
|
||||
// what we need to do here is tag on a final 01, to tag that as SHA3,
|
||||
// and then pad it out, with an 0x80 at the end.
|
||||
self.buffer.push(tag_byte);
|
||||
self.buffer.resize(self.rate_in_bytes, 0);
|
||||
self.buffer[self.rate_in_bytes-1] |= 0x80;
|
||||
absorb!(self, self.buffer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn squeeze(&mut self, output_len: usize) -> Vec<u8>
|
||||
{
|
||||
if let Some(ref result) = self.output {
|
||||
result.clone()
|
||||
} else {
|
||||
let mut res = Vec::new();
|
||||
|
||||
while res.len() < output_len {
|
||||
for i in 0..self.rate_in_longs {
|
||||
res.push( (self.state[i] >> 00) as u8 );
|
||||
res.push( (self.state[i] >> 08) as u8 );
|
||||
res.push( (self.state[i] >> 16) as u8 );
|
||||
res.push( (self.state[i] >> 24) as u8 );
|
||||
res.push( (self.state[i] >> 32) as u8 );
|
||||
res.push( (self.state[i] >> 40) as u8 );
|
||||
res.push( (self.state[i] >> 48) as u8 );
|
||||
res.push( (self.state[i] >> 56) as u8 );
|
||||
}
|
||||
self.permute();
|
||||
}
|
||||
|
||||
res.resize(output_len, 0);
|
||||
self.output = Some(res.clone());
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The SHA3-224 hash.
|
||||
///
|
||||
/// To use, you can run it in incremental mode -- by calling new(),
|
||||
/// update() zero or more times, and then finalize() -- or you can
|
||||
/// just invoke the hash directly. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use simple_crypto::sha::{Hash,SHA3_224};
|
||||
///
|
||||
/// let empty = [0; 0];
|
||||
/// // Do the hash using the incremental API
|
||||
/// let mut hashf = SHA3_224::new();
|
||||
/// hashf.update(&empty);
|
||||
/// let result_incremental = hashf.finalize();
|
||||
/// // Do the hash using the direct API
|
||||
/// let result_direct = SHA3_224::hash(&empty);
|
||||
/// // ... and they should be the same
|
||||
/// assert_eq!(result_incremental,result_direct);
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct SHA3_224 {
|
||||
state: Keccak
|
||||
}
|
||||
|
||||
impl Hash for SHA3_224 {
|
||||
fn new() -> Self
|
||||
{
|
||||
SHA3_224{ state: Keccak::new(1600 - 448) }
|
||||
}
|
||||
|
||||
fn update(&mut self, buffer: &[u8])
|
||||
{
|
||||
self.state.process(&buffer);
|
||||
}
|
||||
|
||||
fn finalize(&mut self) -> Vec<u8>
|
||||
{
|
||||
self.state.tag_and_pad(0x06);
|
||||
self.state.squeeze(224 / 8)
|
||||
}
|
||||
|
||||
fn block_size() -> usize
|
||||
{
|
||||
1152
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod sha224 {
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
|
||||
// see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA3-224_Msg0.pdf
|
||||
#[test]
|
||||
fn nist_empty_example() {
|
||||
let empty = [0; 0];
|
||||
let hashres = [0x6B,0x4E,0x03,0x42,0x36,0x67,0xDB,0xB7,0x3B,0x6E,0x15,
|
||||
0x45,0x4F,0x0E,0xB1,0xAB,0xD4,0x59,0x7F,0x9A,0x1B,0x07,
|
||||
0x8E,0x3F,0x5B,0x5A,0x6B,0xC7];
|
||||
let mine = SHA3_224::hash(&empty);
|
||||
assert_eq!(hashres.to_vec(), mine);
|
||||
}
|
||||
|
||||
// see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA3-224_1600.pdf
|
||||
#[test]
|
||||
fn nist_1600_example() {
|
||||
let example = [0xA3; 200];
|
||||
let hashres = [0x93,0x76,0x81,0x6A,0xBA,0x50,0x3F,0x72,
|
||||
0xF9,0x6C,0xE7,0xEB,0x65,0xAC,0x09,0x5D,
|
||||
0xEE,0xE3,0xBE,0x4B,0xF9,0xBB,0xC2,0xA1,
|
||||
0xCB,0x7E,0x11,0xE0];
|
||||
let mine = SHA3_224::hash(&example);
|
||||
assert_eq!(hashres.to_vec(), mine);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn nist_test_vectors() {
|
||||
let fname = "testdata/sha/nist_sha3_224.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negl, lbytes) = case.get("l").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!negl && !negm && !negd);
|
||||
let msg = if lbytes[0] == 0 { Vec::new() } else { mbytes.clone() };
|
||||
let digest = SHA3_224::hash(&msg);
|
||||
assert_eq!(dbytes, &digest);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// The SHA3-256 hash. [GOOD]
|
||||
///
|
||||
/// To use, you can run it in incremental mode -- by calling new(),
|
||||
/// update() zero or more times, and then finalize() -- or you can
|
||||
/// just invoke the hash directly. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use simple_crypto::sha::{Hash,SHA3_256};
|
||||
///
|
||||
/// let empty = [0; 0];
|
||||
/// // Do the hash using the incremental API
|
||||
/// let mut hashf = SHA3_256::new();
|
||||
/// hashf.update(&empty);
|
||||
/// let result_incremental = hashf.finalize();
|
||||
/// // Do the hash using the direct API
|
||||
/// let result_direct = SHA3_256::hash(&empty);
|
||||
/// // ... and they should be the same
|
||||
/// assert_eq!(result_incremental,result_direct);
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct SHA3_256 {
|
||||
state: Keccak
|
||||
}
|
||||
|
||||
impl Hash for SHA3_256 {
|
||||
fn new() -> Self
|
||||
{
|
||||
SHA3_256{ state: Keccak::new(1600 - 512) }
|
||||
}
|
||||
|
||||
fn update(&mut self, buffer: &[u8])
|
||||
{
|
||||
self.state.process(&buffer);
|
||||
}
|
||||
|
||||
fn finalize(&mut self) -> Vec<u8>
|
||||
{
|
||||
self.state.tag_and_pad(0x06);
|
||||
self.state.squeeze(256 / 8)
|
||||
}
|
||||
|
||||
fn block_size() -> usize
|
||||
{
|
||||
1088
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod sha256 {
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
|
||||
// see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA3-256.pdf
|
||||
#[test]
|
||||
fn nist_empty_example() {
|
||||
let empty = [0; 0];
|
||||
let hashres = [0xA7,0xFF,0xC6,0xF8,0xBF,0x1E,0xD7,0x66,
|
||||
0x51,0xC1,0x47,0x56,0xA0,0x61,0xD6,0x62,
|
||||
0xF5,0x80,0xFF,0x4D,0xE4,0x3B,0x49,0xFA,
|
||||
0x82,0xD8,0x0A,0x4B,0x80,0xF8,0x43,0x4A];
|
||||
let mine = SHA3_256::hash(&empty);
|
||||
assert_eq!(hashres.to_vec(), mine);
|
||||
}
|
||||
|
||||
// see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA3-256_1600.pdf
|
||||
#[test]
|
||||
fn nist_1600_example() {
|
||||
let example = [0xA3; 200];
|
||||
let hashres = [0x79,0xF3,0x8A,0xDE,0xC5,0xC2,0x03,0x07,
|
||||
0xA9,0x8E,0xF7,0x6E,0x83,0x24,0xAF,0xBF,
|
||||
0xD4,0x6C,0xFD,0x81,0xB2,0x2E,0x39,0x73,
|
||||
0xC6,0x5F,0xA1,0xBD,0x9D,0xE3,0x17,0x87];
|
||||
let mine = SHA3_256::hash(&example);
|
||||
assert_eq!(hashres.to_vec(), mine);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn nist_test_vectors() {
|
||||
let fname = "testdata/sha/nist_sha3_256.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negl, lbytes) = case.get("l").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!negl && !negm && !negd);
|
||||
let msg = if lbytes[0] == 0 { Vec::new() } else { mbytes.clone() };
|
||||
let digest = SHA3_256::hash(&msg);
|
||||
assert_eq!(dbytes, &digest);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// The SHA3-384 hash. [BETTER]
|
||||
///
|
||||
/// To use, you can run it in incremental mode -- by calling new(),
|
||||
/// update() zero or more times, and then finalize() -- or you can
|
||||
/// just invoke the hash directly. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use simple_crypto::sha::{Hash,SHA3_384};
|
||||
///
|
||||
/// let empty = [0; 0];
|
||||
/// // Do the hash using the incremental API
|
||||
/// let mut hashf = SHA3_384::new();
|
||||
/// hashf.update(&empty);
|
||||
/// let result_incremental = hashf.finalize();
|
||||
/// // Do the hash using the direct API
|
||||
/// let result_direct = SHA3_384::hash(&empty);
|
||||
/// // ... and they should be the same
|
||||
/// assert_eq!(result_incremental,result_direct);
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct SHA3_384 {
|
||||
state: Keccak
|
||||
}
|
||||
|
||||
impl Hash for SHA3_384 {
|
||||
fn new() -> Self
|
||||
{
|
||||
SHA3_384{ state: Keccak::new(1600 - 768) }
|
||||
}
|
||||
|
||||
fn update(&mut self, buffer: &[u8])
|
||||
{
|
||||
self.state.process(&buffer);
|
||||
}
|
||||
|
||||
fn finalize(&mut self) -> Vec<u8>
|
||||
{
|
||||
self.state.tag_and_pad(0x06);
|
||||
self.state.squeeze(384 / 8)
|
||||
}
|
||||
|
||||
fn block_size() -> usize
|
||||
{
|
||||
832
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod sha384 {
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
|
||||
// see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA3-384_Msg0.pdf
|
||||
#[test]
|
||||
fn nist_empty_example() {
|
||||
let empty = [0; 0];
|
||||
let hashres = [0x0C,0x63,0xA7,0x5B,0x84,0x5E,0x4F,0x7D,
|
||||
0x01,0x10,0x7D,0x85,0x2E,0x4C,0x24,0x85,
|
||||
0xC5,0x1A,0x50,0xAA,0xAA,0x94,0xFC,0x61,
|
||||
0x99,0x5E,0x71,0xBB,0xEE,0x98,0x3A,0x2A,
|
||||
0xC3,0x71,0x38,0x31,0x26,0x4A,0xDB,0x47,
|
||||
0xFB,0x6B,0xD1,0xE0,0x58,0xD5,0xF0,0x04];
|
||||
let mine = SHA3_384::hash(&empty);
|
||||
assert_eq!(hashres.to_vec(), mine);
|
||||
}
|
||||
|
||||
// see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA3-384_1600.pdf
|
||||
#[test]
|
||||
fn nist_1600_example() {
|
||||
let example = [0xA3; 200];
|
||||
let hashres = [0x18,0x81,0xDE,0x2C,0xA7,0xE4,0x1E,0xF9,
|
||||
0x5D,0xC4,0x73,0x2B,0x8F,0x5F,0x00,0x2B,
|
||||
0x18,0x9C,0xC1,0xE4,0x2B,0x74,0x16,0x8E,
|
||||
0xD1,0x73,0x26,0x49,0xCE,0x1D,0xBC,0xDD,
|
||||
0x76,0x19,0x7A,0x31,0xFD,0x55,0xEE,0x98,
|
||||
0x9F,0x2D,0x70,0x50,0xDD,0x47,0x3E,0x8F];
|
||||
let mine = SHA3_384::hash(&example);
|
||||
assert_eq!(hashres.to_vec(), mine);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn nist_test_vectors() {
|
||||
let fname = "testdata/sha/nist_sha3_384.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negl, lbytes) = case.get("l").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!negl && !negm && !negd);
|
||||
let msg = if lbytes[0] == 0 { Vec::new() } else { mbytes.clone() };
|
||||
let digest = SHA3_384::hash(&msg);
|
||||
assert_eq!(dbytes, &digest);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// The SHA3-512 hash. [BEST]
|
||||
///
|
||||
/// To use, you can run it in incremental mode -- by calling new(),
|
||||
/// update() zero or more times, and then finalize() -- or you can
|
||||
/// just invoke the hash directly. For example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use simple_crypto::sha::{Hash,SHA3_512};
|
||||
///
|
||||
/// let empty = [0; 0];
|
||||
/// // Do the hash using the incremental API
|
||||
/// let mut hashf = SHA3_512::new();
|
||||
/// hashf.update(&empty);
|
||||
/// let result_incremental = hashf.finalize();
|
||||
/// // Do the hash using the direct API
|
||||
/// let result_direct = SHA3_512::hash(&empty);
|
||||
/// // ... and they should be the same
|
||||
/// assert_eq!(result_incremental,result_direct);
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct SHA3_512 {
|
||||
state: Keccak
|
||||
}
|
||||
|
||||
impl Hash for SHA3_512 {
|
||||
fn new() -> Self
|
||||
{
|
||||
SHA3_512{ state: Keccak::new(1600 - 1024) }
|
||||
}
|
||||
|
||||
fn update(&mut self, buffer: &[u8])
|
||||
{
|
||||
self.state.process(&buffer);
|
||||
}
|
||||
|
||||
fn finalize(&mut self) -> Vec<u8>
|
||||
{
|
||||
self.state.tag_and_pad(0x06);
|
||||
self.state.squeeze(512 / 8)
|
||||
}
|
||||
|
||||
fn block_size() -> usize
|
||||
{
|
||||
576
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod sha512 {
|
||||
use super::*;
|
||||
use testing::run_test;
|
||||
|
||||
// see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA3-512_Msg0.pdf
|
||||
#[test]
|
||||
fn nist_empty_example() {
|
||||
let empty = [0; 0];
|
||||
let hashres = [0xA6,0x9F,0x73,0xCC,0xA2,0x3A,0x9A,0xC5,
|
||||
0xC8,0xB5,0x67,0xDC,0x18,0x5A,0x75,0x6E,
|
||||
0x97,0xC9,0x82,0x16,0x4F,0xE2,0x58,0x59,
|
||||
0xE0,0xD1,0xDC,0xC1,0x47,0x5C,0x80,0xA6,
|
||||
0x15,0xB2,0x12,0x3A,0xF1,0xF5,0xF9,0x4C,
|
||||
0x11,0xE3,0xE9,0x40,0x2C,0x3A,0xC5,0x58,
|
||||
0xF5,0x00,0x19,0x9D,0x95,0xB6,0xD3,0xE3,
|
||||
0x01,0x75,0x85,0x86,0x28,0x1D,0xCD,0x26];
|
||||
let mine = SHA3_512::hash(&empty);
|
||||
assert_eq!(hashres.to_vec(), mine);
|
||||
}
|
||||
|
||||
// see https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/SHA3-512_1600.pdf
|
||||
#[test]
|
||||
fn nist_1600_example() {
|
||||
let example = [0xA3; 200];
|
||||
let hashres = [0xE7,0x6D,0xFA,0xD2,0x20,0x84,0xA8,0xB1,
|
||||
0x46,0x7F,0xCF,0x2F,0xFA,0x58,0x36,0x1B,
|
||||
0xEC,0x76,0x28,0xED,0xF5,0xF3,0xFD,0xC0,
|
||||
0xE4,0x80,0x5D,0xC4,0x8C,0xAE,0xEC,0xA8,
|
||||
0x1B,0x7C,0x13,0xC3,0x0A,0xDF,0x52,0xA3,
|
||||
0x65,0x95,0x84,0x73,0x9A,0x2D,0xF4,0x6B,
|
||||
0xE5,0x89,0xC5,0x1C,0xA1,0xA4,0xA8,0x41,
|
||||
0x6D,0xF6,0x54,0x5A,0x1C,0xE8,0xBA,0x00];
|
||||
let mine = SHA3_512::hash(&example);
|
||||
assert_eq!(hashres.to_vec(), mine);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn nist_test_vectors() {
|
||||
let fname = "testdata/sha/nist_sha3_512.test";
|
||||
run_test(fname.to_string(), 3, |case| {
|
||||
let (negl, lbytes) = case.get("l").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
|
||||
assert!(!negl && !negm && !negd);
|
||||
let msg = if lbytes[0] == 0 { Vec::new() } else { mbytes.clone() };
|
||||
let digest = SHA3_512::hash(&msg);
|
||||
assert_eq!(dbytes, &digest);
|
||||
});
|
||||
}
|
||||
}
|
||||
101
src/sha/shared.rs
Normal file
101
src/sha/shared.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
macro_rules! ch {
|
||||
($x: expr, $y: expr, $z: expr) => {{
|
||||
let xval = $x;
|
||||
(xval & $y) ^ (!xval & $z)
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! parity {
|
||||
($x: expr, $y: expr, $z: expr) => {
|
||||
$x ^ $y ^ $z
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! maj {
|
||||
($x: expr, $y: expr, $z: expr) => {{
|
||||
/* the original function is (x & y) ^ (x & z) ^ (y & z).
|
||||
if you fire off truth tables, this is equivalent to
|
||||
(x & y) | (x & z) | (y & z)
|
||||
which you can then use distribution on:
|
||||
(x & (y | z)) | (y & z)
|
||||
which saves one operation */
|
||||
let yval = $y;
|
||||
let zval = $z;
|
||||
($x & (yval | zval)) | (yval & zval)
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! process_u32_block {
|
||||
($buf: expr, $off: expr, $self: ident) => {{
|
||||
let w00 = ($buf[$off+0] as u32) << 24 | ($buf[$off+1] as u32) << 16 |
|
||||
($buf[$off+2] as u32) << 8 | ($buf[$off+3] as u32);
|
||||
let w01 = ($buf[$off+4] as u32) << 24 | ($buf[$off+5] as u32) << 16 |
|
||||
($buf[$off+6] as u32) << 8 | ($buf[$off+7] as u32);
|
||||
let w02 = ($buf[$off+8] as u32) << 24 | ($buf[$off+9] as u32) << 16 |
|
||||
($buf[$off+10] as u32) << 8 | ($buf[$off+11] as u32);
|
||||
let w03 = ($buf[$off+12] as u32) << 24 | ($buf[$off+13] as u32) << 16 |
|
||||
($buf[$off+14] as u32) << 8 | ($buf[$off+15] as u32);
|
||||
let w04 = ($buf[$off+16] as u32) << 24 | ($buf[$off+17] as u32) << 16 |
|
||||
($buf[$off+18] as u32) << 8 | ($buf[$off+19] as u32);
|
||||
let w05 = ($buf[$off+20] as u32) << 24 | ($buf[$off+21] as u32) << 16 |
|
||||
($buf[$off+22] as u32) << 8 | ($buf[$off+23] as u32);
|
||||
let w06 = ($buf[$off+24] as u32) << 24 | ($buf[$off+25] as u32) << 16 |
|
||||
($buf[$off+26] as u32) << 8 | ($buf[$off+27] as u32);
|
||||
let w07 = ($buf[$off+28] as u32) << 24 | ($buf[$off+29] as u32) << 16 |
|
||||
($buf[$off+30] as u32) << 8 | ($buf[$off+31] as u32);
|
||||
let w08 = ($buf[$off+32] as u32) << 24 | ($buf[$off+33] as u32) << 16 |
|
||||
($buf[$off+34] as u32) << 8 | ($buf[$off+35] as u32);
|
||||
let w09 = ($buf[$off+36] as u32) << 24 | ($buf[$off+37] as u32) << 16 |
|
||||
($buf[$off+38] as u32) << 8 | ($buf[$off+39] as u32);
|
||||
let w10 = ($buf[$off+40] as u32) << 24 | ($buf[$off+41] as u32) << 16 |
|
||||
($buf[$off+42] as u32) << 8 | ($buf[$off+43] as u32);
|
||||
let w11 = ($buf[$off+44] as u32) << 24 | ($buf[$off+45] as u32) << 16 |
|
||||
($buf[$off+46] as u32) << 8 | ($buf[$off+47] as u32);
|
||||
let w12 = ($buf[$off+48] as u32) << 24 | ($buf[$off+49] as u32) << 16 |
|
||||
($buf[$off+50] as u32) << 8 | ($buf[$off+51] as u32);
|
||||
let w13 = ($buf[$off+52] as u32) << 24 | ($buf[$off+53] as u32) << 16 |
|
||||
($buf[$off+54] as u32) << 8 | ($buf[$off+55] as u32);
|
||||
let w14 = ($buf[$off+56] as u32) << 24 | ($buf[$off+57] as u32) << 16 |
|
||||
($buf[$off+58] as u32) << 8 | ($buf[$off+59] as u32);
|
||||
let w15 = ($buf[$off+60] as u32) << 24 | ($buf[$off+61] as u32) << 16 |
|
||||
($buf[$off+62] as u32) << 8 | ($buf[$off+63] as u32);
|
||||
$self.process(w00, w01, w02, w03, w04, w05, w06, w07,
|
||||
w08, w09, w10, w11, w12, w13, w14, w15);
|
||||
}};
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Calculate the value `k` used in the padding for all the hashes, solving the
|
||||
// equation (l + 1 + k) mod b = a.
|
||||
pub fn calculate_k(a: usize, b: usize, l: usize) -> usize
|
||||
{
|
||||
(a - (l + 1)) % b
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
quickcheck!
|
||||
{
|
||||
fn maj_rewrite_ok(x: u64, y: u64, z: u64) -> bool
|
||||
{
|
||||
let orig = (x & y) ^ (x & z) ^ (y & z);
|
||||
maj!(x, y, z) == orig
|
||||
}
|
||||
|
||||
// Note, these two laws hold because we hash with bytes as the atomic size,
|
||||
// not bits. If we hashed true bit streams, we'd be in trouble.
|
||||
fn sha1_k_plus_1_multiple_of_8(lbytes: usize) -> bool
|
||||
{
|
||||
let l = lbytes * 8;
|
||||
(calculate_k(448,512,l) + 1) % 8 == 0
|
||||
}
|
||||
|
||||
// Note, these two laws hold because we hash with bytes as the atomic size,
|
||||
// not bits. If we hashed true bit streams, we'd be in trouble.
|
||||
fn sha2_k_plus_1_multiple_of_8(lbytes: usize) -> bool
|
||||
{
|
||||
let l = lbytes * 8;
|
||||
(calculate_k(896,1024,l) + 1) % 8 == 0
|
||||
}
|
||||
}
|
||||
|
||||
215
src/shake.rs
Normal file
215
src/shake.rs
Normal file
@@ -0,0 +1,215 @@
|
||||
//! This module implements the SHAKE family of variable-length hash functions,
|
||||
//! which NIST also referes to as Extendable-Output Functions (XOFs). They are
|
||||
//! based on the same underlying hashing mechanism used in SHA3, but can be
|
||||
//! tuned to output a variety of different hash lengths. One trick is that the
|
||||
//! security of the hash is the minimum of the defined bit size (128 for
|
||||
//! SHAKE128, or 256 for SHAKE256) and the output hash length, so if you use
|
||||
//! shorter hashes you lose some amount of collision protection.
|
||||
//!
|
||||
//! Because the output is variable length, these don't quite fit into the
|
||||
//! normal `Hash` trait. Instead, they implement the same basic functions,
|
||||
//! but with `hash` and `finalize` functions extended with an additional
|
||||
//! output length function. Usage is thus in the analagous way to normal
|
||||
//! hashing:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use simple_crypto::shake::SHAKE128;
|
||||
//!
|
||||
//! // Use SHAKE incrementally
|
||||
//! let empty = [0; 0];
|
||||
//! let mut shakef = SHAKE128::new();
|
||||
//! shakef.update(&empty);
|
||||
//! let result_inc = shakef.finalize(384);
|
||||
//! // Use SHAKE directly
|
||||
//! let result_dir = SHAKE128::hash(&empty, 384);
|
||||
//! // ... and the answers should be the same.
|
||||
//! assert_eq!(result_inc, result_dir);
|
||||
//! ```
|
||||
use sha::Keccak;
|
||||
|
||||
/// The SHAKE128 variable-length hash.
|
||||
///
|
||||
/// This generates a variable-length hash value, although it's not necessarily as
|
||||
/// strong as a hash of the same value. My understanding (which is admittedly
|
||||
/// limited; I've never seen these used) is that this is more for convenience
|
||||
/// when you want to fit into particularly-sized regions. The 128 is the
|
||||
/// approximate maximum bit strength of the hash in bits; the true strength is
|
||||
/// the minimum of the length of the output hash and 128.
|
||||
///
|
||||
/// `SHAKE128` does not implement `Hash`, because it is finalized differently,
|
||||
/// but we've kept something of the flavor of the `Hash` interface for
|
||||
/// familiarity.
|
||||
///
|
||||
/// Like the SHA3 variants, this can be used incrementally or directly, as per
|
||||
/// usual:
|
||||
///
|
||||
/// ```rust
|
||||
/// use simple_crypto::shake::SHAKE128;
|
||||
///
|
||||
/// // Use SHAKE incrementally
|
||||
/// let empty = [0; 0];
|
||||
/// let mut shakef = SHAKE128::new();
|
||||
/// shakef.update(&empty);
|
||||
/// let result_inc = shakef.finalize(384);
|
||||
/// // Use SHAKE directly
|
||||
/// let result_dir = SHAKE128::hash(&empty, 384);
|
||||
/// // ... and the answers should be the same.
|
||||
/// assert_eq!(result_inc, result_dir);
|
||||
/// ```
|
||||
pub struct SHAKE128 {
|
||||
state: Keccak
|
||||
}
|
||||
|
||||
impl SHAKE128 {
|
||||
/// Create a fresh, new SHAKE128 instance for incremental use.
|
||||
pub fn new() -> Self
|
||||
{
|
||||
SHAKE128{
|
||||
state: Keccak::new(1600 - 256)
|
||||
}
|
||||
}
|
||||
|
||||
/// Add more data into the hash function for processing.
|
||||
pub fn update(&mut self, buffer: &[u8])
|
||||
{
|
||||
self.state.process(&buffer);
|
||||
}
|
||||
|
||||
/// Generate the final hash. Because this is a variable-length hash,
|
||||
/// you will need to provide the output size in bits. Note that this
|
||||
/// output size *must* be a multiple of 8, and that the security
|
||||
/// strength of the whole hash is approximately the minimum of this
|
||||
/// length and 128 bits.
|
||||
pub fn finalize(&mut self, outsize: usize) -> Vec<u8>
|
||||
{
|
||||
assert_eq!(outsize % 8, 0);
|
||||
self.state.tag_and_pad(0x1F);
|
||||
self.state.squeeze(outsize / 8)
|
||||
}
|
||||
|
||||
/// Directly generate the SHAKE128 hash of the given buffer, returning
|
||||
/// a hash value of the given size (in bits). Presently, the output
|
||||
/// size *must* be a multiple of 8, although this may change in the
|
||||
/// future.
|
||||
pub fn hash(buffer: &[u8], outsize: usize) -> Vec<u8>
|
||||
{
|
||||
let mut x = Self::new();
|
||||
x.update(&buffer);
|
||||
x.finalize(outsize)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use testing::run_test;
|
||||
#[cfg(test)]
|
||||
use cryptonum::unsigned::{Decoder,U192};
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn shake128() {
|
||||
let fname = "testdata/sha/shake128.test";
|
||||
run_test(fname.to_string(), 4, |case| {
|
||||
let (negl, lbytes) = case.get("l").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
let (nego, obytes) = case.get("o").unwrap();
|
||||
|
||||
assert!(!negl && !negm && !negd && !nego);
|
||||
let msg = if lbytes[0] == 0 { Vec::new() } else { mbytes.clone() };
|
||||
let osize = usize::from(U192::from_bytes(obytes));
|
||||
let digest = SHAKE128::hash(&msg, osize);;
|
||||
assert_eq!(dbytes, &digest);
|
||||
});
|
||||
}
|
||||
|
||||
/// The SHAKE256 variable-length hash.
|
||||
///
|
||||
/// This generates a variable-length hash value, although it's not necessarily as
|
||||
/// strong as a hash of the same value. My understanding (which is admittedly
|
||||
/// limited; I've never seen these used) is that this is more for convenience
|
||||
/// when you want to fit into particularly-sized regions. The 256 is the
|
||||
/// approximate maximum bit strength of the hash in bits; the true strength is
|
||||
/// the minimum of the length of the output hash and 256.
|
||||
///
|
||||
/// `SHAKE256` does not implement `Hash`, because it is finalized differently,
|
||||
/// but we've kept something of the flavor of the `Hash` interface for
|
||||
/// familiarity.
|
||||
///
|
||||
/// Like the SHA3 variants, this can be used incrementally or directly, as per
|
||||
/// usual:
|
||||
///
|
||||
/// ```rust
|
||||
/// use simple_crypto::shake::SHAKE256;
|
||||
///
|
||||
/// // Use SHAKE incrementally
|
||||
/// let empty = [0; 0];
|
||||
/// let mut shakef = SHAKE256::new();
|
||||
/// shakef.update(&empty);
|
||||
/// let result_inc = shakef.finalize(384);
|
||||
/// // Use SHAKE directly
|
||||
/// let result_dir = SHAKE256::hash(&empty, 384);
|
||||
/// // ... and the answers should be the same.
|
||||
/// assert_eq!(result_inc, result_dir);
|
||||
/// ```
|
||||
pub struct SHAKE256 {
|
||||
state: Keccak
|
||||
}
|
||||
|
||||
impl SHAKE256 {
|
||||
/// Create a fresh, new SHAKE256 instance for incremental use.
|
||||
pub fn new() -> Self
|
||||
{
|
||||
SHAKE256{
|
||||
state: Keccak::new(1600 - 512)
|
||||
}
|
||||
}
|
||||
|
||||
/// Add more data into the hash function for processing.
|
||||
pub fn update(&mut self, buffer: &[u8])
|
||||
{
|
||||
self.state.process(&buffer);
|
||||
}
|
||||
|
||||
/// Generate the final hash. Because this is a variable-length hash,
|
||||
/// you will need to provide the output size in bits. Note that this
|
||||
/// output size *must* be a multiple of 8, and that the security
|
||||
/// strength of the whole hash is approximately the minimum of this
|
||||
/// length and 256 bits.
|
||||
pub fn finalize(&mut self, outsize: usize) -> Vec<u8>
|
||||
{
|
||||
assert_eq!(outsize % 8, 0);
|
||||
self.state.tag_and_pad(0x1F);
|
||||
self.state.squeeze(outsize / 8)
|
||||
}
|
||||
|
||||
/// Directly generate the SHAKE256 hash of the given buffer, returning
|
||||
/// a hash value of the given size (in bits). Presently, the output
|
||||
/// size *must* be a multiple of 8, although this may change in the
|
||||
/// future.
|
||||
pub fn hash(buffer: &[u8], outsize: usize) -> Vec<u8>
|
||||
{
|
||||
let mut x = Self::new();
|
||||
x.update(&buffer);
|
||||
x.finalize(outsize)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn shake256() {
|
||||
let fname = "testdata/sha/shake256.test";
|
||||
run_test(fname.to_string(), 4, |case| {
|
||||
let (negl, lbytes) = case.get("l").unwrap();
|
||||
let (negm, mbytes) = case.get("m").unwrap();
|
||||
let (negd, dbytes) = case.get("d").unwrap();
|
||||
let (nego, obytes) = case.get("o").unwrap();
|
||||
|
||||
assert!(!negl && !negm && !negd && !nego);
|
||||
let msg = if lbytes[0] == 0 { Vec::new() } else { mbytes.clone() };
|
||||
let osize = usize::from(U192::from_bytes(obytes));
|
||||
let digest = SHAKE256::hash(&msg, osize);;
|
||||
assert_eq!(dbytes, &digest);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
75
src/ssh/dsa.rs
Normal file
75
src/ssh/dsa.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use cryptonum::unsigned::*;
|
||||
use dsa::{DSAKeyPair,DSAParameters,DSAPublicKey,DSAPrivateKey,L1024N160};
|
||||
use std::io::{Read,Write};
|
||||
use ssh::errors::{SSHKeyParseError,SSHKeyRenderError};
|
||||
use ssh::frame::*;
|
||||
use ssh::SSHKey;
|
||||
|
||||
impl SSHKey for DSAKeyPair<L1024N160> {
|
||||
fn valid_keytype(s: &str) -> bool {
|
||||
(s == "ssh-dss") || (s == "dss")
|
||||
}
|
||||
|
||||
fn parse_ssh_public_info<I: Read>(inp: &mut I) -> Result<Self::Public,SSHKeyParseError>
|
||||
{
|
||||
let pubkey_type = parse_openssh_string(inp)?;
|
||||
if !Self::valid_keytype(&pubkey_type) {
|
||||
return Err(SSHKeyParseError::UnknownKeyType(pubkey_type));
|
||||
}
|
||||
let pubp = parse_openssh_number(inp)?;
|
||||
let pubq = parse_openssh_number(inp)?;
|
||||
let pubg = parse_openssh_number(inp)?;
|
||||
let pubparams = L1024N160::new(pubp, pubg, pubq);
|
||||
let puby: U1024 = parse_openssh_number(inp)?;
|
||||
for _ in inp.bytes() { return Err(SSHKeyParseError::UnknownTrailingData); }
|
||||
Ok(DSAPublicKey::<L1024N160>::new(pubparams.clone(), puby.clone()))
|
||||
}
|
||||
|
||||
fn parse_ssh_private_info<I: Read>(inp: &mut I) -> Result<(Self::Private,String),SSHKeyParseError>
|
||||
{
|
||||
let check1 = parse_openssh_u32(inp)?;
|
||||
let check2 = parse_openssh_u32(inp)?;
|
||||
if check1 != check2 {
|
||||
return Err(SSHKeyParseError::PrivateKeyCorruption);
|
||||
}
|
||||
let privkey_type = parse_openssh_string(inp)?;
|
||||
if !Self::valid_keytype(&privkey_type) {
|
||||
return Err(SSHKeyParseError::InconsistentKeyTypes("ssh-dss".to_string(), privkey_type));
|
||||
}
|
||||
let privp = parse_openssh_number(inp)?;
|
||||
let privq = parse_openssh_number(inp)?;
|
||||
let privg = parse_openssh_number(inp)?;
|
||||
let privparams = L1024N160::new(privp, privg, privq);
|
||||
let _ = parse_openssh_buffer(inp)?; // a copy of y we don't need
|
||||
let privx = parse_openssh_number(inp)?;
|
||||
|
||||
let privkey = DSAPrivateKey::<L1024N160>::new(privparams, privx);
|
||||
let comment = parse_openssh_string(inp)?;
|
||||
for (idx,byte) in inp.bytes().enumerate() {
|
||||
if ((idx+1) as u8) != byte? {
|
||||
return Err(SSHKeyParseError::InvalidPadding);
|
||||
}
|
||||
}
|
||||
|
||||
Ok((privkey,comment))
|
||||
}
|
||||
|
||||
fn render_ssh_public_info<O: Write>(&self, out: &mut O) -> Result<(),SSHKeyRenderError>
|
||||
{
|
||||
render_openssh_string(out, "ssh-dss")?;
|
||||
render_openssh_number(out, &self.public.params.p)?;
|
||||
render_openssh_number(out, &self.public.params.q)?;
|
||||
render_openssh_number(out, &self.public.params.g)?;
|
||||
render_openssh_number(out, &self.public.y)
|
||||
}
|
||||
|
||||
fn render_ssh_private_info<O: Write>(&self, out: &mut O) -> Result<(),SSHKeyRenderError>
|
||||
{
|
||||
render_openssh_string(out, "ssh-dss")?;
|
||||
render_openssh_number(out, &self.private.params.p)?;
|
||||
render_openssh_number(out, &self.private.params.q)?;
|
||||
render_openssh_number(out, &self.private.params.g)?;
|
||||
render_openssh_number(out, &self.public.y)?;
|
||||
render_openssh_number(out, &self.private.x)
|
||||
}
|
||||
}
|
||||
171
src/ssh/ecdsa.rs
Normal file
171
src/ssh/ecdsa.rs
Normal file
@@ -0,0 +1,171 @@
|
||||
use cryptonum::unsigned::*;
|
||||
use ecdsa::{ECDSAPair,ECDSAPublic,ECCPublicKey,ECDSAPrivate,ECCPrivateKey};
|
||||
use ecdsa::{EllipticCurve,P256,P384,P521};
|
||||
use std::io::{Read,Write};
|
||||
use ssh::errors::{SSHKeyParseError,SSHKeyRenderError};
|
||||
use ssh::frame::*;
|
||||
use ssh::SSHKey;
|
||||
|
||||
impl SSHKey for ECDSAPair {
|
||||
fn valid_keytype(s: &str) -> bool {
|
||||
(s == "ssh-ecdsa") || (s == "ecdsa") || (s == "ecdsa-sha2-nistp256") ||
|
||||
(s == "ecdsa-sha2-nistp384") || (s == "ecdsa-sha2-nistp521")
|
||||
}
|
||||
|
||||
fn parse_ssh_public_info<I: Read>(inp: &mut I) -> Result<Self::Public,SSHKeyParseError>
|
||||
{
|
||||
let pubkey_type = parse_openssh_string(inp)?;
|
||||
if !Self::valid_keytype(&pubkey_type) {
|
||||
return Err(SSHKeyParseError::UnknownKeyType(pubkey_type));
|
||||
}
|
||||
// this peaks a little under the cover a bit (it'd be nice to pretend
|
||||
// that we didn't know the number format was the same as the buffer
|
||||
// one), but we need to infer what kind of key this is, and this appears
|
||||
// to be the easiest / fastest way.
|
||||
let curve = parse_openssh_string(inp)?;
|
||||
match curve.as_ref() {
|
||||
"nistp256" => {
|
||||
let val = parse_openssh_buffer(inp)?;
|
||||
if val[0] != 4 || val.len() != 65 {
|
||||
return Err(SSHKeyParseError::InvalidECPointCompression);
|
||||
}
|
||||
let x = U256::from_bytes(&val[1..33]);
|
||||
let y = U256::from_bytes(&val[33..]);
|
||||
let p = P256::new_point(x, y);
|
||||
let pbl = ECCPublicKey::<P256>::new(p);
|
||||
Ok(ECDSAPublic::P256(pbl))
|
||||
}
|
||||
"nistp384" => {
|
||||
let val = parse_openssh_buffer(inp)?;
|
||||
if val[0] != 4 || val.len() != 97 {
|
||||
return Err(SSHKeyParseError::InvalidECPointCompression);
|
||||
}
|
||||
let x = U384::from_bytes(&val[1..49]);
|
||||
let y = U384::from_bytes(&val[49..]);
|
||||
let p = P384::new_point(x, y);
|
||||
let pbl = ECCPublicKey::<P384>::new(p);
|
||||
Ok(ECDSAPublic::P384(pbl))
|
||||
}
|
||||
"nistp521" => {
|
||||
let val = parse_openssh_buffer(inp)?;
|
||||
if val[0] != 4 || val.len() != 133 {
|
||||
return Err(SSHKeyParseError::InvalidECPointCompression);
|
||||
}
|
||||
let x = U576::from_bytes(&val[1..67]);
|
||||
let y = U576::from_bytes(&val[67..]);
|
||||
let p = P521::new_point(x, y);
|
||||
let pbl = ECCPublicKey::<P521>::new(p);
|
||||
Ok(ECDSAPublic::P521(pbl))
|
||||
}
|
||||
_ => {
|
||||
return Err(SSHKeyParseError::UnknownECDSACurve(curve))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_ssh_private_info<I: Read>(inp: &mut I) -> Result<(Self::Private,String),SSHKeyParseError>
|
||||
{
|
||||
let check1 = parse_openssh_u32(inp)?;
|
||||
let check2 = parse_openssh_u32(inp)?;
|
||||
if check1 != check2 {
|
||||
return Err(SSHKeyParseError::PrivateKeyCorruption);
|
||||
}
|
||||
let res = match ECDSAPair::parse_ssh_public_info(inp)? {
|
||||
ECDSAPublic::P192(_) => return Err(SSHKeyParseError::PrivateKeyCorruption),
|
||||
ECDSAPublic::P224(_) => return Err(SSHKeyParseError::PrivateKeyCorruption),
|
||||
ECDSAPublic::P256(_) => {
|
||||
let mut dbytes = parse_openssh_buffer(inp)?;
|
||||
while dbytes[0] == 0 { dbytes.remove(0); }
|
||||
assert!(dbytes.len() <= 32);
|
||||
let d = U256::from_bytes(&dbytes);
|
||||
ECDSAPrivate::P256(ECCPrivateKey::<P256>::new(d))
|
||||
}
|
||||
ECDSAPublic::P384(_) => {
|
||||
let mut dbytes = parse_openssh_buffer(inp)?;
|
||||
while dbytes[0] == 0 { dbytes.remove(0); }
|
||||
assert!(dbytes.len() <= 48);
|
||||
let d = U384::from_bytes(&dbytes);
|
||||
ECDSAPrivate::P384(ECCPrivateKey::<P384>::new(d))
|
||||
}
|
||||
ECDSAPublic::P521(_) => {
|
||||
let mut dbytes = parse_openssh_buffer(inp)?;
|
||||
while dbytes[0] == 0 { dbytes.remove(0); }
|
||||
assert!(dbytes.len() <= 66);
|
||||
let d = U576::from_bytes(&dbytes);
|
||||
ECDSAPrivate::P521(ECCPrivateKey::<P521>::new(d))
|
||||
}
|
||||
};
|
||||
let comment = parse_openssh_string(inp)?;
|
||||
for (idx,byte) in inp.bytes().enumerate() {
|
||||
if ((idx+1) as u8) != byte? {
|
||||
return Err(SSHKeyParseError::InvalidPadding);
|
||||
}
|
||||
}
|
||||
|
||||
Ok((res, comment))
|
||||
}
|
||||
|
||||
fn render_ssh_public_info<O: Write>(&self, out: &mut O) -> Result<(),SSHKeyRenderError>
|
||||
{
|
||||
render_openssh_string(out, "ssh-ecdsa")?;
|
||||
match self {
|
||||
ECDSAPair::P192(_,_) =>
|
||||
return Err(SSHKeyRenderError::IllegalECDSAKeyType("P192".to_string())),
|
||||
ECDSAPair::P224(_,_) =>
|
||||
return Err(SSHKeyRenderError::IllegalECDSAKeyType("P224".to_string())),
|
||||
ECDSAPair::P256(pu,_) => {
|
||||
render_openssh_string(out, "nistp256")?;
|
||||
let mut vec = Vec::with_capacity(66);
|
||||
vec.write(&[4u8])?;
|
||||
render_number(256, &mut vec, &U256::from(pu.q.x.clone()))?;
|
||||
render_number(256, &mut vec, &U256::from(pu.q.y.clone()))?;
|
||||
render_openssh_buffer(out, &vec)?;
|
||||
}
|
||||
ECDSAPair::P384(pu,_) => {
|
||||
render_openssh_string(out, "nistp384")?;
|
||||
let mut vec = Vec::with_capacity(66);
|
||||
vec.write(&[4u8])?;
|
||||
render_number(384, &mut vec, &U384::from(pu.q.x.clone()))?;
|
||||
render_number(384, &mut vec, &U384::from(pu.q.y.clone()))?;
|
||||
render_openssh_buffer(out, &vec)?;
|
||||
}
|
||||
ECDSAPair::P521(pu,_) => {
|
||||
render_openssh_string(out, "nistp521")?;
|
||||
let mut vec = Vec::with_capacity(66);
|
||||
vec.write(&[4u8])?;
|
||||
render_number(521, &mut vec, &U576::from(pu.q.x.clone()))?;
|
||||
render_number(521, &mut vec, &U576::from(pu.q.y.clone()))?;
|
||||
render_openssh_buffer(out, &vec)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_ssh_private_info<O: Write>(&self, out: &mut O) -> Result<(),SSHKeyRenderError>
|
||||
{
|
||||
self.render_ssh_public_info(out)?;
|
||||
match self {
|
||||
ECDSAPair::P192(_,_) =>
|
||||
return Err(SSHKeyRenderError::IllegalECDSAKeyType("P192".to_string())),
|
||||
ECDSAPair::P224(_,_) =>
|
||||
return Err(SSHKeyRenderError::IllegalECDSAKeyType("P224".to_string())),
|
||||
ECDSAPair::P256(_,pr) => { render_openssh_u32(out, 256/8)?; render_number(256, out, &pr.d)?; }
|
||||
ECDSAPair::P384(_,pr) => { render_openssh_u32(out, 384/8)?; render_number(384, out, &pr.d)?; }
|
||||
ECDSAPair::P521(_,pr) => { render_openssh_u32(out, 528/8)?; render_number(521, out, &pr.d)?; }
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn render_number<O,N>(bitlen: usize, out: &mut O, val: &N) -> Result<(),SSHKeyRenderError>
|
||||
where
|
||||
O: Write,
|
||||
N: Encoder
|
||||
{
|
||||
let mut outvec = Vec::new();
|
||||
outvec.write(&val.to_bytes())?;
|
||||
while outvec.len() < ((bitlen + 7) / 8) { outvec.insert(0,0); }
|
||||
while outvec.len() > ((bitlen + 7) / 8) { outvec.remove(0); }
|
||||
out.write(&outvec)?;
|
||||
Ok(())
|
||||
}
|
||||
58
src/ssh/ed25519.rs
Normal file
58
src/ssh/ed25519.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use ed25519::{ED25519KeyPair,ED25519Private,ED25519Public};
|
||||
use std::io::{Read,Write};
|
||||
use ssh::errors::{SSHKeyParseError,SSHKeyRenderError};
|
||||
use ssh::frame::*;
|
||||
use ssh::SSHKey;
|
||||
|
||||
impl SSHKey for ED25519KeyPair {
|
||||
fn valid_keytype(s: &str) -> bool {
|
||||
(s == "ssh-ed25519")
|
||||
}
|
||||
|
||||
fn parse_ssh_public_info<I: Read>(inp: &mut I) -> Result<Self::Public,SSHKeyParseError>
|
||||
{
|
||||
let pubkey_type = parse_openssh_string(inp)?;
|
||||
if !Self::valid_keytype(&pubkey_type) {
|
||||
return Err(SSHKeyParseError::UnknownKeyType(pubkey_type));
|
||||
}
|
||||
let pubkey_bytes = parse_openssh_buffer(inp)?;
|
||||
Ok(ED25519Public::new(&pubkey_bytes)?)
|
||||
}
|
||||
|
||||
fn parse_ssh_private_info<I: Read>(inp: &mut I) -> Result<(Self::Private,String),SSHKeyParseError>
|
||||
{
|
||||
let check1 = parse_openssh_u32(inp)?;
|
||||
let check2 = parse_openssh_u32(inp)?;
|
||||
if check1 != check2 {
|
||||
return Err(SSHKeyParseError::PrivateKeyCorruption);
|
||||
}
|
||||
let public = ED25519KeyPair::parse_ssh_public_info(inp)?;
|
||||
let private_bytes = parse_openssh_buffer(inp)?;
|
||||
let private = ED25519Private::from_seed(&private_bytes[0..32]);
|
||||
let comment = parse_openssh_string(inp)?;
|
||||
for (idx,byte) in inp.bytes().enumerate() {
|
||||
if ((idx+1) as u8) != byte? {
|
||||
return Err(SSHKeyParseError::InvalidPadding);
|
||||
}
|
||||
}
|
||||
assert_eq!(public, ED25519Public::from(&private));
|
||||
|
||||
Ok((private, comment))
|
||||
}
|
||||
|
||||
fn render_ssh_public_info<O: Write>(&self, out: &mut O) -> Result<(),SSHKeyRenderError>
|
||||
{
|
||||
render_openssh_string(out, "ssh-ed25519")?;
|
||||
render_openssh_buffer(out, &self.public.to_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_ssh_private_info<O: Write>(&self, out: &mut O) -> Result<(),SSHKeyRenderError>
|
||||
{
|
||||
self.render_ssh_public_info(out)?;
|
||||
let mut private_bytes = self.private.to_bytes();
|
||||
private_bytes.append(&mut self.public.to_bytes());
|
||||
render_openssh_buffer(out, &private_bytes)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
68
src/ssh/errors.rs
Normal file
68
src/ssh/errors.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use base64::DecodeError;
|
||||
use ed25519::ED25519PublicImportError;
|
||||
use std::io;
|
||||
|
||||
/// A whole pile of errors that you can get when parsing an SSH key from
|
||||
/// disk or memory.
|
||||
#[derive(Debug)]
|
||||
pub enum SSHKeyParseError
|
||||
{
|
||||
DecodeError(DecodeError),
|
||||
IOError(io::Error),
|
||||
NoBeginBannerFound, NoEndBannerFound,
|
||||
NoOpenSSHMagicHeader,
|
||||
UnknownKeyCipher(String),
|
||||
UnknownKDF(String), UnexpectedKDFOptions,
|
||||
InvalidNumberOfKeys(u32),
|
||||
UnknownTrailingData,
|
||||
UnknownKeyType(String),
|
||||
InvalidPublicKeyMaterial,
|
||||
PrivateKeyCorruption,
|
||||
InconsistentKeyTypes(String,String),
|
||||
InconsistentPublicKeyValue,
|
||||
InvalidPrivateKeyValue,
|
||||
InvalidPadding,
|
||||
InvalidPublicKeyType,
|
||||
BrokenPublicKeyLine,
|
||||
UnknownECDSACurve(String),
|
||||
InvalidECPointCompression
|
||||
}
|
||||
|
||||
impl From<DecodeError> for SSHKeyParseError {
|
||||
fn from(e: DecodeError) -> SSHKeyParseError {
|
||||
SSHKeyParseError::DecodeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for SSHKeyParseError {
|
||||
fn from(e: io::Error) -> SSHKeyParseError {
|
||||
SSHKeyParseError::IOError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ED25519PublicImportError> for SSHKeyParseError {
|
||||
fn from(e: ED25519PublicImportError) -> SSHKeyParseError {
|
||||
match e {
|
||||
ED25519PublicImportError::WrongNumberOfBytes(_) =>
|
||||
SSHKeyParseError::InvalidPublicKeyMaterial,
|
||||
ED25519PublicImportError::InvalidPublicPoint =>
|
||||
SSHKeyParseError::InvalidPublicKeyMaterial,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A much smaller set of errors you can get when rendering an SSH key into
|
||||
/// a file or memory block.
|
||||
#[derive(Debug)]
|
||||
pub enum SSHKeyRenderError {
|
||||
IOError(io::Error),
|
||||
StringTooLong,
|
||||
BufferTooLarge,
|
||||
IllegalECDSAKeyType(String)
|
||||
}
|
||||
|
||||
impl From<io::Error> for SSHKeyRenderError {
|
||||
fn from(e: io::Error) -> SSHKeyRenderError {
|
||||
SSHKeyRenderError::IOError(e)
|
||||
}
|
||||
}
|
||||
320
src/ssh/frame.rs
Normal file
320
src/ssh/frame.rs
Normal file
@@ -0,0 +1,320 @@
|
||||
use base64::{decode,encode};
|
||||
use byteorder::{BigEndian,ReadBytesExt,WriteBytesExt};
|
||||
use cryptonum::unsigned::{Decoder,Encoder};
|
||||
use ssh::errors::{SSHKeyParseError,SSHKeyRenderError};
|
||||
#[cfg(test)]
|
||||
use std::io::Cursor;
|
||||
#[cfg(test)]
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
use std::iter::Iterator;
|
||||
|
||||
const OPENER: &'static str = "-----BEGIN OPENSSH PRIVATE KEY-----";
|
||||
const CLOSER: &'static str = "-----END OPENSSH PRIVATE KEY-----";
|
||||
|
||||
/// Given a string defining an ASCII SSH key blob (one that starts with
|
||||
/// "--BEGIN..."), decode the body of the blob and return it as binary
|
||||
/// data.
|
||||
pub fn parse_ssh_private_key_data(s: &str) -> Result<Vec<u8>,SSHKeyParseError>
|
||||
{
|
||||
if s.starts_with(OPENER) {
|
||||
if let Some(endidx) = s.find(CLOSER) {
|
||||
let b64str: String = s[OPENER.len()..endidx].chars().filter(|x| !x.is_whitespace()).collect();
|
||||
let bytes = decode(&b64str)?;
|
||||
Ok(bytes)
|
||||
} else {
|
||||
Err(SSHKeyParseError::NoEndBannerFound)
|
||||
}
|
||||
} else {
|
||||
Err(SSHKeyParseError::NoBeginBannerFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// Once you've figured out the binary data you want to produce for an SSH key
|
||||
/// blob, use this routine to render it into its ASCII encoding.
|
||||
pub fn render_ssh_private_key_data(bytes: &[u8]) -> String
|
||||
{
|
||||
let mut bytestr = encode(bytes);
|
||||
let mut output = String::new();
|
||||
|
||||
output.push_str(OPENER);
|
||||
#[cfg(target_os="windows")]
|
||||
output.push_str("\r");
|
||||
output.push_str("\n");
|
||||
while bytestr.len() > 70 {
|
||||
let rest = bytestr.split_off(70);
|
||||
output.push_str(&bytestr);
|
||||
#[cfg(target_os="windows")]
|
||||
output.push_str("\r");
|
||||
output.push_str("\n");
|
||||
bytestr = rest;
|
||||
}
|
||||
output.push_str(&bytestr);
|
||||
#[cfg(target_os="windows")]
|
||||
output.push_str("\r");
|
||||
output.push_str("\n");
|
||||
output.push_str(CLOSER);
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const OPENSSH_MAGIC_HEADER: &'static str = "openssh-key-v1\0";
|
||||
const OPENSSH_MAGIC_HEADER_LEN: usize = 15;
|
||||
|
||||
/// Parse the magic header in an SSH key file.
|
||||
pub fn parse_openssh_header<R: Read>(input: &mut R) -> Result<(),SSHKeyParseError>
|
||||
{
|
||||
let mut limited_input_header = input.take(OPENSSH_MAGIC_HEADER_LEN as u64);
|
||||
let mut header: [u8; OPENSSH_MAGIC_HEADER_LEN] = [0; OPENSSH_MAGIC_HEADER_LEN];
|
||||
|
||||
assert_eq!(OPENSSH_MAGIC_HEADER.len(), OPENSSH_MAGIC_HEADER_LEN);
|
||||
limited_input_header.read_exact(&mut header)?;
|
||||
|
||||
for (left, right) in OPENSSH_MAGIC_HEADER.bytes().zip(header.iter()) {
|
||||
if left != *right {
|
||||
return Err(SSHKeyParseError::NoOpenSSHMagicHeader)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Render the magic header in an SSH key file.
|
||||
pub fn render_openssh_header<O: Write>(output: &mut O) -> Result<(),SSHKeyRenderError>
|
||||
{
|
||||
Ok(output.write_all(OPENSSH_MAGIC_HEADER.as_bytes())?)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Parse an unsigned u32 from the SSH key stream. (This does the appropriate
|
||||
/// conversion from network order to native order.)
|
||||
pub fn parse_openssh_u32<I: Read>(input: &mut I) -> Result<u32,SSHKeyParseError>
|
||||
{
|
||||
let mut limited_input_header = input.take(4);
|
||||
let res = limited_input_header.read_u32::<BigEndian>()?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Render an unsigned u32 from the SSH key stream. (This does the appropriate
|
||||
/// conversion from network order to native order.)
|
||||
pub fn render_openssh_u32<O: Write>(output: &mut O, val: u32) -> Result<(),SSHKeyRenderError>
|
||||
{
|
||||
Ok(output.write_u32::<BigEndian>(val)?)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Parse a string from the SSH key stream. This does some validation to ensure
|
||||
/// that the data being read is actually in a form that Rust will recognize as
|
||||
/// being a valid string.
|
||||
pub fn parse_openssh_string<I: Read>(input: &mut I) -> Result<String,SSHKeyParseError>
|
||||
{
|
||||
let length = parse_openssh_u32(input)?;
|
||||
let mut limited_input = input.take(length as u64);
|
||||
let mut result = String::new();
|
||||
limited_input.read_to_string(&mut result)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Render a string into the SSH key stream.
|
||||
pub fn render_openssh_string<O: Write>(output: &mut O, v: &str) -> Result<(),SSHKeyRenderError>
|
||||
{
|
||||
let vbytes: Vec<u8> = v.bytes().collect();
|
||||
let len = vbytes.len();
|
||||
|
||||
if len > 0xFFFFFFFF {
|
||||
return Err(SSHKeyRenderError::StringTooLong);
|
||||
}
|
||||
|
||||
render_openssh_u32(output, vbytes.len() as u32)?;
|
||||
output.write_all(&vbytes)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Read a buffer from the SSH key stream.
|
||||
pub fn parse_openssh_buffer<I: Read>(input: &mut I) -> Result<Vec<u8>,SSHKeyParseError>
|
||||
{
|
||||
let length = parse_openssh_u32(input)?;
|
||||
let mut limited_input = input.take(length as u64);
|
||||
let mut res = Vec::with_capacity(length as usize);
|
||||
limited_input.read_to_end(&mut res)?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Render a buffer into the SSH key stream.
|
||||
pub fn render_openssh_buffer<O: Write>(output: &mut O, b: &[u8]) -> Result<(),SSHKeyRenderError>
|
||||
{
|
||||
if b.len() > 0xFFFFFFFF {
|
||||
return Err(SSHKeyRenderError::BufferTooLarge);
|
||||
}
|
||||
|
||||
render_openssh_u32(output, b.len() as u32)?;
|
||||
if b.len() > 0 {
|
||||
output.write_all(b)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Parse a fixed-width number from the SSH key stream and return it.
|
||||
pub fn parse_openssh_number<I,D>(input: &mut I) -> Result<D,SSHKeyParseError>
|
||||
where
|
||||
I: Read,
|
||||
D: Decoder
|
||||
{
|
||||
let mut buffer = parse_openssh_buffer(input)?;
|
||||
while buffer[0] == 0 { buffer.remove(0); }
|
||||
Ok(D::from_bytes(&buffer))
|
||||
}
|
||||
|
||||
/// Render a fixed-width number into the SSH key stream.
|
||||
pub fn render_openssh_number<O,D>(output: &mut O, n: &D) -> Result<(),SSHKeyRenderError>
|
||||
where
|
||||
O: Write,
|
||||
D: Encoder
|
||||
{
|
||||
let bytes = n.to_bytes();
|
||||
render_openssh_buffer(output, &bytes)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
use cryptonum::unsigned::{U192,U1024,U2048,U4096};
|
||||
|
||||
#[cfg(test)]
|
||||
quickcheck! {
|
||||
fn bytes_roundtrip(x: Vec<u8>) -> bool {
|
||||
let rendered = render_ssh_private_key_data(&x);
|
||||
let returned = parse_ssh_private_key_data(&rendered).unwrap();
|
||||
returned == x
|
||||
}
|
||||
|
||||
fn blocks_formatted(x: Vec<u8>) -> bool {
|
||||
let rendered = render_ssh_private_key_data(&x);
|
||||
let mut is_ok = true;
|
||||
|
||||
for line in rendered.lines() {
|
||||
let clean_line: String = line.chars().filter(|x| *x != '\r').collect();
|
||||
is_ok &= clean_line.len() <= 70;
|
||||
}
|
||||
|
||||
is_ok
|
||||
}
|
||||
|
||||
fn u32s_roundtrip_rp(x: u32) -> bool {
|
||||
let mut buffer = vec![];
|
||||
render_openssh_u32(&mut buffer, x).unwrap();
|
||||
let mut cursor = Cursor::new(buffer);
|
||||
let check = parse_openssh_u32(&mut cursor).unwrap();
|
||||
x == check
|
||||
}
|
||||
|
||||
fn u32s_roundtrip_pr(a: u8, b: u8, c: u8, d: u8) -> bool {
|
||||
let block = [a,b,c,d];
|
||||
let mut cursor = Cursor::new(block);
|
||||
let base = parse_openssh_u32(&mut cursor).unwrap();
|
||||
let mut rendered = vec![];
|
||||
render_openssh_u32(&mut rendered, base).unwrap();
|
||||
(block[0] == rendered[0]) &&
|
||||
(block[1] == rendered[1]) &&
|
||||
(block[2] == rendered[2]) &&
|
||||
(block[3] == rendered[3])
|
||||
}
|
||||
|
||||
fn string_roundtrip(s: String) -> bool {
|
||||
let mut buffer = vec![];
|
||||
render_openssh_string(&mut buffer, &s).unwrap();
|
||||
let mut cursor = Cursor::new(buffer);
|
||||
let check = parse_openssh_string(&mut cursor).unwrap();
|
||||
s == check
|
||||
}
|
||||
|
||||
fn buffer(os: Vec<u8>) -> bool {
|
||||
let mut buffer = vec![];
|
||||
render_openssh_buffer(&mut buffer, &os).unwrap();
|
||||
let mut cursor = Cursor::new(buffer);
|
||||
let check = parse_openssh_buffer(&mut cursor).unwrap();
|
||||
os == check
|
||||
}
|
||||
|
||||
fn u192(x: U192) -> bool {
|
||||
let mut buffer = vec![];
|
||||
render_openssh_number(&mut buffer, &x).unwrap();
|
||||
let mut cursor = Cursor::new(buffer);
|
||||
let check: U192 = parse_openssh_number(&mut cursor).unwrap();
|
||||
check == x
|
||||
}
|
||||
|
||||
fn u1024(x: U1024) -> bool {
|
||||
let mut buffer = vec![];
|
||||
render_openssh_number(&mut buffer, &x).unwrap();
|
||||
let mut cursor = Cursor::new(buffer);
|
||||
let check: U1024 = parse_openssh_number(&mut cursor).unwrap();
|
||||
check == x
|
||||
}
|
||||
|
||||
fn u2048(x: U2048) -> bool {
|
||||
let mut buffer = vec![];
|
||||
render_openssh_number(&mut buffer, &x).unwrap();
|
||||
let mut cursor = Cursor::new(buffer);
|
||||
let check: U2048 = parse_openssh_number(&mut cursor).unwrap();
|
||||
check == x
|
||||
}
|
||||
|
||||
fn u4096(x: U4096) -> bool {
|
||||
let mut buffer = vec![];
|
||||
render_openssh_number(&mut buffer, &x).unwrap();
|
||||
let mut cursor = Cursor::new(buffer);
|
||||
let check: U4096 = parse_openssh_number(&mut cursor).unwrap();
|
||||
check == x
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn pregenerated_reencode() {
|
||||
let test_files = ["dsa1024-1", "dsa1024-2", "dsa1024-3",
|
||||
"ecdsa256-1", "ecdsa256-2", "ecdsa256-3",
|
||||
"ecdsa384-1", "ecdsa384-2", "ecdsa384-3",
|
||||
"ecdsa521-1", "ecdsa521-2", "ecdsa521-3",
|
||||
"ed25519-1", "ed25519-2", "ed25519-3",
|
||||
"rsa1024-1", "rsa1024-2", "rsa1024-3",
|
||||
"rsa2048-1", "rsa2048-2", "rsa2048-3",
|
||||
"rsa3072-1", "rsa3072-2", "rsa3072-3",
|
||||
"rsa4096-1", "rsa4096-2", "rsa4096-3",
|
||||
"rsa8192-1", "rsa8192-2", "rsa8192-3" ];
|
||||
|
||||
for file in test_files.iter() {
|
||||
let path = format!("testdata/ssh/{}",file);
|
||||
let mut fd = File::open(path).unwrap();
|
||||
let mut contents = String::new();
|
||||
fd.read_to_string(&mut contents).unwrap();
|
||||
let parsed = parse_ssh_private_key_data(&contents).unwrap();
|
||||
let rendered = render_ssh_private_key_data(&parsed);
|
||||
// we remove white space in this to avoid a couple issues with files
|
||||
// generated in Windows or not, as well as trailing white space that
|
||||
// doesn't really matter.
|
||||
let cleaned_orig: String = contents.chars().filter(|x| !x.is_whitespace()).collect();
|
||||
let cleaned_rend: String = rendered.chars().filter(|x| !x.is_whitespace()).collect();
|
||||
assert_eq!(cleaned_orig, cleaned_rend);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn header_roundtrips() {
|
||||
let mut vec = Vec::new();
|
||||
assert!(render_openssh_header(&mut vec).is_ok());
|
||||
let mut cursor = Cursor::new(vec);
|
||||
assert!(parse_openssh_header(&mut cursor).is_ok());
|
||||
}
|
||||
428
src/ssh/mod.rs
Normal file
428
src/ssh/mod.rs
Normal file
@@ -0,0 +1,428 @@
|
||||
//! Most of the routines you want are exported from this module as functions,
|
||||
//! not as structs, macros, enums, or what have you. In particular, you
|
||||
//! probably want the `decode` or `encode` functions, or one of the functions
|
||||
//! that `load`s data from disk or `write`s it. Here's some example code
|
||||
//! to get you started, using a generated ED25519 key for fun:
|
||||
//!
|
||||
//! ```rust
|
||||
//! use simple_crypto::ed25519::ED25519KeyPair;
|
||||
//! use simple_crypto::ssh::*;
|
||||
//!
|
||||
//! // Generate a new ED25519 key
|
||||
//! let mut rng = rand::rngs::OsRng::new().unwrap();
|
||||
//! let kp = ED25519KeyPair::generate(&mut rng);
|
||||
//!
|
||||
//! // Now that we have it, we can encode it as a handy ASCII string in memory,
|
||||
//! // using a totally fake email address for fun:
|
||||
//! let ascii_rep = encode_ssh(&kp, "fake@email.addr").expect("Encode failure!");
|
||||
//!
|
||||
//! // As usual, we should be able to decode anything we encode, and the
|
||||
//! // keys should match:
|
||||
//! let (kp2, addr2) = decode_ssh(&ascii_rep).expect("Decode failure!");
|
||||
//! assert_eq!(kp, kp2);
|
||||
//! assert_eq!(&addr2, "fake@email.addr");
|
||||
//!
|
||||
//! // If you want to write this to a file, you can just do so directly:
|
||||
//! write_ssh_keyfile("test.ed25519", &kp, "fake@email.addr").expect("write error");
|
||||
//! // And then load it back:
|
||||
//! let (kp3, addr3) = load_ssh_keyfile("test.ed25519").expect("load error");
|
||||
//! // And, of course, it should be the same.
|
||||
//! assert_eq!(kp, kp3);
|
||||
//! assert_eq!(addr2, addr3);
|
||||
//! ```
|
||||
mod dsa;
|
||||
mod ecdsa;
|
||||
mod ed25519;
|
||||
mod errors;
|
||||
pub mod frame;
|
||||
mod rsa;
|
||||
|
||||
pub use self::errors::{SSHKeyParseError,SSHKeyRenderError};
|
||||
|
||||
use base64::decode;
|
||||
use self::frame::*;
|
||||
use std::fs::File;
|
||||
use std::io::{Cursor,Read,Write};
|
||||
use std::path::Path;
|
||||
use super::KeyPair;
|
||||
|
||||
/// A trait defining keys that can be parsed / rendered by this library. Note
|
||||
/// that you probably don't want to use these routines directly; they're mostly
|
||||
/// used by the internal functions. Perhaps the only reason to use them is to
|
||||
/// implement them, because you've got another kind of key you want to parse that
|
||||
/// isn't already part of the library. (In that case, though ... maybe send a
|
||||
/// patch?)
|
||||
pub trait SSHKey: Sized + KeyPair {
|
||||
/// Return true if the given string is a valid key type identifier for this
|
||||
/// key type. (i.e., "ssh-ed25519" is the identifier for ED25519, and "dss"
|
||||
/// and "ssh-dss" are both valid identifiers for DSA keys.)
|
||||
fn valid_keytype(s: &str) -> bool;
|
||||
|
||||
/// Parse the public blob info within an SSH blob. I strongly recommend
|
||||
/// using the functions in `ssh::frame` for this.
|
||||
fn parse_ssh_public_info<I: Read>(inp: &mut I) -> Result<Self::Public,SSHKeyParseError>;
|
||||
/// Parse the private blob info within an SSH blob. I strongly recommend
|
||||
/// using the functions in `ssh::frame` for this.
|
||||
fn parse_ssh_private_info<I: Read>(inp: &mut I) -> Result<(Self::Private,String),SSHKeyParseError>;
|
||||
|
||||
/// Render the public blob info within an SSH blob. I strongly recommend
|
||||
/// using the functions in `ssh::frame` for this.
|
||||
fn render_ssh_public_info<O: Write>(&self, out: &mut O) -> Result<(),SSHKeyRenderError>;
|
||||
/// Render the private blob info within an SSH blob. I strongly recommend
|
||||
/// using the functions in `ssh::frame` for this.
|
||||
fn render_ssh_private_info<O: Write>(&self, out: &mut O) -> Result<(),SSHKeyRenderError>;
|
||||
}
|
||||
|
||||
/// Decode a string containing a private key into the appropriate key type and
|
||||
/// the comment associated with it, usually an email address or similar.
|
||||
pub fn decode_ssh<KP: SSHKey>(x: &str) -> Result<(KP, String),SSHKeyParseError>
|
||||
{
|
||||
let bytes = parse_ssh_private_key_data(x)?;
|
||||
let data_size = bytes.len() as u64;
|
||||
let mut byte_cursor = Cursor::new(bytes);
|
||||
|
||||
parse_openssh_header(&mut byte_cursor)?;
|
||||
let ciphername = parse_openssh_string(&mut byte_cursor)?;
|
||||
if ciphername != "none" {
|
||||
return Err(SSHKeyParseError::UnknownKeyCipher(ciphername));
|
||||
}
|
||||
let kdfname = parse_openssh_string(&mut byte_cursor)?;
|
||||
if kdfname != "none" {
|
||||
return Err(SSHKeyParseError::UnknownKeyCipher(kdfname));
|
||||
}
|
||||
let kdfoptions = parse_openssh_buffer(&mut byte_cursor)?;
|
||||
if kdfoptions.len() > 0 {
|
||||
return Err(SSHKeyParseError::UnexpectedKDFOptions);
|
||||
}
|
||||
let numkeys = parse_openssh_u32(&mut byte_cursor)?;
|
||||
if numkeys != 1 {
|
||||
return Err(SSHKeyParseError::InvalidNumberOfKeys(numkeys));
|
||||
}
|
||||
let pubkey0 = parse_openssh_buffer(&mut byte_cursor)?;
|
||||
let privkeys = parse_openssh_buffer(&mut byte_cursor)?;
|
||||
if byte_cursor.position() < data_size {
|
||||
return Err(SSHKeyParseError::UnknownTrailingData);
|
||||
}
|
||||
|
||||
let mut pubcursor = Cursor::new(pubkey0);
|
||||
let public = KP::parse_ssh_public_info(&mut pubcursor)?;
|
||||
let mut privcursor = Cursor::new(privkeys);
|
||||
let (private, comment) = KP::parse_ssh_private_info(&mut privcursor)?;
|
||||
|
||||
Ok((KP::new(public, private), comment))
|
||||
}
|
||||
|
||||
/// Decode a string containing a public key into an appropriate key type and
|
||||
/// the comment associated with it, usually an email address or similar.
|
||||
pub fn decode_ssh_pubkey<KP: SSHKey>(s: &str) -> Result<(KP::Public, String),SSHKeyParseError>
|
||||
{
|
||||
let mut splitter = s.split_whitespace();
|
||||
|
||||
match (splitter.next(), splitter.next(), splitter.next(), splitter.next()) {
|
||||
(Some(keytype), Some(keymaterial), Some(comment), None) => {
|
||||
if !KP::valid_keytype(keytype) {
|
||||
return Err(SSHKeyParseError::InvalidPublicKeyType);
|
||||
}
|
||||
|
||||
let bytes = decode(keymaterial)?;
|
||||
let mut byte_cursor = Cursor::new(bytes);
|
||||
let key = KP::parse_ssh_public_info(&mut byte_cursor)?;
|
||||
|
||||
Ok((key, comment.to_string()))
|
||||
}
|
||||
_ =>
|
||||
Err(SSHKeyParseError::BrokenPublicKeyLine)
|
||||
}
|
||||
}
|
||||
|
||||
/// Load an SSH private key file, returning the appropriate key type and the
|
||||
/// comment associated with it.
|
||||
pub fn load_ssh_keyfile<KP,P>(path: P) -> Result<(KP, String),SSHKeyParseError>
|
||||
where
|
||||
KP: SSHKey,
|
||||
P: AsRef<Path>
|
||||
{
|
||||
let mut file = File::open(path)?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
decode_ssh(&contents)
|
||||
}
|
||||
|
||||
/// Load all the public keys from a file into memory.
|
||||
pub fn load_ssh_pubkeys<KP,P>(path: P) -> Result<Vec<(KP::Public, String)>,SSHKeyParseError>
|
||||
where
|
||||
KP: SSHKey,
|
||||
P: AsRef<Path>
|
||||
{
|
||||
let mut file = File::open(path)?;
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents)?;
|
||||
let mut result = Vec::new();
|
||||
|
||||
for line in contents.lines() {
|
||||
result.push( decode_ssh_pubkey::<KP>(line)? );
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Encode a supported key into its ASCII SSH format, with the given comment.
|
||||
pub fn encode_ssh<KP: SSHKey>(x: &KP, comment: &str) -> Result<String,SSHKeyRenderError>
|
||||
{
|
||||
let mut pubkeybin = Vec::with_capacity(8192);
|
||||
let mut privkeybin = Vec::with_capacity(8192);
|
||||
let mut binary = Vec::with_capacity(16384);
|
||||
|
||||
// create the public key bits
|
||||
x.render_ssh_public_info(&mut pubkeybin)?;
|
||||
// create the private key bits
|
||||
render_openssh_u32(&mut privkeybin, 0xDEADBEEF)?; // FIXME: Any reason for this to be random?
|
||||
render_openssh_u32(&mut privkeybin, 0xDEADBEEF)?; // ditto
|
||||
x.render_ssh_private_info(&mut privkeybin)?;
|
||||
render_openssh_string(&mut privkeybin, comment)?;
|
||||
// add some padding (not quite sure why)
|
||||
let mut i = comment.len();
|
||||
while (i % 16) != 0 {
|
||||
privkeybin.write(&[(i - comment.len() + 1) as u8])?;
|
||||
i += 1;
|
||||
}
|
||||
// render a bunch of the framing stuff
|
||||
render_openssh_header(&mut binary)?;
|
||||
render_openssh_string(&mut binary, "none")?; // ciphername
|
||||
render_openssh_string(&mut binary, "none")?; // kdfname
|
||||
render_openssh_buffer(&mut binary, &[])?; // kdfoptions
|
||||
render_openssh_u32(&mut binary, 1)?; // numkeys
|
||||
render_openssh_buffer(&mut binary, &pubkeybin)?;
|
||||
render_openssh_buffer(&mut binary, &privkeybin)?;
|
||||
Ok(render_ssh_private_key_data(&binary))
|
||||
}
|
||||
|
||||
/// Encode a supported key into the given file, with the given comment.
|
||||
pub fn write_ssh_keyfile<KP,P>(path: P, x: &KP, comment: &str) -> Result<(),SSHKeyRenderError>
|
||||
where
|
||||
KP: SSHKey,
|
||||
P: AsRef<Path>
|
||||
{
|
||||
let mut file = File::create(path)?;
|
||||
let contents = encode_ssh(x, comment)?;
|
||||
let bytes = contents.into_bytes();
|
||||
file.write_all(&bytes)?;
|
||||
file.sync_all()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use dsa::{DSAKeyPair,DSAPublicKey,L1024N160};
|
||||
#[cfg(test)]
|
||||
use ecdsa::ECDSAPair;
|
||||
#[cfg(test)]
|
||||
use ed25519::ED25519KeyPair;
|
||||
#[cfg(test)]
|
||||
use rsa::{RSAPair,RSAPublic,SIGNING_HASH_SHA256};
|
||||
#[cfg(test)]
|
||||
use sha::SHA256;
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn dsa_examples() {
|
||||
let test_files = ["dsa1024-1", "dsa1024-2", "dsa1024-3"];
|
||||
|
||||
for file in test_files.iter() {
|
||||
let path = format!("testdata/ssh/{}",file);
|
||||
let mkeypair = load_ssh_keyfile(path);
|
||||
match mkeypair {
|
||||
Err(e) => assert!(false, format!("reading error: {:?}", e)),
|
||||
Ok((keypair, comment)) => {
|
||||
let buffer = [0,1,2,3,4,6,2];
|
||||
let _ : DSAKeyPair<L1024N160> = keypair;
|
||||
let sig = keypair.private.sign::<SHA256>(&buffer);
|
||||
assert!(keypair.public.verify::<SHA256>(&buffer, &sig));
|
||||
let buffer2 = [0,1,2,3,4,6,5];
|
||||
assert!(!keypair.public.verify::<SHA256>(&buffer2, &sig));
|
||||
match encode_ssh(&keypair, &comment) {
|
||||
Err(e2) => assert!(false, format!("render error: {:?}", e2)),
|
||||
Ok(encodedstr) => {
|
||||
match decode_ssh(&encodedstr) {
|
||||
Err(e3) => assert!(false, format!("reparse error: {:?}", e3)),
|
||||
Ok((keypair2,comment2)) => {
|
||||
let _ : DSAKeyPair<L1024N160> = keypair2;
|
||||
assert_eq!(keypair.public.params.p,keypair2.public.params.p,"failed to reparse key pair (p)");
|
||||
assert_eq!(keypair.public.params.q,keypair2.public.params.q,"failed to reparse key pair (q)");
|
||||
assert_eq!(keypair.public.params.g,keypair2.public.params.g,"failed to reparse key pair (g)");
|
||||
assert_eq!(keypair.private.params.p,keypair2.private.params.p,"failed to reparse key pair (p)");
|
||||
assert_eq!(keypair.private.params.q,keypair2.private.params.q,"failed to reparse key pair (q)");
|
||||
assert_eq!(keypair.private.params.g,keypair2.private.params.g,"failed to reparse key pair (g)");
|
||||
assert_eq!(keypair.public.y,keypair2.public.y,"failed to reparse key pair (y)");
|
||||
assert_eq!(keypair.private.x,keypair2.private.x,"failed to reparse key pair (x)");
|
||||
assert_eq!(comment,comment2,"failed to reparse comment");
|
||||
let ppath = format!("testdata/ssh/{}.pub",file);
|
||||
match load_ssh_pubkeys::<DSAKeyPair<L1024N160>,String>(ppath) {
|
||||
Err(e4) => assert!(false, format!("pubkey error: {:?}", e4)),
|
||||
Ok(pubkeys) => {
|
||||
let _ : Vec<(DSAPublicKey<L1024N160>,String)> = pubkeys;
|
||||
for (pubkey, comment3) in pubkeys {
|
||||
assert_eq!(pubkey.params.p, keypair.public.params.p, "public key check (p)");
|
||||
assert_eq!(pubkey.params.q, keypair.public.params.q, "public key check (q)");
|
||||
assert_eq!(pubkey.params.g, keypair.public.params.g, "public key check (g)");
|
||||
assert_eq!(pubkey.y, keypair.public.y, "public key check (y)");
|
||||
assert_eq!(comment, comment3, "public key check comment")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn rsa_examples() {
|
||||
let test_files = ["rsa1024-1", "rsa1024-2", "rsa1024-3",
|
||||
"rsa2048-1", "rsa2048-2", "rsa2048-3",
|
||||
"rsa3072-1", "rsa3072-2", "rsa3072-3",
|
||||
"rsa4096-1", "rsa4096-2", "rsa4096-3",
|
||||
"rsa8192-1", "rsa8192-2", "rsa8192-3"];
|
||||
|
||||
for file in test_files.iter() {
|
||||
let path = format!("testdata/ssh/{}",file);
|
||||
let mkeypair = load_ssh_keyfile::<RSAPair,String>(path);
|
||||
match mkeypair {
|
||||
Err(e) => assert!(false, format!("reading error: {:?}", e)),
|
||||
Ok((keypair, comment)) => {
|
||||
let buffer = [0,1,2,3,4,6,2];
|
||||
let sig = keypair.sign(&SIGNING_HASH_SHA256, &buffer);
|
||||
assert!(keypair.verify(&SIGNING_HASH_SHA256, &buffer, &sig));
|
||||
match encode_ssh(&keypair, &comment) {
|
||||
Err(e2) => assert!(false, format!("render error: {:?}", e2)),
|
||||
Ok(encodedstr) => {
|
||||
match decode_ssh(&encodedstr) {
|
||||
Err(e3) => assert!(false, format!("reparse error: {:?}", e3)),
|
||||
Ok((keypair2,comment2)) => {
|
||||
assert_eq!(keypair,keypair2,"failed to reparse key pair");
|
||||
assert_eq!(comment,comment2,"failed to reparse comment");
|
||||
let ppath = format!("testdata/ssh/{}.pub",file);
|
||||
match load_ssh_pubkeys::<RSAPair,String>(ppath) {
|
||||
Err(e4) => assert!(false, format!("pubkey error: {:?}", e4)),
|
||||
Ok(pubkeys) => {
|
||||
let _ : Vec<(RSAPublic,String)> = pubkeys;
|
||||
for (pubkey, comment3) in pubkeys {
|
||||
assert_eq!(pubkey, keypair.public(), "public key check");
|
||||
assert_eq!(comment, comment3, "public key check comment");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn ecdsa_examples() {
|
||||
let test_files = ["ecdsa256-1", "ecdsa256-2", "ecdsa256-3",
|
||||
"ecdsa384-1", "ecdsa384-2", "ecdsa384-3",
|
||||
"ecdsa521-1", "ecdsa521-2", "ecdsa521-3"];
|
||||
|
||||
for file in test_files.iter() {
|
||||
let path = format!("testdata/ssh/{}",file);
|
||||
match load_ssh_keyfile::<ECDSAPair,String>(path) {
|
||||
Err(e) =>
|
||||
assert!(false, "SSH ECDSA parse error: {:?}", e),
|
||||
Ok((keypair,comment)) => {
|
||||
// first see if this roundtrips
|
||||
let buffer = vec![0,1,2,4,5,6,9];
|
||||
match keypair {
|
||||
ECDSAPair::P192(_,_) =>
|
||||
assert!(false, "Somehow got a P192 in read test"),
|
||||
ECDSAPair::P224(_,_) =>
|
||||
assert!(false, "Somehow got a P224 in read test"),
|
||||
ECDSAPair::P256(ref pu, ref pr) => {
|
||||
let sig = pr.sign::<SHA256>(&buffer);
|
||||
assert!(pu.verify::<SHA256>(&buffer, &sig));
|
||||
}
|
||||
ECDSAPair::P384(ref pu, ref pr) => {
|
||||
let sig = pr.sign::<SHA256>(&buffer);
|
||||
assert!(pu.verify::<SHA256>(&buffer, &sig));
|
||||
}
|
||||
ECDSAPair::P521(ref pu, ref pr) => {
|
||||
let sig = pr.sign::<SHA256>(&buffer);
|
||||
assert!(pu.verify::<SHA256>(&buffer, &sig));
|
||||
}
|
||||
}
|
||||
// encode this, parse it again
|
||||
match encode_ssh(&keypair, &comment) {
|
||||
Err(e) =>
|
||||
assert!(false, "SSH ECDSA encoding error: {:?}", e),
|
||||
Ok(coded) => {
|
||||
match (decode_ssh(&coded), keypair) {
|
||||
(Err(e), _) =>
|
||||
assert!(false, "SSSH ECDSA redecoding error: {:?}", e),
|
||||
(Ok((ECDSAPair::P256(pu2, pr2), comment2)), ECDSAPair::P256(pu,pr)) => {
|
||||
assert_eq!(pu, pu2, "public key mismatch");
|
||||
assert_eq!(pr, pr2, "public key mismatch");
|
||||
assert_eq!(comment, comment2, "comment mismatch");
|
||||
}
|
||||
(Ok((ECDSAPair::P384(pu2, pr2), comment2)), ECDSAPair::P384(pu,pr)) => {
|
||||
assert_eq!(pu, pu2, "public key mismatch");
|
||||
assert_eq!(pr, pr2, "public key mismatch");
|
||||
assert_eq!(comment, comment2, "comment mismatch");
|
||||
}
|
||||
(Ok((ECDSAPair::P521(pu2, pr2), comment2)), ECDSAPair::P521(pu,pr)) => {
|
||||
assert_eq!(pu, pu2, "public key mismatch");
|
||||
assert_eq!(pr, pr2, "public key mismatch");
|
||||
assert_eq!(comment, comment2, "comment mismatch");
|
||||
}
|
||||
_ =>
|
||||
assert!(false, "Failed to accurately re-parse key")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn ed25519_examples() {
|
||||
let test_files = ["ed25519-1", "ed25519-2", "ed25519-3"];
|
||||
|
||||
for file in test_files.iter() {
|
||||
let path = format!("testdata/ssh/{}",file);
|
||||
match load_ssh_keyfile::<ED25519KeyPair,String>(path) {
|
||||
Err(e) =>
|
||||
assert!(false, "SSH ED25519 parse error: {:?}", e),
|
||||
Ok((keypair,comment)) => {
|
||||
// first see if this roundtrips
|
||||
let buffer = vec![0,1,2,4,5,6,9];
|
||||
let sig = keypair.private.sign(&buffer);
|
||||
assert!(keypair.public.verify(&buffer, &sig));
|
||||
match encode_ssh(&keypair, &comment) {
|
||||
Err(e) =>
|
||||
assert!(false, "SSH ED25519 encoding error: {:?}", e),
|
||||
Ok(coded) => {
|
||||
match decode_ssh(&coded) {
|
||||
Err(e) =>
|
||||
assert!(false, "SSSH ECDSA redecoding error: {:?}", e),
|
||||
Ok((keypair2, comment2)) => {
|
||||
let _ : ED25519KeyPair = keypair2;
|
||||
assert_eq!(keypair.public, keypair2.public, "public key mismatch");
|
||||
assert_eq!(keypair.private, keypair2.private, "public key mismatch");
|
||||
assert_eq!(comment, comment2, "comment mismatch");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
201
src/ssh/rsa.rs
Normal file
201
src/ssh/rsa.rs
Normal file
@@ -0,0 +1,201 @@
|
||||
use cryptonum::unsigned::*;
|
||||
use rsa::{RSAPair,RSAPublic,RSAPublicKey,RSAPrivate,RSAPrivateKey};
|
||||
use std::io::{Read,Write};
|
||||
use ssh::errors::{SSHKeyParseError,SSHKeyRenderError};
|
||||
use ssh::frame::*;
|
||||
use ssh::SSHKey;
|
||||
|
||||
impl SSHKey for RSAPair {
|
||||
fn valid_keytype(s: &str) -> bool {
|
||||
(s == "ssh-rsa") || (s == "rsa")
|
||||
}
|
||||
|
||||
fn parse_ssh_public_info<I: Read>(inp: &mut I) -> Result<Self::Public,SSHKeyParseError>
|
||||
{
|
||||
let pubkey_type = parse_openssh_string(inp)?;
|
||||
if !Self::valid_keytype(&pubkey_type) {
|
||||
return Err(SSHKeyParseError::UnknownKeyType(pubkey_type));
|
||||
}
|
||||
// this peaks a little under the cover a bit (it'd be nice to pretend
|
||||
// that we didn't know the number format was the same as the buffer
|
||||
// one), but we need to infer what kind of key this is, and this appears
|
||||
// to be the easiest / fastest way.
|
||||
let mut ebuf = parse_openssh_buffer(inp)?;
|
||||
let mut nbuf = parse_openssh_buffer(inp)?;
|
||||
|
||||
while ebuf[0] == 0 { ebuf.remove(0); }
|
||||
while nbuf[0] == 0 { nbuf.remove(0); }
|
||||
|
||||
if nbuf.len() > (8192 / 8) {
|
||||
let e = U15360::from_bytes(&ebuf);
|
||||
let n = U15360::from_bytes(&nbuf);
|
||||
Ok(RSAPublic::Key15360(RSAPublicKey::<U15360>::new(n, e)))
|
||||
} else if nbuf.len() > (4096 / 8) {
|
||||
let e = U8192::from_bytes(&ebuf);
|
||||
let n = U8192::from_bytes(&nbuf);
|
||||
Ok(RSAPublic::Key8192(RSAPublicKey::<U8192>::new(n, e)))
|
||||
} else if nbuf.len() > (3072 / 8) {
|
||||
let e = U4096::from_bytes(&ebuf);
|
||||
let n = U4096::from_bytes(&nbuf);
|
||||
Ok(RSAPublic::Key4096(RSAPublicKey::<U4096>::new(n, e)))
|
||||
} else if nbuf.len() > (2048 / 8) {
|
||||
let e = U3072::from_bytes(&ebuf);
|
||||
let n = U3072::from_bytes(&nbuf);
|
||||
Ok(RSAPublic::Key3072(RSAPublicKey::<U3072>::new(n, e)))
|
||||
} else if nbuf.len() > (1024 / 8) {
|
||||
let e = U2048::from_bytes(&ebuf);
|
||||
let n = U2048::from_bytes(&nbuf);
|
||||
Ok(RSAPublic::Key2048(RSAPublicKey::<U2048>::new(n, e)))
|
||||
} else if nbuf.len() > (512 / 8) {
|
||||
let e = U1024::from_bytes(&ebuf);
|
||||
let n = U1024::from_bytes(&nbuf);
|
||||
Ok(RSAPublic::Key1024(RSAPublicKey::<U1024>::new(n, e)))
|
||||
} else {
|
||||
let e = U512::from_bytes(&ebuf);
|
||||
let n = U512::from_bytes(&nbuf);
|
||||
Ok(RSAPublic::Key512(RSAPublicKey::<U512>::new(n, e)))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_ssh_private_info<I: Read>(inp: &mut I) -> Result<(Self::Private,String),SSHKeyParseError>
|
||||
{
|
||||
let check1 = parse_openssh_u32(inp)?;
|
||||
let check2 = parse_openssh_u32(inp)?;
|
||||
if check1 != check2 {
|
||||
return Err(SSHKeyParseError::PrivateKeyCorruption);
|
||||
}
|
||||
let privkey_type = parse_openssh_string(inp)?;
|
||||
if !Self::valid_keytype(&privkey_type) {
|
||||
return Err(SSHKeyParseError::InconsistentKeyTypes("ssh-rsa".to_string(), privkey_type));
|
||||
}
|
||||
|
||||
// See the comment in the public key section.
|
||||
let mut nbuf = parse_openssh_buffer(inp)?;
|
||||
let _ebuf = parse_openssh_buffer(inp)?;
|
||||
let mut dbuf = parse_openssh_buffer(inp)?;
|
||||
let _iqmp = parse_openssh_buffer(inp)?;
|
||||
let _pbuf = parse_openssh_buffer(inp)?;
|
||||
let _qbuf = parse_openssh_buffer(inp)?;
|
||||
let comment = parse_openssh_string(inp)?;
|
||||
for (idx,byte) in inp.bytes().enumerate() {
|
||||
if ((idx+1) as u8) != byte? {
|
||||
return Err(SSHKeyParseError::InvalidPadding);
|
||||
}
|
||||
}
|
||||
|
||||
while dbuf[0] == 0 { dbuf.remove(0); }
|
||||
while nbuf[0] == 0 { nbuf.remove(0); }
|
||||
|
||||
if nbuf.len() > (8192 / 8) {
|
||||
let d = U15360::from_bytes(&dbuf);
|
||||
let n = U15360::from_bytes(&nbuf);
|
||||
Ok((RSAPrivate::Key15360(RSAPrivateKey::<U15360>::new(n, d)), comment))
|
||||
} else if nbuf.len() > (4096 / 8) {
|
||||
let d = U8192::from_bytes(&dbuf);
|
||||
let n = U8192::from_bytes(&nbuf);
|
||||
Ok((RSAPrivate::Key8192(RSAPrivateKey::<U8192>::new(n, d)), comment))
|
||||
} else if nbuf.len() > (3072 / 8) {
|
||||
let d = U4096::from_bytes(&dbuf);
|
||||
let n = U4096::from_bytes(&nbuf);
|
||||
Ok((RSAPrivate::Key4096(RSAPrivateKey::<U4096>::new(n, d)), comment))
|
||||
} else if nbuf.len() > (2048 / 8) {
|
||||
let d = U3072::from_bytes(&dbuf);
|
||||
let n = U3072::from_bytes(&nbuf);
|
||||
Ok((RSAPrivate::Key3072(RSAPrivateKey::<U3072>::new(n, d)), comment))
|
||||
} else if nbuf.len() > (1024 / 8) {
|
||||
let d = U2048::from_bytes(&dbuf);
|
||||
let n = U2048::from_bytes(&nbuf);
|
||||
Ok((RSAPrivate::Key2048(RSAPrivateKey::<U2048>::new(n, d)), comment))
|
||||
} else if nbuf.len() > (512 / 8) {
|
||||
let d = U1024::from_bytes(&dbuf);
|
||||
let n = U1024::from_bytes(&nbuf);
|
||||
Ok((RSAPrivate::Key1024(RSAPrivateKey::<U1024>::new(n, d)), comment))
|
||||
} else {
|
||||
let d = U512::from_bytes(&dbuf);
|
||||
let n = U512::from_bytes(&nbuf);
|
||||
Ok((RSAPrivate::Key512(RSAPrivateKey::<U512>::new(n, d)), comment))
|
||||
}
|
||||
}
|
||||
|
||||
fn render_ssh_public_info<O: Write>(&self, out: &mut O) -> Result<(),SSHKeyRenderError>
|
||||
{
|
||||
render_openssh_string(out, "ssh-rsa")?;
|
||||
match self {
|
||||
RSAPair::R512(pbl,_) => {
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
}
|
||||
RSAPair::R1024(pbl,_) => {
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
}
|
||||
RSAPair::R2048(pbl,_) => {
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
}
|
||||
RSAPair::R3072(pbl,_) => {
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
}
|
||||
RSAPair::R4096(pbl,_) => {
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
}
|
||||
RSAPair::R8192(pbl,_) => {
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
}
|
||||
RSAPair::R15360(pbl,_) => {
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_ssh_private_info<O: Write>(&self, out: &mut O) -> Result<(),SSHKeyRenderError>
|
||||
{
|
||||
render_openssh_string(out, "ssh-rsa")?;
|
||||
match self {
|
||||
RSAPair::R512(pbl,prv) => {
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &prv.d)?;
|
||||
}
|
||||
RSAPair::R1024(pbl,prv) => {
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &prv.d)?;
|
||||
}
|
||||
RSAPair::R2048(pbl,prv) => {
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &prv.d)?;
|
||||
}
|
||||
RSAPair::R3072(pbl,prv) => {
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &prv.d)?;
|
||||
}
|
||||
RSAPair::R4096(pbl,prv) => {
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &prv.d)?;
|
||||
}
|
||||
RSAPair::R8192(pbl,prv) => {
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &prv.d)?;
|
||||
}
|
||||
RSAPair::R15360(pbl,prv) => {
|
||||
render_openssh_number(out, &pbl.n)?;
|
||||
render_openssh_number(out, &pbl.e)?;
|
||||
render_openssh_number(out, &prv.d)?;
|
||||
}
|
||||
}
|
||||
/* iqmp */ render_openssh_buffer(out, &vec![])?;
|
||||
/* p */ render_openssh_buffer(out, &vec![])?;
|
||||
/* q */ render_openssh_buffer(out, &vec![])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
67
src/testing.rs
Normal file
67
src/testing.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::str::Lines;
|
||||
|
||||
pub fn build_test_path(dir: &str, typename: &str) -> String
|
||||
{
|
||||
format!("testdata/{}/{}.test", dir, typename)
|
||||
}
|
||||
|
||||
fn next_value_set(line: &str) -> (String, bool, Vec<u8>)
|
||||
{
|
||||
assert!(line.is_ascii());
|
||||
let mut items = line.split(": ");
|
||||
let key = items.next().unwrap();
|
||||
let valbits = items.next().unwrap();
|
||||
let neg = valbits.contains('-');
|
||||
let valbitsnoneg = valbits.trim_start_matches("-");
|
||||
|
||||
let mut nibble_iter = valbitsnoneg.chars().rev();
|
||||
let mut val = Vec::new();
|
||||
|
||||
while let Some(c1) = nibble_iter.next() {
|
||||
match nibble_iter.next() {
|
||||
None => {
|
||||
val.push( c1.to_digit(16).expect(&format!("Unexpected character: |{}|", c1)) as u8 );
|
||||
}
|
||||
Some(c2) => {
|
||||
let b1 = c1.to_digit(16).expect(&format!("Unexpected character: |{}|", c1)) as u8;
|
||||
let b2 = c2.to_digit(16).expect(&format!("Unexpected character: |{}|", c2)) as u8;
|
||||
val.push( (b2 << 4) | b1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
val.reverse();
|
||||
|
||||
(key.to_string(), neg, val)
|
||||
}
|
||||
|
||||
fn next_test_case(contents: &mut Lines, lines: usize) ->
|
||||
Option<HashMap<String,(bool,Vec<u8>)>>
|
||||
{
|
||||
let mut res = HashMap::new();
|
||||
let mut count = 0;
|
||||
|
||||
while count < lines {
|
||||
let line = contents.next()?;
|
||||
let (key, neg, val) = next_value_set(line);
|
||||
res.insert(key, (neg,val));
|
||||
count += 1;
|
||||
}
|
||||
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn run_test<F>(fname: String, i: usize, f: F)
|
||||
where F: Fn(HashMap<String,(bool,Vec<u8>)>)
|
||||
{
|
||||
let mut file = File::open(fname).unwrap();
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents).unwrap();
|
||||
let mut iter = contents.lines();
|
||||
|
||||
while let Some(scase) = next_test_case(&mut iter, i) {
|
||||
f(scase);
|
||||
}
|
||||
}
|
||||
57
src/utils.rs
Normal file
57
src/utils.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use cryptonum::unsigned::*;
|
||||
use num::{BigInt,BigUint};
|
||||
use num::bigint::Sign;
|
||||
|
||||
pub trait TranslateNums<N>: Sized {
|
||||
fn from_num(x: &N) -> Option<Self>;
|
||||
fn to_num(&self) -> N;
|
||||
}
|
||||
|
||||
macro_rules! from_biguint {
|
||||
($uname: ident, $size: expr) => {
|
||||
impl TranslateNums<BigUint> for $uname {
|
||||
fn from_num(x: &BigUint) -> Option<$uname> {
|
||||
let mut base_vec = x.to_bytes_be();
|
||||
let target_bytes = $size / 8;
|
||||
|
||||
if target_bytes < base_vec.len() {
|
||||
return None;
|
||||
|
||||
}
|
||||
while target_bytes > base_vec.len() {
|
||||
base_vec.insert(0,0);
|
||||
}
|
||||
|
||||
Some($uname::from_bytes(&base_vec))
|
||||
}
|
||||
|
||||
fn to_num(&self) -> BigUint {
|
||||
let bytes = self.to_bytes();
|
||||
BigUint::from_bytes_be(&bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl TranslateNums<BigInt> for $uname {
|
||||
fn from_num(x: &BigInt) -> Option<$uname> {
|
||||
let ux = x.to_biguint()?;
|
||||
$uname::from_num(&ux)
|
||||
}
|
||||
|
||||
fn to_num(&self) -> BigInt {
|
||||
BigInt::from_biguint(Sign::Plus, self.to_num())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
from_biguint!(U192, 192);
|
||||
from_biguint!(U256, 256);
|
||||
from_biguint!(U384, 384);
|
||||
from_biguint!(U512, 512);
|
||||
from_biguint!(U576, 576);
|
||||
from_biguint!(U1024, 1024);
|
||||
from_biguint!(U2048, 2048);
|
||||
from_biguint!(U3072, 3072);
|
||||
from_biguint!(U4096, 4096);
|
||||
from_biguint!(U8192, 8192);
|
||||
from_biguint!(U15360, 15360);
|
||||
393
src/x509/algident.rs
Normal file
393
src/x509/algident.rs
Normal file
@@ -0,0 +1,393 @@
|
||||
use num::BigUint;
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1};
|
||||
use x509::error::X509ParseError;
|
||||
|
||||
/// A supported x509 hash algorithm
|
||||
#[derive(Clone,Copy,Debug,PartialEq)]
|
||||
pub enum HashAlgorithm { SHA1, SHA224, SHA256, SHA384, SHA512 }
|
||||
|
||||
/// A supported x509 asymmetric crypto algorithm
|
||||
#[derive(Clone,Copy,Debug,PartialEq)]
|
||||
pub enum PublicKeyInfo { RSA, DSA, ECDSA }
|
||||
|
||||
/// The algorithm used, either in a certificate or as part of the signing
|
||||
/// process. We only actually support a subset of the possible values,
|
||||
/// here, although we try to catch them all.
|
||||
///
|
||||
/// Specifically, this library supports:
|
||||
///
|
||||
/// | | *RSA* | *DSA* | *ECDSA* |
|
||||
/// |----------|-------|-------|---------|
|
||||
/// | *SHA1* | X | X | X |
|
||||
/// | *SHA224* | X | X | X |
|
||||
/// | *SHA256* | X | X | X |
|
||||
/// | *SHA384* | X | | X |
|
||||
/// | *SHA512* | X | | X |
|
||||
///
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct AlgorithmIdentifier {
|
||||
pub hash: HashAlgorithm,
|
||||
pub algo: PublicKeyInfo
|
||||
}
|
||||
|
||||
impl FromASN1 for AlgorithmIdentifier {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(AlgorithmIdentifier,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match v.split_first() {
|
||||
None =>
|
||||
Err(X509ParseError::NotEnoughData),
|
||||
Some((x, rest)) => {
|
||||
let v = decode_algorithm_ident(&x)?;
|
||||
Ok((v, rest))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_algorithm_ident(x: &ASN1Block)
|
||||
-> Result<AlgorithmIdentifier,X509ParseError>
|
||||
{
|
||||
// AlgorithmIdentifier ::= SEQUENCE {
|
||||
// algorithm OBJECT IDENTIFIER,
|
||||
// parameters ANY DEFINED BY algorithm OPTIONAL }
|
||||
match x {
|
||||
&ASN1Block::Sequence(_, _, ref v) if v.len() >= 1 => {
|
||||
match v[0] {
|
||||
ASN1Block::ObjectIdentifier(_, _, ref oid) => {
|
||||
if oid == oid!(1,2,840,113549,1,1,5) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA1,
|
||||
algo: PublicKeyInfo::RSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,113549,1,1,11) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA256,
|
||||
algo: PublicKeyInfo::RSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,113549,1,1,12) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA384,
|
||||
algo: PublicKeyInfo::RSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,113549,1,1,13) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA512,
|
||||
algo: PublicKeyInfo::RSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,113549,1,1,14) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA224,
|
||||
algo: PublicKeyInfo::RSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,10040,4,3) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA1,
|
||||
algo: PublicKeyInfo::DSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,10045,4,1) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA1,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,10045,4,3,1) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA224,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,10045,4,3,2) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA256,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,10045,4,3,3) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA384,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(1,2,840,10045,4,3,4) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA512,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
});
|
||||
}
|
||||
// if oid == oid!(2,16,840,1,101,3,4,2,1) {
|
||||
// return Ok(AlgorithmIdentifier {
|
||||
// hash: HashAlgorithm::SHA256,
|
||||
// algo: PublicKeyInfo::RSAPSS
|
||||
// });
|
||||
// }
|
||||
// if oid == oid!(2,16,840,1,101,3,4,2,2) {
|
||||
// return Ok(AlgorithmIdentifier {
|
||||
// hash: HashAlgorithm::SHA384,
|
||||
// algo: PublicKeyInfo::RSAPSS
|
||||
// });
|
||||
// }
|
||||
// if oid == oid!(2,16,840,1,101,3,4,2,3) {
|
||||
// return Ok(AlgorithmIdentifier {
|
||||
// hash: HashAlgorithm::SHA512,
|
||||
// algo: PublicKeyInfo::RSAPSS
|
||||
// });
|
||||
// }
|
||||
// if oid == oid!(2,16,840,1,101,3,4,2,4) {
|
||||
// return Ok(AlgorithmIdentifier {
|
||||
// hash: HashAlgorithm::SHA224,
|
||||
// algo: PublicKeyInfo::RSAPSS
|
||||
// });
|
||||
// }
|
||||
if oid == oid!(2,16,840,1,101,3,4,3,1) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA224,
|
||||
algo: PublicKeyInfo::DSA
|
||||
});
|
||||
}
|
||||
if oid == oid!(2,16,840,1,101,3,4,3,2) {
|
||||
return Ok(AlgorithmIdentifier {
|
||||
hash: HashAlgorithm::SHA256,
|
||||
algo: PublicKeyInfo::DSA
|
||||
});
|
||||
}
|
||||
Err(X509ParseError::UnknownAlgorithm)
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::UnknownAlgorithm)
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedAlgoInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub enum SigAlgEncodeError {
|
||||
ASN1Error(ASN1EncodeErr),
|
||||
InvalidDSAValue, InvalidHash
|
||||
}
|
||||
|
||||
impl From<ASN1EncodeErr> for SigAlgEncodeError {
|
||||
fn from(e: ASN1EncodeErr) -> SigAlgEncodeError {
|
||||
SigAlgEncodeError::ASN1Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ToASN1 for AlgorithmIdentifier {
|
||||
type Error = SigAlgEncodeError;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,SigAlgEncodeError>
|
||||
{
|
||||
let block = encode_algorithm_ident(c, self)?;
|
||||
Ok(vec![block])
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_algorithm_ident(c: ASN1Class, x: &AlgorithmIdentifier)
|
||||
-> Result<ASN1Block,SigAlgEncodeError>
|
||||
{
|
||||
match x.algo {
|
||||
PublicKeyInfo::RSA => {
|
||||
match x.hash {
|
||||
HashAlgorithm::SHA1 => {
|
||||
let o = oid!(1,2,840,113549,1,1,5);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA224 => {
|
||||
let o = oid!(1,2,840,113549,1,1,14);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA256 => {
|
||||
let o = oid!(1,2,840,113549,1,1,11);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA384 => {
|
||||
let o = oid!(1,2,840,113549,1,1,12);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA512 => {
|
||||
let o = oid!(1,2,840,113549,1,1,13);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
}
|
||||
}
|
||||
PublicKeyInfo::DSA => {
|
||||
match x.hash {
|
||||
HashAlgorithm::SHA1 => {
|
||||
let o = oid!(1,2,840,10040,4,3);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA224 => {
|
||||
let o = oid!(2,16,840,1,101,3,4,3,1);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA256 => {
|
||||
let o = oid!(2,16,840,1,101,3,4,3,2);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
_ =>
|
||||
Err(SigAlgEncodeError::InvalidHash),
|
||||
}
|
||||
}
|
||||
PublicKeyInfo::ECDSA=> {
|
||||
match x.hash {
|
||||
HashAlgorithm::SHA1 => {
|
||||
let o = oid!(1,2,840,10045,4,1);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA224 => {
|
||||
let o = oid!(1,2,840,10045,4,3,1);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA256 => {
|
||||
let o = oid!(1,2,840,10045,4,3,2);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA384 => {
|
||||
let o = oid!(1,2,840,10045,4,3,3);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
HashAlgorithm::SHA512 => {
|
||||
let o = oid!(1,2,840,10045,4,3,4);
|
||||
let obj = ASN1Block::ObjectIdentifier(c, 0, o);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![obj]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use rand::prelude::SliceRandom;
|
||||
use super::*;
|
||||
|
||||
const RSA1: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA1,
|
||||
algo: PublicKeyInfo::RSA
|
||||
};
|
||||
|
||||
const RSA224: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA224,
|
||||
algo: PublicKeyInfo::RSA
|
||||
};
|
||||
|
||||
const RSA256: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA256,
|
||||
algo: PublicKeyInfo::RSA
|
||||
};
|
||||
|
||||
const RSA384: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA384,
|
||||
algo: PublicKeyInfo::RSA
|
||||
};
|
||||
|
||||
const RSA512: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA512,
|
||||
algo: PublicKeyInfo::RSA
|
||||
};
|
||||
|
||||
const DSA1: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA1,
|
||||
algo: PublicKeyInfo::DSA
|
||||
};
|
||||
|
||||
const DSA224: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA224,
|
||||
algo: PublicKeyInfo::DSA
|
||||
};
|
||||
|
||||
const DSA256: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA256,
|
||||
algo: PublicKeyInfo::DSA
|
||||
};
|
||||
|
||||
const EC1: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA1,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
};
|
||||
|
||||
const EC224: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA224,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
};
|
||||
|
||||
const EC256: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA256,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
};
|
||||
|
||||
const EC384: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA384,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
};
|
||||
|
||||
const EC512: AlgorithmIdentifier =
|
||||
AlgorithmIdentifier{
|
||||
hash: HashAlgorithm::SHA512,
|
||||
algo: PublicKeyInfo::ECDSA
|
||||
};
|
||||
|
||||
impl Arbitrary for AlgorithmIdentifier {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> AlgorithmIdentifier {
|
||||
let opts = [RSA1, RSA224, RSA256, RSA384, RSA512,
|
||||
DSA1, DSA224, DSA256,
|
||||
EC1, EC224, EC256, EC384, EC512];
|
||||
opts.choose(g).unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck!{
|
||||
fn algident_roundtrips(v: AlgorithmIdentifier) -> bool {
|
||||
match encode_algorithm_ident(ASN1Class::Universal, &v) {
|
||||
Err(_) =>
|
||||
false,
|
||||
Ok(block) => {
|
||||
match decode_algorithm_ident(&block) {
|
||||
Err(_) =>
|
||||
false,
|
||||
Ok(v2) =>
|
||||
v == v2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
370
src/x509/atv.rs
Normal file
370
src/x509/atv.rs
Normal file
@@ -0,0 +1,370 @@
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,ToASN1};
|
||||
use std::ops::Index;
|
||||
use x509::error::X509ParseError;
|
||||
pub use x509::name::X520Name;
|
||||
|
||||
/// All of the various bits of information that are encoded within an x.509
|
||||
/// certificate.
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct InfoBlock {
|
||||
pub fields: Vec<AttributeTypeValue>
|
||||
}
|
||||
|
||||
const EMPTY_STRING: &'static str = "";
|
||||
|
||||
impl Index<X520Name> for InfoBlock {
|
||||
type Output = str;
|
||||
|
||||
fn index(&self, name: X520Name) -> &str {
|
||||
for atv in self.fields.iter() {
|
||||
if name == atv.attrtype {
|
||||
return &atv.value;
|
||||
}
|
||||
}
|
||||
&EMPTY_STRING
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for InfoBlock {
|
||||
fn eq(&self, other: &InfoBlock) -> bool {
|
||||
for x in self.fields.iter() {
|
||||
if !other.fields.contains(x) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for x in other.fields.iter() {
|
||||
if !self.fields.contains(x) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_info_block(x: &ASN1Block)
|
||||
-> Result<InfoBlock,X509ParseError>
|
||||
{
|
||||
// Name ::= CHOICE { -- only one possibility for now --
|
||||
// rdnSequence RDNSequence }
|
||||
//
|
||||
// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
|
||||
//
|
||||
// RelativeDistinguishedName ::=
|
||||
// SET SIZE (1..MAX) OF AttributeTypeAndValue
|
||||
match x {
|
||||
&ASN1Block::Sequence(_, _, ref items) => {
|
||||
let mut atvs = Vec::new();
|
||||
|
||||
for set in items.iter() {
|
||||
match set {
|
||||
&ASN1Block::Set(_, _, ref setitems) => {
|
||||
for atv in setitems.iter() {
|
||||
let v = decode_attribute_type_value(atv)?;
|
||||
atvs.push(v);
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
return Err(X509ParseError::IllFormedInfoBlock)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(InfoBlock{ fields: atvs })
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedInfoBlock)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for InfoBlock {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(InfoBlock,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match v.split_first() {
|
||||
None =>
|
||||
Err(X509ParseError::NotEnoughData),
|
||||
Some((x, rest)) => {
|
||||
let v = decode_info_block(&x)?;
|
||||
Ok((v, rest))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_info_block(c: ASN1Class, b: &InfoBlock)
|
||||
-> Result<ASN1Block,ASN1EncodeErr>
|
||||
{
|
||||
let mut encoded_fields = Vec::with_capacity(b.fields.len());
|
||||
|
||||
for fld in b.fields.iter() {
|
||||
let val = encode_attribute_type_value(c, fld)?;
|
||||
encoded_fields.push(val);
|
||||
}
|
||||
|
||||
let set = ASN1Block::Set(c, 0, encoded_fields);
|
||||
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![set]))
|
||||
}
|
||||
|
||||
impl ToASN1 for InfoBlock {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
|
||||
{
|
||||
let block = encode_info_block(c, self)?;
|
||||
Ok(vec![block])
|
||||
}
|
||||
}
|
||||
|
||||
/// An attribute within an x.509 key and its associated string value.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct AttributeTypeValue {
|
||||
pub attrtype: X520Name,
|
||||
pub value: String
|
||||
}
|
||||
|
||||
fn decode_attribute_type_value(x: &ASN1Block)
|
||||
-> Result<AttributeTypeValue,X509ParseError>
|
||||
{
|
||||
// AttributeTypeAndValue ::= SEQUENCE {
|
||||
// type AttributeType,
|
||||
// value AttributeValue }
|
||||
match x {
|
||||
&ASN1Block::Sequence(_, _, ref xs) => {
|
||||
let (name, rest) = X520Name::from_asn1(xs)?;
|
||||
match rest.first() {
|
||||
None => Err(X509ParseError::NotEnoughData),
|
||||
Some(ref x) => {
|
||||
let atvstr = get_atv_string(name, x)?;
|
||||
Ok(AttributeTypeValue{
|
||||
attrtype: name,
|
||||
value: atvstr
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedAttrTypeValue)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for AttributeTypeValue {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(AttributeTypeValue,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match v.split_first() {
|
||||
None =>
|
||||
Err(X509ParseError::NotEnoughData),
|
||||
Some((x, rest)) => {
|
||||
let v = decode_attribute_type_value(&x)?;
|
||||
Ok((v, rest))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_attribute_type_value(c: ASN1Class, x: &AttributeTypeValue)
|
||||
-> Result<ASN1Block,ASN1EncodeErr>
|
||||
{
|
||||
let mut resvec = x.attrtype.to_asn1_class(c)?;
|
||||
let value = match x.attrtype {
|
||||
X520Name::CountryName =>
|
||||
ASN1Block::PrintableString(c,0,x.value.clone()),
|
||||
X520Name::SerialNumber =>
|
||||
ASN1Block::PrintableString(c,0,x.value.clone()),
|
||||
X520Name::DomainComponent =>
|
||||
ASN1Block::IA5String(c,0,x.value.clone()),
|
||||
X520Name::EmailAddress =>
|
||||
ASN1Block::IA5String(c,0,x.value.clone()),
|
||||
_ =>
|
||||
ASN1Block::UTF8String(c,0,x.value.clone())
|
||||
};
|
||||
resvec.push(value);
|
||||
Ok(ASN1Block::Sequence(c, 0, resvec))
|
||||
}
|
||||
|
||||
impl ToASN1 for AttributeTypeValue {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
|
||||
{
|
||||
let block = encode_attribute_type_value(c, self)?;
|
||||
Ok(vec![block])
|
||||
}
|
||||
}
|
||||
|
||||
fn get_atv_string(n: X520Name, x: &ASN1Block)
|
||||
-> Result<String,X509ParseError>
|
||||
{
|
||||
match n {
|
||||
X520Name::CountryName => {
|
||||
let res = get_printable_val(x)?;
|
||||
if res.len() != 2 {
|
||||
return Err(X509ParseError::IllegalStringValue);
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
X520Name::SerialNumber => get_printable_val(x),
|
||||
X520Name::DomainComponent => get_ia5_val(x),
|
||||
X520Name::EmailAddress => get_ia5_val(x),
|
||||
_ => get_string_val(x),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_string_val(a: &ASN1Block) -> Result<String,X509ParseError>
|
||||
{
|
||||
match a {
|
||||
&ASN1Block::TeletexString(_,_,ref v) => Ok(v.clone()),
|
||||
&ASN1Block::PrintableString(_,_,ref v) => Ok(v.clone()),
|
||||
&ASN1Block::UniversalString(_,_,ref v) => Ok(v.clone()),
|
||||
&ASN1Block::UTF8String(_,_,ref v) => Ok(v.clone()),
|
||||
&ASN1Block::BMPString(_,_,ref v) => Ok(v.clone()),
|
||||
_ =>
|
||||
Err(X509ParseError::IllegalStringValue)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_printable_val(a: &ASN1Block) -> Result<String,X509ParseError>
|
||||
{
|
||||
match a {
|
||||
&ASN1Block::PrintableString(_,_,ref v) => Ok(v.clone()),
|
||||
_ =>
|
||||
Err(X509ParseError::IllegalStringValue)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ia5_val(a: &ASN1Block) -> Result<String,X509ParseError>
|
||||
{
|
||||
match a {
|
||||
&ASN1Block::IA5String(_,_,ref v) => Ok(v.clone()),
|
||||
_ =>
|
||||
Err(X509ParseError::IllegalStringValue)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use rand::Rng;
|
||||
use rand::prelude::SliceRandom;
|
||||
use std::iter::FromIterator;
|
||||
use super::*;
|
||||
|
||||
impl Arbitrary for X520Name {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> X520Name {
|
||||
let names = vec![X520Name::Name,
|
||||
X520Name::Surname,
|
||||
X520Name::GivenName,
|
||||
X520Name::Initials,
|
||||
X520Name::GenerationQualifier,
|
||||
X520Name::CommonName,
|
||||
X520Name::LocalityName,
|
||||
X520Name::StateOrProvinceName,
|
||||
X520Name::OrganizationName,
|
||||
X520Name::OrganizationalUnit,
|
||||
X520Name::Title,
|
||||
X520Name::DNQualifier,
|
||||
X520Name::CountryName,
|
||||
X520Name::SerialNumber,
|
||||
X520Name::Pseudonym,
|
||||
X520Name::DomainComponent,
|
||||
X520Name::EmailAddress];
|
||||
names.choose(g).unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for AttributeTypeValue {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> AttributeTypeValue {
|
||||
let name = X520Name::arbitrary(g);
|
||||
let val = match name {
|
||||
X520Name::CountryName => {
|
||||
let mut base = gen_printable(g);
|
||||
base.push('U');
|
||||
base.push('S');
|
||||
base.truncate(2);
|
||||
base
|
||||
}
|
||||
X520Name::SerialNumber => gen_printable(g),
|
||||
X520Name::DomainComponent => gen_ia5(g),
|
||||
X520Name::EmailAddress => gen_ia5(g),
|
||||
_ => gen_utf8(g)
|
||||
};
|
||||
AttributeTypeValue{ attrtype: name, value: val }
|
||||
}
|
||||
}
|
||||
|
||||
const PRINTABLE_CHARS: &'static str =
|
||||
"ABCDEFGHIJKLMOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'()+,-./:=? ";
|
||||
|
||||
fn gen_printable<G: Gen>(g: &mut G) -> String {
|
||||
let count = g.gen_range(0, 384);
|
||||
let mut items = Vec::with_capacity(count);
|
||||
|
||||
for _ in 0..count {
|
||||
let v = PRINTABLE_CHARS.as_bytes().choose(g).unwrap();
|
||||
items.push(*v as char);
|
||||
}
|
||||
String::from_iter(items.iter())
|
||||
}
|
||||
|
||||
fn gen_ia5<G: Gen>(g: &mut G) -> String {
|
||||
let count = g.gen_range(0, 384);
|
||||
let mut items = Vec::with_capacity(count);
|
||||
|
||||
for _ in 0..count {
|
||||
items.push(g.gen::<u8>() as char);
|
||||
}
|
||||
String::from_iter(items.iter())
|
||||
}
|
||||
|
||||
fn gen_utf8<G: Gen>(g: &mut G) -> String {
|
||||
String::arbitrary(g)
|
||||
}
|
||||
|
||||
impl Arbitrary for InfoBlock {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> InfoBlock {
|
||||
let count = g.gen_range(0,12);
|
||||
let mut items = Vec::with_capacity(count);
|
||||
let mut names = Vec::with_capacity(count);
|
||||
|
||||
while items.len() < count {
|
||||
let atv = AttributeTypeValue::arbitrary(g);
|
||||
if !names.contains(&atv.attrtype) {
|
||||
names.push(atv.attrtype);
|
||||
items.push(atv);
|
||||
}
|
||||
}
|
||||
|
||||
InfoBlock{ fields: items }
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn attrtypeval_roundtrips(v: AttributeTypeValue) -> bool {
|
||||
match encode_attribute_type_value(ASN1Class::Universal, &v) {
|
||||
Err(_) => false,
|
||||
Ok(bstr) =>
|
||||
match decode_attribute_type_value(&bstr) {
|
||||
Err(_) => false,
|
||||
Ok(v2) => v == v2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn infoblock_roundtrips(v: InfoBlock) -> bool {
|
||||
match encode_info_block(ASN1Class::Universal, &v) {
|
||||
Err(_) => false,
|
||||
Ok(bstr) =>
|
||||
match decode_info_block(&bstr) {
|
||||
Err(_) => false,
|
||||
Ok(v2) => v == v2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/x509/error.rs
Normal file
53
src/x509/error.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use dsa::rfc6979::DSADecodeError;
|
||||
use ecdsa::ECDSADecodeErr;
|
||||
use rsa::RSAError;
|
||||
use simple_asn1::{ASN1DecodeErr,ASN1EncodeErr};
|
||||
|
||||
/// The error type for parsing and validating an X.509 certificate.
|
||||
#[derive(Debug)]
|
||||
pub enum X509ParseError {
|
||||
ASN1DecodeError(ASN1DecodeErr), ASN1EncodeError(ASN1EncodeErr),
|
||||
RSAError(RSAError), DSADecodeError(DSADecodeError), ECDSADecodeError(ECDSADecodeErr),
|
||||
RSASignatureWrong, DSASignatureWrong,
|
||||
NotEnoughData,
|
||||
IllFormedName, IllFormedAttrTypeValue, IllFormedInfoBlock,
|
||||
IllFormedValidity, IllFormedCertificateInfo, IllFormedSerialNumber,
|
||||
IllFormedAlgoInfo, IllFormedKey, IllFormedEverything,
|
||||
IllegalStringValue, NoSerialNumber, InvalidDSAInfo, ItemNotFound,
|
||||
UnknownAlgorithm, InvalidRSAKey, InvalidDSAKey, InvalidSignatureData,
|
||||
InvalidSignatureHash, InvalidECDSAKey, InvalidPointForm,
|
||||
UnknownEllipticCurve,
|
||||
CompressedPointUnsupported,
|
||||
KeyNotFound,
|
||||
SignatureNotFound, SignatureVerificationFailed
|
||||
}
|
||||
|
||||
impl From<ASN1DecodeErr> for X509ParseError {
|
||||
fn from(e: ASN1DecodeErr) -> X509ParseError {
|
||||
X509ParseError::ASN1DecodeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ASN1EncodeErr> for X509ParseError {
|
||||
fn from(e: ASN1EncodeErr) -> X509ParseError {
|
||||
X509ParseError::ASN1EncodeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RSAError> for X509ParseError {
|
||||
fn from(e: RSAError) -> X509ParseError {
|
||||
X509ParseError::RSAError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ECDSADecodeErr> for X509ParseError {
|
||||
fn from(e: ECDSADecodeErr) -> X509ParseError {
|
||||
X509ParseError::ECDSADecodeError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DSADecodeError> for X509ParseError {
|
||||
fn from(e: DSADecodeError) -> X509ParseError {
|
||||
X509ParseError::DSADecodeError(e)
|
||||
}
|
||||
}
|
||||
197
src/x509/misc.rs
Normal file
197
src/x509/misc.rs
Normal file
@@ -0,0 +1,197 @@
|
||||
use num::{BigInt,BigUint,One,ToPrimitive,Zero};
|
||||
use num::bigint::ToBigInt;
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,ToASN1};
|
||||
use x509::error::X509ParseError;
|
||||
|
||||
/// Which version of x.509 certificate this is.
|
||||
#[derive(Clone,Copy,Debug,PartialEq)]
|
||||
pub enum X509Version { V1, V2, V3 }
|
||||
|
||||
fn decode_version(bs: &[ASN1Block])
|
||||
-> Result<(X509Version,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match bs.split_first() {
|
||||
Some((&ASN1Block::Integer(_, _, ref v), rest)) => {
|
||||
match v.to_u8() {
|
||||
Some(0) => Ok((X509Version::V1, rest)),
|
||||
Some(1) => Ok((X509Version::V2, rest)),
|
||||
Some(2) => Ok((X509Version::V3, rest)),
|
||||
_ => Ok((X509Version::V1, &bs))
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::NotEnoughData)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for X509Version {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(X509Version,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
decode_version(v)
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_version(c: ASN1Class, v: X509Version) -> Vec<ASN1Block> {
|
||||
match v {
|
||||
X509Version::V1 => {
|
||||
let zero: BigInt = Zero::zero();
|
||||
let block = ASN1Block::Integer(c, 0, zero);
|
||||
vec![block]
|
||||
}
|
||||
X509Version::V2 => {
|
||||
let one: BigInt = One::one();
|
||||
let block = ASN1Block::Integer(c, 0, one);
|
||||
vec![block]
|
||||
}
|
||||
X509Version::V3 => {
|
||||
let two: BigInt = BigInt::from(2 as u64);
|
||||
let block = ASN1Block::Integer(c, 0, two);
|
||||
vec![block]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToASN1 for X509Version {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
|
||||
{
|
||||
Ok(encode_version(c, *self))
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
/// The serial number for this certificate.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct X509Serial {
|
||||
num: BigUint
|
||||
}
|
||||
|
||||
fn decode_serial(x: &ASN1Block)
|
||||
-> Result<X509Serial,X509ParseError>
|
||||
{
|
||||
match x {
|
||||
&ASN1Block::Integer(_, _, ref v) => {
|
||||
match v.to_biguint() {
|
||||
None =>
|
||||
Err(X509ParseError::IllFormedSerialNumber),
|
||||
Some(n) =>
|
||||
Ok(X509Serial{ num: n })
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::NoSerialNumber)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for X509Serial {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(X509Serial,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match v.split_first() {
|
||||
None =>
|
||||
Err(X509ParseError::NoSerialNumber),
|
||||
Some((x, rest)) => {
|
||||
let v = decode_serial(x)?;
|
||||
Ok((v, rest))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum SerialEncodeErr { ASN1Error(ASN1EncodeErr), InvalidSerialNumber }
|
||||
|
||||
impl From<ASN1EncodeErr> for SerialEncodeErr {
|
||||
fn from(e: ASN1EncodeErr) -> SerialEncodeErr {
|
||||
SerialEncodeErr::ASN1Error(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_serial(c: ASN1Class, serial: &X509Serial)
|
||||
-> Result<ASN1Block,SerialEncodeErr>
|
||||
{
|
||||
match serial.num.to_bigint() {
|
||||
None =>
|
||||
Err(SerialEncodeErr::InvalidSerialNumber),
|
||||
Some(n) =>
|
||||
Ok(ASN1Block::Integer(c, 0, n))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToASN1 for X509Serial {
|
||||
type Error = SerialEncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,SerialEncodeErr>
|
||||
{
|
||||
let v = encode_serial(c, self)?;
|
||||
Ok(vec![v])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode_signature(x: &ASN1Block)
|
||||
-> Result<Vec<u8>,X509ParseError>
|
||||
{
|
||||
match x {
|
||||
&ASN1Block::BitString(_, _, size, ref sig) if size % 8 == 0 => {
|
||||
Ok(sig.to_vec())
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::SignatureNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use rand::Rng;
|
||||
use rand::distributions::Uniform;
|
||||
use super::*;
|
||||
|
||||
fn check_version_roundtrip(v: X509Version) {
|
||||
let blocks = encode_version(ASN1Class::Universal, v);
|
||||
match decode_version(&blocks) {
|
||||
Err(_) =>
|
||||
assert!(false),
|
||||
Ok((v2,_)) =>
|
||||
assert_eq!(v, v2)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn versions_roundtrip() {
|
||||
check_version_roundtrip(X509Version::V1);
|
||||
check_version_roundtrip(X509Version::V2);
|
||||
check_version_roundtrip(X509Version::V3);
|
||||
}
|
||||
|
||||
impl Arbitrary for X509Serial {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> X509Serial {
|
||||
let count = g.gen_range(0,16);
|
||||
let dist = Uniform::new_inclusive(0,255);
|
||||
let bits = g.sample_iter(&dist).take(count).collect();
|
||||
let val = BigUint::new(bits);
|
||||
X509Serial{ num: val }
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn serial_roundtrips(s: X509Serial) -> bool {
|
||||
match encode_serial(ASN1Class::Universal, &s) {
|
||||
Err(_) => false,
|
||||
Ok(block) =>
|
||||
match decode_serial(&block) {
|
||||
Err(_) => false,
|
||||
Ok(s2) => s == s2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
295
src/x509/mod.rs
Normal file
295
src/x509/mod.rs
Normal file
@@ -0,0 +1,295 @@
|
||||
mod algident;
|
||||
mod atv;
|
||||
mod error;
|
||||
mod misc;
|
||||
mod name;
|
||||
mod publickey;
|
||||
mod validity;
|
||||
|
||||
use dsa::DSAPublic;
|
||||
use ecdsa::ECDSAPublic;
|
||||
use rsa::{SIGNING_HASH_SHA1,SIGNING_HASH_SHA224,SIGNING_HASH_SHA256,SIGNING_HASH_SHA384,SIGNING_HASH_SHA512};
|
||||
use sha::{SHA1,SHA224,SHA256,SHA384,SHA512};
|
||||
use simple_asn1::{ASN1Block,FromASN1,der_decode,from_der};
|
||||
pub use x509::validity::Validity;
|
||||
pub use x509::algident::{AlgorithmIdentifier,HashAlgorithm,PublicKeyInfo};
|
||||
use x509::algident::{decode_algorithm_ident};
|
||||
pub use x509::atv::InfoBlock;
|
||||
use x509::error::X509ParseError;
|
||||
pub use x509::misc::{X509Serial,X509Version};
|
||||
use x509::misc::{decode_signature};
|
||||
pub use x509::publickey::X509PublicKey;
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* The actual certificate data type and methods
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/// The type of an X.509 certificate.
|
||||
pub struct GenericCertificate {
|
||||
pub version: X509Version,
|
||||
pub serial: X509Serial,
|
||||
pub signature_alg: AlgorithmIdentifier,
|
||||
pub issuer: InfoBlock,
|
||||
pub subject: InfoBlock,
|
||||
pub validity: Validity,
|
||||
pub subject_key: X509PublicKey,
|
||||
pub extensions: Vec<()>
|
||||
}
|
||||
|
||||
fn decode_certificate(x: &ASN1Block)
|
||||
-> Result<GenericCertificate,X509ParseError>
|
||||
{
|
||||
//
|
||||
// TBSCertificate ::= SEQUENCE {
|
||||
// version [0] Version DEFAULT v1,
|
||||
// serialNumber CertificateSerialNumber,
|
||||
// signature AlgorithmIdentifier,
|
||||
// issuer Name,
|
||||
// validity Validity,
|
||||
// subject Name,
|
||||
// subjectPublicKeyInfo SubjectPublicKeyInfo,
|
||||
// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
|
||||
// -- If present, version MUST be v2 or v3
|
||||
// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
|
||||
// -- If present, version MUST be v2 or v3
|
||||
// extensions [3] Extensions OPTIONAL
|
||||
// -- If present, version MUST be v3 -- }
|
||||
//
|
||||
match x {
|
||||
&ASN1Block::Sequence(_, _, ref b0) => {
|
||||
let (version, b1) = X509Version::from_asn1(b0)?;
|
||||
let (serial, b2) = X509Serial::from_asn1(b1)?;
|
||||
let (ident, b3) = AlgorithmIdentifier::from_asn1(b2)?;
|
||||
let (issuer, b4) = InfoBlock::from_asn1(b3)?;
|
||||
let (validity, b5) = Validity::from_asn1(b4)?;
|
||||
let (subject, b6) = InfoBlock::from_asn1(b5)?;
|
||||
let (subkey, _ ) = X509PublicKey::from_asn1(b6)?;
|
||||
Ok(GenericCertificate {
|
||||
version: version,
|
||||
serial: serial,
|
||||
signature_alg: ident,
|
||||
issuer: issuer,
|
||||
subject: subject,
|
||||
validity: validity,
|
||||
subject_key: subkey,
|
||||
extensions: vec![]
|
||||
})
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedCertificateInfo)
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* X.509 parsing routines
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/// Parse an X.590 certificate in memory into a generic certificate that can
|
||||
/// be used by a program.
|
||||
pub fn parse_x509(buffer: &[u8]) -> Result<GenericCertificate,X509ParseError> {
|
||||
let blocks = from_der(&buffer[..])?;
|
||||
match blocks.first() {
|
||||
None =>
|
||||
Err(X509ParseError::NotEnoughData),
|
||||
Some(&ASN1Block::Sequence(_, _, ref x)) => {
|
||||
let cert = decode_certificate(&x[0])?;
|
||||
let cert_block_start = x[0].offset();
|
||||
let cert_block_end = x[1].offset();
|
||||
let cert_block = &buffer[cert_block_start..cert_block_end];
|
||||
let alginfo = decode_algorithm_ident(&x[1])?;
|
||||
let sig = decode_signature(&x[2])?;
|
||||
check_signature(&alginfo, &cert.subject_key, cert_block, sig)?;
|
||||
Ok(cert)
|
||||
}
|
||||
Some(_) =>
|
||||
Err(X509ParseError::IllFormedEverything)
|
||||
}
|
||||
}
|
||||
|
||||
fn check_signature(alg: &AlgorithmIdentifier,
|
||||
key: &X509PublicKey,
|
||||
block: &[u8],
|
||||
sig: Vec<u8>)
|
||||
-> Result<(),X509ParseError>
|
||||
{
|
||||
match (alg.algo, key) {
|
||||
(PublicKeyInfo::RSA, &X509PublicKey::RSA(ref key)) => {
|
||||
let sighash = match alg.hash {
|
||||
HashAlgorithm::SHA1 => &SIGNING_HASH_SHA1,
|
||||
HashAlgorithm::SHA224 => &SIGNING_HASH_SHA224,
|
||||
HashAlgorithm::SHA256 => &SIGNING_HASH_SHA256,
|
||||
HashAlgorithm::SHA384 => &SIGNING_HASH_SHA384,
|
||||
HashAlgorithm::SHA512 => &SIGNING_HASH_SHA512,
|
||||
};
|
||||
|
||||
if !key.verify(sighash, block, &sig) {
|
||||
return Err(X509ParseError::RSASignatureWrong);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
(PublicKeyInfo::DSA, &X509PublicKey::DSA(DSAPublic::DSAPublicL1024N160(ref key))) => {
|
||||
let dsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1
|
||||
if key.verify::<SHA1>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224
|
||||
if key.verify::<SHA224>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<SHA256>(block, &dsa_sig) =>
|
||||
Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::DSA, &X509PublicKey::DSA(DSAPublic::DSAPublicL2048N224(ref key))) => {
|
||||
let dsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1
|
||||
if key.verify::<SHA1>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224
|
||||
if key.verify::<SHA224>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<SHA256>(block, &dsa_sig) =>
|
||||
Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::DSA, &X509PublicKey::DSA(DSAPublic::DSAPublicL2048N256(ref key))) => {
|
||||
let dsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1
|
||||
if key.verify::<SHA1>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224
|
||||
if key.verify::<SHA224>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<SHA256>(block, &dsa_sig) =>
|
||||
Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::DSA, &X509PublicKey::DSA(DSAPublic::DSAPublicL3072N256(ref key))) => {
|
||||
let dsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1
|
||||
if key.verify::<SHA1>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224
|
||||
if key.verify::<SHA224>(block, &dsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<SHA256>(block, &dsa_sig) =>
|
||||
Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::ECDSA, &X509PublicKey::ECDSA(ECDSAPublic::P192(ref key))) => {
|
||||
let ecdsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1 if key.verify::<SHA1>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224 if key.verify::<SHA224>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<SHA256>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA384 if key.verify::<SHA384>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA512 if key.verify::<SHA512>(block, &ecdsa_sig) => Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::ECDSA, &X509PublicKey::ECDSA(ECDSAPublic::P224(ref key))) => {
|
||||
let ecdsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1 if key.verify::<SHA1>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224 if key.verify::<SHA224>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<SHA256>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA384 if key.verify::<SHA384>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA512 if key.verify::<SHA512>(block, &ecdsa_sig) => Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::ECDSA, &X509PublicKey::ECDSA(ECDSAPublic::P256(ref key))) => {
|
||||
let ecdsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1 if key.verify::<SHA1>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224 if key.verify::<SHA224>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<SHA256>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA384 if key.verify::<SHA384>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA512 if key.verify::<SHA512>(block, &ecdsa_sig) => Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::ECDSA, &X509PublicKey::ECDSA(ECDSAPublic::P384(ref key))) => {
|
||||
let ecdsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1 if key.verify::<SHA1>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224 if key.verify::<SHA224>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<SHA256>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA384 if key.verify::<SHA384>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA512 if key.verify::<SHA512>(block, &ecdsa_sig) => Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
(PublicKeyInfo::ECDSA, &X509PublicKey::ECDSA(ECDSAPublic::P521(ref key))) => {
|
||||
let ecdsa_sig = der_decode(&sig)?;
|
||||
match alg.hash {
|
||||
HashAlgorithm::SHA1 if key.verify::<SHA1>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA224 if key.verify::<SHA224>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA256 if key.verify::<SHA256>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA384 if key.verify::<SHA384>(block, &ecdsa_sig) => Ok(()),
|
||||
HashAlgorithm::SHA512 if key.verify::<SHA512>(block, &ecdsa_sig) => Ok(()),
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureHash)
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidSignatureData)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Testing is for winners!
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use super::*;
|
||||
|
||||
fn can_parse(f: &str) -> Result<GenericCertificate,X509ParseError> {
|
||||
let mut fd = File::open(f).unwrap();
|
||||
let mut buffer = Vec::new();
|
||||
let _amt = fd.read_to_end(&mut buffer);
|
||||
parse_x509(&buffer)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rsa_tests() {
|
||||
assert!(can_parse("testdata/x509/rsa2048-1.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/rsa2048-2.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/rsa4096-1.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/rsa4096-2.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/rsa4096-3.der").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dsa_tests() {
|
||||
assert!(can_parse("testdata/x509/dsa2048-1.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/dsa2048-2.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/dsa3072-1.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/dsa3072-2.der").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ecc_tests() {
|
||||
assert!(can_parse("testdata/x509/ec384-1.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/ec384-2.der").is_ok());
|
||||
assert!(can_parse("testdata/x509/ec384-3.der").is_ok());
|
||||
}
|
||||
}
|
||||
138
src/x509/name.rs
Normal file
138
src/x509/name.rs
Normal file
@@ -0,0 +1,138 @@
|
||||
use num::BigUint;
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1};
|
||||
use x509::error::X509ParseError;
|
||||
|
||||
/// One of the various attributes that can be encoded within an x.509 name. To
|
||||
/// see one of these paired with its value, consider `AttributeTypeValue`.
|
||||
#[derive(Copy,Clone,Debug,Eq,Hash,PartialEq)]
|
||||
pub enum X520Name {
|
||||
Name, Surname, GivenName, Initials, GenerationQualifier, CommonName,
|
||||
LocalityName, StateOrProvinceName, OrganizationName, OrganizationalUnit,
|
||||
Title, DNQualifier, CountryName, SerialNumber, Pseudonym, DomainComponent,
|
||||
EmailAddress
|
||||
}
|
||||
|
||||
impl FromASN1 for X520Name {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(X520Name,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match v.split_first() {
|
||||
None =>
|
||||
Err(X509ParseError::NotEnoughData),
|
||||
Some((x,rest)) => {
|
||||
let name = decode_name(&x)?;
|
||||
Ok((name,rest))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_name(val: &ASN1Block)
|
||||
-> Result<X520Name,X509ParseError>
|
||||
{
|
||||
match val {
|
||||
&ASN1Block::ObjectIdentifier(_, _, ref oid) => {
|
||||
if oid == oid!(2,5,4,41) {return Ok(X520Name::Name) }
|
||||
if oid == oid!(2,5,4,4) {return Ok(X520Name::Surname) }
|
||||
if oid == oid!(2,5,4,42) {return Ok(X520Name::GivenName) }
|
||||
if oid == oid!(2,5,4,43) {return Ok(X520Name::Initials) }
|
||||
if oid == oid!(2,5,4,44) {return Ok(X520Name::GenerationQualifier)}
|
||||
if oid == oid!(2,5,4,3) {return Ok(X520Name::CommonName) }
|
||||
if oid == oid!(2,5,4,7) {return Ok(X520Name::LocalityName) }
|
||||
if oid == oid!(2,5,4,8) {return Ok(X520Name::StateOrProvinceName)}
|
||||
if oid == oid!(2,5,4,10) {return Ok(X520Name::OrganizationName) }
|
||||
if oid == oid!(2,5,4,11) {return Ok(X520Name::OrganizationalUnit) }
|
||||
if oid == oid!(2,5,4,12) {return Ok(X520Name::Title) }
|
||||
if oid == oid!(2,5,4,46) {return Ok(X520Name::DNQualifier) }
|
||||
if oid == oid!(2,5,4,6) {return Ok(X520Name::CountryName) }
|
||||
if oid == oid!(2,5,4,5) {return Ok(X520Name::SerialNumber) }
|
||||
if oid == oid!(2,5,4,65) {return Ok(X520Name::Pseudonym) }
|
||||
if oid == oid!(0,9,2342,19200300,100,1,25) {
|
||||
return Ok(X520Name::DomainComponent);
|
||||
}
|
||||
if oid == oid!(1,2,840,113549,1,9,1) {
|
||||
return Ok(X520Name::EmailAddress);
|
||||
}
|
||||
Err(X509ParseError::IllFormedName)
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedName)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToASN1 for X520Name {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
|
||||
{
|
||||
let block = encode_name(c, *self);
|
||||
Ok(vec![block])
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_name(class: ASN1Class, name: X520Name)
|
||||
-> ASN1Block
|
||||
{
|
||||
let oid = match name {
|
||||
X520Name::Name => oid!(2,5,4,41),
|
||||
X520Name::Surname => oid!(2,5,4,4),
|
||||
X520Name::GivenName => oid!(2,5,4,42),
|
||||
X520Name::Initials => oid!(2,5,4,43),
|
||||
X520Name::GenerationQualifier => oid!(2,5,4,44),
|
||||
X520Name::CommonName => oid!(2,5,4,3),
|
||||
X520Name::LocalityName => oid!(2,5,4,7),
|
||||
X520Name::StateOrProvinceName => oid!(2,5,4,8),
|
||||
X520Name::OrganizationName => oid!(2,5,4,10),
|
||||
X520Name::OrganizationalUnit => oid!(2,5,4,11),
|
||||
X520Name::Title => oid!(2,5,4,12),
|
||||
X520Name::DNQualifier => oid!(2,5,4,46),
|
||||
X520Name::CountryName => oid!(2,5,4,6),
|
||||
X520Name::SerialNumber => oid!(2,5,4,5),
|
||||
X520Name::Pseudonym => oid!(2,5,4,65),
|
||||
X520Name::DomainComponent => oid!(0,9,2342,19200300,100,1,25),
|
||||
X520Name::EmailAddress => oid!(1,2,840,113549,1,9,1)
|
||||
};
|
||||
|
||||
ASN1Block::ObjectIdentifier(class, 0, oid)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn encdec_test(n: X520Name) {
|
||||
let block = encode_name(ASN1Class::Universal, n);
|
||||
let vec = vec![block];
|
||||
match X520Name::from_asn1(&vec) {
|
||||
Err(_) =>
|
||||
assert!(false),
|
||||
Ok((m, _)) =>
|
||||
assert_eq!(n,m)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn name_encoding_roundtrips() {
|
||||
encdec_test(X520Name::Name);
|
||||
encdec_test(X520Name::Surname);
|
||||
encdec_test(X520Name::GivenName);
|
||||
encdec_test(X520Name::Initials);
|
||||
encdec_test(X520Name::GenerationQualifier);
|
||||
encdec_test(X520Name::CommonName);
|
||||
encdec_test(X520Name::LocalityName);
|
||||
encdec_test(X520Name::StateOrProvinceName);
|
||||
encdec_test(X520Name::OrganizationName);
|
||||
encdec_test(X520Name::OrganizationalUnit);
|
||||
encdec_test(X520Name::Title);
|
||||
encdec_test(X520Name::DNQualifier);
|
||||
encdec_test(X520Name::CountryName);
|
||||
encdec_test(X520Name::SerialNumber);
|
||||
encdec_test(X520Name::Pseudonym);
|
||||
encdec_test(X520Name::DomainComponent);
|
||||
encdec_test(X520Name::EmailAddress);
|
||||
}
|
||||
}
|
||||
|
||||
330
src/x509/publickey.rs
Normal file
330
src/x509/publickey.rs
Normal file
@@ -0,0 +1,330 @@
|
||||
use cryptonum::unsigned::{U3072,U2048,U1024,U256,U192};
|
||||
use dsa::{DSAPublic,DSAPublicKey,DSAParameters};
|
||||
use dsa::{L3072N256,L2048N256,L2048N224,L1024N160};
|
||||
use ecdsa::{ECDSAEncodeErr,ECDSAPublic,ECCPublicKey};
|
||||
use ecdsa::{P192,P224,P256,P384,P521};
|
||||
use num::BigUint;
|
||||
use rsa::RSAPublic;
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1,
|
||||
der_decode,der_encode,from_der};
|
||||
use utils::TranslateNums;
|
||||
use x509::error::X509ParseError;
|
||||
|
||||
/// A general type that includes all the supported public key types that we
|
||||
/// could read in an x.509 certificate.
|
||||
pub enum X509PublicKey {
|
||||
DSA(DSAPublic),
|
||||
RSA(RSAPublic),
|
||||
ECDSA(ECDSAPublic)
|
||||
}
|
||||
|
||||
impl From<X509PublicKey> for Option<DSAPublic> {
|
||||
fn from(x: X509PublicKey) -> Option<DSAPublic> {
|
||||
match x {
|
||||
X509PublicKey::DSA(x) => Some(x),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<X509PublicKey> for Option<RSAPublic> {
|
||||
fn from(x: X509PublicKey) -> Option<RSAPublic> {
|
||||
match x {
|
||||
X509PublicKey::RSA(x) => Some(x),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<X509PublicKey> for Option<ECDSAPublic> {
|
||||
fn from(x: X509PublicKey) -> Option<ECDSAPublic> {
|
||||
match x {
|
||||
X509PublicKey::ECDSA(x) => Some(x),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum X509EncodeErr {
|
||||
ASN1EncodeErr(ASN1EncodeErr),
|
||||
ECDSAEncodeErr(ECDSAEncodeErr)
|
||||
}
|
||||
|
||||
impl From<ASN1EncodeErr> for X509EncodeErr {
|
||||
fn from(x: ASN1EncodeErr) -> X509EncodeErr {
|
||||
X509EncodeErr::ASN1EncodeErr(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ECDSAEncodeErr> for X509EncodeErr {
|
||||
fn from(x: ECDSAEncodeErr) -> X509EncodeErr {
|
||||
X509EncodeErr::ECDSAEncodeErr(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToASN1 for X509PublicKey {
|
||||
type Error = X509EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class) -> Result<Vec<ASN1Block>,X509EncodeErr> {
|
||||
let block = match self {
|
||||
X509PublicKey::RSA(x) => encode_rsa_key(c, x)?,
|
||||
X509PublicKey::DSA(x) => encode_dsa_key(c, x)?,
|
||||
X509PublicKey::ECDSA(x) => encode_ecdsa_key(c, x)?,
|
||||
};
|
||||
Ok(vec![block])
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for X509PublicKey {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block]) -> Result<(X509PublicKey, &[ASN1Block]), Self::Error>
|
||||
{
|
||||
let (block, rest) = v.split_first().ok_or(X509ParseError::NotEnoughData)?;
|
||||
|
||||
// SubjectPublicKeyInfo ::= SEQUENCE {
|
||||
// algorithm AlgorithmIdentifier,
|
||||
// subjectPublicKey BIT STRING }
|
||||
if let &ASN1Block::Sequence(_, _, ref info) = block {
|
||||
let (id, malginfo) = strip_algident(&info[0])?;
|
||||
|
||||
if id == oid!(1,2,840,113549,1,1,1) {
|
||||
let key = decode_rsa_key(&info[1])?;
|
||||
return Ok((X509PublicKey::RSA(key), rest));
|
||||
}
|
||||
|
||||
if id == oid!(1,2,840,10040,4,1) {
|
||||
if let Some(alginfo) = malginfo {
|
||||
let key = decode_dsa_key(alginfo, &info[1])?;
|
||||
return Ok((X509PublicKey::DSA(key), rest));
|
||||
}
|
||||
}
|
||||
|
||||
if id == oid!(1,2,840,10045,2,1) {
|
||||
if let Some(alginfo) = malginfo {
|
||||
let key = decode_ecdsa_key(alginfo, &info[1..])?;
|
||||
return Ok((X509PublicKey::ECDSA(key), rest));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(X509ParseError::IllFormedKey)
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// RSA Public Key encoding / decoding
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
fn encode_rsa_key(c: ASN1Class, x: &RSAPublic) -> Result<ASN1Block,ASN1EncodeErr>
|
||||
{
|
||||
let objoid = ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,113549,1,1,1));
|
||||
let bstr = der_encode(x)?;
|
||||
let objkey = ASN1Block::BitString(c, 0, bstr.len() * 8, bstr);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![objoid, objkey]))
|
||||
}
|
||||
|
||||
fn decode_rsa_key(x: &ASN1Block) -> Result<RSAPublic,X509ParseError>
|
||||
{
|
||||
if let &ASN1Block::BitString(_, _, _, ref bstr) = x {
|
||||
der_decode(bstr).map_err(|x| X509ParseError::RSAError(x))
|
||||
} else {
|
||||
Err(X509ParseError::NotEnoughData)
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// DSA Public Key encoding / decoding
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
fn encode_dsa_key(c: ASN1Class, x: &DSAPublic) -> Result<ASN1Block,ASN1EncodeErr>
|
||||
{
|
||||
let objoid = ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,10040,4,1));
|
||||
let (mut objparams, bstr) = match x {
|
||||
DSAPublic::DSAPublicL1024N160(x) => (x.params.to_asn1_class(c)?, der_encode(x)?),
|
||||
DSAPublic::DSAPublicL2048N224(x) => (x.params.to_asn1_class(c)?, der_encode(x)?),
|
||||
DSAPublic::DSAPublicL2048N256(x) => (x.params.to_asn1_class(c)?, der_encode(x)?),
|
||||
DSAPublic::DSAPublicL3072N256(x) => (x.params.to_asn1_class(c)?, der_encode(x)?)
|
||||
};
|
||||
objparams.insert(0, objoid);
|
||||
let headinfo = ASN1Block::Sequence(c, 0, objparams);
|
||||
let objkey = ASN1Block::BitString(c, 0, bstr.len() * 8, bstr);
|
||||
Ok(ASN1Block::Sequence(c, 0, vec![headinfo, objkey]))
|
||||
}
|
||||
|
||||
fn decode_dsa_key(info: ASN1Block, key: &ASN1Block) -> Result<DSAPublic,X509ParseError>
|
||||
{
|
||||
if let ASN1Block::Sequence(_, _, pqg) = info {
|
||||
if pqg.len() != 3 { return Err(X509ParseError::InvalidDSAInfo); }
|
||||
|
||||
let puint = decode_biguint(&pqg[0])?;
|
||||
let guint = decode_biguint(&pqg[1])?;
|
||||
let quint = decode_biguint(&pqg[2])?;
|
||||
|
||||
if puint.bits() > 2048 {
|
||||
let p = U3072::from_num(&puint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let q = U3072::from_num(&quint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let g = U256::from_num(&guint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let params = L3072N256::new(p, q, g);
|
||||
|
||||
if let ASN1Block::BitString(_, _, _, ybstr) = key {
|
||||
let blocks = from_der(ybstr)?;
|
||||
let (iblk,_) = blocks.split_first().ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
if let ASN1Block::Integer(_,_,ynum) = iblk {
|
||||
let y = U3072::from_num(ynum).ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
let key = DSAPublicKey::<L3072N256>::new(params, y);
|
||||
let reskey = DSAPublic::DSAPublicL3072N256(key);
|
||||
return Ok(reskey);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(X509ParseError::InvalidDSAKey)
|
||||
}
|
||||
|
||||
if puint.bits() > 1024 {
|
||||
if guint.bits() > 224 {
|
||||
let p = U2048::from_num(&puint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let q = U2048::from_num(&quint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let g = U256::from_num(&guint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let params = L2048N256::new(p, q, g);
|
||||
|
||||
if let ASN1Block::BitString(_, _, _, ybstr) = key {
|
||||
let blocks = from_der(ybstr)?;
|
||||
let (iblk,_) = blocks.split_first().ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
if let ASN1Block::Integer(_,_,ynum) = iblk {
|
||||
let y = U2048::from_num(ynum).ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
let key = DSAPublicKey::<L2048N256>::new(params, y);
|
||||
let reskey = DSAPublic::DSAPublicL2048N256(key);
|
||||
return Ok(reskey);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(X509ParseError::InvalidDSAKey)
|
||||
} else {
|
||||
let p = U2048::from_num(&puint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let q = U2048::from_num(&quint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let g = U256::from_num(&guint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let params = L2048N224::new(p, q, g);
|
||||
|
||||
if let ASN1Block::BitString(_, _, _, ybstr) = key {
|
||||
let blocks = from_der(ybstr)?;
|
||||
let (iblk,_) = blocks.split_first().ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
if let ASN1Block::Integer(_,_,ynum) = iblk {
|
||||
let y = U2048::from_num(ynum).ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
let key = DSAPublicKey::<L2048N224>::new(params, y);
|
||||
let reskey = DSAPublic::DSAPublicL2048N224(key);
|
||||
return Ok(reskey);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(X509ParseError::InvalidDSAKey)
|
||||
}
|
||||
}
|
||||
|
||||
let p = U1024::from_num(&puint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let q = U1024::from_num(&quint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let g = U192::from_num(&guint).ok_or(X509ParseError::InvalidDSAInfo)?;
|
||||
let params = L1024N160::new(p, q, g);
|
||||
|
||||
if let ASN1Block::BitString(_, _, _, ybstr) = key {
|
||||
let blocks = from_der(ybstr)?;
|
||||
let (iblk,_) = blocks.split_first().ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
if let ASN1Block::Integer(_,_,ynum) = iblk {
|
||||
let y = U1024::from_num(ynum).ok_or(X509ParseError::InvalidDSAKey)?;
|
||||
let key = DSAPublicKey::<L1024N160>::new(params, y);
|
||||
let reskey = DSAPublic::DSAPublicL1024N160(key);
|
||||
return Ok(reskey);
|
||||
}
|
||||
}
|
||||
|
||||
return Err(X509ParseError::InvalidDSAKey)
|
||||
}
|
||||
|
||||
Err(X509ParseError::InvalidDSAInfo)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// ECDSA Public Key encoding
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
fn encode_ecdsa_key(c: ASN1Class, x: &ECDSAPublic) -> Result<ASN1Block,ECDSAEncodeErr>
|
||||
{
|
||||
let objoid = ASN1Block::ObjectIdentifier(c, 0, oid!(1,2,840,10045,2,1));
|
||||
let (base_curve_oid, mut keyvec) = match x {
|
||||
ECDSAPublic::P192(k) => (oid!(1,2,840,10045,3,1,1), k.to_asn1_class(c)?),
|
||||
ECDSAPublic::P224(k) => (oid!(1,3,132,0,33), k.to_asn1_class(c)?),
|
||||
ECDSAPublic::P256(k) => (oid!(1,2,840,10045,3,1,7), k.to_asn1_class(c)?),
|
||||
ECDSAPublic::P384(k) => (oid!(1,3,132,0,34), k.to_asn1_class(c)?),
|
||||
ECDSAPublic::P521(k) => (oid!(1,3,132,0,35), k.to_asn1_class(c)?),
|
||||
};
|
||||
let curve_oid = ASN1Block::ObjectIdentifier(c, 0, base_curve_oid);
|
||||
let header = ASN1Block::Sequence(c, 0, vec![objoid, curve_oid]);
|
||||
keyvec.insert(0, header);
|
||||
Ok(ASN1Block::Sequence(c, 0, keyvec))
|
||||
}
|
||||
|
||||
fn decode_ecdsa_key(info: ASN1Block, keybls: &[ASN1Block]) -> Result<ECDSAPublic,X509ParseError>
|
||||
{
|
||||
if let ASN1Block::ObjectIdentifier(_, _, oid) = info {
|
||||
if oid == oid!(1,2,840,10045,3,1,1) {
|
||||
let (res, _) = ECCPublicKey::<P192>::from_asn1(keybls)?;
|
||||
return Ok(ECDSAPublic::P192(res));
|
||||
}
|
||||
|
||||
if oid == oid!(1,3,132,0,33) {
|
||||
let (res, _) = ECCPublicKey::<P224>::from_asn1(keybls)?;
|
||||
return Ok(ECDSAPublic::P224(res));
|
||||
}
|
||||
|
||||
if oid == oid!(1,2,840,10045,3,1,7) {
|
||||
let (res, _) = ECCPublicKey::<P256>::from_asn1(keybls)?;
|
||||
return Ok(ECDSAPublic::P256(res));
|
||||
}
|
||||
|
||||
if oid == oid!(1,3,132,0,34) {
|
||||
let (res, _) = ECCPublicKey::<P384>::from_asn1(keybls)?;
|
||||
return Ok(ECDSAPublic::P384(res));
|
||||
}
|
||||
|
||||
if oid == oid!(1,3,132,0,35) {
|
||||
let (res, _) = ECCPublicKey::<P521>::from_asn1(keybls)?;
|
||||
return Ok(ECDSAPublic::P521(res));
|
||||
}
|
||||
}
|
||||
|
||||
Err(X509ParseError::UnknownEllipticCurve)
|
||||
}
|
||||
|
||||
fn strip_algident(block: &ASN1Block)
|
||||
-> Result<(OID, Option<ASN1Block>),X509ParseError>
|
||||
{
|
||||
match block {
|
||||
&ASN1Block::ObjectIdentifier(_, _, ref oid) => {
|
||||
Ok((oid.clone(), None))
|
||||
}
|
||||
&ASN1Block::Sequence(_, _, ref items) => {
|
||||
let (oid, _) = strip_algident(&items[0])?;
|
||||
Ok((oid, Some(items[1].clone())))
|
||||
}
|
||||
_ => Err(X509ParseError::IllFormedAlgoInfo)
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_biguint(b: &ASN1Block) -> Result<BigUint,X509ParseError> {
|
||||
match b {
|
||||
&ASN1Block::Integer(_, _, ref v) => {
|
||||
match v.to_biguint() {
|
||||
Some(sn) => Ok(sn),
|
||||
_ => Err(X509ParseError::InvalidDSAInfo)
|
||||
}
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::InvalidDSAInfo)
|
||||
}
|
||||
}
|
||||
119
src/x509/validity.rs
Normal file
119
src/x509/validity.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
use chrono::{DateTime,Utc};
|
||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,ToASN1};
|
||||
use x509::error::X509ParseError;
|
||||
|
||||
/// The range of dates in which this certificate is valid.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub struct Validity {
|
||||
pub not_before: DateTime<Utc>,
|
||||
pub not_after: DateTime<Utc>
|
||||
}
|
||||
|
||||
fn decode_validity_data(bs: &ASN1Block) -> Result<Validity,X509ParseError> {
|
||||
// Validity ::= SEQUENCE {
|
||||
// notBefore Time,
|
||||
// notAfter Time }
|
||||
match bs {
|
||||
&ASN1Block::Sequence(_, _, ref valxs) => {
|
||||
if valxs.len() != 2 {
|
||||
return Err(X509ParseError::IllFormedValidity);
|
||||
}
|
||||
let nb = get_time(&valxs[0])?;
|
||||
let na = get_time(&valxs[1])?;
|
||||
Ok(Validity{ not_before: nb, not_after: na })
|
||||
}
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedValidity)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromASN1 for Validity {
|
||||
type Error = X509ParseError;
|
||||
|
||||
fn from_asn1(v: &[ASN1Block])
|
||||
-> Result<(Validity,&[ASN1Block]),X509ParseError>
|
||||
{
|
||||
match v.split_first() {
|
||||
None =>
|
||||
Err(X509ParseError::NotEnoughData),
|
||||
Some((x, rest)) => {
|
||||
let v = decode_validity_data(&x)?;
|
||||
Ok((v, rest))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_validity_data(c: ASN1Class, v: &Validity) -> ASN1Block {
|
||||
let mut vs = Vec::with_capacity(2);
|
||||
vs.push(ASN1Block::GeneralizedTime(c, 0, v.not_before));
|
||||
vs.push(ASN1Block::GeneralizedTime(c, 0, v.not_after));
|
||||
ASN1Block::Sequence(c, 0, vs)
|
||||
}
|
||||
|
||||
impl ToASN1 for Validity {
|
||||
type Error = ASN1EncodeErr;
|
||||
|
||||
fn to_asn1_class(&self, c: ASN1Class)
|
||||
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
|
||||
{
|
||||
let block = encode_validity_data(c, self);
|
||||
Ok(vec![block])
|
||||
}
|
||||
}
|
||||
|
||||
fn get_time(b: &ASN1Block) -> Result<DateTime<Utc>, X509ParseError> {
|
||||
match b {
|
||||
&ASN1Block::UTCTime(_, _, v) => Ok(v.clone()),
|
||||
&ASN1Block::GeneralizedTime(_, _, v) => Ok(v.clone()),
|
||||
_ =>
|
||||
Err(X509ParseError::IllFormedValidity)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use chrono::TimeZone;
|
||||
use chrono::offset::LocalResult;
|
||||
use quickcheck::{Arbitrary,Gen};
|
||||
use rand::Rng;
|
||||
use super::*;
|
||||
|
||||
fn arbitrary_date<G: Gen>(g: &mut G) -> DateTime<Utc> {
|
||||
loop {
|
||||
let y = g.gen_range(1900,3000);
|
||||
let mo = g.gen_range(0,12);
|
||||
let d = g.gen_range(0,31);
|
||||
let h = g.gen_range(0,24);
|
||||
let mi = g.gen_range(0,60);
|
||||
let s = g.gen_range(0,60);
|
||||
match Utc.ymd_opt(y,mo,d).and_hms_opt(h,mi,s) {
|
||||
LocalResult::None =>
|
||||
continue,
|
||||
LocalResult::Single(x) =>
|
||||
return x,
|
||||
LocalResult::Ambiguous(x,_) =>
|
||||
return x
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Validity {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> Validity {
|
||||
Validity {
|
||||
not_before: arbitrary_date(g),
|
||||
not_after: arbitrary_date(g)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
fn validity_roundtrips(v: Validity) -> bool {
|
||||
let bstr = encode_validity_data(ASN1Class::Universal, &v);
|
||||
match decode_validity_data(&bstr) {
|
||||
Err(_) => false,
|
||||
Ok(v2) => v == v2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
test-generator/.gitignore
vendored
Normal file
2
test-generator/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
dist/
|
||||
dist-newstyle/
|
||||
84
test-generator/DSA.hs
Normal file
84
test-generator/DSA.hs
Normal file
@@ -0,0 +1,84 @@
|
||||
{-# LANGUAGE PackageImports #-}
|
||||
module DSA(dsaTasks)
|
||||
where
|
||||
|
||||
import Codec.Crypto.DSA.Pure
|
||||
import Crypto.Hash(Digest, SHA256, hash)
|
||||
import "cryptonite" Crypto.Random(DRG(..),getRandomBytes,withDRG)
|
||||
import Data.ByteArray(convert)
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.Lazy(ByteString)
|
||||
import qualified Data.ByteString.Lazy as BSL
|
||||
import qualified Data.Map.Strict as Map
|
||||
import Math(showX,showBin)
|
||||
import Task(Task(..),liftTest)
|
||||
import Utils(HashAlg(..),generateHash,showHash)
|
||||
|
||||
dsaSizes :: [(ParameterSizes, Int)]
|
||||
dsaSizes = [(L1024_N160, 400),
|
||||
(L2048_N224, 100),
|
||||
(L2048_N256, 50),
|
||||
(L3072_N256, 25)]
|
||||
|
||||
dsaTasks :: [Task]
|
||||
dsaTasks = concatMap generateTask dsaSizes
|
||||
|
||||
generateTask :: (ParameterSizes, Int) -> [Task]
|
||||
generateTask (s, c) = [signTest s c]
|
||||
|
||||
showParam :: ParameterSizes -> String
|
||||
showParam L1024_N160 = "L1024N160"
|
||||
showParam L2048_N224 = "L2048N224"
|
||||
showParam L2048_N256 = "L2048N256"
|
||||
showParam L3072_N256 = "L3072N256"
|
||||
|
||||
signTest :: ParameterSizes -> Int -> Task
|
||||
signTest sz cnt = Task {
|
||||
taskName = "DSA " ++ show sz ++ " signing",
|
||||
taskFile = "../testdata/dsa/sign" ++ showParam sz ++ ".test",
|
||||
taskTest = liftTest go,
|
||||
taskCount = cnt
|
||||
}
|
||||
where
|
||||
go (memory, drg0) =
|
||||
case generateProbablePrimes sz drg0 sha256 Nothing of
|
||||
Left _ -> goAdvance memory drg0
|
||||
Right (p, q, _, drg1) ->
|
||||
case generateUnverifiableGenerator p q of
|
||||
Nothing -> goAdvance memory drg1
|
||||
Just g ->
|
||||
let params = Params p g q
|
||||
in case generateKeyPairWithParams params drg1 of
|
||||
Left _ -> goAdvance memory drg1
|
||||
Right (pub, priv, drg2) ->
|
||||
let (msg, drg3) = withDRG drg2 $ getRandomBytes =<< ((fromIntegral . BS.head) `fmap` getRandomBytes 1)
|
||||
(hashf, drg4) = withDRG drg3 generateHash
|
||||
in case signMessage' (translateHash hashf) kViaRFC6979 drg4 priv (BSL.fromStrict msg) of
|
||||
Left _ ->
|
||||
go (memory, drg4)
|
||||
Right (sig, drg5) ->
|
||||
let res = Map.fromList [("p", showX p),
|
||||
("q", showX q),
|
||||
("g", showX g),
|
||||
("y", showX (public_y pub)),
|
||||
("x", showX (private_x priv)),
|
||||
("m", showBin msg),
|
||||
("h", showHash hashf),
|
||||
("r", showX (sign_r sig)),
|
||||
("s", showX (sign_s sig))]
|
||||
in (res, p, (memory, drg5))
|
||||
--
|
||||
goAdvance memory drg0 =
|
||||
let (bstr, drg1) = randomBytesGenerate 37 drg0
|
||||
in BS.null bstr `seq` go (memory, drg1)
|
||||
--
|
||||
translateHash Sha224 = Codec.Crypto.DSA.Pure.SHA224
|
||||
translateHash Sha256 = Codec.Crypto.DSA.Pure.SHA256
|
||||
translateHash Sha384 = Codec.Crypto.DSA.Pure.SHA384
|
||||
translateHash Sha512 = Codec.Crypto.DSA.Pure.SHA512
|
||||
|
||||
sha256 :: ByteString -> ByteString
|
||||
sha256 = BSL.fromStrict . convert' . hash . BSL.toStrict
|
||||
where
|
||||
convert' :: Digest SHA256 -> BS.ByteString
|
||||
convert' = convert
|
||||
50
test-generator/Database.hs
Normal file
50
test-generator/Database.hs
Normal file
@@ -0,0 +1,50 @@
|
||||
{-# LANGUAGE PackageImports #-}
|
||||
module Database(
|
||||
Database,
|
||||
emptyDatabase,
|
||||
generateNum, genSign
|
||||
)
|
||||
where
|
||||
|
||||
import "crypto-api" Crypto.Random(CryptoRandomGen(..),SystemRandom)
|
||||
import "cryptonite" Crypto.Random(DRG(..))
|
||||
import Data.ByteArray(convert)
|
||||
import Data.Bits(shiftL,testBit)
|
||||
import qualified Data.ByteString as S
|
||||
import Data.Map.Strict(Map)
|
||||
import qualified Data.Map.Strict as Map
|
||||
|
||||
type Database = (Map String [Integer], SystemRandom)
|
||||
|
||||
instance DRG SystemRandom where
|
||||
randomBytesGenerate x g =
|
||||
case genBytes x g of
|
||||
Left e -> error ("Data generation error: " ++ show e)
|
||||
Right (res, g') -> (convert res, g')
|
||||
|
||||
emptyDatabase :: SystemRandom -> Database
|
||||
emptyDatabase g0 = (Map.empty, g0)
|
||||
|
||||
generateNum :: Database -> String -> Int -> (Integer, Database)
|
||||
generateNum (db, rng0) varname size =
|
||||
let (x, rng1) = randomBytesGenerate (size `div` 8) rng0
|
||||
x' = integerize x
|
||||
before = Map.findWithDefault [] varname db
|
||||
in if length (filter (== x') before) < 10
|
||||
then (x', (Map.insert varname (x':before) db, rng1))
|
||||
else generateNum (db, rng1) varname size
|
||||
|
||||
genSign :: (Integer, Database) -> (Integer, Database)
|
||||
genSign (x, (db, rng0)) =
|
||||
let (n, rng1) = randomBytesGenerate 1 rng0
|
||||
n' = integerize n
|
||||
in if testBit n' 0 then (0 - x, (db, rng1)) else (x, (db, rng1))
|
||||
|
||||
integerize :: S.ByteString -> Integer
|
||||
integerize = go 0
|
||||
where
|
||||
go acc bstr =
|
||||
case S.uncons bstr of
|
||||
Nothing -> acc
|
||||
Just (v,rest) ->
|
||||
go ((acc `shiftL` 8) + fromIntegral v) rest
|
||||
193
test-generator/ECDSATesting.hs
Normal file
193
test-generator/ECDSATesting.hs
Normal file
@@ -0,0 +1,193 @@
|
||||
{-# LANGUAGE PackageImports #-}
|
||||
module ECDSATesting(
|
||||
ecdsaTasks
|
||||
)
|
||||
where
|
||||
|
||||
import Crypto.Hash(SHA224(..),SHA256(..),SHA384(..),SHA512(..))
|
||||
import Crypto.Number.Generate(generateBetween)
|
||||
import Crypto.PubKey.ECC.ECDSA(PrivateKey(..),PublicKey(..),Signature(..),signWith)
|
||||
import Crypto.PubKey.ECC.Generate(generate)
|
||||
import Crypto.PubKey.ECC.Prim(scalarGenerate,pointAdd,pointNegate,pointDouble,pointBaseMul,pointMul,pointAddTwoMuls)
|
||||
import Crypto.PubKey.ECC.Types(Curve,CurveName(..),Point(..),common_curve,curveSizeBits,ecc_n,getCurveByName)
|
||||
import "cryptonite" Crypto.Random(DRG(..),getRandomBytes,withDRG)
|
||||
import qualified Data.ByteString as S
|
||||
import qualified Data.Map.Strict as Map
|
||||
import Math(showX,showBin)
|
||||
import RFC6979(generateKStream)
|
||||
import Task(Task(..),liftTest)
|
||||
import Utils(HashAlg(..),generateHash,runHash,showHash)
|
||||
|
||||
curves :: [(String, Curve)]
|
||||
curves = [("P192", getCurveByName SEC_p192r1),
|
||||
("P224", getCurveByName SEC_p224r1),
|
||||
("P256", getCurveByName SEC_p256r1),
|
||||
("P384", getCurveByName SEC_p384r1),
|
||||
("P521", getCurveByName SEC_p521r1)]
|
||||
|
||||
negateTest :: String -> Curve -> Task
|
||||
negateTest name curve = Task {
|
||||
taskName = name ++ " point negation",
|
||||
taskFile = "../testdata/ecc/negate/" ++ name ++ ".test",
|
||||
taskTest = liftTest go,
|
||||
taskCount = 1000
|
||||
}
|
||||
where
|
||||
go (memory0, drg) =
|
||||
let (scalar, drg') = withDRG drg (scalarGenerate curve)
|
||||
point = pointBaseMul curve scalar
|
||||
dbl = pointNegate curve point
|
||||
in case (point, dbl) of
|
||||
(PointO, _) -> go (memory0, drg')
|
||||
(_, PointO) -> go (memory0, drg')
|
||||
(Point basex basey, Point dblx dbly) ->
|
||||
let res = Map.fromList [("x", showX basex), ("y", showX basey),
|
||||
("a", showX dblx), ("b", showX dbly)]
|
||||
in (res, scalar, (memory0, drg'))
|
||||
|
||||
doubleTest :: String -> Curve -> Task
|
||||
doubleTest name curve = Task {
|
||||
taskName = name ++ " point doubling",
|
||||
taskFile = "../testdata/ecc/double/" ++ name ++ ".test",
|
||||
taskTest = liftTest go,
|
||||
taskCount = 1000
|
||||
}
|
||||
where
|
||||
go (memory0, drg) =
|
||||
let (scalar, drg') = withDRG drg (scalarGenerate curve)
|
||||
point = pointBaseMul curve scalar
|
||||
dbl = pointDouble curve point
|
||||
in case (point, dbl) of
|
||||
(PointO, _) -> go (memory0, drg')
|
||||
(_, PointO) -> go (memory0, drg')
|
||||
(Point basex basey, Point dblx dbly) ->
|
||||
let res = Map.fromList [("x", showX basex), ("y", showX basey),
|
||||
("a", showX dblx), ("b", showX dbly)]
|
||||
in (res, scalar, (memory0, drg'))
|
||||
|
||||
addTest :: String -> Curve -> Task
|
||||
addTest name curve = Task {
|
||||
taskName = name ++ " point addition",
|
||||
taskFile = "../testdata/ecc/add/" ++ name ++ ".test",
|
||||
taskTest = liftTest go,
|
||||
taskCount = 1000
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
let (scalar1, drg1) = withDRG drg0 (scalarGenerate curve)
|
||||
(scalar2, drg2) = withDRG drg1 (scalarGenerate curve)
|
||||
point1 = pointBaseMul curve scalar1
|
||||
point2 = pointBaseMul curve scalar2
|
||||
pointr = pointAdd curve point1 point2
|
||||
in case (point1, point2, pointr) of
|
||||
(Point x1 y1, Point x2 y2, Point xr yr) ->
|
||||
let res = Map.fromList [("x", showX x1), ("y", showX y1),
|
||||
("u", showX x2), ("v", showX y2),
|
||||
("a", showX xr), ("b", showX yr)]
|
||||
in (res, scalar1, (memory0, drg2))
|
||||
_ ->
|
||||
go (memory0, drg2)
|
||||
|
||||
scaleTest :: String -> Curve -> Task
|
||||
scaleTest name curve = Task {
|
||||
taskName = name ++ " point scaling",
|
||||
taskFile = "../testdata/ecc/scale/" ++ name ++ ".test",
|
||||
taskTest = liftTest go,
|
||||
taskCount = 1000
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
let (scalar0, drg1) = withDRG drg0 (scalarGenerate curve)
|
||||
(scalar1, drg2) = withDRG drg1 (scalarGenerate curve)
|
||||
(negbs, drg3) = randomBytesGenerate 1 drg2
|
||||
[negbyte] = S.unpack negbs
|
||||
k = if odd negbyte then scalar1 else -scalar1
|
||||
point = pointBaseMul curve scalar0
|
||||
respnt = pointMul curve k point
|
||||
in case (point, respnt) of
|
||||
(PointO, _) -> go (memory0, drg3)
|
||||
(_, PointO) -> go (memory0, drg3)
|
||||
(Point basex basey, Point resx resy) ->
|
||||
let res = Map.fromList [("x", showX basex), ("y", showX basey),
|
||||
("k", showX k),
|
||||
("a", showX resx), ("b", showX resy)]
|
||||
in (res, scalar0, (memory0, drg3))
|
||||
|
||||
addScaleTest :: String -> Curve -> Task
|
||||
addScaleTest name curve = Task {
|
||||
taskName = name ++ " point addition of two scalings",
|
||||
taskFile = "../testdata/ecc/add_scale2/" ++ name ++ ".test",
|
||||
taskTest = liftTest go,
|
||||
taskCount = 1000
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
let (scalar1, drg1) = withDRG drg0 (scalarGenerate curve)
|
||||
(scalar2, drg2) = withDRG drg1 (scalarGenerate curve)
|
||||
(n, drg3) = withDRG drg2 (scalarGenerate curve)
|
||||
(m, drg4) = withDRG drg3 (scalarGenerate curve)
|
||||
point1 = pointBaseMul curve scalar1
|
||||
point2 = pointBaseMul curve scalar2
|
||||
pointr = pointAddTwoMuls curve n point1 m point2
|
||||
in case (point1, point2, pointr) of
|
||||
(Point x1 y1, Point x2 y2, Point xr yr) ->
|
||||
let res = Map.fromList [("x", showX x1), ("y", showX y1),
|
||||
("p", showX x2), ("q", showX y2),
|
||||
("n", showX n), ("m", showX m),
|
||||
("r", showX xr), ("s", showX yr)]
|
||||
in (res, scalar1, (memory0, drg4))
|
||||
_ ->
|
||||
go (memory0, drg4)
|
||||
|
||||
|
||||
signTest :: String -> Curve -> Task
|
||||
signTest name curve = Task {
|
||||
taskName = name ++ " curve signing",
|
||||
taskFile = "../testdata/ecc/sign/" ++ name ++ ".test",
|
||||
taskTest = liftTest go,
|
||||
taskCount = 1000
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
let ((pub, priv), drg1) = withDRG drg0 (generate curve)
|
||||
(msg, drg2) = withDRG drg1 $ do x <- generateBetween 0 256
|
||||
getRandomBytes (fromIntegral x)
|
||||
(hash, drg3) = withDRG drg2 generateHash
|
||||
n = ecc_n (common_curve curve)
|
||||
PrivateKey _ d = priv
|
||||
kStream = generateKStream hash msg d n (curveSizeBits curve)
|
||||
findGoodK stream =
|
||||
case stream of
|
||||
[] ->
|
||||
go (memory0, drg3)
|
||||
(k : restks) ->
|
||||
case signWith' k priv hash msg of
|
||||
Nothing ->
|
||||
findGoodK restks
|
||||
Just sig ->
|
||||
let PublicKey _ (Point x y) = pub
|
||||
res = Map.fromList [("d", showX d), ("k", showX k),
|
||||
("x", showX x), ("y", showX y),
|
||||
("m", showBin msg), ("h", showHash hash),
|
||||
("n", showBin (runHash hash msg)),
|
||||
("r", showX (sign_r sig)),
|
||||
("s", showX (sign_s sig))]
|
||||
in (res, d, (memory0, drg3))
|
||||
in findGoodK kStream
|
||||
|
||||
signWith' :: Integer -> PrivateKey -> HashAlg -> S.ByteString -> Maybe Signature
|
||||
signWith' k priv Sha224 msg = signWith k priv SHA224 msg
|
||||
signWith' k priv Sha256 msg = signWith k priv SHA256 msg
|
||||
signWith' k priv Sha384 msg = signWith k priv SHA384 msg
|
||||
signWith' k priv Sha512 msg = signWith k priv SHA512 msg
|
||||
|
||||
generateTasks :: (String, Curve) -> [Task]
|
||||
generateTasks (name, curve) = [negateTest name curve,
|
||||
doubleTest name curve,
|
||||
addTest name curve,
|
||||
scaleTest name curve,
|
||||
addScaleTest name curve,
|
||||
signTest name curve]
|
||||
|
||||
ecdsaTasks :: [Task]
|
||||
ecdsaTasks = concatMap generateTasks curves
|
||||
735
test-generator/ED25519.hs
Normal file
735
test-generator/ED25519.hs
Normal file
@@ -0,0 +1,735 @@
|
||||
{-# LANGUAGE PackageImports #-}
|
||||
module ED25519(ed25519Tasks)
|
||||
where
|
||||
|
||||
import Control.Monad(unless)
|
||||
import Crypto.Error(CryptoFailable(CryptoPassed))
|
||||
import "crypto-api" Crypto.Random(SystemRandom)
|
||||
import "cryptonite" Crypto.Random(getRandomBytes,withDRG)
|
||||
import Crypto.PubKey.Ed25519
|
||||
import Data.ByteArray(convert)
|
||||
import Data.ByteString(ByteString,pack,useAsCString)
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.Int(Int32)
|
||||
import qualified Data.Map.Strict as Map
|
||||
import Data.Word(Word8,Word32,Word64)
|
||||
import ED25519.PrecompPoints
|
||||
import Foreign.C.Types(CChar)
|
||||
import Foreign.Marshal.Alloc(alloca)
|
||||
import Foreign.Marshal.Array(allocaArray,peekArray,pokeArray)
|
||||
import Foreign.Ptr(Ptr,castPtr)
|
||||
import Foreign.Storable(Storable(..))
|
||||
import Math(showX,showBin)
|
||||
import Task(Task(..))
|
||||
|
||||
cTEST_COUNT :: Int
|
||||
cTEST_COUNT = 1000
|
||||
|
||||
ed25519Tasks :: [Task]
|
||||
ed25519Tasks = [ loadTests, byteTests, addsubTests, mulTests,
|
||||
squaringTests, inversionTests, negateTests,
|
||||
cmovTests, isTests, square2Tests,
|
||||
pow22523Tests, fbvTests, conversionTests,
|
||||
ptDoubleTests, maddsubTests, ptAddSubTests,
|
||||
scalarMultBaseTests, slideTests, scalarMultTests,
|
||||
reduceTests, muladdTests, pubPrivTests,
|
||||
signTest ]
|
||||
|
||||
loadTests :: Task
|
||||
loadTests = Task {
|
||||
taskName = "ed25519 byte loading",
|
||||
taskFile = "../testdata/ed25519/load.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
do let (bytes, drg1) = withDRG drg0 (getRandomBytes 4)
|
||||
res3 <- useAsCString bytes (\ptr -> load_3 ptr)
|
||||
res4 <- useAsCString bytes (\ptr -> load_4 ptr)
|
||||
let res = Map.fromList [("x", showBin bytes), ("a", showX res3), ("b", showX res4)]
|
||||
return (res, fromIntegral res4, (memory0, drg1))
|
||||
|
||||
byteTests :: Task
|
||||
byteTests = Task {
|
||||
taskName = "ed25519 byte / element conversion",
|
||||
taskFile = "../testdata/ed25519/bytes.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomPackedBytes drg0 $ \ ptra drg1 ->
|
||||
alloca $ \ ptrc ->
|
||||
allocaArray 32 $ \ rptr ->
|
||||
do clearSpace ptrc
|
||||
pokeArray (rptr :: Ptr Word8) (replicate 32 0)
|
||||
fe_frombytes ptrc ptra
|
||||
b <- convertFE ptrc
|
||||
fe_tobytes (castPtr rptr) ptrc
|
||||
start <- peek ptra
|
||||
end <- peek (castPtr rptr)
|
||||
unless (start == end) $
|
||||
fail "field element tobytes/frombytes doesn't round trip"
|
||||
bytes' <- pack `fmap` peekArray 32 (castPtr ptra :: Ptr Word8)
|
||||
let res = Map.fromList [("a", showBin bytes'),
|
||||
("b", showBin b)]
|
||||
return (res, toNumber b, (memory0, drg1))
|
||||
|
||||
addsubTests :: Task
|
||||
addsubTests = Task {
|
||||
taskName = "ed25519 addition/subtraction tests",
|
||||
taskFile = "../testdata/ed25519/addsub.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomElement drg0 $ \ ptrel1 drg1 ->
|
||||
randomElement drg1 $ \ ptrel2 drg2 ->
|
||||
alloca $ \ ptrc ->
|
||||
alloca $ \ ptrd ->
|
||||
do fe_add ptrc ptrel1 ptrel2
|
||||
fe_sub ptrd ptrel1 ptrel2
|
||||
[a, b, c, d] <- mapM convertFE [ptrel1, ptrel2, ptrc, ptrd]
|
||||
let res = Map.fromList [("a", showBin a),
|
||||
("b", showBin b),
|
||||
("c", showBin c),
|
||||
("d", showBin d)]
|
||||
return (res, toNumber c, (memory0, drg2))
|
||||
|
||||
mulTests :: Task
|
||||
mulTests = Task {
|
||||
taskName = "ed25519 multiplication tests",
|
||||
taskFile = "../testdata/ed25519/mul.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomElement drg0 $ \ ptrel1 drg1 ->
|
||||
randomElement drg1 $ \ ptrel2 drg2 ->
|
||||
alloca $ \ ptrc ->
|
||||
do fe_mul ptrc ptrel1 ptrel2
|
||||
[a, b, c] <- mapM convertFE [ptrel1, ptrel2, ptrc]
|
||||
let res = Map.fromList [("a", showBin a),
|
||||
("b", showBin b),
|
||||
("c", showBin c)]
|
||||
return (res, toNumber c, (memory0, drg2))
|
||||
|
||||
squaringTests :: Task
|
||||
squaringTests = Task {
|
||||
taskName = "ed25519 squaring tests",
|
||||
taskFile = "../testdata/ed25519/square.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomElement drg0 $ \ ptrel drg1 ->
|
||||
alloca $ \ ptrc ->
|
||||
do fe_square ptrc ptrel
|
||||
[a, c] <- mapM convertFE [ptrel, ptrc]
|
||||
let res = Map.fromList [("a", showBin a),
|
||||
("c", showBin c)]
|
||||
return (res, toNumber c, (memory0, drg1))
|
||||
|
||||
inversionTests :: Task
|
||||
inversionTests = Task {
|
||||
taskName = "ed25519 inversion tests",
|
||||
taskFile = "../testdata/ed25519/invert.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomElement drg0 $ \ ptrel drg1 ->
|
||||
alloca $ \ ptrc ->
|
||||
do fe_invert ptrc ptrel
|
||||
a <- convertFE ptrel
|
||||
c <- convertFE ptrc
|
||||
let res = Map.fromList [("a", showBin a),
|
||||
("c", showBin c)]
|
||||
return (res, toNumber a, (memory0, drg1))
|
||||
|
||||
negateTests :: Task
|
||||
negateTests = Task {
|
||||
taskName = "ed25519 negation tests",
|
||||
taskFile = "../testdata/ed25519/negate.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomElement drg0 $ \ ptrel drg1 ->
|
||||
alloca $ \ ptrc ->
|
||||
do fe_negate ptrc ptrel
|
||||
a <- convertFE ptrel
|
||||
c <- convertFE ptrc
|
||||
let res = Map.fromList [("a", showBin a),
|
||||
("c", showBin c)]
|
||||
return (res, toNumber a, (memory0, drg1))
|
||||
|
||||
cmovTests :: Task
|
||||
cmovTests = Task {
|
||||
taskName = "ed25519 conditional mov tests",
|
||||
taskFile = "../testdata/ed25519/cmov.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomElement drg0 $ \ aelptr drg1 ->
|
||||
do let (bbytes, drg2) = withDRG drg1 (getRandomBytes 1)
|
||||
b = even (BS.head bbytes)
|
||||
bvalLib = if b then 0 else 1
|
||||
bvalOut = if b then 0 else 0xFFFFFF :: Word32
|
||||
alloca $ \ celptr ->
|
||||
do clearSpace celptr
|
||||
fe_cmov celptr aelptr bvalLib
|
||||
a <- convertFE aelptr
|
||||
c <- convertFE celptr
|
||||
let res = Map.fromList [("a", showBin a),
|
||||
("b", showX bvalOut),
|
||||
("c", showBin c)]
|
||||
return (res, toNumber a, (memory0, drg2))
|
||||
|
||||
isTests :: Task
|
||||
isTests = Task {
|
||||
taskName = "ed25519 predicate tests",
|
||||
taskFile = "../testdata/ed25519/istests.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomElement drg0 $ \ aptr drg1 ->
|
||||
do a <- convertFE aptr
|
||||
z <- fe_isnonzero aptr
|
||||
n <- fe_isnegative aptr
|
||||
let res = Map.fromList [("a", showBin a),
|
||||
("z", showX (if z == 0 then 0 :: Word32 else 0xFFFFFF)),
|
||||
("n", showX (if n == 0 then 0 :: Word32 else 0xFFFFFF))]
|
||||
return (res, toNumber a, (memory0, drg1))
|
||||
|
||||
square2Tests :: Task
|
||||
square2Tests = Task {
|
||||
taskName = "ed25519 square2 tests",
|
||||
taskFile = "../testdata/ed25519/square2.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomElement drg0 $ \ aptr drg1 ->
|
||||
alloca $ \ cptr ->
|
||||
do clearSpace cptr
|
||||
fe_square2 cptr aptr
|
||||
a <- convertFE aptr
|
||||
c <- convertFE cptr
|
||||
let res = Map.fromList [("a", showBin a), ("c", showBin c)]
|
||||
return (res, toNumber a, (memory0, drg1))
|
||||
|
||||
pow22523Tests :: Task
|
||||
pow22523Tests = Task {
|
||||
taskName = "ed25519 pow22523 tests",
|
||||
taskFile = "../testdata/ed25519/pow22523.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomElement drg0 $ \ aptr drg1 ->
|
||||
alloca $ \ cptr ->
|
||||
do clearSpace cptr
|
||||
fe_pow22523 cptr aptr
|
||||
a <- convertFE aptr
|
||||
c <- convertFE cptr
|
||||
let res = Map.fromList [("a", showBin a), ("c", showBin c)]
|
||||
return (res, toNumber a, (memory0, drg1))
|
||||
|
||||
fbvTests :: Task
|
||||
fbvTests = Task {
|
||||
taskName = "ed25519 from bytes (vartime) tests",
|
||||
taskFile = "../testdata/ed25519/fbv.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
do let (abytes, drg1) = withDRG drg0 (getRandomBytes 32)
|
||||
useAsCString abytes $ \ aptr ->
|
||||
do let aptr' = castPtr aptr :: Ptr PackedBytes
|
||||
curve25519_scalar_mask aptr'
|
||||
alloca $ \ dest ->
|
||||
do clearSpace dest
|
||||
ok <- point_frombytes dest aptr'
|
||||
a <- pack `fmap` peekArray 32 (castPtr aptr)
|
||||
c <- pack `fmap` peekArray (4 * 10 * 4) (castPtr dest)
|
||||
let c' | ok = c
|
||||
| otherwise = BS.empty
|
||||
let res = Map.fromList [("a", showBin a),
|
||||
("b", showBin c'),
|
||||
("c", showBin c)]
|
||||
return (res, if ok then (toNumber abytes) else 0, (memory0, drg1))
|
||||
|
||||
conversionTests :: Task
|
||||
conversionTests = Task {
|
||||
taskName = "ed25519 point form conversion tests",
|
||||
taskFile = "../testdata/ed25519/conversion.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomPoint3 drg0 $ \ ptr3 drg' ->
|
||||
alloca $ \ ptrCached ->
|
||||
alloca $ \ ptr2 ->
|
||||
alloca $ \ ptrP1P1 ->
|
||||
alloca $ \ ptr2' ->
|
||||
alloca $ \ ptr3' ->
|
||||
do clearSpace ptrCached
|
||||
clearSpace ptr2
|
||||
clearSpace ptrP1P1
|
||||
clearSpace ptr2'
|
||||
clearSpace ptr3'
|
||||
p3_to_cached ptrCached ptr3
|
||||
ge_p3_to_p2 ptr2 ptr3
|
||||
ge_p3_dbl ptrP1P1 ptr3
|
||||
p1p1_to_p2 ptr2' ptrP1P1
|
||||
p1p1_to_p3 ptr3' ptrP1P1
|
||||
a <- convertPoint ptr3
|
||||
c <- convertPoint ptrCached
|
||||
t <- convertPoint ptr2
|
||||
o <- convertPoint ptrP1P1
|
||||
d <- convertPoint ptr2'
|
||||
b <- convertPoint ptr3'
|
||||
let res = Map.fromList [("a", showBin a), ("c", showBin c),
|
||||
("t", showBin t), ("o", showBin o),
|
||||
("d", showBin d), ("b", showBin b)]
|
||||
return (res, toNumber a, (memory0, drg'))
|
||||
|
||||
ptDoubleTests :: Task
|
||||
ptDoubleTests = Task {
|
||||
taskName = "ed25519 point doubling tests",
|
||||
taskFile = "../testdata/ed25519/pt_double.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomPoint3 drg0 $ \ ptra drg1 ->
|
||||
randomPoint2 drg1 $ \ ptrc drg2 ->
|
||||
alloca $ \ ptrb ->
|
||||
alloca $ \ ptrd ->
|
||||
do clearSpace ptrb
|
||||
clearSpace ptrd
|
||||
ge_p3_dbl ptrb ptra
|
||||
ge_p2_dbl ptrd ptrc
|
||||
a <- convertPoint ptra
|
||||
b <- convertPoint ptrb
|
||||
c <- convertPoint ptrc
|
||||
d <- convertPoint ptrd
|
||||
let res = Map.fromList [("a", showBin a), ("b", showBin b),
|
||||
("c", showBin c), ("d", showBin d)]
|
||||
return (res, toNumber a, (memory0, drg2))
|
||||
|
||||
maddsubTests :: Task
|
||||
maddsubTests = Task {
|
||||
taskName = "ed25519 point madd/msub tests",
|
||||
taskFile = "../testdata/ed25519/maddsub.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomPoint3 drg0 $ \ ptra drg1 ->
|
||||
randomPointPrecomp drg1 $ \ ptrc drg2 ->
|
||||
alloca $ \ ptrb ->
|
||||
alloca $ \ ptrd ->
|
||||
do clearSpace ptrb
|
||||
clearSpace ptrd
|
||||
ge_madd ptrb ptra ptrc
|
||||
ge_msub ptrd ptra ptrc
|
||||
a <- convertPoint ptra
|
||||
b <- convertPoint ptrb
|
||||
c <- convertPoint ptrc
|
||||
d <- convertPoint ptrd
|
||||
let res = Map.fromList [("a", showBin a), ("b", showBin b),
|
||||
("c", showBin c), ("d", showBin d)]
|
||||
return (res, toNumber a, (memory0, drg2))
|
||||
|
||||
ptAddSubTests :: Task
|
||||
ptAddSubTests = Task {
|
||||
taskName = "ed25519 point add/sub tests",
|
||||
taskFile = "../testdata/ed25519/ptaddsub.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomPoint3 drg0 $ \ ptra drg1 ->
|
||||
randomPointCached drg1 $ \ ptrc drg2 ->
|
||||
alloca $ \ ptrb ->
|
||||
alloca $ \ ptrd ->
|
||||
do clearSpace ptrb
|
||||
clearSpace ptrd
|
||||
ge_add ptrb ptra ptrc
|
||||
ge_sub ptrd ptra ptrc
|
||||
a <- convertPoint ptra
|
||||
b <- convertPoint ptrb
|
||||
c <- convertPoint ptrc
|
||||
d <- convertPoint ptrd
|
||||
let res = Map.fromList [("a", showBin a), ("b", showBin b),
|
||||
("c", showBin c), ("d", showBin d)]
|
||||
return (res, toNumber a, (memory0, drg2))
|
||||
|
||||
scalarMultBaseTests :: Task
|
||||
scalarMultBaseTests = Task {
|
||||
taskName = "ed25519 point add/sub tests",
|
||||
taskFile = "../testdata/ed25519/scalar_mult.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomPackedBytes drg0 $ \ ptra drg1 ->
|
||||
alloca $ \ ptrb ->
|
||||
do clearSpace ptrb
|
||||
x25519_ge_scalarmult_base ptrb ptra
|
||||
PB abytes <- peek ptra
|
||||
let a = pack abytes
|
||||
b <- convertPoint ptrb
|
||||
let res = Map.fromList [("a", showBin a), ("b", showBin b)]
|
||||
return (res, toNumber a, (memory0, drg1))
|
||||
|
||||
slideTests :: Task
|
||||
slideTests = Task {
|
||||
taskName = "ed25519 slide helper function tests",
|
||||
taskFile = "../testdata/ed25519/slide.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomPackedBytes drg0 $ \ ptra drg1 ->
|
||||
allocaArray 256 $ \ ptrb ->
|
||||
do pokeArray ptrb (replicate 256 0)
|
||||
slide ptrb ptra
|
||||
a <- pack `fmap` peekArray 32 (castPtr ptra)
|
||||
b <- pack `fmap` peekArray 356 ptrb
|
||||
let res = Map.fromList [("a", showBin a), ("b", showBin b)]
|
||||
return (res, toNumber a, (memory0, drg1))
|
||||
|
||||
scalarMultTests :: Task
|
||||
scalarMultTests = Task {
|
||||
taskName = "ed25519 point general scalar multiplication tests",
|
||||
taskFile = "../testdata/ed25519/scalar_mult_gen.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomPackedBytes drg0 $ \ ptra drg1 ->
|
||||
randomPoint3 drg1 $ \ ptrb drg2 ->
|
||||
randomPackedBytes drg2 $ \ ptrc drg3 ->
|
||||
alloca $ \ ptrd ->
|
||||
do clearSpace ptrd
|
||||
ge_double_scalarmult_vartime ptrd ptra ptrb ptrc
|
||||
PB abytes <- peek ptra
|
||||
let a = pack abytes
|
||||
b <- convertPoint ptrb
|
||||
PB cbytes <- peek ptrc
|
||||
let c = pack cbytes
|
||||
d <- convertPoint ptrd
|
||||
let res = Map.fromList [("a", showBin a), ("b", showBin b),
|
||||
("c", showBin c), ("d", showBin d)]
|
||||
return (res, toNumber a, (memory0, drg3))
|
||||
|
||||
reduceTests :: Task
|
||||
reduceTests = Task {
|
||||
taskName = "ed25519 reduce tests",
|
||||
taskFile = "../testdata/ed25519/reduce.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
do let (a, drg1) = withDRG drg0 (getRandomBytes 64)
|
||||
allocaArray 64 $ \ target ->
|
||||
do pokeArray target (BS.unpack a)
|
||||
sc_reduce target
|
||||
b <- pack `fmap` peekArray 32 target
|
||||
let res = Map.fromList [("a", showBin a), ("b", showBin b)]
|
||||
return (res, toNumber a, (memory0, drg1))
|
||||
|
||||
muladdTests :: Task
|
||||
muladdTests = Task {
|
||||
taskName = "ed25519 multiplication+addition tests",
|
||||
taskFile = "../testdata/ed25519/muladd.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomPackedBytes drg0 $ \ ptra drg1 ->
|
||||
randomPackedBytes drg1 $ \ ptrb drg2 ->
|
||||
randomPackedBytes drg2 $ \ ptrc drg3 ->
|
||||
alloca $ \ ptrd ->
|
||||
do clearSpace ptrd
|
||||
sc_muladd ptrd ptra ptrb ptrc
|
||||
a <- repackBytes ptra
|
||||
b <- repackBytes ptrb
|
||||
c <- repackBytes ptrc
|
||||
d <- repackBytes ptrd
|
||||
let res = Map.fromList [("a", showBin a), ("b", showBin b),
|
||||
("c", showBin c), ("d", showBin d)]
|
||||
return (res, toNumber a, (memory0, drg3))
|
||||
|
||||
pubPrivTests :: Task
|
||||
pubPrivTests = Task {
|
||||
taskName = "ed25519 private -> public conversion tests",
|
||||
taskFile = "../testdata/ed25519/pubfrompriv.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
randomPackedBytes drg0 $ \ ptra drg1 ->
|
||||
alloca $ \ ptrb ->
|
||||
do clearSpace ptrb
|
||||
public_from_private ptrb ptra
|
||||
a <- repackBytes ptra
|
||||
b <- repackBytes ptrb
|
||||
let res = Map.fromList [("a", showBin a), ("b", showBin b)]
|
||||
return (res, toNumber a, (memory0, drg1))
|
||||
|
||||
signTest :: Task
|
||||
signTest = Task {
|
||||
taskName = "ed25519 signing tests",
|
||||
taskFile = "../testdata/ed25519/sign.test",
|
||||
taskTest = go,
|
||||
taskCount = cTEST_COUNT
|
||||
}
|
||||
where
|
||||
go (memory0, drg0) =
|
||||
let (priv, drg1) = withDRG drg0 generateSecretKey
|
||||
(msg, drg2) = withDRG drg1 $ getRandomBytes =<< ((fromIntegral . BS.head) `fmap` getRandomBytes 1)
|
||||
pub = toPublic priv
|
||||
privBytes = convert priv
|
||||
pubBytes = convert pub
|
||||
sig = convert (sign priv pub msg)
|
||||
res = Map.fromList [("u", showBin pubBytes), ("r", showBin privBytes),
|
||||
("m", showBin msg), ("s", showBin sig)]
|
||||
in return (res, toNumber privBytes, (memory0, drg2))
|
||||
|
||||
|
||||
data PackedBytes = PB [Word8]
|
||||
deriving (Eq)
|
||||
|
||||
instance Storable PackedBytes where
|
||||
sizeOf _ = 32
|
||||
alignment _ = 8
|
||||
peek p = PB `fmap` peekArray 32 (castPtr p)
|
||||
poke p (PB v) = pokeArray (castPtr p) v
|
||||
|
||||
randomPackedBytes :: SystemRandom -> (Ptr PackedBytes -> SystemRandom -> IO a) -> IO a
|
||||
randomPackedBytes drg action =
|
||||
do let (bytes, drg') = withDRG drg (getRandomBytes 32)
|
||||
useAsCString bytes $ \ ptr ->
|
||||
do let ptr' = castPtr ptr :: Ptr PackedBytes
|
||||
curve25519_scalar_mask ptr'
|
||||
action ptr' drg'
|
||||
|
||||
repackBytes :: Ptr PackedBytes -> IO ByteString
|
||||
repackBytes ptr =
|
||||
do PB xs <- peek ptr
|
||||
return (pack xs)
|
||||
|
||||
data Element = FE [Int32]
|
||||
|
||||
instance Storable Element where
|
||||
sizeOf _ = 10 * sizeOf (undefined :: Int32)
|
||||
alignment _ = 8
|
||||
peek p = FE `fmap` peekArray 10 (castPtr p)
|
||||
poke p (FE v) = pokeArray (castPtr p) v
|
||||
|
||||
randomElement :: SystemRandom -> (Ptr Element -> SystemRandom -> IO a) -> IO a
|
||||
randomElement drg action =
|
||||
randomPackedBytes drg $ \ ptrpb drg' -> alloca $ \ ptrel ->
|
||||
do clearSpace ptrel
|
||||
fe_frombytes ptrel ptrpb
|
||||
action ptrel drg'
|
||||
|
||||
data Point3 = P3 [Element]
|
||||
|
||||
instance Storable Point3 where
|
||||
sizeOf _ = 4 * sizeOf (undefined :: Element)
|
||||
alignment _ = 8
|
||||
peek p = P3 `fmap` peekArray 4 (castPtr p)
|
||||
poke p (P3 v) = pokeArray (castPtr p) v
|
||||
|
||||
randomPoint3 :: SystemRandom -> (Ptr Point3 -> SystemRandom -> IO a) -> IO a
|
||||
randomPoint3 drg0 action = allocaArray (4 * 10) (go drg0)
|
||||
where
|
||||
go drg dest =
|
||||
do mres <- randomPackedBytes drg $ \ aptr drg' ->
|
||||
do clearSpace dest
|
||||
worked <- point_frombytes dest aptr
|
||||
if worked
|
||||
then Right `fmap` action (castPtr dest) drg'
|
||||
else return (Left drg')
|
||||
case mres of
|
||||
Right x -> return x
|
||||
Left drg' -> go drg' dest
|
||||
|
||||
data PointCached = PC [Element]
|
||||
|
||||
instance Storable PointCached where
|
||||
sizeOf _ = 4 * sizeOf (undefined :: Element)
|
||||
alignment _ = 8
|
||||
peek p = PC `fmap` peekArray 4 (castPtr p)
|
||||
poke p (PC v) = pokeArray (castPtr p) v
|
||||
|
||||
randomPointCached :: SystemRandom -> (Ptr PointCached -> SystemRandom -> IO a) -> IO a
|
||||
randomPointCached drg action =
|
||||
randomPoint3 drg $ \ ptr drg' ->
|
||||
allocaArray (4 * 10) $ \ dest ->
|
||||
do pokeArray (castPtr dest :: Ptr Int32) (replicate (4 * 10) 0)
|
||||
p3_to_cached dest ptr
|
||||
action (castPtr dest) drg'
|
||||
|
||||
data Point2 = P2 [Element]
|
||||
|
||||
instance Storable Point2 where
|
||||
sizeOf _ = 3 * sizeOf (undefined :: Element)
|
||||
alignment _ = 8
|
||||
peek p = P2 `fmap` peekArray 3 (castPtr p)
|
||||
poke p (P2 v) = pokeArray (castPtr p) v
|
||||
|
||||
randomPoint2 :: SystemRandom -> (Ptr Point2 -> SystemRandom -> IO a) -> IO a
|
||||
randomPoint2 drg action =
|
||||
randomPoint3 drg $ \ ptr3 drg' ->
|
||||
allocaArray (3 * 10) $ \ dest ->
|
||||
do pokeArray (castPtr dest :: Ptr Int32) (replicate (3 * 10) 0)
|
||||
ge_p3_to_p2 dest ptr3
|
||||
action (castPtr dest) drg'
|
||||
|
||||
data PointP1P1 = P1P1 [Element]
|
||||
|
||||
instance Storable PointP1P1 where
|
||||
sizeOf _ = 4 * sizeOf (undefined :: Element)
|
||||
alignment _ = 8
|
||||
peek p = P1P1 `fmap` peekArray 4 (castPtr p)
|
||||
poke p (P1P1 v) = pokeArray (castPtr p) v
|
||||
|
||||
_randomPointP1P1 :: SystemRandom -> (Ptr PointP1P1 -> SystemRandom -> IO a) -> IO a
|
||||
_randomPointP1P1 drg action =
|
||||
randomPoint3 drg $ \ ptr3 drg' ->
|
||||
allocaArray (4 * 10) $ \ dest ->
|
||||
do pokeArray (castPtr dest :: Ptr Int32) (replicate (4 * 10) 0)
|
||||
ge_p3_dbl dest ptr3
|
||||
action (castPtr dest) drg'
|
||||
|
||||
data PointPrecomp = PP [Element]
|
||||
|
||||
instance Storable PointPrecomp where
|
||||
sizeOf _ = 4 * sizeOf (undefined :: Element)
|
||||
alignment _ = 8
|
||||
peek p = PP `fmap` peekArray 4 (castPtr p)
|
||||
poke p (PP v) = pokeArray (castPtr p) v
|
||||
|
||||
randomPointPrecomp :: SystemRandom -> (Ptr PointPrecomp -> SystemRandom -> IO a) -> IO a
|
||||
randomPointPrecomp drg action =
|
||||
do let ([a,b,c,d], drg') = withDRG drg (BS.unpack `fmap` getRandomBytes 4)
|
||||
mix = fromIntegral a + fromIntegral b + fromIntegral c + fromIntegral d
|
||||
idx = mix `mod` (length precompPoints)
|
||||
val = PP (map FE (precompPoints !! idx))
|
||||
alloca $ \ ptr ->
|
||||
do poke ptr val
|
||||
action ptr drg'
|
||||
|
||||
clearSpace :: Storable a => Ptr a -> IO ()
|
||||
clearSpace x = meh x undefined
|
||||
where
|
||||
meh :: Storable a => Ptr a -> a -> IO ()
|
||||
meh p v = pokeArray (castPtr p) (replicate (sizeOf v) (0 :: Word8))
|
||||
|
||||
convertFE :: Ptr Element -> IO ByteString
|
||||
convertFE feptr = pack `fmap` peekArray 40 (castPtr feptr :: Ptr Word8)
|
||||
|
||||
convertPoint :: Storable a => Ptr a -> IO ByteString
|
||||
convertPoint x = meh x undefined
|
||||
where
|
||||
meh :: Storable a => Ptr a -> a -> IO ByteString
|
||||
meh p v = pack `fmap` peekArray (sizeOf v) (castPtr p)
|
||||
|
||||
toNumber :: ByteString -> Integer
|
||||
toNumber = BS.foldr (\ x a -> fromIntegral x + a) 0
|
||||
|
||||
foreign import ccall unsafe "load_3"
|
||||
load_3 :: Ptr CChar -> IO Word64
|
||||
foreign import ccall unsafe "load_4"
|
||||
load_4 :: Ptr CChar -> IO Word64
|
||||
foreign import ccall unsafe "GFp_curve25519_scalar_mask"
|
||||
curve25519_scalar_mask :: Ptr PackedBytes -> IO ()
|
||||
foreign import ccall unsafe "fe_frombytes"
|
||||
fe_frombytes :: Ptr Element -> Ptr PackedBytes -> IO ()
|
||||
foreign import ccall unsafe "GFp_fe_tobytes"
|
||||
fe_tobytes :: Ptr PackedBytes -> Ptr Element -> IO ()
|
||||
foreign import ccall unsafe "fe_add"
|
||||
fe_add :: Ptr Element -> Ptr Element -> Ptr Element -> IO ()
|
||||
foreign import ccall unsafe "fe_sub"
|
||||
fe_sub :: Ptr Element -> Ptr Element -> Ptr Element -> IO ()
|
||||
foreign import ccall unsafe "GFp_fe_mul"
|
||||
fe_mul :: Ptr Element -> Ptr Element -> Ptr Element -> IO ()
|
||||
foreign import ccall unsafe "fe_sq"
|
||||
fe_square :: Ptr Element -> Ptr Element -> IO ()
|
||||
foreign import ccall unsafe "GFp_fe_invert"
|
||||
fe_invert :: Ptr Element -> Ptr Element -> IO ()
|
||||
foreign import ccall unsafe "fe_neg"
|
||||
fe_negate :: Ptr Element -> Ptr Element -> IO ()
|
||||
foreign import ccall unsafe "fe_cmov"
|
||||
fe_cmov :: Ptr Element -> Ptr Element -> Word32 -> IO ()
|
||||
foreign import ccall unsafe "fe_isnonzero"
|
||||
fe_isnonzero :: Ptr Element -> IO Int32
|
||||
foreign import ccall unsafe "GFp_fe_isnegative"
|
||||
fe_isnegative :: Ptr Element -> IO Word8
|
||||
foreign import ccall unsafe "fe_sq2"
|
||||
fe_square2 :: Ptr Element -> Ptr Element -> IO ()
|
||||
foreign import ccall unsafe "fe_pow22523"
|
||||
fe_pow22523 :: Ptr Element -> Ptr Element -> IO ()
|
||||
foreign import ccall unsafe "GFp_x25519_ge_frombytes_vartime"
|
||||
point_frombytes :: Ptr Point3 -> Ptr PackedBytes -> IO Bool
|
||||
foreign import ccall unsafe "x25519_ge_p3_to_cached"
|
||||
p3_to_cached :: Ptr PointCached -> Ptr Point3 -> IO ()
|
||||
foreign import ccall unsafe "x25519_ge_p1p1_to_p2"
|
||||
p1p1_to_p2 :: Ptr Point2 -> Ptr PointP1P1 -> IO ()
|
||||
foreign import ccall unsafe "x25519_ge_p1p1_to_p3"
|
||||
p1p1_to_p3 :: Ptr Point3 -> Ptr PointP1P1 -> IO ()
|
||||
foreign import ccall unsafe "ge_p2_dbl"
|
||||
ge_p2_dbl :: Ptr PointP1P1 -> Ptr Point2 -> IO ()
|
||||
foreign import ccall unsafe "ge_p3_dbl"
|
||||
ge_p3_dbl :: Ptr PointP1P1 -> Ptr Point3 -> IO ()
|
||||
foreign import ccall unsafe "ge_p3_to_p2"
|
||||
ge_p3_to_p2 :: Ptr Point2 -> Ptr Point3 -> IO ()
|
||||
foreign import ccall unsafe "ge_madd"
|
||||
ge_madd :: Ptr PointP1P1 -> Ptr Point3 -> Ptr PointPrecomp -> IO ()
|
||||
foreign import ccall unsafe "ge_msub"
|
||||
ge_msub :: Ptr PointP1P1 -> Ptr Point3 -> Ptr PointPrecomp -> IO ()
|
||||
foreign import ccall unsafe "x25519_ge_add"
|
||||
ge_add :: Ptr PointP1P1 -> Ptr Point3 -> Ptr PointCached -> IO ()
|
||||
foreign import ccall unsafe "x25519_ge_sub"
|
||||
ge_sub :: Ptr PointP1P1 -> Ptr Point3 -> Ptr PointCached -> IO ()
|
||||
foreign import ccall unsafe "GFp_x25519_ge_scalarmult_base"
|
||||
x25519_ge_scalarmult_base :: Ptr Point3 -> Ptr PackedBytes -> IO ()
|
||||
foreign import ccall unsafe "slide"
|
||||
slide :: Ptr Word8 -> Ptr PackedBytes -> IO ()
|
||||
foreign import ccall unsafe "GFp_ge_double_scalarmult_vartime"
|
||||
ge_double_scalarmult_vartime :: Ptr Point2 -> Ptr PackedBytes -> Ptr Point3 -> Ptr PackedBytes -> IO ()
|
||||
foreign import ccall unsafe "GFp_x25519_sc_reduce"
|
||||
sc_reduce :: Ptr Word8 -> IO ()
|
||||
foreign import ccall unsafe "GFp_x25519_sc_muladd"
|
||||
sc_muladd :: Ptr PackedBytes -> Ptr PackedBytes -> Ptr PackedBytes -> Ptr PackedBytes -> IO ()
|
||||
foreign import ccall unsafe "GFp_x25519_public_from_private"
|
||||
public_from_private :: Ptr PackedBytes -> Ptr PackedBytes -> IO ()
|
||||
2120
test-generator/ED25519/PrecompPoints.hs
Normal file
2120
test-generator/ED25519/PrecompPoints.hs
Normal file
File diff suppressed because it is too large
Load Diff
44
test-generator/Main.hs
Normal file
44
test-generator/Main.hs
Normal file
@@ -0,0 +1,44 @@
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE PackageImports #-}
|
||||
import Control.Concurrent(forkIO)
|
||||
import Control.Concurrent.Chan(Chan,newChan,readChan,writeChan)
|
||||
import Control.Concurrent.MVar(MVar,newMVar,modifyMVar)
|
||||
import Control.Exception(SomeException,catch)
|
||||
import Control.Monad(replicateM_,void)
|
||||
import "crypto-api" Crypto.Random(CryptoRandomGen(..),SystemRandom)
|
||||
import DSA(dsaTasks)
|
||||
import ECDSATesting(ecdsaTasks)
|
||||
import ED25519(ed25519Tasks)
|
||||
import GHC.Conc(getNumCapabilities)
|
||||
import RFC6979(rfcTasks)
|
||||
import RSA(rsaTasks)
|
||||
import System.Console.AsciiProgress
|
||||
import Task(Task, runTask)
|
||||
|
||||
taskExecutor :: MVar [Task] -> Chan () -> SystemRandom -> IO SystemRandom
|
||||
taskExecutor taskList done gen =
|
||||
do mnext <- modifyMVar taskList (\case
|
||||
[] -> return ([], Nothing)
|
||||
(x:xs) -> return (xs, Just x))
|
||||
case mnext of
|
||||
Nothing -> do writeChan done ()
|
||||
return gen
|
||||
Just x -> do gen' <- runTask gen x
|
||||
taskExecutor taskList done gen'
|
||||
|
||||
spawnExecutor :: MVar [Task] -> Chan () -> IO ()
|
||||
spawnExecutor tasks done =
|
||||
do gen <- newGenIO
|
||||
void (forkIO (catch (void (taskExecutor tasks done gen)) handler))
|
||||
where
|
||||
handler :: SomeException -> IO ()
|
||||
handler e = putStrLn ("ERROR: " ++ show e)
|
||||
|
||||
main :: IO ()
|
||||
main = displayConsoleRegions $
|
||||
do
|
||||
executors <- getNumCapabilities
|
||||
done <- newChan
|
||||
tasks <- newMVar (dsaTasks ++ ecdsaTasks ++ rfcTasks ++ rsaTasks ++ ed25519Tasks)
|
||||
replicateM_ executors (spawnExecutor tasks done)
|
||||
replicateM_ executors (void $ readChan done)
|
||||
166
test-generator/Math.hs
Normal file
166
test-generator/Math.hs
Normal file
@@ -0,0 +1,166 @@
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
module Math(
|
||||
extendedGCD
|
||||
, barrett, computeK, base
|
||||
, modulate, modulate'
|
||||
, isqrt
|
||||
, divmod
|
||||
, showX, showB, showBin
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Bits(shiftL,shiftR,(.&.))
|
||||
import qualified Data.ByteString as S
|
||||
import GHC.Integer.GMP.Internals(recipModInteger)
|
||||
import Numeric(showHex)
|
||||
|
||||
data AlgState = AlgState {
|
||||
u :: Integer,
|
||||
v :: Integer,
|
||||
bigA :: Integer,
|
||||
bigB :: Integer,
|
||||
bigC :: Integer,
|
||||
bigD :: Integer
|
||||
}
|
||||
|
||||
printState :: AlgState -> IO ()
|
||||
printState a =
|
||||
do putStrLn ("u: " ++ showX (u a))
|
||||
putStrLn ("v: " ++ showX (v a))
|
||||
putStrLn ("A: " ++ showX (bigA a))
|
||||
putStrLn ("B: " ++ showX (bigB a))
|
||||
putStrLn ("C: " ++ showX (bigC a))
|
||||
putStrLn ("D: " ++ showX (bigD a))
|
||||
|
||||
extendedGCD :: Integer -> Integer -> (Integer, Integer, Integer)
|
||||
extendedGCD x y = (a, b, g * (v finalState))
|
||||
where
|
||||
(x', y', g, initState) = initialState x y 1
|
||||
finalState = runAlgorithm x' y' initState
|
||||
a = bigC finalState
|
||||
b = bigD finalState
|
||||
|
||||
initialState :: Integer -> Integer -> Integer -> (Integer, Integer, Integer, AlgState)
|
||||
initialState x y g | even x && even y = initialState (x `div` 2) (y `div` 2) (g * 2)
|
||||
| otherwise = (x, y, g, AlgState x y 1 0 0 1)
|
||||
|
||||
runAlgorithm :: Integer -> Integer -> AlgState -> AlgState
|
||||
runAlgorithm x y state | u state == 0 = state
|
||||
| otherwise = runAlgorithm x y state6
|
||||
where
|
||||
state4 = step4 x y state
|
||||
state5 = step5 x y state4
|
||||
state6 = step6 state5
|
||||
|
||||
step4 :: Integer -> Integer -> AlgState -> AlgState
|
||||
step4 x y input@AlgState{..} | even u = step4 x y input'
|
||||
| otherwise = input
|
||||
where
|
||||
input' = AlgState u' v bigA' bigB' bigC bigD
|
||||
u' = u `div` 2
|
||||
bigA' | even bigA && even bigB = bigA `div` 2
|
||||
| otherwise = (bigA + y) `div` 2
|
||||
bigB' | even bigA && even bigB = bigB `div` 2
|
||||
| otherwise = (bigB - x) `div` 2
|
||||
|
||||
step5 :: Integer -> Integer -> AlgState -> AlgState
|
||||
step5 x y input@AlgState{..} | even v = step5 x y input'
|
||||
| otherwise = input
|
||||
where
|
||||
input' = AlgState u v' bigA bigB bigC' bigD'
|
||||
v' = v `div` 2
|
||||
bigC' | even bigC && even bigD = bigC `div` 2
|
||||
| otherwise = (bigC + y) `div` 2
|
||||
bigD' | even bigC && even bigD = bigD `div` 2
|
||||
| otherwise = (bigD - x) `div` 2
|
||||
|
||||
step6 :: AlgState -> AlgState
|
||||
step6 AlgState{..}
|
||||
| u >= v = AlgState (u - v) v (bigA - bigC) (bigB - bigD) bigC bigD
|
||||
| otherwise = AlgState u (v - u) bigA bigB (bigC - bigA) (bigD - bigB)
|
||||
|
||||
barrett :: Integer -> Integer
|
||||
barrett m = (base ^ (2 * k)) `div` m
|
||||
where
|
||||
k = computeK m
|
||||
|
||||
computeK :: Integer -> Int
|
||||
computeK v = go 0 1
|
||||
where
|
||||
go k acc | v <= acc = k
|
||||
| otherwise = go (k + 1) (acc * base)
|
||||
|
||||
base :: Integer
|
||||
base = 2 ^ (64 :: Integer)
|
||||
|
||||
modulate :: Integer -> Int -> Integer
|
||||
modulate x size = x `mod` (2 ^ size)
|
||||
|
||||
modulate' :: Integer -> Int -> Integer
|
||||
modulate' x size = signum x * (abs x `mod` (2 ^ size))
|
||||
|
||||
showX :: (Integral a, Show a) => a -> String
|
||||
showX x | x < 0 = "-" ++ showX (abs x)
|
||||
| otherwise = showHex x ""
|
||||
|
||||
showB :: Bool -> String
|
||||
showB False = "0"
|
||||
showB True = "1"
|
||||
|
||||
showBin :: S.ByteString -> String
|
||||
showBin bstr =
|
||||
case S.uncons bstr of
|
||||
Nothing -> ""
|
||||
Just (x,rest) ->
|
||||
showX (x `shiftR` 4) ++ showX (x .&. 0xF) ++ showBin rest
|
||||
|
||||
isqrt :: Int -> Integer -> Integer
|
||||
isqrt bits val = final
|
||||
where
|
||||
bit' = part1 (1 `shiftL` (bits - 2))
|
||||
--
|
||||
part1 x | x > val = part1 (x `shiftR` 2)
|
||||
| otherwise = x
|
||||
--
|
||||
final = loop val 0 bit'
|
||||
--
|
||||
loop num res bit
|
||||
| bit == 0 = res
|
||||
| otherwise = let (num', res') = adjust num res bit
|
||||
in loop num' (res' `shiftR` 1) (bit `shiftR` 2)
|
||||
adjust num res bit
|
||||
| num >= (res + bit) = (num - (res + bit), res + (bit `shiftL` 1))
|
||||
| otherwise = (num, res)
|
||||
|
||||
divmod :: Integer -> Integer -> Integer -> Maybe Integer
|
||||
divmod x y m =
|
||||
let y' = y `mod` m
|
||||
in case recipModInteger y' m of
|
||||
0 -> Nothing
|
||||
i -> Just ((x * i) `mod` m)
|
||||
|
||||
_run :: Integer -> Integer -> IO ()
|
||||
_run inputx inputy =
|
||||
do let (x, y, g, initState) = initialState inputx inputy 1
|
||||
finalState <- go x y initState
|
||||
putStrLn ("-- FINAL STATE -----------------------")
|
||||
printState finalState
|
||||
putStrLn ("Final value: " ++ showX (g * v finalState))
|
||||
putStrLn ("-- RUN ------")
|
||||
printState (runAlgorithm x y initState)
|
||||
putStrLn ("-- NORMAL ------")
|
||||
let (a, b, v) = extendedGCD inputx inputy
|
||||
putStrLn ("a: " ++ showX a)
|
||||
putStrLn ("b: " ++ showX b)
|
||||
putStrLn ("v: " ++ showX v)
|
||||
|
||||
where
|
||||
go x y state =
|
||||
do putStrLn "-- STATE -----------------------------"
|
||||
printState state
|
||||
if u state == 0
|
||||
then return state
|
||||
else do let state' = step4 x y state
|
||||
state'' = step5 x y state'
|
||||
state''' = step6 state''
|
||||
go x y state'''
|
||||
113
test-generator/RFC6979.hs
Normal file
113
test-generator/RFC6979.hs
Normal file
@@ -0,0 +1,113 @@
|
||||
{-# LANGUAGE PackageImports #-}
|
||||
module RFC6979
|
||||
-- (
|
||||
-- rfcTasks
|
||||
-- )
|
||||
where
|
||||
|
||||
import Crypto.Hash(SHA224(..),SHA256(..),SHA384(..),SHA512(..))
|
||||
import Crypto.MAC.HMAC(HMAC,hmac)
|
||||
import Crypto.Number.Generate(generateBetween)
|
||||
import "cryptonite" Crypto.Random(getRandomBytes,withDRG)
|
||||
import Data.Bits(shiftL,shiftR,(.&.))
|
||||
import qualified Data.ByteArray as B
|
||||
import qualified Data.ByteString as S
|
||||
import Data.Char(toUpper)
|
||||
import qualified Data.Map.Strict as Map
|
||||
import Math(showBin,showX)
|
||||
import Task(Task(..),liftTest)
|
||||
import Utils(HashAlg(..), runHash)
|
||||
|
||||
|
||||
runHMAC :: HashAlg -> S.ByteString -> S.ByteString -> S.ByteString
|
||||
runHMAC Sha224 key msg = S.pack (B.unpack (hmac key msg :: HMAC SHA224))
|
||||
runHMAC Sha256 key msg = S.pack (B.unpack (hmac key msg :: HMAC SHA256))
|
||||
runHMAC Sha384 key msg = S.pack (B.unpack (hmac key msg :: HMAC SHA384))
|
||||
runHMAC Sha512 key msg = S.pack (B.unpack (hmac key msg :: HMAC SHA512))
|
||||
|
||||
generateKStream :: HashAlg -> S.ByteString -> Integer -> Integer -> Int -> [Integer]
|
||||
generateKStream alg m x q qlen = nextK bigK2 bigV2
|
||||
where
|
||||
h1 = runHash alg m
|
||||
bigV0 = S.replicate (S.length h1) 0x01
|
||||
bigK0 = S.replicate (S.length h1) 0x00
|
||||
seed1 = S.concat [bigV0, S.singleton 0x00, int2octets qlen x, bits2octets qlen q h1]
|
||||
bigK1 = runHMAC alg bigK0 seed1
|
||||
bigV1 = runHMAC alg bigK1 bigV0
|
||||
seed2 = S.concat [bigV1, S.singleton 0x01, int2octets qlen x, bits2octets qlen q h1]
|
||||
bigK2 = runHMAC alg bigK1 seed2
|
||||
bigV2 = runHMAC alg bigK2 bigV1
|
||||
--
|
||||
nextK bigK bigV =
|
||||
let (bigV', bigT) = buildT bigK bigV S.empty
|
||||
k = bits2int qlen bigT
|
||||
bigK' = runHMAC alg bigK (bigV' `S.append` S.singleton 0)
|
||||
bigV'' = runHMAC alg bigK' bigV'
|
||||
in if k < q then (k : nextK bigK' bigV'') else nextK bigK' bigV''
|
||||
buildT bigK bigV bigT
|
||||
| S.length bigT * 8 >= qlen = (bigV, bigT)
|
||||
| otherwise =
|
||||
let bigV' = runHMAC alg bigK bigV
|
||||
in buildT bigK bigV' (bigT `S.append` bigV')
|
||||
|
||||
bits2int :: Int -> S.ByteString -> Integer
|
||||
bits2int qlen bstr = reduce (go bstr 0)
|
||||
where
|
||||
reduce x =
|
||||
let vlen = S.length bstr * 8
|
||||
in if vlen > qlen
|
||||
then x `shiftR` (vlen - qlen)
|
||||
else x
|
||||
--
|
||||
go x acc =
|
||||
case S.uncons x of
|
||||
Nothing -> acc
|
||||
Just (v, rest) ->
|
||||
go rest ((acc `shiftL` 8) + fromIntegral v)
|
||||
|
||||
int2octets :: Int -> Integer -> S.ByteString
|
||||
int2octets lenBits x =
|
||||
S.pack (pad (rlen `div` 8) (reverse (go x)))
|
||||
where
|
||||
rlen = 8 * ((lenBits + 7) `div` 8)
|
||||
pad target ls
|
||||
| length ls > target = drop (length ls - target) ls
|
||||
| length ls < target = pad target (0 : ls)
|
||||
| otherwise = ls
|
||||
--
|
||||
go 0 = []
|
||||
go v = (fromIntegral (v .&. 0xFF)) : go (v `shiftR` 8)
|
||||
|
||||
bits2octets :: Int -> Integer -> S.ByteString -> S.ByteString
|
||||
bits2octets qlen q bstr =
|
||||
let z1 = bits2int qlen bstr
|
||||
z2 = if z1 > q then z1 - q else z1
|
||||
in int2octets qlen z2
|
||||
|
||||
rfc6979Test :: HashAlg -> Task
|
||||
rfc6979Test alg = Task {
|
||||
taskName = name ++ " RFC 6979 deterministic k-generation",
|
||||
taskFile = "../testdata/rfc6979/" ++ name ++ ".test",
|
||||
taskTest = liftTest go,
|
||||
taskCount = 1000
|
||||
}
|
||||
where
|
||||
name = map toUpper (show alg)
|
||||
go (memory0, drg0) =
|
||||
let (qlen, drg1) = withDRG drg0 $ generateBetween 160 521
|
||||
(key, drg2) = withDRG drg1 $ generateBetween 1 ((2 ^ qlen) - 1)
|
||||
(q, drg3) = withDRG drg2 $ generateBetween 1 ((2 ^ qlen) - 1)
|
||||
(dataSize, drg4) = withDRG drg3 $ generateBetween 1 1024
|
||||
(msg, drg5) = withDRG drg4 $ getRandomBytes (fromIntegral dataSize)
|
||||
h1 = runHash alg msg
|
||||
ks = generateKStream alg msg key q (fromIntegral qlen)
|
||||
res = Map.fromList [("q", showX q), ("l", showX qlen),
|
||||
("x", showX key), ("h", showBin h1),
|
||||
("k", showX (ks !! 0)),
|
||||
("y", showX (ks !! 1)),
|
||||
("z", showX (ks !! 2))]
|
||||
in (res, qlen, (memory0, drg5))
|
||||
|
||||
rfcTasks :: [Task]
|
||||
rfcTasks = [rfc6979Test Sha224, rfc6979Test Sha256,
|
||||
rfc6979Test Sha384, rfc6979Test Sha512]
|
||||
126
test-generator/RSA.hs
Normal file
126
test-generator/RSA.hs
Normal file
@@ -0,0 +1,126 @@
|
||||
{-# LANGUAGE PackageImports #-}
|
||||
module RSA(rsaTasks)
|
||||
where
|
||||
|
||||
import Crypto.Hash(SHA224(..),SHA256(..),SHA384(..),SHA512(..))
|
||||
import "cryptonite" Crypto.Random(MonadRandom,MonadPseudoRandom,getRandomBytes,withDRG)
|
||||
import "crypto-api" Crypto.Random(SystemRandom)
|
||||
import Crypto.PubKey.MaskGenFunction(mgf1)
|
||||
import Crypto.PubKey.RSA
|
||||
import Crypto.PubKey.RSA.PKCS15(sign)
|
||||
import Crypto.PubKey.RSA.OAEP(OAEPParams(..),encrypt)
|
||||
import qualified Data.ByteString as BS
|
||||
import qualified Data.ByteString.Char8 as BSC
|
||||
import Data.Char(chr,isPrint)
|
||||
import Data.Map.Strict(Map)
|
||||
import qualified Data.Map.Strict as Map
|
||||
import Data.Maybe(fromMaybe,isJust)
|
||||
import Data.Word(Word8)
|
||||
import Database(Database)
|
||||
import Math(barrett,computeK,showX,showBin)
|
||||
import Task(Task(..),liftTest)
|
||||
import Utils(HashAlg(..),generateHash,showHash)
|
||||
|
||||
rsaSizes :: [(Int, Int)]
|
||||
rsaSizes = [(512, 400),
|
||||
(1024, 200),
|
||||
(2048, 100),
|
||||
(3072, 50),
|
||||
(4096, 50),
|
||||
(8192, 10),
|
||||
(15360, 5)]
|
||||
|
||||
rsaTasks :: [Task]
|
||||
rsaTasks = concatMap generateTask rsaSizes
|
||||
|
||||
generateTask :: (Int, Int) -> [Task]
|
||||
generateTask (s, c) = [signTest s c, encryptTest s c]
|
||||
|
||||
signTest :: Int -> Int -> Task
|
||||
signTest sz cnt = Task {
|
||||
taskName = "RSA " ++ show sz ++ " signing",
|
||||
taskFile = "../testdata/rsa/sign" ++ show sz ++ ".test",
|
||||
taskTest = liftTest go,
|
||||
taskCount = cnt
|
||||
}
|
||||
where
|
||||
go db = withDRG' db go2
|
||||
--
|
||||
go2 :: MonadRandom m => m (Map String String, Integer)
|
||||
go2 = do (public, private) <- generate (sz `div` 8) 65537
|
||||
let d = private_d private
|
||||
let n = public_n public
|
||||
msg <- getRandomBytes =<< ((fromIntegral . BS.head) `fmap` getRandomBytes 1)
|
||||
hash <- generateHash
|
||||
case signWith hash private msg of
|
||||
Left _ -> go2
|
||||
Right sig -> return $ (Map.fromList [("d", showX d),
|
||||
("n", showX n),
|
||||
("k", showX (computeK n)),
|
||||
("u", showX (barrett n)),
|
||||
("h", showHash hash),
|
||||
("m", showBin msg),
|
||||
("s", showBin sig)], n)
|
||||
|
||||
withDRG' :: Database -> MonadPseudoRandom SystemRandom (Map String String, Integer) ->
|
||||
(Map String String, Integer, Database)
|
||||
withDRG' (memory, drg) action =
|
||||
let ((res, n), drg') = withDRG drg action
|
||||
in (res, n, (memory, drg'))
|
||||
|
||||
signWith :: HashAlg -> PrivateKey -> BS.ByteString -> Either Error BS.ByteString
|
||||
signWith Sha224 = sign Nothing (Just SHA224)
|
||||
signWith Sha256 = sign Nothing (Just SHA256)
|
||||
signWith Sha384 = sign Nothing (Just SHA384)
|
||||
signWith Sha512 = sign Nothing (Just SHA512)
|
||||
|
||||
encryptTest :: Int -> Int -> Task
|
||||
encryptTest sz cnt = Task {
|
||||
taskName = "RSA " ++ show sz ++ " encryption",
|
||||
taskFile = "../testdata/rsa/encrypt" ++ show sz ++ ".test",
|
||||
taskTest = liftTest go,
|
||||
taskCount = cnt
|
||||
}
|
||||
where
|
||||
go db = withDRG' db go2
|
||||
go2 = do (public, private) <- generate (sz `div` 8) 65537
|
||||
let d = private_d private
|
||||
let n = public_n public
|
||||
msg <- getRandomBytes =<< ((fromIntegral . BS.head) `fmap` getRandomBytes 1)
|
||||
hash <- generateHash
|
||||
label <- do len <- BS.head `fmap` getRandomBytes 1
|
||||
if odd len
|
||||
then return Nothing
|
||||
else Just `fmap` genASCII (len `div` 2)
|
||||
let labelbstr = fromMaybe BS.empty (BSC.pack `fmap` label)
|
||||
labelAlive = if isJust label then 1 else (0 :: Integer)
|
||||
res <- encryptWith hash (BSC.pack `fmap` label) public msg
|
||||
case res of
|
||||
Left _ -> go2
|
||||
Right cipher ->
|
||||
return $ (Map.fromList [("d", showX d),
|
||||
("n", showX n),
|
||||
("k", showX (computeK n)),
|
||||
("u", showX (barrett n)),
|
||||
("h", showHash hash),
|
||||
("m", showBin msg),
|
||||
("l", showBin labelbstr),
|
||||
("e", showX labelAlive),
|
||||
("c", showBin cipher)], n)
|
||||
|
||||
genASCII :: MonadRandom m => Word8 -> m String
|
||||
genASCII 0 = return ""
|
||||
genASCII x =
|
||||
do v <- (BS.head `fmap` getRandomBytes 1)
|
||||
let c = chr (fromIntegral v)
|
||||
if (v < 128) && isPrint c
|
||||
then (c :) `fmap` genASCII (x - 1)
|
||||
else genASCII x
|
||||
|
||||
encryptWith :: MonadRandom m =>
|
||||
HashAlg -> Maybe BS.ByteString -> PublicKey -> BS.ByteString ->
|
||||
m (Either Error BS.ByteString)
|
||||
encryptWith Sha224 mlabel = encrypt (OAEPParams SHA224 (mgf1 SHA224) mlabel)
|
||||
encryptWith Sha256 mlabel = encrypt (OAEPParams SHA256 (mgf1 SHA256) mlabel)
|
||||
encryptWith Sha384 mlabel = encrypt (OAEPParams SHA384 (mgf1 SHA384) mlabel)
|
||||
encryptWith Sha512 mlabel = encrypt (OAEPParams SHA512 (mgf1 SHA512) mlabel)
|
||||
56
test-generator/Task.hs
Normal file
56
test-generator/Task.hs
Normal file
@@ -0,0 +1,56 @@
|
||||
{-# LANGUAGE PackageImports #-}
|
||||
module Task(
|
||||
Test,
|
||||
Task(..),
|
||||
runTask,
|
||||
liftTest
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Monad(foldM, forM_)
|
||||
import "crypto-api" Crypto.Random(SystemRandom)
|
||||
import qualified Data.Map.Strict as Map
|
||||
import Database
|
||||
import System.Console.AsciiProgress
|
||||
import System.Directory(createDirectoryIfMissing,doesFileExist)
|
||||
import System.FilePath(takeDirectory)
|
||||
import System.IO(Handle,IOMode(..),hPutStrLn,withFile)
|
||||
|
||||
type Test = Database -> IO (Map.Map String String, Integer, Database)
|
||||
|
||||
data Task = Task {
|
||||
taskName :: String,
|
||||
taskFile :: FilePath,
|
||||
taskTest :: Test,
|
||||
taskCount :: Int
|
||||
}
|
||||
|
||||
liftTest :: (Database -> (Map.Map String String, Integer, Database)) ->
|
||||
(Database -> IO (Map.Map String String, Integer, Database))
|
||||
liftTest f db = return (f db)
|
||||
|
||||
runTask :: SystemRandom -> Task -> IO SystemRandom
|
||||
runTask gen task =
|
||||
do createDirectoryIfMissing True (takeDirectory (taskFile task))
|
||||
alreadyDone <- doesFileExist (taskFile task)
|
||||
if alreadyDone
|
||||
then return gen
|
||||
else withFile (taskFile task) WriteMode $ \ hndl ->
|
||||
do pg <- newProgressBar def{ pgOnCompletion = Just ("Finished " ++ taskName task),
|
||||
pgFormat = taskName task ++ " " ++ pgFormat def,
|
||||
pgTotal = fromIntegral (taskCount task) }
|
||||
let initval = emptyDatabase gen
|
||||
(_, gen') <- foldM (writer hndl pg (taskTest task)) initval [0..taskCount task]
|
||||
return gen'
|
||||
where
|
||||
writer :: Handle -> ProgressBar -> Test -> Database -> Int -> IO Database
|
||||
writer hndl pg runner db x =
|
||||
do (output, key, acc@(db',gen')) <- runner db
|
||||
let before = Map.findWithDefault [] "RESULT" db'
|
||||
if length (filter (== key) before) >= 10
|
||||
then writer hndl pg runner acc x
|
||||
else do forM_ (Map.toList output) $ \ (outkey, val) ->
|
||||
hPutStrLn hndl (outkey ++ ": " ++ val)
|
||||
tick pg
|
||||
return (Map.insert "RESULT" (key : before) db', gen')
|
||||
|
||||
35
test-generator/Utils.hs
Normal file
35
test-generator/Utils.hs
Normal file
@@ -0,0 +1,35 @@
|
||||
{-# LANGUAGE PackageImports #-}
|
||||
module Utils(HashAlg(..), generateHash, runHash, showHash)
|
||||
where
|
||||
|
||||
import Crypto.Hash(Digest,SHA224(..),SHA256(..),SHA384(..),SHA512(..),hash)
|
||||
import Crypto.Number.Generate(generateBetween)
|
||||
import "cryptonite" Crypto.Random(MonadRandom)
|
||||
import qualified Data.ByteArray as B
|
||||
import qualified Data.ByteString as S
|
||||
import Math(showX)
|
||||
|
||||
data HashAlg = Sha224 | Sha256 | Sha384 | Sha512
|
||||
deriving (Eq, Show)
|
||||
|
||||
runHash :: HashAlg -> S.ByteString -> S.ByteString
|
||||
runHash Sha224 x = S.pack (B.unpack (hash x :: Digest SHA224))
|
||||
runHash Sha256 x = S.pack (B.unpack (hash x :: Digest SHA256))
|
||||
runHash Sha384 x = S.pack (B.unpack (hash x :: Digest SHA384))
|
||||
runHash Sha512 x = S.pack (B.unpack (hash x :: Digest SHA512))
|
||||
|
||||
showHash :: HashAlg -> String
|
||||
showHash Sha224 = showX (224 :: Int)
|
||||
showHash Sha256 = showX (256 :: Int)
|
||||
showHash Sha384 = showX (384 :: Int)
|
||||
showHash Sha512 = showX (512 :: Int)
|
||||
|
||||
generateHash :: MonadRandom m => m HashAlg
|
||||
generateHash =
|
||||
do x <- generateBetween 0 3
|
||||
case x of
|
||||
0 -> return Sha224
|
||||
1 -> return Sha256
|
||||
2 -> return Sha384
|
||||
3 -> return Sha512
|
||||
_ -> fail "Incompatible random number"
|
||||
96
test-generator/cbits/GFp/aes.h
Normal file
96
test-generator/cbits/GFp/aes.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/* ====================================================================
|
||||
* Copyright (c) 2002-2006 The OpenSSL Project. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
|
||||
*
|
||||
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please contact
|
||||
* openssl-core@openssl.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "OpenSSL"
|
||||
* nor may "OpenSSL" appear in their names without prior written
|
||||
* permission of the OpenSSL Project.
|
||||
*
|
||||
* 6. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
|
||||
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* ==================================================================== */
|
||||
|
||||
#ifndef OPENSSL_HEADER_AES_H
|
||||
#define OPENSSL_HEADER_AES_H
|
||||
|
||||
#include <GFp/base.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Raw AES functions. */
|
||||
|
||||
|
||||
#define AES_ENCRYPT 1
|
||||
#define AES_DECRYPT 0
|
||||
|
||||
/* AES_MAXNR is the maximum number of AES rounds. */
|
||||
#define AES_MAXNR 14
|
||||
|
||||
#define AES_BLOCK_SIZE 16
|
||||
|
||||
/* aes_key_st should be an opaque type, but EVP requires that the size be
|
||||
* known. */
|
||||
struct aes_key_st {
|
||||
uint32_t rd_key[4 * (AES_MAXNR + 1)];
|
||||
unsigned rounds;
|
||||
};
|
||||
typedef struct aes_key_st AES_KEY;
|
||||
|
||||
/* GFp_AES_set_encrypt_key configures |aeskey| to encrypt with the |bits|-bit
|
||||
* key, |key|.
|
||||
*
|
||||
* WARNING: unlike other OpenSSL functions, this returns zero on success and a
|
||||
* negative number on error. */
|
||||
OPENSSL_EXPORT int GFp_AES_set_encrypt_key(const uint8_t *key, unsigned bits,
|
||||
AES_KEY *aeskey);
|
||||
|
||||
/* AES_encrypt encrypts a single block from |in| to |out| with |key|. The |in|
|
||||
* and |out| pointers may overlap. */
|
||||
OPENSSL_EXPORT void GFp_AES_encrypt(const uint8_t *in, uint8_t *out,
|
||||
const AES_KEY *key);
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* extern C */
|
||||
#endif
|
||||
|
||||
#endif /* OPENSSL_HEADER_AES_H */
|
||||
123
test-generator/cbits/GFp/arm_arch.h
Normal file
123
test-generator/cbits/GFp/arm_arch.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/* ====================================================================
|
||||
* Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
|
||||
*
|
||||
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please contact
|
||||
* openssl-core@openssl.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "OpenSSL"
|
||||
* nor may "OpenSSL" appear in their names without prior written
|
||||
* permission of the OpenSSL Project.
|
||||
*
|
||||
* 6. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
|
||||
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This product includes cryptographic software written by Eric Young
|
||||
* (eay@cryptsoft.com). This product includes software written by Tim
|
||||
* Hudson (tjh@cryptsoft.com). */
|
||||
|
||||
#ifndef OPENSSL_HEADER_ARM_ARCH_H
|
||||
#define OPENSSL_HEADER_ARM_ARCH_H
|
||||
|
||||
#if !defined(__ARM_ARCH__)
|
||||
# if defined(__CC_ARM)
|
||||
# define __ARM_ARCH__ __TARGET_ARCH_ARM
|
||||
# if defined(__BIG_ENDIAN)
|
||||
# define __ARMEB__
|
||||
# else
|
||||
# define __ARMEL__
|
||||
# endif
|
||||
# elif defined(__GNUC__)
|
||||
# if defined(__aarch64__)
|
||||
# define __ARM_ARCH__ 8
|
||||
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define __ARMEB__
|
||||
# else
|
||||
# define __ARMEL__
|
||||
# endif
|
||||
/* Why doesn't gcc define __ARM_ARCH__? Instead it defines
|
||||
* bunch of below macros. See all_architectires[] table in
|
||||
* gcc/config/arm/arm.c. On a side note it defines
|
||||
* __ARMEL__/__ARMEB__ for little-/big-endian. */
|
||||
# elif defined(__ARM_ARCH)
|
||||
# define __ARM_ARCH__ __ARM_ARCH
|
||||
# elif defined(__ARM_ARCH_8A__)
|
||||
# define __ARM_ARCH__ 8
|
||||
# elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || \
|
||||
defined(__ARM_ARCH_7R__)|| defined(__ARM_ARCH_7M__) || \
|
||||
defined(__ARM_ARCH_7EM__)
|
||||
# define __ARM_ARCH__ 7
|
||||
# elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \
|
||||
defined(__ARM_ARCH_6K__)|| defined(__ARM_ARCH_6M__) || \
|
||||
defined(__ARM_ARCH_6Z__)|| defined(__ARM_ARCH_6ZK__) || \
|
||||
defined(__ARM_ARCH_6T2__)
|
||||
# define __ARM_ARCH__ 6
|
||||
# elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) || \
|
||||
defined(__ARM_ARCH_5E__)|| defined(__ARM_ARCH_5TE__) || \
|
||||
defined(__ARM_ARCH_5TEJ__)
|
||||
# define __ARM_ARCH__ 5
|
||||
# elif defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__)
|
||||
# define __ARM_ARCH__ 4
|
||||
# else
|
||||
# error "unsupported ARM architecture"
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Even when building for 32-bit ARM, support for aarch64 crypto instructions
|
||||
* will be included. */
|
||||
#if !defined(__ARM_MAX_ARCH__)
|
||||
#define __ARM_MAX_ARCH__ 8
|
||||
#endif
|
||||
|
||||
/* ARMV7_NEON is true when a NEON unit is present in the current CPU. */
|
||||
#define ARMV7_NEON (1 << 0)
|
||||
|
||||
/* ARMV8_AES indicates support for hardware AES instructions. */
|
||||
#define ARMV8_AES (1 << 2)
|
||||
|
||||
/* ARMV8_SHA1 indicates support for hardware SHA-1 instructions. */
|
||||
#define ARMV8_SHA1 (1 << 3)
|
||||
|
||||
/* ARMV8_SHA256 indicates support for hardware SHA-256 instructions. */
|
||||
#define ARMV8_SHA256 (1 << 4)
|
||||
|
||||
/* ARMV8_PMULL indicates support for carryless multiplication. */
|
||||
#define ARMV8_PMULL (1 << 5)
|
||||
|
||||
|
||||
#endif /* OPENSSL_HEADER_ARM_ARCH_H */
|
||||
122
test-generator/cbits/GFp/base.h
Normal file
122
test-generator/cbits/GFp/base.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/* ====================================================================
|
||||
* Copyright (c) 1998-2001 The OpenSSL Project. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
|
||||
*
|
||||
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please contact
|
||||
* openssl-core@openssl.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "OpenSSL"
|
||||
* nor may "OpenSSL" appear in their names without prior written
|
||||
* permission of the OpenSSL Project.
|
||||
*
|
||||
* 6. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
|
||||
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This product includes cryptographic software written by Eric Young
|
||||
* (eay@cryptsoft.com). This product includes software written by Tim
|
||||
* Hudson (tjh@cryptsoft.com). */
|
||||
|
||||
#ifndef OPENSSL_HEADER_BASE_H
|
||||
#define OPENSSL_HEADER_BASE_H
|
||||
|
||||
|
||||
/* This file should be the first included by all BoringSSL headers. */
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__x86_64) || defined(_M_AMD64) || defined(_M_X64)
|
||||
#define OPENSSL_64_BIT
|
||||
#define OPENSSL_X86_64
|
||||
#elif defined(__x86) || defined(__i386) || defined(__i386__) || defined(_M_IX86)
|
||||
#define OPENSSL_32_BIT
|
||||
#define OPENSSL_X86
|
||||
#elif defined(__aarch64__)
|
||||
#define OPENSSL_64_BIT
|
||||
#define OPENSSL_AARCH64
|
||||
#elif defined(__arm) || defined(__arm__) || defined(_M_ARM)
|
||||
#define OPENSSL_32_BIT
|
||||
#define OPENSSL_ARM
|
||||
#elif (defined(__PPC64__) || defined(__powerpc64__)) && defined(_LITTLE_ENDIAN)
|
||||
#define OPENSSL_64_BIT
|
||||
#define OPENSSL_PPC64LE
|
||||
#elif defined(__mips__) && !defined(__LP64__)
|
||||
#define OPENSSL_32_BIT
|
||||
#define OPENSSL_MIPS
|
||||
#elif defined(__mips__) && defined(__LP64__)
|
||||
#define OPENSSL_64_BIT
|
||||
#define OPENSSL_MIPS64
|
||||
#elif defined(__pnacl__)
|
||||
#define OPENSSL_32_BIT
|
||||
#define OPENSSL_PNACL
|
||||
#elif defined(__myriad2__)
|
||||
#define OPENSSL_32_BIT
|
||||
#else
|
||||
#error "Unknown target CPU"
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define OPENSSL_APPLE
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define OPENSSL_WINDOWS
|
||||
#endif
|
||||
|
||||
#define OPENSSL_IS_BORINGSSL
|
||||
#define OPENSSL_IS_RING
|
||||
#define OPENSSL_VERSION_NUMBER 0x10002000
|
||||
|
||||
/* *ring* doesn't support the `BORINGSSL_SHARED_LIBRARY` configuration, so
|
||||
* the default (usually "hidden") visibility is always used, even for exported
|
||||
* items. */
|
||||
#define OPENSSL_EXPORT
|
||||
|
||||
typedef struct bignum_st BIGNUM;
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* extern C */
|
||||
#endif
|
||||
|
||||
#endif /* OPENSSL_HEADER_BASE_H */
|
||||
256
test-generator/cbits/GFp/bn.h
Normal file
256
test-generator/cbits/GFp/bn.h
Normal file
@@ -0,0 +1,256 @@
|
||||
/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* This package is an SSL implementation written
|
||||
* by Eric Young (eay@cryptsoft.com).
|
||||
* The implementation was written so as to conform with Netscapes SSL.
|
||||
*
|
||||
* This library is free for commercial and non-commercial use as long as
|
||||
* the following conditions are aheared to. The following conditions
|
||||
* apply to all code found in this distribution, be it the RC4, RSA,
|
||||
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
|
||||
* included with this distribution is covered by the same copyright terms
|
||||
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
|
||||
*
|
||||
* Copyright remains Eric Young's, and as such any Copyright notices in
|
||||
* the code are not to be removed.
|
||||
* If this package is used in a product, Eric Young should be given attribution
|
||||
* as the author of the parts of the library used.
|
||||
* This can be in the form of a textual message at program startup or
|
||||
* in documentation (online or textual) provided with the package.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* "This product includes cryptographic software written by
|
||||
* Eric Young (eay@cryptsoft.com)"
|
||||
* The word 'cryptographic' can be left out if the rouines from the library
|
||||
* being used are not cryptographic related :-).
|
||||
* 4. If you include any Windows specific code (or a derivative thereof) from
|
||||
* the apps directory (application code) you must include an acknowledgement:
|
||||
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* The licence and distribution terms for any publically available version or
|
||||
* derivative of this code cannot be changed. i.e. this code cannot simply be
|
||||
* copied and put under another distribution licence
|
||||
* [including the GNU Public Licence.]
|
||||
*/
|
||||
/* ====================================================================
|
||||
* Copyright (c) 1998-2006 The OpenSSL Project. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
|
||||
*
|
||||
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please contact
|
||||
* openssl-core@openssl.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "OpenSSL"
|
||||
* nor may "OpenSSL" appear in their names without prior written
|
||||
* permission of the OpenSSL Project.
|
||||
*
|
||||
* 6. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
|
||||
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This product includes cryptographic software written by Eric Young
|
||||
* (eay@cryptsoft.com). This product includes software written by Tim
|
||||
* Hudson (tjh@cryptsoft.com).
|
||||
*
|
||||
*/
|
||||
/* ====================================================================
|
||||
* Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
|
||||
*
|
||||
* Portions of the attached software ("Contribution") are developed by
|
||||
* SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project.
|
||||
*
|
||||
* The Contribution is licensed pursuant to the Eric Young open source
|
||||
* license provided above.
|
||||
*
|
||||
* The binary polynomial arithmetic software is originally written by
|
||||
* Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems
|
||||
* Laboratories. */
|
||||
|
||||
#ifndef OPENSSL_HEADER_BN_H
|
||||
#define OPENSSL_HEADER_BN_H
|
||||
|
||||
#include <GFp/base.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* BN provides support for working with arbitrary sized integers. For example,
|
||||
* although the largest integer supported by the compiler might be 64 bits, BN
|
||||
* will allow you to work with numbers until you run out of memory. */
|
||||
|
||||
|
||||
/* BN_ULONG is the native word size when working with big integers.
|
||||
*
|
||||
* Note: on some platforms, inttypes.h does not define print format macros in
|
||||
* C++ unless |__STDC_FORMAT_MACROS| defined. As this is a public header, bn.h
|
||||
* does not define |__STDC_FORMAT_MACROS| itself. C++ source files which use the
|
||||
* FMT macros must define it externally. */
|
||||
#if defined(OPENSSL_64_BIT)
|
||||
#define BN_ULONG uint64_t
|
||||
#define BN_BITS2 64
|
||||
#elif defined(OPENSSL_32_BIT)
|
||||
#define BN_ULONG uint32_t
|
||||
#define BN_BITS2 32
|
||||
#else
|
||||
#error "Must define either OPENSSL_32_BIT or OPENSSL_64_BIT"
|
||||
#endif
|
||||
|
||||
|
||||
/* Allocation and freeing. */
|
||||
|
||||
/* GFp_BN_init initialises a stack allocated |BIGNUM|. */
|
||||
OPENSSL_EXPORT void GFp_BN_init(BIGNUM *bn);
|
||||
|
||||
/* GFp_BN_free frees the data referenced by |bn| and, if |bn| was originally
|
||||
* allocated on the heap, frees |bn| also. */
|
||||
OPENSSL_EXPORT void GFp_BN_free(BIGNUM *bn);
|
||||
|
||||
/* GFp_BN_copy sets |dest| equal to |src| and returns one on success or zero on
|
||||
* failure. */
|
||||
OPENSSL_EXPORT int GFp_BN_copy(BIGNUM *dest, const BIGNUM *src);
|
||||
|
||||
|
||||
/* Basic functions. */
|
||||
|
||||
/* GFp_BN_zero sets |bn| to zero. */
|
||||
OPENSSL_EXPORT void GFp_BN_zero(BIGNUM *bn);
|
||||
|
||||
|
||||
/* Internal functions.
|
||||
*
|
||||
* These functions are useful for code that is doing low-level manipulations of
|
||||
* BIGNUM values. However, be sure that no other function in this file does
|
||||
* what you want before turning to these. */
|
||||
|
||||
/* bn_correct_top decrements |bn->top| until |bn->d[top-1]| is non-zero or
|
||||
* until |top| is zero. */
|
||||
OPENSSL_EXPORT void GFp_bn_correct_top(BIGNUM *bn);
|
||||
|
||||
/* bn_wexpand ensures that |bn| has at least |words| works of space without
|
||||
* altering its value. It returns one on success and zero on allocation
|
||||
* failure. */
|
||||
OPENSSL_EXPORT int GFp_bn_wexpand(BIGNUM *bn, size_t words);
|
||||
|
||||
|
||||
/* Simple arithmetic */
|
||||
|
||||
/* GFp_BN_mul_no_alias sets |r| = |a| * |b|, where |r| must not be the same pointer
|
||||
* as |a| or |b|. Returns one on success and zero otherwise. */
|
||||
OPENSSL_EXPORT int GFp_BN_mul_no_alias(BIGNUM *r, const BIGNUM *a, const BIGNUM *b);
|
||||
|
||||
|
||||
/* Comparison functions */
|
||||
|
||||
/* GFp_BN_is_odd returns one if |bn| is odd and zero otherwise. */
|
||||
OPENSSL_EXPORT int GFp_BN_is_odd(const BIGNUM *bn);
|
||||
|
||||
|
||||
/* Bitwise operations. */
|
||||
|
||||
/* GFp_BN_is_bit_set returns the value of the |n|th, least-significant bit in
|
||||
* |a|, or zero if the bit doesn't exist. */
|
||||
OPENSSL_EXPORT int GFp_BN_is_bit_set(const BIGNUM *a, int n);
|
||||
|
||||
|
||||
/* Modulo arithmetic. */
|
||||
|
||||
/* GFp_BN_mod_mul_mont set |r| equal to |a| * |b|, in the Montgomery domain.
|
||||
* Both |a| and |b| must already be in the Montgomery domain (by
|
||||
* |GFp_BN_to_mont|). In particular, |a| and |b| are assumed to be in the range
|
||||
* [0, n), where |n| is the Montgomery modulus. It returns one on success or
|
||||
* zero on error. */
|
||||
OPENSSL_EXPORT int GFp_BN_mod_mul_mont(
|
||||
BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *n,
|
||||
const BN_ULONG n0[/*BN_MONT_CTX_N0_LIMBS*/]);
|
||||
|
||||
/* GFp_BN_reduce_montgomery returns |a % n| in constant-ish time using
|
||||
* Montgomery reduction. |a| is assumed to be in the range [0, n**2), where |n|
|
||||
* is the Montgomery modulus. It returns one on success or zero on error. */
|
||||
int GFp_BN_reduce_mont(BIGNUM *r, const BIGNUM *a, const BIGNUM *n,
|
||||
const BN_ULONG n0[/*BN_MONT_CTX_N0_LIMBS*/]);
|
||||
|
||||
|
||||
/* Exponentiation. */
|
||||
|
||||
OPENSSL_EXPORT int GFp_BN_mod_exp_mont_consttime(
|
||||
BIGNUM *rr, const BIGNUM *a_mont, const BIGNUM *p, size_t p_bits,
|
||||
const BIGNUM *one_mont, const BIGNUM *n,
|
||||
const BN_ULONG n0[/*BN_MONT_CTX_N0_LIMBS*/]);
|
||||
|
||||
|
||||
/* Private functions */
|
||||
|
||||
/* Keep in sync with `BIGNUM` in `ring::rsa::bigint`. */
|
||||
struct bignum_st {
|
||||
BN_ULONG *d; /* Pointer to an array of 'BN_BITS2' bit chunks in little-endian
|
||||
order. */
|
||||
int top; /* Index of last used element in |d|, plus one. */
|
||||
int dmax; /* Size of |d|, in words. */
|
||||
int flags; /* bitmask of BN_FLG_* values */
|
||||
};
|
||||
|
||||
#define BN_FLG_MALLOCED 0x01
|
||||
#define BN_FLG_STATIC_DATA 0x02
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* extern C */
|
||||
#endif
|
||||
|
||||
#endif /* OPENSSL_HEADER_BN_H */
|
||||
189
test-generator/cbits/GFp/cpu.h
Normal file
189
test-generator/cbits/GFp/cpu.h
Normal file
@@ -0,0 +1,189 @@
|
||||
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* This package is an SSL implementation written
|
||||
* by Eric Young (eay@cryptsoft.com).
|
||||
* The implementation was written so as to conform with Netscapes SSL.
|
||||
*
|
||||
* This library is free for commercial and non-commercial use as long as
|
||||
* the following conditions are aheared to. The following conditions
|
||||
* apply to all code found in this distribution, be it the RC4, RSA,
|
||||
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
|
||||
* included with this distribution is covered by the same copyright terms
|
||||
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
|
||||
*
|
||||
* Copyright remains Eric Young's, and as such any Copyright notices in
|
||||
* the code are not to be removed.
|
||||
* If this package is used in a product, Eric Young should be given attribution
|
||||
* as the author of the parts of the library used.
|
||||
* This can be in the form of a textual message at program startup or
|
||||
* in documentation (online or textual) provided with the package.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* "This product includes cryptographic software written by
|
||||
* Eric Young (eay@cryptsoft.com)"
|
||||
* The word 'cryptographic' can be left out if the rouines from the library
|
||||
* being used are not cryptographic related :-).
|
||||
* 4. If you include any Windows specific code (or a derivative thereof) from
|
||||
* the apps directory (application code) you must include an acknowledgement:
|
||||
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* The licence and distribution terms for any publically available version or
|
||||
* derivative of this code cannot be changed. i.e. this code cannot simply be
|
||||
* copied and put under another distribution licence
|
||||
* [including the GNU Public Licence.]
|
||||
*
|
||||
* This product includes cryptographic software written by Eric Young
|
||||
* (eay@cryptsoft.com). This product includes software written by Tim
|
||||
* Hudson (tjh@cryptsoft.com). */
|
||||
|
||||
#ifndef OPENSSL_HEADER_CPU_H
|
||||
#define OPENSSL_HEADER_CPU_H
|
||||
|
||||
#include <GFp/base.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Runtime CPU feature support */
|
||||
|
||||
|
||||
#if defined(OPENSSL_X86) || defined(OPENSSL_X86_64)
|
||||
/* GFp_ia32cap_P contains the Intel CPUID bits when running on an x86 or
|
||||
* x86-64 system.
|
||||
*
|
||||
* Index 0:
|
||||
* EDX for CPUID where EAX = 1
|
||||
* Bit 20 is always zero
|
||||
* Bit 28 is adjusted to reflect whether the data cache is shared between
|
||||
* multiple logical cores
|
||||
* Bit 30 is used to indicate an Intel CPU
|
||||
* Index 1:
|
||||
* ECX for CPUID where EAX = 1
|
||||
* Bit 11 is used to indicate AMD XOP support, not SDBG
|
||||
* Index 2:
|
||||
* EBX for CPUID where EAX = 7
|
||||
* Index 3 is set to zero.
|
||||
*
|
||||
* Note: the CPUID bits are pre-adjusted for the OSXSAVE bit and the YMM and XMM
|
||||
* bits in XCR0, so it is not necessary to check those. */
|
||||
extern uint32_t GFp_ia32cap_P[4];
|
||||
#endif
|
||||
|
||||
#if defined(OPENSSL_ARM) || defined(OPENSSL_AARCH64)
|
||||
|
||||
#if defined(OPENSSL_APPLE)
|
||||
/* iOS builds use the static ARM configuration. */
|
||||
#define OPENSSL_STATIC_ARMCAP
|
||||
|
||||
#if defined(OPENSSL_AARCH64)
|
||||
#define OPENSSL_STATIC_ARMCAP_AES
|
||||
#define OPENSSL_STATIC_ARMCAP_SHA1
|
||||
#define OPENSSL_STATIC_ARMCAP_SHA256
|
||||
#define OPENSSL_STATIC_ARMCAP_PMULL
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if !defined(OPENSSL_STATIC_ARMCAP)
|
||||
|
||||
/* GFp_is_NEON_capable_at_runtime returns true if the current CPU has a NEON
|
||||
* unit. Note that |OPENSSL_armcap_P| also exists and contains the same
|
||||
* information in a form that's easier for assembly to use. */
|
||||
OPENSSL_EXPORT uint8_t GFp_is_NEON_capable_at_runtime(void);
|
||||
|
||||
/* GFp_is_NEON_capable returns true if the current CPU has a NEON unit. If
|
||||
* this is known statically then it returns one immediately. */
|
||||
static inline int GFp_is_NEON_capable(void) {
|
||||
/* On 32-bit ARM, one CPU is known to have a broken NEON unit which is known
|
||||
* to fail with on some hand-written NEON assembly. Assume that non-Android
|
||||
* applications will not use that buggy CPU but still support Android users
|
||||
* that do, even when the compiler is instructed to freely emit NEON code.
|
||||
* See https://crbug.com/341598 and https://crbug.com/606629. */
|
||||
#if defined(__ARM_NEON__) && (!defined(OPENSSL_ARM) || !defined(__ANDROID__))
|
||||
return 1;
|
||||
#else
|
||||
return GFp_is_NEON_capable_at_runtime();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(OPENSSL_ARM)
|
||||
/* GFp_has_broken_NEON returns one if the current CPU is known to have a
|
||||
* broken NEON unit. See https://crbug.com/341598. */
|
||||
OPENSSL_EXPORT int GFp_has_broken_NEON(void);
|
||||
#endif
|
||||
|
||||
/* GFp_is_ARMv8_AES_capable returns true if the current CPU supports the
|
||||
* ARMv8 AES instruction. */
|
||||
int GFp_is_ARMv8_AES_capable(void);
|
||||
|
||||
/* GFp_is_ARMv8_PMULL_capable returns true if the current CPU supports the
|
||||
* ARMv8 PMULL instruction. */
|
||||
int GFp_is_ARMv8_PMULL_capable(void);
|
||||
|
||||
#else
|
||||
|
||||
static inline int GFp_is_NEON_capable(void) {
|
||||
#if defined(OPENSSL_STATIC_ARMCAP_NEON) || defined(__ARM_NEON__)
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int GFp_is_ARMv8_AES_capable(void) {
|
||||
#if defined(OPENSSL_STATIC_ARMCAP_AES)
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int GFp_is_ARMv8_PMULL_capable(void) {
|
||||
#if defined(OPENSSL_STATIC_ARMCAP_PMULL)
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* OPENSSL_STATIC_ARMCAP */
|
||||
#endif /* OPENSSL_ARM || OPENSSL_AARCH64 */
|
||||
|
||||
#if defined(OPENSSL_PPC64LE)
|
||||
|
||||
/* CRYPTO_is_PPC64LE_vcrypto_capable returns true iff the current CPU supports
|
||||
* the Vector.AES category of instructions. */
|
||||
int CRYPTO_is_PPC64LE_vcrypto_capable(void);
|
||||
|
||||
#endif /* OPENSSL_PPC64LE */
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* extern C */
|
||||
#endif
|
||||
|
||||
#endif /* OPENSSL_HEADER_CPU_H */
|
||||
93
test-generator/cbits/GFp/mem.h
Normal file
93
test-generator/cbits/GFp/mem.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* This package is an SSL implementation written
|
||||
* by Eric Young (eay@cryptsoft.com).
|
||||
* The implementation was written so as to conform with Netscapes SSL.
|
||||
*
|
||||
* This library is free for commercial and non-commercial use as long as
|
||||
* the following conditions are aheared to. The following conditions
|
||||
* apply to all code found in this distribution, be it the RC4, RSA,
|
||||
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
|
||||
* included with this distribution is covered by the same copyright terms
|
||||
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
|
||||
*
|
||||
* Copyright remains Eric Young's, and as such any Copyright notices in
|
||||
* the code are not to be removed.
|
||||
* If this package is used in a product, Eric Young should be given attribution
|
||||
* as the author of the parts of the library used.
|
||||
* This can be in the form of a textual message at program startup or
|
||||
* in documentation (online or textual) provided with the package.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* "This product includes cryptographic software written by
|
||||
* Eric Young (eay@cryptsoft.com)"
|
||||
* The word 'cryptographic' can be left out if the rouines from the library
|
||||
* being used are not cryptographic related :-).
|
||||
* 4. If you include any Windows specific code (or a derivative thereof) from
|
||||
* the apps directory (application code) you must include an acknowledgement:
|
||||
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* The licence and distribution terms for any publically available version or
|
||||
* derivative of this code cannot be changed. i.e. this code cannot simply be
|
||||
* copied and put under another distribution licence
|
||||
* [including the GNU Public Licence.] */
|
||||
|
||||
#ifndef OPENSSL_HEADER_MEM_H
|
||||
#define OPENSSL_HEADER_MEM_H
|
||||
|
||||
#include <GFp/base.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Memory and string functions, see also buf.h.
|
||||
*
|
||||
* OpenSSL has, historically, had a complex set of malloc debugging options.
|
||||
* However, that was written in a time before Valgrind and ASAN. Since we now
|
||||
* have those tools, the OpenSSL allocation functions are simply macros around
|
||||
* the standard memory functions. */
|
||||
|
||||
|
||||
#define OPENSSL_malloc malloc
|
||||
#define OPENSSL_realloc realloc
|
||||
#define OPENSSL_free free
|
||||
|
||||
/* GFp_memcmp returns zero iff the |len| bytes at |a| and |b| are equal. It
|
||||
* takes an amount of time dependent on |len|, but independent of the contents
|
||||
* of |a| and |b|. Unlike memcmp, it cannot be used to put elements into a
|
||||
* defined order as the return value when a != b is undefined, other than to be
|
||||
* non-zero. */
|
||||
OPENSSL_EXPORT int GFp_memcmp(const void *a, const void *b, size_t len);
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* extern C */
|
||||
#endif
|
||||
|
||||
#endif /* OPENSSL_HEADER_MEM_H */
|
||||
75
test-generator/cbits/GFp/type_check.h
Normal file
75
test-generator/cbits/GFp/type_check.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* This package is an SSL implementation written
|
||||
* by Eric Young (eay@cryptsoft.com).
|
||||
* The implementation was written so as to conform with Netscapes SSL.
|
||||
*
|
||||
* This library is free for commercial and non-commercial use as long as
|
||||
* the following conditions are aheared to. The following conditions
|
||||
* apply to all code found in this distribution, be it the RC4, RSA,
|
||||
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
|
||||
* included with this distribution is covered by the same copyright terms
|
||||
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
|
||||
*
|
||||
* Copyright remains Eric Young's, and as such any Copyright notices in
|
||||
* the code are not to be removed.
|
||||
* If this package is used in a product, Eric Young should be given attribution
|
||||
* as the author of the parts of the library used.
|
||||
* This can be in the form of a textual message at program startup or
|
||||
* in documentation (online or textual) provided with the package.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* "This product includes cryptographic software written by
|
||||
* Eric Young (eay@cryptsoft.com)"
|
||||
* The word 'cryptographic' can be left out if the rouines from the library
|
||||
* being used are not cryptographic related :-).
|
||||
* 4. If you include any Windows specific code (or a derivative thereof) from
|
||||
* the apps directory (application code) you must include an acknowledgement:
|
||||
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* The licence and distribution terms for any publically available version or
|
||||
* derivative of this code cannot be changed. i.e. this code cannot simply be
|
||||
* copied and put under another distribution licence
|
||||
* [including the GNU Public Licence.] */
|
||||
|
||||
#ifndef OPENSSL_HEADER_TYPE_CHECK_H
|
||||
#define OPENSSL_HEADER_TYPE_CHECK_H
|
||||
|
||||
#include <GFp/base.h>
|
||||
|
||||
|
||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
|
||||
#define OPENSSL_COMPILE_ASSERT(cond, msg) _Static_assert(cond, #msg)
|
||||
#elif defined(__GNUC__)
|
||||
#define OPENSSL_COMPILE_ASSERT(cond, msg) \
|
||||
typedef char OPENSSL_COMPILE_ASSERT_##msg[((cond) ? 1 : -1)] \
|
||||
__attribute__((unused))
|
||||
#else
|
||||
#define OPENSSL_COMPILE_ASSERT(cond, msg) \
|
||||
typedef char OPENSSL_COMPILE_ASSERT_##msg[((cond) ? 1 : -1)]
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* OPENSSL_HEADER_TYPE_CHECK_H */
|
||||
2133
test-generator/cbits/asm/x25519-asm-arm.S
Normal file
2133
test-generator/cbits/asm/x25519-asm-arm.S
Normal file
File diff suppressed because it is too large
Load Diff
1921
test-generator/cbits/asm/x25519-asm-x86_64.S
Normal file
1921
test-generator/cbits/asm/x25519-asm-x86_64.S
Normal file
File diff suppressed because it is too large
Load Diff
4726
test-generator/cbits/curve25519.c
Normal file
4726
test-generator/cbits/curve25519.c
Normal file
File diff suppressed because it is too large
Load Diff
109
test-generator/cbits/internal.h
Normal file
109
test-generator/cbits/internal.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* Copyright (c) 2015, Google Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
|
||||
#ifndef OPENSSL_HEADER_CURVE25519_INTERNAL_H
|
||||
#define OPENSSL_HEADER_CURVE25519_INTERNAL_H
|
||||
|
||||
#include <GFp/base.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(OPENSSL_X86_64) && !defined(OPENSSL_SMALL) && \
|
||||
!defined(OPENSSL_WINDOWS) && !defined(OPENSSL_NO_ASM)
|
||||
/* This isn't compatible with Windows because the asm code makes use of the red
|
||||
* zone, which Windows doesn't support. */
|
||||
#define BORINGSSL_X25519_X86_64
|
||||
|
||||
void GFp_x25519_x86_64(uint8_t out[32], const uint8_t scalar[32],
|
||||
const uint8_t point[32]);
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(OPENSSL_ARM) && !defined(OPENSSL_NO_ASM)
|
||||
#define BORINGSSL_X25519_NEON
|
||||
|
||||
/* x25519_NEON is defined in asm/x25519-arm.S. */
|
||||
void GFp_x25519_NEON(uint8_t out[32], const uint8_t scalar[32],
|
||||
const uint8_t point[32]);
|
||||
#endif
|
||||
|
||||
/* fe means field element. Here the field is \Z/(2^255-19). An element t,
|
||||
* entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77
|
||||
* t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on
|
||||
* context.
|
||||
*
|
||||
* Keep in sync with `Elem` and `ELEM_LIMBS` in curve25519/ops.rs. */
|
||||
typedef int32_t fe[10];
|
||||
|
||||
/* ge means group element.
|
||||
|
||||
* Here the group is the set of pairs (x,y) of field elements (see fe.h)
|
||||
* satisfying -x^2 + y^2 = 1 + d x^2y^2
|
||||
* where d = -121665/121666.
|
||||
*
|
||||
* Representations:
|
||||
* ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
|
||||
* ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
|
||||
* ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
|
||||
* ge_precomp (Duif): (y+x,y-x,2dxy)
|
||||
*/
|
||||
|
||||
/* Keep in sync with `Point` in curve25519/ops.rs. */
|
||||
typedef struct {
|
||||
fe X;
|
||||
fe Y;
|
||||
fe Z;
|
||||
} ge_p2;
|
||||
|
||||
|
||||
/* Keep in sync with `ExtPoint` in curve25519/ops.rs. */
|
||||
typedef struct {
|
||||
fe X;
|
||||
fe Y;
|
||||
fe Z;
|
||||
fe T;
|
||||
} ge_p3;
|
||||
|
||||
typedef struct {
|
||||
fe X;
|
||||
fe Y;
|
||||
fe Z;
|
||||
fe T;
|
||||
} ge_p1p1;
|
||||
|
||||
typedef struct {
|
||||
fe yplusx;
|
||||
fe yminusx;
|
||||
fe xy2d;
|
||||
} ge_precomp;
|
||||
|
||||
typedef struct {
|
||||
fe YplusX;
|
||||
fe YminusX;
|
||||
fe Z;
|
||||
fe T2d;
|
||||
} ge_cached;
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* extern C */
|
||||
#endif
|
||||
|
||||
#endif /* OPENSSL_HEADER_CURVE25519_INTERNAL_H */
|
||||
361
test-generator/cbits/internal2.h
Normal file
361
test-generator/cbits/internal2.h
Normal file
@@ -0,0 +1,361 @@
|
||||
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* This package is an SSL implementation written
|
||||
* by Eric Young (eay@cryptsoft.com).
|
||||
* The implementation was written so as to conform with Netscapes SSL.
|
||||
*
|
||||
* This library is free for commercial and non-commercial use as long as
|
||||
* the following conditions are aheared to. The following conditions
|
||||
* apply to all code found in this distribution, be it the RC4, RSA,
|
||||
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
|
||||
* included with this distribution is covered by the same copyright terms
|
||||
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
|
||||
*
|
||||
* Copyright remains Eric Young's, and as such any Copyright notices in
|
||||
* the code are not to be removed.
|
||||
* If this package is used in a product, Eric Young should be given attribution
|
||||
* as the author of the parts of the library used.
|
||||
* This can be in the form of a textual message at program startup or
|
||||
* in documentation (online or textual) provided with the package.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* "This product includes cryptographic software written by
|
||||
* Eric Young (eay@cryptsoft.com)"
|
||||
* The word 'cryptographic' can be left out if the rouines from the library
|
||||
* being used are not cryptographic related :-).
|
||||
* 4. If you include any Windows specific code (or a derivative thereof) from
|
||||
* the apps directory (application code) you must include an acknowledgement:
|
||||
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* The licence and distribution terms for any publically available version or
|
||||
* derivative of this code cannot be changed. i.e. this code cannot simply be
|
||||
* copied and put under another distribution licence
|
||||
* [including the GNU Public Licence.]
|
||||
*/
|
||||
/* ====================================================================
|
||||
* Copyright (c) 1998-2001 The OpenSSL Project. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
|
||||
*
|
||||
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please contact
|
||||
* openssl-core@openssl.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "OpenSSL"
|
||||
* nor may "OpenSSL" appear in their names without prior written
|
||||
* permission of the OpenSSL Project.
|
||||
*
|
||||
* 6. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by the OpenSSL Project
|
||||
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
|
||||
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This product includes cryptographic software written by Eric Young
|
||||
* (eay@cryptsoft.com). This product includes software written by Tim
|
||||
* Hudson (tjh@cryptsoft.com). */
|
||||
|
||||
#ifndef OPENSSL_HEADER_CRYPTO_INTERNAL_H
|
||||
#define OPENSSL_HEADER_CRYPTO_INTERNAL_H
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(__clang__) || defined(_MSC_VER)
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <GFp/base.h>
|
||||
#include <GFp/type_check.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push, 3)
|
||||
#include <intrin.h>
|
||||
#pragma warning(pop)
|
||||
#if !defined(__cplusplus)
|
||||
#define alignas(x) __declspec(align(x))
|
||||
#define alignof __alignof
|
||||
#endif
|
||||
#elif !defined(__clang__) && defined(__GNUC__) && __GNUC__ == 4 && \
|
||||
__GNUC_MINOR__ <= 6
|
||||
#define alignas(x) __attribute__((aligned (x)))
|
||||
#define alignof __alignof__
|
||||
#else
|
||||
#include <stdalign.h>
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(OPENSSL_X86) || defined(OPENSSL_X86_64) || defined(OPENSSL_ARM) || \
|
||||
defined(OPENSSL_AARCH64) || defined(OPENSSL_PPC64LE)
|
||||
/* OPENSSL_cpuid_setup initializes the platform-specific feature cache. */
|
||||
void GFp_cpuid_setup(void);
|
||||
#endif
|
||||
|
||||
#define OPENSSL_LITTLE_ENDIAN 1
|
||||
#define OPENSSL_BIG_ENDIAN 2
|
||||
|
||||
#if defined(OPENSSL_X86_64) || defined(OPENSSL_X86) || \
|
||||
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
|
||||
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
#define OPENSSL_ENDIAN OPENSSL_LITTLE_ENDIAN
|
||||
#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
|
||||
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#define OPENSSL_ENDIAN OPENSSL_BIG_ENDIAN
|
||||
#else
|
||||
#error "Cannot determine endianness"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define bswap_u32(x) __builtin_bswap32(x)
|
||||
#define bswap_u64(x) __builtin_bswap64(x)
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma intrinsic(_byteswap_ulong, _byteswap_uint64)
|
||||
#define bswap_u32(x) _byteswap_ulong(x)
|
||||
#define bswap_u64(x) _byteswap_uint64(x)
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(_MSC_VER) && defined(OPENSSL_64_BIT)
|
||||
typedef __int128_t int128_t;
|
||||
typedef __uint128_t uint128_t;
|
||||
#endif
|
||||
|
||||
|
||||
/* Constant-time utility functions.
|
||||
*
|
||||
* The following methods return a bitmask of all ones (0xff...f) for true and 0
|
||||
* for false. This is useful for choosing a value based on the result of a
|
||||
* conditional in constant time. */
|
||||
|
||||
/* constant_time_msb returns the given value with the MSB copied to all the
|
||||
* other bits. */
|
||||
static inline unsigned int constant_time_msb_unsigned(unsigned int a) {
|
||||
return (unsigned int)((int)(a) >> (sizeof(int) * 8 - 1));
|
||||
}
|
||||
|
||||
OPENSSL_COMPILE_ASSERT(sizeof(ptrdiff_t) == sizeof(size_t),
|
||||
ptrdiff_t_and_size_t_are_different_sizes);
|
||||
|
||||
static inline size_t constant_time_msb_size_t(size_t a) {
|
||||
return (size_t)((ptrdiff_t)(a) >> (sizeof(ptrdiff_t) * 8 - 1));
|
||||
}
|
||||
|
||||
|
||||
/* constant_time_is_zero returns 0xff..f if a == 0 and 0 otherwise. */
|
||||
static inline unsigned int constant_time_is_zero_unsigned(unsigned int a) {
|
||||
/* Here is an SMT-LIB verification of this formula:
|
||||
*
|
||||
* (define-fun is_zero ((a (_ BitVec 32))) (_ BitVec 32)
|
||||
* (bvand (bvnot a) (bvsub a #x00000001))
|
||||
* )
|
||||
*
|
||||
* (declare-fun a () (_ BitVec 32))
|
||||
*
|
||||
* (assert (not (= (= #x00000001 (bvlshr (is_zero a) #x0000001f)) (= a #x00000000))))
|
||||
* (check-sat)
|
||||
* (get-model)
|
||||
*/
|
||||
return constant_time_msb_unsigned(~a & (a - 1));
|
||||
}
|
||||
|
||||
/* constant_time_is_zero_size_t is like |constant_time_is_zero_unsigned| but
|
||||
* operates on |size_t|. */
|
||||
static inline size_t constant_time_is_zero_size_t(size_t a) {
|
||||
return constant_time_msb_size_t(~a & (a - 1));
|
||||
}
|
||||
|
||||
static inline size_t constant_time_is_nonzero_size_t(size_t a) {
|
||||
return constant_time_is_zero_size_t(constant_time_is_zero_size_t(a));
|
||||
}
|
||||
|
||||
/* constant_time_eq_int returns 0xff..f if a == b and 0 otherwise. */
|
||||
static inline unsigned int constant_time_eq_int(int a, int b) {
|
||||
return constant_time_is_zero_unsigned((unsigned)(a) ^ (unsigned)(b));
|
||||
}
|
||||
|
||||
/* constant_time_eq_size_t acts like |constant_time_eq_int| but operates on
|
||||
* |size_t|. */
|
||||
static inline size_t constant_time_eq_size_t(size_t a, size_t b) {
|
||||
return constant_time_is_zero_size_t(a ^ b);
|
||||
}
|
||||
|
||||
/* constant_time_select_size_t returns (mask & a) | (~mask & b). When |mask| is
|
||||
* all 1s or all 0s (as returned by the methods above), the select methods
|
||||
* return either |a| (if |mask| is nonzero) or |b| (if |mask| is zero). it is
|
||||
* derived from BoringSSL's |constant_time_select|. */
|
||||
static inline size_t constant_time_select_size_t(size_t mask, size_t a,
|
||||
size_t b) {
|
||||
return (mask & a) | (~mask & b);
|
||||
}
|
||||
|
||||
/* from_be_u32_ptr returns the 32-bit big-endian-encoded value at |data|. */
|
||||
static inline uint32_t from_be_u32_ptr(const uint8_t *data) {
|
||||
#if defined(__clang__) || defined(_MSC_VER)
|
||||
/* XXX: Unlike GCC, Clang doesn't optimize compliant access to unaligned data
|
||||
* well. See https://llvm.org/bugs/show_bug.cgi?id=20605,
|
||||
* https://llvm.org/bugs/show_bug.cgi?id=17603,
|
||||
* http://blog.regehr.org/archives/702, and
|
||||
* http://blog.regehr.org/archives/1055. MSVC seems to have similar problems.
|
||||
*/
|
||||
uint32_t value;
|
||||
memcpy(&value, data, sizeof(value));
|
||||
#if OPENSSL_ENDIAN != OPENSSL_BIG_ENDIAN
|
||||
value = bswap_u32(value);
|
||||
#endif
|
||||
return value;
|
||||
#else
|
||||
return ((uint32_t)data[0] << 24) |
|
||||
((uint32_t)data[1] << 16) |
|
||||
((uint32_t)data[2] << 8) |
|
||||
((uint32_t)data[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* from_be_u64_ptr returns the 64-bit big-endian-encoded value at |data|. */
|
||||
static inline uint64_t from_be_u64_ptr(const uint8_t *data) {
|
||||
#if defined(__clang__) || defined(_MSC_VER)
|
||||
/* XXX: Unlike GCC, Clang doesn't optimize compliant access to unaligned data
|
||||
* well. See https://llvm.org/bugs/show_bug.cgi?id=20605,
|
||||
* https://llvm.org/bugs/show_bug.cgi?id=17603,
|
||||
* http://blog.regehr.org/archives/702, and
|
||||
* http://blog.regehr.org/archives/1055. MSVC seems to have similar problems.
|
||||
*/
|
||||
uint64_t value;
|
||||
memcpy(&value, data, sizeof(value));
|
||||
#if OPENSSL_ENDIAN != OPENSSL_BIG_ENDIAN
|
||||
value = bswap_u64(value);
|
||||
#endif
|
||||
return value;
|
||||
#else
|
||||
return ((uint64_t)data[0] << 56) |
|
||||
((uint64_t)data[1] << 48) |
|
||||
((uint64_t)data[2] << 40) |
|
||||
((uint64_t)data[3] << 32) |
|
||||
((uint64_t)data[4] << 24) |
|
||||
((uint64_t)data[5] << 16) |
|
||||
((uint64_t)data[6] << 8) |
|
||||
((uint64_t)data[7]);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* to_be_u32_ptr writes the value |x| to the location |out| in big-endian
|
||||
order. */
|
||||
static inline void to_be_u32_ptr(uint8_t *out, uint32_t value) {
|
||||
#if defined(__clang__) || defined(_MSC_VER)
|
||||
/* XXX: Unlike GCC, Clang doesn't optimize compliant access to unaligned data
|
||||
* well. See https://llvm.org/bugs/show_bug.cgi?id=20605,
|
||||
* https://llvm.org/bugs/show_bug.cgi?id=17603,
|
||||
* http://blog.regehr.org/archives/702, and
|
||||
* http://blog.regehr.org/archives/1055. MSVC seems to have similar problems.
|
||||
*/
|
||||
#if OPENSSL_ENDIAN != OPENSSL_BIG_ENDIAN
|
||||
value = bswap_u32(value);
|
||||
#endif
|
||||
memcpy(out, &value, sizeof(value));
|
||||
#else
|
||||
out[0] = (uint8_t)(value >> 24);
|
||||
out[1] = (uint8_t)(value >> 16);
|
||||
out[2] = (uint8_t)(value >> 8);
|
||||
out[3] = (uint8_t)value;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* to_be_u64_ptr writes the value |value| to the location |out| in big-endian
|
||||
order. */
|
||||
static inline void to_be_u64_ptr(uint8_t *out, uint64_t value) {
|
||||
#if defined(__clang__) || defined(_MSC_VER)
|
||||
/* XXX: Unlike GCC, Clang doesn't optimize compliant access to unaligned data
|
||||
* well. See https://llvm.org/bugs/show_bug.cgi?id=20605,
|
||||
* https://llvm.org/bugs/show_bug.cgi?id=17603,
|
||||
* http://blog.regehr.org/archives/702, and
|
||||
* http://blog.regehr.org/archives/1055. MSVC seems to have similar problems.
|
||||
*/
|
||||
#if OPENSSL_ENDIAN != OPENSSL_BIG_ENDIAN
|
||||
value = bswap_u64(value);
|
||||
#endif
|
||||
memcpy(out, &value, sizeof(value));
|
||||
#else
|
||||
out[0] = (uint8_t)(value >> 56);
|
||||
out[1] = (uint8_t)(value >> 48);
|
||||
out[2] = (uint8_t)(value >> 40);
|
||||
out[3] = (uint8_t)(value >> 32);
|
||||
out[4] = (uint8_t)(value >> 24);
|
||||
out[5] = (uint8_t)(value >> 16);
|
||||
out[6] = (uint8_t)(value >> 8);
|
||||
out[7] = (uint8_t)value;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* from_be_u64 returns the native representation of the 64-bit
|
||||
* big-endian-encoded value |x|. */
|
||||
static inline uint64_t from_be_u64(uint64_t x) {
|
||||
#if OPENSSL_ENDIAN != OPENSSL_BIG_ENDIAN
|
||||
x = bswap_u64(x);
|
||||
#endif
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* extern C */
|
||||
#endif
|
||||
|
||||
#endif /* OPENSSL_HEADER_CRYPTO_INTERNAL_H */
|
||||
1921
test-generator/cbits/x25519-asm-x86_64.S
Normal file
1921
test-generator/cbits/x25519-asm-x86_64.S
Normal file
File diff suppressed because it is too large
Load Diff
244
test-generator/cbits/x25519-x86_64.c
Normal file
244
test-generator/cbits/x25519-x86_64.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/* Copyright (c) 2015, Google Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
|
||||
/* This code is mostly taken from the ref10 version of Ed25519 in SUPERCOP
|
||||
* 20141124 (http://bench.cr.yp.to/supercop.html). That code is released as
|
||||
* public domain but this file has the ISC license just to keep licencing
|
||||
* simple.
|
||||
*
|
||||
* The field functions are shared by Ed25519 and X25519 where possible. */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
#if defined(BORINGSSL_X25519_X86_64)
|
||||
|
||||
typedef struct { uint64_t v[5]; } fe25519;
|
||||
|
||||
/* These functions are defined in asm/x25519-x86_64.S */
|
||||
void GFp_x25519_x86_64_work_cswap(fe25519 *, uint64_t);
|
||||
void GFp_x25519_x86_64_mul(fe25519 *out, const fe25519 *a, const fe25519 *b);
|
||||
void GFp_x25519_x86_64_square(fe25519 *out, const fe25519 *a);
|
||||
void GFp_x25519_x86_64_freeze(fe25519 *);
|
||||
void GFp_x25519_x86_64_ladderstep(fe25519 *work);
|
||||
|
||||
static void fe25519_setint(fe25519 *r, unsigned v) {
|
||||
r->v[0] = v;
|
||||
r->v[1] = 0;
|
||||
r->v[2] = 0;
|
||||
r->v[3] = 0;
|
||||
r->v[4] = 0;
|
||||
}
|
||||
|
||||
/* Assumes input x being reduced below 2^255 */
|
||||
static void fe25519_pack(unsigned char r[32], const fe25519 *x) {
|
||||
fe25519 t;
|
||||
t = *x;
|
||||
GFp_x25519_x86_64_freeze(&t);
|
||||
|
||||
r[0] = (uint8_t)(t.v[0] & 0xff);
|
||||
r[1] = (uint8_t)((t.v[0] >> 8) & 0xff);
|
||||
r[2] = (uint8_t)((t.v[0] >> 16) & 0xff);
|
||||
r[3] = (uint8_t)((t.v[0] >> 24) & 0xff);
|
||||
r[4] = (uint8_t)((t.v[0] >> 32) & 0xff);
|
||||
r[5] = (uint8_t)((t.v[0] >> 40) & 0xff);
|
||||
r[6] = (uint8_t)((t.v[0] >> 48));
|
||||
|
||||
r[6] ^= (uint8_t)((t.v[1] << 3) & 0xf8);
|
||||
r[7] = (uint8_t)((t.v[1] >> 5) & 0xff);
|
||||
r[8] = (uint8_t)((t.v[1] >> 13) & 0xff);
|
||||
r[9] = (uint8_t)((t.v[1] >> 21) & 0xff);
|
||||
r[10] = (uint8_t)((t.v[1] >> 29) & 0xff);
|
||||
r[11] = (uint8_t)((t.v[1] >> 37) & 0xff);
|
||||
r[12] = (uint8_t)((t.v[1] >> 45));
|
||||
|
||||
r[12] ^= (uint8_t)((t.v[2] << 6) & 0xc0);
|
||||
r[13] = (uint8_t)((t.v[2] >> 2) & 0xff);
|
||||
r[14] = (uint8_t)((t.v[2] >> 10) & 0xff);
|
||||
r[15] = (uint8_t)((t.v[2] >> 18) & 0xff);
|
||||
r[16] = (uint8_t)((t.v[2] >> 26) & 0xff);
|
||||
r[17] = (uint8_t)((t.v[2] >> 34) & 0xff);
|
||||
r[18] = (uint8_t)((t.v[2] >> 42) & 0xff);
|
||||
r[19] = (uint8_t)((t.v[2] >> 50));
|
||||
|
||||
r[19] ^= (uint8_t)((t.v[3] << 1) & 0xfe);
|
||||
r[20] = (uint8_t)((t.v[3] >> 7) & 0xff);
|
||||
r[21] = (uint8_t)((t.v[3] >> 15) & 0xff);
|
||||
r[22] = (uint8_t)((t.v[3] >> 23) & 0xff);
|
||||
r[23] = (uint8_t)((t.v[3] >> 31) & 0xff);
|
||||
r[24] = (uint8_t)((t.v[3] >> 39) & 0xff);
|
||||
r[25] = (uint8_t)((t.v[3] >> 47));
|
||||
|
||||
r[25] ^= (uint8_t)((t.v[4] << 4) & 0xf0);
|
||||
r[26] = (uint8_t)((t.v[4] >> 4) & 0xff);
|
||||
r[27] = (uint8_t)((t.v[4] >> 12) & 0xff);
|
||||
r[28] = (uint8_t)((t.v[4] >> 20) & 0xff);
|
||||
r[29] = (uint8_t)((t.v[4] >> 28) & 0xff);
|
||||
r[30] = (uint8_t)((t.v[4] >> 36) & 0xff);
|
||||
r[31] = (uint8_t)((t.v[4] >> 44));
|
||||
}
|
||||
|
||||
static void fe25519_unpack(fe25519 *r, const uint8_t x[32]) {
|
||||
r->v[0] = x[0];
|
||||
r->v[0] += (uint64_t)x[1] << 8;
|
||||
r->v[0] += (uint64_t)x[2] << 16;
|
||||
r->v[0] += (uint64_t)x[3] << 24;
|
||||
r->v[0] += (uint64_t)x[4] << 32;
|
||||
r->v[0] += (uint64_t)x[5] << 40;
|
||||
r->v[0] += ((uint64_t)x[6] & 7) << 48;
|
||||
|
||||
r->v[1] = x[6] >> 3;
|
||||
r->v[1] += (uint64_t)x[7] << 5;
|
||||
r->v[1] += (uint64_t)x[8] << 13;
|
||||
r->v[1] += (uint64_t)x[9] << 21;
|
||||
r->v[1] += (uint64_t)x[10] << 29;
|
||||
r->v[1] += (uint64_t)x[11] << 37;
|
||||
r->v[1] += ((uint64_t)x[12] & 63) << 45;
|
||||
|
||||
r->v[2] = x[12] >> 6;
|
||||
r->v[2] += (uint64_t)x[13] << 2;
|
||||
r->v[2] += (uint64_t)x[14] << 10;
|
||||
r->v[2] += (uint64_t)x[15] << 18;
|
||||
r->v[2] += (uint64_t)x[16] << 26;
|
||||
r->v[2] += (uint64_t)x[17] << 34;
|
||||
r->v[2] += (uint64_t)x[18] << 42;
|
||||
r->v[2] += ((uint64_t)x[19] & 1) << 50;
|
||||
|
||||
r->v[3] = x[19] >> 1;
|
||||
r->v[3] += (uint64_t)x[20] << 7;
|
||||
r->v[3] += (uint64_t)x[21] << 15;
|
||||
r->v[3] += (uint64_t)x[22] << 23;
|
||||
r->v[3] += (uint64_t)x[23] << 31;
|
||||
r->v[3] += (uint64_t)x[24] << 39;
|
||||
r->v[3] += ((uint64_t)x[25] & 15) << 47;
|
||||
|
||||
r->v[4] = x[25] >> 4;
|
||||
r->v[4] += (uint64_t)x[26] << 4;
|
||||
r->v[4] += (uint64_t)x[27] << 12;
|
||||
r->v[4] += (uint64_t)x[28] << 20;
|
||||
r->v[4] += (uint64_t)x[29] << 28;
|
||||
r->v[4] += (uint64_t)x[30] << 36;
|
||||
r->v[4] += ((uint64_t)x[31] & 127) << 44;
|
||||
}
|
||||
|
||||
static void fe25519_invert(fe25519 *r, const fe25519 *x) {
|
||||
fe25519 z2;
|
||||
fe25519 z9;
|
||||
fe25519 z11;
|
||||
fe25519 z2_5_0;
|
||||
fe25519 z2_10_0;
|
||||
fe25519 z2_20_0;
|
||||
fe25519 z2_50_0;
|
||||
fe25519 z2_100_0;
|
||||
fe25519 t;
|
||||
int i;
|
||||
|
||||
/* 2 */ GFp_x25519_x86_64_square(&z2, x);
|
||||
/* 4 */ GFp_x25519_x86_64_square(&t, &z2);
|
||||
/* 8 */ GFp_x25519_x86_64_square(&t, &t);
|
||||
/* 9 */ GFp_x25519_x86_64_mul(&z9, &t, x);
|
||||
/* 11 */ GFp_x25519_x86_64_mul(&z11, &z9, &z2);
|
||||
/* 22 */ GFp_x25519_x86_64_square(&t, &z11);
|
||||
/* 2^5 - 2^0 = 31 */ GFp_x25519_x86_64_mul(&z2_5_0, &t, &z9);
|
||||
|
||||
/* 2^6 - 2^1 */ GFp_x25519_x86_64_square(&t, &z2_5_0);
|
||||
/* 2^20 - 2^10 */ for (i = 1; i < 5; i++) { GFp_x25519_x86_64_square(&t, &t); }
|
||||
/* 2^10 - 2^0 */ GFp_x25519_x86_64_mul(&z2_10_0, &t, &z2_5_0);
|
||||
|
||||
/* 2^11 - 2^1 */ GFp_x25519_x86_64_square(&t, &z2_10_0);
|
||||
/* 2^20 - 2^10 */ for (i = 1; i < 10; i++) { GFp_x25519_x86_64_square(&t, &t); }
|
||||
/* 2^20 - 2^0 */ GFp_x25519_x86_64_mul(&z2_20_0, &t, &z2_10_0);
|
||||
|
||||
/* 2^21 - 2^1 */ GFp_x25519_x86_64_square(&t, &z2_20_0);
|
||||
/* 2^40 - 2^20 */ for (i = 1; i < 20; i++) { GFp_x25519_x86_64_square(&t, &t); }
|
||||
/* 2^40 - 2^0 */ GFp_x25519_x86_64_mul(&t, &t, &z2_20_0);
|
||||
|
||||
/* 2^41 - 2^1 */ GFp_x25519_x86_64_square(&t, &t);
|
||||
/* 2^50 - 2^10 */ for (i = 1; i < 10; i++) { GFp_x25519_x86_64_square(&t, &t); }
|
||||
/* 2^50 - 2^0 */ GFp_x25519_x86_64_mul(&z2_50_0, &t, &z2_10_0);
|
||||
|
||||
/* 2^51 - 2^1 */ GFp_x25519_x86_64_square(&t, &z2_50_0);
|
||||
/* 2^100 - 2^50 */ for (i = 1; i < 50; i++) { GFp_x25519_x86_64_square(&t, &t); }
|
||||
/* 2^100 - 2^0 */ GFp_x25519_x86_64_mul(&z2_100_0, &t, &z2_50_0);
|
||||
|
||||
/* 2^101 - 2^1 */ GFp_x25519_x86_64_square(&t, &z2_100_0);
|
||||
/* 2^200 - 2^100 */ for (i = 1; i < 100; i++) {
|
||||
GFp_x25519_x86_64_square(&t, &t);
|
||||
}
|
||||
/* 2^200 - 2^0 */ GFp_x25519_x86_64_mul(&t, &t, &z2_100_0);
|
||||
|
||||
/* 2^201 - 2^1 */ GFp_x25519_x86_64_square(&t, &t);
|
||||
/* 2^250 - 2^50 */ for (i = 1; i < 50; i++) { GFp_x25519_x86_64_square(&t, &t); }
|
||||
/* 2^250 - 2^0 */ GFp_x25519_x86_64_mul(&t, &t, &z2_50_0);
|
||||
|
||||
/* 2^251 - 2^1 */ GFp_x25519_x86_64_square(&t, &t);
|
||||
/* 2^252 - 2^2 */ GFp_x25519_x86_64_square(&t, &t);
|
||||
/* 2^253 - 2^3 */ GFp_x25519_x86_64_square(&t, &t);
|
||||
|
||||
/* 2^254 - 2^4 */ GFp_x25519_x86_64_square(&t, &t);
|
||||
|
||||
/* 2^255 - 2^5 */ GFp_x25519_x86_64_square(&t, &t);
|
||||
/* 2^255 - 21 */ GFp_x25519_x86_64_mul(r, &t, &z11);
|
||||
}
|
||||
|
||||
static void mladder(fe25519 *xr, fe25519 *zr, const uint8_t s[32]) {
|
||||
fe25519 work[5];
|
||||
|
||||
work[0] = *xr;
|
||||
fe25519_setint(work + 1, 1);
|
||||
fe25519_setint(work + 2, 0);
|
||||
work[3] = *xr;
|
||||
fe25519_setint(work + 4, 1);
|
||||
|
||||
int i, j;
|
||||
uint8_t prevbit = 0;
|
||||
|
||||
j = 6;
|
||||
for (i = 31; i >= 0; i--) {
|
||||
while (j >= 0) {
|
||||
const uint8_t bit = 1 & (s[i] >> j);
|
||||
const uint64_t swap = bit ^ prevbit;
|
||||
prevbit = bit;
|
||||
GFp_x25519_x86_64_work_cswap(work + 1, swap);
|
||||
GFp_x25519_x86_64_ladderstep(work);
|
||||
j -= 1;
|
||||
}
|
||||
j = 7;
|
||||
}
|
||||
|
||||
*xr = work[1];
|
||||
*zr = work[2];
|
||||
}
|
||||
|
||||
void GFp_x25519_x86_64(uint8_t out[32], const uint8_t scalar[32],
|
||||
const uint8_t point[32]) {
|
||||
uint8_t e[32];
|
||||
memcpy(e, scalar, sizeof(e));
|
||||
|
||||
e[0] &= 248;
|
||||
e[31] &= 127;
|
||||
e[31] |= 64;
|
||||
|
||||
fe25519 t;
|
||||
fe25519 z;
|
||||
fe25519_unpack(&t, point);
|
||||
mladder(&t, &z, e);
|
||||
fe25519_invert(&z, &z);
|
||||
GFp_x25519_x86_64_mul(&t, &t, &z);
|
||||
fe25519_pack(out, &t);
|
||||
}
|
||||
|
||||
#endif /* BORINGSSL_X25519_X86_64 */
|
||||
34
test-generator/gcd.hs
Normal file
34
test-generator/gcd.hs
Normal file
@@ -0,0 +1,34 @@
|
||||
import Numeric
|
||||
|
||||
data Set = Set { r :: Integer, s :: Integer, t :: Integer }
|
||||
|
||||
step :: Set -> Set -> Set
|
||||
step old new = Set r' s' t'
|
||||
where
|
||||
quotient = r old `div` r new
|
||||
r' = r old - (r new * quotient)
|
||||
s' = s old - (s new * quotient)
|
||||
t' = t old - (t new * quotient)
|
||||
|
||||
run :: Integer -> Integer -> IO Set
|
||||
run self rhs = go (Set self 1 0) (Set rhs 0 1)
|
||||
where
|
||||
go old new | r new == 0 =
|
||||
do putStrLn "------------------------------"
|
||||
putStrLn ("res_r: " ++ showX (r old))
|
||||
putStrLn ("res_s: " ++ showX (s old))
|
||||
putStrLn ("res_t: " ++ showX (t old))
|
||||
return old
|
||||
| otherwise =
|
||||
do putStrLn "------------------------------"
|
||||
putStrLn ("old_r: " ++ showX (r old))
|
||||
putStrLn ("old_s: " ++ showX (s old))
|
||||
putStrLn ("old_t: " ++ showX (t old))
|
||||
putStrLn ("new_r: " ++ showX (r new))
|
||||
putStrLn ("new_s: " ++ showX (s new))
|
||||
putStrLn ("new_t: " ++ showX (t new))
|
||||
go new (step old new)
|
||||
|
||||
showX :: Integer -> String
|
||||
showX x | x < 0 = "-" ++ showX (abs x)
|
||||
| otherwise = showHex x ""
|
||||
32
test-generator/test-generator.cabal
Normal file
32
test-generator/test-generator.cabal
Normal file
@@ -0,0 +1,32 @@
|
||||
cabal-version: >=1.10
|
||||
-- Initial package description 'test-generator.cabal' generated by 'cabal
|
||||
-- init'. For further documentation, see
|
||||
-- http://haskell.org/cabal/users-guide/
|
||||
|
||||
name: test-generator
|
||||
version: 0.1.0.0
|
||||
synopsis: Test generation helper
|
||||
-- description:
|
||||
homepage: http://github.com/acw
|
||||
-- bug-reports:
|
||||
license: ISC
|
||||
license-file: ../LICENSE
|
||||
author: Adam Wick
|
||||
maintainer: awick@uhsure.com
|
||||
-- copyright:
|
||||
category: Testing
|
||||
build-type: Simple
|
||||
extra-source-files: CHANGELOG.md
|
||||
|
||||
executable gen-tests
|
||||
main-is: Main.hs
|
||||
other-modules: Database, DSA, ECDSATesting, ED25519, ED25519.PrecompPoints, Math, RFC6979, RSA, Task, Utils
|
||||
-- other-extensions:
|
||||
build-depends: base >=4.11 && < 4.14, ascii-progress, bytestring, containers, crypto-api, cryptonite, directory, DSA, filepath, integer-gmp, memory, random
|
||||
hs-source-dirs: .
|
||||
c-sources: cbits/curve25519.c cbits/x25519-x86_64.c cbits/x25519-asm-x86_64.S
|
||||
include-dirs: cbits
|
||||
default-language: Haskell2010
|
||||
ghc-options: -Wall -O2 -threaded -rtsopts -with-rtsopts=-N
|
||||
if os(Windows) || !arch(x86_64)
|
||||
buildable: False
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user