Cryptopp folks,
I'm attempting to interop ECIES encryption between bouncy castle and cryptopp. Unfortunately, I've run into a problem and I think it's a bug in cryptopp.
When cryptopp creates the digest for ECIES, it sends the payload, followed by the encoding parameters, followed by the encoding parameters' size in bytes as an 8-byte value. For example, if the encoding parameter is 4 bytes long, it writes the length as 00 00 00 00 00 00 00 04
. If its 32 bytes long it writes00 00 00 00 00 00 00 20
.
This is driven by code in gfpcrypt.h:
mac.Update(encodingParameters.begin(), encodingParameters.size());
if (DHAES_MODE)
{
byte L[8] = {0,0,0,0};
PutWord(false, BIG_ENDIAN_ORDER, L+4, word32(encodingParameters.size()));
mac.Update(L, 8);
}
Bouncy castle writes the encoding parameter size in bits as a 4-byte value. For example, if the encoding parameter is 4 bytes long it writes 00 00 00 20
. If its 32 bytes long, bouncy castle writes 00 00 01 00
.
This is driven by code in IESEngine.java:
byte[] L2 = new byte[4];
if (V.length != 0)
{
if (P2 != null)
{
Pack.intToBigEndian(P2.length * 8, L2, 0);
}
else
{
Pack.intToBigEndian(0, L2, 0);
}
}
...
if (V.length != 0)
{
mac.update(L2, 0, L2.length);
}
I cross posted this message to the bouncy castle list, and David Hook replied with a quote from Victor Shoup's paper:
In particular, we multiply |L| by 8 so as to encode the length of L in bits, rather than bytes, since the IEEE P1363a version of ECIES allows (in theory, but not really in practice) messages and labels that are bit strings rather than byte strings.
Is it a bug?
Thanks!
Jesse and I think we have found the problem. Here is the patch to fix it. This has been confirmed to work and we were able to encrypt in CryptoPP and decrypt in Bouncy Castle.
Here’s a full encryption and decryption with a golden private key.
Security.addProvider(new BouncyCastleProvider());
KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(fromHex("3059301306072a8648ce3d020106"
+ "082a8648ce3d03010703420004b629e654f0df6ebb559c8fa0a2e7902777030f4871b76d048beee5f505b556"
+ "8f32084eacf91eef6cce758f47bc82f8cb2137b28e33975ac26c052a1cbca22140"));
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(fromHex("308193020100301306072a864"
+ "8ce3d020106082a8648ce3d0301070479307702010104209219a2380634f27db6e7a0871b377765f4630db76"
+ "d9ac73f5e6bc0c443bdecdba00a06082a8648ce3d030107a14403420004b629e654f0df6ebb559c8fa0a2e79"
+ "02777030f4871b76d048beee5f505b5568f32084eacf91eef6cce758f47bc82f8cb2137b28e33975ac26c052"
+ "a1cbca22140"));
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
KeyPair keys = new KeyPair(publicKey, privateKey);
byte[] data = randomByteString(100);
Cipher encryptCipher = Cipher.getInstance("ECIES/DHAES/NOPADDING", "BC");
encryptCipher.init(Cipher.ENCRYPT_MODE, keys.getPublic(), new SecureRandom());
byte[] ciphertext = encryptCipher.doFinal(data);
IESParameterSpec parameterSpec = new IESParameterSpec(null, new byte[0], 128, 128);
Cipher decryptCipher = Cipher.getInstance("ECIES/DHAES/NOPADDING", "BC");
decryptCipher.init(Cipher.DECRYPT_MODE, keys.getPrivate(), parameterSpec, new SecureRandom());
byte[] decrypted = decryptCipher.doFinal(ciphertext);
assertThat(decrypted).isEqualTo(data);
And this will generate a new key pair:
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
keyPairGenerator.initialize(256, new SecureRandom());
KeyPair keys = keyPairGenerator.generateKeyPair();