From 70fca34c5d32d55d6cf7d35a3c54e31b88f56412 Mon Sep 17 00:00:00 2001 From: David Miguel Lozano Date: Mon, 9 Jul 2018 21:00:27 +0200 Subject: [PATCH] Support devices runing KitKat (API 19) #6 --- app/build.gradle | 4 +- .../favre/lib/armadillo/AesGcmEncryption.java | 37 +++++++++++++++---- .../armadillo/AuthenticatedEncryption.java | 7 +++- .../java/at/favre/lib/armadillo/Utils.java | 17 +++++++++ 4 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 armadillo/src/main/java/at/favre/lib/armadillo/Utils.java diff --git a/app/build.gradle b/app/build.gradle index 1404758..e421885 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,7 +5,7 @@ android { buildToolsVersion rootProject.ext.buildToolsVersion defaultConfig { - minSdkVersion 21 + minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode rootProject.ext.versionCode versionName rootProject.ext.versionNameApp @@ -22,7 +22,7 @@ android { } dependencies { - implementation 'at.favre.lib:armadillo:0.4.3' + implementation project(':armadillo') implementation "com.android.support:appcompat-v7:$rootProject.ext.dependencies.support" testImplementation "junit:junit:$rootProject.ext.dependencies.junit" androidTestImplementation 'com.android.support.test:runner:1.0.2' diff --git a/armadillo/src/main/java/at/favre/lib/armadillo/AesGcmEncryption.java b/armadillo/src/main/java/at/favre/lib/armadillo/AesGcmEncryption.java index 1ec86aa..d2f94d6 100644 --- a/armadillo/src/main/java/at/favre/lib/armadillo/AesGcmEncryption.java +++ b/armadillo/src/main/java/at/favre/lib/armadillo/AesGcmEncryption.java @@ -5,13 +5,17 @@ import java.nio.ByteBuffer; import java.security.Provider; import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import at.favre.lib.bytes.Bytes; +import static at.favre.lib.armadillo.Utils.isKitKat; + /** * Implements AES (Advanced Encryption Standard) with Galois/Counter Mode (GCM), which is a mode of * operation for symmetric key cryptographic block ciphers that has been widely adopted because of @@ -31,11 +35,12 @@ * @author Patrick Favre-Bulle * @since 18.12.2017 */ -@SuppressWarnings("WeakerAccess") +@SuppressWarnings({"WeakerAccess", "SameParameterValue"}) final class AesGcmEncryption implements AuthenticatedEncryption { + private static final String ALGORITHM = "AES/GCM/NoPadding"; - private static final int TAG_LENGTH_BIT = 128; - private static final int IV_LENGTH_BYTE = 12; + private static final int TAG_LENGTH_BYTES = 16; + private static final int IV_LENGTH_BYTES = 12; private final SecureRandom secureRandom; private final Provider provider; @@ -61,13 +66,14 @@ public byte[] encrypt(byte[] rawEncryptionKey, byte[] rawData, @Nullable byte[] } try { - byte[] iv = new byte[IV_LENGTH_BYTE]; + byte[] iv = new byte[IV_LENGTH_BYTES]; secureRandom.nextBytes(iv); final Cipher cipher = getCipher(); - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(rawEncryptionKey, "AES"), new GCMParameterSpec(TAG_LENGTH_BIT, iv)); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(rawEncryptionKey, "AES"), getParams(iv)); - if (associatedData != null) { + if (associatedData != null && associatedData.length != 0 && !isKitKat()) { + // associated data is not available on Android KitKat cipher.updateAAD(associatedData); } @@ -99,8 +105,9 @@ public byte[] decrypt(byte[] rawEncryptionKey, byte[] encryptedData, @Nullable b byteBuffer.get(encrypted); final Cipher cipher = getCipher(); - cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(rawEncryptionKey, "AES"), new GCMParameterSpec(TAG_LENGTH_BIT, iv)); - if (associatedData != null) { + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(rawEncryptionKey, "AES"), getParams(iv)); + if (associatedData != null && associatedData.length != 0 && !isKitKat()) { + // associated data is not available on Android KitKat cipher.updateAAD(associatedData); } return cipher.doFinal(encrypted); @@ -132,4 +139,18 @@ private Cipher getCipher() { } return cipher; } + + private AlgorithmParameterSpec getParams(final byte[] iv) { + return getParams(iv, 0, iv.length); + } + + private AlgorithmParameterSpec getParams(final byte[] buf, int offset, int len) { + if (isKitKat()) { + // GCMParameterSpec should always be present in Java 7 or newer, but it's missing on + // some Android devices with API level <= 19. Fortunately, we can initialize the cipher + // with just an IvParameterSpec. It will use a tag size of 128 bits. + return new IvParameterSpec(buf, offset, len); + } + return new GCMParameterSpec(TAG_LENGTH_BYTES * 8, buf, offset, len); + } } diff --git a/armadillo/src/main/java/at/favre/lib/armadillo/AuthenticatedEncryption.java b/armadillo/src/main/java/at/favre/lib/armadillo/AuthenticatedEncryption.java index ac1356e..5032a57 100644 --- a/armadillo/src/main/java/at/favre/lib/armadillo/AuthenticatedEncryption.java +++ b/armadillo/src/main/java/at/favre/lib/armadillo/AuthenticatedEncryption.java @@ -39,7 +39,9 @@ public interface AuthenticatedEncryption { * * @param rawEncryptionKey to use as encryption key material * @param rawData to encrypt - * @param associatedData additional data used to create the auth tag and will be subject to integrity/authentication check + * @param associatedData additional data used to create the auth tag and will be subject to + * integrity/authentication check (associatedData is not available on + * Android KitKat (API level 19), it will be ignored) * @return encrypted content * @throws AuthenticatedEncryptionException if any crypto fails */ @@ -51,7 +53,8 @@ public interface AuthenticatedEncryption { * @param rawEncryptionKey to use as decryption key material * @param encryptedData to decrypt * @param associatedData additional data used to create the auth tag; must be same as provided - * in the encrypt step + * in the encrypt step (associatedData is not available on Android KitKat + * (API level 19), it will be ignored) * @return decrypted, original data * @throws AuthenticatedEncryptionException if any crypto fails */ diff --git a/armadillo/src/main/java/at/favre/lib/armadillo/Utils.java b/armadillo/src/main/java/at/favre/lib/armadillo/Utils.java new file mode 100644 index 0000000..af68dd7 --- /dev/null +++ b/armadillo/src/main/java/at/favre/lib/armadillo/Utils.java @@ -0,0 +1,17 @@ +package at.favre.lib.armadillo; + +import android.os.Build; + +class Utils { + + private Utils() { + // Utility class + } + + /** + * Checks whether the device runs Android KitKat (API level 19). + */ + static boolean isKitKat() { + return Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT; + } +}