rustcrypto / stream-ciphers Goto Github PK
View Code? Open in Web Editor NEWCollection of stream cipher algorithms
Collection of stream cipher algorithms
aes-256-cfb, after run a period of time, got error:
thread 'main' panicked at 'range end index 8444544247700826640 out of range for slice of length 16', /root/.cargo/registry/src/github.com-1ecc6299db9ec823/cfb-mode-0.7.1/src/lib.rs:85:33
All three of these crates use highly similar (copypasta) buffering logic:
Original ctr
code:
Derived chacha20
and salsa20
code:
The main problem is the ctr
is presently specialized to Ctr128
and intended to operate in conjunction with a block cipher, whereas it'd be nice if ChaCha20
and Salsa20
took an integer (32-bit and 64-bit respectively) used to compute the block, but reused the same buffering logic as Ctr128
.
ChaCha20 could also benefit from parallel block computation via its AVX2 backend.
There are several constructions based on stream ciphers that use a 32-bit counter:
I was thinking it would be interesting to have a Ctr32
type which is generic around the block size. If so, I could impl the ChaCha20 and Salsa20 core functions as a sort of 512-bit wide pseudo-BlockCipher
, which would let us have reusable stream cipher buffer management and seeking across all three of the aforementioned ciphers. Note that pulling this off would also need for it to be generic around endianness, as I believe AES-GCM needs a big endian counter and AES-GCM-SIV and ChaCha20Poly1305 need a little endian counter.
If we had that, it would also eliminate the need for the salsa20-core
crate, or rather, we could merge it into the salsa20
crate.
Notably right now the only other ctr
type, Ctr128
, is specialized to a 128-bit block size. This actually seems ok to me, as it seems rather unusual to use a 128-bit counter with anything other than a 128-bit block size.
The minimum Rust version for this repo is presently 1.27.0.
I'd be interested in bumping that up to 1.31.0 and doing a 2018 edition upgrade on all crates.
I've noticed that #333 is missing zeroize
for the SIMD backends, and that the zeroize
crate seems to support SIMD registers. There are 2 ways that I can identify for incorporating zeroize
. Both methods, however, would require the MSRV to be increased to 1.60
.
The first method is kind of easy, as it requires a relatively small amount of code, but it is a little inefficient. Basically, .zeroize()
could be called on the SIMD results arrays, as well as the state arrays after generating results.
Pros:
Cons:
PAR_BLOCKS
blocks of output, it will need to zeroize the SIMD registersThis would involve a little bit of a reimplementation of some features that chacha20
previously had (persisting Core
structs via autodetect.rs
and backend.rs
). The persisting Core
structs can provide a few benefits:
.zeroize()
could only be applied once to the SIMD registers, instead of every time the Core
generates resultsunsafe fn generate(&mut self, dest_ptr: *mut u8, num_blocks: usize)
to achieve a performance that is comparable with .apply_keystream()
on AVX2... unless .apply_keystream()
's performance also increases by 5-7%. The RNG could still benefit from using a pointer though.Cons:
Here's a link to v0.8.1
for reference. I will need it if I will be adding the functionality back:
https://github.com/RustCrypto/stream-ciphers/blob/338c078d731692fba3b8256e45de2c3e334d46d8/chacha20/src/backend.rs
Hi thanks for making the cfb-mode crate, think it will work well for my purposes, so I started converting from openssl, however, I'm seeing different decryption output after the first call as compared to openssl. Reduced to this example:
extern crate aes;
extern crate cfb_mode;
use aes::Aes128;
use cfb_mode::Cfb;
type Aes128Cfb = Cfb<Aes128>;
fn main() {
let key = &[17, 72, 215, 19, 142, 61, 79, 230, 54, 89, 66, 160, 184, 156, 232, 31];
let mut cipher = Aes128Cfb::new_var(key, key).unwrap();
let mut data = [130];
cipher.decrypt(&mut data);
println!("decrypted = {:?}", data);
let mut data2 = [238, 158, 146];
cipher.decrypt(&mut data2);
println!("decrypted2 = {:?}", data2);
}
and for ease of comparison (although I also see the correct output with the openssl 0.7.8 crate), this Python script using PyCrypto decrypting the same data:
from Crypto.Cipher import AES
key=b'\x11H\xd7\x13\x8e=O\xe66YB\xa0\xb8\x9c\xe8\x1f'
iv=key
cipher=AES.new(key, AES.MODE_CFB, iv, segment_size=8)
print "decrypted =",map(ord, cipher.decrypt("\x82")) # expect [3]
print "decrypted2 =",map(ord, cipher.decrypt("\xee\x9e\x92")) # expect [3, 128, 2]
Python (and OpenSSL) decrypts to what I expect:
decrypted = [3]
decrypted2 = [3, 128, 2]
Rust with cfb-mode decrypts the first time ok, but then the second decrypted2 result is different:
decrypted = [3]
decrypted2 = [210, 218, 215]
Hello,
Today ChaCha20 (and by extension (X)ChaCha20-Poly1305
) is painfully slow on arm64 devices.
NEON support was merged last year (#310) but is still not available for use.
Is it possible to publish a new version and update the docs so we can use the NEON version?
I can create a PR for the doc (adding a few explanation on how to enable NEON support) if necessary.
Many thanks!
There are two big optimizations we could do on both the chacha20
and salsa20
crates.
EDIT: both crates now have a new
method to compute the initial state, and separate apply_keystream
/ generate
methods to compute a block
chacha20
cratesalsa20
crateRFC 8439 Section 3 describes caching the initial block state once computed as a performance optimization:
Each block of ChaCha20 involves 16 move operations and one increment
operation for loading the state, 80 each of XOR, addition and roll
operations for the rounds, 16 more add operations and 16 XOR
operations for protecting the plaintext. Section 2.3 describes the
ChaCha block function as "adding the original input words". This
implies that before starting the rounds on the ChaCha state, we copy
it aside, only to add it in later. This is correct, but we can save
a few operations if we instead copy the state and do the work on the
copy. This way, for the next block you don't need to recreate the
state, but only to increment the block counter. This saves
approximately 5.5% of the cycles.
Both ChaCha20 and Salsa20 are amenable to SIMD optimizations. We should add SIMD optimizations on x86
/x86_64
at the very least.
x86
/x86_64
cc @emc2 @sseemayer
There's presently a warning for salsa20-core
:
https://github.com/RustCrypto/stream-ciphers/blob/1dc6af/salsa20-core/src/salsa_family_state.rs#L96
Warning from CI:
https://travis-ci.org/RustCrypto/stream-ciphers/jobs/571061677#L202
warning: value assigned to `word_idx` is never read
--> salsa20-core/src/salsa_family_state.rs:96:25
|
96 | word_idx = 0;
| ^^^^^^^^
|
= note: #[warn(unused_assignments)] on by default
= help: maybe it is overwritten before being read?
It's unclear to me what the intention was here, or if it can simply be removed. The "maybe it is overwritten before being read" help text has me curious if in the refactor it isn't being preserved somewhere it needed to be read, or if it's now being unconditionally overwritten in a way it shouldn't be.
Hello, we're currently migrating from rust-crypto crate because of wasm-build related problems (see issue emeraldpay/emerald-vault#286) and the decomposition you've done looks very promising in terms of integration into wasm-capable project.
What's the current state of stream-ciphers and when do you expect it to be possible to post it on crates.io?
Several crates in this repo use the aes
crate as a dev-dependency.
The aes
crate lists ctr
in its (hard) dependencies
section now.
This means that upgrading the cipher
crate right now involves at least a temporary breakage. Here are some example PRs where this has happened:
There are at least two different ways this could be addressed, I think:
aes
crate into the ctr
crate, since it's not ctr
-specificdev-dependency
Each of these crates could use a README.md with some basic information, and the following added to their Cargo.toml files:
readme = "README.md"
Right now they look pretty bare on crates.io, e.g.:
Hello, I noticed that there isn't an implementation of XSalsa20 in the salsa20
crate. Is there any particular reason for this? If it's just because no one has gotten around to it yet, I have had a go at implementing this myself (https://github.com/joejacobs/stream-ciphers). Happy to create a PR if you'd like.
Hi, can you add the rabbit encryption method? I am using rabbit in my c++ project using cryptopp, the reason is it is said rabbit is the fastest stream cipher. Thank you for awesome job!
None of chacha20::ChaCha*Rng
implement CryptoRng
despite rand_chacha::ChaCha*Rng
doing so. Oversight or intended?
Edit: My motivating use is for a deterministic impl RngCore + CryptoRng
for tests.
Similar to #2, I am migrating away from rust-crypto
and would like to use the Salsa20 and ChaCha20 implementations contained in stream-ciphers
. Would it be possible to push them to crates.io?
But most other crates now depend on [email protected] making it impossible to upgrade for me
I think to better optimize the ChaCha20 implementation we might consider switching to something like the BlockCipher
trait (or potentially impling it and optionally exposing the raw block function as a BlockCipher
), which I think would make it easier to implement a parallel implementation and also provides a common interface for all ChaCha20 block function implementations to conform to, e.g. using the encrypt_blocks
method:
(we could also use a ChaCha20-specific internal trait to avoid the extra dependency if we don't actually want to expose the block-based API publically)
I'd also like to make salsa20-core
an optional dependency, allowing the crate to be used solely as a rand-core
RNG instead, and if we expose the raw block function, via that API instead.
The chacha20poly1305
crate has much simpler needs than what the full stream-cipher
API exposes, since it's doing a fairly straightforward CTR-mode-on-a-buffer operation, which makes it much easier to produce an optimized parallel implementation (with fewer dependencies).
Here's an example of the same idea in the aes-gcm
crate:
https://github.com/RustCrypto/AEADs/blob/master/aes-gcm/src/ctr32.rs#L62
Interestingly, while chacha20 seek fails at values > 1<<38, x chacha20 works for the entire range of u64.
use chacha20::cipher::{NewCipher, StreamCipher, StreamCipherSeek};
fn chacha20_seek() {
let key: chacha20::Key = [0u8; 32].into();
let nonce: chacha20::Nonce = [0u8; 12].into();
let mut cipher = chacha20::ChaCha20::new(&key, &nonce);
cipher.seek(1u64<<37); // 38 fails
let mut data = [1u8; 1024];
cipher.apply_keystream(&mut data);
}
fn xchacha20_seek() {
let key: chacha20::Key = [0u8; 32].into();
let nonce: chacha20::XNonce = [0u8; 24].into();
let mut cipher = chacha20::XChaCha20::new(&key, &nonce);
cipher.seek(1u64<<62);
let mut data = [1u8; 1024];
cipher.apply_keystream(&mut data);
}
In #276 they have been left for a later PR.
hedy can you please provide some sample code as to how to encrypt strings i tried everything thanks mate.
Running it locally for me gives:
Compiling aes v0.3.2
error: cannot find macro `vec!` in this scope
--> aes-ctr/benches/aes128_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error: cannot find macro `vec!` in this scope
--> aes-ctr/benches/aes256_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error: cannot find macro `vec!` in this scope
--> aes-ctr/benches/aes192_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes192Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0412]: cannot find type `Vec` in this scope
--> aes-ctr/benches/aes256_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0412]: cannot find type `Vec` in this scope
--> aes-ctr/benches/aes128_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0412]: cannot find type `Vec` in this scope
--> aes-ctr/benches/aes192_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes192Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes256_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes256_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes256_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes256_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes256_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error: aborting due to 7 previous errors
Some errors have detailed explanations: E0277, E0412.
For more information about an error, try `rustc --explain E0277`.
error: Could not compile `aes-ctr`.
warning: build failed, waiting for other jobs to finish...
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes192_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes192Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes128_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes192_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes192Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes192_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes192Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes128_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes128_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes192_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes192Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes192_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes192Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes128_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error: aborting due to 7 previous errors
Some errors have detailed explanations: E0277, E0412.
For more information about an error, try `rustc --explain E0277`.
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> aes-ctr/benches/aes128_ctr.rs:7:1
|
7 | bench_sync!(aes_ctr::Aes128Ctr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `core::marker::Sized` is not implemented for `[u8]`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error: aborting due to 7 previous errors
They want to use this feature in shadowsocks-crypto
crate, but this is not yet available. When will this feature available version be published?
see also: shadowsocks/shadowsocks-crypto#19
Maybe we could have a feature to ignore MSRV, that is not activated by default
While the example from the documentation works for Aes128Ctr, it does not seem to work for Aes256Ctr.
error[E0308]: mismatched types
--> quinn-proto/src/crypto.rs:467:41
|
467 | AesCtr256 => Aes256Ctr::new(key, nonce).apply_keystream(in_out),
| ^^^ expected struct `aes_ctr::stream_cipher::generic_array::typenum::UInt`, found struct `aes_ctr::stream_cipher::generic_array::typenum::UTerm`
|
= note: expected type `&aes_ctr::stream_cipher::generic_array::GenericArray<u8, aes_ctr::stream_cipher::generic_array::typenum::UInt<aes_ctr::stream_cipher::generic_array::typenum::UInt<aes_ctr::stream_cipher::generic_array::typenum::UInt<aes_ctr::stream_cipher::generic_array::typenum::UInt<aes_ctr::stream_cipher::generic_array::typenum::UInt<aes_ctr::stream_cipher::generic_array::typenum::UInt<aes_ctr::stream_cipher::generic_array::typenum::UTerm, aes_ctr::stream_cipher::generic_array::typenum::B1>, aes_ctr::stream_cipher::generic_array::typenum::B0>, aes_ctr::stream_cipher::generic_array::typenum::B0>, aes_ctr::stream_cipher::generic_array::typenum::B0>, aes_ctr::stream_cipher::generic_array::typenum::B0>, aes_ctr::stream_cipher::generic_array::typenum::B0>>`
found type `&aes_ctr::stream_cipher::generic_array::GenericArray<u8, aes_ctr::stream_cipher::generic_array::typenum::UInt<aes_ctr::stream_cipher::generic_array::typenum::UInt<aes_ctr::stream_cipher::generic_array::typenum::UInt<aes_ctr::stream_cipher::generic_array::typenum::UInt<aes_ctr::stream_cipher::generic_array::typenum::UInt<aes_ctr::stream_cipher::generic_array::typenum::UTerm, aes_ctr::stream_cipher::generic_array::typenum::B1>, aes_ctr::stream_cipher::generic_array::typenum::B0>, aes_ctr::stream_cipher::generic_array::typenum::B0>, aes_ctr::stream_cipher::generic_array::typenum::B0>, aes_ctr::stream_cipher::generic_array::typenum::B0>>`
I have a couple of suggestions that might help people understand the security properties of this library, even if they don't know much about crypto.
The ChaCha20Legacy
construction, i.e. the djb variant, is supposed to use a 64-bit counter but currently uses a 32-bit counter because it shares its core implementation with the IETF construction which uses a 32-bit counter.
This results in a counter overflow after generating 256 GiB of keystream. Compatible implementations are able to generate larger keystreams.
I'm not sure how much of a practical concern this actually is, but it did come up in discussions here: rust-random/rand#934 (comment)
We can probably make the counter type generic between u32
/u64
in the core implementation if need be.
Follow up from librespot-org/librespot#239
Seem to be having Bus errors running on aarch64 (32bit kernel).
Tested with this snippet.
extern crate aes_ctr;
use aes_ctr::stream_cipher::generic_array::GenericArray;
use aes_ctr::stream_cipher::{NewFixStreamCipher, StreamCipherCore};
use aes_ctr::Aes128Ctr;
const AUDIO_AESIV: [u8; 16] = [
0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb, 0xcf, 0x77, 0xeb, 0xe8, 0xbc, 0x64, 0x3f, 0x63, 0x0d, 0x93,
];
#[inline(never)]
fn new_cipher(key: &[u8]) -> Aes128Ctr {
// println!("key {:?}", key);
// Will not error if this prints
Aes128Ctr::new(
GenericArray::from_slice(key),
GenericArray::from_slice(&AUDIO_AESIV),
)
}
#[inline(never)]
fn encrypt(cipher: &mut Aes128Ctr) -> [u8; 10] {
let mut data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
cipher.apply_keystream(&mut data);
data
}
fn main() {
let mut cipher = new_cipher(b"0123456789abcdef");
let data = encrypt(&mut cipher);
println!("{:?}", data);
}
Starting program: /home/ash/test_stream_ciphers
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/arm-linux-gnueabihf/libthread_db.so.1".
Program received signal SIGBUS, Bus error.
0xaaab1d64 in _$LT$ctr..Ctr128$LT$C$GT$$u20$as$u20$stream_cipher..NewFixStreamCipher$GT$::new::h1cd20b0be966ea7c ()
(gdb) bt
#0 0xaaab1d64 in _$LT$ctr..Ctr128$LT$C$GT$$u20$as$u20$stream_cipher..NewFixStreamCipher$GT$::new::h1cd20b0be966ea7c ()
#1 0xaaab224c in test_stream_ciphers::new_cipher::h315560409baa366e ()
#2 0xaaab22e4 in test_stream_ciphers::main::h5cdcb843d2f82ea6 ()
#3 0xaaab1610 in std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::hb4152d57b6ff1750 ()
#4 0xaaab7984 in {{closure}} () at libstd/rt.rs:59
#5 std::panicking::try::do_call::h97d304993e5c755b () at libstd/panicking.rs:310
#6 0xaaac8ba0 in __rust_maybe_catch_panic () at libpanic_unwind/lib.rs:105
#7 0xaaab6bc4 in try<i32,closure> () at libstd/panicking.rs:289
#8 catch_unwind<closure,i32> () at libstd/panic.rs:392
#9 std::rt::lang_start_internal::h025c75f9b8aa9041 () at libstd/rt.rs:58
#10 0xaaab2388 in main ()
(gdb)
Cross compiled with:
/src# rustup show
Default host: x86_64-unknown-linux-gnu
installed targets for active toolchain
--------------------------------------
aarch64-unknown-linux-gnu
arm-unknown-linux-gnueabi
arm-unknown-linux-gnueabihf
i686-unknown-linux-gnu
x86_64-unknown-linux-gnu
active toolchain
----------------
stable-x86_64-unknown-linux-gnu (default)
rustc 1.29.2 (17a9dc751 2018-10-05)
root@3cd916fde064:/src# arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-gcc (Debian 6.3.0-18) 6.3.0 20170516
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
The document says:
// decrypt ciphertext by applying keystream again
cipher.apply_keystream(&mut buffer);
Does it have explicit methods for encryption and decryption rather than applying key again?
When I provide a certain combination of key, iv and data, I get different results from the openssl
and the aes-ctr
encryption.
In particular, I used this wrapper:
pub extern crate generic_array;
extern crate crypto;
use aes_ctr::Aes128Ctr;
use aes_ctr::Aes192Ctr;
use aes_ctr::Aes256Ctr;
use aes_ctr::stream_cipher::generic_array::GenericArray;
use aes_ctr::stream_cipher::{
NewStreamCipher, SyncStreamCipher, SyncStreamCipherSeek
};
use openssl::symm;
use std::fs;
use std::env;
//use crypto::aes::{ctr, KeySize};
//use crypto::symmetriccipher::SynchronousStreamCipher;
use crypto::aes;
use std::vec::from_elem;
fn transform_data(key_size: usize,nonce_size: usize, data: &[u8])
-> Result<(Vec<u8>,Vec<u8>, Vec<u8>),&'static str>
{
if data.len() < key_size+nonce_size+1 {
return Err("Data too short");
}
let key = data[0..key_size].to_owned();
let nonce = data[key_size..key_size+nonce_size].to_owned();
let mut crypto_data = data[key_size+nonce_size..].to_owned();
Ok((key,nonce,crypto_data))
}
macro_rules! generate_aes_call {
(128,$key_generic:expr,$nonce_generic:expr) => {
Aes128Ctr::new(&$key_generic, &$nonce_generic);
};
(256,$key_generic:expr,$nonce_generic:expr) => {
Aes256Ctr::new(&$key_generic, &$nonce_generic);
}
}
macro_rules! generate_aes_openssl {
(128) => {
openssl::symm::Cipher::aes_128_ctr();
};
(256) => {
openssl::symm::Cipher::aes_256_ctr();
}
}
macro_rules! generate_crypto_aes_call {
(128,$key:expr,$nonce:expr) => {
crypto::aes::ctr(crypto::aes::KeySize::KeySize128, $key.as_slice(), $nonce.as_slice());
};
(256,$key:expr,$nonce:expr) => {
crypto::aes::ctr(crypto::aes::KeySize::KeySize256, $key.as_slice(), $nonce.as_slice());
};
}
fn main(){
let args: Vec<String> = env::args().collect();
let data = &fs::read(&args[1]).unwrap();
let (key, nonce, crypto_data) = match(transform_data((128 / 8), 16, data)){
Ok((key,nonce,crypto_data)) => (key,nonce,crypto_data),
Err(err_str) => {println!("Err: {:?}", err_str); return}
};
let original_data = crypto_data.to_owned();
let mut aes_ctr_crypto_data = crypto_data.to_owned();
let key_generic = GenericArray::from_slice(&key);
let nonce_generic = GenericArray::from_slice(&nonce);
let mut cipher = Aes128Ctr::new(&key_generic, &nonce_generic);
// apply keystream (encrypt)
cipher.apply_keystream(&mut aes_ctr_crypto_data);
println!("Keysize: {:?}", 128);
let openssl_cipher = generate_aes_openssl!(128);
let openssl_ciphertext = openssl::symm::encrypt(
openssl_cipher,
&key,
Some(&nonce),
&original_data).unwrap();
let mut cipher_crypto_aes = generate_crypto_aes_call!(128,key,nonce);
let mut output_crypto_aes: Vec<u8> = vec![0; original_data.len()];
cipher_crypto_aes.process(&original_data, output_crypto_aes.as_mut_slice());
println!("Key: {:?}",key);
println!("Nonce: {:?}", nonce);
println!("Key generic array: {:?}", key_generic);
println!("Nonce generic array: {:?}", nonce_generic);
println!("Data:\naes-ctr: {:?}\nopenssl: {:?}\ncrypto::aes: {:?}\noriginal_text: {:?}",aes_ctr_crypto_data,openssl_ciphertext,output_crypto_aes,original_data);
assert_eq!(output_crypto_aes,openssl_ciphertext,"Opensll and rust::crypto::aes not equal!");
assert_eq!(crypto_data,openssl_ciphertext, "Openssl and aes_ctr not equal");
println!("All equal\n");
// seek to the keystream beginning and apply it again to the `data` (decrypt)
cipher.seek(0);
cipher.apply_keystream(&mut aes_ctr_crypto_data);
assert_eq!(aes_ctr_crypto_data, original_data, "Decrypted data not equal");
}
And caledl it like so:
cargo run debug.crash
(file is attached), which reveals the following difference in encryption results between openssl and aes-ctr is revealed:
aes-ctr: [108, 253, 73, 159, 41, 43, 94, 79, 15, 121, 128, 186, 135, 246, 194, 87, 210, 245, 143, 26, 252, 148, 40, 251, 221, 123, 151, 112, 198, 148, 194, 86, 112, 124, 212, 82, 16, 202, 32, 36, 21, 69, 127, 56, 201, 111, 252, 108, 123, 166, 116, 191, 155, 79, 254, 41, 214, 68, 210, 96, 235, 239, 143, 101, 236, 141, 110, 227, 88, 157, 94, 39, 237, 104, 244, 167, 255, 124, 114, 238, 9, 185, 155, 13, 123, 34, 248, 104, 110, 165, 81, 34, 14, 84, 153, 97, 166, 34, 52, 106, 75, 253]
openssl: [108, 253, 73, 159, 41, 43, 94, 79, 15, 121, 128, 186, 135, 246, 194, 87, 27, 222, 233, 216, 2, 74, 106, 79, 70, 239, 105, 93, 125, 169, 59, 243, 171, 225, 15, 165, 102, 87, 79, 1, 31, 125, 151, 72, 199, 184, 71, 14, 69, 200, 13, 5, 171, 26, 106, 86, 129, 55, 254, 219, 166, 51, 34, 105, 154, 166, 12, 108, 239, 100, 153, 125, 229, 136, 86, 30, 233, 149, 169, 77, 154, 25, 226, 107, 205, 53, 144, 233, 62, 225, 237, 218, 7, 246, 61, 146, 31, 189, 212, 178, 104, 88]
Oddly enough, if I change the nonce in the file, openssl and aes-ctr produce the same result. Is this a bug or am I misusing the library?
Attached is a zip to easily reproduce this and the input the triggers the difference:
poc.zip
Run like so:
cd poc/
cargo run debug.crash
Hello,
I got an issue with current_pos
on Aes256Ctr
while compiling with the recommended flags from the README.
To reproduce:
use aes_ctr;
use aes_ctr::stream_cipher::{NewStreamCipher, SyncStreamCipherSeek};
fn main() {
let key = [0u8; 32];
let counter_block = [0u8; 16];
// Prepare the cipher for further operations
let mut cipher = aes_ctr::Aes256Ctr::new_var(&key, &counter_block).unwrap();
cipher.seek(16);
println!("{}", cipher.current_pos());
}
[dependencies]
aes-ctr = "0"
$ cargo version
cargo 1.42.0 (86334295e 2020-01-31)
Here's what I obtain:
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/test_aes`
16
$ cargo run --release
Finished release [optimized] target(s) in 0.01s
Running `target/release/test_aes`
16
$ RUSTFLAGS="-C target-feature=+aes,+ssse3" cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/test_aes`
0
$ RUSTFLAGS="-C target-feature=+aes,+ssse3" cargo run --release
Finished release [optimized] target(s) in 0.01s
Running `target/release/test_aes`
0
Am I doing something wrong?
Instead of .seek
, I've also tried with:
let mut temp = [0u8; 0x1000];
cipher.encrypt(&mut temp);
println!("{}", cipher.current_pos());
Leading to the same incoherent result.
Regarding rustc flags:
+aes,+sse2
, +aes
, +ssse3
lead to correct results+aes,+ssse3
leads to the result aboveThere's an open PR to add support for CTR mode with a 128-bit block size and 32-bit counter: #170.
This PR uses the name Ctr32
but this does not reflect the hardcoded 128-bit block size.
Additionally #75 adds support for natively 128-bit counters, versus the 64-bit counter currently used by the Ctr128
type.
How should we handle these discrepancies? Some suggestions:
Ctr128x32
I've been digging into c2-chacha
to figure out why its performance is consistently better than chacha20
. I think I understand the main difference now: c2-chacha
has a "wide" mode where it processes four ChaCha blocks at a time. This requires two sets of registers per state word (since a single 256-bit register only has room for two blocks in parallel), but I guess on modern CPUs there are enough registers that it can handle the increased number of temporaries, and the resulting interleaved instructions seem to parallelise well.
Rust support for arduino is lagging and at the moment a number of bugs in the toolchain prevent the use of any rust newer than nightly-2021-01-07.
When I try to build I get the following error:
error: failed to download `inout v0.1.2`
Caused by:
unable to get packages from source
Caused by:
failed to parse manifest at `/home/thoth/.cargo/registry/src/github.com-1ecc6299db9ec823/inout-0.1.2/Cargo.toml`
Caused by:
failed to parse the `edition` key
Caused by:
this version of Cargo is older than the `2021` edition, and only supports `2015` and `2018` editions.
There are a number of solutions, most of which are out of your hands:
I ported jedisct1/aes-stream to Rust: vks/aesrng. Would this be of interest to this repository?
See: https://github.com/RustCrypto/stream-ciphers/runs/7970722385?check_suite_focus=true#step:8:28
It occurred running the integration tests in the autodetect
CI job for this dependabot PR to bump cpufeatures
from 0.2.3 to 0.2.4, so that's possibly implicated: #303
cc @newpavlov @str4d
SyncStreamCipherSeek::seek
accepts a u64 and doesn't return a result. API docs should mention that ciphers with periods shorter than 2^64 bytes (e.g. Ietf ChaCha) will panic on overflow. An alternative Result<> interface may also be useful.
XChaCha20 can support a full 64-bit keystream for any given nonce, so the MAX_BLOCKS limit of 96-bit-nonce ChaCha20 shouldn't apply.
Technically this wouldn't comply with https://tools.ietf.org/html/draft-arciszewski-xchacha-03 but a cursory glance through the counter handling code makes me think that nothing would actually go wrong -- everything is done with a 64bit counter and split into two 32bit words in the backends.
As a side note, I'm preeeeetty sure that in this implementation 32-bit counter "overflows" would roll over to the next nonce instead of actually leading to nonce reuse. Not great, but #64 may not have been as critical as it looked.
To avoid repeating the keystream on counter overflow, #68 added an assertion that the counter value is less than chacha20::MAX_BLOCKS
.
Ideally SalsaFamilyCipher::block
can be changed to use a fallible API, so instead of panicking it returns an error when the counter overflows.
I may be missing something obvious but why does the aes-ctr
crate exist? Is there a reason to use aes_ctr::Aes128Ctr
over ctr::Ctr128<aes::Aes128>
?
The docs state:
This crate will select appropriate implementation at compile time depending on target architecture and enabled target features.
but I though that was already handled by the aes
crate.
Seems like we have fairly reasonable ChaCha20 crates and Salsa20 crates now, and I see newpavlov snagged https://crates.io/crates/poly1305
A poly1305
crate seems like the next logical step, but where should it go? Right now I think the closest fit is https://github.com/RustCrypto/hashes but that seems a little weird
Should we stick it under hashes
, or do we need a new toplevel project like universal-hashes
or one-time-authenticators
?
As it were, I have a mostly complete (but completely untested) Rust implementation of POLYVAL that needs a home and could be located in the same place: https://github.com/miscreant/miscreant.rs/blob/047ae8b3fd68d62eeb1e8510110439ba24473df3/src/polyval.rs
Hi!
What's the best place to discuss possible security issues regarding the code in this repo?
(FWIW I'm not sure it's a big issue, but I want to be sure to go through the proper channels :))
The salsa20 implementation has a few test vectors here, some of them seems to be from here.
I was wondering if this implementation should add some more test vectors in order to lock the functionality against future changes and have more coverage.
I am pretty sure this implementation will pass all these test vectors but will be nice to confirm it:
These are the only test vectors from some sort of reliable source that i was able to find online for salsa20.
This is a lot of vectors, an option will be to just implement some here instead. Another approach taken here is a shell script that generate the tests. The simpler one will be to just add an ecrypt
module inside the tests
, hardcode the vectors and run them.
I could create a PR for this if there is some interest.
I have target-cpu=native
set in my rustflags
, in ~/.cargo/config
:
[target.'cfg(any(windows, unix))']
rustflags = ["-Ctarget-cpu=native"]
This causes aes-ctr
to break my projects' build:
Compiling aes-soft v0.3.3
Compiling ctr v0.3.2
Compiling aes-ctr v0.3.0
error[E0463]: can't find crate for `aesni`
--> /home/faux/.cargo/registry/src/github.com-1ecc6299db9ec823/aes-ctr-0.3.0/src/lib.rs:62:1
|
62 | extern crate aesni;
| ^^^^^^^^^^^^^^^^^^^ can't find crate
error: aborting due to previous error
I guess cargo
is using different rules to interpret target_feature
in Cargo.toml
vs. during the build? Note how it is loading the aes-soft
crate above.
I tested stable and nightly cargo
:
cargo 1.35.0-nightly (6f3e9c367 2019-04-04)
cargo 1.34.0 (6789d8a0a 2019-04-01)
If I remove ~/.cargo/config
, and instead set RUSTFLAGS=
, everything seems to work as expected:
% RUSTFLAGS=-Ctarget-cpu=native cargo build
...
Compiling aesni v0.6.0
Compiling aes-ctr v0.3.0
Compiling lol v0.1.0 (/var/tmp/faux190416.sciatica/lol)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.