diff --git a/src/cryptonum.rs b/src/cryptonum.rs new file mode 100644 index 0000000..124d1dd --- /dev/null +++ b/src/cryptonum.rs @@ -0,0 +1,409 @@ +//! # 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. + +use std::cmp::Ordering; +use std::ops::*; + +/// A 512-bit unsigned value +#[derive(PartialEq,Eq,Debug,Clone)] +pub struct U512 { + // Why 9? Remember, we represent numbers in base 2^63, so that we can + // recover the carry bit as necessary. So we actually need ceiling(512/63) + // = 9 bytes to hold a 512-bit number. + contents: [u64; 9] +} + +impl U512 { + /// 0! + pub fn zero() -> U512 { + U512 { + contents: [0, 0, 0, 0, 0, 0, 0, 0, 0] + } + } + + /// The maximum possible value: 2^512 - 1. + pub fn max() -> U512 { + U512 { + contents: [0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, + 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, + 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, + 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, + 0xFF] + } + } + + /// Convert a `u8` to a `U512`. This is always safe. + pub fn from_u8(x: u8) -> U512 { + U512 { + contents: [x as u64, 0, 0, 0, 0, 0, 0, 0, 0] + } + } + + /// Convert a U512 into a `u8`. This should be the equivalent of masking + /// the U512 with `0xFF` and then converting to a `u8`. + pub fn to_u8(&self) -> u8 { + self.contents[0] as u8 + } + + /// Convert a `u16` to a `U512`. This is always safe. + pub fn from_u16(x: u16) -> U512 { + U512 { + contents: [x as u64, 0, 0, 0, 0, 0, 0, 0, 0] + } + } + + /// Convert a U512 into a `u16`. This should be the equivalent of masking + /// the U512 with `0xFFFF` and then converting to a `u16`. + pub fn to_u16(&self) -> u16 { + self.contents[0] as u16 + } + + /// Convert a `u32` to a `U512`. This is always safe. + pub fn from_u32(x: u32) -> U512 { + U512 { + contents: [x as u64, 0, 0, 0, 0, 0, 0, 0, 0] + } + } + + /// Convert a U512 into a `u32`. This should be the equivalent of masking + /// the U512 with `0xFFFFFFFF` and then converting to a `u32`. + pub fn to_u32(&self) -> u32 { + self.contents[0] as u32 + } + + /// Convert a `u64` to a `U512`. This is always safe. + pub fn from_u64(x: u64) -> U512 { + U512 { + contents: [x & 0x7FFFFFFFFFFFFFFF, x >> 63, 0, 0, 0, 0, 0, 0, 0] + } + } + + /// Convert a U512 into a `u64`. This should be the equivalent of masking + /// the U512 with `0xFFFFFFFFFFFFFFFF` and then converting to a `u64`. + pub fn to_u64(&self) -> u64 { + (self.contents[0] as u64) | (self.contents[1] << 63) + } +} + +//------------------------------------------------------------------------------ + +impl PartialOrd for U512 { + fn partial_cmp(&self, other: &U512) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for U512 { + fn cmp(&self, other: &U512) -> Ordering { + let sback = self.contents.iter().rev(); + let oback = other.contents.iter().rev(); + for (x,y) in sback.zip(oback) { + match x.cmp(y) { + Ordering::Equal => {}, + res => return res + } + } + Ordering::Equal + } +} + +//------------------------------------------------------------------------------ + +impl BitOrAssign for U512 { + fn bitor_assign(&mut self, other: U512) { + self.bitor_assign(&other) + } +} + +impl<'a> BitOrAssign<&'a U512> for U512 { + fn bitor_assign(&mut self, other: &U512) { + let mut oback = other.contents.iter(); + for x in self.contents.iter_mut() { + match oback.next() { + None => panic!("Internal error in cryptonum (|=)."), + Some(v) => *x = *x | *v + } + } + } +} + +impl BitOr for U512 { + type Output = U512; + + fn bitor(self, rhs: U512) -> U512 { + let mut copy = self.clone(); + copy |= rhs; + copy + } +} + +impl<'a> BitOr<&'a U512> for U512 { + type Output = U512; + + fn bitor(self, rhs: &U512) -> U512 { + let mut copy = self.clone(); + copy |= rhs; + copy + } +} + +impl<'a> BitOr for &'a U512 { + type Output = U512; + + fn bitor(self, rhs: U512) -> U512 { + let mut copy = self.clone(); + copy |= rhs; + copy + } +} + +impl<'a> BitOr<&'a U512> for &'a U512 { + type Output = U512; + + fn bitor(self, rhs: &U512) -> U512 { + let mut copy = self.clone(); + copy |= rhs; + copy + } +} + +//------------------------------------------------------------------------------ + +impl Not for U512 { + type Output = U512; + + fn not(self) -> U512 { + !&self + } +} + +impl<'a> Not for &'a U512 { + type Output = U512; + + fn not(self) -> U512 { + let mut output = self.clone(); + + for x in output.contents.iter_mut() { + *x = !*x & 0x7FFFFFFFFFFFFFFF; + } + output.contents[8] &= 0xFF; + output + } +} + +//------------------------------------------------------------------------------ + +impl BitAndAssign for U512 { + fn bitand_assign(&mut self, other: U512) { + self.bitand_assign(&other) + } +} + +impl<'a> BitAndAssign<&'a U512> for U512 { + fn bitand_assign(&mut self, other: &U512) { + let mut oback = other.contents.iter(); + for x in self.contents.iter_mut() { + match oback.next() { + None => panic!("Internal error in cryptonum (&=)."), + Some(v) => *x = *x & *v + } + } + } +} + +impl BitAnd for U512 { + type Output = U512; + + fn bitand(self, rhs: U512) -> U512 { + let mut copy = self.clone(); + copy &= rhs; + copy + } +} + +impl<'a> BitAnd<&'a U512> for U512 { + type Output = U512; + + fn bitand(self, rhs: &U512) -> U512 { + let mut copy = self.clone(); + copy &= rhs; + copy + } +} + +impl<'a> BitAnd for &'a U512 { + type Output = U512; + + fn bitand(self, rhs: U512) -> U512 { + let mut copy = self.clone(); + copy &= rhs; + copy + } +} + +impl<'a> BitAnd<&'a U512> for &'a U512 { + type Output = U512; + + fn bitand(self, rhs: &U512) -> U512 { + let mut copy = self.clone(); + copy &= rhs; + copy + } +} + +//------------------------------------------------------------------------------ + +#[cfg(test)] +mod test { + use quickcheck::{Arbitrary,Gen}; + use super::*; + + #[test] + fn test_builders() { + assert_eq!(U512{ contents: [0,0,0,0,0,0,0,0,0] }, + U512::from_u8(0)); + assert_eq!(U512{ contents: [0x7F,0,0,0,0,0,0,0,0] }, + U512::from_u8(0x7F)); + assert_eq!(U512{ contents: [0x7F7F,0,0,0,0,0,0,0,0] }, + U512::from_u16(0x7F7F)); + assert_eq!(U512{ contents: [0xCA5CADE5,0,0,0,0,0,0,0,0] }, + U512::from_u32(0xCA5CADE5)); + assert_eq!(U512{ contents: [0xCA5CADE5,0,0,0,0,0,0,0,0] }, + U512::from_u64(0xCA5CADE5)); + assert_eq!(U512{ contents: [0x7FFFFFFFFFFFFFFF,1,0,0,0,0,0,0,0] }, + U512::from_u64(0xFFFFFFFFFFFFFFFF)); + } + + #[test] + fn test_max() { + assert_eq!(U512::from_u64(u64::max_value()).to_u64(), + u64::max_value()); + assert_eq!(U512::max().to_u64(), u64::max_value()); + } + + quickcheck! { + fn builder_u8_upgrade_u16(x: u8) -> bool { + U512::from_u8(x) == U512::from_u16(x as u16) + } + fn builder_u16_upgrade_u32(x: u16) -> bool { + U512::from_u16(x) == U512::from_u32(x as u32) + } + fn builder_u32_upgrade_u64(x: u32) -> bool { + U512::from_u32(x) == U512::from_u64(x as u64) + } + fn builder_u8_roundtrips(x: u8) -> bool { + x == U512::from_u8(x).to_u8() + } + fn builder_u16_roundtrips(x: u16) -> bool { + x == U512::from_u16(x).to_u16() + } + fn builder_u32_roundtrips(x: u32) -> bool { + x == U512::from_u32(x).to_u32() + } + fn builder_u64_roundtrips(x: u64) -> bool { + x == U512::from_u64(x).to_u64() + } + } + + quickcheck! { + fn partial_ord64_works(x: u64, y: u64) -> bool { + let x512 = U512::from_u64(x); + let y512 = U512::from_u64(y); + x512.partial_cmp(&y512) == x.partial_cmp(&y) + } + fn ord64_works(x: u64, y: u64) -> bool { + let x512 = U512::from_u64(x); + let y512 = U512::from_u64(y); + x512.cmp(&y512) == x.cmp(&y) + } + } + + impl Arbitrary for U512 { + fn arbitrary(g: &mut G) -> U512 { + let x1 = g.next_u64() & 0x7FFFFFFFFFFFFFFF; + let x2 = g.next_u64() & 0x7FFFFFFFFFFFFFFF; + let x3 = g.next_u64() & 0x7FFFFFFFFFFFFFFF; + let x4 = g.next_u64() & 0x7FFFFFFFFFFFFFFF; + let x5 = g.next_u64() & 0x7FFFFFFFFFFFFFFF; + let x6 = g.next_u64() & 0x7FFFFFFFFFFFFFFF; + let x7 = g.next_u64() & 0x7FFFFFFFFFFFFFFF; + let x8 = g.next_u64() & 0x7FFFFFFFFFFFFFFF; + let x9 = g.next_u64() & 0xFF; + U512{ contents: [x1, x2, x3, x4, x5, x6, x7, x8, x9] } + } + } + + quickcheck! { + fn and_annulment(x: U512) -> bool { + (x & U512::zero()) == U512::zero() + } + fn or_annulment(x: U512) -> bool { + (x | U512::max()) == U512::max() + } + fn and_identity(x: U512) -> bool { + (&x & U512::max()) == x + } + fn or_identity(x: U512) -> bool { + (&x | U512::zero()) == x + } + fn and_idempotent(x: U512) -> bool { + (&x & &x) == x + } + fn or_idempotent(x: U512) -> bool { + (&x | &x) == x + } + fn and_complement(x: U512) -> bool { + (&x & &x) == x + } + fn or_complement(x: U512) -> bool { + (&x | !&x) == U512::max() + } + fn and_commutative(x: U512, y: U512) -> bool { + (&x & &y) == (&y & &x) + } + fn or_commutative(x: U512, y: U512) -> bool { + (&x | &y) == (&y | &x) + } + fn double_negation(x: U512) -> bool { + !!&x == x + } + fn or_distributive(a: U512, b: U512, c: U512) -> bool { + (&a & (&b | &c)) == ((&a & &b) | (&a & &c)) + } + fn and_distributive(a: U512, b: U512, c: U512) -> bool { + (&a | (&b & &c)) == ((&a | &b) & (&a | &c)) + } + fn or_absorption(a: U512, b: U512) -> bool { + (&a | (&a & &b)) == a + } + fn and_absorption(a: U512, b: U512) -> bool { + (&a & (&a | &b)) == a + } + fn or_associative(a: U512, b: U512, c: U512) -> bool { + (&a | (&b | &c)) == ((&a | &b) | &c) + } + fn and_associative(a: U512, b: U512, c: U512) -> bool { + (&a & (&b & &c)) == ((&a & &b) & &c) + } + fn small_or_check(x: u64, y: u64) -> bool { + let x512 = U512::from_u64(x); + let y512 = U512::from_u64(y); + let z512 = x512 | y512; + z512.to_u64() == (x | y) + } + fn small_and_check(x: u64, y: u64) -> bool { + let x512 = U512::from_u64(x); + let y512 = U512::from_u64(y); + let z512 = x512 & y512; + z512.to_u64() == (x & y) + } + fn small_neg_check(x: u64) -> bool { + let x512 = U512::from_u64(x); + let z512 = !x512; + z512.to_u64() == !x + } + } +}