GHSA-JP3W-3Q88-34CF

Vulnerability from github – Published: 2022-06-17 00:17 – Updated: 2022-06-17 00:17
VLAI?
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.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "crates.io",
        "name": "rust-crypto"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "0.2.36"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [],
    "github_reviewed": true,
    "github_reviewed_at": "2022-06-17T00:17:08Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "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.\n\n",
  "id": "GHSA-jp3w-3q88-34cf",
  "modified": "2022-06-17T00:17:08Z",
  "published": "2022-06-17T00:17:08Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/DaGenix/rust-crypto/issues/476"
    },
    {
      "type": "WEB",
      "url": "https://github.com/DaGenix/rust-crypto"
    },
    {
      "type": "WEB",
      "url": "https://rustsec.org/advisories/RUSTSEC-2022-0011.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [],
  "summary": "Miscomputation when performing AES encryption in rust-crypto"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

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…

Detection rules are retrieved from Rulezet.

Loading…

Loading…