Closed idish closed 6 years ago
Very on point:
1) You should use the key length for the crypto you are using. If you are already using 128 bits, that's fine. Generate 16 bits. if you want to upgrade change it. That's the key length that will be generated, so it depends on what you need.
If I'm not wrong the AndroidConceal.createDefaultCrypto(...) default creates one with 256 bits. But you can still use the createCrypto128Bits(...) method.
2) For now there's only one implementation for the KeyChain, the one that randomly generates one and stores it in SharedPreferences, but there should be more.
There's some work to do here around key handling (which is as important as encryption itself!). I didn't work lately on any encryption related projects so I couldn't contribute on it.
Said that. If you are basing your encryption key on a password, that's cool because it doesn't get stored anyway (only in user's brain) so there's no problem of how to store/communicate the key securely.
What you need to do is implement a KeyChain interface based on the generator.
public class PasswordGeneratedKeyChain implements KeyChain {
private final CryptoConfig mConfig;
private final PasswordBasedKeyDerivation mDerivation;
private final byte[] mKey;
public PasswordGeneratedKeyChain(CryptoConfig config) {
mConfig = config;
mDerivation = new PasswordBasedKeyDerivation(AndroidConceal.get().nativeLibrary);
mDerivation.setKeyLengthInBytes(config.keyLength);
}
public void setPassword(String pwd) { mDerivation.setPassword(pwd); }
// used only for reading, it should read the salt from the same place the encrypted content is
public void setSalt(byte[] salt) { mDerivation.setSalt(salt); }
// used only for encrypting, you will need to store it in the same place you're writing the encrypted content
public byte[] getSalt() { return mDerivation.getSalt(); }
public void generate() {
mKey = mDerivation.generate();
}
/// implementing Key Chain
// key for encryption
public byte[] getCipherKey() {
if (mKey == null) throw new IllegalStateException("You need to call generate() first");
return mKey;
}
// key for mac
public byte[] getMacKey() {
// if you need mac you need a second derivation object
throw new UnsupportedOperationException("implemented only for encryption, not mac");
}
// this is just a glorified "get me a new nonce"
public byte[] getNewIV() {
byte[] result = new byte[mConfig.ivLength];
AndroidConceal.get().secureRandom.nextBytes(result);
return result;
}
public void destroyKeys() {
Arrays.fill(mKey, (byte) 0);
mKey = null;
}
}
And then you can use your PasswordGeneratedKeyChain where you need it:
PasswordGeneratedKeyChain pgkc = new PasswordGeneratedKeyChain(CryptoConfig.KEY_256);
pgkc.setPassword(pwd);
pgkc.generate(); // generates a new salt + key
Crypto c = AndroidConceal.get().createDefaultCrypto(pgkc);
// use c to encrypt
and
PasswordGeneratedKeyChain pgkc = new PasswordGeneratedKeyChain(CryptoConfig.KEY_256);
pgkc.setPassword(pwd);
pgkc.setSalt(saltStoredAlongTheEncryptedContent);
pgkc.generate(); // generates a new salt + key
Crypto c = AndroidConceal.get().createDefaultCrypto(pgkc);
// use c to decrypt
I should probably add something like this to the library so you don't need to reimplement it. For now copy that and check it out!
NOTICE: the previous message assumed that each password/pin was per encrypted content.
In the case you're using the same password/pin per user, or unique, the salt could be the same for several different encrypted contents as it has nothing to do with it:
USER_PIN + SALT -> USER_KEY
In that case you could store it somewhere (shared prefs?). But that depends on how to intend to use it.
Thank you, that is great, works as expected!
I've implemented another KeyChain
representing a key chain based on a given key, and not generated one.
If you would like to add this one as well to the library, here it is (or would you like me to PR?)
I'm not sure about the naming of the class tho:
public class ConstKeyChain implements KeyChain {
private byte[] mKey;
private final CryptoConfig mConfig;
public ConstKeyChain(byte[] key, CryptoConfig config) {
if (key.length != config.keyLength) {
throw new IllegalStateException("key must be the same length as the config states");
}
mConfig = config;
mKey = key;
}
@Override
public byte[] getCipherKey() throws KeyChainException {
return mKey;
}
@Override
public byte[] getMacKey() throws KeyChainException {
throw new UnsupportedOperationException("implemented only for encryption, not mac");
}
// this is just a glorified "get me a new nonce"
public byte[] getNewIV() {
byte[] result = new byte[mConfig.ivLength];
AndroidConceal.get().secureRandom.nextBytes(result);
return result;
}
@Override
public void destroyKeys() {
Arrays.fill(mKey, (byte) 0);
mKey = null;
}
}
I have to sit down and do some housekeeping. I will take the opportunity to upload it then. Thanks for the testing!
I've been trying to use conceal to encrypt/decrypt data based on a user's passphrase and not a random key.
2 questions:
README
file that keys should use 256 bit keys, and not 128 since version 1.1.3?Crypto
object out of thisbyte[]
type key. Do I need to generate aKeyChain
object before? How? Unfortunately I couldn't find any hint about this one, also in thetests
package.Thank you!