rustsec-2022-0011
Vulnerability from osv_rustsec
Published
2022-02-28 12:00
Modified
2023-06-13 13:10
Summary
Miscomputation when performing AES encryption in rust-crypto
Details
The following Rust program demonstrates some strangeness in AES encryption - if you have an immutable key slice and then operate on that slice, you get different encryption output than if you operate on a copy of that key.
For these functions, we expect that extending a 16 byte key to a 32 byte key by repeating it gives the same encrypted data, because the underlying rust-crypto functions repeat key data up to the necessary key size for the cipher.
use crypto::{
aes, blockmodes, buffer,
buffer::{BufferResult, ReadBuffer, WriteBuffer},
symmetriccipher,
};
fn encrypt(
key: &[u8],
iv: &[u8],
data: &str,
) -> Result<String, symmetriccipher::SymmetricCipherError> {
let mut encryptor =
aes::cbc_encryptor(aes::KeySize::KeySize256, key, iv, blockmodes::PkcsPadding);
let mut encrypted_data = Vec::<u8>::new();
let mut read_buffer = buffer::RefReadBuffer::new(data.as_bytes());
let mut buffer = [0; 4096];
let mut write_buffer = buffer::RefWriteBuffer::new(&mut buffer);
loop {
let result = encryptor.encrypt(&mut read_buffer, &mut write_buffer, true)?;
encrypted_data.extend(
write_buffer
.take_read_buffer()
.take_remaining()
.iter()
.copied(),
);
match result {
BufferResult::BufferUnderflow => break,
BufferResult::BufferOverflow => {}
}
}
Ok(hex::encode(encrypted_data))
}
fn working() {
let data = "data";
let iv = [
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE,
0xFF,
];
let key = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
0x0F,
];
// The copy here makes the code work.
let key_copy = key;
let key2: Vec<u8> = key_copy.iter().cycle().take(32).copied().collect();
println!("key1:{} key2: {}", hex::encode(&key), hex::encode(&key2));
let x1 = encrypt(&key, &iv, data).unwrap();
println!("X1: {}", x1);
let x2 = encrypt(&key2, &iv, data).unwrap();
println!("X2: {}", x2);
assert_eq!(x1, x2);
}
fn broken() {
let data = "data";
let iv = [
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE,
0xFF,
];
let key = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
0x0F,
];
// This operation shouldn't affect the contents of key at all.
let key2: Vec<u8> = key.iter().cycle().take(32).copied().collect();
println!("key1:{} key2: {}", hex::encode(&key), hex::encode(&key2));
let x1 = encrypt(&key, &iv, data).unwrap();
println!("X1: {}", x1);
let x2 = encrypt(&key2, &iv, data).unwrap();
println!("X2: {}", x2);
assert_eq!(x1, x2);
}
fn main() {
working();
broken();
}
The output from this program:
Running `target/host/debug/rust-crypto-test`
key1:000102030405060708090a0b0c0d0e0f key2: 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
X1: 90462bbe32965c8e7ea0addbbed4cddb
X2: 90462bbe32965c8e7ea0addbbed4cddb
key1:000102030405060708090a0b0c0d0e0f key2: 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
X1: 26e847e5e7df1947bf82a650548a7d5b
X2: 90462bbe32965c8e7ea0addbbed4cddb
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `"26e847e5e7df1947bf82a650548a7d5b"`,
right: `"90462bbe32965c8e7ea0addbbed4cddb"`', src/main.rs:83:5
Notably, the X1 key in the broken() test changes every time after rerunning the program.
{
"affected": [
{
"database_specific": {
"categories": [
"crypto-failure"
],
"cvss": null,
"informational": null
},
"ecosystem_specific": {
"affected_functions": null,
"affects": {
"arch": [],
"functions": [],
"os": []
}
},
"package": {
"ecosystem": "crates.io",
"name": "rust-crypto",
"purl": "pkg:cargo/rust-crypto"
},
"ranges": [
{
"events": [
{
"introduced": "0.0.0-0"
}
],
"type": "SEMVER"
}
],
"versions": []
}
],
"aliases": [
"GHSA-jp3w-3q88-34cf"
],
"database_specific": {
"license": "CC0-1.0"
},
"details": "The following Rust program demonstrates some strangeness in AES encryption - if you have an immutable key slice and then operate on that slice, you get different encryption output than if you operate on a copy of that key.\n\nFor these functions, we expect that extending a 16 byte key to a 32 byte key by repeating it gives the same encrypted data, because the underlying rust-crypto functions repeat key data up to the necessary key size for the cipher.\n\n```rust\nuse crypto::{\n aes, blockmodes, buffer,\n buffer::{BufferResult, ReadBuffer, WriteBuffer},\n symmetriccipher,\n};\n\nfn encrypt(\n key: \u0026[u8],\n iv: \u0026[u8],\n data: \u0026str,\n) -\u003e Result\u003cString, symmetriccipher::SymmetricCipherError\u003e {\n let mut encryptor =\n aes::cbc_encryptor(aes::KeySize::KeySize256, key, iv, blockmodes::PkcsPadding);\n\n let mut encrypted_data = Vec::\u003cu8\u003e::new();\n let mut read_buffer = buffer::RefReadBuffer::new(data.as_bytes());\n let mut buffer = [0; 4096];\n let mut write_buffer = buffer::RefWriteBuffer::new(\u0026mut buffer);\n\n loop {\n let result = encryptor.encrypt(\u0026mut read_buffer, \u0026mut write_buffer, true)?;\n\n encrypted_data.extend(\n write_buffer\n .take_read_buffer()\n .take_remaining()\n .iter()\n .copied(),\n );\n\n match result {\n BufferResult::BufferUnderflow =\u003e break,\n BufferResult::BufferOverflow =\u003e {}\n }\n }\n\n Ok(hex::encode(encrypted_data))\n}\n\nfn working() {\n let data = \"data\";\n let iv = [\n 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE,\n 0xFF,\n ];\n let key = [\n 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,\n 0x0F,\n ];\n // The copy here makes the code work.\n let key_copy = key;\n let key2: Vec\u003cu8\u003e = key_copy.iter().cycle().take(32).copied().collect();\n println!(\"key1:{} key2: {}\", hex::encode(\u0026key), hex::encode(\u0026key2));\n\n let x1 = encrypt(\u0026key, \u0026iv, data).unwrap();\n println!(\"X1: {}\", x1);\n\n let x2 = encrypt(\u0026key2, \u0026iv, data).unwrap();\n println!(\"X2: {}\", x2);\n\n assert_eq!(x1, x2);\n}\n\nfn broken() {\n let data = \"data\";\n let iv = [\n 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE,\n 0xFF,\n ];\n let key = [\n 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,\n 0x0F,\n ];\n // This operation shouldn\u0027t affect the contents of key at all.\n let key2: Vec\u003cu8\u003e = key.iter().cycle().take(32).copied().collect();\n println!(\"key1:{} key2: {}\", hex::encode(\u0026key), hex::encode(\u0026key2));\n\n let x1 = encrypt(\u0026key, \u0026iv, data).unwrap();\n println!(\"X1: {}\", x1);\n\n let x2 = encrypt(\u0026key2, \u0026iv, data).unwrap();\n println!(\"X2: {}\", x2);\n\n assert_eq!(x1, x2);\n}\n\nfn main() {\n working();\n broken();\n}\n```\n\nThe output from this program:\n\n```shell\n Running `target/host/debug/rust-crypto-test`\nkey1:000102030405060708090a0b0c0d0e0f key2: 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\nX1: 90462bbe32965c8e7ea0addbbed4cddb\nX2: 90462bbe32965c8e7ea0addbbed4cddb\nkey1:000102030405060708090a0b0c0d0e0f key2: 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\nX1: 26e847e5e7df1947bf82a650548a7d5b\nX2: 90462bbe32965c8e7ea0addbbed4cddb\nthread \u0027main\u0027 panicked at \u0027assertion failed: `(left == right)`\n left: `\"26e847e5e7df1947bf82a650548a7d5b\"`,\n right: `\"90462bbe32965c8e7ea0addbbed4cddb\"`\u0027, src/main.rs:83:5\n```\n\nNotably, the X1 key in the `broken()` test changes every time after rerunning the program.",
"id": "RUSTSEC-2022-0011",
"modified": "2023-06-13T13:10:24Z",
"published": "2022-02-28T12:00:00Z",
"references": [
{
"type": "PACKAGE",
"url": "https://crates.io/crates/rust-crypto"
},
{
"type": "ADVISORY",
"url": "https://rustsec.org/advisories/RUSTSEC-2022-0011.html"
}
],
"related": [],
"severity": [],
"summary": "Miscomputation when performing AES encryption in rust-crypto"
}
Loading…
Loading…
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Loading…
Loading…