The counter mode KDF was not correctly following NIST SP 800-108.

Fixes bug #25. CMAC is used as the PRF. Note that we're currently using
a version of Spongy Castle with a CMAC implementation that's vulnerable
to a side-channel attack - this has been reported and fixed upstream but
we haven't yet upgraded to the fixed version.
This commit is contained in:
akwizgran
2013-07-11 15:13:48 +01:00
parent d02266d827
commit e9859e9f38

View File

@@ -29,11 +29,13 @@ import net.sf.briar.util.ByteUtils;
import org.spongycastle.crypto.AsymmetricCipherKeyPair; import org.spongycastle.crypto.AsymmetricCipherKeyPair;
import org.spongycastle.crypto.BlockCipher; import org.spongycastle.crypto.BlockCipher;
import org.spongycastle.crypto.CipherParameters; import org.spongycastle.crypto.CipherParameters;
import org.spongycastle.crypto.Mac;
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement; import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
import org.spongycastle.crypto.digests.SHA384Digest; import org.spongycastle.crypto.digests.SHA384Digest;
import org.spongycastle.crypto.engines.AESFastEngine; import org.spongycastle.crypto.engines.AESFastEngine;
import org.spongycastle.crypto.generators.ECKeyPairGenerator; import org.spongycastle.crypto.generators.ECKeyPairGenerator;
import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator; import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.spongycastle.crypto.macs.CMac;
import org.spongycastle.crypto.modes.AEADBlockCipher; import org.spongycastle.crypto.modes.AEADBlockCipher;
import org.spongycastle.crypto.modes.GCMBlockCipher; import org.spongycastle.crypto.modes.GCMBlockCipher;
import org.spongycastle.crypto.params.ECKeyGenerationParameters; import org.spongycastle.crypto.params.ECKeyGenerationParameters;
@@ -44,7 +46,6 @@ import org.spongycastle.util.Strings;
class CryptoComponentImpl implements CryptoComponent { class CryptoComponentImpl implements CryptoComponent {
private static final int CIPHER_BLOCK_BYTES = 16; // 128 bits
private static final int CIPHER_KEY_BYTES = 32; // 256 bits private static final int CIPHER_KEY_BYTES = 32; // 256 bits
private static final int AGREEMENT_KEY_PAIR_BITS = 384; private static final int AGREEMENT_KEY_PAIR_BITS = 384;
private static final int SIGNATURE_KEY_PAIR_BITS = 384; private static final int SIGNATURE_KEY_PAIR_BITS = 384;
@@ -392,25 +393,31 @@ class CryptoComponentImpl implements CryptoComponent {
return output; return output;
} }
// Key derivation function based on a block cipher in CTR mode - see // Key derivation function based on a PRF in counter mode - see
// NIST SP 800-108, section 5.1 // NIST SP 800-108, section 5.1
private byte[] counterModeKdf(byte[] secret, byte[] label, long context) { private byte[] counterModeKdf(byte[] secret, byte[] label, long context) {
if(secret.length != CIPHER_KEY_BYTES) if(secret.length != CIPHER_KEY_BYTES)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
if(Arrays.equals(secret, BLANK_SECRET)) if(Arrays.equals(secret, BLANK_SECRET))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
// The label and context must leave a byte free for the counter if(label[label.length - 1] != '\0')
if(label.length + 4 >= CIPHER_BLOCK_BYTES)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
byte[] counter = new byte[CIPHER_BLOCK_BYTES]; Mac prf = new CMac(new AESFastEngine());
System.arraycopy(label, 0, counter, 0, label.length); prf.init(new KeyParameter(secret));
ByteUtils.writeUint32(context, counter, label.length); int macLength = prf.getMacSize();
BlockCipher cipher = new AESFastEngine(); byte[] mac = new byte[macLength], output = new byte[CIPHER_KEY_BYTES];
cipher.init(true, new KeyParameter(secret)); byte[] contextBytes = new byte[4];
byte[] output = new byte[CIPHER_KEY_BYTES]; ByteUtils.writeUint32(context, contextBytes, 0);
for(int i = 0; i * CIPHER_BLOCK_BYTES < output.length; i++) { for(int i = 0; i * macLength < CIPHER_KEY_BYTES; i++) {
counter[counter.length - 1] = (byte) i; prf.update((byte) i); // Counter
cipher.processBlock(counter, 0, output, i * CIPHER_BLOCK_BYTES); prf.update(label, 0, label.length);
prf.update(contextBytes, 0, contextBytes.length);
prf.update((byte) CIPHER_KEY_BYTES); // Output length
prf.doFinal(mac, 0);
int bytesNeeded = CIPHER_KEY_BYTES - i * macLength;
int bytesToUse = Math.min(bytesNeeded, macLength);
System.arraycopy(mac, 0, output, i * macLength, bytesToUse);
ByteUtils.erase(mac);
} }
return output; return output;
} }