package com.yakivmospan.scytale; import org.hamcrest.Matchers; import org.junit.Test; import org.junit.runner.RunWith; import android.os.Build; import android.support.test.runner.AndroidJUnit4; import java.math.BigInteger; import java.security.KeyPair; import java.util.Calendar; import javax.security.auth.x500.X500Principal; import static com.yakivmospan.scytale.Options.DECRYPTION_BLOCK_SIZE; import static com.yakivmospan.scytale.Options.ENCRYPTION_BLOCK_SIZE; import static com.yakivmospan.scytale.Options.TRANSFORMATION_ASYMMETRIC; import static com.yakivmospan.scytale.Options.TRANSFORMATION_SYMMETRIC; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.core.IsNot.not; import static org.junit.Assert.assertThat; @RunWith(AndroidJUnit4.class) public class AsymmetricKeyTest extends BaseContextTest implements KeyTest { @Test @Override public void generateDefaultKeyIsNotNull() { Store store = new Store(context, STORE_NAME, STORE_PASSWORD); KeyPair keyPair = store.generateAsymmetricKey(KEY_ALIAS_ASYMMETRIC, KEY_PASSWORD); assertThat(keyPair, is(notNullValue())); store.deleteKey(KEY_ALIAS_ASYMMETRIC); } @Test @Override public void generateKeyIsNotNull() { Store store = new Store(context, STORE_NAME, STORE_PASSWORD); final Calendar start = Calendar.getInstance(); final Calendar end = Calendar.getInstance(); end.add(Calendar.YEAR, 1); KeyPair keyPair = store.generateAsymmetricKey(new KeyProps.Builder() .setAlias(KEY_ALIAS_ASYMMETRIC) .setPassword(KEY_PASSWORD) .setKeySize(KEY_SIZE) .setKeyType("RSA") .setSerialNumber(BigInteger.ONE) .setSubject(new X500Principal("CN=" + KEY_ALIAS_ASYMMETRIC + " CA Certificate")) .setStartDate(start.getTime()) .setEndDate(start.getTime()) .setBlockModes("ECB") .setEncryptionPaddings("PKCS1Padding") .setSignatureAlgorithm("SHA256WithRSAEncryption") .build()); assertThat(keyPair, is(notNullValue())); store.deleteKey(KEY_ALIAS_ASYMMETRIC); } @Test @Override public void generateKeyHasWrongType() { Store store = new Store(context, STORE_NAME, STORE_PASSWORD); final Calendar start = Calendar.getInstance(); final Calendar end = Calendar.getInstance(); end.add(Calendar.YEAR, 1); KeyPair keyPair = store.generateAsymmetricKey(new KeyProps.Builder() .setAlias(KEY_ALIAS_ASYMMETRIC) .setPassword(KEY_PASSWORD) .setKeySize(KEY_SIZE) .setKeyType("no-such-key-type") .setSerialNumber(BigInteger.ONE) .setSubject(new X500Principal("CN=" + KEY_ALIAS_ASYMMETRIC + " CA Certificate")) .setStartDate(start.getTime()) .setEndDate(start.getTime()) .setBlockModes("ECB") .setEncryptionPaddings("PKCS1Padding") .setSignatureAlgorithm("SHA256WithRSAEncryption") .build()); assertThat(keyPair, is(nullValue())); } @Test @Override public void generateKeyHasNoBlockModes() { Store store = new Store(context, STORE_NAME, STORE_PASSWORD); final Calendar start = Calendar.getInstance(); final Calendar end = Calendar.getInstance(); end.add(Calendar.YEAR, 1); KeyPair keyPair = store.generateAsymmetricKey(new KeyProps.Builder() .setAlias(KEY_ALIAS_ASYMMETRIC) .setPassword(KEY_PASSWORD) .setKeySize(KEY_SIZE) .setKeyType("RSA") .setSerialNumber(BigInteger.ONE) .setSubject(new X500Principal("CN=" + KEY_ALIAS_ASYMMETRIC + " CA Certificate")) .setStartDate(start.getTime()) .setEndDate(start.getTime()) .setBlockModes("no-such-block-modes") .setEncryptionPaddings("PKCS1Padding") .setSignatureAlgorithm("SHA256WithRSAEncryption") .build()); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { assertThat(keyPair, is(notNullValue())); store.deleteKey(KEY_ALIAS_ASYMMETRIC); } else { assertThat(keyPair, is(nullValue())); } } @Test @Override public void generateKeyHasNoEncryptionPaddings() { Store store = new Store(context, STORE_NAME, STORE_PASSWORD); final Calendar start = Calendar.getInstance(); final Calendar end = Calendar.getInstance(); end.add(Calendar.YEAR, 1); KeyPair keyPair = store.generateAsymmetricKey(new KeyProps.Builder() .setAlias(KEY_ALIAS_ASYMMETRIC) .setPassword(KEY_PASSWORD) .setKeySize(KEY_SIZE) .setKeyType("RSA") .setSerialNumber(BigInteger.ONE) .setSubject(new X500Principal("CN=" + KEY_ALIAS_ASYMMETRIC + " CA Certificate")) .setStartDate(start.getTime()) .setEndDate(start.getTime()) .setBlockModes("ECB") .setEncryptionPaddings("no-such-encryption-paddings") .setSignatureAlgorithm("SHA256WithRSAEncryption") .build()); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { assertThat(keyPair, is(notNullValue())); store.deleteKey(KEY_ALIAS_ASYMMETRIC); } else { assertThat(keyPair, is(nullValue())); } } @Test @Override public void getKeyIsNotNull() { Store store = new Store(context, STORE_NAME, STORE_PASSWORD); store.generateAsymmetricKey(KEY_ALIAS_ASYMMETRIC, KEY_PASSWORD); KeyPair keyPair = store.getAsymmetricKey(KEY_ALIAS_ASYMMETRIC, KEY_PASSWORD); assertThat(keyPair, is(notNullValue())); store = new Store(context, STORE_NAME, STORE_PASSWORD); keyPair = store.getAsymmetricKey(KEY_ALIAS_ASYMMETRIC, KEY_PASSWORD); assertThat(keyPair, is(notNullValue())); store.deleteKey(KEY_ALIAS_ASYMMETRIC); } @Test @Override public void getKeyIsNull() { Store store = new Store(context, STORE_NAME, STORE_PASSWORD); KeyPair keyPair = store.getAsymmetricKey(KEY_ALIAS_ASYMMETRIC, KEY_PASSWORD); assertThat(keyPair, is(nullValue())); } @Test @Override public void hasKeyIsTrue() { Store store = new Store(context, STORE_NAME, STORE_PASSWORD); store.generateAsymmetricKey(KEY_ALIAS_ASYMMETRIC, KEY_PASSWORD); assertThat(store.hasKey(KEY_ALIAS_ASYMMETRIC), is(true)); store.deleteKey(KEY_ALIAS_ASYMMETRIC); } @Test @Override public void hasKeyIsFalse() { Store store = new Store(context, STORE_NAME, STORE_PASSWORD); assertThat(store.hasKey(KEY_ALIAS_ASYMMETRIC), is(false)); } @Test @Override public void deleteKeyIsWorking() { Store store = new Store(context, STORE_NAME, STORE_PASSWORD); store.generateAsymmetricKey(KEY_ALIAS_ASYMMETRIC, KEY_PASSWORD); store.deleteKey(KEY_ALIAS_ASYMMETRIC); assertThat(store.hasKey(KEY_ALIAS_ASYMMETRIC), is(false)); // make sure that new instance of store also doesn't contains the key store = new Store(context, STORE_NAME, STORE_PASSWORD); assertThat(store.hasKey(KEY_ALIAS_ASYMMETRIC), is(false)); } @Test @Override public void encryptSmallDataIsValid() { Store store = new Store(context, STORE_NAME, STORE_PASSWORD); KeyPair keyPair = store.generateAsymmetricKey(KEY_ALIAS_ASYMMETRIC, KEY_PASSWORD); Crypto crypto = new Crypto(TRANSFORMATION_ASYMMETRIC); String encrypt = crypto.encrypt(SMALL_DATA, keyPair); String decrypt = crypto.decrypt(encrypt, keyPair); assertThat(SMALL_DATA, is(decrypt)); store.deleteKey(KEY_ALIAS_ASYMMETRIC); } @Test @Override public void encryptLargeDataIsValid() { Store store = new Store(context, STORE_NAME, STORE_PASSWORD); KeyPair keyPair = store.generateAsymmetricKey(KEY_ALIAS_ASYMMETRIC, KEY_PASSWORD); Crypto crypto = new Crypto(TRANSFORMATION_ASYMMETRIC, ENCRYPTION_BLOCK_SIZE, DECRYPTION_BLOCK_SIZE); String encrypt = crypto.encrypt(LARGE_DATA, keyPair); String decrypt = crypto.decrypt(encrypt, keyPair); assertThat(LARGE_DATA, is(decrypt)); store.deleteKey(KEY_ALIAS_ASYMMETRIC); } @Test public void encryptLargeDataWith512KeySizeIsValid() { encryptLargeDataIsValid(512); } @Test public void encryptLargeDataWith1024KeySizeIsValid() { encryptLargeDataIsValid(1024); } @Test public void encryptLargeDataWith2048KeySizeIsValid() { encryptLargeDataIsValid(2048); } @Test public void encryptLargeDataWith3072KeySizeIsValid() { encryptLargeDataIsValid(3072); } @Test public void encryptLargeDataWith4096KeySizeIsValid() { encryptLargeDataIsValid(4096); } @Test @Override public void encryptDataIsNotValid() { // different keys encryption Store store = new Store(context, STORE_NAME, STORE_PASSWORD); KeyPair keyPair = store.generateAsymmetricKey(KEY_ALIAS_ASYMMETRIC, KEY_PASSWORD); KeyPair keyPair2 = store.generateAsymmetricKey("key-pair-2", KEY_PASSWORD); Crypto crypto = new Crypto(TRANSFORMATION_SYMMETRIC); String encrypt = crypto.encrypt(SMALL_DATA, keyPair); String decrypt = crypto.decrypt(encrypt, keyPair2); assertThat(SMALL_DATA, is(not(decrypt))); store.deleteKey(KEY_ALIAS_ASYMMETRIC); store.deleteKey("key-pair-2"); // wrong block props for large data try { keyPair = store.generateAsymmetricKey(KEY_ALIAS_ASYMMETRIC, KEY_PASSWORD); crypto = new Crypto(TRANSFORMATION_ASYMMETRIC); encrypt = crypto.encrypt(LARGE_DATA, keyPair); decrypt = crypto.decrypt(encrypt, keyPair); } catch (Exception e) { assertThat(Build.VERSION.SDK_INT, is(Matchers.lessThan(Build.VERSION_CODES.JELLY_BEAN_MR2))); assertThat(e, is(instanceOf(ArrayIndexOutOfBoundsException.class))); } assertThat(LARGE_DATA, is(not(decrypt))); store.deleteKey(KEY_ALIAS_ASYMMETRIC); } private void encryptLargeDataIsValid(int keySize) { Store store = new Store(context, STORE_NAME, STORE_PASSWORD); final Calendar start = Calendar.getInstance(); final Calendar end = Calendar.getInstance(); end.add(Calendar.YEAR, 1); KeyProps keyProps = new KeyProps.Builder() .setAlias(KEY_ALIAS_ASYMMETRIC) .setPassword(KEY_PASSWORD) .setKeySize(keySize) .setKeyType("RSA") .setSerialNumber(BigInteger.ONE) .setSubject(new X500Principal("CN=" + KEY_ALIAS_ASYMMETRIC + " CA Certificate")) .setStartDate(start.getTime()) .setEndDate(start.getTime()) .setBlockModes("ECB") .setEncryptionPaddings("PKCS1Padding") .setSignatureAlgorithm("SHA256WithRSAEncryption") .build(); KeyPair keyPair = store.generateAsymmetricKey(keyProps); int encryptionBlock; int decryptionBlock; if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR2) { encryptionBlock = Options.RSA_ECB_PKCS1PADDING_ENCRYPTION_BLOCK_SIZE_FOR_JELLY_BEAN; decryptionBlock = Options.RSA_ECB_PKCS1PADDING_DECRYPTION_BLOCK_SIZE_FOR_JELLY_BEAN; } else { encryptionBlock = keySize / 8 - 11; decryptionBlock = keySize / 8; } Crypto crypto = new Crypto(TRANSFORMATION_ASYMMETRIC, encryptionBlock, decryptionBlock); String encrypt = crypto.encrypt(LARGE_DATA, keyPair); String decrypt = crypto.decrypt(encrypt, keyPair); assertThat(LARGE_DATA, is(decrypt)); store.deleteKey(KEY_ALIAS_ASYMMETRIC); } }