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