[wip] WIP on wrapping AES-256-XTS for Rust with cxx
Change-Id: Iad41c36b326979d13422fec3a0e129eefcacc8cf
diff --git a/src/lib/fuchsia-cxx/BUILD.gn b/src/lib/fuchsia-cxx/BUILD.gn
index 97198cc..862c8f9 100644
--- a/src/lib/fuchsia-cxx/BUILD.gn
+++ b/src/lib/fuchsia-cxx/BUILD.gn
@@ -14,7 +14,9 @@
# It is recommended that you read https://docs.rs/cxx first to understand how cxx works, and to
# understand what cxx does (and does not) do. All C++ code is still "unsafe" from the Rust
# perspective; cxx just helps with building the Rust side of the FFI layer safely.
- visibility = []
+ visibility = [
+ "//src/security/fcrypto-rust",
+ ]
cxx_crate = "//third_party/rust_crates/vendor/cxx"
sources = [
diff --git a/src/lib/fuchsia-cxx/rust_cxx_ffi_library.gni b/src/lib/fuchsia-cxx/rust_cxx_ffi_library.gni
index 01e486f4..b701f098 100644
--- a/src/lib/fuchsia-cxx/rust_cxx_ffi_library.gni
+++ b/src/lib/fuchsia-cxx/rust_cxx_ffi_library.gni
@@ -180,9 +180,10 @@
deps = generation_targets + rust_deps
}
+ crate_name = string_replace(package_name, "-", "_")
generated_file(generated_rs_target) {
outputs = [ "$source_gen_dir/staticlib_source_root.rs" ]
- contents = "pub use " + package_name + "::*;"
+ contents = "pub use " + crate_name + "::*;"
}
# Create a staticlib from the rustc_library above. This uses a generated source_root that just
diff --git a/src/security/BUILD.gn b/src/security/BUILD.gn
index 99e1e52..83cb8d1 100644
--- a/src/security/BUILD.gn
+++ b/src/security/BUILD.gn
@@ -17,6 +17,7 @@
"codelab:tests",
"csa_helper:tests",
"fcrypto:tests",
+ "fcrypto-rust:tests",
"kms:tests",
"scrutiny:tests",
"tee_manager:tests",
diff --git a/src/security/fcrypto-rust/BUILD.gn b/src/security/fcrypto-rust/BUILD.gn
new file mode 100644
index 0000000..23cfee9
--- /dev/null
+++ b/src/security/fcrypto-rust/BUILD.gn
@@ -0,0 +1,53 @@
+# Copyright 2021 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/components.gni")
+import("//src/lib/fuchsia-cxx/rust_cxx_ffi_library.gni")
+
+rust_cxx_ffi_library("fcrypto-rust") {
+ name = "fcrypto_rust"
+ rust_source_root = "ffi.rs"
+ rust_sources = [ "ffi.rs" ]
+ bridge_sources = [ "ffi.rs" ]
+ rust_deps = [
+ "//src/lib/zircon/rust:fuchsia-zircon",
+ "//src/lib/zircon/rust:fuchsia-zircon-sys",
+ "//third_party/rust_crates:cxx",
+ ]
+ cpp_sources = [
+ "ffi.cc",
+ "ffi.h",
+ ]
+ public_deps = [ "//src/security/fcrypto" ]
+}
+
+rustc_test("fcrypto-rust-ffi-test") {
+ name = "fcrypto_rust_ffi_test"
+ edition = "2018"
+
+ source_root = "ffi_test.rs"
+ sources = [ "ffi_test.rs" ]
+
+ deps = [
+ ":fcrypto-rust",
+ "//src/lib/zircon/rust:fuchsia-zircon",
+ "//src/lib/zircon/rust:fuchsia-zircon-sys",
+ "//third_party/rust_crates:cxx",
+ ]
+}
+
+fuchsia_component("fcrypto-rust-test") {
+ testonly = true
+ manifest = "meta/fcrypto-rust-ffi-test.cml"
+ deps = [ ":fcrypto-rust-ffi-test" ]
+}
+
+fuchsia_test_package("fcrypto-rust-tests") {
+ test_components = [ ":fcrypto-rust-test" ]
+}
+
+group("tests") {
+ testonly = true
+ deps = [ ":fcrypto-rust-tests" ]
+}
diff --git a/src/security/fcrypto-rust/ffi.cc b/src/security/fcrypto-rust/ffi.cc
new file mode 100644
index 0000000..3692620
--- /dev/null
+++ b/src/security/fcrypto-rust/ffi.cc
@@ -0,0 +1,69 @@
+// Copyright 2021 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/security/fcrypto-rust/ffi.h"
+
+#include <memory>
+
+#include "src/security/fcrypto/bytes.h"
+#include "src/security/fcrypto/cipher.h"
+#include "src/security/fcrypto/secret.h"
+
+namespace crypto {
+
+std::unique_ptr<Cipher> new_cipher() { return std::make_unique<Cipher>(); }
+
+static zx_status_t init_internal(Cipher& cipher, Cipher::Direction direction,
+ rust::Slice<const uint8_t>& secret, rust::Slice<const uint8_t>& iv,
+ uint64_t alignment) {
+ // Allocate Secret, Bytes
+ Secret crypto_secret;
+ zx_status_t rc;
+ uint8_t* secret_inner;
+ rc = crypto_secret.Allocate(secret.length(), &secret_inner);
+ if (rc != ZX_OK) {
+ return rc;
+ }
+
+ Bytes crypto_iv;
+ rc = crypto_iv.Resize(iv.length());
+ if (rc != ZX_OK) {
+ return rc;
+ }
+
+ // Populate the buffers.
+ memcpy(secret_inner, secret.data(), secret.length());
+ memcpy(crypto_iv.get(), iv.data(), iv.length());
+
+ return cipher.Init(crypto::Cipher::Algorithm::kAES256_XTS, direction, crypto_secret, crypto_iv,
+ alignment);
+}
+
+zx_status_t init_for_encipher(Cipher& cipher, rust::Slice<const uint8_t> secret,
+ rust::Slice<const uint8_t> iv, uint64_t alignment) {
+ return init_internal(cipher, Cipher::Direction::kEncrypt, secret, iv, alignment);
+}
+
+zx_status_t init_for_decipher(Cipher& cipher, rust::Slice<const uint8_t> secret,
+ rust::Slice<const uint8_t> iv, uint64_t alignment) {
+ return init_internal(cipher, Cipher::Direction::kDecrypt, secret, iv, alignment);
+}
+
+zx_status_t encipher(Cipher& cipher, rust::Slice<const uint8_t> plaintext, uint64_t offset,
+ rust::Slice<uint8_t> ciphertext) {
+ if (plaintext.length() != ciphertext.length()) {
+ return ZX_ERR_INVALID_ARGS;
+ }
+ return cipher.Encrypt(plaintext.data(), offset, plaintext.length(), ciphertext.data());
+}
+
+zx_status_t decipher(Cipher& cipher, rust::Slice<const uint8_t> ciphertext, uint64_t offset,
+ rust::Slice<uint8_t> plaintext) {
+ if (plaintext.length() != ciphertext.length()) {
+ return ZX_ERR_INVALID_ARGS;
+ }
+ return cipher.Decrypt(ciphertext.data(), offset, ciphertext.length(), plaintext.data());
+}
+
+} // namespace crypto
diff --git a/src/security/fcrypto-rust/ffi.h b/src/security/fcrypto-rust/ffi.h
new file mode 100644
index 0000000..8cf3c83
--- /dev/null
+++ b/src/security/fcrypto-rust/ffi.h
@@ -0,0 +1,31 @@
+// Copyright 2021 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SRC_SECURITY_FCRYPTO_RUST_FFI_H_
+#define SRC_SECURITY_FCRYPTO_RUST_FFI_H_
+
+#include <zircon/status.h>
+
+#include "src/security/fcrypto/cipher.h"
+
+// Header generated from Rust cxxbridge declarations. Include dir added by build template.
+#include "src/security/fcrypto-rust/ffi.rs.h"
+
+namespace crypto {
+
+std::unique_ptr<Cipher> new_cipher();
+
+zx_status_t init_for_encipher(Cipher& cipher, rust::Slice<const uint8_t> secret,
+ rust::Slice<const uint8_t> iv, uint64_t alignment);
+zx_status_t init_for_decipher(Cipher& cipher, rust::Slice<const uint8_t> secret,
+ rust::Slice<const uint8_t> iv, uint64_t alignment);
+
+zx_status_t encipher(Cipher& cipher, rust::Slice<const uint8_t> plaintext, uint64_t offset,
+ rust::Slice<uint8_t> ciphertext);
+zx_status_t decipher(Cipher& cipher, rust::Slice<const uint8_t> ciphertext, uint64_t offset,
+ rust::Slice<uint8_t> plaintext);
+
+} // namespace crypto
+
+#endif // SRC_SECURITY_FCRYPTO_RUST_FFI_H_
diff --git a/src/security/fcrypto-rust/ffi.rs b/src/security/fcrypto-rust/ffi.rs
new file mode 100644
index 0000000..c9a0f99
--- /dev/null
+++ b/src/security/fcrypto-rust/ffi.rs
@@ -0,0 +1,136 @@
+// Copyright 2021 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The `cxx` crate's expanded code appears to trigger this elided lifetimes in paths warning
+#![allow(elided_lifetimes_in_paths)]
+#![allow(dead_code)]
+
+use cxx::UniquePtr;
+use fuchsia_zircon_sys as sys;
+use std::pin::Pin;
+
+#[cxx::bridge(namespace = "crypto")]
+mod ffi {
+ // No shared structs
+
+ unsafe extern "C++" {
+ // C++ declarations
+ include!("src/security/fcrypto-rust/ffi.h");
+
+ type Cipher;
+
+ fn new_cipher() -> UniquePtr<Cipher>;
+
+ fn init_for_encipher(
+ cipher: Pin<&mut Cipher>,
+ secret: &[u8],
+ iv: &[u8],
+ alignment: u64,
+ ) -> i32;
+ fn init_for_decipher(
+ cipher: Pin<&mut Cipher>,
+ secret: &[u8],
+ iv: &[u8],
+ alignment: u64,
+ ) -> i32;
+
+ fn encipher(
+ cipher: Pin<&mut Cipher>,
+ plaintext: &[u8],
+ offset: u64,
+ ciphertext: &mut [u8],
+ ) -> i32;
+ fn decipher(
+ cipher: Pin<&mut Cipher>,
+ plaintext: &[u8],
+ offset: u64,
+ ciphertext: &mut [u8],
+ ) -> i32;
+ }
+}
+
+pub struct Aes256XtsCipher {
+ encipherer: Aes256XtsEncipherer,
+ decipherer: Aes256XtsDecipherer,
+}
+
+// TODO: make these failures return a better error type than i32
+impl Aes256XtsCipher {
+ pub fn new(secret: &[u8], iv: &[u8]) -> Result<Aes256XtsCipher, i32> {
+ // Allocate a pair of C++ Cipher objects.
+ let encipher_inner = ffi::new_cipher();
+ let decipher_inner = ffi::new_cipher();
+ let mut encipherer = Aes256XtsEncipherer { inner: encipher_inner };
+ let mut decipherer = Aes256XtsDecipherer { inner: decipher_inner };
+
+ encipherer.init(secret, iv)?;
+ decipherer.init(secret, iv)?;
+
+ Ok(Aes256XtsCipher { encipherer, decipherer })
+ }
+
+ pub fn encrypt(&mut self, offset: u64, plaintext: &[u8], ciphertext: &mut [u8]) -> Result<(), i32> {
+ self.encipherer.encipher(offset, plaintext, ciphertext)
+ }
+
+ pub fn decrypt(&mut self, offset: u64, ciphertext: &[u8], plaintext: &mut [u8]) -> Result<(), i32> {
+ self.decipherer.decipher(offset, ciphertext, plaintext)
+ }
+}
+
+pub struct Aes256XtsEncipherer {
+ inner: UniquePtr<ffi::Cipher>,
+}
+
+pub struct Aes256XtsDecipherer {
+ inner: UniquePtr<ffi::Cipher>,
+}
+
+const ALIGN: u64 = 128;
+
+impl Aes256XtsEncipherer {
+ fn init(&mut self, secret: &[u8], iv: &[u8]) -> Result<(), i32> {
+ let mut cref = self.inner.as_mut().ok_or(sys::ZX_ERR_INVALID_ARGS)?;
+ let res = ffi::init_for_encipher(Pin::as_mut(&mut cref), secret, iv, ALIGN);
+ if res == 0 {
+ Ok(())
+ } else {
+ Err(res)
+ }
+ }
+
+ fn encipher(&mut self, offset: u64, plaintext: &[u8], ciphertext: &mut [u8]) -> Result<(), i32> {
+ assert!(plaintext.len() == ciphertext.len());
+ let mut cref = self.inner.as_mut().ok_or(sys::ZX_ERR_INVALID_ARGS)?;
+ let res = ffi::encipher(Pin::as_mut(&mut cref), plaintext, offset, ciphertext);
+ if res == 0 {
+ Ok(())
+ } else {
+ Err(res)
+ }
+ }
+}
+
+impl Aes256XtsDecipherer {
+ fn init(&mut self, secret: &[u8], iv: &[u8]) -> Result<(), i32> {
+ let mut cref = self.inner.as_mut().ok_or(sys::ZX_ERR_INVALID_ARGS)?;
+ let res = ffi::init_for_decipher(Pin::as_mut(&mut cref), secret, iv, ALIGN);
+ if res == 0 {
+ Ok(())
+ } else {
+ Err(res)
+ }
+ }
+
+ fn decipher(&mut self, offset: u64, ciphertext: &[u8], plaintext: &mut [u8]) -> Result<(), i32> {
+ assert!(plaintext.len() == ciphertext.len());
+ let mut cref = self.inner.as_mut().ok_or(sys::ZX_ERR_INVALID_ARGS)?;
+ let res = ffi::decipher(Pin::as_mut(&mut cref), ciphertext, offset, plaintext);
+ if res == 0 {
+ Ok(())
+ } else {
+ Err(res)
+ }
+ }
+}
diff --git a/src/security/fcrypto-rust/ffi_test.rs b/src/security/fcrypto-rust/ffi_test.rs
new file mode 100644
index 0000000..eefb48e
--- /dev/null
+++ b/src/security/fcrypto-rust/ffi_test.rs
@@ -0,0 +1,17 @@
+// TODO: write a test that makes use of the AES-256-XTS bindings with some test vectors
+
+#![allow(elided_lifetimes_in_paths)]
+
+use fcrypto_rust::Aes256XtsCipher;
+
+#[test]
+fn test_cipher() {
+ // TODO: double-check these lengths
+ let key = [0u8; 32];
+ let iv = [0u8; 32];
+ let mut cipher = Aes256XtsCipher::new(&key, &iv).expect("create and init cipher");
+ let ctext = [0u8 ; 128];
+ let mut ptext = [0u8 ; 128];
+ cipher.encrypt(0, &ctext, &mut ptext).expect("encrypt");
+}
+
diff --git a/src/security/fcrypto-rust/meta/fcrypto-rust-ffi-test.cml b/src/security/fcrypto-rust/meta/fcrypto-rust-ffi-test.cml
new file mode 100644
index 0000000..4b32576
--- /dev/null
+++ b/src/security/fcrypto-rust/meta/fcrypto-rust-ffi-test.cml
@@ -0,0 +1,12 @@
+// Copyright 2021 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+{
+ include: [
+ "//src/sys/test_runners/rust/default.shard.cml",
+ "syslog/client.shard.cml",
+ ],
+ program: {
+ binary: "bin/fcrypto_rust_ffi_test",
+ },
+}