bc-java icon indicating copy to clipboard operation
bc-java copied to clipboard

Different wrap value in bcprov-jdk18on 1.78.1 with input <= 8 characters

Open nicolasb29 opened this issue 8 months ago • 3 comments

Hi,

We have a problem since an update of bcprov when wrapping a string <= 8 characters. The wrap value is different:

Our code

import java.nio.charset.StandardCharsets;

import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.AESWrapEngine;
import org.bouncycastle.crypto.params.KeyParameter;

public class TestRFC3394 {

    public static void main(String[] args) throws InvalidCipherTextException {
        String secret = "toto1234";

        System.out.println("secret: " + secret);

        String key = "34B6E58B090922C15641EE98183682F5";

        AESWrapEngine aes = new AESWrapEngine();
        KeyParameter keyparam = new KeyParameter(key.getBytes(StandardCharsets.UTF_8));
        aes.init(true, keyparam);

        byte[] encryptedString = aes.wrap(secret.getBytes(StandardCharsets.UTF_8), 0, 8);
        String wrappedString = new String(Hex.encodeHex(encryptedString));
        System.out.println("wrapped: " + wrappedString);

        aes.init(false, keyparam);
        byte[] unwrappedString = aes.unwrap(encryptedString, 0, encryptedString.length);
        System.out.println("unwrapped: " + new String(unwrappedString));
    }
}

With bcprov-jdk15on, here is the output:

secret: toto1234
wrapped: b9f7a272d8cecf28a27b724d5a75d83e
unwrapped: toto1234 

With bcprov-jdk18on 1.78.1:

secret: toto1234
wrapped: 48ad8cbfc3dd53aec3a45c78b2d3fc88
unwrapped: toto1234 

We have made test with a Rust code to check the output and we have the same output as bcprov-jdk15on:

use aes_kw::Kek;
use anyhow::*;

fn main() -> anyhow::Result<()> {
    let secret = "toto1234";
    println!("secret: {}", secret);

    let key_bytes = "34B6E58B090922C15641EE98183682F5".as_bytes();
    let mut k32 = [0u8; 32];
    k32.copy_from_slice(&key_bytes);
    let kek = Kek::from(k32);

    let mut out = vec![0; 16];
    let _ = kek.wrap(&secret.as_bytes(), &mut out);
    println!("wrapped: {}", hex::encode(&out));

    let mut out2 = vec![0; 8];
    let _ = kek.unwrap(&out, &mut out2);
    println!("unwrapped: {}", String::from_utf8(out2)?);
    Ok(())
} 
secret: toto1234
wrapped: b9f7a272d8cecf28a27b724d5a75d83e
unwrapped: toto1234 

Why do we have a different wrap value ? It's a problem when two applications is using the value with different version of bcprov.

Regards

nicolasb29 avatar May 22 '25 09:05 nicolasb29

Any idea about this issue?

nicolasb29 avatar Jun 11 '25 18:06 nicolasb29

Presumably this commit: https://github.com/bcgit/bc-java/commit/b11c2442b01c2a638dbcbfe6eabbc7947897bdc5 ? That would have landed in version 1.73. Could you clarify what the original version you were using was?

As I recall this was made for compatibility with other implementations, although I believe there is a fair amount of disagreement and/or misunderstanding about what the spec actually says for small keys.

peterdettman avatar Jun 11 '25 19:06 peterdettman

Yes, it is most certainly from this commit. In the "1-block" case, the code was running 6 passes before (same as long wrapped value), but only 1 after, resulting in different output with the next version.

The only other implementation i tried was the rust one described above, which complies with the original code (6 passes in all cases). And the real problem for us is compatibility between multiple servers using different versions of BC.

boobin avatar Jun 11 '25 19:06 boobin