66 Commits

Author SHA1 Message Date
bdf7f81b20 [BROKEN] Trying to get elliptic curve working, which is much too slow at the moment. 2018-05-31 18:37:18 +01:00
dde1092f49 Start building out ECC infrastructure. 2018-05-21 18:38:56 +01:00
6fabbe6af1 Initial port-over of ECDSA signing. 2018-05-16 22:00:17 -07:00
f83b8a3fe5 Ignore more things. 2018-05-15 15:42:51 -07:00
61f4a009a0 Update the Travis build to use stable, and to leave some time for the tests to run. 2018-05-15 14:45:39 -07:00
1d67b4c775 Split the encryption test suite into two parts, to help reduce the cost of the test suite. 2018-05-15 07:28:00 -07:00
81ccf3e06b Trying to cut down the time taken running the test suite, so that we fit in Travis's expectations. 2018-05-12 16:41:55 -07:00
219641da5e Try to speed up DSA a bit using Barrett reduction. 2018-05-12 16:41:33 -07:00
f0f4891abe DSA! Working, with tests! 2018-05-12 08:04:33 -07:00
3d767c3e13 Switch to a Java-based test generator, which seems to work better. 2018-05-06 21:22:10 -07:00
a2b4baa087 Initial DSA support.
Should be upgraded with faster modulo operations and a
more full test suite.
2018-05-05 21:06:11 -07:00
213c75ad51 Add some #[ignore]s for the longer-running tests. 2018-05-05 19:47:59 -07:00
29a14b39e6 Fix the bits() implementation, and add is_multiple_of() and gcd(). 2018-05-05 19:42:21 -07:00
c34629aa47 Add conversions for BigInt/BigUint. 2018-05-05 19:41:30 -07:00
b01c59a094 I've started playing with IDEs, let's ignore their leavings. 2018-05-05 19:38:48 -07:00
fa04efa5fe Encryption! With test cases. 2018-05-02 17:05:17 -07:00
bd0ddd848b A very slightly faster modexp. 2018-05-01 23:04:06 -07:00
9c60a3bc3e Ignore the Haskell executable I generated. 2018-05-01 22:31:04 -07:00
7c28727f73 RSA signature verification. 2018-05-01 22:30:07 -07:00
c9092ffe6a Slightly better test generation for RSA signatures. 2018-05-01 22:29:37 -07:00
296bb6ad90 Remove an unnecessary mut. 2018-05-01 22:28:59 -07:00
d9df506920 Start with RSA signing! Looks like it works against Haskell RSA test vectors. 2018-04-30 13:05:57 -07:00
2eacea8ff9 Factor out the testing code, so we can use it later. 2018-04-30 13:05:10 -07:00
153d88237f Clean up (and make a lot more flexible) the code to translate to/from bytes. 2018-04-30 13:04:46 -07:00
5758b6e22b Factor out the gold testing infrastructure so we can use it elsewhere. 2018-04-23 20:31:02 -07:00
baa70a6ce6 Starting to include RSA crypto. 2018-04-14 10:45:11 -07:00
b5a5cbdd98 Serialization routines. 2018-04-14 07:56:03 -07:00
4985426e74 Prospective prime support. 2018-04-14 07:16:50 -07:00
c45235473a Support modular inverses. 2018-04-14 07:05:57 -07:00
b1c659087d Add a quick test to ensure that our GCD algorithm works. 2018-04-14 07:01:59 -07:00
0d08f53d70 Make sure we print 0. 2018-04-14 06:59:49 -07:00
109e23789a Support fast modular exponentiation for when your base is roughly the same order of magnitude as the modulo. 2018-04-13 11:51:39 -04:00
551ebeac3b Fix some println!() leavings. 2018-04-13 11:06:42 -04:00
017392ff6c Fix the conversion functions, make sure we can do usize, too. 2018-04-13 10:57:22 -04:00
d98baa1381 Fix Barrett reduction. 2018-04-13 10:56:59 -04:00
330dabe017 Shift the gold testing infrastructure into its own module, and add the Haskell program I used to generate the tests. 2018-04-13 10:56:42 -04:00
675f8adc7e [BROKEN] Division seems to be broken? Might need better test vectors. 2018-04-05 18:02:03 -07:00
5868553c74 First whack at prime numbers and such. 2018-04-04 17:27:07 -07:00
ceb1e9eb58 Fix some shifting issues. 2018-04-04 17:26:49 -07:00
3cd37a881d First whack at modular inverses. 2018-04-04 18:47:02 -04:00
2f16a45784 Quickcheck properties for signed numbers. 2018-04-04 18:28:01 -04:00
f06f83583f Whoops. Don't correct for rounding if there's no remainder on the division. 2018-04-04 18:13:50 -04:00
ae6a33f4b8 Support negation for signed numbers. 2018-04-04 18:12:30 -04:00
8a4693d30d Zero is a problem for me. 2018-04-04 17:57:11 -04:00
20592a3d65 I guess i128_type isn't stable in the beta after all. 2018-04-03 07:04:38 -04:00
acda294bac Signed numbers! 2018-04-02 16:36:28 -04:00
c4409d9c25 Fix some printlns and other bad bits. 2018-04-02 15:26:41 -04:00
ec0f0dc597 Add support for gold value testing, as well, and test some stuff. 2018-04-02 15:26:16 -04:00
80f57b9f22 Fix division by not returning a weirdly-shifted remainder when the quotient is zero. 2018-04-02 15:24:48 -04:00
fa33de88db Fix subtraction. 2018-04-02 15:23:38 -04:00
b92b47d971 [BROKEN] First crach at division. 2018-04-01 20:43:19 -07:00
a4e65fa35f Fix multiplication when either argument is zero. 2018-04-01 20:42:49 -07:00
30bff2a22f Multiplication. 2018-03-31 21:25:10 -07:00
185881df91 Addition and subtraction. 2018-03-31 18:03:17 -07:00
824718eafc Also now enable beta builds in the Travis file. 2018-03-29 21:45:14 -07:00
bfdede4241 Remove the i128 feature, as it's now good in nightlies. 2018-03-29 21:26:15 -07:00
d6c59b5037 Fix shifts! 2018-03-29 21:25:51 -07:00
fd9254a322 [BROKEN] Broken definitions of the shift operators. 2018-03-27 17:02:54 -07:00
f9b25ab03a Add lowercase hex formatting. 2018-03-25 20:18:13 -07:00
d53cdb6c97 And, or, and xor. 2018-03-25 20:14:25 -07:00
a595eb349d Negation. 2018-03-24 16:00:02 -07:00
03765c2ff6 Shrink to clean. 2018-03-24 15:56:02 -07:00
edd7c7fee3 Add a shrink function to clean up 0s at the end of numbers. 2018-03-24 13:06:50 -07:00
6b9783c69a Comparisons! 2018-03-24 12:03:29 -07:00
9c4ea7ae26 Start working variable-length numbers. 2018-03-23 22:12:34 -07:00
7a8bb7b4fd Nevermind on the whole fixed size thing? 2018-03-23 22:11:09 -07:00
66 changed files with 377943 additions and 1968 deletions

14
.gitignore vendored
View File

@@ -11,3 +11,17 @@ Cargo.lock
# And these are just annoying # And these are just annoying
.DS_Store .DS_Store
# And some Haskell stuff, because I can't shake it!
**/cabal.sandbox.config
**/.cabal-sandbox
# Test generation leavings
**/*.hi
**/*.o
**/gen
**/*.class
**/*.jar
# And I started playing with IDEs, so ...
.vscode

View File

@@ -1,3 +1,8 @@
language: rust language: rust
rust: rust:
- stable
- beta
- nightly - nightly
script:
- cargo build --verbose
- travis_wait 25 cargo test --verbose

View File

@@ -9,7 +9,14 @@ license-file = "LICENSE"
repository = "https://github.com/acw/simple_crypto" repository = "https://github.com/acw/simple_crypto"
[dependencies] [dependencies]
base64 = "^0.9.1"
digest = "^0.7.1"
hmac = "^0.5.0"
num = "^0.1.42"
rand = "^0.3" rand = "^0.3"
sha-1 = "^0.7.0"
sha2 = "^0.7.0"
simple_asn1 = "^0.1.0"
[dev-dependencies] [dev-dependencies]
quickcheck = "^0.4.1" quickcheck = "^0.4.1"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,126 @@
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
}
}
}
}
macro_rules! derive_shift_operators
{
($type: ident, $asncl: ident, $cl: ident,
$asnfn: ident, $fn: ident,
$base: ident) =>
{
impl $asncl<$base> for $type {
fn $asnfn(&mut self, rhs: $base) {
self.$asnfn(rhs as u64);
}
}
derive_shifts_from_shift_assign!($type, $asncl, $cl,
$asnfn, $fn,
$base);
}
}
macro_rules! derive_shifts_from_shift_assign
{
($type: ident, $asncl: ident, $cl: ident,
$asnfn: ident, $fn: ident,
$base: ident) =>
{
impl $cl<$base> for $type {
type Output = $type;
fn $fn(self, rhs: $base) -> $type {
let mut copy = self.clone();
copy.$asnfn(rhs as u64);
copy
}
}
impl<'a> $cl<$base> for &'a $type {
type Output = $type;
fn $fn(self, rhs: $base) -> $type {
let mut copy = self.clone();
copy.$asnfn(rhs as u64);
copy
}
}
}
}
macro_rules! derive_signed_shift_operators
{
($type: ident, $base: ident, $signed_base: ident) => {
impl ShlAssign<$signed_base> for $type {
fn shl_assign(&mut self, rhs: $signed_base) {
if rhs < 0 {
self.shr_assign(-rhs);
} else {
self.shl_assign(rhs as $base);
}
}
}
impl ShrAssign<$signed_base> for $type {
fn shr_assign(&mut self, rhs: $signed_base) {
if rhs < 0 {
self.shl_assign(-rhs);
} else {
self.shr_assign(rhs as $base);
}
}
}
derive_shifts_from_shift_assign!($type, ShlAssign, Shl,
shl_assign, shl, $signed_base);
derive_shifts_from_shift_assign!($type, ShrAssign, Shr,
shr_assign, shr, $signed_base);
}
}

View File

@@ -0,0 +1,85 @@
macro_rules! define_from
{
($type: ident, $base: ident) => {
impl From<$base> for $type {
fn from(x: $base) -> $type {
if x == 0 {
UCN{ contents: Vec::new() }
} else {
UCN{ contents: vec![x as u64] }
}
}
}
}
}
macro_rules! define_signed_from
{
($type: ident, $base: ident, $uns: ident) => {
impl From<$uns> for $type {
fn from(x: $uns) -> $type {
SCN{ negative: false, value: UCN::from(x) }
}
}
impl From<$base> for $type {
fn from(x: $base) -> $type {
let neg = x < 0;
let absx = x.abs();
SCN{ negative: neg, value: UCN::from(absx as $uns) }
}
}
}
}
macro_rules! define_into
{
($type: ident, $base: ident) => {
impl<'a> From<&'a $type> for $base {
fn from(x: &$type) -> $base {
if x.contents.is_empty() {
0
} else {
x.contents[0] as $base
}
}
}
impl From<$type> for $base {
fn from(x: $type) -> $base {
$base::from(&x)
}
}
}
}
macro_rules! define_signed_into
{
($type: ident, $base: ident, $uns: ident) => {
impl<'a> From<&'a $type> for $uns {
fn from(x: &$type) -> $uns {
let res: $uns = $uns::from(&x.value);
if x.negative { 0-res } else { res }
}
}
impl<'a> From<&'a $type> for $base {
fn from(x: &$type) -> $base {
let res: $uns = $uns::from(&x.value);
if x.negative { (0-res) as $base } else { res as $base }
}
}
impl From<$type> for $uns {
fn from(x: $type) -> $uns {
$uns::from(&x)
}
}
impl From<$type> for $base {
fn from(x: $type) -> $base {
$base::from(&x)
}
}
}
}

View File

@@ -1,332 +0,0 @@
use std::cmp::Ordering;
#[inline(always)]
pub fn generic_cmp(a: &[u64], b: &[u64]) -> Ordering {
let mut i = a.len() - 1;
assert!(a.len() == b.len());
loop {
match a[i].cmp(&b[i]) {
Ordering::Equal if i == 0 =>
return Ordering::Equal,
Ordering::Equal =>
i -= 1,
res =>
return res
}
}
}
fn le(a: &[u64], b: &[u64]) -> bool {
generic_cmp(a, b) != Ordering::Greater
}
pub fn ge(a: &[u64], b: &[u64]) -> bool {
generic_cmp(a, b) != Ordering::Less
}
#[inline(always)]
pub fn generic_bitand(a: &mut [u64], b: &[u64]) {
let mut i = 0;
assert!(a.len() == b.len());
while i < a.len() {
a[i] &= b[i];
i += 1;
}
}
#[inline(always)]
pub fn generic_bitor(a: &mut [u64], b: &[u64]) {
let mut i = 0;
assert!(a.len() == b.len());
while i < a.len() {
a[i] |= b[i];
i += 1;
}
}
#[inline(always)]
pub fn generic_bitxor(a: &mut [u64], b: &[u64]) {
let mut i = 0;
assert!(a.len() == b.len());
while i < a.len() {
a[i] ^= b[i];
i += 1;
}
}
#[inline(always)]
pub fn generic_not(a: &mut [u64]) {
for x in a.iter_mut() {
*x = !*x;
}
}
#[inline(always)]
pub fn generic_shl(a: &mut [u64], orig: &[u64], amount: usize) {
let digits = amount / 64;
let bits = amount % 64;
assert!(a.len() == orig.len());
for i in 0..a.len() {
if i < digits {
a[i] = 0;
} else {
let origidx = i - digits;
let prev = if origidx == 0 { 0 } else { orig[origidx - 1] };
let (carry,_) = if bits == 0 { (0, false) }
else { prev.overflowing_shr(64 - bits as u32) };
a[i] = (orig[origidx] << bits) | carry;
}
}
}
#[inline(always)]
pub fn generic_shr(a: &mut [u64], orig: &[u64], amount: usize) {
let digits = amount / 64;
let bits = amount % 64;
assert!(a.len() == orig.len());
for i in 0..a.len() {
let oldidx = i + digits;
let caridx = i + digits + 1;
let old = if oldidx >= a.len() { 0 } else { orig[oldidx] };
let carry = if caridx >= a.len() { 0 } else { orig[caridx] };
let cb = if bits == 0 { 0 } else { carry << (64 - bits) };
a[i] = (old >> bits) | cb;
}
}
#[inline(always)]
pub fn generic_add(a: &mut [u64], b: &[u64]) {
let mut carry = 0;
assert!(a.len() == b.len());
for i in 0..a.len() {
let x = a[i] as u128;
let y = b[i] as u128;
let total = x + y + carry;
a[i] = total as u64;
carry = total >> 64;
}
}
#[inline(always)]
pub fn generic_sub(a: &mut [u64], b: &[u64]) {
let mut negated_rhs = b.to_vec();
generic_not(&mut negated_rhs);
let mut one = Vec::with_capacity(a.len());
one.resize(a.len(), 0);
one[0] = 1;
generic_add(&mut negated_rhs, &one);
generic_add(a, &negated_rhs);
}
#[inline(always)]
pub fn generic_mul(a: &mut [u64], orig: &[u64], b: &[u64]) {
assert!(a.len() == orig.len());
assert!(a.len() == b.len());
assert!(a == orig);
// Build the output table. This is a little bit awkward because we don't
// know how big we're running, but hopefully the compiler is smart enough
// to work all this out.
let mut table = Vec::with_capacity(a.len());
for _ in 0..a.len() {
let mut row = Vec::with_capacity(a.len());
row.resize(a.len(), 0);
table.push(row);
}
// This uses "simple" grade school techniques to work things out. But,
// for reference, consider two 4 digit numbers:
//
// l0c3 l0c2 l0c1 l0c0 [orig]
// x l1c3 l1c2 l1c1 l1c0 [b]
// ------------------------------------------------------------
// (l0c3*l1c0) (l0c2*l1c0) (l0c1*l1c0) (l0c0*l1c0)
// (l0c2*l1c1) (l0c1*l1c1) (l0c0*l1c1)
// (l0c1*l1c2) (l0c0*l1c2)
// (l0c0*l1c3)
// ------------------------------------------------------------
// AAAAA BBBBB CCCCC DDDDD
for line in 0..a.len() {
let maxcol = a.len() - line;
for col in 0..maxcol {
let left = orig[col] as u128;
let right = b[line] as u128;
table[line][col + line] = left * right;
}
}
// ripple the carry across each line, ensuring that each entry in the
// table is 64-bits
for line in 0..a.len() {
let mut carry = 0;
for col in 0..a.len() {
table[line][col] = table[line][col] + carry;
carry = table[line][col] >> 64;
table[line][col] &= 0xFFFFFFFFFFFFFFFF;
}
}
// now do the final addition across the lines, rippling the carry as
// normal
let mut carry = 0;
for col in 0..a.len() {
let mut total = carry;
for line in 0..a.len() {
total += table[line][col];
}
a[col] = total as u64;
carry = total >> 64;
}
}
#[inline(always)]
pub fn expanding_mul(a: &[u64], b: &[u64]) -> Vec<u64> {
assert!(a.len() == b.len());
// The maximum size of an n x n digit multiplication is 2n digits, so
// here's our output array.
let mut result = Vec::with_capacity(a.len() * 2);
result.resize(a.len() * 2, 0);
for (base_idx, digit) in b.iter().enumerate() {
let mut myrow = Vec::with_capacity(a.len() * 2);
let mut carry = 0;
myrow.resize(a.len() * 2, 0);
for (col_idx, digit2) in a.iter().enumerate() {
let left = *digit2 as u128;
let right = *digit as u128;
let combo = (left * right) + carry;
myrow[base_idx + col_idx] = combo as u64;
carry = combo >> 64;
}
generic_add(&mut result, &myrow);
}
result
}
#[inline(always)]
pub fn generic_div(inx: &[u64], iny: &[u64],
outq: &mut [u64], outr: &mut [u64])
{
assert!(inx.len() == inx.len());
assert!(inx.len() == iny.len());
assert!(inx.len() == outq.len());
assert!(inx.len() == outr.len());
// This algorithm is from the Handbook of Applied Cryptography, Chapter 14,
// algorithm 14.20. It has a couple assumptions about the inputs, namely that
// n >= t >= 1 and y[t] != 0, where n and t refer to the number of digits in
// the numbers. Which means that if we used the inputs unmodified, we can't
// divide by single-digit numbers.
//
// To deal with this, we multiply inx and iny by 2^64, so that we push out
// t by one.
//
// In addition, this algorithm starts to go badly when y[t] is very small
// and x[n] is very large. Really, really badly. This can be fixed by
// insuring that the top bit is set in y[t], which we can achieve by
// shifting everyone over a maxiumum of 63 bits.
//
// What this means is, just for safety, we add a 0 at the beginning and
// end of each number.
let mut y = iny.to_vec();
let mut x = inx.to_vec();
y.insert(0,0); y.push(0);
x.insert(0,0); x.push(0);
// 0. Compute 'n' and 't'
let n = x.len() - 1;
let mut t = y.len() - 1;
while (t > 0) && (y[t] == 0) { t -= 1 }
assert!(y[t] != 0); // this is where division by zero will fire
// 0.5. Figure out a shift we can do such that the high bit of y[t] is
// set, and then shift x and y left by that much.
let additional_shift: usize = y[t].leading_zeros() as usize;
let origx = x.clone();
let origy = y.clone();
generic_shl(&mut x, &origx, additional_shift);
generic_shl(&mut y, &origy, additional_shift);
// 1. For j from 0 to (n - 1) do: q_j <- 0
let mut q = Vec::with_capacity(y.len());
q.resize(y.len(), 0);
for qj in q.iter_mut() { *qj = 0 }
// 2. While (x >= yb^(n-t)) do the following:
// q_(n-t) <- q_(n-t) + 1
// x <- x - yb^(n-t)
let mut ybnt = y.clone();
generic_shl(&mut ybnt, &y, 64 * (n - t));
while ge(&x, &ybnt) {
q[n-t] = q[n-t] + 1;
generic_sub(&mut x, &ybnt);
}
// 3. For i from n down to (t + 1) do the following:
let mut i = n;
while i >= (t + 1) {
// 3.1. if x_i = y_t, then set q_(i-t-1) <- b - 1; otherwise set
// q_(i-t-1) <- floor((x_i * b + x_(i-1)) / y_t).
if x[i] == y[t] {
q[i-t-1] = 0xFFFFFFFFFFFFFFFF;
} else {
let top = ((x[i] as u128) << 64) + (x[i-1] as u128);
let bot = y[t] as u128;
let solution = top / bot;
q[i-t-1] = solution as u64;
}
// 3.2. While (q_(i-t-1)(y_t * b + y_(t-1)) > x_i(b2) + x_(i-1)b +
// x_(i-2)) do:
// q_(i - t - 1) <- q_(i - t 1) - 1.
loop {
let mut left = Vec::with_capacity(x.len());
left.resize(x.len(), 0);
left[0] = q[i - t - 1];
let mut leftright = Vec::with_capacity(x.len());
leftright.resize(x.len(), 0);
leftright[0] = y[t-1];
let copy = left.clone();
generic_mul(&mut left, &copy, &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];
}
}

View File

@@ -1,71 +0,0 @@
use cryptonum::signed::Signed;
use cryptonum::traits::*;
use std::ops::*;
pub fn modinv<'a,T>(e: &T, phi: &T) -> T
where
T: Clone + CryptoNumBase + Ord,
T: AddAssign + SubAssign + MulAssign + DivAssign,
T: Add<Output=T> + Sub<Output=T> + Mul<Output=T> + Div<Output=T>,
&'a T: Sub<Output=T>,
T: 'a
{
let (_, mut x, _) = extended_euclidean(e, phi);
let int_phi = Signed::<T>::new(phi.clone());
while x.is_negative() {
x += &int_phi;
}
x.abs()
}
pub fn modexp<T>(b: &T, e: &T, m: &T) -> T
{
panic!("modexp")
}
pub fn extended_euclidean<T>(a: &T, b: &T) -> (Signed<T>, Signed<T>, Signed<T>)
where
T: Clone + CryptoNumBase + Div + Mul + Sub
{
let posinta = Signed::<T>::new(a.clone());
let posintb = Signed::<T>::new(b.clone());
let (mut d, mut x, mut y) = egcd(&posinta, &posintb);
if d.is_negative() {
d.negate();
x.negate();
y.negate();
}
(d, x, y)
}
pub fn egcd<T>(a: &Signed<T>, b: &Signed<T>) -> (Signed<T>,Signed<T>,Signed<T>)
where
T: Clone + CryptoNumBase + Div + Mul + Sub
{
let mut s = Signed::<T>::zero();
let mut old_s = Signed::<T>::from_u8(1);
let mut t = Signed::<T>::from_u8(1);
let mut old_t = Signed::<T>::zero();
let mut r = b.clone();
let mut old_r = a.clone();
while !r.is_zero() {
let quotient = old_r.clone() / r.clone();
let prov_r = r.clone();
let prov_s = s.clone();
let prov_t = t.clone();
r = old_r - (r * &quotient);
s = old_s - (s * &quotient);
t = old_t - (t * &quotient);
old_r = prov_r;
old_s = prov_s;
old_t = prov_t;
}
(old_r, old_s, old_t)
}

187
src/cryptonum/gold_tests.rs Normal file
View File

@@ -0,0 +1,187 @@
use cryptonum::unsigned::BarrettUCN;
use testing::{make_signed,make_unsigned,run_test};
#[test]
fn unsigned_sum_test()
{
run_test("tests/math/unsigned_add.tests", 3, |scase| {
let case = make_unsigned(scase);
let x = case.get("x").unwrap();
let y = case.get("y").unwrap();
let z = case.get("z").unwrap();
let res = x + y;
assert_eq!(res, *z);
});
}
#[test]
fn signed_sum_test()
{
run_test("tests/math/signed_add.tests", 3, |bcase| {
let case = make_signed(bcase);
let x = case.get("x").unwrap();
let y = case.get("y").unwrap();
let z = case.get("z").unwrap();
assert_eq!(x + y, *z);
});
}
#[test]
fn unsigned_sub_test()
{
run_test("tests/math/unsigned_sub.tests", 3, |scase| {
let case = make_unsigned(scase);
let x = case.get("x").unwrap();
let y = case.get("y").unwrap();
let z = case.get("z").unwrap();
assert_eq!(x - y, *z);
});
}
#[test]
fn signed_sub_test()
{
run_test("tests/math/signed_sub.tests", 3, |bcase| {
let case = make_signed(bcase);
let x = case.get("x").unwrap();
let y = case.get("y").unwrap();
let z = case.get("z").unwrap();
assert_eq!(x - y, *z);
});
}
#[test]
fn unsigned_mul_test()
{
run_test("tests/math/unsigned_mul.tests", 3, |scase| {
let case = make_unsigned(scase);
let x = case.get("x").unwrap();
let y = case.get("y").unwrap();
let z = case.get("z").unwrap();
assert_eq!(x * y, *z);
});
}
#[test]
fn signed_mul_test()
{
run_test("tests/math/signed_mul.tests", 3, |bcase| {
let case = make_signed(bcase);
let x = case.get("x").unwrap();
let y = case.get("y").unwrap();
let z = case.get("z").unwrap();
assert_eq!(x * y, *z);
});
}
#[test]
fn unsigned_div_test()
{
run_test("tests/math/unsigned_div.tests", 3, |scase| {
let case = make_unsigned(scase);
let x = case.get("x").unwrap();
let y = case.get("y").unwrap();
let z = case.get("z").unwrap();
assert_eq!(x / y, *z);
});
}
#[test]
fn signed_div_test()
{
run_test("tests/math/signed_div.tests", 3, |bcase| {
let case = make_signed(bcase);
let x = case.get("x").unwrap();
let y = case.get("y").unwrap();
let z = case.get("z").unwrap();
assert_eq!(x / y, *z);
});
}
#[test]
fn unsigned_mod_test()
{
run_test("tests/math/unsigned_mod.tests", 3, |scase| {
let case = make_unsigned(scase);
let x = case.get("x").unwrap();
let y = case.get("y").unwrap();
let z = case.get("z").unwrap();
assert_eq!(x % y, *z);
});
}
#[test]
fn signed_mod_test()
{
run_test("tests/math/signed_mod.tests", 3, |bcase| {
let case = make_signed(bcase);
let x = case.get("x").unwrap();
let y = case.get("y").unwrap();
let z = case.get("z").unwrap();
assert_eq!(x % y, *z);
});
}
#[test]
#[ignore]
fn modular_exponentiation_test()
{
run_test("tests/math/modexp.tests", 4, |scase| {
let case = make_unsigned(scase);
let a = case.get("a").unwrap();
let b = case.get("b").unwrap();
let m = case.get("m").unwrap();
let z = case.get("z").unwrap();
assert_eq!(a.modexp(&b, &m), *z);
});
}
#[test]
fn fast_modular_exponentiation_test()
{
run_test("tests/math/fastmodexp.tests", 6, |scase| {
let case = make_unsigned(scase);
let a = case.get("a").unwrap();
let b = case.get("b").unwrap();
let kbig = case.get("k").unwrap();
let k = usize::from(kbig);
let m = case.get("m").unwrap();
let u = case.get("u").unwrap();
let z = case.get("z").unwrap();
let mu = BarrettUCN{ k: k, u: u.clone(), m: m.clone() };
assert_eq!(a.fastmodexp(&b, &mu), *z);
});
}
#[test]
fn barrett_reduction_test()
{
run_test("tests/math/barrett.tests", 5, |scase| {
let case = make_unsigned(scase);
let kbig = case.get("k").unwrap();
let m = case.get("m").unwrap();
let r = case.get("r").unwrap();
let u = case.get("u").unwrap();
let v = case.get("v").unwrap();
let k = usize::from(kbig);
let barrett = m.barrett_u();
let result = v.reduce(&barrett);
assert_eq!(barrett.k, k);
assert_eq!(&barrett.u, u);
assert_eq!(&barrett.m, m);
assert_eq!(&result, r);
});
}
#[test]
fn modular_inverse_test()
{
run_test("tests/math/modinv.tests", 3, |scase| {
let case = make_unsigned(scase);
let a = case.get("x").unwrap();
let m = case.get("y").unwrap();
let r = case.get("z").unwrap();
let result = a.modinv(m);
assert_eq!(r, &result);
});
}

View File

@@ -1,18 +1,13 @@
//! # 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.
mod core;
#[macro_use] #[macro_use]
mod builder; mod conversions;
//mod extended_math; #[macro_use]
// mod primes; mod complete_arith;
mod primes;
mod signed; mod signed;
mod traits;
mod unsigned; mod unsigned;
#[cfg(test)]
mod gold_tests;
// pub use self::extended_math::{modexp,modinv,extended_euclidean,egcd}; pub use self::signed::SCN;
// pub use self::primes::{probably_prime}; pub use self::unsigned::{BarrettUCN,UCN};
pub use self::signed::{Signed}; pub use self::primes::*;
pub use self::unsigned::{U512,U1024,U2048,U3072,U4096,U7680,U8192,U15360};

View File

@@ -1,9 +1,7 @@
use cryptonum::extended_math::modexp; use cryptonum::unsigned::UCN;
use cryptonum::traits::*;
use rand::Rng; use rand::Rng;
use std::ops::*;
static SMALL_PRIMES: [u64; 310] = [ static SMALL_PRIMES: [u32; 310] = [
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
@@ -37,29 +35,47 @@ static SMALL_PRIMES: [u64; 310] = [
1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053]; 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053];
pub fn probably_prime<G,T>(x: &T, g: &mut G, iters: usize) -> bool impl UCN {
pub fn generate_prime<F,G>(rng: &mut G,
bitlen: usize,
iterations: usize,
check_value: F)
-> UCN
where where
G: Rng, G: Rng,
T: Clone + PartialOrd + Rem + Sub, F: Fn(UCN) -> Option<UCN>
T: CryptoNumBase + CryptoNumSerialization, {
{ let one = UCN::from(1 as u8);
assert!((bitlen % 64) == 0);
loop {
let base = random_number(rng, bitlen);
let candidate = base | &one;
if let Some(proposed) = check_value(candidate) {
if proposed.probably_prime(rng, bitlen, iterations) {
return proposed;
}
}
}
}
fn probably_prime<G: Rng>(&self, g: &mut G, size: usize, iters: usize)
-> bool
{
for tester in SMALL_PRIMES.iter() { for tester in SMALL_PRIMES.iter() {
if (x % T::from_u64(*tester)) == T::zero() { if (self % UCN::from(*tester)).is_zero() {
return false; return false;
} }
} }
miller_rabin(g, x, iters) miller_rabin(g, &self, size, iters)
}
} }
fn miller_rabin<G,T>(g: &mut G, n: T, iters: usize) -> bool fn miller_rabin<G: Rng>(g: &mut G, n: &UCN, size: usize, iters: usize) -> bool {
where let one = UCN::from(1 as u8);
G: Rng, let two = UCN::from(2 as u8);
T: Clone + PartialEq + PartialOrd + Sub, let nm1 = n - &one;
T: CryptoNumBase + CryptoNumSerialization,
{
let one = T::from_u8(1);
let two = T::from_u8(2);
let nm1 = n - one;
// Quoth Wikipedia: // Quoth Wikipedia:
// write n - 1 as 2^r*d with d odd by factoring powers of 2 from n - 1 // 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 d = nm1.clone();
@@ -67,14 +83,14 @@ fn miller_rabin<G,T>(g: &mut G, n: T, iters: usize) -> bool
while d.is_even() { while d.is_even() {
d >>= 1; d >>= 1;
r += 1; r += 1;
assert!(r < n.bit_size()); assert!(r < n.bits());
} }
// WitnessLoop: repeat k times // WitnessLoop: repeat k times
'WitnessLoop: for _k in 0..iters { 'WitnessLoop: for _k in 0..iters {
// pick a random integer a in the range [2, n - 2] // pick a random integer a in the range [2, n - 2]
let a = random_in_range(g, &two, &nm1); let a = random_in_range(g, size, &two, &nm1);
// x <- a^d mod n // x <- a^d mod n
let mut x = modexp(&a, &d, &n); let mut x = a.modexp(&d, &n);
// if x = 1 or x = n - 1 then // if x = 1 or x = n - 1 then
if (&x == &one) || (&x == &nm1) { if (&x == &one) || (&x == &nm1) {
// continue WitnessLoop // continue WitnessLoop
@@ -83,7 +99,7 @@ fn miller_rabin<G,T>(g: &mut G, n: T, iters: usize) -> bool
// repeat r - 1 times: // repeat r - 1 times:
for _i in 0..r { for _i in 0..r {
// x <- x^2 mod n // x <- x^2 mod n
x = modexp(&x, &two, &n); x = x.modexp(&two, &n);
// if x = 1 then // if x = 1 then
if &x == &one { if &x == &one {
// return composite // return composite
@@ -102,14 +118,11 @@ fn miller_rabin<G,T>(g: &mut G, n: T, iters: usize) -> bool
true true
} }
fn random_in_range<G,T>(rng: &mut G, min: &T, max: &T) -> T fn random_in_range<G: Rng>(rng: &mut G, bitlen: usize, min: &UCN, max: &UCN)
where -> UCN
G: Rng,
T: CryptoNumSerialization + PartialOrd
{ {
assert_eq!(min.byte_size(), max.byte_size());
loop { loop {
let candidate = random_number(rng, min.byte_size()); let candidate = random_number(rng, bitlen);
if (&candidate >= min) && (&candidate < max) { if (&candidate >= min) && (&candidate < max) {
return candidate; return candidate;
@@ -117,13 +130,10 @@ fn random_in_range<G,T>(rng: &mut G, min: &T, max: &T) -> T
} }
} }
fn random_number<G,T>(rng: &mut G, bytelen: usize) -> T fn random_number<G: Rng>(rng: &mut G, bitlen: usize) -> UCN {
where assert!(bitlen % 64 == 0);
G: Rng, let wordlen = bitlen / 64;
T: CryptoNumSerialization let components = rng.gen_iter().take(wordlen).collect();
{ UCN{ contents: components }
let components: Vec<u8> = rng.gen_iter().take(bytelen).collect();
T::from_bytes(&components)
} }

View File

@@ -1,431 +1,406 @@
use cryptonum::traits::*; use cryptonum::unsigned::{BarrettUCN,UCN,divmod};
use num::BigInt;
use num::bigint::Sign;
use std::fmt;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt::{Debug,Error,Formatter}; use std::fmt::Write;
use std::ops::*; use std::ops::*;
pub struct Signed<T: Sized> { /// In case you were wondering, it stands for "Signed Crypto Num".
positive: bool, #[derive(Clone,Debug,PartialEq,Eq)]
value: T pub struct SCN {
pub(crate) negative: bool,
pub(crate) value: UCN
} }
impl<T> Signed<T> { impl SCN {
pub fn new(v: T) -> Signed<T> { pub fn zero() -> SCN {
Signed{ positive: true, value: v } SCN{ negative: false, value: UCN::zero() }
} }
pub fn abs(&self) -> T pub fn is_zero(&self) -> bool {
where T: Clone
{
self.value.clone()
}
pub fn is_positive(&self) -> bool
where T: CryptoNumBase
{
self.positive && !self.value.is_zero()
}
pub fn is_negative(&self) -> bool
where T: CryptoNumBase
{
!self.positive && !self.value.is_zero()
}
pub fn negate(&mut self)
{
self.positive = !self.positive;
}
}
impl<T: CryptoNumBase> CryptoNumBase for Signed<T> {
fn zero() -> Signed<T> {
Signed{ positive: true, value: T::zero() }
}
fn max_value() -> Signed<T> {
Signed{ positive: true, value: T::max_value() }
}
fn is_zero(&self) -> bool {
self.value.is_zero() self.value.is_zero()
} }
fn is_odd(&self) -> bool {
self.value.is_odd()
}
fn is_even(&self) -> bool {
self.value.is_even()
}
fn from_u8(x: u8) -> Signed<T> {
Signed{ positive: true, value: T::from_u8(x) }
}
fn to_u8(&self) -> u8 {
self.value.to_u8()
}
fn from_u16(x: u16) -> Signed<T> {
Signed{ positive: true, value: T::from_u16(x) }
}
fn to_u16(&self) -> u16 {
self.value.to_u16()
}
fn from_u32(x: u32) -> Signed<T> {
Signed{ positive: true, value: T::from_u32(x) }
}
fn to_u32(&self) -> u32 {
self.value.to_u32()
}
fn from_u64(x: u64) -> Signed<T> {
Signed{ positive: true, value: T::from_u64(x) }
}
fn to_u64(&self) -> u64 {
self.value.to_u64()
}
}
impl<T: CryptoNumFastMod> CryptoNumFastMod for Signed<T> { pub fn is_negative(&self) -> bool {
type BarrettMu = T::BarrettMu; self.negative
}
fn barrett_mu(&self) -> Option<T::BarrettMu> { pub fn from_str(x: &str) -> SCN {
if self.positive { if x.get(0..1) == Some("-") {
self.value.barrett_mu() SCN{ negative: true, value: UCN::from_str(&x[1..]) }
} else { } else {
None SCN{ negative: false, value: UCN::from_str(x) }
} }
} }
fn fastmod(&self, mu: &T::BarrettMu) -> Signed<T> { fn cleanup(&mut self) {
Signed{ positive: self.positive, value: self.value.fastmod(&mu) } if self.value.is_zero() {
self.negative = false;
}
}
pub fn egcd(self, b: SCN) -> (SCN, SCN, SCN) {
let mut s = SCN::zero();
let mut old_s = SCN::from(1 as u8);
let mut t = SCN::from(1 as u8);
let mut old_t = SCN::zero();
let mut r = b;
let mut old_r = self;
while !r.is_zero() {
let quotient = old_r.clone() / r.clone();
let prov_r = r.clone();
let prov_s = s.clone();
let prov_t = t.clone();
r = old_r - (r * &quotient);
s = old_s - (s * &quotient);
t = old_t - (t * &quotient);
old_r = prov_r;
old_s = prov_s;
old_t = prov_t;
}
(old_r, old_s, old_t)
}
pub fn reduce(&self, m: &BarrettUCN) -> SCN {
println!("signed reduce");
SCN{ negative: false, value: self.value.reduce(m) }
}
pub fn divmod(&self, x: &SCN, m: &BarrettUCN) -> SCN {
println!("STEP1");
let xmod = x.reduce(m);
println!("STEP2");
assert!(!xmod.negative);
println!("STEP3");
let i = xmod.value.modinv(&m.m);
println!("STEP4");
let si = SCN::from(i);
println!("STEP5");
let yi = self * si;
println!("STEP6: {:X}", yi);
println!(" mod {:X}", m.m);
let res = yi.reduce(m);
println!("STEP7");
res
} }
} }
impl<T: Clone> Clone for Signed<T> { impl fmt::UpperHex for SCN {
fn clone(&self) -> Signed<T> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(),fmt::Error> {
Signed{ positive: self.positive, value: self.value.clone() } if self.negative {
fmt.write_char('-')?;
}
self.value.fmt(fmt)
} }
} }
impl<'a,T: PartialEq> PartialEq<&'a Signed<T>> for Signed<T> { //------------------------------------------------------------------------------
fn eq(&self, other: &&Signed<T>) -> bool { //
(self.positive == other.positive) && (self.value == other.value) // Conversions to/from crypto nums.
//
//------------------------------------------------------------------------------
define_signed_from!(SCN, i8, u8);
define_signed_from!(SCN, i16, u16);
define_signed_from!(SCN, i32, u32);
define_signed_from!(SCN, i64, u64);
define_signed_into!(SCN, i8, u8);
define_signed_into!(SCN, i16, u16);
define_signed_into!(SCN, i32, u32);
define_signed_into!(SCN, i64, u64);
impl From<UCN> for SCN {
fn from(x: UCN) -> SCN {
SCN{ negative: false, value: x }
} }
} }
impl<'a,T: PartialEq> PartialEq<Signed<T>> for &'a Signed<T> { impl Into<UCN> for SCN {
fn eq(&self, other: &Signed<T>) -> bool { fn into(self) -> UCN {
(self.positive == other.positive) && (self.value == other.value) self.value
} }
} }
impl<T: PartialEq> PartialEq for Signed<T> { impl From<SCN> for BigInt {
fn eq(&self, other: &Signed<T>) -> bool { fn from(x: SCN) -> BigInt {
(self.positive == other.positive) && (self.value == other.value) let sign = if x.is_negative() { Sign::Minus } else { Sign::Plus };
let numbytes = x.value.contents.len() * 8;
let bytes = x.value.to_bytes(numbytes);
BigInt::from_bytes_be(sign, &bytes)
} }
} }
impl<T: Eq> Eq for Signed<T> {} //------------------------------------------------------------------------------
//
// Comparisons
//
//------------------------------------------------------------------------------
impl<T: Debug> Debug for Signed<T> { impl PartialOrd for SCN {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { fn partial_cmp(&self, other: &SCN) -> Option<Ordering> {
if self.positive {
f.write_str("+")?;
} else {
f.write_str("-")?;
}
self.value.fmt(f)
}
}
impl<T: Ord> Ord for Signed<T> {
fn cmp(&self, other: &Signed<T>) -> Ordering {
match (self.positive, other.positive) {
(true, true) => self.value.cmp(&other.value),
(true, false) => Ordering::Greater,
(false, true) => Ordering::Less,
(false, false) =>
self.value.cmp(&other.value).reverse()
}
}
}
impl<T: Ord> PartialOrd for Signed<T> {
fn partial_cmp(&self, other: &Signed<T>) -> Option<Ordering>{
Some(self.cmp(other)) Some(self.cmp(other))
} }
} }
//------------------------------------------------------------------------------ impl Ord for SCN {
fn cmp(&self, other: &SCN) -> Ordering {
impl<T: Clone> Neg for Signed<T> { match (self.negative, other.negative) {
type Output = Signed<T>; (false, false) => self.value.cmp(&other.value),
(false, true) => Ordering::Greater,
fn neg(self) -> Signed<T> { (true, false) => Ordering::Less,
Signed { (true, true) => self.value.cmp(&other.value).reverse()
positive: !self.positive,
value: self.value.clone()
}
}
}
impl<'a,T: Clone> Neg for &'a Signed<T> {
type Output = Signed<T>;
fn neg(self) -> Signed<T> {
Signed {
positive: !self.positive,
value: self.value.clone()
} }
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
//
impl<T> AddAssign for Signed<T> // Shifts
where //
T: Clone + Ord,
T: AddAssign + SubAssign,
{
fn add_assign(&mut self, other: Signed<T>) {
match (self.positive, other.positive, self.value.cmp(&other.value)) {
// if the signs are the same, we maintain the sign and just increase
// the magnitude
(x, y, _) if x == y =>
self.value.add_assign(other.value),
// 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.
(_, _, Ordering::Equal) => {
self.positive = true;
self.value.sub_assign(other.value);
}
// if the signs are different and the first one is less than the
// second, then we flip the sign and subtract.
(_, _, Ordering::Less) => {
self.positive = !self.positive;
let temp = self.value.clone();
self.value = other.value.clone();
self.value.sub_assign(temp);
}
// if the signs are different and the first one is greater than the
// second, then we leave the sign and subtract.
(_, _, Ordering::Greater) => {
self.value.sub_assign(other.value);
}
}
}
}
impl<'a,T> AddAssign<&'a Signed<T>> for Signed<T>
where
T: Clone + Ord,
T: AddAssign + SubAssign,
T: AddAssign<&'a T> + SubAssign<&'a T>
{
fn add_assign(&mut self, other: &'a Signed<T>) {
match (self.positive, other.positive, self.value.cmp(&other.value)) {
// if the signs are the same, we maintain the sign and just increase
// the magnitude
(x, y, _) if x == y =>
self.value.add_assign(&other.value),
// 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.
(_, _, Ordering::Equal) => {
self.positive = true;
self.value.sub_assign(&other.value);
}
// if the signs are different and the first one is less than the
// second, then we flip the sign and subtract.
(_, _, Ordering::Less) => {
self.positive = !self.positive;
let temp = self.value.clone();
self.value = other.value.clone();
self.value.sub_assign(temp);
}
// if the signs are different and the first one is greater than the
// second, then we leave the sign and subtract.
(_, _, Ordering::Greater) => {
self.value.sub_assign(&other.value);
}
}
}
}
math_operator!(Add,add,add_assign);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
impl<T> SubAssign for Signed<T> impl ShlAssign<u64> for SCN {
where fn shl_assign(&mut self, rhs: u64) {
T: Clone + Ord, self.value <<= rhs;
T: AddAssign + SubAssign,
{
fn sub_assign(&mut self, other: Signed<T>) {
let mut other2 = other.clone();
other2.positive = !other.positive;
self.add_assign(other2);
} }
} }
impl<'a,T> SubAssign<&'a Signed<T>> for Signed<T> impl Shl<u64> for SCN {
where type Output = SCN;
T: Clone + Ord,
T: AddAssign + SubAssign, fn shl(self, rhs: u64) -> SCN {
T: AddAssign<&'a T> + SubAssign<&'a T> let mut copy = self.clone();
{ copy.shl_assign(rhs);
fn sub_assign(&mut self, other: &'a Signed<T>) { copy
let mut other2 = other.clone();
other2.positive = !other.positive;
self.add_assign(other2);
} }
} }
math_operator!(Sub,sub,sub_assign); derive_shift_operators!(SCN, ShlAssign, Shl, shl_assign, shl, usize);
derive_shift_operators!(SCN, ShlAssign, Shl, shl_assign, shl, u32);
derive_shift_operators!(SCN, ShlAssign, Shl, shl_assign, shl, u16);
derive_shift_operators!(SCN, ShlAssign, Shl, shl_assign, shl, u8);
impl ShrAssign<u64> for SCN {
fn shr_assign(&mut self, rhs: u64) {
self.value >>= rhs;
}
}
impl Shr<u64> for SCN {
type Output = SCN;
fn shr(self, rhs: u64) -> SCN {
let mut copy = self.clone();
copy.shr_assign(rhs);
copy
}
}
derive_shift_operators!(SCN, ShrAssign, Shr, shr_assign, shr, usize);
derive_shift_operators!(SCN, ShrAssign, Shr, shr_assign, shr, u32);
derive_shift_operators!(SCN, ShrAssign, Shr, shr_assign, shr, u16);
derive_shift_operators!(SCN, ShrAssign, Shr, shr_assign, shr, u8);
derive_signed_shift_operators!(SCN, usize, isize);
derive_signed_shift_operators!(SCN, u64, i64);
derive_signed_shift_operators!(SCN, u32, i32);
derive_signed_shift_operators!(SCN, u16, i16);
derive_signed_shift_operators!(SCN, u8, i8);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
//
impl<T> MulAssign for Signed<T> // Arithmetic
where //
T: MulAssign
{
fn mul_assign(&mut self, other: Signed<T>) {
self.positive = !(self.positive ^ other.positive);
self.value *= other.value;
}
}
impl<'a,T> MulAssign<&'a Signed<T>> for Signed<T>
where
T: MulAssign + MulAssign<&'a T>
{
fn mul_assign(&mut self, other: &'a Signed<T>) {
self.positive = !(self.positive ^ other.positive);
self.value *= &other.value;
}
}
math_operator!(Mul,mul,mul_assign);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
impl<T> DivAssign for Signed<T> impl Neg for SCN {
where type Output = SCN;
T: DivAssign
{ fn neg(self) -> SCN {
fn div_assign(&mut self, other: Signed<T>) { if self.is_zero() {
self.positive = !(self.positive ^ other.positive); self
self.value /= other.value; } else {
SCN{ negative: !self.negative, value: self.value }
}
} }
} }
impl<'a,T> DivAssign<&'a Signed<T>> for Signed<T> impl<'a> Neg for &'a SCN {
where type Output = SCN;
T: DivAssign + DivAssign<&'a T>
{ fn neg(self) -> SCN {
fn div_assign(&mut self, other: &'a Signed<T>) { if self.is_zero() {
self.positive = !(self.positive ^ other.positive); self.clone()
self.value /= &other.value; } else {
SCN{ negative: !self.negative, value: self.value.clone() }
}
} }
} }
math_operator!(Div,div,div_assign); impl<'a> AddAssign<&'a SCN> for SCN {
fn add_assign(&mut self, rhs: &SCN) {
if self.negative == rhs.negative {
self.value.add_assign(&rhs.value);
} else {
if self.value >= rhs.value {
self.value.sub_assign(&rhs.value);
} else {
self.negative = !self.negative;
self.value = &rhs.value - &self.value;
}
}
self.cleanup();
}
}
impl<'a> SubAssign<&'a SCN> for SCN {
fn sub_assign(&mut self, rhs: &SCN) {
let flipped = SCN{ negative: !rhs.negative, value: rhs.value.clone() };
self.add_assign(&flipped);
self.cleanup();
}
}
impl<'a> MulAssign<&'a SCN> for SCN {
fn mul_assign(&mut self, rhs: &SCN) {
self.negative ^= rhs.negative;
self.value.mul_assign(&rhs.value);
self.cleanup();
}
}
impl<'a> DivAssign<&'a SCN> for SCN {
fn div_assign(&mut self, rhs: &SCN) {
self.negative ^= rhs.negative;
// rounding makes me grumpy
let mut remainder = Vec::new();
let copy = self.value.contents.clone();
divmod(&mut self.value.contents, &mut remainder,
&copy, &rhs.value.contents);
if self.negative && !remainder.is_empty() {
let one = UCN{ contents: vec![1] };
self.sub_assign(SCN{ negative: false, value: one});
}
self.cleanup();
}
}
impl<'a> RemAssign<&'a SCN> for SCN {
fn rem_assign(&mut self, rhs: &SCN) {
let base = &self.value % &rhs.value;
if self.negative == rhs.negative {
self.value = base;
} else {
self.negative = rhs.negative;
self.value = &rhs.value - &base;
}
self.cleanup();
}
}
derive_arithmetic_operators!(SCN, Add, add, AddAssign, add_assign);
derive_arithmetic_operators!(SCN, Sub, sub, SubAssign, sub_assign);
derive_arithmetic_operators!(SCN, Mul, mul, MulAssign, mul_assign);
derive_arithmetic_operators!(SCN, Div, div, DivAssign, div_assign);
derive_arithmetic_operators!(SCN, Rem, rem, RemAssign, rem_assign);
//------------------------------------------------------------------------------
//
// Tests!
//
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#[cfg(test)] #[cfg(test)]
mod tests { mod test {
use cryptonum::unsigned::U512;
use quickcheck::{Arbitrary,Gen}; use quickcheck::{Arbitrary,Gen};
use std::cmp::{max,min};
use super::*; use super::*;
impl<T: Arbitrary + CryptoNumBase> Arbitrary for Signed<T> { impl Arbitrary for SCN {
fn arbitrary<G: Gen>(g: &mut G) -> Signed<T> { fn arbitrary<G: Gen>(g: &mut G) -> SCN {
let value = T::arbitrary(g); let neg = (g.next_u32() & 1) == 1;
if value.is_zero() { SCN{ negative: neg, value: UCN::arbitrary(g) }
Signed {
positive: true,
value: value
}
} else {
Signed {
positive: g.gen_weighted_bool(2),
value: value
}
} }
} }
fn one() -> SCN {
SCN{ negative: false, value: UCN::from(1 as u8) }
} }
quickcheck! { quickcheck! {
fn double_negation(x: Signed<U512>) -> bool { fn additive_identity(x: SCN) -> bool {
&x == (- (- &x)) (&x + &SCN::zero()) == x
} }
fn subtractive_identity(x: SCN) -> bool {
(&x - &SCN::zero()) == x
}
fn multiplicative_identity(x: SCN) -> bool {
(&x * &one()) == x
}
fn division_identity(x: SCN) -> bool {
let result = &x / &one();
result == x
} }
quickcheck! { fn additive_destructor(x: SCN) -> bool {
fn add_associates(x: Signed<U512>, y: Signed<U512>, z: Signed<U512>) (&x + (- &x)) == SCN::zero()
-> bool
{
let mut a = x.clone();
let mut b = y.clone();
let mut c = z.clone();
// we shift these right because rollover makes for weird behavior
a.value >>= 2;
b.value >>= 2;
c.value >>= 2;
(&a + (&b + &c)) == ((&a + &b) + &c)
} }
fn add_commutes(x: Signed<U512>, y: Signed<U512>) -> bool { fn subtractive_destructor(x: SCN) -> bool {
(&x + &y) == (&y + &x) (&x - &x) == SCN::zero()
} }
fn add_identity(x: Signed<U512>) -> bool { fn multiplicative_destructor(x: SCN) -> bool {
let zero = Signed{ positive: true, value: U512::zero() }; (x * SCN::zero()) == SCN::zero()
(&x + &zero) == &x
} }
fn division_deastructor(x: SCN) -> bool {
(&x / &x) == one()
}
fn remainder_destructor(x: SCN) -> bool {
(&x % &x) == SCN::zero()
} }
quickcheck! { fn addition_commutes(a: SCN, b: SCN) -> bool {
fn sub_is_add_negation(x: Signed<U512>, y: Signed<U512>) -> bool { (&a + &b) == (&b + &a)
(&x - &y) == (&x + (- &y))
} }
fn multiplication_commutes(a: SCN, b: SCN) -> bool {
(&a * &b) == (&b * &a)
} }
fn addition_associates(a: SCN, b: SCN, c: SCN) -> bool {
quickcheck! { ((&a + &b) + &c) == (&a + (&b + &c))
fn mul_associates(x: Signed<U512>, y: Signed<U512>, z: Signed<U512>)
-> bool
{
let mut a = x.clone();
let mut b = y.clone();
let mut c = z.clone();
// we shift these right because rollover makes for weird behavior
a.value >>= 258;
b.value >>= 258;
c.value >>= 258;
(&a * (&b * &c)) == ((&a * &b) * &c)
} }
fn mul_commutes(x: Signed<U512>, y: Signed<U512>) -> bool { fn multiplication_associates(a: SCN, b: SCN, c: SCN) -> bool {
(&x * &y) == (&y * &x) ((&a * &b) * &c) == (&a * (&b * &c))
} }
fn mul_identity(x: Signed<U512>) -> bool { fn distribution_works(a: SCN, b: SCN, c: SCN) -> bool {
let one = Signed{ positive: true, value: U512::from_u8(1) };
(&x * &one) == &x
}
}
quickcheck! {
fn add_mul_distribution(x:Signed<U512>,y:Signed<U512>,z:Signed<U512>)
-> bool
{
let mut a = x.clone();
let mut b = y.clone();
let mut c = z.clone();
// we shift these right because rollover makes for weird behavior
a.value >>= 258;
b.value >>= 258;
c.value >>= 258;
(&a * (&b + &c)) == ((&a * &b) + (&a * &c)) (&a * (&b + &c)) == ((&a * &b) + (&a * &c))
} }
fn negation_works(a: SCN) -> bool {
(- &a) == (&a * &SCN{ negative: true, value: UCN::from(1 as u8) })
}
fn double_negation_works(a: SCN) -> bool {
(- (- &a)) == a
}
fn negation_commutes(a: SCN, b: SCN) -> bool {
((- &a) * &b) == (&a * (- &b))
}
fn negation_cancels(a: SCN, b: SCN) -> bool {
((- &a) * (- &b)) == (&a * &b)
}
fn negation_distributes(a: SCN, b: SCN) -> bool {
(- (&a + &b)) == ((- &a) + (- &b))
}
}
quickcheck! {
fn egcd_works(a: SCN, b: SCN) -> bool {
let (d, x, y) = a.clone().egcd(b.clone());
((a * x) + (b * y)) == d
}
} }
} }

View File

@@ -1,58 +0,0 @@
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;
/// Translate a `u8` to this type. This must be safe.
fn from_u8(x: u8) -> Self;
/// Convert this back into a `u8`. This is the equivalent of masking off
/// the lowest 8 bits and then casting to a `u8`.
fn to_u8(&self) -> u8;
/// Translate a `u16` to this type. This must be safe.
fn from_u16(x: u16) -> Self;
/// Convert this back into a `u16`. This is the equivalent of masking off
/// the lowest 16 bits and then casting to a `u16`.
fn to_u16(&self) -> u16;
/// Translate a `u32` to this type. This must be safe.
fn from_u32(x: u32) -> Self;
/// Convert this back into a `u32`. This is the equivalent of masking off
/// the lowest 32 bits and then casting to a `u32`.
fn to_u32(&self) -> u32;
/// Translate a `u64` to this type. This must be safe.
fn from_u64(x: u64) -> Self;
/// Convert this back into a `u64`. This is the equivalent of masking off
/// the lowest 64 bits and then casting to a `u64`.
fn to_u64(&self) -> u64;
}
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;
}

File diff suppressed because it is too large Load Diff

28
src/dsa/errors.rs Normal file
View File

@@ -0,0 +1,28 @@
use simple_asn1::ASN1DecodeErr;
use std::io;
#[derive(Debug)]
pub enum DSAError {
ASN1DecodeErr(ASN1DecodeErr),
InvalidParamSize
}
impl From<ASN1DecodeErr> for DSAError {
fn from(e: ASN1DecodeErr) -> DSAError {
DSAError::ASN1DecodeErr(e)
}
}
#[derive(Debug)]
pub enum DSAGenError {
RngFailure(io::Error),
InvalidSeedLength, InvalidPrimeLength, TooManyGenAttempts
}
impl From<io::Error> for DSAGenError {
fn from(e: io::Error) -> DSAGenError {
DSAGenError::RngFailure(e)
}
}

585
src/dsa/generation.rs Normal file
View File

@@ -0,0 +1,585 @@
use cryptonum::{BarrettUCN,UCN};
use digest::{FixedOutput,Input};
use dsa::errors::DSAGenError;
use dsa::parameters::{DSAParameterSize,n_bits,l_bits};
use rand::Rng;
use sha2::Sha256;
use std::ops::{Add,Div,Rem,Sub};
#[derive(Debug,PartialEq)]
pub struct DSAGenEvidence {
pub first_seed: UCN,
pub p_seed: UCN,
pub q_seed: UCN,
pub pgen_counter: usize,
pub qgen_counter: usize
}
fn get_domain_parameter_seed(ev: &DSAGenEvidence) -> Vec<u8> {
let mut output = Vec::new();
let fssize = (ev.first_seed.bits() + 7) / 8;
output.append(&mut ev.first_seed.to_bytes(fssize));
let psize = (ev.p_seed.bits() + 7) / 8;
output.append(&mut ev.p_seed.to_bytes(psize));
let qsize = (ev.q_seed.bits() + 7) / 8;
output.append(&mut ev.q_seed.to_bytes(qsize));
output
}
pub fn generate_provable_primes<G: Rng>(rng: &mut G,
firstseed: &UCN,
ps: DSAParameterSize)
-> Result<(UCN, UCN, DSAGenEvidence),DSAGenError>
{
let one: UCN = UCN::from(1u64);
let two: UCN = UCN::from(2u64);
let three: UCN = UCN::from(3u64);
// See Page 38 of FIPS 186-4!
let n = n_bits(ps);
let l = l_bits(ps);
// 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, return FAILURE.
//
// Done because sum types are cool.
//
// 2. Using N as the length and firstseed as the input_seed, use the random
// prime generation routine in Appendix C.6 to obtain q, qseed and
// qgen_counter. If FAILURE is returned, then return FAILURE.
let (q, qseed, qgen_counter) = shawe_taylor(rng, n, &firstseed)?;
// 3. Using ceiling(L / 2 + 1) as the length and qseed as the input_seed,
// use the random prime generation routine in Appendix C.6 to obtain
// p0, pseed, and pgen_counter. If FAILURE is returned, then return
// FAILURE.
//
// NOTE: The ceiling isn't required. All of the values of L divide
// evenly by 2, so it's just l / 2 + 1. I'm not sure why the
// spec mentions it, frankly.
let (p0, mut pseed, mut pgen_counter) = shawe_taylor(rng, l/2 + 1, &qseed)?;
// 4. iterations = ceiling(L / outlen) - 1.
let iterations = ceildiv(&l, &256) - 1;
// 5. old_counter = pgen_counter.
let old_counter = pgen_counter;
// 6. x = 0.
let mut x_bytes = Vec::new();
// 7. For i = 0 to iterations fo
// x + x + (Hash(pseed + i) * 2^(i * outlen).
// NOTE: WE run this backwards, much like we do in shawe_taylor()
let mut i: i64 = iterations as i64;
while i >= 0 {
let bigi = UCN::from(i as u64);
let prime_i = &pseed + &bigi;
let mut hash_i = hash(&prime_i, l);
x_bytes.append(&mut hash_i);
i -= 1;
}
let x = UCN::from_bytes(&x_bytes);
// 8. pseed = pseed + iterations + 1.
pseed = &pseed + UCN::from(iterations) + &one;
// 9. x = 2^(L-1) + (x mod 2^(L-1));
let twol1: UCN = &one << (l - 1);
// 10. t = ceiling(x / (2 * q * p_0))
let twoqp0 = &two * &q * &p0;
let mut t = ceildiv(&x, &twoqp0);
loop {
// 11. If (2tqp_0 + 1) > 2^L, then t = ceiling(2^(L-1)/2qp0).
let twotqp0p1 = (&t * &twoqp0) + &one;
let twol = &one << l;
if &twotqp0p1 > &twol {
t = ceildiv(&twol1, &twoqp0);
}
// 12. p = 2tqp_0 + 1
let p = twotqp0p1;
// 13. pgen_counter = pgen_counter + 1
pgen_counter = &pgen_counter + 1;
// 14. a = 0
let mut a_bytes = Vec::new();
// 15. For i = 0 to iterations do
// a = a + (Hash(pseed + i) * 2^(i*outlen).
i = iterations as i64;
while i >= 0 {
let bigi = UCN::from(i as u64);
let prime_i = &pseed + &bigi;
let mut hash_i = hash(&prime_i, l);
a_bytes.append(&mut hash_i);
i -= 1;
}
let mut a = UCN::from_bytes(&a_bytes);
// 16. pseed = pseed + iterations + 1.
pseed = &pseed + UCN::from(iterations) + &one;
// 17. a = 2 + (a mod (p - 3))
let pm3 = &p - &three;
let amodpm3 = &a % &pm3;
a = &two + &amodpm3;
// 18. z = a^(2tq) mod p.
let twotq = &two * &t * &q;
let z = a.modexp(&twotq, &p);
// 19. If ((1 = GCD(z-1,p)) and (1 = z^p0 mod p)), then return SUCCESS
// and the values of p, q, and (optionally) pseed, qseed, pgen_counter,
// and qgen_counter.
let zm1 = &z - &one;
if (&one == &zm1.gcd(&p)) && (&one == &z.modexp(&p0, &p)) {
let evidence = DSAGenEvidence {
first_seed: firstseed.clone(),
p_seed: pseed,
q_seed: qseed,
pgen_counter: pgen_counter,
qgen_counter: qgen_counter
};
return Ok((p, q, evidence));
}
// 20. If (pgen_counter > (4L + old_counter)), then return FAILURE.
if pgen_counter > ((4 * l) + old_counter) {
return Err(DSAGenError::TooManyGenAttempts);
}
// 21. t = t + 1
t = &t + &one;
// 22. Go to step 11.
}
}
pub fn validate_provable_primes<G: Rng>(rng: &mut G,
p: &UCN, q: &UCN,
ev: &DSAGenEvidence)
-> bool
{
let one = UCN::from(1u64);
// This is from Page 40 of 186-4, section A.1.2.2.
// 1. L = len(p);
let l = ((p.bits() + 255) / 256) * 256;
// 2. N = len(q);
let n = ((q.bits() + 15) / 16) * 16;
// 3. Check that the (L, N) pair is in the list of acceptable (L, N) pairs.
// If the pair is not in the list, then return failure.
let params = match (l, n) {
(1024, 160) => DSAParameterSize::L1024N160,
(2048, 224) => DSAParameterSize::L2048N224,
(2048, 256) => DSAParameterSize::L2048N256,
(3072, 256) => DSAParameterSize::L3072N256,
_ => return false
};
// 4. If (firstseed < 2^(n-1), then return FAILURE.
let twon1 = &one << (n - 1);
if &ev.first_seed < &twon1 {
return false;
}
// 5. If (2^n <= q), then return FAILURE.
let twon = &one << n;
if &twon <= q {
return false;
}
// 6. If (2^l <= p), then return FAILURE.
let twol = &one << l;
if &twol <= p {
return false;
}
// 7. If ((p - 1) mod q /= 0), then return FAILURE.
let pm1 = p - &one;
if !pm1.rem(q).is_zero() {
return false;
}
// 8. Using L, N and firstseed, perform the constructive prime generation
// procedure in Appendix A.1.2.1.2 to obtain p_val, q_val, pseed_val,
// qseed_val, pgen_counter_val, and qgen_counter_val. If FAILURE is
// returned, or if (q_val ≠ q) or (qseed_val ≠ qseed) or
// (qgen_counter_val ≠ qgen_counter) or (p_val ≠ p) or (pseed_val ≠
// pseed) or (pgen_counter_val ≠ pgen_counter), then return FAILURE.
match generate_provable_primes(rng, &ev.first_seed, params) {
Err(_) => false,
Ok((p_val, q_val, ev2)) => {
// 9. Return SUCCESS
(&q_val == q) && (&p_val == p) && (ev == &ev2)
}
}
}
pub fn generate_verifiable_generator(p: &UCN, pu: &BarrettUCN,
q: &UCN,
ev: &DSAGenEvidence,
index: u8)
-> Result<UCN,DSAGenError>
{
// See FIPS 186-4, Section A.2.3: Verifiable Canonical Generatio of the
// Generator g
let one = UCN::from(1u64);
let two = UCN::from(2u64);
// 1. If (index is incorrect), then return INVALID.
// NOTE: Can't happen, because types.
// 2. N = len(q)
let _n = q.bits();
// 3. e = (p - 1)/q.
let e = (p - &one) / q;
// 4. count = 0.
let mut count: u16 = 0;
loop {
// 5. count = count + 1;
count = count + 1;
// 6. if (count = 0), then return INVALID.
if count == 0 {
return Err(DSAGenError::TooManyGenAttempts);
}
// 7. U = domain_parameter_seed || "ggen" || index || count
// Comment: "ggen" is the bit string 0x6767656E.
let mut u = get_domain_parameter_seed(&ev);
u.push(0x67); u.push(0x67); u.push(0x65); u.push(0x6E);
u.push(index);
u.push((count >> 8) as u8); u.push((count & 0xFF) as u8);
// 8. W = hash(U)
let mut dgst = Sha256::default();
dgst.process(&u);
let w = UCN::from_bytes(dgst.fixed_result().as_slice());
// 9. g = W^e mod p
let g = w.fastmodexp(&e, &pu);
// 10. if (g < 2), then go to step 5.
if &g >= &two {
// 11. Return VALID and the value of g.
return Ok(g);
}
}
}
pub fn verify_generator(p: &UCN, q: &UCN, ev: &DSAGenEvidence,
index: u8, g: &UCN)
-> bool
{
// FIPS 186.4, Section A.2.4!
let one = UCN::from(1u64);
let two = UCN::from(2u64);
// 1. If (index is incorrect), then return INVALID.
// NOTE: Not sure how this can be invalid.
// 2. Verify that 2 <= g <= (p - 1). If not true, return INVALID.
if g < &two {
return false;
}
if g >= p {
return false;
}
// 3. If (g^q /= 1 mod p), then return INVALID.
if g.modexp(q, p) != one {
return false;
}
// 4. N = len(q)
// let n = ((q.bits() + 15) / 15) * 15;
// 5. e = (p - 1) / q
let e = (p - &one) / q;
// 6. count = 0
let mut count: u16 = 0;
loop {
// 7. count = count + 1
count = count + 1;
// 8. if (count == 0), then return INVALID
if count == 0 {
return false;
}
// 9. U = domain_parameter_seed || "ggen" || index || count.
let mut u = get_domain_parameter_seed(&ev);
u.push(0x67); u.push(0x67); u.push(0x65); u.push(0x6E);
u.push(index);
u.push((count >> 8) as u8); u.push((count & 0xFF) as u8);
// 10. W = Hash(U)
let mut dgst = Sha256::default();
dgst.process(&u);
let w = UCN::from_bytes(dgst.fixed_result().as_slice());
// 11. computed_g = W^e mod p
let computed_g = w.modexp(&e, &p);
// 12. if (computed_g < 2), then go to step 7.
if &computed_g < &two {
continue;
}
// 13. if (computed_g == g), then return VALID, else return INVALID
return &computed_g == g;
}
}
pub fn get_input_seed<G: Rng>(rng: &mut G,
size: DSAParameterSize,
seedlen: usize)
-> Result<UCN,DSAGenError>
{
let mut firstseed = UCN::from(0u64);
let one = UCN::from(1u64);
let n = n_bits(size);
// 3. If (seedlen < N), then return FAILURE
if seedlen < n {
return Err(DSAGenError::InvalidSeedLength)
}
// 4. While firstseed < 2^(n-1) ...
let twonm1 = one << (n - 1);
while &firstseed < &twonm1 {
// Get an arbitrary sequence of seedlen bits as firstseed.
let bytes: Vec<u8> = rng.gen_iter().take(seedlen / 8).collect();
firstseed = UCN::from_bytes(&bytes);
}
// 5. Return SUCCESS and the value of firstseed
Ok(firstseed)
}
// Appendix C.6: Shawe-Taylor Random_Prime Routine. Also referenced in 186-4
// as ST_Random_Prime, so when you see that in a bit, understand that it's a
// recursive call.
fn shawe_taylor<G: Rng>(rng: &mut G, length: usize, input_seed: &UCN)
-> Result<(UCN,UCN,usize),DSAGenError>
{
// 1. If (length < 2), then return (FAILURE, 0, 0 {, 0}).
if length < 2 {
return Err(DSAGenError::InvalidPrimeLength);
}
// 2. If (length ≥ 33), then go to step 14.
if length >= 33 {
shawe_taylor_large(rng, length, input_seed)
} else {
shawe_taylor_small(length, input_seed)
}
}
fn shawe_taylor_small(length: usize, input_seed: &UCN)
-> Result<(UCN,UCN,usize),DSAGenError>
{
let one = UCN::from(1u64);
let two = UCN::from(2u64);
// 3. prime_seed = input_seed.
let mut prime_seed: UCN = input_seed.clone();
// 4. prime_gen_counter = 0
let mut prime_gen_counter = 0;
loop {
// 5. c = Hash(prime_seed) ⊕ Hash(prime_seed + 1).
let cbs = xorvecs(hash(&prime_seed, length),
hash(&(&prime_seed + &one), length));
let mut c = UCN::from_bytes(&cbs);
// 6. c = 2^(length 1) + (c mod 2^(length 1))
let twolm1: UCN = &one << (length - 1);
c = &twolm1 + (c % &twolm1);
// 7. c = (2 floor(c / 2)) + 1.
c = ((c >> 1) << 1) + &one;
// 8. prime_gen_counter = prime_gen_counter + 1.
prime_gen_counter = prime_gen_counter + 1;
// 9. prime_seed = prime_seed + 2.
prime_seed = prime_seed + &two;
// 10. Perform a deterministic primality test on c. For example, since
// c is small, its primality can be tested by trial division. See
// Appendix C.7.
let c_is_prime = prime_test(&c);
// 11. If (c is a prime number), then
if c_is_prime {
// 11.1 prime = c.
let prime = c;
// 11.2 Return (SUCCESS, prime, prime_seed {, prime_gen_counter}).
return Ok((prime, prime_seed.clone(), prime_gen_counter))
}
// 12. If (prime_gen_counter > (4 length)), then
// return (FAILURE, 0, 0 {, 0}).
if prime_gen_counter > (4 * length) {
return Err(DSAGenError::TooManyGenAttempts);
}
// 13. Go to step 5.
}
}
fn shawe_taylor_large<G: Rng>(rng: &mut G, length: usize, input_seed: &UCN)
-> Result<(UCN,UCN,usize),DSAGenError>
{
let one = UCN::from(1u64);
let two = UCN::from(2u64);
let three = UCN::from(3u64);
// 14. (status, c0, prime_seed, prime_gen_counter) =
// (ST_Random_Prime ((ceiling(length / 2) + 1), input_seed).
let len2: usize = ceildiv(&length, &2);
let (c0, mut prime_seed, mut prime_gen_counter) =
shawe_taylor( rng, len2 + 1, input_seed )?;
// 15. If FAILURE is returned, return (FAILURE, 0, 0 {, 0}).
// 16. iterations = ceiling(length / outlen) 1.
let outlen = 256; // the size of the hash function output in bits
let iterations = ceildiv(&length, &outlen) - 1;
// 17. old_counter = prime_gen_counter.
let old_counter = prime_gen_counter;
// 18. x = 0.
let mut x_bytes = Vec::new();
// 19. For i = 0 to iterations do
// x = x + (Hash(prime_seed + i) 2^(i * outlen)).
//
// We're going to actually run this backwards. What this computation
// does is essentially built up a large vector of hashes, one per
// iteration, shifting them bast each other via the 2^(i * outlen)
// term. So we'll just do this directly.
let mut i: i64 = iterations as i64;
while i >= 0 {
let bigi = UCN::from(i as u64);
let prime_i = &prime_seed + &bigi;
let mut hash_i = hash(&prime_i, length);
x_bytes.append(&mut hash_i);
i -= 1;
}
let mut x = UCN::from_bytes(&x_bytes);
// 20. prime_seed = prime_seed + iterations + 1.
prime_seed = &prime_seed + UCN::from(iterations) + &one;
// 21. x = 2^(length 1) + (x mod 2^(length 1)).
let twolm1 = &one << (length - 1);
x = &twolm1 + (&x % &twolm1);
// 22. t = ceiling(x / (2c0)).
let twoc0 = &two * &c0;
let mut t: UCN = ceildiv(&x, &twoc0);
loop {
// 23. If (2tc0 + 1 > 2^length), then
// t = ceiling(2^(length 1) / (2c0)).
let twotc0 = &t * &twoc0;
if (&twotc0 + &one) > (&one << length) {
t = ceildiv(&twolm1, &twoc0);
}
// 24. c = 2tc0 + 1.
let c = &twotc0 + &one;
// 25. prime_gen_counter = prime_gen_counter + 1.
prime_gen_counter = prime_gen_counter + 1;
// 26. a = 0.
let mut a_bytes = Vec::new();
// 27. For i = 0 to iterations do
// a = a + (Hash(prime_seed + i) 2 i * outlen).
//
// As with the last time we did this, we're going to do this more
// constructively
i = iterations as i64;
while i >= 0 {
let bigi = UCN::from(i as u64);
let prime_i = &prime_seed + &bigi;
let mut hash_i = hash(&prime_i, length);
a_bytes.append(&mut hash_i);
i -= 1;
}
let mut a = UCN::from_bytes(&a_bytes);
// 28. prime_seed = prime_seed + iterations + 1.
prime_seed = &prime_seed + UCN::from(iterations) + &one;
// 29. a = 2 + (a mod (c 3)).
a = &two + (a % (&c - &three));
// 30. z = a^2t mod c.
let z: UCN = a.modexp(&(&two * &t), &c);
// 31. If ((1 = GCD(z 1, c)) and (1 = z^c_0 mod c)), then
let gcd_ok = &one == &c.gcd(&(&z - &one));
let modexp_ok = &one == &z.modexp(&c0, &c);
if gcd_ok && modexp_ok {
// 31.1 prime = c.
let prime = c;
// 31.2 Return (SUCCESS, prime, prime_seed {, prime_gen_counter}).
return Ok((prime, prime_seed, prime_gen_counter));
}
// 32. If (prime_gen_counter ≥ ((4 length) + old_counter)), then
// return (FAILURE, 0, 0 {, 0}).
let limit = (4 * length) + old_counter;
if prime_gen_counter >= limit {
return Err(DSAGenError::TooManyGenAttempts)
}
// 33. t = t + 1.
t = t + &one;
// 34. Go to step 23.
}
}
fn ceildiv<T>(a: &T, b: &T) -> T
where T: Add<Output=T>,
T: Sub<Output=T>,
T: Div<Output=T>,
T: From<usize>,
T: Clone
{
let aclone: T = a.clone();
let bclone: T = b.clone();
let one: T = T::from(1 as usize);
let x: T = (aclone + bclone.clone()) - one;
let res = x / bclone;
res
}
fn prime_test(x: &UCN) -> bool {
let two = UCN::from(2 as u64);
let three = UCN::from(3 as u64);
let five = UCN::from(5 as u64);
if x.is_even() {
if x == &two {
return true;
}
return false;
}
if x.is_multiple_of(&three) {
return false;
}
if x.is_multiple_of(&five) {
return false;
}
let mut divisor = UCN::from(7 as u32);
let sqrtx = isqrt(&x);
while &divisor < &sqrtx {
if x.is_multiple_of(&divisor) {
return false;
}
divisor = next_divisor(divisor);
}
true
}
fn isqrt(x: &UCN) -> UCN {
let mut num = x.clone();
let mut res = UCN::from(0u64);
let one = UCN::from(1u64);
let mut bit = one << num.bits();
while &bit > &num {
bit >>= 2;
}
while !bit.is_zero() {
if num >= (&res + &bit) {
num = &num - (&res + &bit);
res = (&res >> 1) + &bit;
} else {
res >>= 1;
}
bit >>= 2;
}
res
}
fn next_divisor(input: UCN) -> UCN {
let two = UCN::from(2 as u64);
let three = UCN::from(3 as u64);
let five = UCN::from(5 as u64);
let mut x = input;
loop {
x = &x + &two;
if x.is_multiple_of(&three) {
continue;
}
if x.is_multiple_of(&five) {
continue;
}
return x;
}
}
fn hash(x: &UCN, len: usize) -> Vec<u8> {
let bytelen = len / 8;
let base = x.to_bytes(bytelen);
let mut dgst = Sha256::default();
dgst.process(&base);
dgst.fixed_result().as_slice().to_vec()
}
fn xorvecs(a: Vec<u8>, b: Vec<u8>) -> Vec<u8> {
assert!(a.len() == b.len());
let mut c = Vec::with_capacity(a.len());
for (a,b) in a.iter().zip(b.iter()) {
c.push(a ^ b);
}
c
}

646
src/dsa/gold_tests.rs Normal file
View File

@@ -0,0 +1,646 @@
use cryptonum::UCN;
use digest::{FixedOutput,Input};
use dsa::generation::*;
use dsa::rfc6979::*;
use dsa::parameters::*;
use dsa::private::DSAPrivate;
use dsa::public::DSAPublic;
use rand::{OsRng,Rng};
use sha1::Sha1;
use sha2::{Sha224,Sha256,Sha384,Sha512};
use simple_asn1::{der_decode,der_encode};
use testing::run_test;
const NUM_TESTS: u32 = 2;
#[test]
#[ignore]
fn pqg_generation_checks() {
let mut rng = OsRng::new().unwrap();
let params = DSAParameterSize::L1024N160;
for _ in 0..NUM_TESTS {
let seed = get_input_seed(&mut rng,params,n_bits(params)).unwrap();
let (p, q, ev) =
generate_provable_primes(&mut rng, &seed, params).unwrap();
assert!(validate_provable_primes(&mut rng, &p, &q, &ev));
let index = rng.gen::<u8>();
let pu = p.barrett_u();
let g = generate_verifiable_generator(&p, &pu, &q, &ev, index).unwrap();
assert!(verify_generator(&p, &q, &ev, index, &g));
}
}
#[test]
fn dsa_verification_tests()
{
run_test("tests/dsa/signature.test", 9, |case| {
let (neg0, pbytes) = case.get("p").unwrap();
let (neg1, gbytes) = case.get("g").unwrap();
let (neg2, qbytes) = case.get("q").unwrap();
let (neg4, ybytes) = case.get("y").unwrap();
let (neg5, hbytes) = case.get("h").unwrap();
let (neg6, msg) = case.get("m").unwrap();
let (neg7, rbytes) = case.get("r").unwrap();
let (neg8, sbytes) = case.get("s").unwrap();
assert!(!neg0 & !neg1 & !neg2 & !neg4 &
!neg5 & !neg6 & !neg7 & !neg8);
let p = UCN::from_bytes(pbytes);
let g = UCN::from_bytes(gbytes);
let q = UCN::from_bytes(qbytes);
let params = DSAParameters::new(p, g, q).unwrap();
let y = UCN::from_bytes(ybytes);
let public = DSAPublic::new(&params, y);
let r = UCN::from_bytes(rbytes);
let s = UCN::from_bytes(sbytes);
let sig = DSASignature{ r: r, s: s };
match usize::from(UCN::from_bytes(hbytes)) {
0x1 => assert!(public.verify::<Sha1>(msg, &sig)),
0x224 => assert!(public.verify::<Sha224>(msg, &sig)),
0x256 => assert!(public.verify::<Sha256>(msg, &sig)),
0x384 => assert!(public.verify::<Sha384>(msg, &sig)),
0x512 => assert!(public.verify::<Sha512>(msg, &sig)),
v => panic!("Bad hash size {}!", v)
}
});
}
#[test]
#[ignore]
fn dsa_signing_tests()
{
run_test("tests/dsa/signature.test", 9, |case| {
let (neg0, pbytes) = case.get("p").unwrap();
let (neg1, gbytes) = case.get("g").unwrap();
let (neg2, qbytes) = case.get("q").unwrap();
let (neg3, xbytes) = case.get("x").unwrap();
let (neg4, ybytes) = case.get("y").unwrap();
let (neg5, hbytes) = case.get("h").unwrap();
let (neg6, msg) = case.get("m").unwrap();
assert!(!neg0 & !neg1 & !neg2 & !neg3 & !neg4 & !neg5 & !neg6);
let p = UCN::from_bytes(pbytes);
let g = UCN::from_bytes(gbytes);
let q = UCN::from_bytes(qbytes);
let params = DSAParameters::new(p, g, q).unwrap();
let y = UCN::from_bytes(ybytes);
let public = DSAPublic::new(&params, y);
let x = UCN::from_bytes(xbytes);
let private = DSAPrivate::new(&params, x);
let hash_size = usize::from(UCN::from_bytes(hbytes));
let sig = match hash_size {
0x1 => private.sign::<Sha1>(msg),
0x224 => private.sign::<Sha224>(msg),
0x256 => private.sign::<Sha256>(msg),
0x384 => private.sign::<Sha384>(msg),
0x512 => private.sign::<Sha512>(msg),
v => panic!("Bad hash size {}!", v)
};
match usize::from(UCN::from_bytes(hbytes)) {
0x1 => assert!(public.verify::<Sha1>(msg, &sig)),
0x224 => assert!(public.verify::<Sha224>(msg, &sig)),
0x256 => assert!(public.verify::<Sha256>(msg, &sig)),
0x384 => assert!(public.verify::<Sha384>(msg, &sig)),
0x512 => assert!(public.verify::<Sha512>(msg, &sig)),
v => panic!("Bad hash size {}!", v)
}
});
}
macro_rules! run_rfc6979_test {
($hash: ty, $val: ident, $public: ident, $private: ident,
k $k: expr,
r $r: expr,
s $s: expr) => ({
let mut digest = <$hash>::default();
digest.process(&$val);
let h1 = digest.fixed_result().as_slice().to_vec();
let rbytes = $r;
let sbytes = $s;
let r = UCN::from_bytes(&rbytes);
let s = UCN::from_bytes(&sbytes);
let mut iter = KIterator::<$hash>::new(&h1,
n_bits($public.params.size),
&$public.params.q,
&$private.x);
let next = iter.next().unwrap();
let size = (next.bits() + 7) / 8;
let k1 = next.to_bytes(size);
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 = UCN::from_bytes(&pbytes);
let q = UCN::from_bytes(&qbytes);
let g = UCN::from_bytes(&gbytes);
let params = DSAParameters::new(p, g, q).unwrap();
let x = UCN::from_bytes(&xbytes);
let y = UCN::from_bytes(&ybytes);
let private = DSAPrivate::new(&params, x);
let public = DSAPublic::new(&params, 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, sample, 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, sample, 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, sample, 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, sample, 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, sample, 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, test, 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, test, 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, test, 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, test, 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, test, 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 = UCN::from_bytes(&pbytes);
let q = UCN::from_bytes(&qbytes);
let g = UCN::from_bytes(&gbytes);
let params = DSAParameters::new(p, g, q).unwrap();
let x = UCN::from_bytes(&xbytes);
let y = UCN::from_bytes(&ybytes);
let private = DSAPrivate::new(&params, x);
let public = DSAPublic::new(&params, 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, sample, 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, sample, 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, sample, 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, sample, 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, sample, 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, test, 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, test, 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, test, 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, test, 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, test, 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]);
}

77
src/dsa/mod.rs Normal file
View File

@@ -0,0 +1,77 @@
mod errors;
mod generation;
#[cfg(test)]
mod gold_tests;
mod parameters;
mod public;
mod private;
pub(crate) mod rfc6979;
pub use self::public::DSAPublic;
pub use self::private::DSAPrivate;
pub use self::rfc6979::DSASignature;
use cryptonum::UCN;
use rand::{OsRng,Rng};
use self::errors::*;
use self::parameters::*;
/// A DSA key pair
#[derive(Clone,Debug,PartialEq)]
pub struct DSAKeyPair {
pub private: DSAPrivate,
pub public: DSAPublic
}
impl DSAKeyPair {
pub fn generate(size: DSAParameterSize)
-> Result<DSAKeyPair,DSAGenError>
{
let mut rng = OsRng::new()?;
DSAKeyPair::generate_rng(&mut rng, size)
}
pub fn generate_rng<G: Rng>(rng: &mut G, size: DSAParameterSize)
-> Result<DSAKeyPair,DSAGenError>
{
let params = DSAParameters::generate_w_rng(rng, size)?;
DSAKeyPair::generate_w_params_rng(rng, &params)
}
pub fn generate_w_params(params: &DSAParameters)
-> Result<DSAKeyPair,DSAGenError>
{
let mut rng = OsRng::new()?;
DSAKeyPair::generate_w_params_rng(&mut rng, params)
}
pub fn generate_w_params_rng<G: Rng>(rng: &mut G, params: &DSAParameters)
-> Result<DSAKeyPair,DSAGenError>
{
// 1. N = len(q); L = len(p);
let n = n_bits(params.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.gen_iter().take(n + 8).collect();
// 5. Convert returned_bits to the (non-negative) integer c.
let c = UCN::from_bytes(&returned_bits);
// 6. x = (c mod (q-1)) + 1.
let one = UCN::from(1 as u64);
let x = (&c % (&params.q - &one)) + &one;
// 7. y = g^x mod p
let y = params.g.fastmodexp(&x, &params.pu);
// 8. Return SUCCESS, x, and y.
let private = DSAPrivate { params: params.clone(), x: x };
let public = DSAPublic { params: params.clone(), y: y };
Ok(DSAKeyPair {
private: private,
public: public
})
}
}

119
src/dsa/parameters.rs Normal file
View File

@@ -0,0 +1,119 @@
use cryptonum::{BarrettUCN,UCN};
use dsa::errors::*;
use dsa::generation::{DSAGenEvidence,verify_generator,
get_input_seed,generate_provable_primes,
generate_verifiable_generator,
validate_provable_primes};
use rand::{OsRng,Rng};
/// These are the legal lengths for L and N when using DSA; essentially,
/// the bit sizes available for the algorithms.
#[derive(Clone,Copy,Debug,PartialEq)]
pub enum DSAParameterSize { L1024N160, L2048N224, L2048N256, L3072N256 }
pub fn n_bits(ps: DSAParameterSize) -> usize {
match ps {
DSAParameterSize::L1024N160 => 160,
DSAParameterSize::L2048N224 => 224,
DSAParameterSize::L2048N256 => 256,
DSAParameterSize::L3072N256 => 256,
}
}
pub fn l_bits(ps: DSAParameterSize) -> usize {
match ps {
DSAParameterSize::L1024N160 => 1024,
DSAParameterSize::L2048N224 => 2048,
DSAParameterSize::L2048N256 => 2048,
DSAParameterSize::L3072N256 => 3072,
}
}
/// A set of DSA parameters, which are shared across both the public and private
/// keys.
#[derive(Clone,Debug,PartialEq)]
pub struct DSAParameters {
pub size: DSAParameterSize,
pub p: UCN,
pub g: UCN,
pub q: UCN,
pub pu: BarrettUCN,
pub qu: BarrettUCN
}
impl DSAParameters {
/// Generate a new set of DSA parameters, from a certificate file or some
/// other source. This will try to find an appropriate size based on the
/// size of the values provided, but will fail (returning
/// `DSAError::InvalidParamSize`) if it can't find a reasonable one.
pub fn new(p: UCN, g: UCN, q: UCN)
-> Result<DSAParameters,DSAError>
{
let l = ((p.bits() + 255) / 256) * 256;
let n = ((q.bits() + 15) / 16) * 16;
let size = match (l, n) {
(1024, 160) => DSAParameterSize::L1024N160,
(2048, 224) => DSAParameterSize::L2048N224,
(2048, 256) => DSAParameterSize::L2048N256,
(3072, 256) => DSAParameterSize::L3072N256,
_ => return Err(DSAError::InvalidParamSize)
};
let pu = p.barrett_u();
let qu = q.barrett_u();
Ok(DSAParameters{ size: size, p: p, g: g, q: q, pu: pu, qu: qu })
}
/// Generate a new set of DSA parameters for use. You probably shouldn't be
/// doing this. This is equivalent to calling `generate_w_rng` with
/// `OsRng`, which is supposed to be cryptographically sound.
pub fn generate(ps: DSAParameterSize)
-> Result<DSAParameters,DSAGenError>
{
let mut rng = OsRng::new()?;
DSAParameters::generate_w_rng(&mut rng, ps)
}
/// Generate a new set of DSA parameters for use, using the given entropy
/// source. I would normally include a note here about making sure to use
/// a good one, but if you're using DSA you've already given up a little
/// bit of the high ground, there.
pub fn generate_w_rng<G: Rng>(rng: &mut G, ps: DSAParameterSize)
-> Result<DSAParameters,DSAGenError>
{
let firstseed = get_input_seed(rng, ps, n_bits(ps))?;
let (p, q, ev) = generate_provable_primes(rng, &firstseed, ps)?;
DSAParameters::generate_g(ps, p, q, ev, 0)
}
/// Using the given p and q values and an index, create a new DSAParameters
/// by creating a new generator g that works with p and q.
fn generate_g(ps: DSAParameterSize,
p: UCN, q: UCN,
ev: DSAGenEvidence,
idx: u8)
-> Result<DSAParameters, DSAGenError>
{
let pu = p.barrett_u();
let qu = q.barrett_u();
let g = generate_verifiable_generator(&p, &pu, &q, &ev, idx)?;
Ok(DSAParameters{ size: ps, p: p, q: q, g: g, pu: pu, qu: qu })
}
/// Given the provided evidence, validate that the domain parameters
/// were appropriately constructed.
pub fn verify(&self, ev: &DSAGenEvidence, idx: u8) -> bool {
let mut rng = OsRng::new().unwrap();
self.verify_w_rng(&mut rng, ev, idx)
}
/// Given the set of inputs you used to generate your system, verify that
/// everything makes sense.
pub fn verify_w_rng<G: Rng>(&self, r: &mut G, ev: &DSAGenEvidence, idx: u8)
-> bool
{
validate_provable_primes(r, &self.p, &self.q, ev) &&
verify_generator(&self.p, &self.q, ev, idx, &self.g)
}
}

89
src/dsa/private.rs Normal file
View File

@@ -0,0 +1,89 @@
use cryptonum::UCN;
use digest::{BlockInput,FixedOutput,Input};
use digest::generic_array::ArrayLength;
use dsa::parameters::{DSAParameters,n_bits};
use dsa::rfc6979::{DSASignature,KIterator,bits2int};
use hmac::Hmac;
use std::ops::Rem;
/// A DSA private key.
#[derive(Clone,Debug,PartialEq)]
pub struct DSAPrivate {
pub params: DSAParameters,
pub(crate) x: UCN
}
impl DSAPrivate {
pub fn new(params: &DSAParameters, x: UCN) -> DSAPrivate {
DSAPrivate {
params: params.clone(),
x: x
}
}
pub fn sign<Hash>(&self, m: &[u8]) -> DSASignature
where
Hash: Clone + BlockInput + Input + FixedOutput + Default,
Hmac<Hash>: Clone,
Hash::BlockSize: ArrayLength<u8>
{
// 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 mut digest = <Hash>::default();
digest.process(m);
let n = n_bits(self.params.size);
let h1: Vec<u8> = digest.fixed_result()
.as_slice()
.iter()
.map(|x| *x)
.collect();
let h0 = bits2int(&h1, n);
let h = h0.reduce(&self.params.qu);
// 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>::new(&h1, n, &self.params.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 r = self.params.g.fastmodexp(&k, &self.params.pu)
.rem(&self.params.q);
if r.is_zero() {
continue;
}
// 4. The value s (modulo q) is computed:
//
// s = (h+x*r)/k mod q
//
// The pair (r, s) is the signature.
let kinv = k.modinv(&self.params.q);
let s = ((&h + (&self.x * &r)) * &kinv).rem(&self.params.q);
return DSASignature{ r: r, s: s };
}
panic!("The world is broken; couldn't find a k in sign().");
}
}

72
src/dsa/public.rs Normal file
View File

@@ -0,0 +1,72 @@
use cryptonum::{SCN,UCN};
use digest::{FixedOutput,Input};
use dsa::parameters::{DSAParameters,n_bits};
use dsa::rfc6979::DSASignature;
use num::BigInt;
use simple_asn1::{ASN1Block,ToASN1,ASN1EncodeErr,ASN1Class};
use std::cmp::min;
use std::ops::Rem;
/// A DSA key pair
#[derive(Clone,Debug,PartialEq)]
pub struct DSAPublic {
pub params: DSAParameters,
pub y: UCN
}
impl DSAPublic {
pub fn new(params: &DSAParameters, y: UCN) -> DSAPublic {
DSAPublic {
params: params.clone(),
y: y
}
}
pub fn verify<Hash>(&self, m: &[u8], sig: &DSASignature) -> bool
where Hash: Clone + Default + Input + FixedOutput
{
if sig.r >= self.params.q {
return false;
}
if sig.s >= self.params.q {
return false;
}
// w = (s')^-1 mod q;
let w = sig.s.modinv(&self.params.q);
// z = the leftmost min(N, outlen) bits of Hash(M').
let mut digest = <Hash>::default();
digest.process(m);
let z = { let mut bytes: Vec<u8> = digest.fixed_result()
.as_slice()
.iter()
.map(|x| *x)
.collect();
let n = n_bits(self.params.size) / 8;
let len = min(n, bytes.len());
bytes.truncate(len);
UCN::from_bytes(&bytes) };
// u1 = (zw) mod q
let u1 = (&z * &w).reduce(&self.params.qu);
// u2 = (rw) mod q
let u2 = (&sig.r * &w).reduce(&self.params.qu);
// v = (((g)^u1(y)^u2) mod p) mod q
let v_1 = self.params.g.fastmodexp(&u1, &self.params.pu);
let v_2 = self.y.fastmodexp(&u2, &self.params.pu);
let v = (&v_1 * &v_2).reduce(&self.params.pu)
.rem(&self.params.q);
// if v = r, then the signature is verified
v == sig.r
}
}
impl ToASN1 for DSAPublic {
type Error = ASN1EncodeErr;
fn to_asn1_class(&self, c: ASN1Class)
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
{
let inty = SCN::from(self.y.clone());
let yblock = ASN1Block::Integer(c, 0, BigInt::from(inty));
Ok(vec![yblock])
}
}

324
src/dsa/rfc6979.rs Normal file
View File

@@ -0,0 +1,324 @@
use cryptonum::UCN;
use digest::{BlockInput,FixedOutput,Input};
use digest::generic_array::ArrayLength;
use hmac::{Hmac,Mac};
use num::{BigInt,Signed};
use simple_asn1::{ASN1Block,ASN1Class,ASN1DecodeErr,ASN1EncodeErr,
FromASN1, ToASN1};
use std::clone::Clone;
#[allow(non_snake_case)]
pub struct KIterator<H>
where
H: Clone + BlockInput + Input + FixedOutput + Default,
H::BlockSize : ArrayLength<u8>
{
hmac_k: Hmac<H>,
V: Vec<u8>,
q: UCN,
qlen: usize
}
impl<H> KIterator<H>
where
H: Clone + BlockInput + Input + FixedOutput + Default,
Hmac<H>: Clone,
H::BlockSize : ArrayLength<u8>
{
pub fn new(h1: &[u8], qlen: usize, q: &UCN, x: &UCN) -> KIterator<H>
{
// 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(&K).unwrap(),
V: V,
q: q.clone(),
qlen: qlen
}
}
}
impl<H> Iterator for KIterator<H>
where
H: Clone + BlockInput + Input + FixedOutput + Default,
Hmac<H>: Clone,
H::BlockSize : ArrayLength<u8>
{
type Item = UCN;
fn next(&mut self) -> Option<UCN> {
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 = bits2int(&t, self.qlen);
//
// If that value of k is within the [1,q-1] range, and is
// suitable for DSA or ECDSA (i.e., it results in an r value
// that is not 0; see Section 3.4), then the generation of k
// is finished. The obtained value of k is used in DSA or
// ECDSA. Otherwise, compute:
//
// K = HMAC_K(V || 0x00)
let mut input = self.V.clone();
input.push(0x00);
#[allow(non_snake_case)]
let K = runhmac(&self.hmac_k, &input);
// V = HMAC_K(V)
self.hmac_k = Hmac::<H>::new(&K).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: &[u8], qlen: usize) -> UCN {
let mut value = UCN::from_bytes(x);
let vlen = x.len() * 8;
if vlen > qlen {
value >>= vlen - qlen;
}
value
}
fn bits2octets(x: &[u8], q: &UCN, qlen: usize) -> Vec<u8> {
let z1 = bits2int(x, qlen);
let res = if &z1 > q { z1 - q } else { z1 };
int2octets(&res, qlen)
}
fn int2octets(x: &UCN, qlen_bits: usize) -> Vec<u8> {
let qlen_bytes = (qlen_bits + 7) / 8;
x.to_bytes(qlen_bytes)
}
fn runhmac<H>(base: &Hmac<H>, m: &[u8]) -> Vec<u8>
where
H: Clone + BlockInput + Input + FixedOutput + Default,
Hmac<H>: Clone,
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: Clone + BlockInput + Input + FixedOutput + Default,
Hmac<H>: Clone,
H::BlockSize : ArrayLength<u8>
{
let mut runner = Hmac::<H>::new(&k).unwrap();
runner.input(&m);
runner.result().code().as_slice().to_vec()
}
/// A DSA Signature
#[derive(Clone,Debug,PartialEq)]
pub struct DSASignature {
pub r: UCN,
pub s: UCN
}
#[derive(Clone,Debug,PartialEq)]
pub enum DSADecodeError {
ASN1Error(ASN1DecodeErr),
NoSignatureFound,
NegativeSigValues
}
impl From<ASN1DecodeErr> for DSADecodeError {
fn from(a: ASN1DecodeErr) -> DSADecodeError {
DSADecodeError::ASN1Error(a)
}
}
impl FromASN1 for DSASignature {
type Error = DSADecodeError;
fn from_asn1(v: &[ASN1Block])
-> Result<(DSASignature,&[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)) => {
if rint.is_negative() || sint.is_negative() {
return Err(DSADecodeError::NegativeSigValues)
}
let r = UCN::from(rint);
let s = UCN::from(sint);
Ok((DSASignature{ r: r, s: s }, rest))
}
_ => Err(DSADecodeError::NoSignatureFound)
}
}
_ => Err(DSADecodeError::NoSignatureFound)
}
}
}
impl ToASN1 for DSASignature {
type Error = ASN1EncodeErr;
fn to_asn1_class(&self, c: ASN1Class)
-> Result<Vec<ASN1Block>,ASN1EncodeErr>
{
let rb = ASN1Block::Integer(c, 0, BigInt::from(self.r.clone()));
let sb = ASN1Block::Integer(c, 0, BigInt::from(self.s.clone()));
Ok(vec![ASN1Block::Sequence(c, 0, vec![rb,sb])])
}
}
#[cfg(test)]
mod tests {
use sha2::Sha256;
use super::*;
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 = UCN::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 = UCN::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 = UCN::from_bytes(&QBYTES);
let x = UCN::from_bytes(&XBYTES);
let mut iter = KIterator::<Sha256>::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 = UCN::from_bytes(&target);
assert_eq!(x, x2);
}
}
}
}

443
src/ecdsa/curves.rs Normal file
View File

@@ -0,0 +1,443 @@
use cryptonum::{BarrettUCN,SCN,UCN};
use ecdsa::point::ECPoint;
use std::fmt;
pub struct EllipticCurve {
pub name: &'static str,
pub p: [u8; 66],
pub n: [u8; 66],
pub seed: [u8; 66],
pub c: [u8; 66],
pub a: [u8; 66],
pub b: [u8; 66],
pub gx: [u8; 66],
pub gy: [u8; 66]
}
impl fmt::Debug for EllipticCurve {
fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
impl PartialEq for EllipticCurve {
fn eq(&self, other: &EllipticCurve) -> bool {
self.name == other.name
}
}
impl EllipticCurve {
pub fn get_p(&self) -> UCN {
UCN::from_bytes(&self.p)
}
pub fn get_pu(&self) -> BarrettUCN {
self.get_p().barrett_uk(self.get_p().contents.len() + 4)
}
pub fn get_n(&self) -> UCN {
UCN::from_bytes(&self.n)
}
pub fn get_seed(&self) -> UCN {
UCN::from_bytes(&self.seed)
}
pub fn get_c(&self) -> UCN {
UCN::from_bytes(&self.c)
}
pub fn get_a(&self) -> UCN {
UCN::from_bytes(&self.a)
}
pub fn get_b(&self) -> UCN {
UCN::from_bytes(&self.b)
}
pub fn default(&'static self) -> ECPoint {
let x = SCN::from(UCN::from_bytes(&self.gx));
let y = SCN::from(UCN::from_bytes(&self.gy));
ECPoint::new(self, x, y)
}
}
pub const NIST_P192: EllipticCurve = EllipticCurve {
name: "secp192r1",
p: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff ],
n: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x99, 0xde,
0xf8, 0x36, 0x14, 0x6b, 0xc9, 0xb1, 0xb4, 0xd2,
0x28, 0x31 ],
seed: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x45,
0xae, 0x6f, 0xc8, 0x42, 0x2f, 0x64, 0xed, 0x57,
0x95, 0x28, 0xd3, 0x81, 0x20, 0xea, 0xe1, 0x21,
0x96, 0xd5 ],
c: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x30, 0x99, 0xd2, 0xbb, 0xbf, 0xcb,
0x25, 0x38, 0x54, 0x2d, 0xcd, 0x5f, 0xb0, 0x78,
0xb6, 0xef, 0x5f, 0x3d, 0x6f, 0xe2, 0xc7, 0x45,
0xde, 0x65 ],
a: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xfc ],
b: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x64, 0x21, 0x05, 0x19, 0xe5, 0x9c,
0x80, 0xe7, 0x0f, 0xa7, 0xe9, 0xab, 0x72, 0x24,
0x30, 0x49, 0xfe, 0xb8, 0xde, 0xec, 0xc1, 0x46,
0xb9, 0xb1 ],
gx: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x18, 0x8d, 0xa8, 0x0e, 0xb0, 0x30,
0x90, 0xf6, 0x7c, 0xbf, 0x20, 0xeb, 0x43, 0xa1,
0x88, 0x00, 0xf4, 0xff, 0x0a, 0xfd, 0x82, 0xff,
0x10, 0x12 ],
gy: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0x19, 0x2b, 0x95, 0xff, 0xc8,
0xda, 0x78, 0x63, 0x10, 0x11, 0xed, 0x6b, 0x24,
0xcd, 0xd5, 0x73, 0xf9, 0x77, 0xa1, 0x1e, 0x79,
0x48, 0x11 ]
};
pub const NIST_P224: EllipticCurve = EllipticCurve {
name: "secp224r1",
p: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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],
n: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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],
seed: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x71,
0x34, 0x47, 0x99, 0xd5, 0xc7, 0xfc, 0xdc, 0x45,
0xb5, 0x9f, 0xa3, 0xb9, 0xab, 0x8f, 0x6a, 0x94,
0x8b, 0xc5],
c: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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],
a: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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],
b: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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],
gx: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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],
gy: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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 const NIST_P256: EllipticCurve = EllipticCurve {
name: "secp256r1",
p: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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],
n: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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],
seed: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0x9d,
0x36, 0x08, 0x86, 0xe7, 0x04, 0x93, 0x6a, 0x66,
0x78, 0xe1, 0x13, 0x9d, 0x26, 0xb7, 0x81, 0x9f,
0x7e, 0x90],
c: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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],
a: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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],
b: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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],
gx: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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],
gy: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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 const NIST_P384: EllipticCurve = EllipticCurve {
name: "secp384r1",
p: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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],
n: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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],
seed: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x35,
0x92, 0x6a, 0xa3, 0x19, 0xa2, 0x7a, 0x1d, 0x00,
0x89, 0x6a, 0x67, 0x73, 0xa4, 0x82, 0x7a, 0xcd,
0xac, 0x73],
c: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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],
a: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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],
b: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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],
gx: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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],
gy: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 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 const NIST_P521: EllipticCurve = EllipticCurve {
name: "secp521r1",
p: [ 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 ],
n: [ 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 ],
seed: [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x9e,
0x88, 0x00, 0x29, 0x1c, 0xb8, 0x53, 0x96, 0xcc,
0x67, 0x17, 0x39, 0x32, 0x84, 0xaa, 0xa0, 0xda,
0x64, 0xba ],
c: [ 0x00, 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 ],
a: [ 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 ],
b: [ 0x00, 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 ],
gx: [ 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 ],
gy: [ 0x00, 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 ]
};

187
src/ecdsa/gold_tests.rs Normal file
View File

@@ -0,0 +1,187 @@
use cryptonum::{SCN,UCN};
//use dsa::rfc6979::*;
use ecdsa::curves::*;
use ecdsa::point::ECPoint;
//use ecdsa::private::ECDSAPrivate;
//use ecdsa::public::ECDSAPublic;
//use sha1::Sha1;
//use sha2::{Sha224,Sha256,Sha384,Sha512};
use testing::run_test;
fn get_curve(cbytes: &[u8]) -> &'static EllipticCurve {
match usize::from(UCN::from_bytes(cbytes)) {
0x192 => &NIST_P192,
0x224 => &NIST_P224,
0x256 => &NIST_P256,
0x384 => &NIST_P384,
0x521 => &NIST_P521,
x => panic!("Unacceptable curve identifier {}", x)
}
}
#[test]
fn point_negate()
{
run_test("tests/ecdsa/ec_negate.test", 5, |case| {
let (neg0, abytes) = case.get("a").unwrap();
let (neg1, bbytes) = case.get("b").unwrap();
let (neg2, cbytes) = case.get("c").unwrap();
let (neg3, xbytes) = case.get("x").unwrap();
let (neg4, ybytes) = case.get("y").unwrap();
assert!(!neg0 && !neg1 && !neg2 && !neg3 && !neg4);
let curve = get_curve(&cbytes);
let x = SCN::from(UCN::from_bytes(xbytes));
let y = SCN::from(UCN::from_bytes(ybytes));
let orig = ECPoint::new(curve, x, y);
let a = SCN::from(UCN::from_bytes(abytes));
let b = SCN::from(UCN::from_bytes(bbytes));
let inverted = ECPoint::new(curve, a, b);
assert_eq!(inverted, orig.negate());
});
}
#[test]
fn point_double()
{
run_test("tests/ecdsa/ec_dble.test", 5, |case| {
println!("START");
let (neg0, abytes) = case.get("a").unwrap();
let (neg1, bbytes) = case.get("b").unwrap();
let (neg2, cbytes) = case.get("c").unwrap();
let (neg3, xbytes) = case.get("x").unwrap();
let (neg4, ybytes) = case.get("y").unwrap();
assert!(!neg0 && !neg1 && !neg2 && !neg3 && !neg4);
println!("SEC1");
let curve = get_curve(&cbytes);
println!("SEC2");
let x = SCN::from(UCN::from_bytes(xbytes));
let y = SCN::from(UCN::from_bytes(ybytes));
let orig = ECPoint::new(curve, x, y);
println!("SEC3");
let a = SCN::from(UCN::from_bytes(abytes));
let b = SCN::from(UCN::from_bytes(bbytes));
let doubled = ECPoint::new(curve, a, b);
println!("SEC4");
assert_eq!(doubled, orig.double());
});
}
#[test]
fn point_add()
{
run_test("tests/ecdsa/ec_add.test", 7, |case| {
let (neg0, abytes) = case.get("a").unwrap();
let (neg1, bbytes) = case.get("b").unwrap();
let (neg2, qbytes) = case.get("q").unwrap();
let (neg3, rbytes) = case.get("r").unwrap();
let (neg4, cbytes) = case.get("c").unwrap();
let (neg5, xbytes) = case.get("x").unwrap();
let (neg6, ybytes) = case.get("y").unwrap();
assert!(!neg0 && !neg1 && !neg2 && !neg3 && !neg4 && !neg5 && !neg6);
let curve = get_curve(&cbytes);
let x = SCN::from(UCN::from_bytes(xbytes));
let y = SCN::from(UCN::from_bytes(ybytes));
let p1 = ECPoint::new(curve, x, y);
let q = SCN::from(UCN::from_bytes(qbytes));
let r = SCN::from(UCN::from_bytes(rbytes));
let p2 = ECPoint::new(curve, q, r);
let a = SCN::from(UCN::from_bytes(abytes));
let b = SCN::from(UCN::from_bytes(bbytes));
let result = ECPoint::new(curve, a, b);
assert_eq!(result, p1.add(&p2));
});
}
#[test]
fn point_scale()
{
run_test("tests/ecdsa/ec_mul.test", 6, |case| {
let (neg0, abytes) = case.get("a").unwrap();
let (neg1, bbytes) = case.get("b").unwrap();
let (neg2, kbytes) = case.get("k").unwrap();
let (neg3, cbytes) = case.get("c").unwrap();
let (neg4, xbytes) = case.get("x").unwrap();
let (neg5, ybytes) = case.get("y").unwrap();
assert!(!neg0 && !neg1 && !neg2 && !neg3 && !neg4 && !neg5);
let curve = get_curve(&cbytes);
let x = SCN::from(UCN::from_bytes(xbytes));
let y = SCN::from(UCN::from_bytes(ybytes));
let base = ECPoint::new(curve, x, y);
let k = UCN::from_bytes(kbytes);
let a = SCN::from(UCN::from_bytes(abytes));
let b = SCN::from(UCN::from_bytes(bbytes));
let result = ECPoint::new(curve, a, b);
assert_eq!(result, base.scale(&k));
});
}
//#[test]
//fn verification_tests()
//{
// run_test("tests/ecdsa/signature.test", 8, |case| {
// let (neg0, cbytes) = case.get("c").unwrap();
// let (negx, xbytes) = case.get("x").unwrap();
// let (negy, ybytes) = case.get("y").unwrap();
// let (neg1, hbytes) = case.get("h").unwrap();
// let (neg2, msg) = case.get("m").unwrap();
// let (neg3, rbytes) = case.get("r").unwrap();
// let (neg4, sbytes) = case.get("s").unwrap();
//
// assert!(!neg0 & !neg1 & !neg2 & !neg3 & !neg4);
// let curve = get_curve(cbytes);
// let ux = UCN::from_bytes(xbytes);
// let uy = UCN::from_bytes(ybytes);
// let x = SCN{ negative: *negx, value: ux };
// let y = SCN{ negative: *negy, value: uy };
// let point = ECCPoint::new(&curve, x, y);
// let public = ECDSAPublic::new(&curve, &point);
// let r = UCN::from_bytes(rbytes);
// let s = UCN::from_bytes(sbytes);
// println!("r: {:X}", r);
// let sig = DSASignature{ r: r, s: s };
//
// match usize::from(UCN::from_bytes(hbytes)) {
// 0x1 => assert!(public.verify::<Sha1>(msg, &sig)),
// 0x224 => assert!(public.verify::<Sha224>(msg, &sig)),
// 0x256 => assert!(public.verify::<Sha256>(msg, &sig)),
// 0x384 => assert!(public.verify::<Sha384>(msg, &sig)),
// 0x512 => assert!(public.verify::<Sha512>(msg, &sig)),
// v => panic!("Bad hash size {}!", v)
// }
// });
//}
//
//#[test]
//fn signing_tests()
//{
// run_test("tests/ecdsa/signature.test", 8, |case| {
// let (neg0, cbytes) = case.get("c").unwrap();
// let (neg1, dbytes) = case.get("d").unwrap();
// let (neg2, hbytes) = case.get("h").unwrap();
// let (neg3, msg) = case.get("m").unwrap();
// let (neg4, rbytes) = case.get("r").unwrap();
// let (neg5, sbytes) = case.get("s").unwrap();
//
// assert!(!neg0 & !neg1 & !neg2 & !neg3 & !neg4 & !neg5);
// let curve = get_curve(cbytes);
// let d = UCN::from_bytes(dbytes);
// let private = ECDSAPrivate::new(&curve, &d);
// let r = UCN::from_bytes(rbytes);
// let s = UCN::from_bytes(sbytes);
// let sig = DSASignature{ r: r, s: s };
//
// match usize::from(UCN::from_bytes(hbytes)) {
// 0x1 => assert_eq!(sig, private.sign::<Sha1>(msg)),
// 0x224 => assert_eq!(sig, private.sign::<Sha224>(msg)),
// 0x256 => assert_eq!(sig, private.sign::<Sha256>(msg)),
// 0x384 => assert_eq!(sig, private.sign::<Sha384>(msg)),
// 0x512 => assert_eq!(sig, private.sign::<Sha512>(msg)),
// v => panic!("Bad hash size {}!", v)
// }
// });
//}

61
src/ecdsa/mod.rs Normal file
View File

@@ -0,0 +1,61 @@
mod curves;
#[cfg(test)]
mod gold_tests;
mod point;
//mod private;
//mod public;
//
//pub use self::private::ECDSAPrivate;
//pub use self::public::ECDSAPublic;
pub use self::curves::{NIST_P192,NIST_P224,NIST_P256,NIST_P384,NIST_P521};
//
//use cryptonum::UCN;
//use rand::{Rng,OsRng};
//use self::curves::EllipticCurve;
//use self::math::ECCPoint;
//
//#[derive(Clone,Debug,PartialEq)]
//pub struct ECDSAKeyPair {
// pub private: ECDSAPrivate,
// pub public: ECDSAPublic
//}
//
//impl ECDSAKeyPair {
// pub fn generate(params: &'static EllipticCurve)
// -> ECDSAKeyPair
// {
// let mut rng = OsRng::new().unwrap();
// ECDSAKeyPair::generate_w_rng(&mut rng, params)
//
// }
//
// pub fn generate_w_rng<G: Rng>(rng: &mut G, params: &'static EllipticCurve)
// -> ECDSAKeyPair
// {
// let one = UCN::from(1u64);
// #[allow(non_snake_case)]
// let N = params.n.bits();
// let bits_to_generate = N + 64;
// let bytes_to_generate = (bits_to_generate + 7) / 8;
// let bits: Vec<u8> = rng.gen_iter().take(bytes_to_generate).collect();
// let bits_generated = bytes_to_generate * 8;
// let mut c = UCN::from_bytes(&bits);
// c >>= bits_generated - bits_to_generate;
// let nm1 = &params.n - &one;
// let d = (c % &nm1) + &one;
// #[allow(non_snake_case)]
// let Q = ECCPoint::default(params).scale(&d);
// ECDSAKeyPair {
// private: ECDSAPrivate {
// curve: params,
// d: d
// },
// public: ECDSAPublic {
// curve: params,
// Q: Q
// }
// }
// }
//}
//
//

227
src/ecdsa/point.rs Normal file
View File

@@ -0,0 +1,227 @@
use cryptonum::{SCN,UCN};
use ecdsa::curves::EllipticCurve;
#[derive(Clone,Debug,PartialEq)]
pub struct ECPoint {
pub curve: &'static EllipticCurve,
pub value: ECPointValue
}
#[derive(Clone,Debug,PartialEq)]
pub enum ECPointValue {
Infinity,
Point(SCN, SCN)
}
impl ECPoint {
pub fn new(ec: &'static EllipticCurve, x: SCN, y: SCN) -> ECPoint {
ECPoint {
curve: ec,
value: ECPointValue::Point(x, y)
}
}
pub fn zero(ec: &'static EllipticCurve) -> ECPoint {
ECPoint { curve: ec, value: ECPointValue::Infinity }
}
pub fn negate(&self) -> ECPoint {
match self.value {
ECPointValue::Infinity =>
self.clone(),
ECPointValue::Point(ref x, ref y) => {
let newy = SCN::from(self.curve.get_p()) - y;
let newv = ECPointValue::Point(x.clone(), newy);
ECPoint{ curve: self.curve, value: newv }
}
}
}
pub fn get_x(&self) -> SCN {
match self.value {
ECPointValue::Infinity =>
SCN::zero(),
ECPointValue::Point(ref x, _) =>
x.clone()
}
}
pub fn get_y(&self) -> SCN {
match self.value {
ECPointValue::Infinity =>
SCN::zero(),
ECPointValue::Point(_, ref y) =>
y.clone()
}
}
pub fn double(&self) -> ECPoint {
match self.value {
ECPointValue::Infinity =>
self.clone(),
ECPointValue::Point(ref x, ref y) => {
let ua = SCN::from(self.curve.get_a());
let up = SCN::from(self.curve.get_p());
// lambda = (3 * xp ^ 2 + a) / 2 yp
let mut lambda = x * x;
lambda *= SCN::from(3);
lambda += &ua;
let twoy = y << 1;
lambda = lambda.divmod(&twoy, &self.curve.get_pu());
// xr = lambda ^ 2 - 2 xp
let mut xr = &lambda * &lambda;
let xr_right = x << 1;
xr -= xr_right;
assert!(!xr.is_negative());
xr %= &up;
// yr = lambda (xp - xr) - yp
let xdiff = x - &xr;
let mut yr = &lambda * &xdiff;
yr -= y;
assert!(!yr.is_negative());
yr %= up;
//
ECPoint {
curve: self.curve,
value: ECPointValue::Point(xr, yr)
}
}
}
}
pub fn add(&self, other: &ECPoint) -> ECPoint {
assert_eq!(self.curve, other.curve);
match (&self.value, &other.value) {
(ECPointValue::Infinity, ECPointValue::Infinity) =>
self.clone(),
(ECPointValue::Infinity, _) =>
other.clone(),
(_, ECPointValue::Infinity) =>
self.clone(),
(ECPointValue::Point(ref sx, ref sy),
ECPointValue::Point(ref ox, ref oy)) => {
let xdiff = sx - ox;
let ydiff = sy - oy;
let pu = self.curve.get_pu();
let s = ydiff.divmod(&xdiff, &pu);
let mut xr = &s * &s;
xr -= sx;
xr -= ox;
xr = xr.reduce(&pu);
let mut yr = sx - &xr;
yr *= &s;
yr -= sy;
yr = yr.reduce(&pu);
let val = ECPointValue::Point(xr, yr);
ECPoint{ curve: self.curve, value: val }
}
}
}
pub fn scale(&self, d: &UCN) -> ECPoint {
match self.value {
ECPointValue::Infinity =>
self.clone(),
ECPointValue::Point(_, _) => {
if d.is_zero() {
return ECPoint::zero(self.curve);
}
let mut q = self.clone();
let i = d.bits() - 2;
let mut mask = UCN::from(1u64) << i;
while !mask.is_zero() {
q = q.double();
let test = d & &mask;
if !test.is_zero() {
q = q.add(&self);
}
mask >>= 1;
}
q
}
}
}
}
//
// pub fn bits2int(x: &[u8], qlen: usize) -> UCN {
// let mut value = UCN::from_bytes(x);
// let vlen = x.len() * 8;
//
// if vlen > qlen {
// value >>= vlen - qlen;
// }
//
// value
// }
//
// pub fn point_add_two_muls(k1: &UCN, p1: &ECCPoint, k2: &UCN, p2: &ECCPoint)
// -> ECCPoint
// {
// panic!("point_add_two_muls()")
// }
//
// #[cfg(test)]
// mod tests {
// use super::*;
//
// #[test]
// fn p256_double() {
// let xbytes = vec![0x7c, 0xf2, 0x7b, 0x18, 0x8d, 0x03, 0x4f, 0x7e,
// 0x8a, 0x52, 0x38, 0x03, 0x04, 0xb5, 0x1a, 0xc3,
// 0xc0, 0x89, 0x69, 0xe2, 0x77, 0xf2, 0x1b, 0x35,
// 0xa6, 0x0b, 0x48, 0xfc, 0x47, 0x66, 0x99, 0x78];
// let ybytes = vec![0x07, 0x77, 0x55, 0x10, 0xdb, 0x8e, 0xd0, 0x40,
// 0x29, 0x3d, 0x9a, 0xc6, 0x9f, 0x74, 0x30, 0xdb,
// 0xba, 0x7d, 0xad, 0xe6, 0x3c, 0xe9, 0x82, 0x29,
// 0x9e, 0x04, 0xb7, 0x9d, 0x22, 0x78, 0x73, 0xd1];
// let x = SCN::from(UCN::from_bytes(&xbytes));
// let y = SCN::from(UCN::from_bytes(&ybytes));
// let base = ECCPoint::default(&EllipticCurve::p256());
// let res = base.double();
// let goal = ECCPoint{ curve: base.curve,
// value: ECCPointValue::Point(x,y) };
// assert_eq!(res, goal);
// }
//
// #[test]
// fn p256_add() {
// let xbytes = vec![0x5e, 0xcb, 0xe4, 0xd1, 0xa6, 0x33, 0x0a, 0x44,
// 0xc8, 0xf7, 0xef, 0x95, 0x1d, 0x4b, 0xf1, 0x65,
// 0xe6, 0xc6, 0xb7, 0x21, 0xef, 0xad, 0xa9, 0x85,
// 0xfb, 0x41, 0x66, 0x1b, 0xc6, 0xe7, 0xfd, 0x6c];
// let ybytes = vec![0x87, 0x34, 0x64, 0x0c, 0x49, 0x98, 0xff, 0x7e,
// 0x37, 0x4b, 0x06, 0xce, 0x1a, 0x64, 0xa2, 0xec,
// 0xd8, 0x2a, 0xb0, 0x36, 0x38, 0x4f, 0xb8, 0x3d,
// 0x9a, 0x79, 0xb1, 0x27, 0xa2, 0x7d, 0x50, 0x32];
// let x = SCN::from(UCN::from_bytes(&xbytes));
// let y = SCN::from(UCN::from_bytes(&ybytes));
// let base = ECCPoint::default(&EllipticCurve::p256());
// let res = base.add(&base.double());
// let goal = ECCPoint{ curve: base.curve,
// value: ECCPointValue::Point(x,y) };
// assert_eq!(res, goal);
// }
//
// #[test]
// fn p256_scale() {
// let xbytes = vec![0xea, 0x68, 0xd7, 0xb6, 0xfe, 0xdf, 0x0b, 0x71,
// 0x87, 0x89, 0x38, 0xd5, 0x1d, 0x71, 0xf8, 0x72,
// 0x9e, 0x0a, 0xcb, 0x8c, 0x2c, 0x6d, 0xf8, 0xb3,
// 0xd7, 0x9e, 0x8a, 0x4b, 0x90, 0x94, 0x9e, 0xe0];
// let ybytes = vec![0x2a, 0x27, 0x44, 0xc9, 0x72, 0xc9, 0xfc, 0xe7,
// 0x87, 0x01, 0x4a, 0x96, 0x4a, 0x8e, 0xa0, 0xc8,
// 0x4d, 0x71, 0x4f, 0xea, 0xa4, 0xde, 0x82, 0x3f,
// 0xe8, 0x5a, 0x22, 0x4a, 0x4d, 0xd0, 0x48, 0xfa];
// let x = SCN::from(UCN::from_bytes(&xbytes));
// let y = SCN::from(UCN::from_bytes(&ybytes));
// let base = ECCPoint::default(&EllipticCurve::p256());
// let res = base.scale(&UCN::from(9 as u64));
// let goal = ECCPoint{ curve: base.curve,
// value: ECCPointValue::Point(x,y) };
// assert_eq!(res, goal);
// }
// }

93
src/ecdsa/private.rs Normal file
View File

@@ -0,0 +1,93 @@
use cryptonum::{SCN,UCN};
use digest::{BlockInput,FixedOutput,Input};
use digest::generic_array::ArrayLength;
use dsa::rfc6979::{DSASignature,KIterator};
use ecdsa::curves::EllipticCurve;
use ecdsa::math::{ECCPoint,bits2int};
use hmac::Hmac;
#[derive(Clone,Debug,PartialEq)]
pub struct ECDSAPrivate {
pub(crate) curve: &'static EllipticCurve,
pub(crate) d: UCN
}
impl ECDSAPrivate {
pub fn new(c: &'static EllipticCurve, d: &UCN)
-> ECDSAPrivate
{
ECDSAPrivate {
curve: c,
d: d.clone()
}
}
pub fn sign<Hash>(&self, m: &[u8]) -> DSASignature
where
Hash: Clone + BlockInput + Input + FixedOutput + Default,
Hmac<Hash>: Clone,
Hash::BlockSize: ArrayLength<u8>
{
// 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 mut digest = <Hash>::default();
digest.process(m);
let n = self.curve.p.bits();
let h1: Vec<u8> = digest.fixed_result()
.as_slice()
.iter()
.map(|x| *x)
.collect();
let h0 = bits2int(&h1, n);
let h = h0 % &self.curve.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>::new(&h1, n, &self.curve.n, &self.curve.b) {
// 3. A value r (modulo q) is computed from k and the key
// parameters:
// * For DSA ...
// * For ECDSA: the point kG is computed; its X coordinate (a
// member of the field over which E is defined) is converted
// to an integer, which is reduced modulo q, yielding r.
//
// 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 = ECCPoint::default(self.curve);
let kg = g.scale(&k);
let ni = SCN::from(self.curve.n.clone());
let r = &kg.get_x() % &ni;
if r.is_zero() {
continue;
}
// 4. The value s (modulo q) is computed:
//
// s = (h+x*r)/k mod q
//
// The pair (r, s) is the signature.
let kinv = SCN::from(k.modinv(&ni.value));
let s = ((SCN::from(h.clone()) + (&kg.get_x() * &r)) * &kinv) % &ni;
if s.is_zero() {
continue;
}
assert!(!r.is_negative());
assert!(!s.is_negative());
return DSASignature{ r: r.value, s: s.value };
}
panic!("The world is broken; couldn't find a k in sign().");
}
}

62
src/ecdsa/public.rs Normal file
View File

@@ -0,0 +1,62 @@
use digest::{BlockInput,FixedOutput,Input};
use digest::generic_array::ArrayLength;
use dsa::rfc6979::DSASignature;
use ecdsa::curves::EllipticCurve;
use ecdsa::math::{ECCPoint,bits2int,point_add_two_muls};
use hmac::Hmac;
#[allow(non_snake_case)]
#[derive(Clone,Debug,PartialEq)]
pub struct ECDSAPublic {
pub(crate) curve: &'static EllipticCurve,
pub(crate) Q: ECCPoint
}
impl ECDSAPublic {
pub fn new(curve: &'static EllipticCurve, point: &ECCPoint)
-> ECDSAPublic
{
ECDSAPublic {
curve: curve,
Q: point.clone()
}
}
pub fn verify<Hash>(&self, m: &[u8], sig: &DSASignature) -> bool
where
Hash: Clone + BlockInput + Input + FixedOutput + Default,
Hmac<Hash>: Clone,
Hash::BlockSize: ArrayLength<u8>
{
let n = &self.curve.n;
if &sig.r > n {
return false;
}
if &sig.s > n {
return false;
}
let c = sig.s.modinv(&n);
let mut digest = <Hash>::default();
digest.process(m);
let h1: Vec<u8> = digest.fixed_result()
.as_slice()
.iter()
.map(|x| *x)
.collect();
let h0 = bits2int(&h1, self.curve.p.bits()) % n;
let u1 = (&h0 * &c) % n;
let u2 = (&sig.r * &c) % n;
let x = point_add_two_muls(&u1, &ECCPoint::default(&self.curve),
&u2, &self.Q);
let xx = x.get_x();
if xx.is_negative() {
return false;
}
(xx.value % n) == sig.r
}
}

View File

@@ -1,4 +1,3 @@
#![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
@@ -11,20 +10,37 @@
//! 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 digest;
extern crate hmac;
extern crate num;
#[cfg(test)] #[cfg(test)]
#[macro_use] #[macro_use]
extern crate quickcheck; extern crate quickcheck;
extern crate rand; extern crate rand;
extern crate sha1;
extern crate sha2;
extern crate simple_asn1;
/// The cryptonum module provides support for large numbers at fixed, /// The `cryptonum` module provides support for large numbers for use in various
/// cryptographically-relevant sizes. /// cryptographically-relevant algorithms.
pub mod cryptonum; pub mod cryptonum;
/// The `rsa` module provides support for RSA-related core algorithms, including
/// signing / verification and encryption / decryption. You can also generate
/// key material there.
pub mod rsa;
/// The `dsa` module provides support for DSA-related signing and verification
/// algorithms, as well as key generation. That being said: don't use this,
/// unless you've got a legacy application or system that you're trying to
/// interact with. DSA is almost always the wrong choice.
pub mod dsa;
/// The 'ecdsa' module provides support for ECDSA-related signing and
/// verification algorithms, as well as key generation. This and RSA should be
/// your go-to choice for asymmetric crypto.
pub mod ecdsa;
#[cfg(test)]
mod testing;
#[cfg(test)] #[cfg(test)]
mod test { mod test {
#[test]
fn testing_works() {
assert!(true);
}
} }

163
src/rsa/core.rs Normal file
View File

@@ -0,0 +1,163 @@
use cryptonum::{BarrettUCN,UCN};
use rand::Rng;
pub static ACCEPTABLE_KEY_SIZES: [(usize,usize); 8] =
[(512, 7),
(1024, 7),
(2048, 4),
(3072, 3),
(4096, 3),
(7680, 3),
(8192, 3),
(15360, 3)];
fn iterations_for_size(l: usize) -> usize {
for &(m, i) in ACCEPTABLE_KEY_SIZES.iter() {
if m == l {
return i;
}
}
panic!("Bad key size, can't get M-R iterations")
}
pub fn generate_pq<G: Rng>(rng: &mut G, e: &UCN, bitlen: usize) -> (UCN, UCN) {
let iterations = iterations_for_size(bitlen);
let sqrt2 = UCN::from(6074001000 as u64);
let topbit = UCN::from(1 as u64) << ((bitlen / 2) - 1);
let minval = sqrt2 << ((bitlen / 2) - 33);
let mindiff = UCN::from(1 as u64) << ((bitlen / 2) - 101);
let validate = |inval| {
let x = &inval | &topbit;
if x < minval {
return None
}
if !gcd_is_one(&e, &x) {
return None
}
Some(x)
};
let p = UCN::generate_prime(rng, bitlen / 2, iterations, validate);
loop {
let q = UCN::generate_prime(rng, bitlen / 2, iterations, validate);
if diff(&p, &q) >= mindiff {
return (p, q);
}
}
}
fn diff(a: &UCN, b: &UCN) -> UCN {
if a > b {
a - b
} else {
b - a
}
}
fn gcd_is_one(a: &UCN, b: &UCN) -> bool {
let mut u = a.clone();
let mut v = b.clone();
if u.is_zero() {
return v == UCN::from(1 as u8);
}
if v.is_zero() {
return u == UCN::from(1 as u8);
}
if u.is_even() && v.is_even() {
return false;
}
while u.is_even() {
u >>= 1;
}
loop {
while v.is_even() {
v >>= 1;
}
// u and v guaranteed to be odd right now.
if u > v {
// make sure that v > u, so that our subtraction works
// out.
let t = u;
u = v;
v = t;
}
v = v - &u;
if v.is_zero() {
return u == UCN::from(1 as u64);
}
}
}
// the RSA encryption function
pub fn ep(nu: &BarrettUCN, e: &UCN, m: &UCN) -> UCN {
m.fastmodexp(e, nu)
}
// the RSA decryption function
pub fn dp(nu: &BarrettUCN, d: &UCN, c: &UCN) -> UCN {
c.fastmodexp(d, nu)
}
// the RSA signature generation function
pub fn sp1(nu: &BarrettUCN, d: &UCN, m: &UCN) -> UCN {
m.fastmodexp(d, nu)
}
// the RSA signature verification function
pub fn vp1(nu: &BarrettUCN, e: &UCN, s: &UCN) -> UCN {
s.fastmodexp(e, nu)
}
// encoding PKCS1 stuff
pub fn pkcs1_pad(ident: &[u8], hash: &[u8], keylen: usize) -> Vec<u8> {
let mut idhash = Vec::new();
idhash.extend_from_slice(ident);
idhash.extend_from_slice(hash);
let tlen = idhash.len();
assert!(keylen > (tlen + 3));
let mut padding = Vec::new();
padding.resize(keylen - tlen - 3, 0xFF);
let mut result = vec![0x00, 0x01];
result.append(&mut padding);
result.push(0x00);
result.append(&mut idhash);
result
}
pub fn xor_vecs(a: &Vec<u8>, b: &Vec<u8>) -> Vec<u8> {
a.iter().zip(b.iter()).map(|(a,b)| a^b).collect()
}
#[cfg(test)]
mod tests {
use rand::OsRng;
use super::*;
#[test]
#[ignore]
fn can_get_p_and_q() {
let mut rng = OsRng::new().unwrap();
let e = UCN::from(65537 as u64);
for &(size, _) in ACCEPTABLE_KEY_SIZES.iter().take(3) {
let (p,q) = generate_pq(&mut rng, &e, size);
let minval = UCN::from(1 as u8) << ((size / 2) - 1);
assert!(p > minval);
assert!(q > minval);
assert!(p != q);
assert!(p.is_odd());
assert!(q.is_odd());
let phi = (p - UCN::from(1 as u64)) * (q - UCN::from(1 as u64));
let d = e.modinv(&phi);
assert_eq!( (&e * &d) % phi, UCN::from(1 as u64) );
}
}
}

38
src/rsa/errors.rs Normal file
View File

@@ -0,0 +1,38 @@
use simple_asn1::ASN1DecodeErr;
use std::io;
#[derive(Debug)]
pub enum RSAKeyGenError {
InvalidKeySize(usize), RngFailure(io::Error)
}
impl From<io::Error> for RSAKeyGenError {
fn from(e: io::Error) -> RSAKeyGenError {
RSAKeyGenError::RngFailure(e)
}
}
#[derive(Debug)]
pub enum RSAError {
BadMessageSize,
KeyTooSmallForHash,
DecryptionError,
DecryptHashMismatch,
InvalidKey,
RandomGenError(io::Error),
ASN1DecodeErr(ASN1DecodeErr)
}
impl From<io::Error> for RSAError {
fn from(e: io::Error) -> RSAError {
RSAError::RandomGenError(e)
}
}
impl From<ASN1DecodeErr> for RSAError {
fn from(e: ASN1DecodeErr) -> RSAError {
RSAError::ASN1DecodeErr(e)
}
}

208
src/rsa/gold_tests.rs Normal file
View File

@@ -0,0 +1,208 @@
use rsa::*;
use rsa::oaep::OAEPParams;
use sha1::Sha1;
use sha2::{Sha224,Sha256,Sha384,Sha512};
use testing::run_test;
fn get_signing_hash(s: usize) -> &'static SigningHash {
match s {
0x1 => &SIGNING_HASH_SHA1,
0x224 => &SIGNING_HASH_SHA224,
0x256 => &SIGNING_HASH_SHA256,
0x384 => &SIGNING_HASH_SHA384,
0x512 => &SIGNING_HASH_SHA512,
_ => panic!("Unacceptable hash")
}
}
#[test]
#[ignore]
fn rsa_signing_tests()
{
run_test("tests/rsa/signature.test", 7, |case| {
let (neg0, dbytes) = case.get("d").unwrap();
let (neg1, nbytes) = case.get("n").unwrap();
let (neg2, hbytes) = case.get("h").unwrap();
let (neg3, kbytes) = case.get("k").unwrap();
let (neg4, msg) = case.get("m").unwrap();
let (neg5, sig) = case.get("s").unwrap();
assert!(!neg0 & !neg1 & !neg2 & !neg3 & !neg4 & !neg5);
let hash = get_signing_hash(usize::from(UCN::from_bytes(hbytes)));
let size = usize::from(UCN::from_bytes(kbytes));
let key = RSAPrivate::new(UCN::from_bytes(nbytes),
UCN::from_bytes(dbytes));
assert!(size % 8 == 0);
assert_eq!(key.byte_len * 8, size);
let sig2 = key.sign(hash, &msg);
assert_eq!(*sig, sig2);
});
}
#[test]
fn rsa_verification_tests()
{
run_test("tests/rsa/signature.test", 7, |case| {
let (neg1, nbytes) = case.get("n").unwrap();
let (neg2, hbytes) = case.get("h").unwrap();
let (neg3, kbytes) = case.get("k").unwrap();
let (neg4, msg) = case.get("m").unwrap();
let (neg5, sig) = case.get("s").unwrap();
assert!(!neg1 & !neg2 & !neg3 & !neg4 & !neg5);
let hash = get_signing_hash(usize::from(UCN::from_bytes(hbytes)));
let size = usize::from(UCN::from_bytes(kbytes));
let key = RSAPublic::new(UCN::from_bytes(nbytes),
UCN::from(65537u64));
assert_eq!(key.byte_len * 8, size);
assert!(key.verify(hash, &msg, sig));
});
}
#[test]
fn rsa_decryption_tests()
{
run_test("tests/rsa/encryption.test", 6, |case| {
let (neg1, dbytes) = case.get("d").unwrap();
let (neg2, nbytes) = case.get("n").unwrap();
let (neg3, hbytes) = case.get("h").unwrap();
let (neg4, lbytes) = case.get("l").unwrap();
let (neg5, msg) = case.get("m").unwrap();
let (neg6, cphtxt) = case.get("c").unwrap();
assert!(!neg1 & !neg2 & !neg3 & !neg4 & !neg5 & !neg6);
let label = String::from_utf8(lbytes.clone()).unwrap();
let key = RSAPrivate::new(UCN::from_bytes(nbytes),
UCN::from_bytes(dbytes));
let wrapped = match usize::from(UCN::from_bytes(hbytes)) {
0x1 => key.decrypt(&OAEPParams::new(Sha1::default(), label),cphtxt),
0x224 => key.decrypt(&OAEPParams::new(Sha224::default(),label),cphtxt),
0x256 => key.decrypt(&OAEPParams::new(Sha256::default(),label),cphtxt),
0x384 => key.decrypt(&OAEPParams::new(Sha384::default(),label),cphtxt),
0x512 => key.decrypt(&OAEPParams::new(Sha512::default(),label),cphtxt),
_ => panic!("Unacceptable hash")
};
let mymsg = wrapped.unwrap();
assert_eq!(msg, &mymsg);
});
}
#[test]
#[ignore]
fn rsa_long_decryption_tests()
{
run_test("tests/rsa/encryption.ext.test", 6, |case| {
let (neg1, dbytes) = case.get("d").unwrap();
let (neg2, nbytes) = case.get("n").unwrap();
let (neg3, hbytes) = case.get("h").unwrap();
let (neg4, lbytes) = case.get("l").unwrap();
let (neg5, msg) = case.get("m").unwrap();
let (neg6, cphtxt) = case.get("c").unwrap();
assert!(!neg1 & !neg2 & !neg3 & !neg4 & !neg5 & !neg6);
let label = String::from_utf8(lbytes.clone()).unwrap();
let key = RSAPrivate::new(UCN::from_bytes(nbytes),
UCN::from_bytes(dbytes));
let wrapped = match usize::from(UCN::from_bytes(hbytes)) {
0x1 => key.decrypt(&OAEPParams::new(Sha1::default(), label),cphtxt),
0x224 => key.decrypt(&OAEPParams::new(Sha224::default(),label),cphtxt),
0x256 => key.decrypt(&OAEPParams::new(Sha256::default(),label),cphtxt),
0x384 => key.decrypt(&OAEPParams::new(Sha384::default(),label),cphtxt),
0x512 => key.decrypt(&OAEPParams::new(Sha512::default(),label),cphtxt),
_ => panic!("Unacceptable hash")
};
let mymsg = wrapped.unwrap();
assert_eq!(msg, &mymsg);
});
}
#[test]
fn rsa_encryption_tests()
{
run_test("tests/rsa/encryption.test", 6, |case| {
let (neg1, dbytes) = case.get("d").unwrap();
let (neg2, nbytes) = case.get("n").unwrap();
let (neg3, hbytes) = case.get("h").unwrap();
let (neg4, lbytes) = case.get("l").unwrap();
let (neg5, msg) = case.get("m").unwrap();
// This one's a little tricky, because there's randomness in the
// encryption phase. So we can't just encrypt and see if we get the
// same value. Instead, we just use this as a test vector to round
// trip, and trust that the decryption test above makes sure we're
// not going off into la la land.
assert!(!neg1 & !neg2 & !neg3 & !neg4 & !neg5);
let label = String::from_utf8(lbytes.clone()).unwrap();
let private = RSAPrivate::new(UCN::from_bytes(nbytes),
UCN::from_bytes(dbytes));
let public = RSAPublic::new(UCN::from_bytes(nbytes),
UCN::from(65537u64));
let wrappedc = match usize::from(UCN::from_bytes(hbytes)) {
0x1 => public.encrypt(&OAEPParams::new(Sha1::default(), label.clone()), &msg),
0x224 => public.encrypt(&OAEPParams::new(Sha224::default(),label.clone()), &msg),
0x256 => public.encrypt(&OAEPParams::new(Sha256::default(),label.clone()), &msg),
0x384 => public.encrypt(&OAEPParams::new(Sha384::default(),label.clone()), &msg),
0x512 => public.encrypt(&OAEPParams::new(Sha512::default(),label.clone()), &msg),
_ => panic!("Unacceptable hash")
};
let ciphertext = wrappedc.unwrap();
let wrappedm = match usize::from(UCN::from_bytes(hbytes)) {
0x1 => private.decrypt(&OAEPParams::new(Sha1::default(), label), &ciphertext),
0x224 => private.decrypt(&OAEPParams::new(Sha224::default(),label), &ciphertext),
0x256 => private.decrypt(&OAEPParams::new(Sha256::default(),label), &ciphertext),
0x384 => private.decrypt(&OAEPParams::new(Sha384::default(),label), &ciphertext),
0x512 => private.decrypt(&OAEPParams::new(Sha512::default(),label), &ciphertext),
_ => panic!("Unacceptable hash")
};
let message = wrappedm.unwrap();
assert_eq!(msg, &message);
});
}
#[test]
#[ignore]
fn rsa_long_encryption_tests()
{
run_test("tests/rsa/encryption.ext.test", 6, |case| {
let (neg1, dbytes) = case.get("d").unwrap();
let (neg2, nbytes) = case.get("n").unwrap();
let (neg3, hbytes) = case.get("h").unwrap();
let (neg4, lbytes) = case.get("l").unwrap();
let (neg5, msg) = case.get("m").unwrap();
// This one's a little tricky, because there's randomness in the
// encryption phase. So we can't just encrypt and see if we get the
// same value. Instead, we just use this as a test vector to round
// trip, and trust that the decryption test above makes sure we're
// not going off into la la land.
assert!(!neg1 & !neg2 & !neg3 & !neg4 & !neg5);
let label = String::from_utf8(lbytes.clone()).unwrap();
let private = RSAPrivate::new(UCN::from_bytes(nbytes),
UCN::from_bytes(dbytes));
let public = RSAPublic::new(UCN::from_bytes(nbytes),
UCN::from(65537u64));
let wrappedc = match usize::from(UCN::from_bytes(hbytes)) {
0x1 => public.encrypt(&OAEPParams::new(Sha1::default(), label.clone()), &msg),
0x224 => public.encrypt(&OAEPParams::new(Sha224::default(),label.clone()), &msg),
0x256 => public.encrypt(&OAEPParams::new(Sha256::default(),label.clone()), &msg),
0x384 => public.encrypt(&OAEPParams::new(Sha384::default(),label.clone()), &msg),
0x512 => public.encrypt(&OAEPParams::new(Sha512::default(),label.clone()), &msg),
_ => panic!("Unacceptable hash")
};
let ciphertext = wrappedc.unwrap();
let wrappedm = match usize::from(UCN::from_bytes(hbytes)) {
0x1 => private.decrypt(&OAEPParams::new(Sha1::default(), label), &ciphertext),
0x224 => private.decrypt(&OAEPParams::new(Sha224::default(),label), &ciphertext),
0x256 => private.decrypt(&OAEPParams::new(Sha256::default(),label), &ciphertext),
0x384 => private.decrypt(&OAEPParams::new(Sha384::default(),label), &ciphertext),
0x512 => private.decrypt(&OAEPParams::new(Sha512::default(),label), &ciphertext),
_ => panic!("Unacceptable hash")
};
let message = wrappedm.unwrap();
assert_eq!(msg, &message);
});
}

109
src/rsa/mod.rs Normal file
View File

@@ -0,0 +1,109 @@
mod core;
mod errors;
#[cfg(test)]
mod gold_tests;
mod oaep;
mod public;
mod private;
mod signing_hashes;
pub use self::public::RSAPublic;
pub use self::private::RSAPrivate;
pub use self::signing_hashes::{SigningHash,
SIGNING_HASH_NULL, SIGNING_HASH_SHA1,
SIGNING_HASH_SHA224, SIGNING_HASH_SHA256,
SIGNING_HASH_SHA384, SIGNING_HASH_SHA512};
use cryptonum::UCN;
use rand::{OsRng,Rng};
use self::core::{ACCEPTABLE_KEY_SIZES,generate_pq};
use self::errors::*;
#[derive(Clone,Debug)]
pub struct RSAKeyPair {
pub public: RSAPublic,
pub private: RSAPrivate
}
impl RSAKeyPair {
/// 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.
///
/// This routine will use `OsRng` for entropy. If you want to use your
/// own random number generator, use `generate_w_rng`.
pub fn generate(len_bits: usize) -> Result<RSAKeyPair,RSAKeyGenError> {
let mut rng = OsRng::new()?;
RSAKeyPair::generate_w_rng(&mut rng, len_bits)
}
/// 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, len_bits: usize)
-> Result<RSAKeyPair,RSAKeyGenError>
{
let e = UCN::from(65537 as u32);
for &(length, _) in ACCEPTABLE_KEY_SIZES.iter() {
if length == len_bits {
let (p, q) = generate_pq(rng, &e, len_bits);
let n = &p * &q;
let one = UCN::from(1 as u8);
let phi = (p - &one) * (q - &one);
let d = e.modinv(&phi);
let public_key = RSAPublic::new(n.clone(), e);
let private_key = RSAPrivate::new(n, d);
return Ok(RSAKeyPair{
private: private_key,
public: public_key
})
}
}
Err(RSAKeyGenError::InvalidKeySize(len_bits))
}
}
#[cfg(test)]
mod tests {
use quickcheck::{Arbitrary,Gen};
use rsa::core::{ep,dp,sp1,vp1};
use super::*;
const TEST_KEY_SIZES: [usize; 2] = [512, 1024];
impl Arbitrary for RSAKeyPair {
fn arbitrary<G: Gen>(g: &mut G) -> RSAKeyPair {
let size = g.choose(&TEST_KEY_SIZES).unwrap();
RSAKeyPair::generate_w_rng(g, *size).unwrap()
}
}
quickcheck! {
#[ignore]
fn rsa_ep_dp_inversion(kp: RSAKeyPair, n: UCN) -> bool {
let m = n.reduce(&kp.public.nu);
let ciphertext = ep(&kp.public.nu, &kp.public.e, &m);
let mprime = dp(&kp.private.nu, &kp.private.d, &ciphertext);
mprime == m
}
#[ignore]
fn rsa_sp_vp_inversion(kp: RSAKeyPair, n: UCN) -> bool {
let m = n.reduce(&kp.public.nu);
let sig = sp1(&kp.private.nu, &kp.private.d, &m);
let mprime = vp1(&kp.public.nu, &kp.public.e, &sig);
mprime == m
}
}
}

47
src/rsa/oaep.rs Normal file
View File

@@ -0,0 +1,47 @@
use cryptonum::UCN;
use digest::{FixedOutput,Input};
/// Parameters for OAEP encryption and decryption: a hash function to use as part of the message
/// generation function (MGF1, if you're curious), and any labels you want to include as part of
/// the encryption.
pub struct OAEPParams<H: Clone + Input + FixedOutput> {
pub hash: H,
pub label: String
}
impl<H: Clone + Input + FixedOutput> OAEPParams<H> {
pub fn new(hash: H, label: String)
-> OAEPParams<H>
{
OAEPParams { hash: hash, label: label }
}
pub fn hash_len(&self) -> usize {
self.hash.clone().fixed_result().as_slice().len()
}
pub fn hash(&self, input: &[u8]) -> Vec<u8> {
let mut digest = self.hash.clone();
digest.process(input);
digest.fixed_result().as_slice().to_vec()
}
pub fn mgf1(&self, input: &[u8], len: usize) -> Vec<u8> {
let mut res = Vec::with_capacity(len);
let mut counter = UCN::from(0u64);
let one = UCN::from(1u64);
while res.len() < len {
let c = counter.to_bytes(4);
let mut digest = self.hash.clone();
digest.process(input);
digest.process(&c);
let chunk = digest.fixed_result();
res.extend_from_slice(chunk.as_slice());
counter = counter + &one;
}
res.truncate(len);
res
}
}

149
src/rsa/private.rs Normal file
View File

@@ -0,0 +1,149 @@
use cryptonum::{BarrettUCN,UCN};
use digest::{FixedOutput,Input};
use rsa::core::{ACCEPTABLE_KEY_SIZES,dp,pkcs1_pad,sp1,xor_vecs};
use rsa::errors::RSAError;
use rsa::oaep::OAEPParams;
use rsa::signing_hashes::SigningHash;
use std::fmt;
#[derive(Clone)]
pub struct RSAPrivate {
pub(crate) byte_len: usize,
pub(crate) n: UCN,
pub(crate) nu: BarrettUCN,
pub(crate) d: UCN
}
#[cfg(test)]
impl fmt::Debug for RSAPrivate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("RSAPrivate")
.field("byte_len", &self.byte_len)
.field("n", &self.n)
.field("nu", &self.nu)
.field("d", &self.d)
.finish()
}
}
#[cfg(test)]
impl PartialEq for RSAPrivate {
fn eq(&self, rhs: &RSAPrivate) -> bool {
(self.byte_len == rhs.byte_len) &&
(self.n == rhs.n) &&
(self.nu == rhs.nu) &&
(self.d == rhs.d)
}
}
#[cfg(test)]
impl Eq for RSAPrivate {}
#[cfg(not(test))]
impl fmt::Debug for RSAPrivate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("RSAPrivateKey<PRIVATE>")
}
}
impl RSAPrivate {
/// Create a new RSA public key from the given components, which you found
/// via some other mechanism.
pub fn new(n: UCN, d: UCN) -> RSAPrivate {
let len = n.bits();
for &(valid_bits, _) in ACCEPTABLE_KEY_SIZES.iter() {
if valid_bits >= len {
return RSAPrivate {
byte_len: valid_bits / 8,
n: n.clone(),
nu: n.barrett_u(),
d: d.clone()
};
}
}
panic!("Invalid RSA key size in new()")
}
/// Sign a message using the given hash.
pub fn sign(&self, sighash: &SigningHash, msg: &[u8]) -> Vec<u8> {
let hash = (sighash.run)(msg);
let em = pkcs1_pad(&sighash.ident, &hash, self.byte_len);
let m = UCN::from_bytes(&em);
let s = sp1(&self.nu, &self.d, &m);
let sig = s.to_bytes(self.byte_len);
sig
}
/// Decrypt a message with the given parameters.
pub fn decrypt<H: Clone + Input + FixedOutput>(&self, oaep: &OAEPParams<H>, msg: &[u8])
-> Result<Vec<u8>,RSAError>
{
let mut res = Vec::new();
for chunk in msg.chunks(self.byte_len) {
let mut dchunk = self.oaep_decrypt(oaep, chunk)?;
res.append(&mut dchunk);
}
Ok(res)
}
fn oaep_decrypt<H: Clone + Input + FixedOutput>(&self, oaep: &OAEPParams<H>, c: &[u8])
-> Result<Vec<u8>,RSAError>
{
// Step 1b
if c.len() != self.byte_len {
return Err(RSAError::DecryptionError);
}
// Step 1c
if self.byte_len < ((2 * oaep.hash_len()) + 2) {
return Err(RSAError::DecryptHashMismatch);
}
// Step 2a
let c_ip = UCN::from_bytes(&c.to_vec());
// Step 2b
let m_ip = dp(&self.nu, &self.d, &c_ip);
// Step 2c
let em = &m_ip.to_bytes(self.byte_len);
// Step 3a
let l_hash = oaep.hash(oaep.label.as_bytes());
// Step 3b
let (y, rest) = em.split_at(1);
let (masked_seed, masked_db) = rest.split_at(oaep.hash_len());
// Step 3c
let seed_mask = oaep.mgf1(masked_db, oaep.hash_len());
// Step 3d
let seed = xor_vecs(&masked_seed.to_vec(), &seed_mask);
// Step 3e
let db_mask = oaep.mgf1(&seed, self.byte_len - oaep.hash_len() - 1);
// Step 3f
let db = xor_vecs(&masked_db.to_vec(), &db_mask);
// Step 3g
let (l_hash2, ps_o_m) = db.split_at(oaep.hash_len());
let o_m = drop0s(ps_o_m);
let (o, m) = o_m.split_at(1);
// Checks!
if o != [1] {
return Err(RSAError::DecryptionError);
}
if l_hash != l_hash2 {
return Err(RSAError::DecryptionError);
}
if y != [0] {
return Err(RSAError::DecryptionError);
}
Ok(m.to_vec())
}
}
fn drop0s(a: &[u8]) -> &[u8] {
let mut idx = 0;
while (idx < a.len()) && (a[idx] == 0) {
idx = idx + 1;
}
&a[idx..]
}

124
src/rsa/public.rs Normal file
View File

@@ -0,0 +1,124 @@
use cryptonum::{BarrettUCN,UCN};
use digest::{FixedOutput,Input};
use rand::{OsRng,Rng};
use rsa::core::{ACCEPTABLE_KEY_SIZES,ep,pkcs1_pad,vp1,xor_vecs};
use rsa::errors::RSAError;
use rsa::oaep::OAEPParams;
use rsa::signing_hashes::SigningHash;
#[derive(Clone,Debug,PartialEq,Eq)]
pub struct RSAPublic {
pub(crate) byte_len: usize,
pub(crate) n: UCN,
pub(crate) nu: BarrettUCN,
pub(crate) e: UCN
}
impl RSAPublic {
/// Create a new RSA public key from the given components, which you found
/// via some other mechanism.
pub fn new(n: UCN, e: UCN) -> RSAPublic {
let len = n.bits();
for &(valid_bits, _) in ACCEPTABLE_KEY_SIZES.iter() {
if valid_bits >= len {
return RSAPublic{
byte_len: valid_bits / 8,
n: n.clone(),
nu: n.barrett_u(),
e: e.clone()
};
}
}
panic!("Invalid RSA key size in new()")
}
/// Verify the signature for a given message, using the given signing hash,
/// returning true iff the signature validates.
pub fn verify(&self, shash: &SigningHash, msg: &[u8], sig: &[u8]) -> bool {
let hash = (shash.run)(msg);
let s = UCN::from_bytes(&sig);
let m = vp1(&self.nu, &self.e, &s);
let em = m.to_bytes(self.byte_len);
let em_ = pkcs1_pad(&shash.ident, &hash, self.byte_len);
(em == em_)
}
/// Encrypt the given data with the public key and parameters, returning
/// the encrypted blob or an error encountered during encryption.
///
/// OAEP encoding is used for this process, which requires a random number
/// generator. This version of the function uses `OsRng`. If you want to
/// use your own RNG, use `encrypt_w_rng`.
pub fn encrypt<H:Clone + Input + FixedOutput>(&self, oaep: &OAEPParams<H>, msg: &[u8])
-> Result<Vec<u8>,RSAError>
{
let mut g = OsRng::new()?;
self.encrypt_with_rng(&mut g, oaep, msg)
}
/// Encrypt the given data with the public key and parameters, returning
/// the encrypted blob or an error encountered during encryption. This
/// version also allows you to provide your own RNG, if you really feel
/// like shooting yourself in the foot.
pub fn encrypt_with_rng<G,H>(&self, g: &mut G, oaep: &OAEPParams<H>, msg: &[u8])
-> Result<Vec<u8>,RSAError>
where G: Rng, H: Clone + Input + FixedOutput
{
if self.byte_len <= ((2 * oaep.hash_len()) + 2) {
return Err(RSAError::KeyTooSmallForHash);
}
let mut res = Vec::new();
for chunk in msg.chunks(self.byte_len - (2 * oaep.hash_len()) - 2) {
let mut newchunk = self.oaep_encrypt(g, oaep, chunk)?;
res.append(&mut newchunk)
}
Ok(res)
}
fn oaep_encrypt<G: Rng,H:Clone + Input + FixedOutput>(&self, g: &mut G, oaep: &OAEPParams<H>, msg: &[u8])
-> Result<Vec<u8>,RSAError>
{
// Step 1b
if msg.len() > (self.byte_len - (2 * oaep.hash_len()) - 2) {
return Err(RSAError::BadMessageSize)
}
// Step 2a
let mut lhash = oaep.hash(oaep.label.as_bytes());
// Step 2b
let num0s = self.byte_len - msg.len() - (2 * oaep.hash_len()) - 2;
let mut ps = Vec::new();
ps.resize(num0s, 0);
// Step 2c
let mut db = Vec::new();
db.append(&mut lhash);
db.append(&mut ps);
db.push(1);
db.extend_from_slice(msg);
// Step 2d
let seed : Vec<u8> = g.gen_iter().take(oaep.hash_len()).collect();
// Step 2e
let db_mask = oaep.mgf1(&seed, self.byte_len - oaep.hash_len() - 1);
// Step 2f
let mut masked_db = xor_vecs(&db, &db_mask);
// Step 2g
let seed_mask = oaep.mgf1(&masked_db, oaep.hash_len());
// Step 2h
let mut masked_seed = xor_vecs(&seed, &seed_mask);
// Step 2i
let mut em = Vec::new();
em.push(0);
em.append(&mut masked_seed);
em.append(&mut masked_db);
// Step 3a
let m_i = UCN::from_bytes(&em);
// Step 3b
let c_i = ep(&self.nu, &self.e, &m_i);
// Step 3c
let c = c_i.to_bytes(self.byte_len);
Ok(c)
}
}

119
src/rsa/signing_hashes.rs Normal file
View File

@@ -0,0 +1,119 @@
use digest::{FixedOutput,Input};
use sha1::Sha1;
use sha2::{Sha224,Sha256,Sha384,Sha512};
use std::fmt;
/// A hash that can be used to sign a message.
#[derive(Clone)]
pub struct SigningHash {
/// The name of this hash (only used for display purposes)
pub name: &'static str,
/// The approved identity string for the hash.
pub ident: &'static [u8],
/// The hash
pub run: fn(&[u8]) -> Vec<u8>
}
impl fmt::Debug for SigningHash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
/// The "null" signing hash. This signing hash has no identity, and will
/// simply pass the data through unhashed. You really should know what
/// you're doing if you use this, and probably using a somewhat strange
/// signing protocol. There's no good reason to use this in new code
/// for a new protocol or system.
pub static SIGNING_HASH_NULL: SigningHash = SigningHash {
name: "NULL",
ident: &[],
run: nohash
};
fn nohash(i: &[u8]) -> Vec<u8> {
i.to_vec()
}
/// Sign a hash based on SHA1. You shouldn't use this unless you're using
/// very small keys, and this is the only one available to you. Even then,
/// why are you using such small keys?!
pub static SIGNING_HASH_SHA1: SigningHash = SigningHash {
name: "SHA1",
ident: &[0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,
0x02,0x1a,0x05,0x00,0x04,0x14],
run: runsha1
};
fn runsha1(i: &[u8]) -> Vec<u8> {
let mut d = Sha1::default();
d.process(i);
d.fixed_result().as_slice().to_vec()
}
/// Sign a hash based on SHA2-224. This is the first reasonable choice
/// we've come across, and is useful when you have smaller RSA key sizes.
/// I wouldn't recommend it, though.
pub static SIGNING_HASH_SHA224: SigningHash = SigningHash {
name: "SHA224",
ident: &[0x30,0x2d,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,
0x01,0x65,0x03,0x04,0x02,0x04,0x05,0x00,0x04,
0x1c],
run: runsha224
};
fn runsha224(i: &[u8]) -> Vec<u8> {
let mut d = Sha224::default();
d.process(i);
d.fixed_result().as_slice().to_vec()
}
/// Sign a hash based on SHA2-256. The first one I'd recommend!
pub static SIGNING_HASH_SHA256: SigningHash = SigningHash {
name: "SHA256",
ident: &[0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,
0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,
0x20],
run: runsha256
};
fn runsha256(i: &[u8]) -> Vec<u8> {
let mut d = Sha256::default();
d.process(i);
d.fixed_result().as_slice().to_vec()
}
/// Sign a hash based on SHA2-384. Approximately 50% better than
/// SHA-256.
pub static SIGNING_HASH_SHA384: SigningHash = SigningHash {
name: "SHA384",
ident: &[0x30,0x41,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,
0x01,0x65,0x03,0x04,0x02,0x02,0x05,0x00,0x04,
0x30],
run: runsha384
};
fn runsha384(i: &[u8]) -> Vec<u8> {
let mut d = Sha384::default();
d.process(i);
d.fixed_result().as_slice().to_vec()
}
/// Sign a hash based on SHA2-512. At this point, you're getting a bit
/// silly. But if you want to through 8kbit RSA keys with a 512 bit SHA2
/// signing hash, we're totally behind you.
pub static SIGNING_HASH_SHA512: SigningHash = SigningHash {
name: "SHA512",
ident: &[0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,
0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,
0x40],
run: runsha512
};
fn runsha512(i: &[u8]) -> Vec<u8> {
let mut d = Sha512::default();
d.process(i);
d.fixed_result().as_slice().to_vec()
}

88
src/testing.rs Normal file
View File

@@ -0,0 +1,88 @@
use cryptonum::{SCN,UCN};
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::str::Lines;
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_left_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 make_signed(m: HashMap<String,(bool,Vec<u8>)>) -> HashMap<String,SCN>
{
let mut res: HashMap<String,SCN> = HashMap::new();
for (key, (neg, bval)) in m.iter() {
let base = UCN::from_bytes(bval);
let val = SCN{ negative: *neg, value: base };
res.insert(key.clone(), val);
}
res
}
pub fn make_unsigned(m: HashMap<String,(bool,Vec<u8>)>) -> HashMap<String,UCN>
{
let mut res: HashMap<String,UCN> = HashMap::new();
for (key, (neg, bval)) in m.iter() {
assert!(!neg);
res.insert(key.clone(), UCN::from_bytes(bval));
}
res
}
pub fn run_test<F>(fname: &'static str, 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);
}
}

225
tests/dsa/Generator.java Normal file
View File

@@ -0,0 +1,225 @@
// Just because I always forget Java compilation:
// javac Generator.java -cp bcprov-ext-jdk15on-159.jar
// java -cp "bcprov-ext-jdk15on-159.jar:." Generator
// Also, go here:
// https://www.bouncycastle.org/latest_releases.html
//
import java.io.FileWriter;
import java.io.IOException;
import java.lang.InterruptedException;
import java.lang.Math;
import java.lang.Thread;
import java.math.BigInteger;
import java.security.SecureRandom;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.digests.SHA224Digest;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.digests.SHA384Digest;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.generators.DSAKeyPairGenerator;
import org.bouncycastle.crypto.generators.DSAParametersGenerator;
import org.bouncycastle.crypto.params.DSAKeyGenerationParameters;
import org.bouncycastle.crypto.params.DSAParameterGenerationParameters;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.signers.DSASigner;
import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
class Generator {
private FileWriter out;
private SecureRandom rng;
final static int NUM_THREADS = 4;
public Generator(SecureRandom r, FileWriter o) {
rng = r;
out = o;
}
public void runTests(int lsize, int nsize, int count)
throws IOException, InterruptedException
{
Thread threads[] = new Thread[NUM_THREADS];
System.out.print("Generating L" + lsize + "N" + nsize + " tests ");
for(int i = 0; i < NUM_THREADS; i++) {
Runner runner = new Runner(lsize, nsize, count / NUM_THREADS, this);
Thread runThread = new Thread(runner);
runThread.start();
threads[i] = runThread;
}
for(Thread thread : threads) {
thread.join();
}
System.out.println(" done.");
}
public synchronized void output(DSAParameters params,
AsymmetricCipherKeyPair kp,
int digestsize,
byte[] message,
BigInteger[] rs)
throws IOException
{
DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic();
DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate();
out.write("p: " + params.getP().toString(16) + "\n");
out.write("q: " + params.getQ().toString(16) + "\n");
out.write("g: " + params.getG().toString(16) + "\n");
out.write("x: " + priv.getX().toString(16) + "\n");
out.write("y: " + pub.getY().toString(16) + "\n");
out.write("h: " + digestsize + "\n");
out.write("m: " + asHex(message) + "\n");
out.write("r: " + rs[0].toString(16) + "\n");
out.write("s: " + rs[1].toString(16) + "\n");
out.flush();
System.out.print(".");
System.out.flush();
}
private Digest appropriateDigest(int nsize)
throws IOException
{
switch(nsize) {
case 1: return new SHA1Digest();
case 160: return new SHA1Digest();
case 224: return new SHA224Digest();
case 256: return new SHA256Digest();
case 384: return new SHA384Digest();
case 512: return new SHA512Digest();
default:
throw new IOException("Bad digest size!");
}
}
private int randomDigestSize()
throws IOException
{
switch(getRandomChoice(5)) {
case 0: return 1;
case 1: return 224;
case 2: return 256;
case 3: return 384;
case 4: return 512;
default:
throw new IOException("The world broke.");
}
}
private int getRandomChoice(int modulus) {
byte randoms[] = new byte[2];
rng.nextBytes(randoms);
int random = ((int)randoms[0] << 8) + ((int)randoms[1]);
return (Math.abs(random) % modulus);
}
private String asHex(byte[] data) {
String result = "";
for(byte value : data) {
result = result + String.format("%02x", value);
}
return result;
}
public static void main(String[] args)
throws IOException, InterruptedException
{
SecureRandom rng = new SecureRandom();
FileWriter outfile = new FileWriter("signature.test", false);
Generator gen = new Generator(rng, outfile);
gen.runTests(1024, 160, 500);
gen.runTests(2048, 224, 500);
gen.runTests(2048, 256, 250);
gen.runTests(3072, 256, 100);
}
private class Runner implements Runnable {
private int lsize;
private int nsize;
private int count;
private Generator parent;
public Runner(int lsize, int nsize, int count, Generator parent)
{
this.lsize = lsize;
this.nsize = nsize;
this.count = count;
this.parent = parent;
}
public void run()
{
for(int i = 0; i < count; i++) {
runTest();
}
}
private DSAParameters getParameters()
throws IOException
{
DSAParameterGenerationParameters genparams =
new DSAParameterGenerationParameters(lsize, nsize, 80, rng);
DSAParametersGenerator gen =
new DSAParametersGenerator(parent.appropriateDigest(nsize));
gen.init(genparams);
return gen.generateParameters();
}
private AsymmetricCipherKeyPair getKeyPair(DSAParameters params)
{
DSAKeyGenerationParameters dsakeygenparams =
new DSAKeyGenerationParameters(rng, params);
DSAKeyPairGenerator keygen = new DSAKeyPairGenerator();
keygen.init(dsakeygenparams);
return keygen.generateKeyPair();
}
private byte[] getMessage()
{
int msgsize = getRandomChoice(1024);
byte message[] = new byte[msgsize];
rng.nextBytes(message);
return message;
}
private byte[] runHash(byte[] msg, int digestsize)
throws IOException
{
Digest digestfn = appropriateDigest(digestsize);
digestfn.update(msg, 0, msg.length);
byte result[] = new byte[digestfn.getDigestSize()];
digestfn.doFinal(result, 0);
return result;
}
private void runTest()
{
try {
DSAParameters params = getParameters();
AsymmetricCipherKeyPair kp = getKeyPair(params);
DSAPublicKeyParameters pub = (DSAPublicKeyParameters)kp.getPublic();
DSAPrivateKeyParameters priv = (DSAPrivateKeyParameters)kp.getPrivate();
byte message[] = getMessage();
int digestsize = randomDigestSize();
byte hash[] = runHash(message, digestsize);
Digest msgdigest = appropriateDigest(digestsize);
HMacDSAKCalculator kgen = new HMacDSAKCalculator(msgdigest);
DSASigner signer = new DSASigner(kgen);
signer.init(true, priv);
BigInteger rs[] = signer.generateSignature(hash);
parent.output(params, kp, digestsize, message, rs);
} catch(IOException exc) {
System.out.println("EXCEPTION!");
run();
}
}
}
}

12132
tests/dsa/signature.test Normal file

File diff suppressed because it is too large Load Diff

318
tests/ecdsa/Generator.java Normal file
View File

@@ -0,0 +1,318 @@
// Just because I always forget Java compilation:
// javac Generator.java -cp bcprov-ext-jdk15on-159.jar
// java -cp "bcprov-ext-jdk15on-159.jar:." Generator
// Also, go here:
// https://www.bouncycastle.org/latest_releases.html
//
import java.io.FileWriter;
import java.io.IOException;
import java.lang.InterruptedException;
import java.lang.Math;
import java.lang.Thread;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.digests.SHA224Digest;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.digests.SHA384Digest;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
import org.bouncycastle.math.ec.ECAlgorithms;
import org.bouncycastle.math.ec.ECPoint;
class Generator {
final static int COUNT = 500;
public static void main(String[] args)
throws IOException, InterruptedException
{
new Generator().run();
}
public Generator() { }
public void run()
throws IOException, InterruptedException
{
OutFiles outfiles = new OutFiles();
String[] curves = { "P-192", "P-224", "P-256", "P-384", "P-521" };
ArrayList<Thread> threads = new ArrayList<Thread>();
System.out.print("Generating: ");
for(String curve : curves) {
X9ECParameters params = NISTNamedCurves.getByName(curve);
ECDomainParameters dp = new ECDomainParameters(params.getCurve(),
params.getG(),
params.getN());
Runner runner = new Runner(outfiles, dp);
Thread thread = new Thread(runner);
thread.start();
threads.add(thread);
}
for(Thread thread: threads) {
thread.join();
}
System.out.println(" done.");
}
public class OutFiles {
public FileWriter negate;
public FileWriter dble;
public FileWriter add;
public FileWriter mul;
public FileWriter add2mul;
public FileWriter sig;
public OutFiles()
{
try {
negate = new FileWriter("ec_negate.test", false);
dble = new FileWriter("ec_dble.test", false);
add = new FileWriter("ec_add.test", false);
mul = new FileWriter("ec_mul.test", false);
add2mul = new FileWriter("ec_add2mul.test", false);
sig = new FileWriter("signature.test", false);
} catch(IOException e) {
System.out.println("Blech: " + e);
}
}
public synchronized void dump(FileWriter file, Map<String,String> x) {
try {
for(Map.Entry<String,String> entry : x.entrySet()) {
file.write(entry.getKey());
file.write(": ");
file.write(entry.getValue());
file.write("\n");
file.flush();
if(file == negate) { System.out.print("N"); };
if(file == dble) { System.out.print("D"); };
if(file == add) { System.out.print("A"); };
if(file == mul) { System.out.print("M"); };
if(file == add2mul) { System.out.print("2"); };
if(file == sig) { System.out.print("S"); };
System.out.flush();
}
} catch(IOException e) {
System.out.println("Argh: " + e);
}
}
}
public class Runner implements Runnable {
private OutFiles outfiles;
private SecureRandom rng;
private ECDomainParameters ecparams;
private BigInteger two;
public Runner(OutFiles outfiles, ECDomainParameters params) {
this.outfiles = outfiles;
this.ecparams = params;
this.rng = new SecureRandom();
this.two = BigInteger.valueOf(2);
}
public void run() {
for(int i = 0; i < COUNT; i++) { generateNegateTests(); }
for(int i = 0; i < COUNT; i++) { generateDoubleTests(); }
for(int i = 0; i < COUNT; i++) { generateAddTests(); }
for(int i = 0; i < COUNT; i++) { generateMulTests(); }
for(int i = 0; i < COUNT; i++) { generateAdd2MulTests(); }
for(int i = 0; i < COUNT; i++) { generateSignatureTests(); }
}
public void generateNegateTests() {
ECPoint p = getPoint();
ECPoint n = p.negate().normalize();
HashMap<String,String> m = new HashMap<String,String>();
m.put("c", Integer.toString(ecparams.getN().bitLength()));
m.put("x", p.getAffineXCoord().toBigInteger().toString(16));
m.put("y", p.getAffineYCoord().toBigInteger().toString(16));
m.put("a", n.getAffineXCoord().toBigInteger().toString(16));
m.put("b", n.getAffineYCoord().toBigInteger().toString(16));
outfiles.dump(outfiles.negate, m);
}
public void generateDoubleTests() {
ECPoint p = getPoint();
ECPoint n = p.twice().normalize();
HashMap<String,String> m = new HashMap<String,String>();
m.put("c", Integer.toString(ecparams.getN().bitLength()));
m.put("x", p.getAffineXCoord().toBigInteger().toString(16));
m.put("y", p.getAffineYCoord().toBigInteger().toString(16));
m.put("a", n.getAffineXCoord().toBigInteger().toString(16));
m.put("b", n.getAffineYCoord().toBigInteger().toString(16));
outfiles.dump(outfiles.dble, m);
}
public void generateAddTests() {
ECPoint p1 = getPoint();
ECPoint p2 = getPoint();
ECPoint q = p1.add(p2).normalize();
HashMap<String,String> m = new HashMap<String,String>();
m.put("c", Integer.toString(ecparams.getN().bitLength()));
m.put("x", p1.getAffineXCoord().toBigInteger().toString(16));
m.put("y", p1.getAffineYCoord().toBigInteger().toString(16));
m.put("q", p2.getAffineXCoord().toBigInteger().toString(16));
m.put("r", p2.getAffineYCoord().toBigInteger().toString(16));
m.put("a", q.getAffineXCoord().toBigInteger().toString(16));
m.put("b", q.getAffineYCoord().toBigInteger().toString(16));
outfiles.dump(outfiles.add, m);
}
public void generateMulTests() {
ECPoint p = getPoint();
BigInteger k = getConstant();
ECPoint q = p.multiply(k).normalize();
HashMap<String,String> m = new HashMap<String,String>();
m.put("c", Integer.toString(ecparams.getN().bitLength()));
m.put("x", p.getAffineXCoord().toBigInteger().toString(16));
m.put("y", p.getAffineYCoord().toBigInteger().toString(16));
m.put("k", k.toString(16));
m.put("a", q.getAffineXCoord().toBigInteger().toString(16));
m.put("b", q.getAffineYCoord().toBigInteger().toString(16));
outfiles.dump(outfiles.mul, m);
}
public void generateAdd2MulTests() {
ECPoint p = getPoint();
BigInteger a = getConstant();
ECPoint q = getPoint();
BigInteger b = getConstant();
ECPoint r = ECAlgorithms.sumOfTwoMultiplies(p,a,q,b).normalize();
HashMap<String,String> m = new HashMap<String,String>();
m.put("c", Integer.toString(ecparams.getN().bitLength()));
m.put("x", p.getAffineXCoord().toBigInteger().toString(16));
m.put("y", p.getAffineYCoord().toBigInteger().toString(16));
m.put("a", a.toString(16));
m.put("q", q.getAffineXCoord().toBigInteger().toString(16));
m.put("r", q.getAffineYCoord().toBigInteger().toString(16));
m.put("b", b.toString(16));
m.put("s", r.getAffineXCoord().toBigInteger().toString(16));
m.put("t", r.getAffineYCoord().toBigInteger().toString(16));
outfiles.dump(outfiles.add2mul, m);
}
public void generateSignatureTests() {
AsymmetricCipherKeyPair kp = getKeyPair();
ECPublicKeyParameters pub = (ECPublicKeyParameters)kp.getPublic();
ECPrivateKeyParameters priv = (ECPrivateKeyParameters)kp.getPrivate();
byte message[] = getMessage();
int digestsize = getDigestSize();
byte hash[] = runHash(message, digestsize);
Digest msgdigest = getHash(digestsize);
HMacDSAKCalculator kgen = new HMacDSAKCalculator(msgdigest);
ECDSASigner signer = new ECDSASigner(kgen);
signer.init(true, priv);
BigInteger rs[] = signer.generateSignature(hash);
HashMap<String,String> m = new HashMap<String,String>();
m.put("c", Integer.toString(ecparams.getN().bitLength()));
m.put("x", pub.getQ().getAffineXCoord().toBigInteger().toString(16));
m.put("y", pub.getQ().getAffineYCoord().toBigInteger().toString(16));
m.put("d", priv.getD().toString(16));
m.put("h", Integer.toString(digestsize));
m.put("m", asHex(message));
m.put("r", rs[0].toString(16));
m.put("s", rs[1].toString(16));
outfiles.dump(outfiles.sig, m);
}
private byte[] runHash(byte[] msg, int size) {
Digest digestfn = getHash(size);
digestfn.update(msg, 0, msg.length);
byte result[] = new byte[digestfn.getDigestSize()];
digestfn.doFinal(result, 0);
return result;
}
private AsymmetricCipherKeyPair getKeyPair() {
ECKeyGenerationParameters params =
new ECKeyGenerationParameters(ecparams, rng);
ECKeyPairGenerator keygen = new ECKeyPairGenerator();
keygen.init(params);
return keygen.generateKeyPair();
}
private byte[] getMessage() {
int msgsize = rng.nextInt(1024);
byte message[] = new byte[msgsize];
rng.nextBytes(message);
return message;
}
private ECPoint getPoint() {
BigInteger k = getConstant();
return ecparams.getG().multiply(k).normalize();
}
private BigInteger getConstant() {
BigInteger n = ecparams.getN();
int nBitLength = n.bitLength();
for(;;) {
BigInteger d = new BigInteger(nBitLength, rng);
if(d.compareTo(two) < 0 || (d.compareTo(n) >= 0)) {
continue;
}
return d;
}
}
private int getDigestSize() {
switch(rng.nextInt(5)) {
case 0: return 1;
case 1: return 224;
case 2: return 256;
case 3: return 384;
case 4: return 512;
default:
return 999;
}
}
private Digest getHash(int nsize) {
switch(nsize) {
case 1: return new SHA1Digest();
case 224: return new SHA1Digest();
case 256: return new SHA1Digest();
case 384: return new SHA1Digest();
case 512: return new SHA1Digest();
default:
return null;
}
}
private String asHex(byte[] data) {
String result = "";
for(byte value : data) {
result = result + String.format("%02x", value);
}
return result;
}
}
}

17500
tests/ecdsa/ec_add.test Normal file

File diff suppressed because it is too large Load Diff

22500
tests/ecdsa/ec_add2mul.test Normal file

File diff suppressed because it is too large Load Diff

12500
tests/ecdsa/ec_dble.test Normal file

File diff suppressed because it is too large Load Diff

15000
tests/ecdsa/ec_mul.test Normal file

File diff suppressed because it is too large Load Diff

12500
tests/ecdsa/ec_negate.test Normal file

File diff suppressed because it is too large Load Diff

20000
tests/ecdsa/signature.test Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,216 @@
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad
import Data.Bits
import Data.List
import qualified Data.Map.Strict as Map
import GHC.Integer.GMP.Internals
import Numeric
import System.IO
import System.Random
import Debug.Trace
type Generator a = StdGen -> a -> (Maybe [(String, Integer)], a, StdGen)
iterations :: Int
iterations = 5000
maxSize :: Int
maxSize = 8
randomVal :: (Integer -> Bool) -> StdGen -> (Integer, StdGen)
randomVal filter g =
let (mySize, g') = randomR (1, maxSize) g
(possible, g'') = go g' mySize
in if filter possible
then (possible, g'')
else randomVal filter g''
where
go rng 0 = (0, rng)
go rng i =
let (other, rng') = go rng (i - 1)
(self, rng'') = random rng'
in ((other `shiftL` 64) + self, rng'')
buildBasicGenerator :: (Integer -> Bool) ->
(Integer -> Integer -> Maybe Integer) ->
Generator ()
buildBasicGenerator filter f g () =
let (x, g') = randomVal filter g
(y, g'') = randomVal filter g'
in case f x y of
Nothing ->
(Nothing, (), g'')
Just z ->
(Just [("x", x), ("y", y), ("z", z)], (), g'')
buildBasicLimitingGenerator :: (Integer -> Bool) ->
(Integer -> Integer -> Maybe Integer) ->
Generator (Map.Map Integer Int)
buildBasicLimitingGenerator filter f g m =
let (x, g') = randomVal filter g
(y, g'') = randomVal filter g'
in case f x y of
Nothing -> (Nothing, m, g'')
Just z ->
case Map.lookup z m of
Nothing ->
(Just [("x",x),("y",y),("z",z)], Map.insert z 1 m, g'')
Just c | c >= 100 ->
(Nothing, m, g'')
Just c ->
(Just [("x",x),("y",y),("z",z)], Map.insert z (c + 1) m, g'')
buildBasicAccGenerator :: (Integer -> Bool) ->
(Integer -> Integer -> a -> Maybe (Integer, a)) ->
Generator a
buildBasicAccGenerator filter f g acc =
let (x, g') = randomVal filter g
(y, g'') = randomVal filter g'
in case f x y acc of
Nothing ->
(Nothing, acc, g'')
Just (z, acc') ->
(Just [("x", x), ("y", y), ("z", z)], acc', g'')
runGenerator :: forall a. StdGen -> String -> a -> Generator a -> IO StdGen
runGenerator g filename initVal generator =
withFile (filename ++ ".tests") WriteMode $ \ hndl ->
do putStrLn ("Generating " ++ filename ++ ".tests")
go hndl g initVal iterations
where
go :: Handle -> StdGen -> a -> Int -> IO StdGen
go _ g _ 0 = return g
go hndl g acc iterations =
case generator g acc of
(Nothing, acc', g') ->
go hndl g' acc' iterations
(Just res, acc', g') ->
do let sorted = sort res
forM_ sorted $ \ (key, val) ->
do let neg = if val < 0 then "-" else ""
val' = abs val
hPutStrLn hndl (key ++ ": " ++ neg ++ showHex val' "")
go hndl g' acc' (iterations - 1)
main :: IO ()
main =
do g0 <- newStdGen
g1 <- runGenerator g0 "unsigned_add" () $
buildBasicGenerator (>= 0) $ \ a b -> Just (a + b)
g2 <- runGenerator g1 "signed_add" () $
buildBasicGenerator (const True) $ \ a b -> Just (a + b)
g3 <- runGenerator g2 "unsigned_sub" () $
buildBasicGenerator (>= 0) $ \ a b ->
if a >= b then Just (a - b) else Nothing
g4 <- runGenerator g3 "signed_sub" () $
buildBasicGenerator (const True) $ \ a b -> Just (a - b)
g5 <- runGenerator g4 "unsigned_mul" () $
buildBasicGenerator (>= 0) $ \ a b -> Just (a * b)
g6 <- runGenerator g5 "signed_mul" () $
buildBasicGenerator (const True) $ \ a b -> Just (a * b)
g7 <- runGenerator g6 "unsigned_div" Map.empty $
buildBasicLimitingGenerator (>= 0) $ \ a b ->
if b == 0 then Nothing else Just (a `div` b)
g8 <- runGenerator g7 "signed_div" Map.empty $
buildBasicLimitingGenerator (const True) $ \ a b ->
if b == 0 then Nothing else Just (a `div` b)
g7 <- runGenerator g6 "unsigned_mod" 0 $
buildBasicAccGenerator (>= 0) $ \ a b i ->
case a `mod` b of
_ | b == 0 -> Nothing
x | (a == x) && (i == 100) -> Nothing
x | a == x -> Just (x, i + 1)
x -> Just (x, i)
g8 <- runGenerator g7 "signed_mod" 0 $
buildBasicAccGenerator (const True) $ \ a b i ->
case a `mod` b of
_ | b == 0 -> Nothing
x | (a == x) && (i == 100) -> Nothing
x | a == x -> Just (x, i + 1)
x -> Just (x, i)
g9 <- runGenerator g8 "modexp" () $ \ g () ->
let (a, g') = randomVal (>= 0) g
(b, g'') = randomVal (>= 0) g'
(m, g''') = randomVal (>= 0) g''
z = powModInteger a b m
res = [("a",a),("b",b),("m",m),("z",z)]
in if m == 0
then (Nothing, (), g''')
else (Just res, (), g''')
g10<- runGenerator g9 "barrett" () $ \ g () ->
let (m, g') = randomVal (>= 0) g
(v, g'') = randomVal (>= 0) g'
barrett = barrett_u m
vk = computeK v
in if vk > (2 * (bk barrett))
then (Nothing, (), g'')
else let me = reduce v barrett
standard = v `mod` m
res = [("m", m), ("v", v), ("r", me),
("u", bu barrett), ("k", fromIntegral (bk barrett))]
in if me /= standard
then error "Barrett broken"
else (Just res, (), g'')
g11<- runGenerator g10 "fastmodexp" () $ \ g () ->
let (a, g') = randomVal (>= 0) g
(b, g'') = randomVal (>= 0) g'
(m, g''') = randomVal (>= 0) g'
z = powModInteger a b m
barrett = barrett_u m
ak = computeK a
in if ak > bk barrett
then (Nothing, (), g''')
else let res = [("a", a), ("b", b), ("z", z),
("m", m), ("u", bu barrett),
("k", fromIntegral (bk barrett))]
in (Just res, (), g''')
_ <- runGenerator g11 "modinv" Map.empty $
buildBasicLimitingGenerator (>= 0) $ \ a b ->
let res == recipModInteger a b
if b == 0 then Nothing else Just (recipModInteger a b)
return ()
-- Implement Barrett reduction using incredibly simplistic implementations, to
-- be sure we got it right.
--
b :: Integer
b = 2 ^ 64
computeK :: Integer -> Int
computeK v = go 0 1
where
go k acc
| v < acc = k
| otherwise = go (k + 1) (acc * b)
data Barrett = Barrett { bm :: Integer, bu :: Integer, bk :: Int }
deriving (Show)
barrett_u :: Integer -> Barrett
barrett_u x = Barrett {
bm = x,
bu = (b ^ (2 * k)) `div` x,
bk = k
}
where k = computeK x
reduce :: Integer -> Barrett -> Integer
reduce x barrett = result
where
k = bk barrett
u = bu barrett
m = bm barrett
--
q1 = x `div` (b ^ (k - 1))
q2 = q1 * u
q3 = q2 `div` (b ^ (k + 1))
r1 = x `mod` (b ^ (k + 1))
r2 = (q3 * m) `mod` (b ^ (k + 1))
r = r1 - r2
r' = if r < 0 then r + (b ^ (k + 1)) else r
result = minimize r' m
minimize :: Integer -> Integer -> Integer
minimize r m | r < 0 = error "BLECH"
| r >= m = minimize (r - m) m
| otherwise = r

25000
tests/math/barrett.tests Normal file

File diff suppressed because it is too large Load Diff

30000
tests/math/fastmodexp.tests Normal file

File diff suppressed because it is too large Load Diff

20000
tests/math/modexp.tests Normal file

File diff suppressed because it is too large Load Diff

15000
tests/math/modinv.tests Normal file

File diff suppressed because it is too large Load Diff

15000
tests/math/signed_add.tests Normal file

File diff suppressed because it is too large Load Diff

15000
tests/math/signed_div.tests Normal file

File diff suppressed because it is too large Load Diff

15000
tests/math/signed_mod.tests Normal file

File diff suppressed because it is too large Load Diff

15000
tests/math/signed_mul.tests Normal file

File diff suppressed because it is too large Load Diff

15000
tests/math/signed_sub.tests Normal file

File diff suppressed because it is too large Load Diff

15000
tests/math/unsigned_add.tests Normal file

File diff suppressed because it is too large Load Diff

15000
tests/math/unsigned_div.tests Normal file

File diff suppressed because it is too large Load Diff

15000
tests/math/unsigned_mod.tests Normal file

File diff suppressed because it is too large Load Diff

15000
tests/math/unsigned_mul.tests Normal file

File diff suppressed because it is too large Load Diff

15000
tests/math/unsigned_sub.tests Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,214 @@
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad
import Codec.Crypto.RSA.Pure
import Control.Concurrent
import Crypto.Random.DRBG
import Data.Bits
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ByteString.Lazy as BSL
import Data.Char
import Data.List
import qualified Data.Map.Strict as Map
import GHC.Integer.GMP.Internals
import Numeric
import System.IO
import System.ProgressBar
import System.Random
import Debug.Trace
keySizes :: [Int]
keySizes = [512,1024,2048,3072,4096,7680,8192,15360]
keyIterations :: [Int]
keyIterations = replicate 500 512 ++
replicate 500 1024 ++
replicate 250 2048 ++
replicate 125 3072 ++
replicate 50 4096 ++
replicate 5 7680 ++
replicate 2 8192 ++
replicate 1 15360
randomByteString :: CryptoRandomGen g => g -> (BS.ByteString, g)
randomByteString g =
let Right (bs, g') = genBytes 2 g
[h,l] = BS.unpack bs
x = (fromIntegral h `shiftL` 8) + (fromIntegral l)
Right (res, g'') = genBytes (x `mod` 1024) g'
in (res, g'')
randomLabel :: CryptoRandomGen g => g -> (BS.ByteString, g)
randomLabel g =
let Right (ls, g') = genBytes 1 g
[l8] = BS.unpack ls
(letters, g'') = go g' (l8 `mod` 24)
in (BSC.pack letters, g'')
where
goodChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ++
"abcdefghijklmnopqrstuvwxyz" ++
"0123456789 .,/?'\";:[{]}\\|-_=+" ++
"`~!@#$%^&*()"
lenGoods = fromIntegral (length goodChars)
--
go g 0 = ("", g)
go g x =
let Right (bs, g') = genBytes 1 g
[x] = BS.unpack bs
idx = fromIntegral (x `mod` lenGoods)
(rest, g'') = go g' (x - 1)
in ((goodChars !! idx) : rest, g'')
randomHash :: CryptoRandomGen g => g -> ((HashInfo, String), g)
randomHash g =
randomElement g [(hashSHA1, "1"),
(hashSHA224, "224"),
(hashSHA256, "256"),
(hashSHA384, "384"),
(hashSHA512, "512")]
showBinary :: BS.ByteString -> String
showBinary v = go v
where
go bstr =
case BS.uncons bstr of
Nothing ->
""
Just (x, rest) ->
let high = showHex (x `shiftR` 4) ""
low = showHex (x .&. 0xF) ""
in high ++ low ++ go rest
dump :: Handle -> [(String,String)] -> IO ()
dump hndl = mapM_ writeItem
where
writeItem (name, value) =
do hPutStr hndl name
hPutStr hndl ": "
hPutStrLn hndl value
mkProgress x y = Progress (fromIntegral x) (fromIntegral y)
runSignatureGenerator :: Chan Int -> Chan [(String,String)] -> IO ()
runSignatureGenerator inputs outputs =
do rng0 :: GenBuffered SystemRandom <- newGenIO
go Nothing rng0
where
go Nothing rng0 =
do keySize <- readChan inputs
go (Just keySize) rng0
go (Just keySize) g0 =
do unless (keySize `elem` keySizes) $
fail ("Bad key size: " ++ show keySize)
let Right (public, private, g1) = generateKeyPair g0 keySize
unless (private_d private `shiftR` keySize == 0) $
fail ("Bad private key size.")
unless (public_n public `shiftR` keySize == 0) $
fail ("Bad private key size.")
let (message, g2) = randomByteString g1
let ((hash, hashname), g3) = randomHash g2
case rsassa_pkcs1_v1_5_sign hash private (BSL.fromStrict message) of
Left _ ->
go (Just keySize) g3
Right sig ->
case rsassa_pkcs1_v1_5_verify hash public (BSL.fromStrict message) sig of
Left err ->
fail ("RSA Verification error: " ++ show err)
Right False ->
fail ("RSA verification failed?!")
Right True ->
do writeChan outputs [("d", showHex (private_d private) ""),
("n", showHex (public_n public) ""),
("h", hashname),
("k", showHex keySize ""),
("l", showHex (BS.length message) ""),
("m", showBinary message),
("s", showBinary (BSL.toStrict sig))]
go Nothing g3
runEncryptionGenerator :: Chan Int -> Chan [(String,String)] -> IO ()
runEncryptionGenerator inputs outputs =
do rng0 :: GenBuffered SystemRandom <- newGenIO
go Nothing rng0
where
go Nothing rng0 =
do keySize <- readChan inputs
go (Just keySize) rng0
go (Just keySize) g0 =
do unless (keySize `elem` keySizes) $
fail ("Bad key size: " ++ show keySize)
let Right (public, private, g1) = generateKeyPair g0 keySize
let (message, g2) = randomByteString g1
let (label, g3) = randomLabel g2
let ((hashinfo, hashname), g4) = randomHash g3
let hash = hashFunction hashinfo
let mgf1 = generateMGF1 hash
let msg = BSL.fromStrict message
lbl = BSL.fromStrict label
case encryptOAEP g4 hash mgf1 lbl public msg of
Left _ ->
go (Just keySize) g4
Right (c, g5) ->
do writeChan outputs [("d", showHex (private_d private) ""),
("n", showHex (public_n public) ""),
("h", hashname),
("l", showBinary label),
("m", showBinary message),
("c", showBinary (BSL.toStrict c))]
go Nothing g5
writeData :: Chan [(String,String)] -> Int -> (Progress -> IO ()) ->
Handle ->
IO ()
writeData outputChan countInt progressBar hndl = go 0
where
count = fromIntegral countInt
go x | x == count = return ()
| otherwise = do output <- readChan outputChan
dump hndl output
hFlush hndl
progressBar (Progress (x + 1) count)
go (x + 1)
main :: IO ()
main =
do sizeChan <- newChan
outputChan <- newChan
let count = length keyIterations
numThreads <- getNumCapabilities
--
unless (all (`elem` keySizes) keyIterations) $
fail "System setup failure."
--
sigthrs <- replicateM numThreads $
forkIO $ runSignatureGenerator sizeChan outputChan
let bar = autoProgressBar (msg "Generating signature tests") percentage 60
writeList2Chan sizeChan keyIterations
g1 <- withFile "signature.test" WriteMode $
writeData outputChan count bar
mapM_ killThread sigthrs
--
encthrs <- replicateM numThreads $
forkIO $ runEncryptionGenerator sizeChan outputChan
let bar = autoProgressBar (msg "Generating encryption tests") percentage 60
writeList2Chan sizeChan (take 1000 keyIterations)
g2 <- withFile "encryption.test" WriteMode $
writeData outputChan 1000 bar
mapM_ killThread encthrs
--
replicateM_ numThreads $
void $ forkIO $ runEncryptionGenerator sizeChan outputChan
let bar = autoProgressBar (msg "Generating encryption tests") percentage 60
writeList2Chan sizeChan (drop 1000 keyIterations)
let i = length keyIterations - 1
g2 <- withFile "encryption.ext.test" WriteMode $
writeData outputChan (count - 1000) bar
--
return ()
randomElement :: CryptoRandomGen g => g -> [a] -> (a, g)
randomElement g xs =
let Right (bs, g') = genBytes 1 g
x = BS.head bs
idx = fromIntegral x `mod` length xs
in (xs !! idx, g')

File diff suppressed because it is too large Load Diff

6000
tests/rsa/encryption.test Normal file

File diff suppressed because one or more lines are too long

10031
tests/rsa/signature.test Normal file

File diff suppressed because it is too large Load Diff