View Javadoc
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 }