/** * @license * Copyright 2016 Google Inc. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.security.wycheproof; import com.google.security.wycheproof.WycheproofRunner.ProviderType; import com.google.security.wycheproof.WycheproofRunner.SlowTest; import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.util.Arrays; import javax.crypto.Cipher; import javax.crypto.spec.DHParameterSpec; import junit.framework.TestCase; /** * Testing DHIES. * * @author bleichen@google.com (Daniel Bleichenbacher) */ // Tested providers: // BC (not recommended) // // TODO(bleichen): // - maybe again CipherInputStream, CipherOutputStream, // - byteBuffer. // - Exception handling // - Is DHIES using the key derivation function for the key stream? // - BouncyCastle knows an algorithm IES. Is this the same as DHIES? public class DhiesTest extends TestCase { // TODO(bleichen): This is the same as DhTest.java // We could move this into some TestUtil. public DHParameterSpec ike2048() { final BigInteger p = new BigInteger( "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16); final BigInteger g = new BigInteger("2"); return new DHParameterSpec(p, g); } /** * WARNING: This test uses weak crypto (i.e. DHIESWithAES). Checks that key agreement using DHIES * works in the sense that it can decrypt what it encrypts. Unfortunately it seems that there is * no secure mode using AES. */ @SuppressWarnings("InsecureCipherMode") public void testDhiesBasic() throws Exception { DHParameterSpec params = ike2048(); KeyPairGenerator kf = KeyPairGenerator.getInstance("DH"); kf.initialize(params); KeyPair keyPair = kf.generateKeyPair(); PrivateKey priv = keyPair.getPrivate(); PublicKey pub = keyPair.getPublic(); byte[] message = "Hello".getBytes("UTF-8"); Cipher dhies = Cipher.getInstance("DHIESwithAES"); dhies.init(Cipher.ENCRYPT_MODE, pub); byte[] ciphertext = dhies.doFinal(message); System.out.println("testDhiesBasic:" + TestUtil.bytesToHex(ciphertext)); dhies.init(Cipher.DECRYPT_MODE, priv); byte[] decrypted = dhies.doFinal(ciphertext); assertEquals(TestUtil.bytesToHex(message), TestUtil.bytesToHex(decrypted)); } /** * WARNING: This test uses weak crypto (i.e. DHIESWithAES). DHIES should be secure against chosen * ciphertexts. Checks that a modification of the ciphertext is dectected. */ @SlowTest(providers = {ProviderType.BOUNCY_CASTLE, ProviderType.SPONGY_CASTLE}) @SuppressWarnings("InsecureCipherMode") public void testDhiesCorrupt() throws Exception { KeyPairGenerator kf = KeyPairGenerator.getInstance("DH"); kf.initialize(ike2048()); KeyPair keyPair = kf.generateKeyPair(); PrivateKey priv = keyPair.getPrivate(); PublicKey pub = keyPair.getPublic(); byte[] message = new byte[32]; Cipher dhies = Cipher.getInstance("DHIESwithAES"); dhies.init(Cipher.ENCRYPT_MODE, pub); byte[] ciphertext = dhies.doFinal(message); for (int i = 0; i < ciphertext.length; i++) { byte[] corrupt = Arrays.copyOf(ciphertext, ciphertext.length); corrupt[i] ^= (byte) 1; try { dhies.init(Cipher.DECRYPT_MODE, priv); dhies.doFinal(corrupt); fail("Corrupt ciphertext accepted:" + i); } catch (GeneralSecurityException ex) { // This is expected } } } /** * Tries to detect if an algorithm is using ECB. Unfortunately, many JCE algorithms use ECB if no * encryption mode is specified. */ @SuppressWarnings("InsecureCipherMode") public void testNotEcb(String algorithm) throws Exception { Cipher dhies; try { dhies = Cipher.getInstance(algorithm); } catch (NoSuchAlgorithmException ex) { // This test is called with short algorithm names such as just "DHIES". // Requiring full names is typically a good practice. Hence it is OK // to not assigning default algorithms. System.out.println("No implementation for:" + algorithm); return; } KeyPairGenerator kf = KeyPairGenerator.getInstance("DH"); kf.initialize(ike2048()); KeyPair keyPair = kf.generateKeyPair(); PublicKey pub = keyPair.getPublic(); byte[] message = new byte[512]; dhies.init(Cipher.ENCRYPT_MODE, pub); byte[] ciphertext = dhies.doFinal(message); for (int i = 0; i + 32 <= ciphertext.length; i++) { String block1 = TestUtil.bytesToHex(Arrays.copyOfRange(ciphertext, i, i + 16)); String block2 = TestUtil.bytesToHex(Arrays.copyOfRange(ciphertext, i + 16, i + 32)); assertTrue( "Ciphertext repeats at " + i + ":" + TestUtil.bytesToHex(ciphertext), !block1.equals(block2)); } } public void testSemanticSecurityDhies() throws Exception { testNotEcb("DHIES"); } }