From f03481522288ed0b060441d770cecfecaf26f209 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Wed, 24 Apr 2019 20:59:26 +0200 Subject: [PATCH 1/8] Use newly stablized TryFrom trait for primitive-types (#127) * Use newly stablized TryFrom trait * Remove ! before cfg --- primitive-types/src/error.rs | 67 ------------------------------------ primitive-types/src/lib.rs | 13 +++++-- 2 files changed, 10 insertions(+), 70 deletions(-) delete mode 100644 primitive-types/src/error.rs diff --git a/primitive-types/src/error.rs b/primitive-types/src/error.rs deleted file mode 100644 index 298fc14ed..000000000 --- a/primitive-types/src/error.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2015-2019 Parity Technologies -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// TODO: Remove TryFrom and TryInto when Rust trait is stablized -// https://github.com/paritytech/parity-common/issues/103 -//! Error, `TryFrom` and `TryInto` trait, should be removed when standard library stablize those. - -/// Error type for conversion. -pub enum Error { - /// Overflow encountered. - Overflow, -} - -/// An attempted conversion that consumes `self`, which may or may not be -/// expensive. -/// -/// Library authors should not directly implement this trait, but should prefer -/// implementing the [`TryFrom`] trait, which offers greater flexibility and -/// provides an equivalent `TryInto` implementation for free, thanks to a -/// blanket implementation in the standard library. For more information on this, -/// see the documentation for [`Into`]. -/// -/// [`TryFrom`]: trait.TryFrom.html -/// [`Into`]: trait.Into.html -pub trait TryInto: Sized { - /// The type returned in the event of a conversion error. - type Error; - - /// Performs the conversion. - fn try_into(self) -> Result; -} - -/// Attempt to construct `Self` via a conversion. -pub trait TryFrom: Sized { - /// The type returned in the event of a conversion error. - type Error; - - /// Performs the conversion. - fn try_from(value: T) -> Result; -} - -// TryFrom implies TryInto -impl TryInto for T where U: TryFrom -{ - type Error = U::Error; - - fn try_into(self) -> Result { - U::try_from(self) - } -} - -pub enum Never { } - -// Infallible conversions are semantically equivalent to fallible conversions -// with an uninhabited error type. -impl TryFrom for T where T: From { - type Error = Never; - - fn try_from(value: U) -> Result { - Ok(T::from(value)) - } -} diff --git a/primitive-types/src/lib.rs b/primitive-types/src/lib.rs index 93e1061b0..d0e2e3cef 100644 --- a/primitive-types/src/lib.rs +++ b/primitive-types/src/lib.rs @@ -14,9 +14,8 @@ #![cfg_attr(not(feature = "std"), no_std)] -mod error; - -pub use error::{Error, TryFrom, TryInto, Never}; +#[cfg(feature = "std")] +extern crate core; #[macro_use] extern crate uint; @@ -36,6 +35,14 @@ extern crate impl_codec; #[macro_use] extern crate impl_rlp; +use core::convert::TryFrom; + +/// Error type for conversion. +pub enum Error { + /// Overflow encountered. + Overflow, +} + construct_uint! { /// 128-bit unsigned integer. pub struct U128(2); From e1188915df6c32874ab743ee4b41dc13246770ef Mon Sep 17 00:00:00 2001 From: cheme Date: Fri, 3 May 2019 12:19:52 +0200 Subject: [PATCH 2/8] rust-crypto dependency removal (#85) * Switch to RustCrypto. * Test values directly define in code. * Move tests in their own file and remove macros * Update block-cipher crate * Update parity-crypto/src/aes.rs Co-Authored-By: cheme --- parity-crypto/Cargo.toml | 19 +- parity-crypto/benches/bench.rs | 56 ++++++ parity-crypto/src/aes.rs | 181 ++++++++++++++++-- parity-crypto/src/digest.rs | 58 ++++-- parity-crypto/src/error.rs | 63 +++++- parity-crypto/src/{hmac.rs => hmac/mod.rs} | 3 + parity-crypto/src/hmac/test.rs | 49 +++++ parity-crypto/src/lib.rs | 8 +- .../src/{pbkdf2.rs => pbkdf2/mod.rs} | 4 + parity-crypto/src/pbkdf2/test.rs | 28 +++ parity-crypto/src/scrypt.rs | 30 ++- 11 files changed, 451 insertions(+), 48 deletions(-) create mode 100644 parity-crypto/benches/bench.rs rename parity-crypto/src/{hmac.rs => hmac/mod.rs} (99%) create mode 100644 parity-crypto/src/hmac/test.rs rename parity-crypto/src/{pbkdf2.rs => pbkdf2/mod.rs} (97%) create mode 100644 parity-crypto/src/pbkdf2/test.rs diff --git a/parity-crypto/Cargo.toml b/parity-crypto/Cargo.toml index 3758f0f5c..be96fc923 100644 --- a/parity-crypto/Cargo.toml +++ b/parity-crypto/Cargo.toml @@ -5,9 +5,24 @@ authors = ["Parity Technologies "] repository = "https://github.com/paritytech/parity-common" description = "Crypto utils used by ethstore and network." license = "GPL-3.0" +autobenches = false + +[[bench]] +name = "bench" +harness = false + [dependencies] quick-error = "1.2.2" -ring = "0.14.3" -rust-crypto = "0.2.36" tiny-keccak = "1.4" +scrypt = { version = "0.1.1", default-features = false } +ripemd160 = "0.8.0" +sha2 = "0.8.0" +digest = "0.8" +aes = "0.3.2" +aes-ctr = "0.3.0" +block-modes = "0.3.3" +ring = "0.14.6" + +[dev-dependencies] +criterion = "0.2" diff --git a/parity-crypto/benches/bench.rs b/parity-crypto/benches/bench.rs new file mode 100644 index 000000000..f9f68dfd1 --- /dev/null +++ b/parity-crypto/benches/bench.rs @@ -0,0 +1,56 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + + +extern crate parity_crypto; + +#[macro_use] +extern crate criterion; + +use criterion::{Criterion, Bencher}; + +criterion_group!(benches, input_len); + +criterion_main!(benches); + +/// general benches for multiple input size +fn input_len(c: &mut Criterion) { + + c.bench_function_over_inputs("ripemd", + |b: &mut Bencher, size: &usize| { + let data = vec![0u8; *size]; + b.iter(|| parity_crypto::digest::ripemd160(&data[..])); + }, + vec![100, 500, 1_000, 10_000, 100_000] + ); + + c.bench_function_over_inputs("aes_ctr", + |b: &mut Bencher, size: &usize| { + let data = vec![0u8; *size]; + let mut dest = vec![0; *size]; + let k = [0; 16]; + let iv = [0; 16]; + + b.iter(||{ + parity_crypto::aes::encrypt_128_ctr(&k[..], &iv[..], &data[..], &mut dest[..]).unwrap(); + // same as encrypt but add it just in case + parity_crypto::aes::decrypt_128_ctr(&k[..], &iv[..], &data[..], &mut dest[..]).unwrap(); + }); + }, + vec![100, 500, 1_000, 10_000, 100_000] + ); + +} diff --git a/parity-crypto/src/aes.rs b/parity-crypto/src/aes.rs index 42a26fad0..885032b69 100644 --- a/parity-crypto/src/aes.rs +++ b/parity-crypto/src/aes.rs @@ -14,40 +14,195 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use block_modes::{BlockMode}; +use block_modes::block_padding::Pkcs7; +use block_modes::block_padding::ZeroPadding; +use block_modes::{ Cbc, Ecb }; +use raes::{ Aes128, Aes256 }; +use aes_ctr::{ Aes128Ctr, Aes256Ctr }; +use aes_ctr::stream_cipher::{ NewStreamCipher, SyncStreamCipher }; use error::SymmError; -use rcrypto::blockmodes::{CtrMode, CbcDecryptor, PkcsPadding}; -use rcrypto::aessafe::{AesSafe128Encryptor, AesSafe128Decryptor}; -use rcrypto::symmetriccipher::{Encryptor, Decryptor}; -use rcrypto::buffer::{RefReadBuffer, RefWriteBuffer, WriteBuffer}; +use raes::block_cipher_trait::generic_array::GenericArray; + + +/// One time encoder/decoder for Ecb mode Aes256 with zero padding +pub struct AesEcb256(Ecb); + +impl AesEcb256 { + + /// New encoder/decoder, no iv for ecb + pub fn new(key: &[u8]) -> Result { + Ok(AesEcb256(Ecb::new_var(key, &[])?)) + } + + /// Encrypt data in place without padding. The data length must be a multiple + /// of the block size. + pub fn encrypt(self, content: &mut [u8]) -> Result<(), SymmError> { + let len = content.len(); + self.0.encrypt(content, len)?; + Ok(()) + } + + /// Decrypt data in place without padding. The data length must be a multiple + /// of the block size. + pub fn decrypt(self, content: &mut [u8]) -> Result<(), SymmError> { + self.0.decrypt(content)?; + Ok(()) + } +} + + +/// Reusable encoder/decoder for Aes256 in Ctr mode and no padding +pub struct AesCtr256(Aes256Ctr); + +impl AesCtr256 { + + /// New encoder/decoder + pub fn new(key: &[u8], iv: &[u8]) -> Result { + Ok(AesCtr256( + Aes256Ctr::new(GenericArray::from_slice(key), GenericArray::from_slice(iv)) + )) + } + + /// In place encrypt a content without padding, the content length must be a multiple + /// of the block size. + pub fn encrypt(&mut self, content: &mut[u8]) -> Result<(), SymmError> { + self.0.try_apply_keystream(content)?; + Ok(()) + } + + /// In place decrypt a content without padding, the content length must be a multiple + /// of the block size. + pub fn decrypt(&mut self, content: &mut[u8]) -> Result<(), SymmError> { + self.0.try_apply_keystream(content)?; + Ok(()) + } +} /// Encrypt a message (CTR mode). /// /// Key (`k`) length and initialisation vector (`iv`) length have to be 16 bytes each. /// An error is returned if the input lengths are invalid. +/// If possible prefer `inplace_encrypt_128_ctr` to avoid a slice copy. pub fn encrypt_128_ctr(k: &[u8], iv: &[u8], plain: &[u8], dest: &mut [u8]) -> Result<(), SymmError> { - let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec()); - encryptor.encrypt(&mut RefReadBuffer::new(plain), &mut RefWriteBuffer::new(dest), true)?; + let mut encryptor = Aes128Ctr::new( + GenericArray::from_slice(k), + GenericArray::from_slice(iv), + ); + &mut dest[..plain.len()].copy_from_slice(plain); + encryptor.try_apply_keystream(dest)?; Ok(()) + +} + +/// Encrypt a message (CTR mode). +/// +/// Key (`k`) length and initialisation vector (`iv`) length have to be 16 bytes each. +/// An error is returned if the input lengths are invalid. +pub fn inplace_encrypt_128_ctr(k: &[u8], iv: &[u8], data: &mut [u8]) -> Result<(), SymmError> { + let mut encryptor = Aes128Ctr::new( + GenericArray::from_slice(k), + GenericArray::from_slice(iv), + ); + encryptor.try_apply_keystream(data)?; + Ok(()) + } /// Decrypt a message (CTR mode). /// /// Key (`k`) length and initialisation vector (`iv`) length have to be 16 bytes each. /// An error is returned if the input lengths are invalid. +/// If possible prefer `inplace_decrypt_128_ctr` instead. pub fn decrypt_128_ctr(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result<(), SymmError> { - let mut encryptor = CtrMode::new(AesSafe128Encryptor::new(k), iv.to_vec()); - encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut RefWriteBuffer::new(dest), true)?; + let mut encryptor = Aes128Ctr::new( + GenericArray::from_slice(k), + GenericArray::from_slice(iv), + ); + + &mut dest[..encrypted.len()].copy_from_slice(encrypted); + encryptor.try_apply_keystream(dest)?; Ok(()) } +/// Decrypt a message (CTR mode). +/// +/// Key (`k`) length and initialisation vector (`iv`) length have to be 16 bytes each. +/// An error is returned if the input lengths are invalid. +pub fn inplace_decrypt_128_ctr(k: &[u8], iv: &[u8], data: &mut [u8]) -> Result<(), SymmError> { + let mut encryptor = Aes128Ctr::new( + GenericArray::from_slice(k), + GenericArray::from_slice(iv), + ); + + encryptor.try_apply_keystream(data)?; + Ok(()) +} + + /// Decrypt a message (CBC mode). /// /// Key (`k`) length and initialisation vector (`iv`) length have to be 16 bytes each. /// An error is returned if the input lengths are invalid. pub fn decrypt_128_cbc(k: &[u8], iv: &[u8], encrypted: &[u8], dest: &mut [u8]) -> Result { - let mut encryptor = CbcDecryptor::new(AesSafe128Decryptor::new(k), PkcsPadding, iv.to_vec()); - let len = dest.len(); - let mut buffer = RefWriteBuffer::new(dest); - encryptor.decrypt(&mut RefReadBuffer::new(encrypted), &mut buffer, true)?; - Ok(len - buffer.remaining()) + let encryptor = Cbc::::new_var(k, iv)?; + &mut dest[..encrypted.len()].copy_from_slice(encrypted); + let unpad_length = { + encryptor.decrypt(&mut dest[..encrypted.len()])?.len() + }; + Ok(unpad_length) +} + + +#[cfg(test)] +mod tests { + + use super::*; + + // only use for test could be expose in the future + fn encrypt_128_cbc(k: &[u8], iv: &[u8], plain: &[u8], dest: &mut [u8]) -> Result<(), SymmError> { + let encryptor = Cbc::::new_var(k, iv)?; + &mut dest[..plain.len()].copy_from_slice(plain); + encryptor.encrypt(dest, plain.len())?; + Ok(()) + } + + #[test] + pub fn test_aes_short() -> Result<(),SymmError> { + let key = [97, 110, 121, 99, 111, 110, 116, 101, 110, 116, 116, 111, 114, 101, 97, 99, 104, 49, 50, 56, 98, 105, 116, 115, 105, 122, 101, 10]; + let salt = [109, 121, 115, 97, 108, 116, 115, 104, 111, 117, 108, 100, 102, 105, 108, 108, 115, 111, 109, 109, 101, 98, 121, 116, 101, 108, 101, 110, 103, 116, 104, 10]; + let content = [83, 111, 109, 101, 32, 99, 111, 110, 116, 101, 110, 116, 32, 116, 111, 32, 116, 101, 115, 116, + 32, 97, 101, 115, 44, 10, 110, 111, 116, 32, 116, 111, 32, 109, 117, 99, 104, 32, 44, 32, 111, 110, 108, 121, + 32, 118, 101, 114, 121, 32, 98, 97, 115, 105, 99, 32, 116, 101, 115, 116, 32, 116, 111, 32, 97, 118, 111, 105, + 100, 32, 111, 98, 118, 105, 111, 117, 115, 32, 114, 101, 103, 114, 101, 115, 115, 105, 111, 110, 32, 119, 104, + 101, 110, 32, 115, 119, 105, 116, 99, 104, 105, 110, 103, 32, 108, 105, 98, 115, 46, 10]; + let ctr_enc = [65, 55, 246, 75, 24, 117, 30, 233, 218, 139, 91, 251, 251, 179, 171, 69, 60, 244, 249, 44, 238, 60, + 10, 66, 71, 10, 199, 111, 54, 24, 124, 223, 153, 250, 159, 154, 164, 109, 232, 82, 20, 199, 182, 40, 174, 104, 64, + 203, 236, 94, 222, 184, 117, 54, 234, 189, 253, 122, 135, 121, 100, 44, 227, 241, 123, 120, 110, 188, 109, 148, 112, + 160, 131, 205, 116, 104, 232, 8, 22, 170, 80, 231, 155, 246, 255, 115, 101, 5, 234, 104, 220, 199, 192, 166, 181, 156, + 113, 255, 187, 51, 38, 128, 75, 29, 237, 178, 205, 98, 101, 110]; + let cbc_enc = [167, 248, 5, 90, 11, 140, 215, 138, 165, 125, 137, 76, 47, 243, 191, 48, 183, 247, 109, 86, 24, 45, + 81, 215, 0, 51, 221, 185, 131, 97, 234, 189, 244, 255, 107, 210, 70, 60, 41, 221, 43, 137, 185, 166, 42, 65, 18, 200, + 151, 233, 255, 192, 109, 25, 105, 115, 161, 209, 126, 235, 99, 192, 241, 241, 19, 249, 87, 244, 28, 146, 186, 189, 108, + 9, 243, 132, 4, 105, 53, 162, 8, 235, 84, 107, 213, 59, 158, 113, 227, 120, 162, 50, 237, 123, 70, 187, 83, 73, 146, 13, + 44, 191, 53, 4, 125, 207, 176, 45, 8, 153, 175, 198]; + let mut dest = vec![0;110]; + let mut dest_padded = vec![0;112]; + let mut dest_padded2 = vec![0;128]; // TODO RustLib need an extra 16bytes in dest : looks extra buggy but function is not currently use (keep it private for now) + encrypt_128_cbc(&key[..16], &salt[..16], &content, &mut dest_padded2)?; + assert!(&dest_padded2[..112] == &cbc_enc[..]); + encrypt_128_ctr(&key[..16], &salt[..16], &content, &mut dest)?; + assert!(&dest[..] == &ctr_enc[..]); + let mut content_data = content.to_vec(); + inplace_encrypt_128_ctr(&key[..16], &salt[..16], &mut content_data[..])?; + assert!(&content_data[..] == &ctr_enc[..]); + decrypt_128_ctr(&key[..16], &salt[..16], &ctr_enc[..], &mut dest)?; + assert!(&dest[..] == &content[..]); + let mut content_data = ctr_enc.to_vec(); + inplace_decrypt_128_ctr(&key[..16], &salt[..16], &mut content_data[..])?; + assert!(&content_data[..] == &content[..]); + let l = decrypt_128_cbc(&key[..16], &salt[..16], &cbc_enc[..], &mut dest_padded)?; + assert!(&dest_padded[..l] == &content[..]); + Ok(()) + } } diff --git a/parity-crypto/src/digest.rs b/parity-crypto/src/digest.rs index b2be0b8ed..40bafe145 100644 --- a/parity-crypto/src/digest.rs +++ b/parity-crypto/src/digest.rs @@ -14,37 +14,46 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use rcrypto::ripemd160; -use ring::digest::{self, Context, SHA256, SHA512}; +use rripemd160; +use rsha2; use std::marker::PhantomData; use std::ops::Deref; +use rdigest::generic_array::GenericArray; +use rdigest::generic_array::typenum::{U20, U32, U64}; +use rsha2::Digest as RDigest; /// The message digest. pub struct Digest(InnerDigest, PhantomData); enum InnerDigest { - Ring(digest::Digest), - Ripemd160([u8; 20]), + Sha256(GenericArray), + Sha512(GenericArray), + Ripemd160(GenericArray), } impl Deref for Digest { type Target = [u8]; fn deref(&self) -> &Self::Target { match self.0 { - InnerDigest::Ring(ref d) => d.as_ref(), - InnerDigest::Ripemd160(ref d) => &d[..] + InnerDigest::Sha256(ref d) => &d[..], + InnerDigest::Sha512(ref d) => &d[..], + InnerDigest::Ripemd160(ref d) => &d[..], } } } /// Single-step sha256 digest computation. pub fn sha256(data: &[u8]) -> Digest { - Digest(InnerDigest::Ring(digest::digest(&SHA256, data)), PhantomData) + let mut hasher = Hasher::sha256(); + hasher.update(data); + hasher.finish() } /// Single-step sha512 digest computation. pub fn sha512(data: &[u8]) -> Digest { - Digest(InnerDigest::Ring(digest::digest(&SHA512, data)), PhantomData) + let mut hasher = Hasher::sha512(); + hasher.update(data); + hasher.finish() } /// Single-step ripemd160 digest computation. @@ -62,34 +71,39 @@ pub enum Ripemd160 {} pub struct Hasher(Inner, PhantomData); enum Inner { - Ring(Context), - Ripemd160(ripemd160::Ripemd160) + Sha256(rsha2::Sha256), + Sha512(rsha2::Sha512), + Ripemd160(rripemd160::Ripemd160) } impl Hasher { pub fn sha256() -> Hasher { - Hasher(Inner::Ring(Context::new(&SHA256)), PhantomData) + Hasher(Inner::Sha256(rsha2::Sha256::default()), PhantomData) } } impl Hasher { pub fn sha512() -> Hasher { - Hasher(Inner::Ring(Context::new(&SHA512)), PhantomData) + Hasher(Inner::Sha512(rsha2::Sha512::default()), PhantomData) } } impl Hasher { pub fn ripemd160() -> Hasher { - Hasher(Inner::Ripemd160(ripemd160::Ripemd160::new()), PhantomData) + Hasher(Inner::Ripemd160(rripemd160::Ripemd160::default()), PhantomData) } } impl Hasher { pub fn update(&mut self, data: &[u8]) { match self.0 { - Inner::Ring(ref mut ctx) => ctx.update(data), + Inner::Sha256(ref mut ctx) => { + ctx.input(data) + }, + Inner::Sha512(ref mut ctx) => { + ctx.input(data) + }, Inner::Ripemd160(ref mut ctx) => { - use rcrypto::digest::Digest; ctx.input(data) } } @@ -97,12 +111,14 @@ impl Hasher { pub fn finish(self) -> Digest { match self.0 { - Inner::Ring(ctx) => Digest(InnerDigest::Ring(ctx.finish()), PhantomData), - Inner::Ripemd160(mut ctx) => { - use rcrypto::digest::Digest; - let mut d = [0; 20]; - ctx.result(&mut d); - Digest(InnerDigest::Ripemd160(d), PhantomData) + Inner::Sha256(ctx) => { + Digest(InnerDigest::Sha256(ctx.result()), PhantomData) + }, + Inner::Sha512(ctx) => { + Digest(InnerDigest::Sha512(ctx.result()), PhantomData) + }, + Inner::Ripemd160(ctx) => { + Digest(InnerDigest::Ripemd160(ctx.result()), PhantomData) } } } diff --git a/parity-crypto/src/error.rs b/parity-crypto/src/error.rs index 4e5582e19..c64d172b2 100644 --- a/parity-crypto/src/error.rs +++ b/parity-crypto/src/error.rs @@ -14,8 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use rcrypto; use ring; +use rscrypt; +use block_modes; +use aes_ctr; +use std::error::Error as StdError; quick_error! { #[derive(Debug)] @@ -28,6 +31,19 @@ quick_error! { cause(e) from() } + AsymShort(det: &'static str) { + description(det) + } + AsymFull(e: Box) { + cause(&**e) + description(e.description()) + } + } +} + +impl Into for Error { + fn into(self) -> std::io::Error { + std::io::Error::new(std::io::ErrorKind::Other, format!("Crypto error: {}",self)) } } @@ -42,24 +58,43 @@ quick_error! { InvalidP { display("Invalid p argument of the scrypt encryption") } + ScryptParam(e: rscrypt::errors::InvalidParams) { + display("invalid params for scrypt: {}", e) + cause(e) + from() + } + ScryptLength(e: rscrypt::errors::InvalidOutputLen) { + display("invalid scrypt output length: {}", e) + cause(e) + from() + } } } + quick_error! { #[derive(Debug)] pub enum SymmError wraps PrivSymmErr { - RustCrypto(e: rcrypto::symmetriccipher::SymmetricCipherError) { + Offset(x: usize) { + display("offset {} greater than slice length", x) + } + BlockMode(e: block_modes::BlockModeError) { display("symmetric crypto error") from() } + KeyStream(e: aes_ctr::stream_cipher::LoopError) { + display("ctr key stream ended") + from() + } + InvalidKeyLength(e: block_modes::InvalidKeyIvLength) { + display("Error with RustCrypto key length : {}", e) + from() + } Ring(e: ring::error::Unspecified) { display("symmetric crypto error") cause(e) from() } - Offset(x: usize) { - display("offset {} greater than slice length", x) - } } } @@ -75,8 +110,20 @@ impl From for SymmError { } } -impl From for SymmError { - fn from(e: rcrypto::symmetriccipher::SymmetricCipherError) -> SymmError { - SymmError(PrivSymmErr::RustCrypto(e)) +impl From for SymmError { + fn from(e: block_modes::BlockModeError) -> SymmError { + SymmError(PrivSymmErr::BlockMode(e)) + } +} + +impl From for SymmError { + fn from(e: block_modes::InvalidKeyIvLength) -> SymmError { + SymmError(PrivSymmErr::InvalidKeyLength(e)) + } +} + +impl From for SymmError { + fn from(e: aes_ctr::stream_cipher::LoopError) -> SymmError { + SymmError(PrivSymmErr::KeyStream(e)) } } diff --git a/parity-crypto/src/hmac.rs b/parity-crypto/src/hmac/mod.rs similarity index 99% rename from parity-crypto/src/hmac.rs rename to parity-crypto/src/hmac/mod.rs index ff337ed02..1372fbe10 100644 --- a/parity-crypto/src/hmac.rs +++ b/parity-crypto/src/hmac/mod.rs @@ -86,3 +86,6 @@ impl VerifyKey { pub fn verify(k: &VerifyKey, data: &[u8], sig: &[u8]) -> bool { hmac::verify(&k.0, data, sig).is_ok() } + +#[cfg(test)] +mod test; diff --git a/parity-crypto/src/hmac/test.rs b/parity-crypto/src/hmac/test.rs new file mode 100644 index 000000000..a04e247e5 --- /dev/null +++ b/parity-crypto/src/hmac/test.rs @@ -0,0 +1,49 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + + +use super::*; +#[test] +fn simple_mac_and_verify() { + let input = b"Some bytes"; + let big_input = vec![7u8;2000]; + + let key1 = vec![3u8;64]; + let key2 = vec![4u8;128]; + + let sig_key1 = SigKey::sha256(&key1[..]); + let sig_key2 = SigKey::sha512(&key2[..]); + + let mut signer1 = Signer::with(&sig_key1); + let mut signer2 = Signer::with(&sig_key2); + + signer1.update(&input[..]); + for i in 0 .. big_input.len() / 33 { + signer2.update(&big_input[i*33..(i+1)*33]); + } + signer2.update(&big_input[(big_input.len() / 33)*33..]); + let sig1 = signer1.sign(); + assert_eq!(&sig1[..], [223, 208, 90, 69, 144, 95, 145, 180, 56, 155, 78, 40, 86, 238, 205, 81, 160, 245, 88, 145, 164, 67, 254, 180, 202, 107, 93, 249, 64, 196, 86, 225]); + let sig2 = signer2.sign(); + assert_eq!(&sig2[..], &[29, 63, 46, 122, 27, 5, 241, 38, 86, 197, 91, 79, 33, 107, 152, 195, 118, 221, 117, 119, 84, 114, 46, 65, 243, 157, 105, 12, 147, 176, 190, 37, 210, 164, 152, 8, 58, 243, 59, 206, 80, 10, 230, 197, 255, 110, 191, 180, 93, 22, 255, 0, 99, 79, 237, 229, 209, 199, 125, 83, 15, 179, 134, 89][..]); + assert_eq!(&sig1[..], &sign(&sig_key1, &input[..])[..]); + assert_eq!(&sig2[..], &sign(&sig_key2, &big_input[..])[..]); + let verif_key1 = VerifyKey::sha256(&key1[..]); + let verif_key2 = VerifyKey::sha512(&key2[..]); + assert!(verify(&verif_key1, &input[..], &sig1[..])); + assert!(verify(&verif_key2, &big_input[..], &sig2[..])); + +} diff --git a/parity-crypto/src/lib.rs b/parity-crypto/src/lib.rs index b80ba46b5..379e2619b 100644 --- a/parity-crypto/src/lib.rs +++ b/parity-crypto/src/lib.rs @@ -16,11 +16,17 @@ //! Crypto utils used by ethstore and network. -extern crate crypto as rcrypto; #[macro_use] extern crate quick_error; extern crate ring; extern crate tiny_keccak; +extern crate scrypt as rscrypt; +extern crate ripemd160 as rripemd160; +extern crate sha2 as rsha2; +extern crate digest as rdigest; +extern crate aes as raes; +extern crate aes_ctr; +extern crate block_modes; pub mod aes; pub mod aes_gcm; diff --git a/parity-crypto/src/pbkdf2.rs b/parity-crypto/src/pbkdf2/mod.rs similarity index 97% rename from parity-crypto/src/pbkdf2.rs rename to parity-crypto/src/pbkdf2/mod.rs index c4d5cca1c..693ef2182 100644 --- a/parity-crypto/src/pbkdf2.rs +++ b/parity-crypto/src/pbkdf2/mod.rs @@ -28,3 +28,7 @@ pub fn sha256(iter: NonZeroU32, salt: Salt, sec: Secret, out: &mut [u8; 32]) { pub fn sha512(iter: NonZeroU32, salt: Salt, sec: Secret, out: &mut [u8; 64]) { ring::pbkdf2::derive(&ring::digest::SHA512, iter, salt.0, sec.0, &mut out[..]) } + + +#[cfg(test)] +mod test; diff --git a/parity-crypto/src/pbkdf2/test.rs b/parity-crypto/src/pbkdf2/test.rs new file mode 100644 index 000000000..6fa594f25 --- /dev/null +++ b/parity-crypto/src/pbkdf2/test.rs @@ -0,0 +1,28 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use super::*; +use std::num::NonZeroU32; + +#[test] +fn basic_test() { + let mut dest = [0;32]; + let salt = [5;32]; + let secret = [7;32]; + sha256(NonZeroU32::new(3).expect("static input;qed"), Salt(&salt[..]), Secret(&secret[..]), &mut dest); + let res = [242, 33, 31, 124, 36, 223, 179, 185, 206, 175, 190, 253, 85, 33, 23, 126, 141, 29, 23, 97, 66, 63, 51, 196, 27, 255, 135, 206, 74, 137, 172, 87]; + assert_eq!(res, dest); +} diff --git a/parity-crypto/src/scrypt.rs b/parity-crypto/src/scrypt.rs index d47e55ed2..c5ba18833 100644 --- a/parity-crypto/src/scrypt.rs +++ b/parity-crypto/src/scrypt.rs @@ -15,8 +15,10 @@ // along with Parity. If not, see . use error::ScryptError; -use rcrypto::scrypt::{scrypt, ScryptParams}; use super::{KEY_LENGTH_AES, KEY_LENGTH}; +use rscrypt::{scrypt, ScryptParams}; +#[cfg(test)] +use std::io::Error; pub fn derive_key(pass: &[u8], salt: &[u8], n: u32, p: u32, r: u32) -> Result<(Vec, Vec), ScryptError> { // sanity checks @@ -30,9 +32,31 @@ pub fn derive_key(pass: &[u8], salt: &[u8], n: u32, p: u32, r: u32) -> Result<(V } let mut derived_key = vec![0u8; KEY_LENGTH]; - let scrypt_params = ScryptParams::new(log_n, r, p); - scrypt(pass, salt, &scrypt_params, &mut derived_key); + let scrypt_params = ScryptParams::new(log_n, r, p)?; + scrypt(pass, salt, &scrypt_params, &mut derived_key)?; let derived_right_bits = &derived_key[0..KEY_LENGTH_AES]; let derived_left_bits = &derived_key[KEY_LENGTH_AES..KEY_LENGTH]; Ok((derived_right_bits.to_vec(), derived_left_bits.to_vec())) } + + +// test is build from previous crypto lib behaviour, values may be incorrect +// if previous crypto lib got a bug. +#[test] +pub fn test_derive() -> Result<(),Error> { + let pass = [109, 121, 112, 97, 115, 115, 10]; + let salt = [109, 121, 115, 97, 108, 116, 115, 104, 111, 117, 108, 100, 102, 105, + 108, 108, 115, 111, 109, 109, 101, 98, 121, 116, 101, 108, 101, 110, 103, 116, 104, 10]; + let r1 = [93, 134, 79, 68, 223, 27, 44, 174, 236, 184, 179, 203, 74, 139, 73, 66]; + let r2 = [2, 24, 239, 131, 172, 164, 18, 171, 132, 207, 22, 217, 150, 20, 203, 37]; + let l1 = [6, 90, 119, 45, 67, 2, 99, 151, 81, 88, 166, 210, 244, 19, 123, 208]; + let l2 = [253, 123, 132, 12, 188, 89, 196, 2, 107, 224, 239, 231, 135, 177, 125, 62]; + + let (l,r) = derive_key(&pass[..],&salt, 262, 1, 8).unwrap(); + assert!(l == r1); + assert!(r == l1); + let (l,r) = derive_key(&pass[..],&salt, 144, 4, 4).unwrap(); + assert!(l == r2); + assert!(r == l2); + Ok(()) +} From 993e59a3036a8dfad8da129d3a0cec50384c4546 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Fri, 3 May 2019 17:00:51 +0200 Subject: [PATCH 3/8] Bump parity-crypto to 0.3.1 (#132) --- parity-crypto/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parity-crypto/Cargo.toml b/parity-crypto/Cargo.toml index be96fc923..f5d113290 100644 --- a/parity-crypto/Cargo.toml +++ b/parity-crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "parity-crypto" -version = "0.3.0" +version = "0.3.1" authors = ["Parity Technologies "] repository = "https://github.com/paritytech/parity-common" description = "Crypto utils used by ethstore and network." From 681faff5a201c63da132d8874191e862503fe010 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Mon, 6 May 2019 12:00:05 +0200 Subject: [PATCH 4/8] Use EntropyRng instead of OsRng (#130) --- fixed-hash/src/hash.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fixed-hash/src/hash.rs b/fixed-hash/src/hash.rs index 83273bc87..7c2c96609 100644 --- a/fixed-hash/src/hash.rs +++ b/fixed-hash/src/hash.rs @@ -513,7 +513,7 @@ macro_rules! impl_rand_for_fixed_hash { /// Assign `self` to a cryptographically random value. pub fn randomize(&mut self) { - let mut rng = $crate::rand::OsRng::new().unwrap(); + let mut rng = $crate::rand::rngs::EntropyRng::new(); self.randomize_using(&mut rng); } From ecdc90cb2bbff3161f3827d3bb38cefe8350fce2 Mon Sep 17 00:00:00 2001 From: Pierre Krieger Date: Mon, 6 May 2019 13:10:53 +0200 Subject: [PATCH 5/8] Bump fixed-hash to 0.3.2 (#134) --- fixed-hash/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fixed-hash/Cargo.toml b/fixed-hash/Cargo.toml index c96ea559e..c85eb2417 100644 --- a/fixed-hash/Cargo.toml +++ b/fixed-hash/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fixed-hash" -version = "0.3.1" +version = "0.3.2" authors = ["Parity Technologies "] license = "MIT" homepage = "https://github.com/paritytech/parity-common" From 3458ebf47bba0c173c84ed902846d4d723e7c99f Mon Sep 17 00:00:00 2001 From: Andronik Ordian Date: Mon, 6 May 2019 15:31:27 +0200 Subject: [PATCH 6/8] uint: convert benchmarks to criterion (#123) * uint: convert benchmarks to criterion * rustfmt * add u256_rem_small * add u512_rem_large bench * uint: add mulmod comparison benches * uint: add comparison with gmp * uint: clean up benches * uint: fix windows ci * uint: fix gmp conversions * uint: make sure bytes are little endian for gmp conversion --- uint/Cargo.toml | 11 + uint/benches/bigint.rs | 892 +++++++++++++++++++++++++---------------- 2 files changed, 551 insertions(+), 352 deletions(-) diff --git a/uint/Cargo.toml b/uint/Cargo.toml index dbd2691ea..298ee9103 100644 --- a/uint/Cargo.toml +++ b/uint/Cargo.toml @@ -25,3 +25,14 @@ name = "modular" [[test]] name = "uint_tests" required-features = ["std"] + +[dev-dependencies] +criterion = "0.2.11" +num-bigint = "0.2" + +[target.'cfg(unix)'.dev-dependencies] +rug = { version = "1.3.0", default-features = false, features = ["integer"] } + +[[bench]] +name = "bigint" +harness = false diff --git a/uint/benches/bigint.rs b/uint/benches/bigint.rs index 0ab541e4d..a19d3819d 100644 --- a/uint/benches/bigint.rs +++ b/uint/benches/bigint.rs @@ -9,15 +9,16 @@ //! benchmarking for bigint //! should be started with: //! ```bash -//! rustup run nightly cargo bench +//! rustup run cargo bench //! ``` -#![feature(test)] - +#[macro_use] +extern crate criterion; extern crate core; -extern crate test; #[macro_use] extern crate uint; +extern crate num_bigint; +extern crate rug; construct_uint! { pub struct U256(4); @@ -34,383 +35,570 @@ impl U256 { } } -use test::{Bencher, black_box}; - -#[bench] -fn u256_add(b: &mut Bencher) { - b.iter(|| { - let n = black_box(10000); - let zero = black_box(U256::zero()); - (0..n).fold(zero, |old, new| { - old.overflowing_add(U256::from(black_box(new))).0 - }) - }); -} - -#[bench] -fn u256_sub(b: &mut Bencher) { - b.iter(|| { - let n = black_box(10000); - let max = black_box(U256::max_value()); - (0..n).fold(max, |old, new| { - old.overflowing_sub(U256::from(black_box(new))).0 - }) - }); -} - -#[bench] -fn u512_sub(b: &mut Bencher) { - b.iter(|| { - let n = black_box(10000); - let max = black_box(U512::max_value()); - (0..n).fold(max, |old, new| { - let new = black_box(new); - let p = new % 2; - old.overflowing_sub(U512([p, p, p, p, p, p, p, new])).0 - }) - }); -} - -#[bench] -fn u512_add(b: &mut Bencher) { - b.iter(|| { - let n = black_box(10000); - let zero = black_box(U512::zero()); - (0..n).fold(zero, |old, new| { - let new = black_box(new); - old.overflowing_add(U512([new, new, new, new, new, new, new, new])) - .0 - }) - }); -} - -#[bench] -fn u512_mul(b: &mut Bencher) { - b.iter(|| { - (1..10000).fold(black_box(U512::one()), |old, new| { - old.overflowing_mul(U512::from(black_box(new | 1))).0 - }) - }); -} - -#[bench] -fn u512_mul_small(b: &mut Bencher) { - b.iter(|| { - (1..153).fold(black_box(U512::one()), |old, _| { - old.overflowing_mul(U512::from(black_box(10))).0 - }) - }); -} - -#[bench] -fn u512_rem(b: &mut Bencher) { - let one = U512([8326634216714383706, 15837136097609390493, 13004317189126203332, 7031796866963419685, 12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); - let two = U512([11707750893627518758, 17679501210898117940, 2472932874039724966, 11177683849610900539, 2096410819092764509, 8483673822214032535, 36306297304129857, 3453]); - b.iter(|| { - black_box(one % two); - }); -} - -#[bench] -fn u256_rem(b: &mut Bencher) { - let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); - let two = U256([2096410819092764509, 8483673822214032535, 36306297304129857, 3453]); - b.iter(|| { - black_box(one % two); - }); -} - -#[bench] -fn u512_div(b: &mut Bencher) { - let one = U512([8326634216714383706, 15837136097609390493, 13004317189126203332, 7031796866963419685, 12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); - let two = U512([11707750893627518758, 17679501210898117940, 2472932874039724966, 11177683849610900539, 2096410819092764509, 8483673822214032535, 36306297304129857, 3453]); - b.iter(|| { - black_box(one / two); - }); -} - -#[bench] -fn u256_div(b: &mut Bencher) { +use criterion::{black_box, Bencher, Criterion, ParameterizedBenchmark}; +use num_bigint::BigUint; +use rug::{integer::Order, Integer}; +use std::str::FromStr; + +criterion_group!( + bigint, + u256_add, + u256_sub, + u256_mul, + u256_mul_full, + u256_div, + u256_rem, + u256_bit_and, + u256_bit_or, + u256_bit_xor, + u256_not, + u256_ord, + u256_shl, + u256_shr, + u256_from_le, + u256_from_be, + u512_add, + u512_sub, + u512_mul, + u512_div, + u512_rem, + mulmod_u512_vs_biguint_vs_gmp, + conversions, + u512_bit_and, + u512_bit_or, + u512_bit_xor, + u512_not, + u512_ord, + u512_shl, + u512_shr, + u128_mul, + from_fixed_array, +); +criterion_main!(bigint); + +fn to_biguint(x: U256) -> BigUint { + let mut bytes = [0u8; 32]; + x.to_little_endian(&mut bytes); + BigUint::from_bytes_le(&bytes) +} + +fn from_biguint(x: BigUint) -> U512 { + let bytes = x.to_bytes_le(); + U512::from_little_endian(&bytes) +} + +fn to_gmp(x: U256) -> Integer { + let U256(ref arr) = x; + Integer::from_digits(&arr[..], Order::Lsf) +} + +fn from_gmp(x: Integer) -> U512 { + let digits = x.to_digits(Order::LsfLe); + U512::from_little_endian(&digits) +} + +fn u256_add(c: &mut Criterion) { + c.bench( + "u256_add", + ParameterizedBenchmark::new( + "", + |b, (x, y)| { + b.iter(|| { + let x = U256::from(*x); + let y = U256::from(*y); + black_box(x.overflowing_add(y).0) + }) + }, + vec![(0u64, 1u64), (u64::max_value(), 1), (42, 100500)], + ), + ); +} + +fn u256_sub(c: &mut Criterion) { + c.bench( + "u256_sub", + ParameterizedBenchmark::new( + "", + |b, (x, y)| { + b.iter(|| { + let y = U256::from(*y); + black_box(x.overflowing_sub(y).0) + }) + }, + vec![(U256::max_value(), 1u64), (U256::from(3), 2)], + ), + ); +} + +fn u256_mul(c: &mut Criterion) { + c.bench( + "u256_mul", + ParameterizedBenchmark::new( + "", + |b, (x, y)| { + b.iter(|| { + let y = U256::from(*y); + black_box(x.overflowing_mul(y).0) + }) + }, + vec![(U256::max_value(), 1u64), (U256::from(3), u64::max_value())], + ), + ); +} + +fn u256_mul_full(c: &mut Criterion) { + c.bench( + "u256_mul_full", + ParameterizedBenchmark::new( + "", + |b, (x, y)| { + b.iter(|| { + let y = *y; + let U512(ref u512words) = x.full_mul(U256([y, y, y, y])); + black_box(U256([u512words[0], u512words[2], u512words[2], u512words[3]])) + }) + }, + vec![(U256::from(42), 1u64), (U256::from(3), u64::max_value())], + ), + ); +} + +fn u256_div(c: &mut Criterion) { let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); let two = U256([2096410819092764509, 8483673822214032535, 36306297304129857, 3453]); - b.iter(|| { - black_box(one / two); - }); -} - -#[bench] -fn u256_mul(b: &mut Bencher) { - b.iter(|| { - (1..10000).fold(black_box(U256::one()), |old, new| { - old.overflowing_mul(U256::from(black_box(new | 1))).0 - }) - }); -} - -#[bench] -fn u256_mul_small(b: &mut Bencher) { - b.iter(|| { - (1..77).fold(black_box(U256::one()), |old, _| { - old.overflowing_mul(U256::from(black_box(10))).0 - }) - }); -} - -#[bench] -fn u256_full_mul(b: &mut Bencher) { - b.iter(|| { - let n = black_box(10000); - let one = black_box(U256::one()); - (1..n).map(|n| n | 1).fold(one, |old, new| { - let new = black_box(new); - let U512(ref u512words) = old.full_mul(U256([new, new, new, new])); - U256([u512words[0], u512words[2], u512words[2], u512words[3]]) - }) - }); + c.bench_function("u256_div", move |b| b.iter(|| black_box(one / two))); +} + +fn u256_rem(c: &mut Criterion) { + c.bench( + "u256_rem", + ParameterizedBenchmark::new( + "", + |b, (x, y)| b.iter(|| black_box(x % y)), + vec![ + (U256::max_value(), U256::from(1u64)), + (U256::from(u64::max_value()), U256::from(u64::from(u32::max_value()) + 1)), + ( + U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]), + U256([2096410819092764509, 8483673822214032535, 36306297304129857, 3453]), + ), + ( + U256::from_str( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + ) + .unwrap(), + U256::from_str( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0", + ) + .unwrap(), + ), + ], + ), + ); +} + +fn u512_pairs() -> Vec<(U512, U512)> { + vec![ + (U512::from(1u64), U512::from(0u64)), + (U512::from(u64::max_value()), U512::from(u64::from(u32::max_value()) + 1)), + ( + U512([ + 12767554894655550452, + 16333049135534778834, + 140317443000293558, + 598963, + 0, + 0, + 0, + 0, + ]), + U512([0, 0, 0, 0, 2096410819092764509, 8483673822214032535, 36306297304129857, 3453]), + ), + ( + U512::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") + .unwrap(), + U512::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0") + .unwrap(), + ), + ] +} + +fn u512_add(c: &mut Criterion) { + c.bench( + "u512_add", + ParameterizedBenchmark::new("", |b, (x, y)| b.iter(|| black_box(x + y)), u512_pairs()), + ); +} + +fn u512_sub(c: &mut Criterion) { + c.bench( + "u512_sub", + ParameterizedBenchmark::new( + "", + |b, (x, y)| b.iter(|| black_box(x.overflowing_sub(*y).0)), + u512_pairs(), + ), + ); +} + +fn u512_mul(c: &mut Criterion) { + c.bench( + "u512_mul", + ParameterizedBenchmark::new( + "", + |b, (x, y)| b.iter(|| black_box(x.overflowing_mul(*y).0)), + u512_pairs(), + ), + ); +} + +fn u512_div(c: &mut Criterion) { + let one = U512([ + 8326634216714383706, + 15837136097609390493, + 13004317189126203332, + 7031796866963419685, + 12767554894655550452, + 16333049135534778834, + 140317443000293558, + 598963, + ]); + let two = U512([ + 11707750893627518758, + 17679501210898117940, + 2472932874039724966, + 11177683849610900539, + 2096410819092764509, + 8483673822214032535, + 36306297304129857, + 3453, + ]); + c.bench_function("u512_div", move |b| b.iter(|| black_box(one / two))); +} + +fn u512_rem(c: &mut Criterion) { + let one = U512([ + 8326634216714383706, + 15837136097609390493, + 13004317189126203332, + 7031796866963419685, + 12767554894655550452, + 16333049135534778834, + 140317443000293558, + 598963, + ]); + let two = U512([ + 11707750893627518758, + 17679501210898117940, + 2472932874039724966, + 11177683849610900539, + 2096410819092764509, + 8483673822214032535, + 36306297304129857, + 3453, + ]); + c.bench_function("u512_rem", move |b| b.iter(|| black_box(one % two))); +} + +fn conversions(c: &mut Criterion) { + c.bench( + "conversions biguint vs gmp", + ParameterizedBenchmark::new( + "BigUint", + |b, i| bench_convert_to_biguit(b, *i), + vec![0, 42, u64::max_value()], + ) + .with_function("gmp", |b, i| bench_convert_to_gmp(b, *i)), + ); +} + +fn bench_convert_to_biguit(b: &mut Bencher, i: u64) { + let z = U256::from(i); + let z512 = U512::from(i); + b.iter(|| { + let zb = to_biguint(z); + assert_eq!(from_biguint(zb), z512); + }); +} + +fn bench_convert_to_gmp(b: &mut Bencher, i: u64) { + let z = U256::from(i); + let z512 = U512::from(i); + b.iter(|| { + let zb = to_gmp(z); + assert_eq!(from_gmp(zb), z512); + }); +} + +fn mulmod_u512_vs_biguint_vs_gmp(c: &mut Criterion) { + let mods = vec![1u64, 42, 10_000_001, u64::max_value()]; + c.bench( + "mulmod u512 vs biguint vs gmp", + ParameterizedBenchmark::new("u512", |b, i| bench_u512_mulmod(b, *i), mods) + .with_function("BigUint", |b, i| bench_biguint_mulmod(b, *i)) + .with_function("gmp", |b, i| bench_gmp_mulmod(b, *i)), + ); +} + +fn bench_biguint_mulmod(b: &mut Bencher, i: u64) { + let x = + U256::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(); + let y = + U256::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(); + let z = U256::from(i); + b.iter(|| { + let w = to_biguint(x) * to_biguint(y); + black_box(from_biguint(w % to_biguint(z))) + }); +} + +fn bench_gmp_mulmod(b: &mut Bencher, i: u64) { + let x = + U256::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(); + let y = + U256::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(); + let z = U256::from(i); + b.iter(|| { + let w = to_gmp(x) * to_gmp(y); + black_box(from_gmp(w % to_gmp(z))) + }); +} + +fn bench_u512_mulmod(b: &mut Bencher, i: u64) { + let x = + U512::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(); + let y = + U512::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(); + let z = U512::from(i); + b.iter(|| { + let w = x.overflowing_mul(y).0; + black_box(w % z) + }); } - -#[bench] // NOTE: uses native `u128` and does not measure this crates performance, // but might be interesting as a comparison. -fn u128_mul(b: &mut Bencher) { - b.iter(|| { - let n = black_box(10000); - (1..n).fold(12345u128, |old, new| { - old.overflowing_mul(u128::from(new | 1u32)).0 - }) - }); +fn u128_mul(c: &mut Criterion) { + c.bench_function("u128_mul", |b| { + b.iter(|| black_box(12345u128 * u128::from(u64::max_value()))) + }); } -#[bench] -fn u256_bit_and(b: &mut Bencher) { +fn u256_bit_and(c: &mut Criterion) { let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); let two = U256([2096410819092764509, 8483673822214032535, 36306297304129857, 3453]); - b.iter(|| { - black_box(one) & black_box(two) - }); -} - -#[bench] -fn u512_bit_and(b: &mut Bencher) { - let one = U512([8326634216714383706, 15837136097609390493, 13004317189126203332, 7031796866963419685, 12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); - let two = U512([11707750893627518758, 17679501210898117940, 2472932874039724966, 11177683849610900539, 2096410819092764509, 8483673822214032535, 36306297304129857, 3453]); - b.iter(|| { - black_box(one) & black_box(two) - }); -} - -#[bench] -fn u256_bit_xor(b: &mut Bencher) { + c.bench_function("u256_bit_and", move |b| b.iter(|| black_box(one & two))); +} + +fn u512_bit_and(c: &mut Criterion) { + let one = U512([ + 8326634216714383706, + 15837136097609390493, + 13004317189126203332, + 7031796866963419685, + 12767554894655550452, + 16333049135534778834, + 140317443000293558, + 598963, + ]); + let two = U512([ + 11707750893627518758, + 17679501210898117940, + 2472932874039724966, + 11177683849610900539, + 2096410819092764509, + 8483673822214032535, + 36306297304129857, + 3453, + ]); + c.bench_function("u512_bit_and", move |b| b.iter(|| black_box(one & two))); +} + +fn u256_bit_xor(c: &mut Criterion) { let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); let two = U256([2096410819092764509, 8483673822214032535, 36306297304129857, 3453]); - b.iter(|| { - black_box(one) ^ black_box(two) - }); -} - -#[bench] -fn u512_bit_xor(b: &mut Bencher) { - let one = U512([8326634216714383706, 15837136097609390493, 13004317189126203332, 7031796866963419685, 12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); - let two = U512([11707750893627518758, 17679501210898117940, 2472932874039724966, 11177683849610900539, 2096410819092764509, 8483673822214032535, 36306297304129857, 3453]); - b.iter(|| { - black_box(one) ^ black_box(two) - }); -} - -#[bench] -fn u256_bit_or(b: &mut Bencher) { + c.bench_function("u256_bit_xor", move |b| b.iter(|| black_box(one ^ two))); +} + +fn u512_bit_xor(c: &mut Criterion) { + let one = U512([ + 8326634216714383706, + 15837136097609390493, + 13004317189126203332, + 7031796866963419685, + 12767554894655550452, + 16333049135534778834, + 140317443000293558, + 598963, + ]); + let two = U512([ + 11707750893627518758, + 17679501210898117940, + 2472932874039724966, + 11177683849610900539, + 2096410819092764509, + 8483673822214032535, + 36306297304129857, + 3453, + ]); + c.bench_function("u512_bit_xor", move |b| b.iter(|| black_box(one ^ two))); +} + +fn u256_bit_or(c: &mut Criterion) { let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); let two = U256([2096410819092764509, 8483673822214032535, 36306297304129857, 3453]); - b.iter(|| { - black_box(one) | black_box(two) - }); -} - -#[bench] -fn u512_bit_or(b: &mut Bencher) { - let one = U512([8326634216714383706, 15837136097609390493, 13004317189126203332, 7031796866963419685, 12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); - let two = U512([11707750893627518758, 17679501210898117940, 2472932874039724966, 11177683849610900539, 2096410819092764509, 8483673822214032535, 36306297304129857, 3453]); - b.iter(|| { - black_box(one) | black_box(two) - }); -} - -#[bench] -fn u256_not(b: &mut Bencher) { + c.bench_function("u256_bit_or", move |b| b.iter(|| black_box(one | two))); +} + +fn u512_bit_or(c: &mut Criterion) { + let one = U512([ + 8326634216714383706, + 15837136097609390493, + 13004317189126203332, + 7031796866963419685, + 12767554894655550452, + 16333049135534778834, + 140317443000293558, + 598963, + ]); + let two = U512([ + 11707750893627518758, + 17679501210898117940, + 2472932874039724966, + 11177683849610900539, + 2096410819092764509, + 8483673822214032535, + 36306297304129857, + 3453, + ]); + c.bench_function("u512_bit_or", move |b| b.iter(|| black_box(one | two))); +} + +fn u256_not(c: &mut Criterion) { let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); - b.iter(|| { - !black_box(one) - }); + c.bench_function("u256_not", move |b| b.iter(|| black_box(!one))); } -#[bench] -fn u512_not(b: &mut Bencher) { - let one = U512([8326634216714383706, 15837136097609390493, 13004317189126203332, 7031796866963419685, 12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); - b.iter(|| { - !black_box(one) - }); +fn u512_not(c: &mut Criterion) { + let one = U512([ + 8326634216714383706, + 15837136097609390493, + 13004317189126203332, + 7031796866963419685, + 12767554894655550452, + 16333049135534778834, + 140317443000293558, + 598963, + ]); + c.bench_function("u512_not", move |b| b.iter(|| black_box(!one))); } -#[bench] -fn u256_shl(b: &mut Bencher) { +fn u256_shl(c: &mut Criterion) { let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); - b.iter(|| { - black_box(one) << 128 - }); + c.bench_function("u256_shl", move |b| b.iter(|| black_box(one << 128))); } -#[bench] -fn u512_shl(b: &mut Bencher) { - let one = U512([8326634216714383706, 15837136097609390493, 13004317189126203332, 7031796866963419685, 12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); - b.iter(|| { - black_box(one) << 128 - }); +fn u512_shl(c: &mut Criterion) { + let one = U512([ + 8326634216714383706, + 15837136097609390493, + 13004317189126203332, + 7031796866963419685, + 12767554894655550452, + 16333049135534778834, + 140317443000293558, + 598963, + ]); + c.bench_function("u512_shl", move |b| b.iter(|| black_box(one >> 128))); } -#[bench] -fn u256_shr(b: &mut Bencher) { +fn u256_shr(c: &mut Criterion) { let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); - b.iter(|| { - black_box(one) >> 128 - }); + c.bench_function("u256_shr", move |b| b.iter(|| black_box(one >> 128))); } -#[bench] -fn u512_shr(b: &mut Bencher) { - let one = U512([8326634216714383706, 15837136097609390493, 13004317189126203332, 7031796866963419685, 12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); - b.iter(|| { - black_box(one) >> 128 - }); +fn u512_shr(c: &mut Criterion) { + let one = U512([ + 8326634216714383706, + 15837136097609390493, + 13004317189126203332, + 7031796866963419685, + 12767554894655550452, + 16333049135534778834, + 140317443000293558, + 598963, + ]); + c.bench_function("u512_shr", move |b| b.iter(|| black_box(one >> 128))); } -#[bench] -fn u256_ord(b: &mut Bencher) { +fn u256_ord(c: &mut Criterion) { let one = U256([12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); let two = U256([2096410819092764509, 8483673822214032535, 36306297304129857, 3453]); - b.iter(|| { - black_box(one) < black_box(two) - }); -} - -#[bench] -fn u512_ord(b: &mut Bencher) { - let one = U512([8326634216714383706, 15837136097609390493, 13004317189126203332, 7031796866963419685, 12767554894655550452, 16333049135534778834, 140317443000293558, 598963]); - let two = U512([11707750893627518758, 17679501210898117940, 2472932874039724966, 11177683849610900539, 2096410819092764509, 8483673822214032535, 36306297304129857, 3453]); - b.iter(|| { - black_box(one) < black_box(two) - }); -} - -#[bench] -fn u256_from_le(b: &mut Bencher) { - b.iter(|| { - let raw = black_box( - [ - 1u8, - 2, - 3, - 5, - 7, - 11, - 13, - 17, - 19, - 23, - 29, - 31, - 37, - 41, - 43, - 47, - 53, - 59, - 61, - 67, - 71, - 73, - 79, - 83, - 89, - 97, - 101, - 103, - 107, - 109, - 113, - 127, - ], - ); - let _ = U256::from_little_endian(&raw[..]); - }); -} - -#[bench] -fn u256_from_be(b: &mut Bencher) { - b.iter(|| { - let raw = black_box( - [ - 1u8, - 2, - 3, - 5, - 7, - 11, - 13, - 17, - 19, - 23, - 29, - 31, - 37, - 41, - 43, - 47, - 53, - 59, - 61, - 67, - 71, - 73, - 79, - 83, - 89, - 97, - 101, - 103, - 107, - 109, - 113, - 127, - ], - ); - let _ = U256::from_big_endian(&raw[..]); - }); -} - -#[bench] -fn from_fixed_array(b: &mut Bencher) { - let ary512 : [u8; 64] = [ - 255, 0, 0, 123, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 121, 0, 0, 0, 0, - 0, 213, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 100, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 45, 0, 0, 67, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 123 - ]; - let ary256 : [u8; 32] = [ - 255, 0, 0, 123, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 121, 0, 0, 0, 0, - 0, 213, 0, 0, 0, 0, 0, 0, - ]; - b.iter(|| { - let n = black_box(1000); - for _i in 0..n { - let _ : U512 = black_box(ary512).into(); - let _ : U256 = black_box(ary256).into(); - } - }) + c.bench_function("u256_ord", move |b| b.iter(|| black_box(one < two))); +} + +fn u512_ord(c: &mut Criterion) { + let one = U512([ + 8326634216714383706, + 15837136097609390493, + 13004317189126203332, + 7031796866963419685, + 12767554894655550452, + 16333049135534778834, + 140317443000293558, + 598963, + ]); + let two = U512([ + 11707750893627518758, + 17679501210898117940, + 2472932874039724966, + 11177683849610900539, + 2096410819092764509, + 8483673822214032535, + 36306297304129857, + 3453, + ]); + c.bench_function("u512_ord", move |b| b.iter(|| black_box(one < two))); +} + +fn u256_from_le(c: &mut Criterion) { + c.bench_function("u256_from_le", |b| { + b.iter(|| { + let raw = black_box([ + 1u8, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, + ]); + black_box(U256::from_little_endian(&raw[..])) + }) + }); +} + +fn u256_from_be(c: &mut Criterion) { + c.bench_function("u256_from_be", |b| { + b.iter(|| { + let raw = black_box([ + 1u8, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, + ]); + black_box(U256::from_big_endian(&raw[..])) + }) + }); +} + +fn from_fixed_array(c: &mut Criterion) { + let ary512: [u8; 64] = [ + 255, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 0, 0, 0, 0, 0, 213, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, 0, 67, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 123, + ]; + let ary256: [u8; 32] = [ + 255, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 0, 0, 0, 0, 0, 213, 0, 0, + 0, 0, 0, 0, + ]; + c.bench_function("from_fixed_array", move |b| { + b.iter(|| { + let _: U512 = black_box(ary512.into()); + let _: U256 = black_box(ary256.into()); + }) + }); } From 4310011dc365a27d73908999ce93bae99f4605a1 Mon Sep 17 00:00:00 2001 From: Andronik Ordian Date: Tue, 7 May 2019 13:44:06 +0200 Subject: [PATCH 7/8] fix rocksdb block cache size (#122) * rocksdb: use KB and MB as constants * fix(rocksdb): cap block lru cache in 8 MB, not 8 bytes * rocksdb: remove the minimum on block cache size, add a comment --- kvdb-rocksdb/src/lib.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/kvdb-rocksdb/src/lib.rs b/kvdb-rocksdb/src/lib.rs index eceebbc43..717a860ee 100644 --- a/kvdb-rocksdb/src/lib.rs +++ b/kvdb-rocksdb/src/lib.rs @@ -59,6 +59,8 @@ fn other_io_err(e: E) -> io::Error where E: Into CompactionProfile { CompactionProfile { - initial_file_size: 64 * 1024 * 1024, - block_size: 16 * 1024, + initial_file_size: 64 * MB as u64, + block_size: 16 * KB, write_rate_limit: None, } } @@ -151,9 +153,9 @@ impl CompactionProfile { /// Slow HDD compaction profile pub fn hdd() -> CompactionProfile { CompactionProfile { - initial_file_size: 256 * 1024 * 1024, - block_size: 64 * 1024, - write_rate_limit: Some(16 * 1024 * 1024), + initial_file_size: 256 * MB as u64, + block_size: 64 * KB, + write_rate_limit: Some(16 * MB as u64), } } } @@ -181,7 +183,7 @@ impl DatabaseConfig { } pub fn memory_budget(&self) -> usize { - self.memory_budget.unwrap_or(DB_DEFAULT_MEMORY_BUDGET_MB) * 1024 * 1024 + self.memory_budget.unwrap_or(DB_DEFAULT_MEMORY_BUDGET_MB) * MB } pub fn memory_budget_per_col(&self) -> usize { @@ -303,7 +305,9 @@ impl Database { { block_opts.set_block_size(config.compaction.block_size); - let cache_size = cmp::max(8, config.memory_budget() / 3); + // Set cache size as recommended by + // https://github.com/facebook/rocksdb/wiki/Setup-Options-and-Basic-Tuning#block-cache-size + let cache_size = config.memory_budget() / 3; let cache = Cache::new(cache_size); block_opts.set_cache(cache); } From a1ea210c144d9f1dc28577d73ee88220b8e49f9e Mon Sep 17 00:00:00 2001 From: Andronik Ordian Date: Tue, 7 May 2019 15:54:43 +0200 Subject: [PATCH 8/8] uint: faster mul by u64 (#125) * uint: convert benchmarks to criterion * rustfmt * add u256_rem_small * add u512_rem_large bench * uint: add mulmod comparison benches * uint: add comparison with gmp * uint: clean up benches * uint: fix windows ci * uint: fix gmp conversions * uint: make sure bytes are little endian for gmp conversion * uint: compare mul by u32 and u64 * uint: fast path for mul by u64 * uint: make free-standing functions static * uint: remove fast-path for mul by u64 --- uint/benches/bigint.rs | 35 +++++++- uint/src/uint.rs | 180 +++++++++++++++++++++-------------------- 2 files changed, 128 insertions(+), 87 deletions(-) diff --git a/uint/benches/bigint.rs b/uint/benches/bigint.rs index a19d3819d..53829064f 100644 --- a/uint/benches/bigint.rs +++ b/uint/benches/bigint.rs @@ -62,6 +62,7 @@ criterion_group!( u512_mul, u512_div, u512_rem, + u512_mul_u32_vs_u64, mulmod_u512_vs_biguint_vs_gmp, conversions, u512_bit_and, @@ -141,7 +142,14 @@ fn u256_mul(c: &mut Criterion) { black_box(x.overflowing_mul(y).0) }) }, - vec![(U256::max_value(), 1u64), (U256::from(3), u64::max_value())], + vec![ + (U256::max_value(), 1u64), + (U256::from(3), u64::max_value()), + ( + U256::from_dec_str("21674844646682989462120101885968193938394323990565507610662749").unwrap(), + 173, + ), + ], ), ); } @@ -330,6 +338,31 @@ fn bench_convert_to_gmp(b: &mut Bencher, i: u64) { }); } +fn u512_mul_u32_vs_u64(c: &mut Criterion) { + let mods = vec![1u32, 42, 10_000_001, u32::max_value()]; + c.bench( + "multiply u512 by u32 vs u64", + ParameterizedBenchmark::new("u32", |b, i| bench_u512_mul_u32(b, *i), mods) + .with_function("u64", |b, i| bench_u512_mul_u64(b, u64::from(*i))), + ); +} + +fn bench_u512_mul_u32(b: &mut Bencher, i: u32) { + let x = + U512::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(); + b.iter(|| { + black_box(x * i) + }); +} + +fn bench_u512_mul_u64(b: &mut Bencher, i: u64) { + let x = + U512::from_str("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").unwrap(); + b.iter(|| { + black_box(x * i) + }); +} + fn mulmod_u512_vs_biguint_vs_gmp(c: &mut Criterion) { let mods = vec![1u64, 42, 10_000_001, u64::max_value()]; c.bench( diff --git a/uint/src/uint.rs b/uint/src/uint.rs index 33f1dd9c9..03d0f03df 100644 --- a/uint/src/uint.rs +++ b/uint/src/uint.rs @@ -173,7 +173,7 @@ macro_rules! uint_full_mul_reg { if $check(me[j], carry) { let a = me[j]; - let (hi, low) = $crate::split_u128(a as u128 * b as u128); + let (hi, low) = Self::split_u128(a as u128 * b as u128); let overflow = { let existing_low = &mut ret[i + j]; @@ -303,13 +303,7 @@ macro_rules! impl_mul_from { result } } - } -} -#[macro_export] -#[doc(hidden)] -macro_rules! impl_mulassign_from { - ($name: ident, $other: ident) => { impl $crate::core_::ops::MulAssign<$other> for $name { fn mul_assign(&mut self, other: $other) { let result = *self * other; @@ -319,29 +313,57 @@ macro_rules! impl_mulassign_from { } } -#[inline(always)] +#[macro_export] #[doc(hidden)] -pub fn mul_u32(a: (u64, u64), b: u64, carry: u64) -> (u64, u64) { - let upper = b * a.0; - let lower = b * a.1; +macro_rules! impl_mul_for_primitive { + ($name: ty, $other: ident) => { + impl $crate::core_::ops::Mul<$other> for $name { + type Output = $name; - let (res1, overflow1) = lower.overflowing_add(upper << 32); - let (res2, overflow2) = res1.overflowing_add(carry); + fn mul(self, other: $other) -> $name { + let (result, carry) = self.overflowing_mul_u64(other as u64); + panic_on_overflow!(carry > 0); + result + } + } - let carry = (upper >> 32) + overflow1 as u64 + overflow2 as u64; - (res2, carry) -} + impl<'a> $crate::core_::ops::Mul<&'a $other> for $name { + type Output = $name; -#[inline(always)] -#[doc(hidden)] -pub fn split(a: u64) -> (u64, u64) { - (a >> 32, a & 0xFFFF_FFFF) -} + fn mul(self, other: &'a $other) -> $name { + let (result, carry) = self.overflowing_mul_u64(*other as u64); + panic_on_overflow!(carry > 0); + result + } + } -#[inline(always)] -#[doc(hidden)] -pub fn split_u128(a: u128) -> (u64, u64) { - ((a >> 64) as _, (a & 0xFFFFFFFFFFFFFFFF) as _) + impl<'a> $crate::core_::ops::Mul<&'a $other> for &'a $name { + type Output = $name; + + fn mul(self, other: &'a $other) -> $name { + let (result, carry) = self.overflowing_mul_u64(*other as u64); + panic_on_overflow!(carry > 0); + result + } + } + + impl<'a> $crate::core_::ops::Mul<$other> for &'a $name { + type Output = $name; + + fn mul(self, other: $other) -> $name { + let (result, carry) = self.overflowing_mul_u64(other as u64); + panic_on_overflow!(carry > 0); + result + } + } + + impl $crate::core_::ops::MulAssign<$other> for $name { + fn mul_assign(&mut self, other: $other) { + let result = *self * (other as u64); + *self = result + } + } + } } #[macro_export] @@ -428,8 +450,8 @@ macro_rules! construct_uint { let mut res = Self::default(); for b in value.bytes().map(|b| b - 48) { - let (r, overflow) = res.overflowing_mul_u32(10); - if overflow { + let (r, overflow) = res.overflowing_mul_u64(10); + if overflow > 0 { return Err($crate::FromDecStrErr::InvalidLength); } let (r, overflow) = r.overflowing_add(b.into()); @@ -512,6 +534,15 @@ macro_rules! construct_uint { return true; } + // Whether this fits u64. + #[inline] + fn fits_word(&self) -> bool { + let &$name(ref arr) = self; + for i in 1..$n_words { if arr[i] != 0 { return false; } } + return true; + } + + /// Return the least number of bits needed to represent the number #[inline] pub fn bits(&self) -> usize { @@ -788,20 +819,35 @@ macro_rules! construct_uint { } } - /// Overflowing multiplication by u32. - fn overflowing_mul_u32(self, other: u32) -> (Self, bool) { - let $name(ref arr) = self; - let mut ret = [0u64; $n_words]; - let mut carry = 0; - let o = other as u64; + #[inline(always)] + fn mul_u64(a: u64, b: u64, carry: u64) -> (u64, u64) { + let (hi, lo) = Self::split_u128(u128::from(a) * u128::from(b) + u128::from(carry)); + (lo, hi) + } - for i in 0..$n_words { - let (res, carry2) = $crate::mul_u32($crate::split(arr[i]), o, carry); - ret[i] = res; - carry = carry2; + #[inline(always)] + fn split(a: u64) -> (u64, u64) { + (a >> 32, a & 0xFFFF_FFFF) + } + + #[inline(always)] + fn split_u128(a: u128) -> (u64, u64) { + ((a >> 64) as _, (a & 0xFFFFFFFFFFFFFFFF) as _) + } + + + /// Overflowing multiplication by u64. + /// Returns the result and carry. + fn overflowing_mul_u64(mut self, other: u64) -> (Self, u64) { + let mut carry = 0u64; + + for d in self.0.iter_mut() { + let (res, c) = Self::mul_u64(*d, other, carry); + *d = res; + carry = c; } - ($name(ret), carry > 0) + (self, carry) } /// Converts from big endian representation bytes in memory. @@ -950,56 +996,18 @@ macro_rules! construct_uint { } } - // specialization for u32 - impl $crate::core_::ops::Mul for $name { - type Output = $name; - - fn mul(self, other: u32) -> $name { - let (ret, overflow) = self.overflowing_mul_u32(other); - panic_on_overflow!(overflow); - ret - } - } - - impl<'a> $crate::core_::ops::Mul for &'a $name { - type Output = $name; - - fn mul(self, other: u32) -> $name { - *self * other - } - } - - impl $crate::core_::ops::MulAssign for $name { - fn mul_assign(&mut self, other: u32) { - let result = *self * other; - *self = result - } - } - // all other impls - impl_mul_from!($name, u8); - impl_mul_from!($name, u16); - impl_mul_from!($name, u64); - impl_mul_from!($name, usize); - - impl_mul_from!($name, i8); - impl_mul_from!($name, i16); - impl_mul_from!($name, i64); - impl_mul_from!($name, isize); - impl_mul_from!($name, $name); - - impl_mulassign_from!($name, u8); - impl_mulassign_from!($name, u16); - impl_mulassign_from!($name, u64); - impl_mulassign_from!($name, usize); - - impl_mulassign_from!($name, i8); - impl_mulassign_from!($name, i16); - impl_mulassign_from!($name, i64); - impl_mulassign_from!($name, isize); - - impl_mulassign_from!($name, $name); + impl_mul_for_primitive!($name, u8); + impl_mul_for_primitive!($name, u16); + impl_mul_for_primitive!($name, u32); + impl_mul_for_primitive!($name, u64); + impl_mul_for_primitive!($name, usize); + impl_mul_for_primitive!($name, i8); + impl_mul_for_primitive!($name, i16); + impl_mul_for_primitive!($name, i32); + impl_mul_for_primitive!($name, i64); + impl_mul_for_primitive!($name, isize); impl $crate::core_::ops::Div for $name where T: Into<$name> { type Output = $name;