This is an automated email from the ASF dual-hosted git repository.

kturner pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/accumulo.git

commit 518c1093de504de77e237b8fdaca28067ce17174
Author: Nick Felts <31989480+pirc...@users.noreply.github.com>
AuthorDate: Fri Jan 19 19:09:50 2018 -0500

    ACCUMULO-4731 Improved handling of key load failure (#315)
    
    CachingHDFSSecretKeyEncryptionStrategy will now throw an exception of a key 
encryption key cannot be loaded from file.
    The key encryption key file size is checked to verify the key length is 
correct.
    Sample key encryption key files were added for a unit test.
---
 .../CachingHDFSSecretKeyEncryptionStrategy.java    | 29 +++++++--
 .../crypto/SecretKeyEncryptionStrategy.java        |  4 +-
 .../accumulo/core/security/crypto/CryptoTest.java  | 73 +++++++++++++++++++++-
 3 files changed, 99 insertions(+), 7 deletions(-)

diff --git 
a/core/src/main/java/org/apache/accumulo/core/security/crypto/CachingHDFSSecretKeyEncryptionStrategy.java
 
b/core/src/main/java/org/apache/accumulo/core/security/crypto/CachingHDFSSecretKeyEncryptionStrategy.java
index 1fa659a..58f1010 100644
--- 
a/core/src/main/java/org/apache/accumulo/core/security/crypto/CachingHDFSSecretKeyEncryptionStrategy.java
+++ 
b/core/src/main/java/org/apache/accumulo/core/security/crypto/CachingHDFSSecretKeyEncryptionStrategy.java
@@ -18,6 +18,7 @@ package org.apache.accumulo.core.security.crypto;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
+import java.io.EOFException;
 import java.io.IOException;
 import java.security.InvalidKeyException;
 import java.security.Key;
@@ -45,13 +46,13 @@ public class CachingHDFSSecretKeyEncryptionStrategy 
implements SecretKeyEncrypti
   private SecretKeyCache secretKeyCache = new SecretKeyCache();
 
   @Override
-  public CryptoModuleParameters encryptSecretKey(CryptoModuleParameters 
context) {
+  public CryptoModuleParameters encryptSecretKey(CryptoModuleParameters 
context) throws IOException {
     try {
       secretKeyCache.ensureSecretKeyCacheInitialized(context);
       doKeyEncryptionOperation(Cipher.WRAP_MODE, context);
     } catch (IOException e) {
       log.error("{}", e.getMessage(), e);
-      throw new RuntimeException(e);
+      throw new IOException(e);
     }
     return context;
   }
@@ -128,11 +129,14 @@ public class CachingHDFSSecretKeyEncryptionStrategy 
implements SecretKeyEncrypti
         pathToKeyName = 
Property.CRYPTO_DEFAULT_KEY_STRATEGY_KEY_LOCATION.getDefaultValue();
       }
 
-      // TODO ACCUMULO-2530 Ensure volumes a properly supported
+      // TODO ACCUMULO-2530 Ensure volumes are properly supported
       Path pathToKey = new Path(pathToKeyName);
       FileSystem fs = FileSystem.get(CachedConfiguration.getInstance());
 
       DataInputStream in = null;
+      boolean invalidFile = false;
+      int keyEncryptionKeyLength = 0;
+
       try {
         if (!fs.exists(pathToKey)) {
           initializeKeyEncryptionKey(fs, pathToKey, context);
@@ -140,14 +144,29 @@ public class CachingHDFSSecretKeyEncryptionStrategy 
implements SecretKeyEncrypti
 
         in = fs.open(pathToKey);
 
-        int keyEncryptionKeyLength = in.readInt();
+        keyEncryptionKeyLength = in.readInt();
+        // If the file length does not correctly relate to the expected key 
size, there is an inconsistency and
+        // we have no way of knowing the correct key length.
+        // The keyEncryptionKeyLength+4 accounts for the integer read from the 
file.
+        if (fs.getFileStatus(pathToKey).getLen() != keyEncryptionKeyLength + 
4) {
+          invalidFile = true;
+       // Passing this exception forward so we can provide the more useful 
error message
+          throw new IOException();
+        }
         keyEncryptionKey = new byte[keyEncryptionKeyLength];
         in.readFully(keyEncryptionKey);
 
         initialized = true;
 
+      } catch (EOFException e) {
+        throw new IOException("Could not initialize key encryption cache, 
malformed key encryption key file", e);
       } catch (IOException e) {
-        log.error("Could not initialize key encryption cache", e);
+        if (invalidFile) {
+          throw new IOException("Could not initialize key encryption cache, 
malformed key encryption key file. Expected key of lengh " + 
keyEncryptionKeyLength
+              + " but file contained " + (fs.getFileStatus(pathToKey).getLen() 
- 4) + "bytes for key encryption key.");
+        } else {
+          throw new IOException("Could not initialize key encryption cache, 
unable to access or find key encryption key file", e);
+        }
       } finally {
         IOUtils.closeQuietly(in);
       }
diff --git 
a/core/src/main/java/org/apache/accumulo/core/security/crypto/SecretKeyEncryptionStrategy.java
 
b/core/src/main/java/org/apache/accumulo/core/security/crypto/SecretKeyEncryptionStrategy.java
index 7d3c333..8dfdee1 100644
--- 
a/core/src/main/java/org/apache/accumulo/core/security/crypto/SecretKeyEncryptionStrategy.java
+++ 
b/core/src/main/java/org/apache/accumulo/core/security/crypto/SecretKeyEncryptionStrategy.java
@@ -16,12 +16,14 @@
  */
 package org.apache.accumulo.core.security.crypto;
 
+import java.io.IOException;
+
 /**
  *
  */
 public interface SecretKeyEncryptionStrategy {
 
-  CryptoModuleParameters encryptSecretKey(CryptoModuleParameters params);
+  CryptoModuleParameters encryptSecretKey(CryptoModuleParameters params) 
throws IOException;
 
   CryptoModuleParameters decryptSecretKey(CryptoModuleParameters params);
 
diff --git 
a/core/src/test/java/org/apache/accumulo/core/security/crypto/CryptoTest.java 
b/core/src/test/java/org/apache/accumulo/core/security/crypto/CryptoTest.java
index a403f69..d4bf4ae 100644
--- 
a/core/src/test/java/org/apache/accumulo/core/security/crypto/CryptoTest.java
+++ 
b/core/src/test/java/org/apache/accumulo/core/security/crypto/CryptoTest.java
@@ -28,8 +28,11 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.file.Files;
 import java.security.InvalidKeyException;
 import java.security.Key;
 import java.security.NoSuchAlgorithmException;
@@ -37,6 +40,7 @@ import java.security.NoSuchProviderException;
 import java.security.SecureRandom;
 import java.util.Arrays;
 import java.util.Map.Entry;
+import java.util.Random;
 
 import javax.crypto.AEADBadTagException;
 import javax.crypto.BadPaddingException;
@@ -50,6 +54,7 @@ import org.apache.accumulo.core.conf.ConfigurationCopy;
 import org.apache.accumulo.core.conf.DefaultConfiguration;
 import org.apache.accumulo.core.conf.Property;
 import org.apache.hadoop.conf.Configuration;
+import org.junit.AfterClass;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -63,6 +68,11 @@ public class CryptoTest {
   public static final String CRYPTO_ON_CONF = "crypto-on-accumulo-site.xml";
   public static final String CRYPTO_OFF_CONF = "crypto-off-accumulo-site.xml";
   public static final String CRYPTO_ON_KEK_OFF_CONF = 
"crypto-on-no-key-encryption-accumulo-site.xml";
+  
+  //Used for kek file testing
+  private static File kekWorks;
+  private static File kekTooLong;
+  private static File kekTooShort;
 
   @Rule
   public ExpectedException exception = ExpectedException.none();
@@ -378,6 +388,68 @@ public class CryptoTest {
   }
 
   @Test
+  public void testKeyEncryptionKeyCatchCorrectlyUsesValidKEKFile() throws 
IOException {
+    kekWorks = createKekFile("kekWorks.kek", 16);
+    testKekFile(kekWorks);
+  }
+
+  @Test
+  public void testKeyEncryptionKeyCacheCorrectlyFailsWithInvalidLongKEKFile() 
throws IOException {
+    kekTooLong = createKekFile("kekTooLong.kek", 8);
+    exception.expect(IOException.class);
+    testKekFile(kekTooLong);
+  }
+
+  @Test
+  public void testKeyEncryptionKeyCacheCorrectlyFailsWithInvalidShortKEKFile() 
throws IOException {
+    kekTooShort = createKekFile("kekTooShort.kek", 32);
+    exception.expect(IOException.class);
+    testKekFile(kekTooShort);
+  }
+
+  @AfterClass
+  public static void removeAllTestKekFiles() throws IOException {
+       Files.deleteIfExists(kekWorks.toPath());
+    Files.deleteIfExists(kekTooShort.toPath());
+    Files.deleteIfExists(kekTooLong.toPath());
+  }
+
+  // Used to check reading of KEK files
+  @SuppressWarnings("deprecation")
+  private void testKekFile(File testFile) throws IOException {
+       assertTrue(testFile.exists());
+       assertFalse(testFile.isDirectory());
+               
+    AccumuloConfiguration conf = setAndGetAccumuloConfig(CRYPTO_ON_CONF);
+    CryptoModuleParameters params = 
CryptoModuleFactory.createParamsObjectFromAccumuloConfiguration(conf);
+    // TODO ACCUMULO-2530 this will need to be fixed when 
CachingHDFSSecretKeyEncryptionStrategy is fixed
+    params.getAllOptions().put(Property.INSTANCE_DFS_DIR.getKey(), 
testFile.getParent());
+    byte[] ptk = new byte[16];
+    params.setPlaintextKey(ptk);
+    CachingHDFSSecretKeyEncryptionStrategy skc = new 
CachingHDFSSecretKeyEncryptionStrategy();
+    
params.getAllOptions().put(Property.CRYPTO_DEFAULT_KEY_STRATEGY_KEY_LOCATION.getKey(),
 testFile.getName());
+    skc.encryptSecretKey(params);
+  }
+
+  private File createKekFile(String filename, Integer size) throws IOException 
{
+    File dir = new File(System.getProperty("user.dir") + "/target/cryptoTest");
+    boolean unused = dir.mkdirs(); // if the directories don't already exist, 
it'll return 1. If they do, 0. Both cases can be fine.
+
+    File testFile = File.createTempFile(filename, ".kek", dir);
+    DataOutputStream os = new DataOutputStream(new FileOutputStream(testFile));
+    Integer kl = 16;
+    byte[] key = new byte[kl];
+    Random rand = new Random();
+    rand.nextBytes(key);
+    os.writeInt(size);
+    os.write(key);
+    os.flush();
+    os.close();
+    
+    return testFile;
+    
+  }
+
   public void AESGCM_Encryption_Test_Correct_Encryption_And_Decryption() 
throws IOException, AEADBadTagException {
     AccumuloConfiguration conf = setAndGetAccumuloConfig(CRYPTO_ON_CONF);
     byte[] encryptedBytes = testEncryption(conf, new byte[] {0, 1, 2, 3, 4, 5, 
6, 7, 8, 9, 10, 20});
@@ -567,5 +639,4 @@ public class CryptoTest {
       return 0;
     }
   }
-
 }

-- 
To stop receiving notification emails like this one, please contact
"commits@accumulo.apache.org" <commits@accumulo.apache.org>.

Reply via email to