Add crypto libraries

Add C++ versions of the BoringSSL crypto functions we need (copied
from keystore) and create Rust wrappers for them.

Test: atest keystore2_test
Change-Id: I21ff8630df26ca73ae36395c5303270e96a6deb6
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index a1ee969..526fd86 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -44,6 +44,7 @@
         "libandroid_hardware_keymint",
         "libanyhow",
         "libbinder_rs",
+        "libkeystore2_crypto_bindgen",
         "libkeystore2_selinux",
         "liblazy_static",
         "liblibsqlite3_sys",
@@ -51,6 +52,24 @@
         "librusqlite",
         "libthiserror",
     ],
+    shared_libs: ["libkeystore2_crypto"],
+}
+
+cc_library {
+    name: "libkeystore2_crypto",
+    srcs: ["src/crypto.cpp"],
+    shared_libs: [
+        "libcrypto",
+        "liblog",
+    ],
+}
+
+rust_bindgen {
+    name: "libkeystore2_crypto_bindgen",
+    wrapper_src: "src/crypto.hpp",
+    crate_name: "keystore2_crypto_bindgen",
+    source_stem: "bindings",
+    host_supported: true,
 }
 
 rust_binary {
diff --git a/keystore2/src/crypto.cpp b/keystore2/src/crypto.cpp
new file mode 100644
index 0000000..8c52e4c
--- /dev/null
+++ b/keystore2/src/crypto.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#define LOG_TAG "keystore2"
+
+#include "crypto.hpp"
+
+#include <log/log.h>
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+
+#include <vector>
+
+// Copied from system/security/keystore/blob.h.
+
+constexpr size_t kGcmTagLength = 128 / 8;
+constexpr size_t kAes128KeySizeBytes = 128 / 8;
+
+// Copied from system/security/keystore/blob.cpp.
+
+#if defined(__clang__)
+#define OPTNONE __attribute__((optnone))
+#elif defined(__GNUC__)
+#define OPTNONE __attribute__((optimize("O0")))
+#else
+#error Need a definition for OPTNONE
+#endif
+
+class ArrayEraser {
+  public:
+    ArrayEraser(uint8_t* arr, size_t size) : mArr(arr), mSize(size) {}
+    OPTNONE ~ArrayEraser() { std::fill(mArr, mArr + mSize, 0); }
+
+  private:
+    volatile uint8_t* mArr;
+    size_t mSize;
+};
+
+/**
+ * Returns a EVP_CIPHER appropriate for the given key size.
+ */
+const EVP_CIPHER* getAesCipherForKey(size_t key_size) {
+    const EVP_CIPHER* cipher = EVP_aes_256_gcm();
+    if (key_size == kAes128KeySizeBytes) {
+        cipher = EVP_aes_128_gcm();
+    }
+    return cipher;
+}
+
+/*
+ * Encrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at
+ * 'iv' and write output to 'out' (which may be the same location as 'in') and 128-bit tag to
+ * 'tag'.
+ */
+bool AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len, const uint8_t* key,
+                     size_t key_size, const uint8_t* iv, uint8_t* tag) {
+
+    // There can be 128-bit and 256-bit keys
+    const EVP_CIPHER* cipher = getAesCipherForKey(key_size);
+
+    bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
+
+    EVP_EncryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key, iv);
+    EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
+
+    std::vector<uint8_t> out_tmp(len);
+    uint8_t* out_pos = out_tmp.data();
+    int out_len;
+
+    EVP_EncryptUpdate(ctx.get(), out_pos, &out_len, in, len);
+    out_pos += out_len;
+    EVP_EncryptFinal_ex(ctx.get(), out_pos, &out_len);
+    out_pos += out_len;
+    if (out_pos - out_tmp.data() != static_cast<ssize_t>(len)) {
+        ALOGD("Encrypted ciphertext is the wrong size, expected %zu, got %zd", len,
+              out_pos - out_tmp.data());
+        return false;
+    }
+
+    std::copy(out_tmp.data(), out_pos, out);
+    EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, kGcmTagLength, tag);
+
+    return true;
+}
+
+/*
+ * Decrypt 'len' data at 'in' with AES-GCM, using 128-bit or 256-bit key at 'key', 96-bit IV at
+ * 'iv', checking 128-bit tag at 'tag' and writing plaintext to 'out'(which may be the same
+ * location as 'in').
+ */
+bool AES_gcm_decrypt(const uint8_t* in, uint8_t* out, size_t len, const uint8_t* key,
+                     size_t key_size, const uint8_t* iv, const uint8_t* tag) {
+
+    // There can be 128-bit and 256-bit keys
+    const EVP_CIPHER* cipher = getAesCipherForKey(key_size);
+
+    bssl::UniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
+
+    EVP_DecryptInit_ex(ctx.get(), cipher, nullptr /* engine */, key, iv);
+    EVP_CIPHER_CTX_set_padding(ctx.get(), 0 /* no padding needed with GCM */);
+    EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, kGcmTagLength, const_cast<uint8_t*>(tag));
+
+    std::vector<uint8_t> out_tmp(len);
+    ArrayEraser out_eraser(out_tmp.data(), len);
+    uint8_t* out_pos = out_tmp.data();
+    int out_len;
+
+    EVP_DecryptUpdate(ctx.get(), out_pos, &out_len, in, len);
+    out_pos += out_len;
+    if (!EVP_DecryptFinal_ex(ctx.get(), out_pos, &out_len)) {
+        ALOGE("Failed to decrypt blob; ciphertext or tag is likely corrupted");
+        return false;
+    }
+    out_pos += out_len;
+    if (out_pos - out_tmp.data() != static_cast<ssize_t>(len)) {
+        ALOGE("Encrypted plaintext is the wrong size, expected %zu, got %zd", len,
+              out_pos - out_tmp.data());
+        return false;
+    }
+
+    std::copy(out_tmp.data(), out_pos, out);
+
+    return true;
+}
+
+// Copied from system/security/keystore/keymaster_enforcement.cpp.
+
+class EvpMdCtx {
+  public:
+    EvpMdCtx() { EVP_MD_CTX_init(&ctx_); }
+    ~EvpMdCtx() { EVP_MD_CTX_cleanup(&ctx_); }
+
+    EVP_MD_CTX* get() { return &ctx_; }
+
+  private:
+    EVP_MD_CTX ctx_;
+};
+
+bool CreateKeyId(const uint8_t* key_blob, size_t len, km_id_t* out_id) {
+    EvpMdCtx ctx;
+
+    uint8_t hash[EVP_MAX_MD_SIZE];
+    unsigned int hash_len;
+    if (EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr /* ENGINE */) &&
+        EVP_DigestUpdate(ctx.get(), key_blob, len) &&
+        EVP_DigestFinal_ex(ctx.get(), hash, &hash_len)) {
+        assert(hash_len >= sizeof(*out_id));
+        memcpy(out_id, hash, sizeof(*out_id));
+        return true;
+    }
+
+    return false;
+}
+
+// Copied from system/security/keystore/user_state.h
+
+static constexpr size_t SALT_SIZE = 16;
+
+// Copied from system/security/keystore/user_state.cpp.
+
+void generateKeyFromPassword(uint8_t* key, size_t key_len, const char* pw, size_t pw_len,
+                             uint8_t* salt) {
+    size_t saltSize;
+    if (salt != nullptr) {
+        saltSize = SALT_SIZE;
+    } else {
+        // Pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found
+        salt = (uint8_t*)"keystore";
+        // sizeof = 9, not strlen = 8
+        saltSize = sizeof("keystore");
+    }
+
+    const EVP_MD* digest = EVP_sha256();
+
+    // SHA1 was used prior to increasing the key size
+    if (key_len == kAes128KeySizeBytes) {
+        digest = EVP_sha1();
+    }
+
+    PKCS5_PBKDF2_HMAC(pw, pw_len, salt, saltSize, 8192, digest, key_len, key);
+}
diff --git a/keystore2/src/crypto.hpp b/keystore2/src/crypto.hpp
new file mode 100644
index 0000000..9a9bb2e
--- /dev/null
+++ b/keystore2/src/crypto.hpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef __CRYPTO_H__
+#define __CRYPTO_H__
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stddef.h>
+
+extern "C" {
+  bool AES_gcm_encrypt(const uint8_t* in, uint8_t* out, size_t len,
+                       const uint8_t* key, size_t key_size, const uint8_t* iv, uint8_t* tag);
+  bool AES_gcm_decrypt(const uint8_t* in, uint8_t* out, size_t len,
+                       const uint8_t* key, size_t key_size, const uint8_t* iv,
+                       const uint8_t* tag);
+
+  // Copied from system/security/keystore/keymaster_enforcement.h.
+  typedef uint64_t km_id_t;
+
+  bool CreateKeyId(const uint8_t* key_blob, size_t len, km_id_t* out_id);
+
+  void generateKeyFromPassword(uint8_t* key, size_t key_len, const char* pw,
+                               size_t pw_len, uint8_t* salt);
+}
+
+#endif  //  __CRYPTO_H__
diff --git a/keystore2/src/crypto.rs b/keystore2/src/crypto.rs
new file mode 100644
index 0000000..b25b648
--- /dev/null
+++ b/keystore2/src/crypto.rs
@@ -0,0 +1,78 @@
+// Copyright 2020, The Android Open Source Project
+//
+// 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.
+
+#[cfg(test)]
+mod tests {
+
+    use keystore2_crypto_bindgen::{
+        generateKeyFromPassword, AES_gcm_decrypt, AES_gcm_encrypt, CreateKeyId,
+    };
+
+    #[test]
+    fn test_encrypt_decrypt() {
+        let input = vec![0; 16];
+        let mut out = vec![0; 16];
+        let mut out2 = vec![0; 16];
+        let key = vec![0; 16];
+        let iv = vec![0; 12];
+        let mut tag = vec![0; 16];
+        unsafe {
+            let res = AES_gcm_encrypt(
+                input.as_ptr(),
+                out.as_mut_ptr(),
+                16,
+                key.as_ptr(),
+                16,
+                iv.as_ptr(),
+                tag.as_mut_ptr(),
+            );
+            assert!(res);
+            assert_ne!(out, input);
+            assert_ne!(tag, input);
+            let res = AES_gcm_decrypt(
+                out.as_ptr(),
+                out2.as_mut_ptr(),
+                16,
+                key.as_ptr(),
+                16,
+                iv.as_ptr(),
+                tag.as_ptr(),
+            );
+            assert!(res);
+            assert_eq!(out2, input);
+        }
+    }
+
+    #[test]
+    fn test_create_key_id() {
+        let blob = vec![0; 16];
+        let mut out: u64 = 0;
+        unsafe {
+            let res = CreateKeyId(blob.as_ptr(), 16, &mut out);
+            assert!(res);
+            assert_ne!(out, 0);
+        }
+    }
+
+    #[test]
+    fn test_generate_key_from_password() {
+        let mut key = vec![0; 16];
+        let pw = vec![0; 16];
+        let mut salt = vec![0; 16];
+        unsafe {
+            generateKeyFromPassword(key.as_mut_ptr(), 16, pw.as_ptr(), 16, salt.as_mut_ptr());
+        }
+        assert_ne!(key, vec![0; 16]);
+    }
+}
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 067399e..3e13c5f 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -14,6 +14,7 @@
 
 //! This crate implements the Android Keystore 2.0 service.
 
+mod crypto;
 pub mod database;
 pub mod error;
 pub mod globals;