Compare commits
14 Commits
refix
...
explicit_s
| Author | SHA1 | Date | |
|---|---|---|---|
| 6418c323ad | |||
| c14fa90c4a | |||
| 52103991d7 | |||
| 49b2c92d87 | |||
| 02aa03ca5c | |||
| 9594a40d32 | |||
| 10a46a7e81 | |||
| 667e32694e | |||
| 8a8c85703a | |||
| 0698272b2c | |||
| 716a165007 | |||
| ded93767ed | |||
| 17da7a43d6 | |||
| 4b90550225 |
12
.gitignore
vendored
12
.gitignore
vendored
@@ -11,15 +11,3 @@ Cargo.lock
|
|||||||
|
|
||||||
# And these are just annoying
|
# And these are just annoying
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.vscode
|
|
||||||
|
|
||||||
# Ignore testing files
|
|
||||||
**/*.o
|
|
||||||
**/*.hi
|
|
||||||
**/gen
|
|
||||||
**/.cabal-sandbox
|
|
||||||
**/cabal.sandbox.config
|
|
||||||
FlameGraph
|
|
||||||
*.user_stacks
|
|
||||||
**/.ghc.environment.*
|
|
||||||
tests/rsa/dist*
|
|
||||||
3
.travis.yml
Normal file
3
.travis.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
language: rust
|
||||||
|
rust:
|
||||||
|
- nightly
|
||||||
25
Cargo.toml
25
Cargo.toml
@@ -9,26 +9,19 @@ license-file = "LICENSE"
|
|||||||
repository = "https://github.com/acw/simple_crypto"
|
repository = "https://github.com/acw/simple_crypto"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "^1.2.7"
|
byteorder = "^1.2.1"
|
||||||
chrono = "^0.4.6"
|
digest = "^0.7.1"
|
||||||
cryptonum = { path = "../cryptonum" }
|
num = "^0.1.39"
|
||||||
digest = "^0.8.0"
|
rand = "^0.3"
|
||||||
hmac = "^0.7.0"
|
sha-1 = "^0.7.0"
|
||||||
num = "^0.2.0"
|
sha2 = "^0.7.0"
|
||||||
rand = "^0.6.0"
|
simple_asn1 = "^0.1.0"
|
||||||
sha-1 = "^0.8.1"
|
|
||||||
sha2 = "^0.8.0"
|
|
||||||
simple_asn1 = "^0.2.0"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
quickcheck = "^0.7.2"
|
quickcheck = "^0.4.1"
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
opt-level = 1
|
|
||||||
overflow-checks = false
|
|
||||||
|
|
||||||
[profile.test]
|
[profile.test]
|
||||||
opt-level = 2
|
opt-level = 2
|
||||||
debug = true
|
debug = true
|
||||||
debug-assertions = true
|
debug-assertions = true
|
||||||
overflow-checks = false
|
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
- Build RSA test cases from Haskell examples
|
|
||||||
- Build DSA test cases from Haskell examples
|
|
||||||
- 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
|
|
||||||
70
src/cryptonum/arithmetic_traits.rs
Normal file
70
src/cryptonum/arithmetic_traits.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
112
src/cryptonum/barrett.rs
Normal file
112
src/cryptonum/barrett.rs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
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
src/cryptonum/builder.rs
Normal file
1
src/cryptonum/builder.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
77
src/cryptonum/conversions.rs
Normal file
77
src/cryptonum/conversions.rs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
332
src/cryptonum/core.rs
Normal file
332
src/cryptonum/core.rs
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/cryptonum/mod.rs
Normal file
63
src/cryptonum/mod.rs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//! # 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);
|
||||||
|
|
||||||
16
src/cryptonum/modops.rs
Normal file
16
src/cryptonum/modops.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
119
src/cryptonum/primes.rs
Normal file
119
src/cryptonum/primes.rs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
237
src/cryptonum/signed.rs
Normal file
237
src/cryptonum/signed.rs
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
79
src/cryptonum/traits.rs
Normal file
79
src/cryptonum/traits.rs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
685
src/cryptonum/unsigned.rs
Normal file
685
src/cryptonum/unsigned.rs
Normal file
@@ -0,0 +1,685 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
mod errors;
|
|
||||||
mod params;
|
|
||||||
mod private;
|
|
||||||
mod public;
|
|
||||||
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;
|
|
||||||
|
|
||||||
pub struct DSAKeyPair<P,L,N>
|
|
||||||
{
|
|
||||||
pub private: DSAPrivKey<P,N>,
|
|
||||||
pub public: DSAPubKey<P,L>
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait DSAKeyGeneration
|
|
||||||
{
|
|
||||||
type Params;
|
|
||||||
|
|
||||||
fn generate<G: Rng>(params: &Self::Params, rng: &mut G) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! generate_dsa_pair {
|
|
||||||
($ptype: ident, $ltype: ident, $ntype: ident, $nbig: ident) => {
|
|
||||||
impl DSAKeyGeneration for DSAKeyPair<$ptype,$ltype,$ntype>
|
|
||||||
{
|
|
||||||
type Params = $ptype;
|
|
||||||
|
|
||||||
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 + 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 = DSAPrivKey::new(params.clone(), x);
|
|
||||||
let public = DSAPubKey::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);
|
|
||||||
@@ -1,212 +0,0 @@
|
|||||||
use cryptonum::unsigned::{CryptoNum,Decoder,Encoder,ModExp,PrimeGen};
|
|
||||||
use cryptonum::unsigned::{U192,U256,U1024,U2048,U3072};
|
|
||||||
use digest::Digest;
|
|
||||||
use sha2::Sha256;
|
|
||||||
use simple_asn1::{ToASN1,ASN1Block,ASN1Class,ASN1EncodeErr};
|
|
||||||
use rand::Rng;
|
|
||||||
use utils::TranslateNums;
|
|
||||||
|
|
||||||
pub trait DSAParameters : ToASN1
|
|
||||||
{
|
|
||||||
type L;
|
|
||||||
type N;
|
|
||||||
|
|
||||||
fn new(p: Self::L, g: Self::L, q: Self::N) -> Self;
|
|
||||||
fn generate<G: Rng>(rng: &mut G) -> Self;
|
|
||||||
fn n_size() -> usize;
|
|
||||||
fn l_size() -> usize;
|
|
||||||
|
|
||||||
fn n_bits(&self) -> usize {
|
|
||||||
Self::n_size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! generate_parameters {
|
|
||||||
($name: ident, $ltype: ident, $ntype: ident, $l: expr, $n: expr) => {
|
|
||||||
#[derive(Clone)]
|
|
||||||
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
|
|
||||||
{
|
|
||||||
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 ulow = if U.is_even() { 0 } else { 1 };
|
|
||||||
let mut q = $ntype::from(1u64) << (N - 1);
|
|
||||||
q += U;
|
|
||||||
q += $ntype::from(1u64 + ulow);
|
|
||||||
// 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());
|
|
||||||
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::digest(&base).as_slice().to_vec()
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
use cryptonum::unsigned::*;
|
|
||||||
use cryptonum::signed::ModInv;
|
|
||||||
use digest::{BlockInput,Digest,Input,FixedOutput,Reset};
|
|
||||||
use dsa::params::*;
|
|
||||||
use dsa::rfc6979::*;
|
|
||||||
use hmac::{Hmac,Mac};
|
|
||||||
|
|
||||||
pub trait DSAPrivateKey {
|
|
||||||
type Params;
|
|
||||||
type L;
|
|
||||||
type N;
|
|
||||||
|
|
||||||
/// Generate a new private key using the given DSA parameters and private
|
|
||||||
/// key value.
|
|
||||||
fn new(params: Self::Params, x: Self::N) -> Self;
|
|
||||||
/// Generate a DSA signature for the given message, using the appropriate
|
|
||||||
/// hash included in the type invocation.
|
|
||||||
fn sign<Hash>(&self, m: &[u8]) -> DSASignature<Self::N>
|
|
||||||
where
|
|
||||||
Hash: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset,
|
|
||||||
Hmac<Hash>: Mac;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DSAPrivKey<Params,N>
|
|
||||||
{
|
|
||||||
pub(crate) params: Params,
|
|
||||||
pub(crate) x: N
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum DSAPrivate {
|
|
||||||
DSA1024Private(DSAPrivKey<L1024N160,U192>),
|
|
||||||
DSA2048SmallPrivate(DSAPrivKey<L2048N224,U256>),
|
|
||||||
DSA2048Private(DSAPrivKey<L2048N256,U256>),
|
|
||||||
DSA3072Private(DSAPrivKey<L3072N256,U256>)
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! privkey_impls {
|
|
||||||
($ptype: ident, $ltype: ident, $ntype: ident, $big: ident, $bigger: ident, $biggest: ident) => {
|
|
||||||
impl DSAPrivateKey for DSAPrivKey<$ptype,$ntype>
|
|
||||||
{
|
|
||||||
type Params = $ptype;
|
|
||||||
type L = $ltype;
|
|
||||||
type N = $ntype;
|
|
||||||
|
|
||||||
fn new(params: $ptype, x: $ntype) -> DSAPrivKey<$ptype,$ntype>
|
|
||||||
{
|
|
||||||
DSAPrivKey{ params, x }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sign<Hash>(&self, m: &[u8]) -> DSASignature<$ntype>
|
|
||||||
where
|
|
||||||
Hash: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset,
|
|
||||||
Hmac<Hash>: Mac
|
|
||||||
{
|
|
||||||
// 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 = <Hash>::digest(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::<Hash,$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);
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
use cryptonum::unsigned::*;
|
|
||||||
use cryptonum::signed::ModInv;
|
|
||||||
use digest::Digest;
|
|
||||||
use dsa::params::*;
|
|
||||||
use dsa::rfc6979::DSASignature;
|
|
||||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,ToASN1};
|
|
||||||
use std::cmp::min;
|
|
||||||
use utils::TranslateNums;
|
|
||||||
|
|
||||||
pub trait DSAPublicKey {
|
|
||||||
type Params : DSAParameters;
|
|
||||||
type L;
|
|
||||||
type N;
|
|
||||||
|
|
||||||
/// Generate a new public key given the parameters and public value.
|
|
||||||
fn new(params: Self::Params, y: Self::L) -> Self;
|
|
||||||
/// Verify the given signature against the given message, using the
|
|
||||||
/// appropriate hash function.
|
|
||||||
fn verify<Hash>(&self, m: &[u8], sig: &DSASignature<Self::N>) -> bool
|
|
||||||
where Hash: Digest;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DSAPubKey<Params,L> {
|
|
||||||
pub(crate) params: Params,
|
|
||||||
pub(crate) y: L
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum DSAPublic {
|
|
||||||
DSAPublicL1024N160(DSAPubKey<L1024N160,U1024>),
|
|
||||||
DSAPublicL2048N224(DSAPubKey<L2048N224,U2048>),
|
|
||||||
DSAPublicL2048N256(DSAPubKey<L2048N256,U2048>),
|
|
||||||
DSAPublicL3072N256(DSAPubKey<L3072N256,U3072>)
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! pubkey_impls {
|
|
||||||
($ptype: ident, $ltype: ident, $ntype: ident, $dbl: ident, $bdbl: ident) => {
|
|
||||||
impl DSAPublicKey for DSAPubKey<$ptype,$ltype>
|
|
||||||
{
|
|
||||||
type Params = $ptype;
|
|
||||||
type L = $ltype;
|
|
||||||
type N = $ntype;
|
|
||||||
|
|
||||||
fn new(params: $ptype, y: $ltype) -> DSAPubKey<$ptype,$ltype>
|
|
||||||
{
|
|
||||||
DSAPubKey{ params, y }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify<Hash>(&self, m: &[u8], sig: &DSASignature<$ntype>) -> bool
|
|
||||||
where Hash: Digest
|
|
||||||
{
|
|
||||||
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 = <Hash>::digest(m).to_vec();
|
|
||||||
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 DSAPubKey<$ptype,$ltype> {
|
|
||||||
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);
|
|
||||||
@@ -1,436 +0,0 @@
|
|||||||
use cryptonum::unsigned::{CryptoNum,Decoder,Encoder};
|
|
||||||
use digest::{BlockInput,Digest,FixedOutput,Input,Reset};
|
|
||||||
use digest::generic_array::ArrayLength;
|
|
||||||
use hmac::{Hmac,Mac};
|
|
||||||
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: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset,
|
|
||||||
N: Clone + Decoder + Encoder + PartialOrd + Shr<usize,Output=N>,
|
|
||||||
Hmac<H>: Mac
|
|
||||||
{
|
|
||||||
hmac_k: Hmac<H>,
|
|
||||||
V: Vec<u8>,
|
|
||||||
q: N,
|
|
||||||
qlen: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H,N> KIterator<H,N>
|
|
||||||
where
|
|
||||||
H: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset,
|
|
||||||
N: Clone + Decoder + Encoder + PartialOrd + Shr<usize,Output=N> + Sub<Output=N>,
|
|
||||||
Hmac<H>: Mac
|
|
||||||
{
|
|
||||||
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(&K, &input);
|
|
||||||
// e. Set:
|
|
||||||
//
|
|
||||||
// V = HMAC_K(V)
|
|
||||||
V = 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(&K, &input);
|
|
||||||
// g. Set:
|
|
||||||
//
|
|
||||||
// V = HMAC_K(V)
|
|
||||||
V = hmac(&K, &V);
|
|
||||||
// h is for later ...
|
|
||||||
KIterator {
|
|
||||||
hmac_k: Hmac::<H>::new_varkey(&K).unwrap(),
|
|
||||||
V: V,
|
|
||||||
q: q.clone(),
|
|
||||||
qlen: qlen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H,N> Iterator for KIterator<H,N>
|
|
||||||
where
|
|
||||||
H: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset,
|
|
||||||
N: Clone + CryptoNum + Decoder + Encoder + PartialOrd + Shr<usize,Output=N>,
|
|
||||||
Hmac<H>: Mac
|
|
||||||
{
|
|
||||||
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_varkey(&K).unwrap();
|
|
||||||
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>(base: &Hmac<H>, m: &[u8]) -> Vec<u8>
|
|
||||||
where
|
|
||||||
H: Clone + BlockInput + Default + Input + FixedOutput + Reset,
|
|
||||||
Hmac<H>: Clone + Mac,
|
|
||||||
H::BlockSize : ArrayLength<u8>
|
|
||||||
{
|
|
||||||
let mut runner = base.clone();
|
|
||||||
runner.input(&m);
|
|
||||||
runner.result().code().as_slice().to_vec()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hmac<H>(k: &[u8], m: &[u8]) -> Vec<u8>
|
|
||||||
where
|
|
||||||
H: BlockInput + Clone + Default + Input + FixedOutput + Reset,
|
|
||||||
Hmac<H>: Clone + Mac,
|
|
||||||
H::BlockSize : ArrayLength<u8>
|
|
||||||
{
|
|
||||||
let mut runner = Hmac::<H>::new_varkey(&k).unwrap();
|
|
||||||
runner.input(&m);
|
|
||||||
runner.result().code().as_slice().to_vec()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 sha2::{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");
|
|
||||||
|
|
||||||
}
|
|
||||||
537
src/dsa/tests.rs
537
src/dsa/tests.rs
@@ -1,537 +0,0 @@
|
|||||||
use cryptonum::unsigned::*;
|
|
||||||
use digest::Digest;
|
|
||||||
use sha1::Sha1;
|
|
||||||
use sha2::{Sha224,Sha256,Sha384,Sha512};
|
|
||||||
use simple_asn1::{der_decode,der_encode};
|
|
||||||
use dsa::params::{DSAParameters,L1024N160,L2048N256};
|
|
||||||
use dsa::private::{DSAPrivateKey,DSAPrivKey};
|
|
||||||
use dsa::public::{DSAPublicKey,DSAPubKey};
|
|
||||||
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>::digest(&$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 = DSAPrivKey::new(params.clone(), x);
|
|
||||||
let public = DSAPubKey::<L1024N160,U1024>::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 = DSAPrivKey::<L2048N256,U256>::new(params.clone(), x);
|
|
||||||
let public = DSAPubKey::<L2048N256,U2048>::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]);
|
|
||||||
}
|
|
||||||
@@ -1,440 +0,0 @@
|
|||||||
use cryptonum::signed::{I192,I256,I384,I576};
|
|
||||||
use cryptonum::unsigned::{Decoder};
|
|
||||||
use cryptonum::unsigned::{U192,U256,U384,U576};
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub trait EllipticCurve {
|
|
||||||
type Unsigned : Clone;
|
|
||||||
type Signed : Clone;
|
|
||||||
|
|
||||||
fn size() -> usize;
|
|
||||||
fn p() -> Self::Unsigned;
|
|
||||||
fn n() -> Self::Unsigned;
|
|
||||||
fn SEED() -> Self::Unsigned;
|
|
||||||
fn c() -> Self::Unsigned;
|
|
||||||
fn a() -> Self::Unsigned;
|
|
||||||
fn b() -> Self::Unsigned;
|
|
||||||
fn Gx() -> Self::Signed;
|
|
||||||
fn Gy() -> Self::Signed;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum P192 {}
|
|
||||||
|
|
||||||
impl EllipticCurve for P192 {
|
|
||||||
type Unsigned = U192;
|
|
||||||
type Signed = I192;
|
|
||||||
|
|
||||||
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]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum P224 {}
|
|
||||||
|
|
||||||
impl EllipticCurve for P224 {
|
|
||||||
type Unsigned = U256;
|
|
||||||
type Signed = I256;
|
|
||||||
|
|
||||||
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
|
|
||||||
]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum P256 {}
|
|
||||||
|
|
||||||
impl EllipticCurve for P256 {
|
|
||||||
type Signed = I256;
|
|
||||||
type Unsigned = U256;
|
|
||||||
|
|
||||||
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
|
|
||||||
]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum P384 {}
|
|
||||||
|
|
||||||
impl EllipticCurve for P384 {
|
|
||||||
type Signed = I384;
|
|
||||||
type Unsigned = U384;
|
|
||||||
|
|
||||||
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
|
|
||||||
]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum P521 {}
|
|
||||||
|
|
||||||
impl EllipticCurve for P521 {
|
|
||||||
type Signed = I576;
|
|
||||||
type Unsigned = U576;
|
|
||||||
|
|
||||||
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
|
|
||||||
]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
pub mod curve;
|
|
||||||
pub mod point;
|
|
||||||
pub mod private;
|
|
||||||
pub 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;
|
|
||||||
use self::curve::{EllipticCurve,P192,P224,P256,P384,P521};
|
|
||||||
use self::point::{ECCPoint,Point};
|
|
||||||
pub use self::private::{ECCPrivateKey,ECCPrivate};
|
|
||||||
pub use self::public::{ECCPublicKey,ECDSAPublic,ECCPubKey};
|
|
||||||
pub use self::public::{ECDSADecodeErr,ECDSAEncodeErr};
|
|
||||||
|
|
||||||
pub trait ECDSAKeyPair<Public,Private> {
|
|
||||||
fn generate<G: Rng>(g: &mut G) -> (Public, Private);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! generate_impl {
|
|
||||||
($curve: ident, $un: ident, $si: ident) => {
|
|
||||||
impl ECDSAKeyPair<ECCPubKey<$curve>,ECCPrivate<$curve>> for $curve {
|
|
||||||
fn generate<G: Rng>(rng: &mut G) -> (ECCPubKey<$curve>, ECCPrivate<$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 = ECCPubKey::<$curve>::new(public_point);
|
|
||||||
let private = ECCPrivate::<$curve>::new(proposed_d);
|
|
||||||
return (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);
|
|
||||||
@@ -1,293 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
macro_rules! point_tests
|
|
||||||
{
|
|
||||||
($curve: ident, $lcurve: ident, $stype: ident, $utype: ident) => {
|
|
||||||
#[cfg(test)]
|
|
||||||
mod $lcurve {
|
|
||||||
use super::*;
|
|
||||||
use testing::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn negate() {
|
|
||||||
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");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn double() {
|
|
||||||
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");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn add() {
|
|
||||||
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");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn scale() {
|
|
||||||
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");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn add_scale2() {
|
|
||||||
let fname = build_test_path("ecc/add_scale2",stringify!($curve));
|
|
||||||
run_test(fname.to_string(), 8, |case| {
|
|
||||||
println!("-----------------------------------------------");
|
|
||||||
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));
|
|
||||||
println!("x1: {:X}", x);
|
|
||||||
println!("y1: {:X}", y);
|
|
||||||
let p = $stype::new(*negp, $utype::from_bytes(pbytes));
|
|
||||||
let q = $stype::new(*negq, $utype::from_bytes(qbytes));
|
|
||||||
println!("x2: {:X}", p);
|
|
||||||
println!("y2: {:X}", q);
|
|
||||||
let n = $stype::new(*negn, $utype::from_bytes(nbytes));
|
|
||||||
println!("n: {:X}", n);
|
|
||||||
let m = $stype::new(*negm, $utype::from_bytes(mbytes));
|
|
||||||
println!("m: {:X}", m);
|
|
||||||
let r = $stype::new(*negr, $utype::from_bytes(rbytes));
|
|
||||||
let s = $stype::new(*negs, $utype::from_bytes(sbytes));
|
|
||||||
println!("rx: {:X}", r);
|
|
||||||
println!("ry: {:X}", s);
|
|
||||||
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);
|
|
||||||
println!("mx: {:X}", res.x);
|
|
||||||
println!("my: {:X}", res.y);
|
|
||||||
assert_eq!(r, res.x, "x equivalence");
|
|
||||||
assert_eq!(s, res.y, "y equivalence");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
point_tests!(P192, p192, I192, U192);
|
|
||||||
point_tests!(P224, p224, I256, U256);
|
|
||||||
point_tests!(P256, p256, I256, U256);
|
|
||||||
point_tests!(P384, p384, I384, U384);
|
|
||||||
point_tests!(P521, p521, I576, U576);
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
use cryptonum::signed::*;
|
|
||||||
use cryptonum::unsigned::*;
|
|
||||||
use digest::{BlockInput,Digest,Input,FixedOutput,Reset};
|
|
||||||
use dsa::rfc6979::{DSASignature,KIterator,bits2int};
|
|
||||||
use ecdsa::curve::{EllipticCurve,P192,P224,P256,P384,P521};
|
|
||||||
use ecdsa::point::{ECCPoint,Point};
|
|
||||||
use hmac::{Hmac,Mac};
|
|
||||||
|
|
||||||
pub struct ECCPrivate<Curve: EllipticCurve> {
|
|
||||||
d: Curve::Unsigned
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ECCPrivateKey {
|
|
||||||
type Unsigned;
|
|
||||||
|
|
||||||
fn new(d: Self::Unsigned) -> Self;
|
|
||||||
fn sign<Hash>(&self, m: &[u8]) -> DSASignature<Self::Unsigned>
|
|
||||||
where
|
|
||||||
Hash: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset,
|
|
||||||
Hmac<Hash>: Mac;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! generate_privates
|
|
||||||
{
|
|
||||||
($curve: ident, $base: ident, $sig: ident, $dbl: ident, $quad: ident) => {
|
|
||||||
impl ECCPrivateKey for ECCPrivate<$curve>
|
|
||||||
{
|
|
||||||
type Unsigned = $base;
|
|
||||||
|
|
||||||
fn new(d: $base) -> ECCPrivate<$curve>
|
|
||||||
{
|
|
||||||
ECCPrivate{ d }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sign<Hash>(&self, m: &[u8]) -> DSASignature<$base>
|
|
||||||
where
|
|
||||||
Hash: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset,
|
|
||||||
Hmac<Hash>: Mac
|
|
||||||
{
|
|
||||||
// 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 = <Hash>::digest(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::<Hash,$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 sha2::{Sha224,Sha256,Sha384,Sha512};
|
|
||||||
#[cfg(test)]
|
|
||||||
use testing::*;
|
|
||||||
|
|
||||||
macro_rules! generate_tests {
|
|
||||||
($name: ident, $curve: ident, $base: ident) => {
|
|
||||||
#[test]
|
|
||||||
fn $name() {
|
|
||||||
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 = ECCPrivate::<$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");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_tests!(p192_sign, P192, U192);
|
|
||||||
generate_tests!(p224_sign, P224, U256);
|
|
||||||
generate_tests!(p256_sign, P256, U256);
|
|
||||||
generate_tests!(p384_sign, P384, U384);
|
|
||||||
generate_tests!(p521_sign, P521, U576);
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
use cryptonum::signed::*;
|
|
||||||
use cryptonum::unsigned::*;
|
|
||||||
use digest::{BlockInput,Digest,Input,FixedOutput,Reset};
|
|
||||||
use dsa::rfc6979::DSASignature;
|
|
||||||
use ecdsa::curve::{EllipticCurve,P192,P224,P256,P384,P521};
|
|
||||||
use ecdsa::point::{ECCPoint,Point};
|
|
||||||
use hmac::{Hmac,Mac};
|
|
||||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1DecodeErr,ASN1EncodeErr,FromASN1,ToASN1};
|
|
||||||
use std::cmp::min;
|
|
||||||
|
|
||||||
pub struct ECCPubKey<Curve: EllipticCurve> {
|
|
||||||
q: Point<Curve>
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ECDSAPublic {
|
|
||||||
ECCPublicP192(ECCPubKey<P192>),
|
|
||||||
ECCPublicP224(ECCPubKey<P224>),
|
|
||||||
ECCPublicP256(ECCPubKey<P256>),
|
|
||||||
ECCPublicP384(ECCPubKey<P384>),
|
|
||||||
ECCPublicP521(ECCPubKey<P521>),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ECCPublicKey {
|
|
||||||
type Curve : EllipticCurve;
|
|
||||||
type Unsigned;
|
|
||||||
|
|
||||||
fn new(d: Point<Self::Curve>) -> Self;
|
|
||||||
fn verify<Hash>(&self, m: &[u8], sig: &DSASignature<Self::Unsigned>) -> bool
|
|
||||||
where
|
|
||||||
Hash: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset,
|
|
||||||
Hmac<Hash>: Mac;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum ECDSAEncodeErr {
|
|
||||||
ASN1EncodeErr(ASN1EncodeErr),
|
|
||||||
XValueNegative, YValueNegative
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ASN1EncodeErr> for ECDSAEncodeErr {
|
|
||||||
fn from(x: ASN1EncodeErr) -> ECDSAEncodeErr {
|
|
||||||
ECDSAEncodeErr::ASN1EncodeErr(x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 for ECCPubKey<$curve>
|
|
||||||
{
|
|
||||||
type Curve = $curve;
|
|
||||||
type Unsigned = $un;
|
|
||||||
|
|
||||||
fn new(q: Point<$curve>) -> ECCPubKey<$curve>
|
|
||||||
{
|
|
||||||
ECCPubKey{ q }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify<Hash>(&self, m: &[u8], sig: &DSASignature<Self::Unsigned>) -> bool
|
|
||||||
where
|
|
||||||
Hash: BlockInput + Clone + Default + Digest + FixedOutput + Input + Reset,
|
|
||||||
Hmac<Hash>: Mac
|
|
||||||
{
|
|
||||||
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 = <Hash>::digest(m).to_vec();
|
|
||||||
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 ECCPubKey<$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 ECCPubKey<$curve> {
|
|
||||||
type Error = ECDSADecodeErr;
|
|
||||||
|
|
||||||
fn from_asn1(bs: &[ASN1Block]) -> Result<(ECCPubKey<$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 = ECCPubKey::<$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 sha2::{Sha224,Sha256,Sha384,Sha512};
|
|
||||||
#[cfg(test)]
|
|
||||||
use testing::*;
|
|
||||||
|
|
||||||
macro_rules! test_impl {
|
|
||||||
($name: ident, $curve: ident, $un: ident, $si: ident) => {
|
|
||||||
#[test]
|
|
||||||
fn $name() {
|
|
||||||
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 = ECCPubKey::<$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)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test_impl!(p192,P192,U192,I192);
|
|
||||||
test_impl!(p224,P224,U256,I256);
|
|
||||||
test_impl!(p256,P256,U256,I256);
|
|
||||||
test_impl!(p384,P384,U384,I384);
|
|
||||||
test_impl!(p521,P521,U576,I576);
|
|
||||||
34
src/lib.rs
34
src/lib.rs
@@ -1,3 +1,4 @@
|
|||||||
|
#![feature(i128_type)]
|
||||||
//! # Simple Crypto: A quaint little crypto library for rust.
|
//! # Simple Crypto: A quaint little crypto library for rust.
|
||||||
//!
|
//!
|
||||||
//! This is the simple_crypto library. Its goal is to provide straightforward
|
//! This is the simple_crypto library. Its goal is to provide straightforward
|
||||||
@@ -9,11 +10,9 @@
|
|||||||
//! that a new user should use, along with documentation regarding how and
|
//! 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
|
//! when they should use it, and examples. For now, it mostly just fowards
|
||||||
//! off to more detailed modules. Help requested!
|
//! off to more detailed modules. Help requested!
|
||||||
|
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate chrono;
|
|
||||||
extern crate cryptonum;
|
|
||||||
extern crate digest;
|
extern crate digest;
|
||||||
extern crate hmac;
|
|
||||||
extern crate num;
|
extern crate num;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@@ -21,24 +20,21 @@ extern crate quickcheck;
|
|||||||
extern crate rand;
|
extern crate rand;
|
||||||
extern crate sha1;
|
extern crate sha1;
|
||||||
extern crate sha2;
|
extern crate sha2;
|
||||||
#[macro_use]
|
|
||||||
extern crate simple_asn1;
|
extern crate simple_asn1;
|
||||||
|
|
||||||
/// The `rsa` module provides bare-bones support for RSA signing, verification,
|
/// The cryptonum module provides support for large numbers at fixed,
|
||||||
/// encryption, decryption, and key generation.
|
/// 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.
|
||||||
pub mod rsa;
|
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 `x509` module supports parsing and generating x.509 certificates, as
|
|
||||||
/// used by TLS and others.
|
|
||||||
pub mod x509;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod testing;
|
mod test {
|
||||||
mod utils;
|
|
||||||
|
#[test]
|
||||||
|
fn testing_works() {
|
||||||
|
assert!(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use num::bigint::BigUint;
|
use cryptonum::{CryptoNumModOps};
|
||||||
|
use num::BigUint;
|
||||||
use rsa::errors::RSAError;
|
use rsa::errors::RSAError;
|
||||||
use simple_asn1::{ASN1Block,ASN1DecodeErr};
|
use simple_asn1::{ASN1DecodeErr,ASN1Block};
|
||||||
|
|
||||||
pub fn pkcs1_pad(ident: &[u8], hash: &[u8], keylen: usize) -> Vec<u8>
|
// encoding PKCS1 stuff
|
||||||
{
|
pub fn pkcs1_pad(ident: &[u8], hash: &[u8], keylen: usize) -> Vec<u8> {
|
||||||
let mut idhash = Vec::new();
|
let mut idhash = Vec::new();
|
||||||
idhash.extend_from_slice(ident);
|
idhash.extend_from_slice(ident);
|
||||||
idhash.extend_from_slice(hash);
|
idhash.extend_from_slice(hash);
|
||||||
@@ -11,25 +12,26 @@ pub fn pkcs1_pad(ident: &[u8], hash: &[u8], keylen: usize) -> Vec<u8>
|
|||||||
assert!(keylen > (tlen + 3));
|
assert!(keylen > (tlen + 3));
|
||||||
let mut padding = Vec::new();
|
let mut padding = Vec::new();
|
||||||
padding.resize(keylen - tlen - 3, 0xFF);
|
padding.resize(keylen - tlen - 3, 0xFF);
|
||||||
let mut result = vec![0x00,0x01];
|
let mut result = vec![0x00, 0x01];
|
||||||
result.append(&mut padding);
|
result.append(&mut padding);
|
||||||
result.push(0x00);
|
result.push(0x00);
|
||||||
result.append(&mut idhash);
|
result.append(&mut idhash);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drop0s(a: &[u8]) -> &[u8] {
|
// the RSA encryption function
|
||||||
let mut idx = 0;
|
pub fn ep<U: CryptoNumModOps>(n: &U, e: &U, m: &U) -> U {
|
||||||
|
m.modexp(e, n)
|
||||||
while (idx < a.len()) && (a[idx] == 0) {
|
|
||||||
idx = idx + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&a[idx..]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn xor_vecs(a: &Vec<u8>, b: &Vec<u8>) -> Vec<u8> {
|
// the RSA decryption function
|
||||||
a.iter().zip(b.iter()).map(|(a,b)| a^b).collect()
|
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 decode_biguint(b: &ASN1Block) -> Result<BigUint,RSAError> {
|
pub fn decode_biguint(b: &ASN1Block) -> Result<BigUint,RSAError> {
|
||||||
@@ -46,3 +48,12 @@ 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,5 +1,16 @@
|
|||||||
use simple_asn1::ASN1DecodeErr;
|
use simple_asn1::{ASN1DecodeErr};
|
||||||
use rand;
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RSAError {
|
pub enum RSAError {
|
||||||
@@ -8,12 +19,13 @@ pub enum RSAError {
|
|||||||
DecryptionError,
|
DecryptionError,
|
||||||
DecryptHashMismatch,
|
DecryptHashMismatch,
|
||||||
InvalidKey,
|
InvalidKey,
|
||||||
RandomGenError(rand::Error),
|
KeySizeMismatch,
|
||||||
|
RandomGenError(io::Error),
|
||||||
ASN1DecodeErr(ASN1DecodeErr)
|
ASN1DecodeErr(ASN1DecodeErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<rand::Error> for RSAError {
|
impl From<io::Error> for RSAError {
|
||||||
fn from(e: rand::Error) -> RSAError {
|
fn from(e: io::Error) -> RSAError {
|
||||||
RSAError::RandomGenError(e)
|
RSAError::RandomGenError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
344
src/rsa/mod.rs
344
src/rsa/mod.rs
@@ -1,22 +1,10 @@
|
|||||||
//! A simple RSA library.
|
//! # An implementation of RSA.
|
||||||
//!
|
|
||||||
//! 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.
|
|
||||||
//!
|
//!
|
||||||
|
//! 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.
|
||||||
mod core;
|
mod core;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod oaep;
|
mod oaep;
|
||||||
@@ -24,161 +12,217 @@ mod private;
|
|||||||
mod public;
|
mod public;
|
||||||
mod signing_hashes;
|
mod signing_hashes;
|
||||||
|
|
||||||
pub use self::errors::RSAError;
|
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::signing_hashes::{SigningHash,
|
pub use self::signing_hashes::{SigningHash,
|
||||||
SIGNING_HASH_NULL,
|
SIGNING_HASH_NULL, SIGNING_HASH_SHA1,
|
||||||
SIGNING_HASH_SHA1,
|
SIGNING_HASH_SHA224, SIGNING_HASH_SHA256,
|
||||||
SIGNING_HASH_SHA224,
|
SIGNING_HASH_SHA384, SIGNING_HASH_SHA512};
|
||||||
SIGNING_HASH_SHA256,
|
|
||||||
SIGNING_HASH_SHA384,
|
|
||||||
SIGNING_HASH_SHA512};
|
|
||||||
pub use self::oaep::OAEPParams;
|
|
||||||
pub use self::private::{RSAPrivate, RSAPrivateKey,
|
|
||||||
RSA512Private, RSA1024Private, RSA2048Private,
|
|
||||||
RSA3072Private, RSA4096Private, RSA8192Private,
|
|
||||||
RSA15360Private};
|
|
||||||
pub use self::public::{RSAPublic, RSAPublicKey,
|
|
||||||
RSA512Public, RSA1024Public, RSA2048Public,
|
|
||||||
RSA3072Public, RSA4096Public, RSA8192Public,
|
|
||||||
RSA15360Public};
|
|
||||||
|
|
||||||
use cryptonum::signed::{EGCD,ModInv};
|
/// An RSA public and private key.
|
||||||
use cryptonum::unsigned::{CryptoNum,PrimeGen};
|
#[derive(Clone,Debug,PartialEq)]
|
||||||
use cryptonum::unsigned::{U256,U512,U1024,U1536,U2048,U3072,U4096,U7680,U8192,U15360};
|
pub struct RSAKeyPair<Size>
|
||||||
use rand::RngCore;
|
|
||||||
use std::ops::Sub;
|
|
||||||
|
|
||||||
fn diff<T>(a: &T, b: &T) -> T
|
|
||||||
where
|
where
|
||||||
T: Clone + PartialOrd,
|
Size: CryptoNumBase + CryptoNumSerialization
|
||||||
T: Sub<T,Output=T>
|
{
|
||||||
|
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
|
||||||
{
|
{
|
||||||
if a > b {
|
if a > b {
|
||||||
a.clone() - b.clone()
|
a - b
|
||||||
} else {
|
} else {
|
||||||
b.clone() - a.clone()
|
b - a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! generate_rsa_pair
|
|
||||||
{
|
|
||||||
($pair: ident, $pub: ident, $priv: ident, $uint: ident, $half: ident, $iterations: expr) => {
|
|
||||||
pub struct $pair {
|
|
||||||
pub public: $pub,
|
|
||||||
pub private: $priv
|
|
||||||
}
|
|
||||||
|
|
||||||
impl $pair {
|
|
||||||
pub fn new(pu: $pub, pr: $priv) -> $pair {
|
|
||||||
$pair {
|
|
||||||
public: pu,
|
|
||||||
private: pr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate<G>(rng: &mut G) -> $pair
|
|
||||||
where G: RngCore
|
|
||||||
{
|
|
||||||
loop {
|
|
||||||
let ebase = 65537u32;
|
|
||||||
let e = $uint::from(ebase);
|
|
||||||
let (p, q) = $pair::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 = $pub::new(n.clone(), e);
|
|
||||||
let private = $priv::new(n, d);
|
|
||||||
return $pair::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!(RSA512KeyPair, RSA512Public, RSA512Private, U512, U256, 7);
|
|
||||||
generate_rsa_pair!(RSA1024KeyPair, RSA1024Public, RSA1024Private, U1024, U512, 7);
|
|
||||||
generate_rsa_pair!(RSA2048KeyPair, RSA2048Public, RSA2048Private, U2048, U1024, 4);
|
|
||||||
generate_rsa_pair!(RSA3072KeyPair, RSA3072Public, RSA3072Private, U3072, U1536, 3);
|
|
||||||
generate_rsa_pair!(RSA4096KeyPair, RSA4096Public, RSA4096Private, U4096, U2048, 3);
|
|
||||||
generate_rsa_pair!(RSA8192KeyPair, RSA8192Public, RSA8192Private, U8192, U4096, 3);
|
|
||||||
generate_rsa_pair!(RSA15360KeyPair, RSA15360Public, RSA15360Private, U15360, U7680, 3);
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod generation {
|
mod tests {
|
||||||
use quickcheck::{Arbitrary,Gen};
|
use quickcheck::{Arbitrary,Gen};
|
||||||
use std::fmt;
|
use rsa::core::{dp,ep,sp1,vp1};
|
||||||
|
use sha2::Sha224;
|
||||||
|
use simple_asn1::{der_decode,der_encode};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
impl Clone for RSA512KeyPair {
|
impl Arbitrary for RSAKeyPair<U512> {
|
||||||
fn clone(&self) -> RSA512KeyPair {
|
fn arbitrary<G: Gen>(g: &mut G) -> RSAKeyPair<U512> {
|
||||||
RSA512KeyPair{
|
RSAKeyPair::generate_w_rng(g).unwrap()
|
||||||
public: RSA512Public {
|
|
||||||
n: self.public.n.clone(),
|
|
||||||
nu: self.public.nu.clone(),
|
|
||||||
e: self.public.e.clone(),
|
|
||||||
},
|
|
||||||
private: RSA512Private {
|
|
||||||
nu: self.private.nu.clone(),
|
|
||||||
d: self.private.d.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for RSA512KeyPair {
|
// Core primitive checks
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
quickcheck! {
|
||||||
f.debug_struct("RSA512KeyPair")
|
fn ep_dp_inversion(kp: RSAKeyPair<U512>, m: U512) -> bool {
|
||||||
.field("n", &self.public.n)
|
let realm = &m % &kp.public.n;
|
||||||
.field("e", &self.public.e)
|
let ciphertext = ep(&kp.public.n, &kp.public.e, &realm);
|
||||||
.field("d", &self.private.d)
|
let mprime = dp(&kp.private.n, &kp.private.d, &ciphertext);
|
||||||
.finish()
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arbitrary for RSA512KeyPair {
|
// Public key serialization
|
||||||
fn arbitrary<G: Gen>(g: &mut G) -> RSA512KeyPair {
|
quickcheck! {
|
||||||
RSA512KeyPair::generate(g)
|
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
|
||||||
|
{
|
||||||
|
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! {
|
quickcheck! {
|
||||||
fn generate_and_sign(keypair: RSA512KeyPair, msg: Vec<u8>) -> bool {
|
fn sign_verifies(kpsh: KeyPairAndSigHash<U512>, m: Message) -> bool {
|
||||||
let sig = keypair.private.sign(&SIGNING_HASH_SHA256, &msg);
|
let sig = kpsh.kp.private.sign(kpsh.sh, &m.m);
|
||||||
keypair.public.verify(&SIGNING_HASH_SHA256, &msg, &sig)
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,43 +1,44 @@
|
|||||||
use byteorder::{BigEndian,ByteOrder};
|
use byteorder::{BigEndian,ByteOrder};
|
||||||
use digest::{Digest,FixedOutput};
|
use digest::{FixedOutput,Input};
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
/// Parameters for OAEP encryption and decryption: a hash function to use as
|
/// Parameters for OAEP encryption and decryption: a hash function to use
|
||||||
/// part of the message generation function (MGF1, if you're curious),
|
/// as part of the message generation function (MGF1, if you're curious),
|
||||||
/// and any labels you want to include as part of the encryption.
|
/// and any labels you want to include as part of the encryption.
|
||||||
pub struct OAEPParams<H: Default + Digest + FixedOutput> {
|
pub struct OAEPParams<H: Clone + Input + FixedOutput> {
|
||||||
pub label: String,
|
pub hash: H,
|
||||||
phantom: PhantomData<H>
|
pub label: String
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H: Default + Digest + FixedOutput> OAEPParams<H> {
|
impl<H: Clone + Input + FixedOutput> OAEPParams<H> {
|
||||||
pub fn new(label: String)
|
pub fn new(hash: H, label: String)
|
||||||
-> OAEPParams<H>
|
-> OAEPParams<H>
|
||||||
{
|
{
|
||||||
OAEPParams { label: label, phantom: PhantomData }
|
OAEPParams { hash: hash, label: label }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_len(&self) -> usize {
|
pub fn hash_len(&self) -> usize {
|
||||||
H::default().fixed_result().as_slice().len()
|
self.hash.clone().fixed_result().as_slice().len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash(&self, input: &[u8]) -> Vec<u8> {
|
pub fn hash(&self, input: &[u8]) -> Vec<u8> {
|
||||||
H::digest(input).as_slice().to_vec()
|
let mut digest = self.hash.clone();
|
||||||
|
digest.process(input);
|
||||||
|
digest.fixed_result().as_slice().to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mgf1(&self, input: &[u8], len: usize) -> Vec<u8> {
|
pub fn mgf1(&self, input: &[u8], len: usize) -> Vec<u8> {
|
||||||
let mut res = Vec::with_capacity(len);
|
let mut res = Vec::with_capacity(len);
|
||||||
let mut counter = 0u32;
|
let mut counter: u32 = 0;
|
||||||
|
|
||||||
while res.len() < len {
|
while res.len() < len {
|
||||||
let mut buffer = [0; 4];
|
let mut c: [u8; 4] = [0; 4];
|
||||||
BigEndian::write_u32(&mut buffer, counter);
|
BigEndian::write_u32(&mut c, counter);
|
||||||
let mut digest = H::default();
|
let mut digest = self.hash.clone();
|
||||||
digest.input(input);
|
digest.process(input);
|
||||||
digest.input(&buffer);
|
digest.process(&c);
|
||||||
let chunk = digest.fixed_result();
|
let chunk = digest.fixed_result();
|
||||||
res.extend_from_slice(chunk.as_slice());
|
res.extend_from_slice(chunk.as_slice());
|
||||||
counter = counter + 1;
|
counter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.truncate(len);
|
res.truncate(len);
|
||||||
|
|||||||
@@ -1,105 +1,65 @@
|
|||||||
use cryptonum::unsigned::*;
|
use cryptonum::{CryptoNumBase,CryptoNumModOps,CryptoNumSerialization};
|
||||||
use digest::{Digest,FixedOutput};
|
use digest::{FixedOutput,Input};
|
||||||
use rsa::core::{drop0s,pkcs1_pad,xor_vecs};
|
use rsa::core::{dp,sp1,pkcs1_pad,xor_vecs};
|
||||||
use rsa::errors::RSAError;
|
use rsa::oaep::{OAEPParams};
|
||||||
use rsa::oaep::OAEPParams;
|
use rsa::errors::{RSAError};
|
||||||
use rsa::signing_hashes::SigningHash;
|
use rsa::signing_hashes::SigningHash;
|
||||||
|
|
||||||
pub trait RSAPrivateKey<N> {
|
/// A RSA private key. As with public keys, I've left the size as a
|
||||||
/// Generate a new private key using the given modulus and private
|
/// parameter: 2048-4096 is standard practice, 512-1024 is weak, and
|
||||||
/// exponent. You probably don't want to use this function directly
|
/// >4096 is going to be slow.
|
||||||
/// unless you're writing your own key generation routine or key
|
#[derive(Clone,Debug,PartialEq)]
|
||||||
/// parsing library.
|
pub struct RSAPrivateKey<Size>
|
||||||
fn new(n: N, d: N) -> Self;
|
where
|
||||||
|
Size: CryptoNumBase + CryptoNumSerialization
|
||||||
/// Sign the given message with the given private key.
|
|
||||||
fn sign(&self, signhash: &SigningHash, msg: &[u8]) -> Vec<u8>;
|
|
||||||
|
|
||||||
/// Decrypt the provided message using the given OAEP parameters. As
|
|
||||||
/// mentioned in the comment for encryption, RSA decryption is really,
|
|
||||||
/// really slow. So if your plaintext is larger than about half the
|
|
||||||
/// bit size of the key, it's almost certainly a better idea to generate
|
|
||||||
/// a fresh symmetric encryption key, encrypt only the key with RSA, and
|
|
||||||
/// then encrypt the message with that key.
|
|
||||||
fn decrypt<H>(&self, oaep: &OAEPParams<H>, msg: &[u8])
|
|
||||||
-> Result<Vec<u8>,RSAError>
|
|
||||||
where H: Default + Digest + FixedOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum RSAPrivate {
|
|
||||||
Key512(RSA512Private),
|
|
||||||
Key1024(RSA1024Private),
|
|
||||||
Key2048(RSA2048Private),
|
|
||||||
Key3072(RSA3072Private),
|
|
||||||
Key4096(RSA4096Private),
|
|
||||||
Key8192(RSA8192Private),
|
|
||||||
Key15360(RSA15360Private)
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn print_vector(name: &'static str, bytes: &[u8])
|
|
||||||
// {
|
|
||||||
// print!("{}: (length {}) ", name, bytes.len());
|
|
||||||
// for x in bytes.iter() {
|
|
||||||
// print!("{:02X}", *x);
|
|
||||||
// }
|
|
||||||
// println!("");
|
|
||||||
// }
|
|
||||||
|
|
||||||
macro_rules! generate_rsa_private
|
|
||||||
{
|
{
|
||||||
($rsa: ident, $num: ident, $bar: ident, $size: expr) => {
|
pub(crate) n: Size,
|
||||||
pub struct $rsa {
|
pub(crate) d: Size
|
||||||
pub(crate) nu: $bar,
|
}
|
||||||
pub(crate) d: $num
|
|
||||||
|
impl<U> RSAPrivateKey<U>
|
||||||
|
where
|
||||||
|
U: CryptoNumBase + CryptoNumModOps + CryptoNumSerialization
|
||||||
|
{
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RSAPrivateKey<$num> for $rsa {
|
/// Sign a message using the given hash.
|
||||||
fn new(n: $num, d: $num) -> $rsa {
|
pub fn sign(&self, sighash: &SigningHash, msg: &[u8]) -> Vec<u8> {
|
||||||
let nu = $bar::new(n.clone());
|
let hash = (sighash.run)(msg);
|
||||||
$rsa { nu: nu, d: d }
|
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);
|
||||||
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();
|
let sig = s.to_bytes();
|
||||||
sig
|
sig
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt<H>(&self, oaep: &OAEPParams<H>, msg: &[u8])
|
/// Decrypt a message with the given parameters.
|
||||||
|
pub fn decrypt<H: Clone + Input + FixedOutput>(&self, oaep: &OAEPParams<H>, msg: &[u8])
|
||||||
-> Result<Vec<u8>,RSAError>
|
-> Result<Vec<u8>,RSAError>
|
||||||
where H: Default + Digest + FixedOutput
|
|
||||||
{
|
{
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
|
let byte_len = self.d.byte_size();
|
||||||
|
|
||||||
for chunk in msg.chunks($size/8) {
|
for chunk in msg.chunks(byte_len) {
|
||||||
let mut dchunk = self.oaep_decrypt(oaep, chunk)?;
|
let mut dchunk = self.oaep_decrypt(oaep, chunk)?;
|
||||||
res.append(&mut dchunk);
|
res.append(&mut dchunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl $rsa {
|
fn oaep_decrypt<H: Clone + Input + FixedOutput>(&self, oaep: &OAEPParams<H>, c: &[u8])
|
||||||
fn sp1(&self, m: &$num) -> $num {
|
|
||||||
m.modexp(&self.d, &self.nu)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dp(&self, c: &$num) -> $num {
|
|
||||||
c.modexp(&self.d, &self.nu)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn oaep_decrypt<H>(&self, oaep: &OAEPParams<H>, c: &[u8])
|
|
||||||
-> Result<Vec<u8>,RSAError>
|
-> Result<Vec<u8>,RSAError>
|
||||||
where
|
|
||||||
H: Default + Digest + FixedOutput
|
|
||||||
{
|
{
|
||||||
let byte_len = $size / 8;
|
let byte_len = self.d.byte_size();
|
||||||
// Step 1b
|
// Step 1b
|
||||||
if c.len() != byte_len {
|
if c.len() != byte_len {
|
||||||
return Err(RSAError::DecryptionError);
|
return Err(RSAError::DecryptionError);
|
||||||
@@ -109,9 +69,9 @@ macro_rules! generate_rsa_private
|
|||||||
return Err(RSAError::DecryptHashMismatch);
|
return Err(RSAError::DecryptHashMismatch);
|
||||||
}
|
}
|
||||||
// Step 2a
|
// Step 2a
|
||||||
let c_ip = $num::from_bytes(&c);
|
let c_ip = U::from_bytes(&c);
|
||||||
// Step 2b
|
// Step 2b
|
||||||
let m_ip = self.dp(&c_ip);
|
let m_ip = dp(&self.n, &self.d, &c_ip);
|
||||||
// Step 2c
|
// Step 2c
|
||||||
let em = m_ip.to_bytes();
|
let em = m_ip.to_bytes();
|
||||||
// Step 3a
|
// Step 3a
|
||||||
@@ -144,130 +104,14 @@ macro_rules! generate_rsa_private
|
|||||||
|
|
||||||
Ok(m.to_vec())
|
Ok(m.to_vec())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
generate_rsa_private!(RSA512Private, U512, BarrettU512, 512);
|
fn drop0s(a: &[u8]) -> &[u8] {
|
||||||
generate_rsa_private!(RSA1024Private, U1024, BarrettU1024, 1024);
|
let mut idx = 0;
|
||||||
generate_rsa_private!(RSA2048Private, U2048, BarrettU2048, 2048);
|
|
||||||
generate_rsa_private!(RSA3072Private, U3072, BarrettU3072, 3072);
|
|
||||||
generate_rsa_private!(RSA4096Private, U4096, BarrettU4096, 4096);
|
|
||||||
generate_rsa_private!(RSA8192Private, U8192, BarrettU8192, 8192);
|
|
||||||
generate_rsa_private!(RSA15360Private, U15360, BarrettU15360, 15360);
|
|
||||||
|
|
||||||
macro_rules! generate_tests {
|
while (idx < a.len()) && (a[idx] == 0) {
|
||||||
( $( ($mod: ident, $rsa: ident, $num: ident, $bar: ident, $num64: ident, $size: expr) ),* ) => {
|
idx = idx + 1;
|
||||||
$(
|
|
||||||
#[cfg(test)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
mod $mod {
|
|
||||||
use cryptonum::unsigned::Decoder;
|
|
||||||
use super::*;
|
|
||||||
use testing::run_test;
|
|
||||||
use rsa::signing_hashes::*;
|
|
||||||
use sha1::Sha1;
|
|
||||||
use sha2::{Sha224,Sha256,Sha384,Sha512};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn sign() {
|
|
||||||
let fname = format!("tests/rsa/rsa{}.test", $size);
|
|
||||||
run_test(fname.to_string(), 8, |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();
|
|
||||||
|
|
||||||
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 privkey = $rsa{ nu: $bar::from_components(k, n, nu), d: d };
|
|
||||||
let hashnum = ((hbytes[0] as u16)<<8) + (hbytes[1] as u16);
|
|
||||||
let sighash = match hashnum {
|
|
||||||
0x160 => &SIGNING_HASH_SHA1,
|
|
||||||
0x224 => &SIGNING_HASH_SHA224,
|
|
||||||
0x256 => &SIGNING_HASH_SHA256,
|
|
||||||
0x384 => &SIGNING_HASH_SHA384,
|
|
||||||
0x512 => &SIGNING_HASH_SHA512,
|
|
||||||
_ => panic!("Bad signing hash: {}", hashnum)
|
|
||||||
};
|
|
||||||
let sig = privkey.sign(sighash, &mbytes);
|
|
||||||
assert_eq!(sig, *sbytes);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
&a[idx..]
|
||||||
fn decrypt() {
|
|
||||||
let fname = format!("tests/rsa/rsa{}.test", $size);
|
|
||||||
run_test(fname.to_string(), 8, |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, cbytes) = case.get("c").unwrap();
|
|
||||||
let (neg5, ubytes) = case.get("u").unwrap();
|
|
||||||
let (neg6, kbytes) = case.get("k").unwrap();
|
|
||||||
|
|
||||||
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 privkey = $rsa{ nu: $bar::from_components(k, n, nu), d: d };
|
|
||||||
let hashnum = ((hbytes[0] as u16)<<8) + (hbytes[1] as u16);
|
|
||||||
let empty = "".to_string();
|
|
||||||
match hashnum {
|
|
||||||
0x160 => {
|
|
||||||
let oaep = OAEPParams::<Sha1>::new(empty);
|
|
||||||
let plain = privkey.decrypt(&oaep, &cbytes);
|
|
||||||
assert!(plain.is_ok());
|
|
||||||
assert_eq!(*mbytes, plain.unwrap());
|
|
||||||
}
|
|
||||||
0x224 =>{
|
|
||||||
let oaep = OAEPParams::<Sha224>::new(empty);
|
|
||||||
let plain = privkey.decrypt(&oaep, &cbytes);
|
|
||||||
assert!(plain.is_ok());
|
|
||||||
assert_eq!(*mbytes, plain.unwrap());
|
|
||||||
}
|
|
||||||
0x256 => {
|
|
||||||
let oaep = OAEPParams::<Sha256>::new(empty);
|
|
||||||
let plain = privkey.decrypt(&oaep, &cbytes);
|
|
||||||
assert!(plain.is_ok());
|
|
||||||
assert_eq!(*mbytes, plain.unwrap());
|
|
||||||
}
|
|
||||||
0x384 => {
|
|
||||||
let oaep = OAEPParams::<Sha384>::new(empty);
|
|
||||||
let plain = privkey.decrypt(&oaep, &cbytes);
|
|
||||||
assert!(plain.is_ok());
|
|
||||||
assert_eq!(*mbytes, plain.unwrap());
|
|
||||||
}
|
|
||||||
0x512 => {
|
|
||||||
let oaep = OAEPParams::<Sha512>::new(empty);
|
|
||||||
let plain = privkey.decrypt(&oaep, &cbytes);
|
|
||||||
assert!(plain.is_ok());
|
|
||||||
assert_eq!(*mbytes, plain.unwrap());
|
|
||||||
}
|
|
||||||
_ => panic!("Bad signing hash: {}", hashnum)
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
generate_tests!( (RSA512, RSA512Private, U512, BarrettU512, U576, 512),
|
|
||||||
(RSA1024, RSA1024Private, U1024, BarrettU1024, U1088, 1024),
|
|
||||||
(RSA2048, RSA2048Private, U2048, BarrettU2048, U2112, 2048)
|
|
||||||
// (RSA3072, RSA3072Private, U3072, BarrettU3072, U3136, 3072),
|
|
||||||
// (RSA4096, RSA4096Private, U4096, BarrettU4096, U4160, 4096),
|
|
||||||
// (RSA8192, RSA8192Private, U8192, BarrettU8192, U8256, 8192),
|
|
||||||
// (RSA15360, RSA15360Private, U15360, BarrettU15360, U15424, 15360)
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -1,264 +1,105 @@
|
|||||||
use cryptonum::unsigned::*;
|
use cryptonum::{CryptoNumBase,CryptoNumModOps,CryptoNumSerialization};
|
||||||
use digest::{Digest,FixedOutput};
|
use digest::{FixedOutput,Input};
|
||||||
use rand::Rng;
|
use num::{BigInt,BigUint};
|
||||||
use rand::rngs::OsRng;
|
use rand::{OsRng,Rng};
|
||||||
use rsa::core::{decode_biguint,pkcs1_pad,xor_vecs};
|
use rsa::core::{ep,vp1,pkcs1_pad,xor_vecs,decode_biguint};
|
||||||
use rsa::errors::RSAError;
|
use rsa::oaep::{OAEPParams};
|
||||||
use rsa::oaep::OAEPParams;
|
use rsa::errors::{RSAError};
|
||||||
use rsa::signing_hashes::SigningHash;
|
use rsa::signing_hashes::SigningHash;
|
||||||
use simple_asn1::{ASN1Block,ASN1DecodeErr,ASN1EncodeErr,
|
use simple_asn1::{FromASN1,ToASN1,ASN1DecodeErr,ASN1EncodeErr};
|
||||||
ASN1Class,FromASN1,ToASN1};
|
use simple_asn1::{ASN1Block,ASN1Class};
|
||||||
#[cfg(test)]
|
|
||||||
use std::fmt;
|
|
||||||
use utils::TranslateNums;
|
|
||||||
|
|
||||||
pub trait RSAPublicKey<N> {
|
/// An RSA public key with the given modulus size. I've left the size as a
|
||||||
/// Generate a new public key pair for the given modulus and
|
/// parameter, instead of hardcoding particular values. That being said,
|
||||||
/// exponent. You should probably not call this directly unless
|
/// you almost certainly want one of `U2048`, `U3072`, or `U4096` if you're
|
||||||
/// you're writing a key generation function or writing your own
|
/// being pretty standard; `U512` or `U1024` if you're interfacing with
|
||||||
/// public key parser.
|
/// legacy code or want to build intentionally weak systems; or `U7680`,
|
||||||
fn new(n: N, e: N) -> Self;
|
/// `U8192`, or `U15360` if you like things running very slowly.
|
||||||
|
#[derive(Clone,Debug,PartialEq)]
|
||||||
/// Verify that the provided signature is valid; that the private
|
pub struct RSAPublicKey<Size>
|
||||||
/// 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.
|
|
||||||
fn verify(&self, signhash: &SigningHash, msg: &[u8], sig: &[u8]) -> bool;
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
fn encrypt_rng<G,H>(&self, g: &mut G, oaep: &OAEPParams<H>, msg: &[u8])
|
|
||||||
-> Result<Vec<u8>,RSAError>
|
|
||||||
where
|
where
|
||||||
G: Rng,
|
Size: CryptoNumBase + CryptoNumSerialization
|
||||||
H: Default + Digest + FixedOutput;
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
fn encrypt<H>(&self,oaep:&OAEPParams<H>,msg:&[u8])
|
|
||||||
-> Result<Vec<u8>,RSAError>
|
|
||||||
where
|
|
||||||
H: Default + Digest + FixedOutput
|
|
||||||
{
|
|
||||||
let mut g = OsRng::new()?;
|
|
||||||
self.encrypt_rng(&mut g, oaep, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum RSAPublic {
|
|
||||||
Key512(RSA512Public),
|
|
||||||
Key1024(RSA1024Public),
|
|
||||||
Key2048(RSA2048Public),
|
|
||||||
Key3072(RSA3072Public),
|
|
||||||
Key4096(RSA4096Public),
|
|
||||||
Key8192(RSA8192Public),
|
|
||||||
Key15360(RSA15360Public)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RSAPublic {
|
|
||||||
pub fn verify(&self, signhash: &SigningHash, msg: &[u8], sig: &[u8]) -> bool
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromASN1 for RSAPublic {
|
|
||||||
type Error = RSAError;
|
|
||||||
|
|
||||||
fn from_asn1(bs: &[ASN1Block])
|
|
||||||
-> Result<(RSAPublic,&[ASN1Block]),RSAError>
|
|
||||||
{
|
|
||||||
match bs.split_first() {
|
|
||||||
None =>
|
|
||||||
Err(RSAError::ASN1DecodeErr(ASN1DecodeErr::EmptyBuffer)),
|
|
||||||
Some((&ASN1Block::Sequence(_, _, ref items), rest))
|
|
||||||
if items.len() == 2 =>
|
|
||||||
{
|
|
||||||
let n = decode_biguint(&items[0])?;
|
|
||||||
let e = decode_biguint(&items[1])?;
|
|
||||||
let nsize = n.bits();
|
|
||||||
let mut rsa_size = 512;
|
|
||||||
|
|
||||||
println!("n': {:X}", n);
|
|
||||||
println!("nsize: {}", nsize);
|
|
||||||
while rsa_size < nsize {
|
|
||||||
rsa_size = rsa_size + 256;
|
|
||||||
}
|
|
||||||
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 = RSA512Public::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 = RSA1024Public::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 = RSA2048Public::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 = RSA3072Public::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 = RSA4096Public::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 = RSA8192Public::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 = RSA15360Public::new(n2, e2);
|
|
||||||
Ok((RSAPublic::Key15360(res), rest))
|
|
||||||
}
|
|
||||||
_ =>
|
|
||||||
Err(RSAError::InvalidKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(_) =>
|
|
||||||
Err(RSAError::InvalidKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToASN1 for RSAPublic {
|
|
||||||
type Error = ASN1EncodeErr;
|
|
||||||
|
|
||||||
fn to_asn1_class(&self, c: ASN1Class)
|
|
||||||
-> Result<Vec<ASN1Block>,Self::Error>
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn print_vector(name: &'static str, bytes: &[u8])
|
|
||||||
// {
|
|
||||||
// print!("{}: (length {}) ", name, bytes.len());
|
|
||||||
// for x in bytes.iter() {
|
|
||||||
// print!("{:02X}", *x);
|
|
||||||
// }
|
|
||||||
// println!("");
|
|
||||||
// }
|
|
||||||
|
|
||||||
macro_rules! generate_rsa_public
|
|
||||||
{
|
{
|
||||||
($rsa: ident, $num: ident, $bar: ident, $var: ident, $size: expr) => {
|
pub(crate) n: Size,
|
||||||
#[derive(PartialEq)]
|
pub(crate) e: Size
|
||||||
pub struct $rsa {
|
}
|
||||||
pub(crate) n: $num,
|
|
||||||
pub(crate) nu: $bar,
|
impl<U> RSAPublicKey<U>
|
||||||
pub(crate) e: $num
|
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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RSAPublicKey<$num> for $rsa {
|
/// Verify the signature for a given message, using the given signing hash,
|
||||||
fn new(n: $num, e: $num) -> $rsa {
|
/// return true iff the signature validates.
|
||||||
let nu = $bar::new(n.clone());
|
pub fn verify(&self, sighash: &SigningHash, msg: &[u8], sig: &[u8]) -> bool
|
||||||
$rsa { n: n, nu: nu, e: e }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify(&self, signhash: &SigningHash, msg: &[u8], sig: &[u8])
|
|
||||||
-> bool
|
|
||||||
{
|
{
|
||||||
let hash: Vec<u8> = (signhash.run)(msg);
|
let hash = (sighash.run)(msg);
|
||||||
let s = $num::from_bytes(&sig);
|
let s = U::from_bytes(sig);
|
||||||
let m = self.vp1(&s);
|
let m = vp1(&self.n, &self.e, &s);
|
||||||
let em = m.to_bytes();
|
let em = s.to_bytes();
|
||||||
let em_ = pkcs1_pad(signhash.ident, &hash, $size/8);
|
let em_ = pkcs1_pad(&sighash.ident, &hash, m.byte_size());
|
||||||
em == em_
|
em == em_
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encrypt_rng<G,H>(&self,g: &mut G,oaep: &OAEPParams<H>,msg: &[u8])
|
/// 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>
|
-> Result<Vec<u8>,RSAError>
|
||||||
where
|
|
||||||
G: Rng,
|
|
||||||
H: Default + Digest + FixedOutput
|
|
||||||
{
|
{
|
||||||
let byte_len = $size / 8;
|
let mut g = OsRng::new()?;
|
||||||
let mut res = Vec::new();
|
self.encrypt_with_rng(&mut g, oaep, msg)
|
||||||
|
}
|
||||||
|
|
||||||
if byte_len <= ((2 * oaep.hash_len()) + 2) {
|
/// 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);
|
return Err(RSAError::KeyTooSmallForHash);
|
||||||
}
|
}
|
||||||
for chunk in msg.chunks(byte_len - (2 * oaep.hash_len()) - 2) {
|
|
||||||
|
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)?;
|
let mut newchunk = self.oaep_encrypt(g, oaep, chunk)?;
|
||||||
res.append(&mut newchunk);
|
res.append(&mut newchunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl $rsa {
|
fn oaep_encrypt<G,H>(&self, g: &mut G, oaep: &OAEPParams<H>, msg: &[u8])
|
||||||
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>
|
-> Result<Vec<u8>,RSAError>
|
||||||
where
|
where
|
||||||
G: Rng,
|
G: Rng, H:Clone + Input + FixedOutput
|
||||||
H: Default + Digest + FixedOutput
|
|
||||||
{
|
{
|
||||||
let byte_len = $size / 8;
|
let mylen = self.e.byte_size();
|
||||||
|
|
||||||
// Step 1b
|
// Step 1b
|
||||||
if m.len() > (byte_len - (2 * oaep.hash_len()) - 2) {
|
if msg.len() > (mylen - (2 * oaep.hash_len()) - 2) {
|
||||||
return Err(RSAError::BadMessageSize)
|
return Err(RSAError::BadMessageSize)
|
||||||
}
|
}
|
||||||
// Step 2a
|
// Step 2a
|
||||||
let mut lhash = oaep.hash(oaep.label.as_bytes());
|
let mut lhash = oaep.hash(oaep.label.as_bytes());
|
||||||
// Step 2b
|
// Step 2b
|
||||||
let num0s = byte_len - m.len() - (2 * oaep.hash_len()) - 2;
|
let num0s = mylen - msg.len() - (2 * oaep.hash_len()) - 2;
|
||||||
let mut ps = Vec::new();
|
let mut ps = Vec::new();
|
||||||
ps.resize(num0s, 0);
|
ps.resize(num0s, 0);
|
||||||
// Step 2c
|
// Step 2c
|
||||||
@@ -266,13 +107,11 @@ macro_rules! generate_rsa_public
|
|||||||
db.append(&mut lhash);
|
db.append(&mut lhash);
|
||||||
db.append(&mut ps);
|
db.append(&mut ps);
|
||||||
db.push(1);
|
db.push(1);
|
||||||
db.extend_from_slice(m);
|
db.extend_from_slice(msg);
|
||||||
// Step 2d
|
// Step 2d
|
||||||
let mut seed: Vec<u8> = Vec::with_capacity(oaep.hash_len());
|
let seed : Vec<u8> = g.gen_iter().take(oaep.hash_len()).collect();
|
||||||
seed.resize(oaep.hash_len(), 0);
|
|
||||||
g.fill(seed.as_mut_slice());
|
|
||||||
// Step 2e
|
// Step 2e
|
||||||
let db_mask = oaep.mgf1(&seed, byte_len - oaep.hash_len() - 1);
|
let db_mask = oaep.mgf1(&seed, mylen - oaep.hash_len() - 1);
|
||||||
// Step 2f
|
// Step 2f
|
||||||
let mut masked_db = xor_vecs(&db, &db_mask);
|
let mut masked_db = xor_vecs(&db, &db_mask);
|
||||||
// Step 2g
|
// Step 2g
|
||||||
@@ -285,173 +124,73 @@ macro_rules! generate_rsa_public
|
|||||||
em.append(&mut masked_seed);
|
em.append(&mut masked_seed);
|
||||||
em.append(&mut masked_db);
|
em.append(&mut masked_db);
|
||||||
// Step 3a
|
// Step 3a
|
||||||
let m_i = $num::from_bytes(&em);
|
let m_i = U::from_bytes(&em);
|
||||||
// Step 3b
|
// Step 3b
|
||||||
let c_i = self.ep(&m_i);
|
let c_i = ep(&self.n, &self.e, &m_i);
|
||||||
// Step 3c
|
// Step 3c
|
||||||
let c = c_i.to_bytes();
|
let c = c_i.to_bytes();
|
||||||
Ok(c)
|
Ok(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromASN1 for $rsa {
|
impl<T> FromASN1 for RSAPublicKey<T>
|
||||||
|
where
|
||||||
|
T: CryptoNumBase + CryptoNumSerialization,
|
||||||
|
T: From<BigUint>
|
||||||
|
{
|
||||||
type Error = RSAError;
|
type Error = RSAError;
|
||||||
|
|
||||||
fn from_asn1(bs: &[ASN1Block])
|
fn from_asn1(bs: &[ASN1Block])
|
||||||
-> Result<($rsa,&[ASN1Block]),RSAError>
|
-> Result<(RSAPublicKey<T>,&[ASN1Block]),RSAError>
|
||||||
{
|
{
|
||||||
let (core, rest) = RSAPublic::from_asn1(bs)?;
|
match bs.split_first() {
|
||||||
|
None =>
|
||||||
|
Err(RSAError::ASN1DecodeErr(ASN1DecodeErr::EmptyBuffer)),
|
||||||
|
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 mut rsa_size = 512;
|
||||||
|
|
||||||
match core {
|
while rsa_size < nsize {
|
||||||
RSAPublic::$var(x) => Ok((x, rest)),
|
rsa_size = rsa_size + 256;
|
||||||
_ => Err(RSAError::InvalidKey)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
rsa_size /= 8;
|
||||||
|
|
||||||
|
if rsa_size != (T::zero()).bit_size() {
|
||||||
|
return Err(RSAError::KeySizeMismatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToASN1 for $rsa {
|
let n = T::from(numn);
|
||||||
|
let e = T::from(nume);
|
||||||
|
|
||||||
|
let res = RSAPublicKey{ n: n, e: e };
|
||||||
|
|
||||||
|
Ok((res, rest))
|
||||||
|
}
|
||||||
|
Some(_) =>
|
||||||
|
Err(RSAError::InvalidKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ToASN1 for RSAPublicKey<T>
|
||||||
|
where
|
||||||
|
T: Clone + Into<BigInt>,
|
||||||
|
T: CryptoNumBase + CryptoNumSerialization
|
||||||
|
{
|
||||||
type Error = ASN1EncodeErr;
|
type Error = ASN1EncodeErr;
|
||||||
|
|
||||||
fn to_asn1_class(&self, c: ASN1Class)
|
fn to_asn1_class(&self, c: ASN1Class)
|
||||||
-> Result<Vec<ASN1Block>,Self::Error>
|
-> Result<Vec<ASN1Block>,Self::Error>
|
||||||
{
|
{
|
||||||
let n = self.n.to_num();
|
let enc_n = ASN1Block::Integer(c, 0, self.n.clone().into());
|
||||||
let e = self.e.to_num();
|
let enc_e = ASN1Block::Integer(c, 0, self.e.clone().into());
|
||||||
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]);
|
let seq = ASN1Block::Sequence(c, 0, vec![enc_n, enc_e]);
|
||||||
Ok(vec![seq])
|
Ok(vec![seq])
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl fmt::Debug for $rsa {
|
|
||||||
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!(RSA512Public, U512, BarrettU512, Key512, 512);
|
|
||||||
generate_rsa_public!(RSA1024Public, U1024, BarrettU1024, Key1024, 1024);
|
|
||||||
generate_rsa_public!(RSA2048Public, U2048, BarrettU2048, Key2048, 2048);
|
|
||||||
generate_rsa_public!(RSA3072Public, U3072, BarrettU3072, Key3072, 3072);
|
|
||||||
generate_rsa_public!(RSA4096Public, U4096, BarrettU4096, Key4096, 4096);
|
|
||||||
generate_rsa_public!(RSA8192Public, U8192, BarrettU8192, Key8192, 8192);
|
|
||||||
generate_rsa_public!(RSA15360Public, U15360, BarrettU15360, Key15360, 15360);
|
|
||||||
|
|
||||||
macro_rules! generate_tests {
|
|
||||||
( $( ($mod: ident, $rsa: 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::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn encode() {
|
|
||||||
let fname = format!("tests/rsa/rsa{}.test", $size);
|
|
||||||
run_test(fname.to_string(), 8, |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);
|
|
||||||
println!("n: {:X}", n);
|
|
||||||
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 = $rsa{ n: n, nu: $bar::from_components(k, n64, nu), e: e };
|
|
||||||
let asn1 = pubkey.to_asn1().unwrap();
|
|
||||||
let (pubkey2, _) = $rsa::from_asn1(&asn1).unwrap();
|
|
||||||
assert_eq!(pubkey, pubkey2);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn verify() {
|
|
||||||
let fname = format!("tests/rsa/rsa{}.test", $size);
|
|
||||||
run_test(fname.to_string(), 8, |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 = $rsa{ n: n, nu: $bar::from_components(k, n64, nu), e: e };
|
|
||||||
let hashnum = ((hbytes[0] as u16)<<8) + (hbytes[1] as u16);
|
|
||||||
let sighash = match hashnum {
|
|
||||||
0x160 => &SIGNING_HASH_SHA1,
|
|
||||||
0x224 => &SIGNING_HASH_SHA224,
|
|
||||||
0x256 => &SIGNING_HASH_SHA256,
|
|
||||||
0x384 => &SIGNING_HASH_SHA384,
|
|
||||||
0x512 => &SIGNING_HASH_SHA512,
|
|
||||||
_ => panic!("Bad signing hash: {}", hashnum)
|
|
||||||
};
|
|
||||||
assert!(pubkey.verify(sighash, &mbytes, &sbytes));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn encrypt() {
|
|
||||||
let fname = format!("tests/rsa/rsa{}.test", $size);
|
|
||||||
run_test(fname.to_string(), 8, |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 = $rsa{ n: n, nu: $bar::from_components(k, n64, nu), e: e };
|
|
||||||
let hashnum = ((hbytes[0] as u16)<<8) + (hbytes[1] as u16);
|
|
||||||
let sighash = match hashnum {
|
|
||||||
0x160 => &SIGNING_HASH_SHA1,
|
|
||||||
0x224 => &SIGNING_HASH_SHA224,
|
|
||||||
0x256 => &SIGNING_HASH_SHA256,
|
|
||||||
0x384 => &SIGNING_HASH_SHA384,
|
|
||||||
0x512 => &SIGNING_HASH_SHA512,
|
|
||||||
_ => panic!("Bad signing hash: {}", hashnum)
|
|
||||||
};
|
|
||||||
assert!(pubkey.verify(sighash, &mbytes, &sbytes));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_tests!( (RSA512, RSA512Public, U512, BarrettU512, U576, 512),
|
|
||||||
(RSA1024, RSA1024Public, U1024, BarrettU1024, U1088, 1024),
|
|
||||||
(RSA2048, RSA2048Public, U2048, BarrettU2048, U2112, 2048),
|
|
||||||
(RSA3072, RSA3072Public, U3072, BarrettU3072, U3136, 3072),
|
|
||||||
(RSA4096, RSA4096Public, U4096, BarrettU4096, U4160, 4096),
|
|
||||||
(RSA8192, RSA8192Public, U8192, BarrettU8192, U8256, 8192),
|
|
||||||
(RSA15360, RSA15360Public, U15360, BarrettU15360, U15424, 15360)
|
|
||||||
);
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use digest::Digest;
|
use digest::{FixedOutput,Input};
|
||||||
use sha1::Sha1;
|
use sha1::Sha1;
|
||||||
use sha2::{Sha224,Sha256,Sha384,Sha512};
|
use sha2::{Sha224,Sha256,Sha384,Sha512};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -46,7 +46,9 @@ pub static SIGNING_HASH_SHA1: SigningHash = SigningHash {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn runsha1(i: &[u8]) -> Vec<u8> {
|
fn runsha1(i: &[u8]) -> Vec<u8> {
|
||||||
Sha1::digest(i).as_slice().to_vec()
|
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
|
/// Sign a hash based on SHA2-224. This is the first reasonable choice
|
||||||
@@ -61,7 +63,9 @@ pub static SIGNING_HASH_SHA224: SigningHash = SigningHash {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn runsha224(i: &[u8]) -> Vec<u8> {
|
fn runsha224(i: &[u8]) -> Vec<u8> {
|
||||||
Sha224::digest(i).as_slice().to_vec()
|
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!
|
/// Sign a hash based on SHA2-256. The first one I'd recommend!
|
||||||
@@ -74,7 +78,9 @@ pub static SIGNING_HASH_SHA256: SigningHash = SigningHash {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn runsha256(i: &[u8]) -> Vec<u8> {
|
fn runsha256(i: &[u8]) -> Vec<u8> {
|
||||||
Sha256::digest(i).as_slice().to_vec()
|
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
|
/// Sign a hash based on SHA2-384. Approximately 50% better than
|
||||||
@@ -88,7 +94,9 @@ pub static SIGNING_HASH_SHA384: SigningHash = SigningHash {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn runsha384(i: &[u8]) -> Vec<u8> {
|
fn runsha384(i: &[u8]) -> Vec<u8> {
|
||||||
Sha384::digest(i).as_slice().to_vec()
|
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
|
/// Sign a hash based on SHA2-512. At this point, you're getting a bit
|
||||||
@@ -103,7 +111,9 @@ pub static SIGNING_HASH_SHA512: SigningHash = SigningHash {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn runsha512(i: &[u8]) -> Vec<u8> {
|
fn runsha512(i: &[u8]) -> Vec<u8> {
|
||||||
Sha512::digest(i).as_slice().to_vec()
|
let mut d = Sha512::default();
|
||||||
|
d.process(i);
|
||||||
|
d.fixed_result().as_slice().to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
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).unwrap() as u8 );
|
|
||||||
}
|
|
||||||
Some(c2) => {
|
|
||||||
let b1 = c1.to_digit(16).unwrap() as u8;
|
|
||||||
let b2 = c2.to_digit(16).unwrap() 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
57
src/utils.rs
@@ -1,57 +0,0 @@
|
|||||||
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);
|
|
||||||
@@ -1,377 +0,0 @@
|
|||||||
use num::BigUint;
|
|
||||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1};
|
|
||||||
use x509::error::X509ParseError;
|
|
||||||
|
|
||||||
#[derive(Clone,Copy,Debug,PartialEq)]
|
|
||||||
pub enum HashAlgorithm { SHA1, SHA224, SHA256, SHA384, SHA512 }
|
|
||||||
|
|
||||||
#[derive(Clone,Copy,Debug,PartialEq)]
|
|
||||||
pub enum PublicKeyInfo { RSA, DSA, ECDSA }
|
|
||||||
|
|
||||||
#[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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
368
src/x509/atv.rs
368
src/x509/atv.rs
@@ -1,368 +0,0 @@
|
|||||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,ToASN1};
|
|
||||||
use std::ops::Index;
|
|
||||||
use x509::error::X509ParseError;
|
|
||||||
use x509::name::X520Name;
|
|
||||||
|
|
||||||
#[derive(Clone,Debug)]
|
|
||||||
pub struct InfoBlock {
|
|
||||||
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])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone,Debug,PartialEq)]
|
|
||||||
struct AttributeTypeValue {
|
|
||||||
attrtype: X520Name,
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
195
src/x509/misc.rs
195
src/x509/misc.rs
@@ -1,195 +0,0 @@
|
|||||||
use num::{BigInt,BigUint,One,ToPrimitive,Zero};
|
|
||||||
use num::bigint::ToBigInt;
|
|
||||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,ToASN1};
|
|
||||||
use x509::error::X509ParseError;
|
|
||||||
|
|
||||||
#[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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
#[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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
301
src/x509/mod.rs
301
src/x509/mod.rs
@@ -1,301 +0,0 @@
|
|||||||
mod algident;
|
|
||||||
mod atv;
|
|
||||||
mod error;
|
|
||||||
mod misc;
|
|
||||||
mod name;
|
|
||||||
mod publickey;
|
|
||||||
mod validity;
|
|
||||||
|
|
||||||
use dsa::{DSAPublic,DSAPublicKey};
|
|
||||||
use ecdsa::{ECDSAPublic,ECCPublicKey};
|
|
||||||
use rsa::{SIGNING_HASH_SHA1,SIGNING_HASH_SHA224,SIGNING_HASH_SHA256,SIGNING_HASH_SHA384,SIGNING_HASH_SHA512};
|
|
||||||
use sha1::Sha1;
|
|
||||||
use sha2::{Sha224,Sha256,Sha384,Sha512};
|
|
||||||
use simple_asn1::{ASN1Block,FromASN1,der_decode,from_der};
|
|
||||||
use x509::validity::Validity;
|
|
||||||
use x509::algident::{AlgorithmIdentifier,HashAlgorithm,PublicKeyInfo,
|
|
||||||
decode_algorithm_ident};
|
|
||||||
use x509::atv::InfoBlock;
|
|
||||||
use x509::error::X509ParseError;
|
|
||||||
use x509::misc::{X509Serial,X509Version,decode_signature};
|
|
||||||
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) => {
|
|
||||||
println!("STEP1");
|
|
||||||
let (version, b1) = X509Version::from_asn1(b0)?;
|
|
||||||
println!("STEP2");
|
|
||||||
let (serial, b2) = X509Serial::from_asn1(b1)?;
|
|
||||||
println!("STEP3");
|
|
||||||
let (ident, b3) = AlgorithmIdentifier::from_asn1(b2)?;
|
|
||||||
println!("STEP4");
|
|
||||||
let (issuer, b4) = InfoBlock::from_asn1(b3)?;
|
|
||||||
println!("STEP5");
|
|
||||||
let (validity, b5) = Validity::from_asn1(b4)?;
|
|
||||||
println!("STEP6");
|
|
||||||
let (subject, b6) = InfoBlock::from_asn1(b5)?;
|
|
||||||
println!("STEP7");
|
|
||||||
let (subkey, _ ) = X509PublicKey::from_asn1(b6)?;
|
|
||||||
println!("STEP8");
|
|
||||||
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
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
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::ECCPublicP192(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::ECCPublicP224(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::ECCPublicP256(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::ECCPublicP384(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::ECCPublicP521(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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
136
src/x509/name.rs
136
src/x509/name.rs
@@ -1,136 +0,0 @@
|
|||||||
use num::BigUint;
|
|
||||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,OID,ToASN1};
|
|
||||||
use x509::error::X509ParseError;
|
|
||||||
|
|
||||||
#[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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,328 +0,0 @@
|
|||||||
use cryptonum::unsigned::{U3072,U2048,U1024,U256,U192};
|
|
||||||
use dsa::{DSAPublic,DSAPublicKey,DSAPubKey,DSAParameters};
|
|
||||||
use dsa::{L3072N256,L2048N256,L2048N224,L1024N160};
|
|
||||||
use ecdsa::{ECDSAEncodeErr,ECDSAPublic,ECCPubKey};
|
|
||||||
use ecdsa::curve::{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;
|
|
||||||
|
|
||||||
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 = DSAPubKey::<L3072N256,U3072>::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 = DSAPubKey::<L2048N256,U2048>::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 = DSAPubKey::<L2048N224,U2048>::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 = DSAPubKey::<L1024N160,U1024>::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::ECCPublicP192(k) => (oid!(1,2,840,10045,3,1,1), k.to_asn1_class(c)?),
|
|
||||||
ECDSAPublic::ECCPublicP224(k) => (oid!(1,3,132,0,33), k.to_asn1_class(c)?),
|
|
||||||
ECDSAPublic::ECCPublicP256(k) => (oid!(1,2,840,10045,3,1,7), k.to_asn1_class(c)?),
|
|
||||||
ECDSAPublic::ECCPublicP384(k) => (oid!(1,3,132,0,34), k.to_asn1_class(c)?),
|
|
||||||
ECDSAPublic::ECCPublicP521(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, _) = ECCPubKey::<P192>::from_asn1(keybls)?;
|
|
||||||
return Ok(ECDSAPublic::ECCPublicP192(res));
|
|
||||||
}
|
|
||||||
|
|
||||||
if oid == oid!(1,3,132,0,33) {
|
|
||||||
let (res, _) = ECCPubKey::<P224>::from_asn1(keybls)?;
|
|
||||||
return Ok(ECDSAPublic::ECCPublicP224(res));
|
|
||||||
}
|
|
||||||
|
|
||||||
if oid == oid!(1,2,840,10045,3,1,7) {
|
|
||||||
let (res, _) = ECCPubKey::<P256>::from_asn1(keybls)?;
|
|
||||||
return Ok(ECDSAPublic::ECCPublicP256(res));
|
|
||||||
}
|
|
||||||
|
|
||||||
if oid == oid!(1,3,132,0,34) {
|
|
||||||
let (res, _) = ECCPubKey::<P384>::from_asn1(keybls)?;
|
|
||||||
return Ok(ECDSAPublic::ECCPublicP384(res));
|
|
||||||
}
|
|
||||||
|
|
||||||
if oid == oid!(1,3,132,0,35) {
|
|
||||||
let (res, _) = ECCPubKey::<P521>::from_asn1(keybls)?;
|
|
||||||
return Ok(ECDSAPublic::ECCPublicP521(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
use chrono::{DateTime,Utc};
|
|
||||||
use simple_asn1::{ASN1Block,ASN1Class,ASN1EncodeErr,FromASN1,ToASN1};
|
|
||||||
use x509::error::X509ParseError;
|
|
||||||
|
|
||||||
#[derive(Clone,Debug,PartialEq)]
|
|
||||||
pub struct Validity {
|
|
||||||
not_before: DateTime<Utc>,
|
|
||||||
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
2
test-generator/.gitignore
vendored
@@ -1,2 +0,0 @@
|
|||||||
dist/
|
|
||||||
dist-newstyle/
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
module Database(
|
|
||||||
Database,
|
|
||||||
emptyDatabase,
|
|
||||||
generateNum, genSign
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Crypto.Random(DRG(..),SystemDRG)
|
|
||||||
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], SystemDRG)
|
|
||||||
|
|
||||||
emptyDatabase :: SystemDRG -> 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 0 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
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
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 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(..))
|
|
||||||
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 = 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 = 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 = 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 = 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 = 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 = 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
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{-# LANGUAGE LambdaCase #-}
|
|
||||||
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.Random(SystemDRG,getSystemDRG)
|
|
||||||
import ECDSATesting(ecdsaTasks)
|
|
||||||
import GHC.Conc(getNumCapabilities)
|
|
||||||
import RFC6979(rfcTasks)
|
|
||||||
import System.Console.AsciiProgress
|
|
||||||
import Task(Task, runTask)
|
|
||||||
|
|
||||||
taskExecutor :: MVar [Task] -> Chan () -> SystemDRG -> IO SystemDRG
|
|
||||||
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 <- getSystemDRG
|
|
||||||
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 (ecdsaTasks ++ rfcTasks)
|
|
||||||
replicateM_ executors (spawnExecutor tasks done)
|
|
||||||
replicateM_ executors (void $ readChan done)
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
{-# 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'''
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
module RFC6979
|
|
||||||
-- (
|
|
||||||
-- rfcTasks
|
|
||||||
-- )
|
|
||||||
where
|
|
||||||
|
|
||||||
import Crypto.Hash(SHA224(..),SHA256(..),SHA384(..),SHA512(..))
|
|
||||||
import Crypto.MAC.HMAC(HMAC,hmac)
|
|
||||||
import Crypto.Number.Generate(generateBetween)
|
|
||||||
import 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(..))
|
|
||||||
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 = 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]
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
module Task(
|
|
||||||
Test,
|
|
||||||
Task(..),
|
|
||||||
runTask
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Control.Monad(foldM, forM_)
|
|
||||||
import Crypto.Random(SystemDRG)
|
|
||||||
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 -> (Map.Map String String, Integer, Database)
|
|
||||||
|
|
||||||
data Task = Task {
|
|
||||||
taskName :: String,
|
|
||||||
taskFile :: FilePath,
|
|
||||||
taskTest :: Test,
|
|
||||||
taskCount :: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
runTask :: SystemDRG -> Task -> IO SystemDRG
|
|
||||||
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 let (output, key, acc@(db',gen')) = runner db
|
|
||||||
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')
|
|
||||||
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
module Utils(HashAlg(..), generateHash, runHash, showHash)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Crypto.Hash(Digest,SHA224(..),SHA256(..),SHA384(..),SHA512(..),hash)
|
|
||||||
import Crypto.Number.Generate(generateBetween)
|
|
||||||
import 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"
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
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 ""
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
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, ECDSATesting, Math, RFC6979, Task, Utils
|
|
||||||
-- other-extensions:
|
|
||||||
build-depends: base >=4.11 && < 4.14, ascii-progress, bytestring, containers, cryptonite, directory, filepath, integer-gmp, memory, random
|
|
||||||
hs-source-dirs: .
|
|
||||||
default-language: Haskell2010
|
|
||||||
ghc-options: -Wall -O2 -threaded -rtsopts -with-rtsopts=-N
|
|
||||||
6006
testdata/ecc/add/P192.test
vendored
6006
testdata/ecc/add/P192.test
vendored
File diff suppressed because it is too large
Load Diff
6006
testdata/ecc/add/P224.test
vendored
6006
testdata/ecc/add/P224.test
vendored
File diff suppressed because it is too large
Load Diff
6006
testdata/ecc/add/P256.test
vendored
6006
testdata/ecc/add/P256.test
vendored
File diff suppressed because it is too large
Load Diff
6006
testdata/ecc/add/P384.test
vendored
6006
testdata/ecc/add/P384.test
vendored
File diff suppressed because it is too large
Load Diff
6006
testdata/ecc/add/P521.test
vendored
6006
testdata/ecc/add/P521.test
vendored
File diff suppressed because it is too large
Load Diff
8008
testdata/ecc/add_scale2/P192.test
vendored
8008
testdata/ecc/add_scale2/P192.test
vendored
File diff suppressed because it is too large
Load Diff
8008
testdata/ecc/add_scale2/P224.test
vendored
8008
testdata/ecc/add_scale2/P224.test
vendored
File diff suppressed because it is too large
Load Diff
8008
testdata/ecc/add_scale2/P256.test
vendored
8008
testdata/ecc/add_scale2/P256.test
vendored
File diff suppressed because it is too large
Load Diff
8008
testdata/ecc/add_scale2/P384.test
vendored
8008
testdata/ecc/add_scale2/P384.test
vendored
File diff suppressed because it is too large
Load Diff
8008
testdata/ecc/add_scale2/P521.test
vendored
8008
testdata/ecc/add_scale2/P521.test
vendored
File diff suppressed because it is too large
Load Diff
4004
testdata/ecc/double/P192.test
vendored
4004
testdata/ecc/double/P192.test
vendored
File diff suppressed because it is too large
Load Diff
4004
testdata/ecc/double/P224.test
vendored
4004
testdata/ecc/double/P224.test
vendored
File diff suppressed because it is too large
Load Diff
4004
testdata/ecc/double/P256.test
vendored
4004
testdata/ecc/double/P256.test
vendored
File diff suppressed because it is too large
Load Diff
4004
testdata/ecc/double/P384.test
vendored
4004
testdata/ecc/double/P384.test
vendored
File diff suppressed because it is too large
Load Diff
4004
testdata/ecc/double/P521.test
vendored
4004
testdata/ecc/double/P521.test
vendored
File diff suppressed because it is too large
Load Diff
4004
testdata/ecc/negate/P192.test
vendored
4004
testdata/ecc/negate/P192.test
vendored
File diff suppressed because it is too large
Load Diff
4004
testdata/ecc/negate/P224.test
vendored
4004
testdata/ecc/negate/P224.test
vendored
File diff suppressed because it is too large
Load Diff
4004
testdata/ecc/negate/P256.test
vendored
4004
testdata/ecc/negate/P256.test
vendored
File diff suppressed because it is too large
Load Diff
4004
testdata/ecc/negate/P384.test
vendored
4004
testdata/ecc/negate/P384.test
vendored
File diff suppressed because it is too large
Load Diff
4004
testdata/ecc/negate/P521.test
vendored
4004
testdata/ecc/negate/P521.test
vendored
File diff suppressed because it is too large
Load Diff
5005
testdata/ecc/scale/P192.test
vendored
5005
testdata/ecc/scale/P192.test
vendored
File diff suppressed because it is too large
Load Diff
5005
testdata/ecc/scale/P224.test
vendored
5005
testdata/ecc/scale/P224.test
vendored
File diff suppressed because it is too large
Load Diff
5005
testdata/ecc/scale/P256.test
vendored
5005
testdata/ecc/scale/P256.test
vendored
File diff suppressed because it is too large
Load Diff
5005
testdata/ecc/scale/P384.test
vendored
5005
testdata/ecc/scale/P384.test
vendored
File diff suppressed because it is too large
Load Diff
5005
testdata/ecc/scale/P521.test
vendored
5005
testdata/ecc/scale/P521.test
vendored
File diff suppressed because it is too large
Load Diff
9009
testdata/ecc/sign/P192.test
vendored
9009
testdata/ecc/sign/P192.test
vendored
File diff suppressed because it is too large
Load Diff
9009
testdata/ecc/sign/P224.test
vendored
9009
testdata/ecc/sign/P224.test
vendored
File diff suppressed because it is too large
Load Diff
9009
testdata/ecc/sign/P256.test
vendored
9009
testdata/ecc/sign/P256.test
vendored
File diff suppressed because it is too large
Load Diff
9009
testdata/ecc/sign/P384.test
vendored
9009
testdata/ecc/sign/P384.test
vendored
File diff suppressed because it is too large
Load Diff
9009
testdata/ecc/sign/P521.test
vendored
9009
testdata/ecc/sign/P521.test
vendored
File diff suppressed because it is too large
Load Diff
7007
testdata/rfc6979/SHA224.test
vendored
7007
testdata/rfc6979/SHA224.test
vendored
File diff suppressed because it is too large
Load Diff
7007
testdata/rfc6979/SHA256.test
vendored
7007
testdata/rfc6979/SHA256.test
vendored
File diff suppressed because it is too large
Load Diff
7007
testdata/rfc6979/SHA384.test
vendored
7007
testdata/rfc6979/SHA384.test
vendored
File diff suppressed because it is too large
Load Diff
7007
testdata/rfc6979/SHA512.test
vendored
7007
testdata/rfc6979/SHA512.test
vendored
File diff suppressed because it is too large
Load Diff
BIN
testdata/x509/dsa2048-1.der
vendored
BIN
testdata/x509/dsa2048-1.der
vendored
Binary file not shown.
BIN
testdata/x509/dsa2048-2.der
vendored
BIN
testdata/x509/dsa2048-2.der
vendored
Binary file not shown.
BIN
testdata/x509/dsa3072-1.der
vendored
BIN
testdata/x509/dsa3072-1.der
vendored
Binary file not shown.
BIN
testdata/x509/dsa3072-2.der
vendored
BIN
testdata/x509/dsa3072-2.der
vendored
Binary file not shown.
BIN
testdata/x509/ec384-1.der
vendored
BIN
testdata/x509/ec384-1.der
vendored
Binary file not shown.
BIN
testdata/x509/ec384-2.der
vendored
BIN
testdata/x509/ec384-2.der
vendored
Binary file not shown.
BIN
testdata/x509/ec384-3.der
vendored
BIN
testdata/x509/ec384-3.der
vendored
Binary file not shown.
BIN
testdata/x509/rsa2048-1.der
vendored
BIN
testdata/x509/rsa2048-1.der
vendored
Binary file not shown.
BIN
testdata/x509/rsa2048-2.der
vendored
BIN
testdata/x509/rsa2048-2.der
vendored
Binary file not shown.
BIN
testdata/x509/rsa4096-1.der
vendored
BIN
testdata/x509/rsa4096-1.der
vendored
Binary file not shown.
BIN
testdata/x509/rsa4096-2.der
vendored
BIN
testdata/x509/rsa4096-2.der
vendored
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user