1 /** 2 * @license 3 * Copyright 2016 Google Inc. All rights reserved. 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.security.wycheproof; 18 19 import com.google.security.wycheproof.WycheproofRunner.ProviderType; 20 import com.google.security.wycheproof.WycheproofRunner.SlowTest; 21 import java.math.BigInteger; 22 import java.security.GeneralSecurityException; 23 import java.security.KeyPair; 24 import java.security.KeyPairGenerator; 25 import java.security.NoSuchAlgorithmException; 26 import java.security.PrivateKey; 27 import java.security.PublicKey; 28 import java.util.Arrays; 29 import javax.crypto.Cipher; 30 import javax.crypto.spec.DHParameterSpec; 31 import junit.framework.TestCase; 32 33 /** 34 * Testing DHIES. 35 * 36 * @author bleichen@google.com (Daniel Bleichenbacher) 37 */ 38 // TODO(bleichen): 39 // - maybe again CipherInputStream, CipherOutputStream, 40 // - byteBuffer. 41 // - Exception handling 42 // - Is DHIES using the key derivation function for the key stream? 43 // - BouncyCastle knows an algorithm IES. Is this the same as DHIES? 44 // - Bouncy fixed a padding oracle bug in version 1.56 (CVE-2016-1000345) 45 // So far we have no test for this bug mainly because this cannot be tested 46 // through the JCA interface. BC does not register and algorithm such as 47 // Cipher.DHIESWITHAES-CBC. 48 // - So far only BouncyCastles is tesed because this is the only provider 49 // we use that implements DHIES. 50 public class DhiesTest extends TestCase { 51 52 // TODO(bleichen): This is the same as DhTest.java 53 // We could move this into some TestUtil. ike2048()54 public DHParameterSpec ike2048() { 55 final BigInteger p = 56 new BigInteger( 57 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" 58 + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" 59 + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" 60 + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" 61 + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" 62 + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" 63 + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" 64 + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" 65 + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" 66 + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" 67 + "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 68 16); 69 final BigInteger g = new BigInteger("2"); 70 return new DHParameterSpec(p, g); 71 } 72 73 /** 74 * WARNING: This test uses weak crypto (i.e. DHIESWithAES), if supported. Checks that key 75 * agreement using DHIES works in the sense that it can decrypt what it encrypts. Unfortunately it 76 * seems that there is no secure mode using AES. 77 */ 78 @SuppressWarnings("InsecureCryptoUsage") testDhiesBasic()79 public void testDhiesBasic() throws Exception { 80 DHParameterSpec params = ike2048(); 81 KeyPairGenerator kf = KeyPairGenerator.getInstance("DH"); 82 kf.initialize(params); 83 KeyPair keyPair = kf.generateKeyPair(); 84 PrivateKey priv = keyPair.getPrivate(); 85 PublicKey pub = keyPair.getPublic(); 86 byte[] message = "Hello".getBytes("UTF-8"); 87 Cipher dhies; 88 try { 89 dhies = Cipher.getInstance("DHIESwithAES"); 90 } catch (NoSuchAlgorithmException ex) { 91 // The algorithm isn't supported - even better! 92 return; 93 } 94 dhies.init(Cipher.ENCRYPT_MODE, pub); 95 byte[] ciphertext = dhies.doFinal(message); 96 System.out.println("testDhiesBasic:" + TestUtil.bytesToHex(ciphertext)); 97 dhies.init(Cipher.DECRYPT_MODE, priv); 98 byte[] decrypted = dhies.doFinal(ciphertext); 99 assertEquals(TestUtil.bytesToHex(message), TestUtil.bytesToHex(decrypted)); 100 } 101 102 /** 103 * WARNING: This test uses weak crypto (i.e. DHIESWithAES). DHIES should be secure against chosen 104 * ciphertexts. Checks that a modification of the ciphertext is dectected. 105 */ 106 @SlowTest(providers = {ProviderType.BOUNCY_CASTLE, ProviderType.SPONGY_CASTLE}) 107 @SuppressWarnings("InsecureCryptoUsage") testDhiesCorrupt()108 public void testDhiesCorrupt() throws Exception { 109 KeyPairGenerator kf = KeyPairGenerator.getInstance("DH"); 110 kf.initialize(ike2048()); 111 KeyPair keyPair = kf.generateKeyPair(); 112 PrivateKey priv = keyPair.getPrivate(); 113 PublicKey pub = keyPair.getPublic(); 114 byte[] message = new byte[32]; 115 Cipher dhies; 116 try { 117 dhies = Cipher.getInstance("DHIESwithAES"); 118 } catch (NoSuchAlgorithmException ex) { 119 // The algorithm isn't supported - even better! 120 return; 121 } 122 dhies.init(Cipher.ENCRYPT_MODE, pub); 123 byte[] ciphertext = dhies.doFinal(message); 124 for (int i = 0; i < ciphertext.length; i++) { 125 byte[] corrupt = Arrays.copyOf(ciphertext, ciphertext.length); 126 corrupt[i] ^= (byte) 1; 127 try { 128 dhies.init(Cipher.DECRYPT_MODE, priv); 129 dhies.doFinal(corrupt); 130 fail("Corrupt ciphertext accepted:" + i); 131 } catch (GeneralSecurityException ex) { 132 // This is expected 133 } 134 } 135 } 136 137 /** 138 * Tries to detect if an algorithm is using ECB. Unfortunately, many JCE algorithms use ECB if no 139 * encryption mode is specified. 140 */ 141 @SuppressWarnings("InsecureCryptoUsage") testNotEcb(String algorithm)142 public void testNotEcb(String algorithm) throws Exception { 143 Cipher dhies; 144 try { 145 dhies = Cipher.getInstance(algorithm); 146 } catch (NoSuchAlgorithmException ex) { 147 // This test is called with short algorithm names such as just "DHIES". 148 // Requiring full names is typically a good practice. Hence it is OK 149 // to not assigning default algorithms. 150 System.out.println("No implementation for:" + algorithm); 151 return; 152 } 153 KeyPairGenerator kf = KeyPairGenerator.getInstance("DH"); 154 kf.initialize(ike2048()); 155 KeyPair keyPair = kf.generateKeyPair(); 156 PublicKey pub = keyPair.getPublic(); 157 byte[] message = new byte[512]; 158 dhies.init(Cipher.ENCRYPT_MODE, pub); 159 byte[] ciphertext = dhies.doFinal(message); 160 for (int i = 0; i + 32 <= ciphertext.length; i++) { 161 String block1 = TestUtil.bytesToHex(Arrays.copyOfRange(ciphertext, i, i + 16)); 162 String block2 = TestUtil.bytesToHex(Arrays.copyOfRange(ciphertext, i + 16, i + 32)); 163 assertTrue( 164 "Ciphertext repeats at " + i + ":" + TestUtil.bytesToHex(ciphertext), 165 !block1.equals(block2)); 166 } 167 } 168 testSemanticSecurityDhies()169 public void testSemanticSecurityDhies() throws Exception { 170 testNotEcb("DHIES"); 171 } 172 } 173