Repository: orc
Updated Branches:
  refs/heads/master d5a6c49bf -> 10c0a858b


ORC-252. Add support for Key Management Servers (kms) to HadoopShims.

Fixes #176

Signed-off-by: Owen O'Malley <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/orc/repo
Commit: http://git-wip-us.apache.org/repos/asf/orc/commit/10c0a858
Tree: http://git-wip-us.apache.org/repos/asf/orc/tree/10c0a858
Diff: http://git-wip-us.apache.org/repos/asf/orc/diff/10c0a858

Branch: refs/heads/master
Commit: 10c0a858b3e0bb8845b898cd78e41626f8365752
Parents: d5a6c49
Author: Owen O'Malley <[email protected]>
Authored: Fri Oct 13 08:27:57 2017 -0700
Committer: Owen O'Malley <[email protected]>
Committed: Wed Oct 18 15:21:31 2017 -0700

----------------------------------------------------------------------
 java/shims/pom.xml                              |  10 +-
 .../org/apache/orc/EncryptionAlgorithm.java     |  96 ++++++++++
 .../java/org/apache/orc/impl/HadoopShims.java   | 102 ++++++++++-
 .../org/apache/orc/impl/HadoopShimsCurrent.java |  13 +-
 .../org/apache/orc/impl/HadoopShimsPre2_3.java  |   7 +
 .../org/apache/orc/impl/HadoopShimsPre2_6.java  | 124 +++++++++++++
 .../org/apache/orc/impl/HadoopShimsPre2_7.java  | 179 +++++++++++++------
 .../apache/orc/impl/TestHadoopShimsPre2_7.java  |  60 +++++++
 java/shims/src/test/resources/log4j.properties  |  20 +++
 java/tools/src/findbugs/exclude.xml             |   2 +-
 .../src/java/org/apache/orc/tools/Driver.java   |   3 +
 .../src/java/org/apache/orc/tools/KeyTool.java  | 118 ++++++++++++
 12 files changed, 668 insertions(+), 66 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/orc/blob/10c0a858/java/shims/pom.xml
----------------------------------------------------------------------
diff --git a/java/shims/pom.xml b/java/shims/pom.xml
index a4b58c2..019790d 100644
--- a/java/shims/pom.xml
+++ b/java/shims/pom.xml
@@ -46,6 +46,13 @@
       <artifactId>hadoop-hdfs</artifactId>
       <version>${hadoop.version}</version>
     </dependency>
+
+    <!-- test inter-project -->
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
@@ -65,9 +72,6 @@
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-javadoc-plugin</artifactId>
         <configuration>
-          <sourceFileExcludes>
-            <exclude>**/OrcProto.java</exclude>
-          </sourceFileExcludes>
           <destDir>${project.artifactId}</destDir>
         </configuration>
       </plugin>

http://git-wip-us.apache.org/repos/asf/orc/blob/10c0a858/java/shims/src/java/org/apache/orc/EncryptionAlgorithm.java
----------------------------------------------------------------------
diff --git a/java/shims/src/java/org/apache/orc/EncryptionAlgorithm.java 
b/java/shims/src/java/org/apache/orc/EncryptionAlgorithm.java
new file mode 100644
index 0000000..6dbf1f2
--- /dev/null
+++ b/java/shims/src/java/org/apache/orc/EncryptionAlgorithm.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.orc;
+
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * The encryption algorithms supported by ORC.
+ *
+ * This class can't reference any of the newer Hadoop classes.
+ */
+public enum EncryptionAlgorithm {
+  AES_128("AES", "CTR/NoPadding", 16, 1),
+  AES_256("AES", "CTR/NoPadding", 32, 2);
+
+  private final String algorithm;
+  private final String mode;
+  private final int keyLength;
+  private final int serialization;
+  private final byte[] zero;
+
+  EncryptionAlgorithm(String algorithm, String mode, int keyLength,
+                      int serialization) {
+    this.algorithm = algorithm;
+    this.mode = mode;
+    this.keyLength = keyLength;
+    this.serialization = serialization;
+    zero = new byte[keyLength];
+  }
+
+  public String getAlgorithm() {
+    return algorithm;
+  }
+
+  public int getIvLength() {
+    return 16;
+  }
+
+  public Cipher createCipher() {
+    try {
+      return Cipher.getInstance(algorithm + "/" + mode);
+    } catch (NoSuchAlgorithmException e) {
+      throw new IllegalArgumentException("Bad algorithm " + algorithm);
+    } catch (NoSuchPaddingException e) {
+      throw new IllegalArgumentException("Bad padding " + algorithm);
+    }
+  }
+
+  public int keyLength() {
+    return keyLength;
+  }
+
+  public byte[] getZeroKey() {
+    return zero;
+  }
+
+  /**
+   * Get the serialization code for this enumeration.
+   * @return the serialization value
+   */
+  public int getSerialization() {
+    return serialization;
+  }
+
+  /**
+   * Get the serialization code for this enumeration.
+   * @return the serialization value
+   */
+  public static EncryptionAlgorithm fromSerialization(int serialization) {
+    for(EncryptionAlgorithm algorithm: values()) {
+      if (algorithm.serialization == serialization) {
+        return algorithm;
+      }
+    }
+    throw new IllegalArgumentException("Unknown code in encryption algorithm " 
+
+        serialization);
+  }
+}

http://git-wip-us.apache.org/repos/asf/orc/blob/10c0a858/java/shims/src/java/org/apache/orc/impl/HadoopShims.java
----------------------------------------------------------------------
diff --git a/java/shims/src/java/org/apache/orc/impl/HadoopShims.java 
b/java/shims/src/java/org/apache/orc/impl/HadoopShims.java
index 3ba3b43..26c0ac9 100644
--- a/java/shims/src/java/org/apache/orc/impl/HadoopShims.java
+++ b/java/shims/src/java/org/apache/orc/impl/HadoopShims.java
@@ -18,12 +18,16 @@
 
 package org.apache.orc.impl;
 
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.orc.EncryptionAlgorithm;
 
 import java.io.Closeable;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
+import java.security.Key;
+import java.util.List;
 
 public interface HadoopShims {
 
@@ -75,25 +79,32 @@ public interface HadoopShims {
 
   /**
    * Provides an HDFS ZeroCopyReader shim.
-   * @param in FSDataInputStream to read from (where the cached/mmap buffers 
are tied to)
+   * @param in FSDataInputStream to read from (where the cached/mmap buffers 
are
+   *          tied to)
    * @param pool ByteBufferPoolShim to allocate fallback buffers with
    *
    * @return returns null if not supported
    */
-  ZeroCopyReaderShim getZeroCopyReader(FSDataInputStream in, 
ByteBufferPoolShim pool) throws IOException;
+  ZeroCopyReaderShim getZeroCopyReader(FSDataInputStream in,
+                                       ByteBufferPoolShim pool
+                                       ) throws IOException;
 
   interface ZeroCopyReaderShim extends Closeable {
+
     /**
-     * Get a ByteBuffer from the FSDataInputStream - this can be either a 
HeapByteBuffer or an MappedByteBuffer.
-     * Also move the in stream by that amount. The data read can be small than 
maxLength.
+     * Get a ByteBuffer from the FSDataInputStream - this can be either a
+     * HeapByteBuffer or an MappedByteBuffer. Also move the in stream by that
+     * amount. The data read can be small than maxLength.
      *
      * @return ByteBuffer read from the stream,
      */
-    ByteBuffer readBuffer(int maxLength, boolean verifyChecksums) throws 
IOException;
+    ByteBuffer readBuffer(int maxLength,
+                          boolean verifyChecksums) throws IOException;
+
     /**
      * Release a ByteBuffer obtained from a read on the
-     * Also move the in stream by that amount. The data read can be small than 
maxLength.
-     *
+     * Also move the in stream by that amount. The data read can be small than
+     * maxLength.
      */
     void releaseBuffer(ByteBuffer buffer);
 
@@ -109,4 +120,81 @@ public interface HadoopShims {
    * @return the number of bytes written
    */
   long padStreamToBlock(OutputStream output, long padding) throws IOException;
+
+  /**
+   * A source of crypto keys. This is usually backed by a Ranger KMS.
+   */
+  interface KeyProvider {
+
+    /**
+     * Get the list of key names from the key provider.
+     * @return a list of key names
+     * @throws IOException
+     */
+    List<String> getKeyNames() throws IOException;
+
+    /**
+     * Get the current metadata for a given key. This is used when encrypting
+     * new data.
+     * @param keyName the name of a key
+     * @return metadata for the current version of the key
+     */
+    KeyMetadata getCurrentKeyVersion(String keyName) throws IOException;
+
+    /**
+     * Create a metadata object while reading.
+     * @param keyName the name of the key
+     * @param version the version of the key to use
+     * @param algorithm the algorithm for that version of the key
+     * @return the metadata for the key version
+     */
+    KeyMetadata getKeyVersion(String keyName, int version,
+                              EncryptionAlgorithm algorithm);
+
+    /**
+     * Create a local key for the given key version and initialization vector.
+     * Given a probabilistically unique iv, it will generate a unique key
+     * with the master key at the specified version. This allows the encryption
+     * to use this local key for the encryption and decryption without ever
+     * having access to the master key.
+     *
+     * This uses KeyProviderCryptoExtension.decryptEncryptedKey with a fixed 
key
+     * of the appropriate length.
+     *
+     * @param key the master key version
+     * @param iv the unique initialization vector
+     * @return the local key's material
+     */
+    Key getLocalKey(KeyMetadata key, byte[] iv) throws IOException;
+  }
+
+  /**
+   * Information about a crypto key.
+   */
+  interface KeyMetadata {
+    /**
+     * Get the name of the key.
+     */
+    String getKeyName();
+
+    /**
+     * Get the encryption algorithm for this key.
+     * @return the algorithm
+     */
+    EncryptionAlgorithm getAlgorithm();
+
+    /**
+     * Get the version of this key.
+     * @return the version
+     */
+    int getVersion();
+  }
+
+  /**
+   * Create a random key for encrypting.
+   * @param conf the configuration
+   * @return a key provider or null if none was provided
+   */
+  KeyProvider getKeyProvider(Configuration conf) throws IOException;
+
 }

http://git-wip-us.apache.org/repos/asf/orc/blob/10c0a858/java/shims/src/java/org/apache/orc/impl/HadoopShimsCurrent.java
----------------------------------------------------------------------
diff --git a/java/shims/src/java/org/apache/orc/impl/HadoopShimsCurrent.java 
b/java/shims/src/java/org/apache/orc/impl/HadoopShimsCurrent.java
index d9c9b6e..ca22fbf 100644
--- a/java/shims/src/java/org/apache/orc/impl/HadoopShimsCurrent.java
+++ b/java/shims/src/java/org/apache/orc/impl/HadoopShimsCurrent.java
@@ -18,6 +18,7 @@
 
 package org.apache.orc.impl;
 
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FSDataInputStream;
 import org.apache.hadoop.hdfs.client.HdfsDataOutputStream;
 
@@ -27,11 +28,16 @@ import java.util.EnumSet;
 
 /**
  * Shims for recent versions of Hadoop
+ *
+ * Adds support for:
+ * <ul>
+ *   <li>Variable length HDFS blocks</li>
+ * </ul>
  */
 public class HadoopShimsCurrent implements HadoopShims {
 
   public DirectDecompressor getDirectDecompressor(DirectCompressionType codec) 
{
-    return HadoopShimsPre2_7.getDecompressor(codec);
+    return HadoopShimsPre2_6.getDecompressor(codec);
   }
 
   @Override
@@ -53,5 +59,10 @@ public class HadoopShimsCurrent implements HadoopShims {
     }
   }
 
+  @Override
+  public KeyProvider getKeyProvider(Configuration conf) throws IOException {
+    return new HadoopShimsPre2_7.KeyProviderImpl(conf);
+  }
+
 
 }

http://git-wip-us.apache.org/repos/asf/orc/blob/10c0a858/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_3.java
----------------------------------------------------------------------
diff --git a/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_3.java 
b/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_3.java
index 3c67691..6190eb2 100644
--- a/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_3.java
+++ b/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_3.java
@@ -18,6 +18,7 @@
 
 package org.apache.orc.impl;
 
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FSDataInputStream;
 
 import java.io.IOException;
@@ -62,4 +63,10 @@ public class HadoopShimsPre2_3 implements HadoopShims {
     return padStream(output, padding);
   }
 
+  @Override
+  public KeyProvider getKeyProvider(Configuration conf) {
+    // Not supported
+    return null;
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/orc/blob/10c0a858/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_6.java
----------------------------------------------------------------------
diff --git a/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_6.java 
b/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_6.java
new file mode 100644
index 0000000..aa312e6
--- /dev/null
+++ b/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_6.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.orc.impl;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FSDataInputStream;
+import 
org.apache.hadoop.io.compress.snappy.SnappyDecompressor.SnappyDirectDecompressor;
+import org.apache.hadoop.io.compress.zlib.ZlibDecompressor;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * Shims for versions of Hadoop less than 2.6
+ *
+ * Adds support for:
+ * <ul>
+ *   <li>Direct buffer decompression</li>
+ *   <li>Zero copy</li>
+ * </ul>
+ */
+public class HadoopShimsPre2_6 implements HadoopShims {
+
+  static class SnappyDirectDecompressWrapper implements DirectDecompressor {
+    private final SnappyDirectDecompressor root;
+
+    SnappyDirectDecompressWrapper(SnappyDirectDecompressor root) {
+      this.root = root;
+    }
+
+    public void decompress(ByteBuffer input, ByteBuffer output) throws 
IOException {
+      root.decompress(input, output);
+    }
+
+    @Override
+    public void reset() {
+      root.reset();
+    }
+
+    @Override
+    public void end() {
+      root.end();
+    }
+  }
+
+  static class ZlibDirectDecompressWrapper implements DirectDecompressor {
+    private final ZlibDecompressor.ZlibDirectDecompressor root;
+
+    ZlibDirectDecompressWrapper(ZlibDecompressor.ZlibDirectDecompressor root) {
+      this.root = root;
+    }
+
+    public void decompress(ByteBuffer input, ByteBuffer output) throws 
IOException {
+      root.decompress(input, output);
+    }
+
+    @Override
+    public void reset() {
+      root.reset();
+    }
+
+    @Override
+    public void end() {
+      root.end();
+    }
+  }
+
+  static DirectDecompressor getDecompressor( DirectCompressionType codec) {
+    switch (codec) {
+      case ZLIB:
+        return new ZlibDirectDecompressWrapper
+            (new ZlibDecompressor.ZlibDirectDecompressor());
+      case ZLIB_NOHEADER:
+        return new ZlibDirectDecompressWrapper
+            (new ZlibDecompressor.ZlibDirectDecompressor
+                (ZlibDecompressor.CompressionHeader.NO_HEADER, 0));
+      case SNAPPY:
+        return new SnappyDirectDecompressWrapper
+            (new SnappyDirectDecompressor());
+      default:
+        return null;
+    }
+  }
+
+  public DirectDecompressor getDirectDecompressor( DirectCompressionType 
codec) {
+    return getDecompressor(codec);
+ }
+
+  @Override
+  public ZeroCopyReaderShim getZeroCopyReader(FSDataInputStream in,
+                                              ByteBufferPoolShim pool
+                                              ) throws IOException {
+    return ZeroCopyShims.getZeroCopyReader(in, pool);
+  }
+
+  @Override
+  public long padStreamToBlock(OutputStream output,
+                               long padding) throws IOException {
+    return HadoopShimsPre2_3.padStream(output, padding);
+  }
+
+  @Override
+  public KeyProvider getKeyProvider(Configuration conf) {
+    // not supported
+    return null;
+  }
+}

http://git-wip-us.apache.org/repos/asf/orc/blob/10c0a858/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_7.java
----------------------------------------------------------------------
diff --git a/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_7.java 
b/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_7.java
index a210353..36cc187 100644
--- a/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_7.java
+++ b/java/shims/src/java/org/apache/orc/impl/HadoopShimsPre2_7.java
@@ -18,96 +18,167 @@
 
 package org.apache.orc.impl;
 
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension;
+import org.apache.hadoop.crypto.key.KeyProviderExtension;
+import org.apache.hadoop.crypto.key.KeyProviderFactory;
 import org.apache.hadoop.fs.FSDataInputStream;
-import org.apache.hadoop.io.compress.snappy.SnappyDecompressor;
-import 
org.apache.hadoop.io.compress.snappy.SnappyDecompressor.SnappyDirectDecompressor;
-import org.apache.hadoop.io.compress.zlib.ZlibDecompressor;
+import org.apache.orc.EncryptionAlgorithm;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import javax.crypto.spec.SecretKeySpec;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.util.List;
 
 /**
- * Shims for versions of Hadoop less than 2.7
+ * Shims for versions of Hadoop less than 2.7.
+ *
+ * Adds support for:
+ * <ul>
+ *   <li>Crypto</li>
+ * </ul>
  */
 public class HadoopShimsPre2_7 implements HadoopShims {
 
-  static class SnappyDirectDecompressWrapper implements DirectDecompressor {
-    private final SnappyDirectDecompressor root;
+  private static final Logger LOG =
+      LoggerFactory.getLogger(HadoopShimsPre2_7.class);
+
+
+  public DirectDecompressor getDirectDecompressor( DirectCompressionType 
codec) {
+    return HadoopShimsPre2_6.getDecompressor(codec);
+ }
+
+  @Override
+  public ZeroCopyReaderShim getZeroCopyReader(FSDataInputStream in,
+                                              ByteBufferPoolShim pool
+                                              ) throws IOException {
+    return ZeroCopyShims.getZeroCopyReader(in, pool);
+  }
+
+  @Override
+  public long padStreamToBlock(OutputStream output,
+                               long padding) throws IOException {
+    return HadoopShimsPre2_3.padStream(output, padding);
+  }
+
+  static String buildKeyVersionName(KeyMetadata key) {
+    return key.getKeyName() + "@" + key.getVersion();
+  }
+
+  /**
+   * Shim implementation for Hadoop's KeyProvider API that lets applications 
get
+   * access to encryption keys.
+   */
+  static class KeyProviderImpl implements KeyProvider {
+    private final org.apache.hadoop.crypto.key.KeyProvider provider;
+
+    KeyProviderImpl(Configuration conf) throws IOException {
+      List<org.apache.hadoop.crypto.key.KeyProvider> result =
+          KeyProviderFactory.getProviders(conf);
+      if (result.size() != 1) {
+        throw new IllegalArgumentException("Can't get KeyProvider for ORC" +
+            " encryption. Got " + result.size() + " results.");
+      }
+      provider = result.get(0);
+    }
 
-    SnappyDirectDecompressWrapper(SnappyDirectDecompressor root) {
-      this.root = root;
+    @Override
+    public List<String> getKeyNames() throws IOException {
+      return provider.getKeys();
     }
 
-    public void decompress(ByteBuffer input, ByteBuffer output) throws 
IOException {
-      root.decompress(input, output);
+    @Override
+    public KeyMetadata getCurrentKeyVersion(String keyName) throws IOException 
{
+      return new KeyMetadataImpl(keyName, provider.getMetadata(keyName));
     }
 
     @Override
-    public void reset() {
-      root.reset();
+    public KeyMetadata getKeyVersion(String keyName, int version,
+                                     EncryptionAlgorithm algorithm) {
+      return new KeyMetadataImpl(keyName, version, algorithm);
     }
 
     @Override
-    public void end() {
-      root.end();
+    public Key getLocalKey(KeyMetadata key, byte[] iv) throws IOException {
+      EncryptionAlgorithm algorithm = key.getAlgorithm();
+      KeyProviderCryptoExtension.EncryptedKeyVersion encryptedKey =
+          KeyProviderCryptoExtension.EncryptedKeyVersion.createForDecryption(
+              key.getKeyName(), buildKeyVersionName(key), iv,
+              algorithm.getZeroKey());
+      try {
+        KeyProviderCryptoExtension.KeyVersion decrypted =
+            ((KeyProviderCryptoExtension.CryptoExtension) provider)
+                .decryptEncryptedKey(encryptedKey);
+        return new SecretKeySpec(decrypted.getMaterial(),
+            algorithm.getAlgorithm());
+      } catch (GeneralSecurityException e) {
+        throw new IOException("Problem decrypting key " + key.getKeyName(), e);
+      }
     }
   }
 
-  static class ZlibDirectDecompressWrapper implements DirectDecompressor {
-    private final ZlibDecompressor.ZlibDirectDecompressor root;
+  static class KeyMetadataImpl implements KeyMetadata {
+    private final String keyName;
+    private final int version;
+    private final EncryptionAlgorithm algorithm;
 
-    ZlibDirectDecompressWrapper(ZlibDecompressor.ZlibDirectDecompressor root) {
-      this.root = root;
+    KeyMetadataImpl(String keyName, KeyProviderExtension.Metadata metadata) {
+      this.keyName = keyName;
+      version = metadata.getVersions() - 1;
+      algorithm = findAlgorithm(metadata);
     }
 
-    public void decompress(ByteBuffer input, ByteBuffer output) throws 
IOException {
-      root.decompress(input, output);
+    KeyMetadataImpl(String keyName, int version, EncryptionAlgorithm 
algorithm){
+      this.keyName = keyName;
+      this.version = version;
+      this.algorithm = algorithm;
     }
 
     @Override
-    public void reset() {
-      root.reset();
+    public String getKeyName() {
+      return keyName;
     }
 
     @Override
-    public void end() {
-      root.end();
+    public EncryptionAlgorithm getAlgorithm() {
+      return algorithm;
     }
-  }
 
-  static DirectDecompressor getDecompressor( DirectCompressionType codec) {
-    switch (codec) {
-      case ZLIB:
-        return new ZlibDirectDecompressWrapper
-            (new ZlibDecompressor.ZlibDirectDecompressor());
-      case ZLIB_NOHEADER:
-        return new ZlibDirectDecompressWrapper
-            (new ZlibDecompressor.ZlibDirectDecompressor
-                (ZlibDecompressor.CompressionHeader.NO_HEADER, 0));
-      case SNAPPY:
-        return new SnappyDirectDecompressWrapper
-            (new SnappyDecompressor.SnappyDirectDecompressor());
-      default:
-        return null;
+    @Override
+    public int getVersion() {
+      return version;
     }
-  }
-
-  public DirectDecompressor getDirectDecompressor( DirectCompressionType 
codec) {
-    return getDecompressor(codec);
- }
 
-  @Override
-  public ZeroCopyReaderShim getZeroCopyReader(FSDataInputStream in,
-                                              ByteBufferPoolShim pool
-                                              ) throws IOException {
-    return ZeroCopyShims.getZeroCopyReader(in, pool);
+    /**
+     * Find the correct algorithm based on the key's metadata.
+     * @param meta the key's metadata
+     * @return the correct algorithm
+     */
+    static EncryptionAlgorithm 
findAlgorithm(KeyProviderCryptoExtension.Metadata meta) {
+      String cipher = meta.getCipher();
+      if (cipher.startsWith("AES/")) {
+        int bitLength = meta.getBitLength();
+        if (bitLength == 128) {
+          return EncryptionAlgorithm.AES_128;
+        } else {
+          if (bitLength != 256) {
+            LOG.info("ORC column encryption does not support " + bitLength +
+                " bit keys. Using 256 bits instead.");
+          }
+          return EncryptionAlgorithm.AES_256;
+        }
+      }
+      throw new IllegalArgumentException("ORC column encryption only supports" 
+
+          " AES and not " + cipher);
+    }
   }
 
   @Override
-  public long padStreamToBlock(OutputStream output,
-                               long padding) throws IOException {
-    return HadoopShimsPre2_3.padStream(output, padding);
+  public KeyProvider getKeyProvider(Configuration conf) throws IOException {
+    return new KeyProviderImpl(conf);
   }
-
 }

http://git-wip-us.apache.org/repos/asf/orc/blob/10c0a858/java/shims/src/test/org/apache/orc/impl/TestHadoopShimsPre2_7.java
----------------------------------------------------------------------
diff --git a/java/shims/src/test/org/apache/orc/impl/TestHadoopShimsPre2_7.java 
b/java/shims/src/test/org/apache/orc/impl/TestHadoopShimsPre2_7.java
new file mode 100644
index 0000000..c6f2cc2
--- /dev/null
+++ b/java/shims/src/test/org/apache/orc/impl/TestHadoopShimsPre2_7.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.orc.impl;
+
+import org.apache.hadoop.crypto.key.KeyProvider;
+import org.apache.hadoop.crypto.key.kms.KMSClientProvider;
+import org.apache.orc.EncryptionAlgorithm;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.sql.Date;
+import java.util.HashMap;
+
+import static junit.framework.Assert.assertEquals;
+
+public class TestHadoopShimsPre2_7 {
+
+  @Test(expected = IllegalArgumentException.class)
+  public void testFindingUnknownEncryption() throws Exception {
+    KeyProvider.Metadata meta = new KMSClientProvider.KMSMetadata(
+        "XXX/CTR/NoPadding", 128, "", new HashMap<String, String>(),
+        new Date(0), 1);
+    HadoopShimsPre2_7.KeyMetadataImpl.findAlgorithm(meta);
+  }
+
+  @Test
+  public void testFindingAesEncryption() throws Exception {
+    KeyProvider.Metadata meta = new KMSClientProvider.KMSMetadata(
+        "AES/CTR/NoPadding", 128, "", new HashMap<String, String>(),
+        new Date(0), 1);
+    assertEquals(EncryptionAlgorithm.AES_128,
+        HadoopShimsPre2_7.KeyMetadataImpl.findAlgorithm(meta));
+    meta = new KMSClientProvider.KMSMetadata(
+        "AES/CTR/NoPadding", 256, "", new HashMap<String, String>(),
+        new Date(0), 1);
+    assertEquals(EncryptionAlgorithm.AES_256,
+        HadoopShimsPre2_7.KeyMetadataImpl.findAlgorithm(meta));
+    meta = new KMSClientProvider.KMSMetadata(
+        "AES/CTR/NoPadding", 512, "", new HashMap<String, String>(),
+        new Date(0), 1);
+    assertEquals(EncryptionAlgorithm.AES_256,
+        HadoopShimsPre2_7.KeyMetadataImpl.findAlgorithm(meta));
+  }
+}

http://git-wip-us.apache.org/repos/asf/orc/blob/10c0a858/java/shims/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/java/shims/src/test/resources/log4j.properties 
b/java/shims/src/test/resources/log4j.properties
new file mode 100644
index 0000000..fae44b6
--- /dev/null
+++ b/java/shims/src/test/resources/log4j.properties
@@ -0,0 +1,20 @@
+# 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.
+
+log4j.rootLogger=WARN,stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%p\t%d{ISO8601}\t%r\t%c\t[%t]\t%m%n
+
+# Suppress the warnings about native io not being available
+log4j.logger.org.apache.hadoop.util.NativeCodeLoader=ERROR
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/orc/blob/10c0a858/java/tools/src/findbugs/exclude.xml
----------------------------------------------------------------------
diff --git a/java/tools/src/findbugs/exclude.xml 
b/java/tools/src/findbugs/exclude.xml
index d6441f1..fd215c0 100644
--- a/java/tools/src/findbugs/exclude.xml
+++ b/java/tools/src/findbugs/exclude.xml
@@ -14,6 +14,6 @@
 -->
 <FindBugsFilter>
   <Match>
-    <Bug pattern="EI_EXPOSE_REP,EI_EXPOSE_REP2"/>
+    <Bug pattern="EI_EXPOSE_REP,EI_EXPOSE_REP2,DM_EXIT"/>
   </Match>
 </FindBugsFilter>

http://git-wip-us.apache.org/repos/asf/orc/blob/10c0a858/java/tools/src/java/org/apache/orc/tools/Driver.java
----------------------------------------------------------------------
diff --git a/java/tools/src/java/org/apache/orc/tools/Driver.java 
b/java/tools/src/java/org/apache/orc/tools/Driver.java
index ae8f5e1..01334c0 100644
--- a/java/tools/src/java/org/apache/orc/tools/Driver.java
+++ b/java/tools/src/java/org/apache/orc/tools/Driver.java
@@ -91,6 +91,7 @@ public class Driver {
       System.err.println("   scan - scan the ORC file");
       System.err.println("   convert - convert CSV and JSON files to ORC");
       System.err.println("   json-schema - scan JSON files to determine their 
schema");
+      System.err.println("   key - print information about the keys");
       System.err.println();
       System.err.println("To get more help, provide -h to the command");
       System.exit(1);
@@ -110,6 +111,8 @@ public class Driver {
       JsonSchemaFinder.main(conf, options.commandArgs);
     } else if ("convert".equals(options.command)) {
       ConvertTool.main(conf, options.commandArgs);
+    } else if ("key".equals(options.command)) {
+      KeyTool.main(conf, options.commandArgs);
     } else {
       System.err.println("Unknown subcommand: " + options.command);
       System.exit(1);

http://git-wip-us.apache.org/repos/asf/orc/blob/10c0a858/java/tools/src/java/org/apache/orc/tools/KeyTool.java
----------------------------------------------------------------------
diff --git a/java/tools/src/java/org/apache/orc/tools/KeyTool.java 
b/java/tools/src/java/org/apache/orc/tools/KeyTool.java
new file mode 100644
index 0000000..98ff8d6
--- /dev/null
+++ b/java/tools/src/java/org/apache/orc/tools/KeyTool.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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 org.apache.orc.tools;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.io.BytesWritable;
+import org.apache.orc.EncryptionAlgorithm;
+import org.apache.orc.impl.HadoopShims;
+import org.apache.orc.impl.HadoopShimsFactory;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONWriter;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+
+/**
+ * Print the information about the encryption keys.
+ */
+public class KeyTool {
+
+  static void printKey(JSONWriter writer,
+                       HadoopShims.KeyProvider provider,
+                       String keyName) throws JSONException, IOException {
+    HadoopShims.KeyMetadata meta = provider.getCurrentKeyVersion(keyName);
+    writer.object();
+    writer.key("name");
+    writer.value(keyName);
+    EncryptionAlgorithm algorithm = meta.getAlgorithm();
+    writer.key("algorithm");
+    writer.value(algorithm.getAlgorithm());
+    writer.key("keyLength");
+    writer.value(algorithm.keyLength());
+    writer.key("version");
+    writer.value(meta.getVersion());
+    byte[] iv = new byte[algorithm.getIvLength()];
+    byte[] key = provider.getLocalKey(meta, iv).getEncoded();
+    writer.key("key 0");
+    writer.value(new BytesWritable(key).toString());
+    writer.endObject();
+  }
+
+  private final OutputStreamWriter writer;
+  private final Configuration conf;
+
+  public KeyTool(Configuration conf,
+                 String[] args) throws IOException, ParseException {
+    CommandLine opts = parseOptions(args);
+    PrintStream stream;
+    if (opts.hasOption('o')) {
+      stream = new PrintStream(opts.getOptionValue('o'), "UTF-8");
+    } else {
+      stream = System.out;
+    }
+    writer = new OutputStreamWriter(stream, "UTF-8");
+    this.conf = conf;
+  }
+
+  void run() throws IOException, JSONException {
+    HadoopShims.KeyProvider provider =
+        HadoopShimsFactory.get().getKeyProvider(conf);
+    if (provider == null) {
+      System.err.println("No key provider available.");
+      System.exit(1);
+    }
+    for(String keyName: provider.getKeyNames()) {
+      JSONWriter writer = new JSONWriter(this.writer);
+      printKey(writer, provider, keyName);
+      this.writer.write('\n');
+    }
+    this.writer.close();
+  }
+
+  private static CommandLine parseOptions(String[] args) throws ParseException 
{
+    Options options = new Options();
+
+    options.addOption(
+        Option.builder("h").longOpt("help").desc("Provide help").build());
+    options.addOption(
+        Option.builder("o").longOpt("output").desc("Output filename")
+            .hasArg().build());
+    CommandLine cli = new DefaultParser().parse(options, args);
+    if (cli.hasOption('h')) {
+      HelpFormatter formatter = new HelpFormatter();
+      formatter.printHelp("key", options);
+      System.exit(1);
+    }
+    return cli;
+  }
+
+  public static void main(Configuration conf,
+                          String[] args
+                          ) throws IOException, ParseException, JSONException {
+    KeyTool tool = new KeyTool(conf, args);
+    tool.run();
+  }
+}

Reply via email to