1 package civitas.crypto.tests;
2
3 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
4 import static org.junit.jupiter.api.Assertions.assertEquals;
5 import static org.junit.jupiter.api.Assertions.assertFalse;
6 import static org.junit.jupiter.api.Assertions.assertThrows;
7 import static org.junit.jupiter.api.Assertions.assertTrue;
8
9 import java.security.KeyPair;
10 import java.security.PrivateKey;
11 import java.security.PublicKey;
12 import java.security.SecureRandom;
13 import java.security.spec.InvalidKeySpecException;
14 import java.security.spec.PKCS8EncodedKeySpec;
15 import java.security.spec.X509EncodedKeySpec;
16 import java.util.Random;
17 import java.util.stream.IntStream;
18
19 import javax.crypto.Cipher;
20 import javax.crypto.SecretKey;
21 import javax.crypto.spec.SecretKeySpec;
22
23 import org.junit.jupiter.api.DisplayName;
24 import org.junit.jupiter.api.Test;
25 import org.mockito.InjectMocks;
26
27 import civitas.crypto.Constants;
28 import civitas.crypto.CryptoBase;
29 import civitas.crypto.CryptoError;
30 import civitas.util.CivitasBigInteger;
31 import civitas.util.tests.BasicValuesTestData;
32 import io.github.magwas.konveyor.testing.TestBase;
33
34 class CryptoBaseTest extends TestBase implements Constants, BasicValuesTestData {
35
36 @InjectMocks
37 CryptoBase cryptoBase;
38
39 @Test
40 @DisplayName("we can generate a shared key, create an identical key "
41 + "using the shared key factory, and when we encrypt some bytes "
42 + "with the first key and decrypt it with the second one, we get our bytes back")
43 void test() throws InvalidKeySpecException {
44
45 SecretKey sharedKey =
46 cryptoBase.getSharedKeyGenerator(SHARED_KEY_LENGTH).generateKey();
47 SecretKeySpec skeySpec = new SecretKeySpec(sharedKey.getEncoded(), SHARED_KEY_ALG);
48
49 SecretKey sharedKey2 = cryptoBase.sharedKeyFactory.generateSecret(skeySpec);
50
51 byte[] encrypted =
52 cryptoBase.doCrypto(SHARED_KEY_ALG, SHARED_KEY_PROVIDER, sharedKey, Cipher.ENCRYPT_MODE, BYTES);
53
54 byte[] decrypted =
55 cryptoBase.doCrypto(SHARED_KEY_ALG, SHARED_KEY_PROVIDER, sharedKey2, Cipher.DECRYPT_MODE, encrypted);
56
57 assertArrayEquals(BYTES, decrypted);
58 }
59
60 @Test
61 @DisplayName("getSharedKeyGenerator caches its result")
62 void test4() {
63 assertEquals(
64 cryptoBase.getSharedKeyGenerator(SHARED_KEY_LENGTH),
65 cryptoBase.getSharedKeyGenerator(SHARED_KEY_LENGTH));
66 }
67
68 @Test
69 @DisplayName("getPublicKeyGenerator caches its result")
70 void test5() {
71 assertEquals(
72 cryptoBase.getPublicKeyGenerator(PUBLIC_KEY_LENGTH),
73 cryptoBase.getPublicKeyGenerator(PUBLIC_KEY_LENGTH));
74 }
75
76 @Test
77 @DisplayName("getPublicKeyGenerator throws CryptoError for bad keylength")
78 void test7() {
79 assertThrows(CryptoError.class, () -> cryptoBase.getPublicKeyGenerator(-1));
80 }
81
82 @Test
83 @DisplayName("doCrypto throws CryptoError if anything goes wrong")
84 void test6() {
85 SecretKeySpec skeySpec = new SecretKeySpec(SOMESTRING.getBytes(), SHARED_KEY_ALG);
86 assertThrows(
87 CryptoError.class,
88 () -> cryptoBase.doCrypto(PUBLIC_KEY_ALG, PUBLIC_KEY_PROVIDER, skeySpec, Cipher.ENCRYPT_MODE, BYTES));
89 }
90
91 @Test
92 @DisplayName("we can generate a public key pair, store them as byte array, "
93 + "recover them from the byte arrays, encrypt some bytes and "
94 + "decrypt the ciphertext correctly")
95 void testPublic() throws InvalidKeySpecException {
96 KeyPair keypair = cryptoBase.getPublicKeyGenerator(PUBLIC_KEY_LENGTH).generateKeyPair();
97 PublicKey pubKey = keypair.getPublic();
98 PrivateKey privKey = keypair.getPrivate();
99 byte[] pubkeyBytes = pubKey.getEncoded();
100 byte[] privkeyBytes = privKey.getEncoded();
101 PublicKey pubKey2 = cryptoBase.publicKeyFactory.generatePublic(new X509EncodedKeySpec(pubkeyBytes));
102 PrivateKey privKey2 = cryptoBase.publicKeyFactory.generatePrivate(new PKCS8EncodedKeySpec(privkeyBytes));
103
104 byte[] encrypted =
105 cryptoBase.doCrypto(PUBLIC_KEY_ALG, PUBLIC_KEY_PROVIDER, pubKey2, Cipher.ENCRYPT_MODE, BYTES);
106
107 byte[] decrypted =
108 cryptoBase.doCrypto(PUBLIC_KEY_ALG, PUBLIC_KEY_PROVIDER, privKey2, Cipher.DECRYPT_MODE, encrypted);
109
110 assertArrayEquals(BYTES, decrypted);
111 }
112
113 @Test
114 @DisplayName("we can obtain a probable prime with a given bit length,"
115 + "and it probably is indeed a prime with the given bit length")
116 void test1() {
117 IntStream.range(0, RANDOM_RUNS).forEach(n -> {
118 CivitasBigInteger prime = cryptoBase.obtainProbablePrime(BITLENGTH);
119 assertEquals(BITLENGTH, prime.bitLength());
120 assertTrue(prime.isProbablePrime(CERTAINTY));
121 });
122 }
123
124 @Test
125 @DisplayName("we can generate a random element for Q which is less than Q and " + "their bit lengths are similar")
126 void testGenerate() {
127 IntStream.range(0, RANDOM_RUNS).forEach(n -> {
128 CivitasBigInteger element = cryptoBase.generateRandomElement(BIGINT_A);
129 assertTrue(element.compareTo(BIGINT_A) <= 0);
130 assertTrue(BIGINT_A.bitLength() >= element.bitLength());
131 });
132 }
133
134 @Test
135 @DisplayName("we can get a random generator which is up to date and secure")
136 void test2() {
137 Random generator = cryptoBase.getRandomGenerator();
138 assertFalse(generator.isDeprecated());
139 assertEquals(SecureRandom.class, generator.getClass());
140 }
141
142 @Test
143 @DisplayName("we can get byte arrays of random numbers" + "(tested with the monoBit test from NIST 800-22 at 1%)")
144 void test3() {
145 byte[] bytes = new byte[SHARED_KEY_LENGTH];
146 cryptoBase.nextBytes(bytes);
147 monoBitTest(bytes);
148 }
149
150 public void monoBitTest(final byte[] bytes) {
151 int sum = 0;
152 for (byte element : bytes) {
153 int current = element;
154 for (int j = 0; j < 8; j++) {
155 int isOne = current & 1;
156 current = current >>> 1;
157 if (1 == isOne) {
158 sum++;
159 } else {
160 sum--;
161 }
162 }
163 }
164 double s = Math.abs(sum) / Math.sqrt(bytes.length * 8d) / Math.sqrt(2);
165 assertTrue(s < 1.82, "S=" + s);
166 }
167 }