Skip to content

Commit

Permalink
Switches to safe bit computation in crypto (iss #224)
Browse files Browse the repository at this point in the history
  • Loading branch information
vdzhuvinov committed Jun 1, 2017
1 parent 6a29f10 commit 0d2bd64
Show file tree
Hide file tree
Showing 15 changed files with 245 additions and 50 deletions.
10 changes: 7 additions & 3 deletions src/main/java/com/nimbusds/jose/crypto/AAD.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.nimbusds.jose.JWEHeader;
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jose.util.ByteUtils;
import com.nimbusds.jose.util.IntegerOverflowException;


/**
Expand All @@ -32,7 +33,7 @@
* <p>See RFC 7518 (JWA), section 5.1, point 14.
*
* @author Vladimir Dzhuvinov
* @version 2015-05-14
* @version 2017-06-01
*/
class AAD {

Expand Down Expand Up @@ -75,10 +76,13 @@ public static byte[] compute(final Base64URL encodedJWEHeader) {
*
* @return The computed AAD bit length, as a 64 bit big-endian
* representation (8 byte array).
*
* @throws IntegerOverflowException On a integer overflow.
*/
public static byte[] computeLength(final byte[] aad) {
public static byte[] computeLength(final byte[] aad)
throws IntegerOverflowException {

final int bitLength = ByteUtils.bitLength(aad);
final int bitLength = ByteUtils.safeBitLength(aad);
return ByteBuffer.allocate(8).putLong(bitLength).array();
}
}
17 changes: 8 additions & 9 deletions src/main/java/com/nimbusds/jose/crypto/AESEncrypter.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,12 @@
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import net.jcip.annotations.ThreadSafe;

import com.nimbusds.jose.*;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jose.util.ByteUtils;
import com.nimbusds.jose.util.Container;
import net.jcip.annotations.ThreadSafe;


/**
Expand Down Expand Up @@ -71,7 +70,7 @@
* @author Melisa Halsband
* @author Vladimir Dzhuvinov
* @author Dimitar A. Stoikov
* @version 2016-10-13
* @version 2017-06-01
*/
@ThreadSafe
public class AESEncrypter extends AESCryptoProvider implements JWEEncrypter {
Expand Down Expand Up @@ -145,42 +144,42 @@ public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText)

if (alg.equals(JWEAlgorithm.A128KW)) {

if(ByteUtils.bitLength(getKey().getEncoded()) != 128){
if(ByteUtils.safeBitLength(getKey().getEncoded()) != 128){
throw new KeyLengthException("The Key Encryption Key (KEK) length must be 128 bits for A128KW encryption");
}
algFamily = AlgFamily.AESKW;

} else if (alg.equals(JWEAlgorithm.A192KW)) {

if(ByteUtils.bitLength(getKey().getEncoded()) != 192){
if(ByteUtils.safeBitLength(getKey().getEncoded()) != 192){
throw new KeyLengthException("The Key Encryption Key (KEK) length must be 192 bits for A192KW encryption");
}
algFamily = AlgFamily.AESKW;

} else if (alg.equals(JWEAlgorithm.A256KW)) {

if (ByteUtils.bitLength(getKey().getEncoded()) != 256) {
if (ByteUtils.safeBitLength(getKey().getEncoded()) != 256) {
throw new KeyLengthException("The Key Encryption Key (KEK) length must be 256 bits for A256KW encryption");
}
algFamily = AlgFamily.AESKW;

} else if (alg.equals(JWEAlgorithm.A128GCMKW)) {

if(ByteUtils.bitLength(getKey().getEncoded()) != 128){
if(ByteUtils.safeBitLength(getKey().getEncoded()) != 128){
throw new KeyLengthException("The Key Encryption Key (KEK) length must be 128 bits for A128GCMKW encryption");
}
algFamily = AlgFamily.AESGCMKW;

} else if (alg.equals(JWEAlgorithm.A192GCMKW)) {

if(ByteUtils.bitLength(getKey().getEncoded()) != 192){
if(ByteUtils.safeBitLength(getKey().getEncoded()) != 192){
throw new KeyLengthException("The Key Encryption Key (KEK) length must be 192 bits for A192GCMKW encryption");
}
algFamily = AlgFamily.AESGCMKW;

} else if (alg.equals(JWEAlgorithm.A256GCMKW)) {

if(ByteUtils.bitLength(getKey().getEncoded()) != 256){
if(ByteUtils.safeBitLength(getKey().getEncoded()) != 256){
throw new KeyLengthException("The Key Encryption Key (KEK) length must be 256 bits for A256GCMKW encryption");
}
algFamily = AlgFamily.AESGCMKW;
Expand Down
10 changes: 4 additions & 6 deletions src/main/java/com/nimbusds/jose/crypto/AESGCM.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,13 @@

import java.security.*;
import java.security.spec.InvalidParameterSpecException;

import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;

import net.jcip.annotations.ThreadSafe;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.util.ByteUtils;
import com.nimbusds.jose.util.Container;
import net.jcip.annotations.ThreadSafe;


/**
Expand All @@ -40,7 +38,7 @@
* @author Vladimir Dzhuvinov
* @author Axel Nennker
* @author Dimitar A. Stoikov
* @version 2016-10-13
* @version 2017-06-01
*/
@ThreadSafe
class AESGCM {
Expand Down Expand Up @@ -199,8 +197,8 @@ private static byte[] actualIVOf(final Cipher cipher)
private static void validate(final byte[] iv, final int authTagLength)
throws JOSEException {

if (ByteUtils.bitLength(iv) != IV_BIT_LENGTH) {
throw new JOSEException(String.format("IV length of %d bits is required, got %d", IV_BIT_LENGTH, ByteUtils.bitLength(iv)));
if (ByteUtils.safeBitLength(iv) != IV_BIT_LENGTH) {
throw new JOSEException(String.format("IV length of %d bits is required, got %d", IV_BIT_LENGTH, ByteUtils.safeBitLength(iv)));
}

if (authTagLength != AUTH_TAG_BIT_LENGTH) {
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/nimbusds/jose/crypto/AESGCMKW.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
*
* @author Melisa Halsband
* @author Vladimir Dzhuvinov
* @version 2016-10-13
* @version 2017-06-01
*/
@ThreadSafe
class AESGCMKW {
Expand Down Expand Up @@ -97,9 +97,9 @@ public static SecretKey decryptCEK(final SecretKey kek,

byte[] keyBytes = AESGCM.decrypt(kek, iv, authEncrCEK.getCipherText(), new byte[0], authEncrCEK.getAuthenticationTag(), provider);

if (ByteUtils.bitLength(keyBytes) != keyLength) {
if (ByteUtils.safeBitLength(keyBytes) != keyLength) {

throw new KeyLengthException("CEK key length mismatch: " + ByteUtils.bitLength(keyBytes) + " != " + keyLength);
throw new KeyLengthException("CEK key length mismatch: " + ByteUtils.safeBitLength(keyBytes) + " != " + keyLength);
}

return new SecretKeySpec(keyBytes, "AES");
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/nimbusds/jose/crypto/ConcatKDF.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
* <p>See NIST.800-56A.
*
* @author Vladimir Dzhuvinov
* @version 2017-05-05
* @version 2017-06-01
*/
@ThreadSafe
class ConcatKDF implements JCAAware<JCAContext> {
Expand Down Expand Up @@ -115,7 +115,7 @@ public SecretKey deriveKey(final SecretKey sharedSecret,

final MessageDigest md = getMessageDigest();

for (int i=1; i <= computeDigestCycles(ByteUtils.bitLength(md.getDigestLength()), keyLengthBits); i++) {
for (int i=1; i <= computeDigestCycles(ByteUtils.safeBitLength(md.getDigestLength()), keyLengthBits); i++) {

byte[] counterBytes = IntegerUtils.toBytes(i);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jose.util.ByteUtils;
import com.nimbusds.jose.util.Container;
import com.nimbusds.jose.util.IntegerOverflowException;


/**
* JWE content encryption / decryption provider.
*
* @author Vladimir Dzhuvinov
* @version 2016-10-13
* @version 2017-06-01
*/
class ContentCryptoProvider {

Expand Down Expand Up @@ -126,8 +127,12 @@ public static SecretKey generateCEK(final EncryptionMethod enc, final SecureRand
private static void checkCEKLength(final SecretKey cek, final EncryptionMethod enc)
throws KeyLengthException {

if (enc.cekBitLength() != ByteUtils.bitLength(cek.getEncoded())) {
throw new KeyLengthException("The Content Encryption Key (CEK) length for " + enc + " must be " + enc.cekBitLength() + " bits");
try {
if (enc.cekBitLength() != ByteUtils.safeBitLength(cek.getEncoded())) {
throw new KeyLengthException("The Content Encryption Key (CEK) length for " + enc + " must be " + enc.cekBitLength() + " bits");
}
} catch (IntegerOverflowException e) {
throw new KeyLengthException("The Content Encryption Key (CEK) is too long: " + e.getMessage());
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/nimbusds/jose/crypto/DirectEncrypter.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
* </ul>
*
* @author Vladimir Dzhuvinov
* @version 2014-06-29
* @version 2017-06-01
*/
@ThreadSafe
public class DirectEncrypter extends DirectCryptoProvider implements JWEEncrypter {
Expand Down Expand Up @@ -132,7 +132,7 @@ public JWECryptoParts encrypt(final JWEHeader header, final byte[] clearText)
// Check key length matches encryption method
EncryptionMethod enc = header.getEncryptionMethod();

if (enc.cekBitLength() != ByteUtils.bitLength(getKey().getEncoded())) {
if (enc.cekBitLength() != ByteUtils.safeBitLength(getKey().getEncoded())) {
throw new KeyLengthException(enc.cekBitLength(), enc);
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/nimbusds/jose/crypto/RSA1_5.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
* decryption. This class is thread-safe.
*
* @author Vladimir Dzhuvinov
* @version 2016-12-04
* @version 2017-06-01
*/
@ThreadSafe
class RSA1_5 {
Expand Down Expand Up @@ -98,7 +98,7 @@ public static SecretKey decryptCEK(final PrivateKey priv,
cipher.init(Cipher.DECRYPT_MODE, priv);
byte[] secretKeyBytes = cipher.doFinal(encryptedCEK);

if (ByteUtils.bitLength(secretKeyBytes) != keyLength) {
if (ByteUtils.safeBitLength(secretKeyBytes) != keyLength) {
// CEK key length mismatch
return null;
}
Expand Down
13 changes: 7 additions & 6 deletions src/main/java/com/nimbusds/jose/jwk/OctetSequenceKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@

import com.nimbusds.jose.Algorithm;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.util.Base64;
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jose.util.ByteUtils;
import com.nimbusds.jose.util.JSONObjectUtils;
import com.nimbusds.jose.util.*;
import net.jcip.annotations.Immutable;
import net.minidev.json.JSONObject;

Expand Down Expand Up @@ -65,7 +62,7 @@
*
* @author Justin Richer
* @author Vladimir Dzhuvinov
* @version 2017-04-08
* @version 2017-06-01
*/
@Immutable
public final class OctetSequenceKey extends JWK implements SecretJWK {
Expand Down Expand Up @@ -539,7 +536,11 @@ public OctetSequenceKey toPublicJWK() {
@Override
public int size() {

return ByteUtils.bitLength(k.decode());
try {
return ByteUtils.safeBitLength(k.decode());
} catch (IntegerOverflowException e) {
throw new ArithmeticException(e.getMessage());
}
}


Expand Down
12 changes: 7 additions & 5 deletions src/main/java/com/nimbusds/jose/jwk/RSAKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@
import com.nimbusds.jose.Algorithm;
import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.util.Base64;
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jose.util.ByteUtils;
import com.nimbusds.jose.util.JSONObjectUtils;
import com.nimbusds.jose.util.*;
import net.jcip.annotations.Immutable;
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
Expand Down Expand Up @@ -133,7 +131,7 @@
* @author Vladimir Dzhuvinov
* @author Justin Richer
* @author Cedric Staub
* @version 2017-04-08
* @version 2017-06-01
*/
@Immutable
public final class RSAKey extends JWK implements AssymetricJWK {
Expand Down Expand Up @@ -1933,7 +1931,11 @@ public boolean isPrivate() {
@Override
public int size() {

return ByteUtils.bitLength(n.decode());
try {
return ByteUtils.safeBitLength(n.decode());
} catch (IntegerOverflowException e) {
throw new ArithmeticException(e.getMessage());
}
}


Expand Down
45 changes: 44 additions & 1 deletion src/main/java/com/nimbusds/jose/util/ByteUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* Byte utilities.
*
* @author Vladimir Dzhuvinov
* @version 2015-05-12
* @version 2017-06-01
*/
public class ByteUtils {

Expand Down Expand Up @@ -92,6 +92,28 @@ public static int bitLength(final int byteLength) {
}


/**
* Returns the bit length of the specified byte length, preventing
* integer overflow.
*
* @param byteLength The byte length.
*
* @return The bit length.
*
* @throws IntegerOverflowException On a integer overflow.
*/
public static int safeBitLength(final int byteLength)
throws IntegerOverflowException {

long longResult = (long)byteLength * (long)8;
if((long)((int)longResult) != longResult) {
throw new IntegerOverflowException();
} else {
return (int)longResult;
}
}


/**
* Returns the byte length of the specified byte array.
*
Expand All @@ -109,6 +131,27 @@ public static int bitLength(final byte[] byteArray) {
}


/**
* Returns the byte length of the specified byte array, preventing
* integer overflow.
*
* @param byteArray The byte array. May be {@code null}.
*
* @return The bite length, zero if the array is {@code null}.
*
* @throws IntegerOverflowException On a integer overflow.
*/
public static int safeBitLength(final byte[] byteArray)
throws IntegerOverflowException {

if (byteArray == null) {
return 0;
} else {
return safeBitLength(byteArray.length);
}
}


/**
* Returns the byte length of the specified bit length.
*
Expand Down
Loading

0 comments on commit 0d2bd64

Please sign in to comment.