Skip to content
This repository has been archived by the owner on Apr 25, 2023. It is now read-only.

Commit

Permalink
Started adding support for non-fingerprint encryption
Browse files Browse the repository at this point in the history
  • Loading branch information
BrianEstrada committed Apr 13, 2018
1 parent c96ccae commit e6f627e
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 113 deletions.
61 changes: 37 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The library itself is written in Java but most of the examples you'll find here
```Kotlin
private fun showFingerprintDialog(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
FingerprintDialog.Builder(this) // Provide context for our dialog
Locksmith.getFingerprintDialogBuilder(this) // Provide context for our dialog
.setTitle(titleText) // Title Text
.setSubtitle(subtitleText) // Subtitle Text
.setDescription(descriptionText) // Description Text
Expand Down Expand Up @@ -117,30 +117,43 @@ If the only thing you're looking to do is get verification for a login then the

##### Step 3) Handling Errors

```Kotlin
private fun handleException(e: LocksmithEncryptionException) {
when (e.type) {
UninitiatedCipher-> {
// Will return this if the Fingerprint Dialog was not shown first before trying to encrypt/decrypt
}
Unauthenticated -> {
// Returns this error if the key used to encrypt/decrypt has been invalidated, if you get this error you
// Should show the fingerprint dialog again
}
InvalidData -> {
// Will return this if the data provided is not valid encrypted data
}
InvalidKey,
InvalidAlgorithm,
IllegalBlockSize,
BadPadding -> {
// Will return this if for whatever reason we fail to encrypt/decrypt data
}
Generic -> {
// A generic catch for our error
}
}
```Koltin
private fun handleException(e: LocksmithEncryptionException) {
Log.e(TAG, "handleException")
when (e.type) {
/**
* Will return this type if the cipher/algorithm was not properly initiated
*/
Uninitiated -> {}
/**
* Will return this type if key has expired
* (will usually require you to go through the fingerprint validation sequence again)
*/
Unauthenticated -> {}
/**
* Will return this type if the data fed to the encrypt method isn't a valid encrypted message
*/
InvalidData -> {}
/**
* Will return this type if the data is too long or the wrong size
*/
EncryptionError -> {}
/**
* Thrown when an unknown error is caught
*/
Generic -> {}
}
}
```

Because of the way Kotlin handles checked exceptions we opted to wrap all exceptions and return a ENUM with the exception as well (makes it easier)
Expand Down
20 changes: 9 additions & 11 deletions app/src/main/java/dk/nodes/locksmith/example/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity
import android.util.Log
import dk.nodes.locksmith.core.Locksmith
import dk.nodes.locksmith.core.encryption.EncryptionManager
import dk.nodes.locksmith.core.exceptions.LocksmithEncryptionException
import dk.nodes.locksmith.core.exceptions.LocksmithEncryptionException.Type.*
import dk.nodes.locksmith.core.fingerprint.FingerprintDialog
Expand Down Expand Up @@ -91,12 +90,14 @@ class MainActivity : AppCompatActivity(), FingerprintDialog.OnFingerprintDialogE

private fun handleException(e: LocksmithEncryptionException) {
Log.e(TAG, "handleException")
e.printStackTrace()


when (e.type) {
UninitiatedCipher,
Uninitiated -> {
Log.e(TAG, "Uninitiated")
showFingerprintDialog()
}
Unauthenticated -> {
Log.e(TAG, "UninitiatedCipher,Unauthenticated")
Log.e(TAG, "Unauthenticated")
showFingerprintDialog()
}
InvalidData -> {
Expand All @@ -106,18 +107,15 @@ class MainActivity : AppCompatActivity(), FingerprintDialog.OnFingerprintDialogE
Snackbar.LENGTH_SHORT
).show()
}
InvalidKey,
InvalidAlgorithm,
IllegalBlockSize,
BadPadding -> {
EncryptionError -> {
Snackbar.make(
mainRootContainer,
R.string.errorGeneric,
Snackbar.LENGTH_SHORT
).show()
}
Generic -> {
Log.e(TAG, "Generic")
Log.e(TAG, "Generic", e)
}
}
}
Expand All @@ -134,7 +132,7 @@ class MainActivity : AppCompatActivity(), FingerprintDialog.OnFingerprintDialogE
val successMessage = getString(R.string.fingerprintDialogSuccessMessage)
val errorMessage = getString(R.string.fingerprintDialogErrorMessage)

FingerprintDialog.Builder(this)
Locksmith.getFingerprintDialogBuilder(this)
.setTitle(titleText)
.setSubtitle(subtitleText)
.setDescription(descriptionText)
Expand Down
2 changes: 1 addition & 1 deletion locksmith/maven-push.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ allprojects { ext."signing.password" = System.getenv('password') }

// Artifact settings
def _group = 'dk.nodes.locksmith'
def _version = '1.0.1'
def _version = '1.0.2'
def _archivesBaseName = 'core'

def _name = 'Locksmith Encryption Library'
Expand Down
20 changes: 13 additions & 7 deletions locksmith/src/main/java/dk/nodes/locksmith/core/Locksmith.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,36 @@
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;

import dk.nodes.locksmith.core.encryption.EncryptionManager;
import dk.nodes.locksmith.core.encryption.FingerprintEncryptionManager;
import dk.nodes.locksmith.core.exceptions.LocksmithEncryptionException;
import dk.nodes.locksmith.core.fingerprint.FingerprintDialog;

public class Locksmith {
@Nullable
public static EncryptionManager encryptionManager;
private static FingerprintEncryptionManager fingerprintEncryptionManager;

public static void init(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
encryptionManager = new EncryptionManager();
fingerprintEncryptionManager = new FingerprintEncryptionManager();
}
}

@RequiresApi(api = Build.VERSION_CODES.M)
public static String encrypt(String data) throws LocksmithEncryptionException {
// This call requires version M so we should assume that the encryption manager will not be null
assert encryptionManager != null;
return encryptionManager.encryptString(data);
assert fingerprintEncryptionManager != null;
return fingerprintEncryptionManager.encrypt(data);
}

@RequiresApi(api = Build.VERSION_CODES.M)
public static String decrypt(String data) throws LocksmithEncryptionException {
// This call requires version M so we should assume that the encryption manager will not be null
assert encryptionManager != null;
return encryptionManager.decryptString(data);
assert fingerprintEncryptionManager != null;
return fingerprintEncryptionManager.decrypt(data);
}

@RequiresApi(api = Build.VERSION_CODES.M)
public static FingerprintDialog.Builder getFingerprintDialogBuilder(Context context) {
return new FingerprintDialog.Builder(context, fingerprintEncryptionManager);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
import android.security.keystore.KeyProperties;
import android.security.keystore.UserNotAuthenticatedException;
import android.support.annotation.RequiresApi;
import android.util.Log;

import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
Expand All @@ -20,55 +21,62 @@
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

import dk.nodes.locksmith.core.exceptions.LocksmithCreationException;
import dk.nodes.locksmith.core.exceptions.LocksmithEncryptionException;
import dk.nodes.locksmith.core.models.EncryptionData;

@RequiresApi(api = Build.VERSION_CODES.M)
public class EncryptionManager {
private static final String TAG = EncryptionManager.class.getSimpleName();

public class FingerprintEncryptionManager {
private KeyStore keyStore;
private Cipher cipher;
private String KEY_NAME_ENCRYPTION = "LockSmithEncryptionKey";

private String KEY_NAME_ENCRYPTION = "LockSmithFingerprintEncryptionKey";

private Charset charset = Charset.forName("UTF-8");

public void init(int validityDuration) {
public void init(int validityDuration) throws LocksmithCreationException {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);

if (!keyStore.containsAlias(KEY_NAME_ENCRYPTION)) {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");

keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME_ENCRYPTION, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationValidityDurationSeconds(validityDuration)
.build());

keyGenerator.generateKey();
generateKey(KEY_NAME_ENCRYPTION, validityDuration);
}

cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7);

} catch (Exception e) {
Log.e(TAG, "Initiation Failed", e);
throw new LocksmithCreationException(e);
}
}

public String encryptString(String data) throws LocksmithEncryptionException {
private void generateKey(String keyName, int validityDuration) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");

KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyName, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(validityDuration);

keyGenerator.init(builder.build());

keyGenerator.generateKey();
}

public String encrypt(String data) throws LocksmithEncryptionException {
byte[] decryptedData = data.getBytes(charset);
EncryptionData encryptionData = encrypt(decryptedData);
EncryptionData encryptionData = encryptBytes(decryptedData);
return encryptionData.encode();
}

public String decryptString(String data) throws LocksmithEncryptionException {
public String decrypt(String data) throws LocksmithEncryptionException {
EncryptionData encryptionData = new EncryptionData(data);
byte[] decryptedData = decrypt(encryptionData.data, encryptionData.iv);
byte[] decryptedData = decryptBytes(encryptionData.data, encryptionData.iv);
return new String(decryptedData, charset);
}

private EncryptionData encrypt(byte[] data) throws LocksmithEncryptionException {
private EncryptionData encryptBytes(byte[] data) throws LocksmithEncryptionException {
try {
keyStore.load(null);
SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME_ENCRYPTION, null);
Expand All @@ -80,25 +88,23 @@ private EncryptionData encrypt(byte[] data) throws LocksmithEncryptionException

return new EncryptionData(resultData, resultIv);
} catch (NullPointerException e) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.UninitiatedCipher, e);
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.Uninitiated, e);
} catch (UserNotAuthenticatedException e) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.Unauthenticated, e);
} catch (InvalidKeyException e) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.InvalidKey, e);
} catch (BadPaddingException e) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.BadPadding, e);
} catch (InvalidKeyException | BadPaddingException e) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.EncryptionError, e);
} catch (IllegalBlockSizeException e) {
if (e.getCause() instanceof KeyStoreException) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.Unauthenticated, e);
} else {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.IllegalBlockSize, e);
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.EncryptionError, e);
}
} catch (Exception e) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.Generic, e);
}
}

private byte[] decrypt(byte[] data, byte[] iv) throws LocksmithEncryptionException {
private byte[] decryptBytes(byte[] data, byte[] iv) throws LocksmithEncryptionException {
try {
keyStore.load(null);
SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME_ENCRYPTION, null);
Expand All @@ -107,21 +113,17 @@ private byte[] decrypt(byte[] data, byte[] iv) throws LocksmithEncryptionExcepti
cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);
return cipher.doFinal(data);
} catch (NullPointerException e) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.UninitiatedCipher, e);
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.Uninitiated, e);
} catch (UserNotAuthenticatedException e) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.Unauthenticated, e);
} catch (InvalidKeyException e) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.InvalidKey, e);
} catch (BadPaddingException e) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.BadPadding, e);
} catch (InvalidKeyException | BadPaddingException | InvalidAlgorithmParameterException e) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.EncryptionError, e);
} catch (IllegalBlockSizeException e) {
if (e.getCause() instanceof KeyStoreException) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.Unauthenticated, e);
} else {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.IllegalBlockSize, e);
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.EncryptionError, e);
}
} catch (InvalidAlgorithmParameterException e) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.InvalidAlgorithm, e);
} catch (Exception e) {
throw new LocksmithEncryptionException(LocksmithEncryptionException.Type.Generic, e);
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dk.nodes.locksmith.core.exceptions;

public class LocksmithCreationException extends Exception {
public LocksmithCreationException(Exception e) {
super(e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,26 @@ public Type getType() {
}

public enum Type {
// Initiation Errors
/**
* Will return this type if the cipher/algorithm was not properly initiated
*/
Uninitiated,
// Key Error
/**
* Will return this type if key has expired (will usually require you to go through the fingerprint validation sequence again)
*/
Unauthenticated,
// Data Errors
/**
* Will return this type if the data fed to the encrypt method isn't a valid encrypted message
*/
InvalidData,
// Algorithm Errors
/**
* Will return this type if the data is too long or the wrong size
*/
IllegalBlockSize,
/**
* Will return this type if the selected padding is incorrect
*/
BadPadding,
/**
* Will return this type if the key provided is the wrong one
*/
InvalidKey,
/**
* Will return this type if the algorithm selected is invalid
*/
InvalidAlgorithm,
/**
* Will return this type if the cipher was not properly initiated
*/
UninitiatedCipher,
EncryptionError,
/**
* Thrown when an unknown error is caught
*/
Expand Down
Loading

0 comments on commit e6f627e

Please sign in to comment.