From 358379ce7b24d15198446a519a7880537a2fef1d Mon Sep 17 00:00:00 2001 From: Matt Palmer Date: Fri, 7 Oct 2022 11:25:13 +1100 Subject: [PATCH] Move static keys into a dedicated type I always thought that it was a bit of a hack to implement `KeyProvider` directly on Vec, and so I've decided to nuke that idea entirely. Also took the opportunity to go through the Ruby bindings and normalise the naming conventions and code patterns for turning Ruby object arguments into Rust structs. --- ruby/ext/enquo/src/lib.rs | 113 +++++++++++++++++--------------- rust/src/crypto/ore16v1.rs | 8 ++- rust/src/crypto/ore64v1.rs | 8 ++- rust/src/crypto/ore6v1.rs | 8 ++- rust/src/datatype/date_v1.rs | 8 ++- rust/src/datatype/i64v1.rs | 8 ++- rust/src/field.rs | 13 ++-- rust/src/key_provider/mod.rs | 3 +- rust/src/key_provider/slice.rs | 24 ------- rust/src/key_provider/static.rs | 31 +++++++++ rust/src/lib.rs | 2 +- rust/src/root.rs | 3 +- 12 files changed, 128 insertions(+), 101 deletions(-) delete mode 100644 rust/src/key_provider/slice.rs create mode 100644 rust/src/key_provider/static.rs diff --git a/ruby/ext/enquo/src/lib.rs b/ruby/ext/enquo/src/lib.rs index 201ec9c..750bcc7 100644 --- a/ruby/ext/enquo/src/lib.rs +++ b/ruby/ext/enquo/src/lib.rs @@ -1,17 +1,19 @@ #[macro_use] extern crate rutie; -use enquo_core::{Date, Field, Root, I64}; +use enquo_core::{key_provider, Date, Field, Root, I64}; use rutie::{AnyObject, Class, Integer, Module, Object, RString, Symbol, VerifiedObject, VM}; class!(EnquoRoot); class!(EnquoRootKeyStatic); class!(EnquoField); -type StaticRootKey = Vec; - wrappable_struct!(Root<'static>, RootWrapper, ROOT_WRAPPER); -wrappable_struct!(StaticRootKey, StaticRootKeyWrapper, STATIC_ROOT_KEY_WRAPPER); +wrappable_struct!( + key_provider::Static, + StaticRootKeyWrapper, + STATIC_ROOT_KEY_WRAPPER +); wrappable_struct!(Field, FieldWrapper, FIELD_WRAPPER); fn maybe_raise(r: Result, s: &str) -> T { @@ -24,27 +26,24 @@ fn maybe_raise(r: Result, s: &str) -> T { .unwrap() } -methods!( +unsafe_methods!( EnquoRoot, rbself, fn enquo_root_new_from_static_root_key(root_key_obj: EnquoRootKeyStatic) -> EnquoRoot { - let root_key = root_key_obj.unwrap(); - // Not so needless after all, Clippy... - #[allow(clippy::needless_borrow)] - let rk = root_key.get_data(&*STATIC_ROOT_KEY_WRAPPER); + let rk = root_key_obj.get_data(&*STATIC_ROOT_KEY_WRAPPER); let root = maybe_raise(Root::new(rk), "Failed to create Enquo::Root"); let klass = Module::from_existing("Enquo").get_nested_class("Root"); klass.wrap_data(root, &*ROOT_WRAPPER) }, - fn enquo_root_field(relation: RString, name: RString) -> EnquoField { + fn enquo_root_field(relation_obj: RString, name_obj: RString) -> EnquoField { + let relation = relation_obj.to_vec_u8_unchecked(); + let name = name_obj.to_vec_u8_unchecked(); + let root = rbself.get_data(&*ROOT_WRAPPER); let field = maybe_raise( - root.field( - &relation.unwrap().to_vec_u8_unchecked(), - &name.unwrap().to_vec_u8_unchecked(), - ), + root.field(&relation, &name), "Failed to create Enquo::Field", ); @@ -53,12 +52,13 @@ methods!( } ); -methods!( +unsafe_methods!( EnquoRootKeyStatic, _rbself, - fn enquo_root_key_static_new(root_key: RString) -> EnquoRootKeyStatic { - #[allow(clippy::redundant_clone)] - let k = root_key.unwrap().to_vec_u8_unchecked().to_owned(); + fn enquo_root_key_static_new(root_key_obj: RString) -> EnquoRootKeyStatic { + let root_key = root_key_obj.to_vec_u8_unchecked(); + + let k = key_provider::Static::new(&root_key); let klass = Module::from_existing("Enquo") .get_nested_class("RootKey") .get_nested_class("Static"); @@ -81,85 +81,90 @@ impl VerifiedObject for EnquoRootKeyStatic { // rustfmt fucks this so it doesn't compile #[rustfmt::skip] -methods!( +unsafe_methods!( EnquoField, rbself, - fn enquo_field_encrypt_i64(value: Integer, context: RString, mode: Symbol) -> RString { - let i = value.unwrap().to_i64(); + fn enquo_field_encrypt_i64(i_obj: Integer, context_obj: RString, mode_obj: Symbol) -> RString { + let i = i_obj.to_i64(); + let context = context_obj.to_vec_u8_unchecked(); + let mode = mode_obj.to_str(); + let field = rbself.get_data(&*FIELD_WRAPPER); - let r_mode = mode.unwrap(); - let s_mode = r_mode.to_str(); let mut res = maybe_raise( - if s_mode == "unsafe" { - I64::new_with_unsafe_parts(i, &context.unwrap().to_vec_u8_unchecked(), field) + if mode == "unsafe" { + I64::new_with_unsafe_parts(i, &context, field) } else { - I64::new(i, &context.unwrap().to_vec_u8_unchecked(), field) + I64::new(i, &context, field) }, "Failed to create encrypted i64", ); - if s_mode == "no_query" { + if mode == "no_query" { res.drop_ore_ciphertext(); } - RString::new_utf8(&serde_json::to_string(&res).unwrap()) + RString::new_utf8(&maybe_raise(serde_json::to_string(&res), "Failed to JSONify ciphertext")) }, - fn enquo_field_decrypt_i64(ciphertext: RString, context: RString) -> Integer { - let ct_r = ciphertext.unwrap(); - let ct = ct_r.to_str_unchecked(); + fn enquo_field_decrypt_i64(ciphertext_obj: RString, context_obj: RString) -> Integer { + let ct = ciphertext_obj.to_str_unchecked(); + let context = context_obj.to_vec_u8_unchecked(); + + let field = rbself.get_data(&*FIELD_WRAPPER); + let e_value: I64 = maybe_raise(serde_json::from_str(ct), "Failed to deserialize ciphertext"); - let field = rbself.get_data(&*FIELD_WRAPPER); let value = maybe_raise( - e_value.decrypt(&context.unwrap().to_vec_u8_unchecked(), field), + e_value.decrypt(&context, field), "Failed to decrypt i64 value", ); Integer::from(value) }, fn enquo_field_encrypt_date( - y_r: Integer, - m_r: Integer, - d_r: Integer, - context: RString, - mode: Symbol + y_obj: Integer, + m_obj: Integer, + d_obj: Integer, + context_obj: RString, + mode_obj: Symbol ) -> RString { - let y = y_r.unwrap().to_i32() as i16; - let m = m_r.unwrap().to_i32() as u8; - let d = d_r.unwrap().to_i32() as u8; + let y = y_obj.to_i32() as i16; + let m = m_obj.to_i32() as u8; + let d = d_obj.to_i32() as u8; + let context = context_obj.to_vec_u8_unchecked(); + let mode = mode_obj.to_str(); + let field = rbself.get_data(&*FIELD_WRAPPER); - let r_mode = mode.unwrap(); - let s_mode = r_mode.to_str(); let mut res = maybe_raise( - if s_mode == "unsafe" { + if mode == "unsafe" { Date::new_with_unsafe_parts( (y, m, d), - &context.unwrap().to_vec_u8_unchecked(), + &context, field, ) } else { - Date::new((y, m, d), &context.unwrap().to_vec_u8_unchecked(), field) + Date::new((y, m, d), &context, field) }, "Failed to create encrypted date", ); - if s_mode == "no_query" { + if mode == "no_query" { res.drop_ore_ciphertexts(); } - RString::new_utf8(&serde_json::to_string(&res).unwrap()) + RString::new_utf8(&maybe_raise(serde_json::to_string(&res), "Failed to JSONify ciphertext")) }, - fn enquo_field_decrypt_date(ciphertext: RString, context: RString) -> AnyObject { - let ct_r = ciphertext.unwrap(); - let ct = ct_r.to_str_unchecked(); - let e_value: Date = - maybe_raise(serde_json::from_str(ct), "Failed to deserialize ciphertext"); + fn enquo_field_decrypt_date(ciphertext_obj: RString, context_obj: RString) -> AnyObject { + let ct = ciphertext_obj.to_str_unchecked(); + let context = context_obj.to_vec_u8_unchecked(); let field = rbself.get_data(&*FIELD_WRAPPER); + let e_value: Date = + maybe_raise(serde_json::from_str(ct), "Failed to deserialize ciphertext"); + let (y, m, d) = maybe_raise( - e_value.decrypt(&context.unwrap().to_vec_u8_unchecked(), field), + e_value.decrypt(&context, field), "Failed to decrypt date value", ); let klass = Class::from_existing("Date"); diff --git a/rust/src/crypto/ore16v1.rs b/rust/src/crypto/ore16v1.rs index 5604047..9dd73ff 100644 --- a/rust/src/crypto/ore16v1.rs +++ b/rust/src/crypto/ore16v1.rs @@ -113,11 +113,13 @@ impl Eq for ORE16v1 {} #[cfg(test)] mod tests { use super::*; - use crate::{Field, Root}; + use crate::{key_provider::Static, Field, Root}; fn field() -> Field { - let rk: &[u8] = b"testkey"; - Root::new(&rk).unwrap().field(b"foo", b"bar").unwrap() + Root::new(&Static::new(b"testkey")) + .unwrap() + .field(b"foo", b"bar") + .unwrap() } quickcheck! { diff --git a/rust/src/crypto/ore64v1.rs b/rust/src/crypto/ore64v1.rs index 5b294d2..e6d3fa5 100644 --- a/rust/src/crypto/ore64v1.rs +++ b/rust/src/crypto/ore64v1.rs @@ -113,11 +113,13 @@ impl Eq for ORE64v1 {} #[cfg(test)] mod tests { use super::*; - use crate::{Field, Root}; + use crate::{key_provider::Static, Field, Root}; fn field() -> Field { - let rk: &[u8] = b"testkey"; - Root::new(&rk).unwrap().field(b"foo", b"bar").unwrap() + Root::new(&Static::new(b"testkey")) + .unwrap() + .field(b"foo", b"bar") + .unwrap() } quickcheck! { diff --git a/rust/src/crypto/ore6v1.rs b/rust/src/crypto/ore6v1.rs index 2dcffb2..8a4f1dc 100644 --- a/rust/src/crypto/ore6v1.rs +++ b/rust/src/crypto/ore6v1.rs @@ -113,12 +113,14 @@ impl Eq for ORE6v1 {} #[cfg(test)] mod tests { use super::*; - use crate::{Field, Root}; + use crate::{key_provider::Static, Field, Root}; use quickcheck::TestResult; fn field() -> Field { - let rk: &[u8] = b"testkey"; - Root::new(&rk).unwrap().field(b"foo", b"bar").unwrap() + Root::new(&Static::new(b"testkey")) + .unwrap() + .field(b"foo", b"bar") + .unwrap() } quickcheck! { diff --git a/rust/src/datatype/date_v1.rs b/rust/src/datatype/date_v1.rs index 4c6187d..6d2be70 100644 --- a/rust/src/datatype/date_v1.rs +++ b/rust/src/datatype/date_v1.rs @@ -152,11 +152,13 @@ impl Eq for DateV1 {} #[cfg(test)] mod tests { use super::*; - use crate::Root; + use crate::{key_provider::Static, Root}; fn field() -> Field { - let k: &[u8] = b"testkey"; - Root::new(&k).unwrap().field(b"foo", b"bar").unwrap() + Root::new(&Static::new(b"testkey")) + .unwrap() + .field(b"foo", b"bar") + .unwrap() } #[test] diff --git a/rust/src/datatype/i64v1.rs b/rust/src/datatype/i64v1.rs index c609f67..bdbecb6 100644 --- a/rust/src/datatype/i64v1.rs +++ b/rust/src/datatype/i64v1.rs @@ -112,11 +112,13 @@ impl Eq for I64v1 {} #[cfg(test)] mod tests { use super::*; - use crate::Root; + use crate::{key_provider::Static, Root}; fn field() -> Field { - let k: &[u8] = b"testkey"; - Root::new(&k).unwrap().field(b"foo", b"bar").unwrap() + Root::new(&Static::new(b"testkey")) + .unwrap() + .field(b"foo", b"bar") + .unwrap() } #[test] diff --git a/rust/src/field.rs b/rust/src/field.rs index 1518e9f..4d2c6f9 100644 --- a/rust/src/field.rs +++ b/rust/src/field.rs @@ -1,14 +1,16 @@ -use crate::{Error, KeyProvider, Root}; +use crate::{key_provider::Static, Error, KeyProvider, Root}; pub struct Field { - field_key: Vec, + field_key: Static, } impl Field { pub fn new(root: &Root, collection: &[u8], name: &[u8]) -> Result { let field_key = Self::field_key(root, collection, name)?; - Ok(Field { field_key }) + Ok(Field { + field_key: Static::new(&field_key), + }) } pub fn subkey(&self, identifier: &[u8]) -> Result, Error> { @@ -31,11 +33,12 @@ impl Field { #[cfg(test)] mod tests { use super::*; + use crate::key_provider::Static; use hex_literal::hex; #[test] fn derives_field_key_correctly() { - let rk: &[u8] = b"testkey"; + let rk = Static::new(b"testkey"); let f = Root::new(&rk) .unwrap() .field(b"users", b"full_name") @@ -43,7 +46,7 @@ mod tests { assert_eq!( hex!["382beeb4093bc280 163017113af33e12 ca5d55b84e42e1b9 758d66ddcbd9b9d8"].to_vec(), - f.field_key + f.field_key.key() ); } } diff --git a/rust/src/key_provider/mod.rs b/rust/src/key_provider/mod.rs index 37050f8..85aa7d6 100644 --- a/rust/src/key_provider/mod.rs +++ b/rust/src/key_provider/mod.rs @@ -1,4 +1,5 @@ -mod slice; +mod r#static; +pub use r#static::Static; use crate::Error; diff --git a/rust/src/key_provider/slice.rs b/rust/src/key_provider/slice.rs deleted file mode 100644 index 5c636f6..0000000 --- a/rust/src/key_provider/slice.rs +++ /dev/null @@ -1,24 +0,0 @@ -use hmac::{Hmac, Mac}; -use sha2::Sha256; - -use super::KeyProvider; -use crate::Error; - -impl KeyProvider for &[u8] { - fn derive_key(&self, id: &[u8]) -> Result, Error> { - let k: &[u8] = self; - let mut keygen = Hmac::::new_from_slice(k).unwrap(); - keygen.update(id); - - Ok(keygen.finalize().into_bytes().to_vec()) - } -} - -impl KeyProvider for Vec { - fn derive_key(&self, id: &[u8]) -> Result, Error> { - let mut keygen = Hmac::::new_from_slice(self).unwrap(); - keygen.update(id); - - Ok(keygen.finalize().into_bytes().to_vec()) - } -} diff --git a/rust/src/key_provider/static.rs b/rust/src/key_provider/static.rs new file mode 100644 index 0000000..94eb36e --- /dev/null +++ b/rust/src/key_provider/static.rs @@ -0,0 +1,31 @@ +use hmac::{Hmac, Mac}; +use sha2::Sha256; + +use super::KeyProvider; +use crate::Error; + +#[derive(Clone)] +pub struct Static { + pub key: Vec, +} + +impl Static { + pub fn new(key: &[u8]) -> Static { + Static { key: key.to_vec() } + } + + #[cfg(test)] + pub fn key(&self) -> Vec { + self.key.to_owned() + } +} + +impl KeyProvider for Static { + fn derive_key(&self, id: &[u8]) -> Result, Error> { + let mut keygen = Hmac::::new_from_slice(&self.key) + .map_err(|_| Error::KeyError("Failed to create HMAC KBKDF instance".to_string()))?; + keygen.update(id); + + Ok(keygen.finalize().into_bytes().to_vec()) + } +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 7562ade..5ca45a1 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -2,7 +2,7 @@ mod crypto; mod datatype; mod error; mod field; -mod key_provider; +pub mod key_provider; mod root; pub use crate::{datatype::*, error::Error, field::Field, key_provider::KeyProvider, root::Root}; diff --git a/rust/src/root.rs b/rust/src/root.rs index cea7888..46bc061 100644 --- a/rust/src/root.rs +++ b/rust/src/root.rs @@ -21,10 +21,11 @@ impl Root<'_> { #[cfg(test)] mod tests { use super::*; + use crate::key_provider::Static; #[test] fn generates_a_field() { - let k: &[u8] = &[0; 32]; + let k = Static::new(&[0; 32]); let root = Root::new(&k).unwrap(); root.field(b"users", b"full_name").unwrap(); }