Add some more tests

This adds tests for all of the importers, a new scrypt test and some more OTP tests. More to come.
This commit is contained in:
Alexander Bakker 2020-12-16 21:44:37 +01:00
parent dea13f56f5
commit 4f8a0b9020
40 changed files with 974 additions and 321 deletions

View file

@ -1,43 +0,0 @@
package com.beemdevelopment.aegis;
import com.beemdevelopment.aegis.crypto.CryptoUtils;
import com.beemdevelopment.aegis.crypto.SCryptParameters;
import com.beemdevelopment.aegis.encoding.EncodingException;
import com.beemdevelopment.aegis.encoding.Hex;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import javax.crypto.SecretKey;
import static org.junit.jupiter.api.Assertions.*;
public class SCryptTest {
@Test
public void testTrailingNullCollision() throws EncodingException {
byte[] salt = new byte[0];
SCryptParameters params = new SCryptParameters(
CryptoUtils.CRYPTO_SCRYPT_N,
CryptoUtils.CRYPTO_SCRYPT_p,
CryptoUtils.CRYPTO_SCRYPT_r,
salt
);
byte[] head = new byte[]{'t', 'e', 's', 't'};
byte[] expectedKey = Hex.decode("41cd8110d0c66ede16f97ce84fd8e2bd2269c9318532a01437789dfbadd1392e");
for (int i = 0; i < 128; i += 4) {
byte[] input = new byte[head.length + i];
System.arraycopy(head, 0, input, 0, head.length);
// once the length of the input is over 64 bytes, trailing nulls do not cause a collision anymore
SecretKey key = CryptoUtils.deriveKey(input, params);
if (input.length <= 64) {
assertArrayEquals(expectedKey, key.getEncoded());
} else {
assertFalse(Arrays.equals(expectedKey, key.getEncoded()));
}
}
}
}

View file

@ -0,0 +1,88 @@
package com.beemdevelopment.aegis.crypto;
import com.beemdevelopment.aegis.encoding.EncodingException;
import com.beemdevelopment.aegis.encoding.Hex;
import org.bouncycastle.crypto.generators.SCrypt;
import org.junit.Test;
import java.util.Arrays;
import javax.crypto.SecretKey;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
public class SCryptTest {
private static class Vector {
private final byte[] _key;
private final char[] _password;
private final byte[] _salt;
private final int _n;
private final int _r;
private final int _p;
private final int _len;
public Vector(String key, String password, String salt, int n, int r, int p, int len) throws EncodingException {
_key = Hex.decode(key);
_password = password.toCharArray();
_salt = CryptoUtils.toBytes(salt.toCharArray());
_n = n;
_r = r;
_p = p;
_len = len;
}
public void validate() {
SCryptParameters params = new SCryptParameters(_n, _r, _p, _salt);
byte[] key = SCrypt.generate(CryptoUtils.toBytes(_password), params.getSalt(), params.getN(), params.getR(), params.getP(), _len);
assertArrayEquals(_key, key);
}
}
@Test
public void vectorsMatch() throws EncodingException {
// https://tools.ietf.org/html/rfc7914.html#section-12
final Vector[] vectors = new Vector[]{
new Vector("77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906",
"", "", 1 << 4, 1, 1, 64),
new Vector("fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640",
"password", "NaCl", 1 << 10, 8, 16, 64),
new Vector("7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887",
"pleaseletmein", "SodiumChloride", 1 << 14, 8, 1, 64),
new Vector("2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4",
"pleaseletmein", "SodiumChloride", 1 << 20, 8, 1, 64)
};
for (Vector vector : vectors) {
vector.validate();
}
}
@Test
public void testTrailingNullCollision() throws EncodingException {
byte[] salt = new byte[0];
SCryptParameters params = new SCryptParameters(
CryptoUtils.CRYPTO_SCRYPT_N,
CryptoUtils.CRYPTO_SCRYPT_p,
CryptoUtils.CRYPTO_SCRYPT_r,
salt
);
byte[] head = new byte[]{'t', 'e', 's', 't'};
byte[] expectedKey = Hex.decode("41cd8110d0c66ede16f97ce84fd8e2bd2269c9318532a01437789dfbadd1392e");
for (int i = 0; i < 128; i += 4) {
byte[] input = new byte[head.length + i];
System.arraycopy(head, 0, input, 0, head.length);
// once the length of the input is over 64 bytes, trailing nulls do not cause a collision anymore
SecretKey key = CryptoUtils.deriveKey(input, params);
if (input.length <= 64) {
assertArrayEquals(expectedKey, key.getEncoded());
} else {
assertFalse(Arrays.equals(expectedKey, key.getEncoded()));
}
}
}
}

View file

@ -1,18 +1,15 @@
package com.beemdevelopment.aegis;
package com.beemdevelopment.aegis.crypto.otp;
import com.beemdevelopment.aegis.crypto.otp.HOTP;
import com.beemdevelopment.aegis.crypto.otp.OTP;
import org.junit.jupiter.api.Test;
import org.junit.Test;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.Assert.assertEquals;
public class HOTPTest {
// https://tools.ietf.org/html/rfc4226#page-32
private final String[] _vectors = {
public static final String[] VECTORS = {
"755224", "287082",
"359152", "969429",
"338314", "254676",
@ -20,16 +17,16 @@ public class HOTPTest {
"399871", "520489"
};
private final byte[] _secret = new byte[]{
public static final byte[] SECRET = new byte[]{
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30,
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30
};
@Test
public void vectorsMatch() throws InvalidKeyException, NoSuchAlgorithmException {
for (int i = 0; i < _vectors.length; i++) {
OTP otp = HOTP.generateOTP(_secret, "HmacSHA1", 6, i);
assertEquals(_vectors[i], otp.toString());
for (int i = 0; i < VECTORS.length; i++) {
OTP otp = HOTP.generateOTP(SECRET, "HmacSHA1", 6, i);
assertEquals(VECTORS[i], otp.toString());
}
}
}

View file

@ -1,18 +1,14 @@
package com.beemdevelopment.aegis;
package com.beemdevelopment.aegis.crypto.otp;
import com.beemdevelopment.aegis.crypto.otp.OTP;
import com.beemdevelopment.aegis.crypto.otp.TOTP;
import org.junit.jupiter.api.Test;
import org.junit.Test;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.Assert.assertEquals;
public class TOTPTest {
private static class Vector {
public static class Vector {
public long Time;
public String OTP;
public String Algo;
@ -25,7 +21,7 @@ public class TOTPTest {
}
// https://tools.ietf.org/html/rfc6238#appendix-B
private final Vector[] _vectors = {
public static final Vector[] VECTORS = {
new Vector(59, "94287082", "HmacSHA1"),
new Vector(59, "46119246", "HmacSHA256"),
new Vector(59, "90693936", "HmacSHA512"),
@ -46,19 +42,19 @@ public class TOTPTest {
new Vector(20000000000L, "47863826", "HmacSHA512")
};
private final byte[] _seed = new byte[]{
private static final byte[] SEED = new byte[]{
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30,
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30
};
private final byte[] _seed32 = new byte[]{
private static final byte[] SEED32 = new byte[]{
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32
};
private final byte[] _seed64 = new byte[]{
private static final byte[] SEED64 = new byte[]{
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
@ -67,26 +63,23 @@ public class TOTPTest {
@Test
public void vectorsMatch() throws NoSuchAlgorithmException, InvalidKeyException {
for (Vector vector : _vectors) {
byte[] seed;
switch (vector.Algo) {
case "HmacSHA1":
seed = _seed;
break;
case "HmacSHA256":
seed = _seed32;
break;
case "HmacSHA512":
seed = _seed64;
break;
default:
fail("unsupported mode");
return;
}
for (Vector vector : VECTORS) {
byte[] seed = getSeed(vector.Algo);
OTP otp = TOTP.generateOTP(seed, vector.Algo, 8, 30, vector.Time);
assertEquals(vector.OTP, otp.toString());
}
}
public static byte[] getSeed(String algorithm) {
switch (algorithm) {
case "HmacSHA1":
return SEED;
case "HmacSHA256":
return SEED32;
case "HmacSHA512":
return SEED64;
default:
throw new RuntimeException(String.format("Unsupported algorithm: %s", algorithm));
}
}
}

View file

@ -0,0 +1,331 @@
package com.beemdevelopment.aegis.importers;
import android.content.Context;
import android.os.Build;
import androidx.test.core.app.ApplicationProvider;
import com.beemdevelopment.aegis.encoding.Base32;
import com.beemdevelopment.aegis.encoding.EncodingException;
import com.beemdevelopment.aegis.otp.HotpInfo;
import com.beemdevelopment.aegis.otp.OtpInfoException;
import com.beemdevelopment.aegis.otp.SteamInfo;
import com.beemdevelopment.aegis.otp.TotpInfo;
import com.beemdevelopment.aegis.util.UUIDMap;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@Config(sdk = { Build.VERSION_CODES.P })
@RunWith(RobolectricTestRunner.class)
public class DatabaseImporterTest {
private List<VaultEntry> _vectors;
/**
* The procedure for adding new importer tests is as follows:
* 1. Generate QR codes for each test vector:
* -> while read line; do (qrencode "$line" -o - | feh -); done < ./app/src/test/resources/com/beemdevelopment/aegis/importers/plain
* 2. Scan the QR codes with the app we want to test our import functionality of
* 3. Create an export and add the file to the importers resource directory.
* 4. Add a new test for it here.
*/
@Before
public void initVectors() throws EncodingException, OtpInfoException {
_vectors = Lists.newArrayList(
new VaultEntry(new TotpInfo(Base32.decode("4SJHB4GSD43FZBAI7C2HLRJGPQ")), "Mason", "Deno"),
new VaultEntry(new TotpInfo(Base32.decode("5OM4WOOGPLQEF6UGN3CPEOOLWU"), "SHA256", 7, 20), "James", "SPDX"),
new VaultEntry(new TotpInfo(Base32.decode("7ELGJSGXNCCTV3O6LKJWYFV2RA"), "SHA512", 8, 50), "Elijah", "Airbnb"),
new VaultEntry(new HotpInfo(Base32.decode("YOOMIXWS5GN6RTBPUFFWKTW5M4"), "SHA1", 6, 1), "James", "Issuu"),
new VaultEntry(new HotpInfo(Base32.decode("KUVJJOM753IHTNDSZVCNKL7GII"), "SHA256", 7, 50), "Benjamin", "Air Canada"),
new VaultEntry(new HotpInfo(Base32.decode("5VAML3X35THCEBVRLV24CGBKOY"), "SHA512", 8, 10300), "Mason", "WWE"),
new VaultEntry(new SteamInfo(Base32.decode("JRZCL47CMXVOQMNPZR2F7J4RGI"), "SHA1", 5, 30), "Sophia", "Boeing")
);
}
@Test
public void testImportPlainText() throws IOException, DatabaseImporterException {
List<VaultEntry> entries = importPlain(GoogleAuthUriImporter.class, "plain.txt");
checkImportedEntries(entries);
}
@Test
public void testImportAegisPlain() throws IOException, DatabaseImporterException {
List<VaultEntry> entries = importPlain(AegisImporter.class, "aegis_plain.json");
checkImportedEntries(entries);
}
@Test
public void testImportAegisEncrypted() throws IOException, DatabaseImporterException {
List<VaultEntry> entries = importEncrypted(AegisImporter.class, "aegis_encrypted.json", encryptedState -> {
final char[] password = "test".toCharArray();
return ((AegisImporter.EncryptedState) encryptedState).decrypt(password);
});
checkImportedEntries(entries);
}
@Test
public void testImportWinAuth() throws IOException, DatabaseImporterException {
List<VaultEntry> entries = importPlain(WinAuthImporter.class, "plain.txt");
for (VaultEntry entry : entries) {
VaultEntry entryVector = getEntryVectorBySecret(entry.getInfo().getSecret());
entryVector.setIssuer(entryVector.getName());
entryVector.setName("WinAuth");
checkImportedEntry(entryVector, entry);
}
}
@Test
public void testImportAndOTP() throws IOException, DatabaseImporterException {
List<VaultEntry> entries = importPlain(AndOtpImporter.class, "andotp_plain.json");
checkImportedEntries(entries);
}
@Test
public void testImportAndOTPEncrypted() throws IOException, DatabaseImporterException {
List<VaultEntry> entries = importEncrypted(AndOtpImporter.class, "andotp_encrypted.bin", encryptedState -> {
final char[] password = "test".toCharArray();
return ((AndOtpImporter.EncryptedState) encryptedState).decryptNewFormat(password);
});
checkImportedEntries(entries);
}
@Test
public void testImportAndOTPEncryptedOld() throws IOException, DatabaseImporterException {
List<VaultEntry> entries = importEncrypted(AndOtpImporter.class, "andotp_encrypted_old.bin", encryptedState -> {
final char[] password = "test".toCharArray();
return ((AndOtpImporter.EncryptedState) encryptedState).decryptOldFormat(password);
});
for (VaultEntry entry : entries) {
// old versions of andOTP have a bug where the issuer/name is not parsed correctly, so account for that here
VaultEntry entryVector = getEntryVectorBySecret(entry.getInfo().getSecret());
entryVector.setName(String.format("%s:%s", entryVector.getIssuer(), entryVector.getName()));
checkImportedEntry(entryVector, entry);
}
}
@Test
public void testImportTotpAuthenticator() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importEncrypted(TotpAuthenticatorImporter.class, "totp_authenticator.bin", encryptedState -> {
final char[] password = "Testtest1".toCharArray();
return ((TotpAuthenticatorImporter.EncryptedState) encryptedState).decrypt(password);
});
checkImportedTotpAuthenticatorEntries(entries);
}
@Test
public void testImportTotpAuthenticatorInternal() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importPlain(TotpAuthenticatorImporter.class, "totp_authenticator_internal.xml", true);
checkImportedTotpAuthenticatorEntries(entries);
}
@Test
public void testImportAuthy() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importPlain(AuthyImporter.class, "authy_plain.xml");
checkImportedAuthyEntries(entries);
}
@Test
public void testImportAuthyEncrypted() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importEncrypted(AuthyImporter.class, "authy_encrypted.xml", encryptedState -> {
final char[] password = "testtest".toCharArray();
return ((AuthyImporter.EncryptedState) encryptedState).decrypt(password);
});
checkImportedAuthyEntries(entries);
}
@Test
public void testImportFreeOtp() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importPlain(FreeOtpImporter.class, "freeotp.xml");
checkImportedFreeOtpEntries(entries);
}
@Test
public void testImportFreeOtpPlus() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importPlain(FreeOtpPlusImporter.class, "freeotp_plus.json");
checkImportedFreeOtpEntries(entries);
}
@Test
public void testImportFreeOtpPlusInternal() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importPlain(FreeOtpPlusImporter.class, "freeotp_plus_internal.xml", true);
checkImportedFreeOtpEntries(entries);
}
@Test
public void testImportGoogleAuthenticator() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importPlain(GoogleAuthImporter.class, "google_authenticator.sqlite");
for (VaultEntry entry : entries) {
// Google Authenticator doesn't support different hash algorithms, periods or digits, so fix those up here
VaultEntry entryVector = getEntryVectorBySecret(entry.getInfo().getSecret());
entryVector.getInfo().setDigits(6);
if (entryVector.getInfo() instanceof TotpInfo) {
((TotpInfo) entryVector.getInfo()).setPeriod(30);
}
entryVector.getInfo().setAlgorithm("SHA1");
checkImportedEntry(entryVector, entry);
}
}
@Test
public void testImportMicrosoftAuthenticator() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importPlain(MicrosoftAuthImporter.class, "microsoft_authenticator.sqlite");
for (VaultEntry entry : entries) {
// Microsoft Authenticator doesn't support HOTP, different hash algorithms, periods or digits, so fix those up here
VaultEntry entryVector = getEntryVectorBySecret(entry.getInfo().getSecret());
entryVector.setInfo(new TotpInfo(entryVector.getInfo().getSecret()));
checkImportedEntry(entryVector, entry);
}
}
@Test
public void testImportSteam() throws IOException, DatabaseImporterException {
List<VaultEntry> entries = importPlain(SteamImporter.class, "steam.json");
for (VaultEntry entry : entries) {
VaultEntry entryVector = getEntryVectorBySecret(entry.getInfo().getSecret());
entryVector.setIssuer("Steam");
checkImportedEntry(entryVector, entry);
}
}
@Test
public void testImportAuthenticatorPlus() throws IOException, DatabaseImporterException, OtpInfoException {
List<VaultEntry> entries = importEncrypted(AuthenticatorPlusImporter.class, "authenticator_plus.zip", encryptedState -> {
final char[] password = "testtesttest".toCharArray();
return ((AuthenticatorPlusImporter.EncryptedState) encryptedState).decrypt(password);
});
checkImportedEntries(entries);
}
private List<VaultEntry> importPlain(Class<? extends DatabaseImporter> type, String resName)
throws IOException, DatabaseImporterException {
return importPlain(type, resName, false);
}
private List<VaultEntry> importPlain(Class<? extends DatabaseImporter> type, String resName, boolean isInternal)
throws IOException, DatabaseImporterException {
Context context = ApplicationProvider.getApplicationContext();
DatabaseImporter importer = DatabaseImporter.create(context, type);
try (InputStream stream = openResource(resName)) {
DatabaseImporter.State state = importer.read(stream, isInternal);
assertFalse(state.isEncrypted());
DatabaseImporter.Result result = state.convert();
return Lists.newArrayList(getEntries(result));
}
}
private List<VaultEntry> importEncrypted(Class<? extends DatabaseImporter> type, String resName, Decryptor decryptor)
throws IOException, DatabaseImporterException {
return importEncrypted(type, resName, false, decryptor);
}
private List<VaultEntry> importEncrypted(Class<? extends DatabaseImporter> type, String resName, boolean isInternal, Decryptor decryptor)
throws IOException, DatabaseImporterException {
Context context = ApplicationProvider.getApplicationContext();
DatabaseImporter importer = DatabaseImporter.create(context, type);
try (InputStream stream = openResource(resName)) {
DatabaseImporter.State state = importer.read(stream, isInternal);
assertTrue(state.isEncrypted());
DatabaseImporter.Result result = decryptor.decrypt(state).convert();
return Lists.newArrayList(getEntries(result));
}
}
private static UUIDMap<VaultEntry> getEntries(DatabaseImporter.Result result) {
for (DatabaseImporterEntryException e : result.getErrors()) {
fail(e.toString());
}
return result.getEntries();
}
private void checkImportedAuthyEntries(List<VaultEntry> entries) throws OtpInfoException {
for (VaultEntry entry : entries) {
// Authy doesn't support different hash algorithms or periods, so fix those up here
VaultEntry entryVector = getEntryVectorBySecret(entry.getInfo().getSecret());
entryVector.getInfo().setAlgorithm("SHA1");
((TotpInfo) entry.getInfo()).setPeriod(((TotpInfo) entryVector.getInfo()).getPeriod());
checkImportedEntry(entryVector, entry);
}
}
private void checkImportedTotpAuthenticatorEntries(List<VaultEntry> entries) throws OtpInfoException {
for (VaultEntry entry : entries) {
// TOTP Authenticator doesn't support different hash algorithms, periods or digits, so fix those up here
VaultEntry entryVector = getEntryVectorBySecret(entry.getInfo().getSecret());
entryVector.getInfo().setDigits(6);
((TotpInfo) entryVector.getInfo()).setPeriod(30);
entryVector.getInfo().setAlgorithm("SHA1");
entryVector.setName(entryVector.getName().toLowerCase());
checkImportedEntry(entryVector, entry);
}
}
private void checkImportedFreeOtpEntries(List<VaultEntry> entries) throws OtpInfoException {
for (VaultEntry entry : entries) {
// for some reason, FreeOTP adds -1 to the counter
VaultEntry entryVector = getEntryVectorBySecret(entry.getInfo().getSecret());
if (entryVector.getInfo() instanceof HotpInfo) {
((HotpInfo) entryVector.getInfo()).setCounter(((HotpInfo) entryVector.getInfo()).getCounter() - 1);
}
checkImportedEntry(entryVector, entry);
}
}
private void checkImportedEntries(List<VaultEntry> entries) {
for (VaultEntry entry : entries) {
checkImportedEntry(entry);
}
}
private void checkImportedEntry(VaultEntry entry) {
VaultEntry entryVector = getEntryVectorBySecret(entry.getInfo().getSecret());
checkImportedEntry(entryVector, entry);
}
private void checkImportedEntry(VaultEntry entryVector, VaultEntry entry) {
String message = String.format("Entries are not equivalent: (%s) (%s)", entryVector.toJson().toString(), entry.toJson().toString());
assertTrue(message, entryVector.equivalates(entry));
assertEquals(message, entryVector.getInfo().getOtp(), entry.getInfo().getOtp());
}
private VaultEntry getEntryVectorBySecret(byte[] secret) {
for (VaultEntry entry : _vectors) {
if (Arrays.equals(entry.getInfo().getSecret(), secret)) {
return entry;
}
}
fail(String.format("No entry found for secret: %s", Base32.encode(secret)));
return null;
}
private InputStream openResource(String name) {
return getClass().getResourceAsStream(name);
}
private interface Decryptor {
DatabaseImporter.State decrypt(DatabaseImporter.State encryptedState) throws DatabaseImporterException;
}
}

View file

@ -0,0 +1,27 @@
package com.beemdevelopment.aegis.otp;
import com.beemdevelopment.aegis.crypto.otp.HOTPTest;
import com.beemdevelopment.aegis.crypto.otp.TOTPTest;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class OtpTest {
@Test
public void testHotpInfoOtp() throws OtpInfoException {
for (int i = 0; i < HOTPTest.VECTORS.length; i++) {
HotpInfo info = new HotpInfo(HOTPTest.SECRET, "SHA1", 6, i);
assertEquals(HOTPTest.VECTORS[i], info.getOtp());
}
}
@Test
public void testTotpInfoOtp() throws OtpInfoException {
for (TOTPTest.Vector vector : TOTPTest.VECTORS) {
byte[] seed = TOTPTest.getSeed(vector.Algo);
TotpInfo info = new TotpInfo(seed, vector.Algo, 8, 30);
assertEquals(vector.OTP, info.getOtp(vector.Time));
}
}
}

View file

@ -1,22 +1,25 @@
package com.beemdevelopment.aegis;
package com.beemdevelopment.aegis.util;
import com.beemdevelopment.aegis.util.Cloner;
import com.beemdevelopment.aegis.util.UUIDMap;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
public class UUIDMapTest {
private UUIDMap<Value> _map;
@BeforeEach
@Before
public void init() {
_map = new UUIDMap<>();
}
@ -87,7 +90,7 @@ public class UUIDMapTest {
// swap the values and see if the lists are equal now
_map.swap(value1, value4);
_map.swap(value2, value3);
assertIterableEquals(values, ref);
assertArrayEquals(values.toArray(), ref.toArray());
}
private Value addNewValue() {

View file

@ -1,15 +1,11 @@
package com.beemdevelopment.aegis;
package com.beemdevelopment.aegis.vault.slots;
import com.beemdevelopment.aegis.crypto.CryptoUtils;
import com.beemdevelopment.aegis.crypto.MasterKey;
import com.beemdevelopment.aegis.crypto.SCryptParameters;
import com.beemdevelopment.aegis.vault.slots.PasswordSlot;
import com.beemdevelopment.aegis.vault.slots.RawSlot;
import com.beemdevelopment.aegis.vault.slots.SlotException;
import com.beemdevelopment.aegis.vault.slots.SlotIntegrityException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.Before;
import org.junit.Test;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
@ -20,13 +16,13 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertThrows;
public class SlotTest {
private MasterKey _masterKey;
@BeforeEach
@Before
public void init() {
_masterKey = MasterKey.generate();
}
@ -75,18 +71,26 @@ public class SlotTest {
public void testSlotIntegrity() throws
InvalidAlgorithmParameterException, NoSuchAlgorithmException,
InvalidKeyException, NoSuchPaddingException,
SlotException {
SlotException, SlotIntegrityException {
RawSlot slot = new RawSlot();
SecretKey rawKey = CryptoUtils.generateKey();
Cipher cipher = CryptoUtils.createEncryptCipher(rawKey);
slot.setKey(_masterKey, cipher);
// garble the first byte of the key
byte[] rawKeyBytes = rawKey.getEncoded();
rawKeyBytes[0] = (byte) ~rawKeyBytes[0];
rawKey = new SecretKeySpec(rawKeyBytes, "AES");
// try to decrypt with good key/ciphertext first
final Cipher decryptCipher = slot.createDecryptCipher(rawKey);
slot.getKey(decryptCipher);
Cipher decryptCipher = slot.createDecryptCipher(rawKey);
// garble the first byte of the key and try to decrypt
byte[] garbledKeyBytes = rawKey.getEncoded();
garbledKeyBytes[0] = (byte) ~garbledKeyBytes[0];
SecretKey garbledKey = new SecretKeySpec(garbledKeyBytes, "AES");
final Cipher garbledDecryptCipher = slot.createDecryptCipher(garbledKey);
assertThrows(SlotIntegrityException.class, () -> slot.getKey(garbledDecryptCipher));
// garble the first byte of the ciphertext and try to decrypt
byte[] garbledCiphertext = slot.getEncryptedMasterKey();
garbledCiphertext[0] = (byte) ~garbledCiphertext[0];
assertThrows(SlotIntegrityException.class, () -> slot.getKey(decryptCipher));
}
}