From fb031166b8c8726bec378e2f259cf373b0497dd2 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Thu, 14 Feb 2019 14:33:13 -0500 Subject: [PATCH 1/3] api: expose keyctl_dh_compute_kdf functionality --- libkeyutils-sys/src/functions.rs | 13 ++++ src/api.rs | 126 ++++++++++++++++++++++++++++++- 2 files changed, 138 insertions(+), 1 deletion(-) diff --git a/libkeyutils-sys/src/functions.rs b/libkeyutils-sys/src/functions.rs index cf63b23..31cd144 100644 --- a/libkeyutils-sys/src/functions.rs +++ b/libkeyutils-sys/src/functions.rs @@ -143,4 +143,17 @@ extern "C" { buffer: *mut libc::c_char, buflen: libc::size_t) -> libc::c_long; + pub fn keyctl_dh_compute_kdf( + private: key_serial_t, + prime: key_serial_t, + base: key_serial_t, + // These are typos in the original API that can't be fixed. + // hashname: *mut libc::c_char, + // otherinfo: *mut libc::c_char, + hashname: *const libc::c_char, + otherinfo: *const libc::c_void, + otherinfolen: libc::size_t, + buffer: *mut libc::c_char, + buflen: libc::size_t) + -> libc::c_long; } diff --git a/src/api.rs b/src/api.rs index 6b1d5d8..18c87a2 100644 --- a/src/api.rs +++ b/src/api.rs @@ -24,7 +24,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::borrow::Borrow; +use std::borrow::{Borrow, Cow}; use std::ffi::CString; use std::mem; use std::ptr; @@ -502,6 +502,76 @@ pub struct Key { id: KeyringSerial, } +/// Hashes supported by the kernel. +#[derive(Debug, Clone)] +// #[non_exhaustive] +pub enum KeyctlHash { + /// The MD4 hash. + Md4, + /// The MD5 hash. + Md5, + /// The SHA1 hash. + Sha1, + /// The sha224 hash. + Sha224, + /// The sha256 hash. + Sha256, + /// The sha384 hash. + Sha384, + /// The sha512 hash. + Sha512, + /// The rmd128 hash. + RipeMd128, + /// The rmd160 hash. + RipeMd160, + /// The rmd256 hash. + RipeMd256, + /// The rmd320 hash. + RipeMd320, + /// The wp256 hash. + Wp256, + /// The wp384 hash. + Wp384, + /// The wp512 hash. + Wp512, + /// The tgr128 hash. + Tgr128, + /// The tgr160 hash. + Tgr160, + /// The tgr192 hash. + Tgr192, + /// The sm3-256 hash. + Sm3_256, + /// For extensibility. + OtherEncoding(Cow<'static, str>), +} + +impl KeyctlHash { + fn hash(&self) -> &str { + match *self { + KeyctlHash::Md4 => "md4", + KeyctlHash::Md5 => "md5", + KeyctlHash::Sha1 => "sha1", + KeyctlHash::Sha224 => "sha224", + KeyctlHash::Sha256 => "sha256", + KeyctlHash::Sha384 => "sha384", + KeyctlHash::Sha512 => "sha512", + KeyctlHash::RipeMd128 => "rmd128", + KeyctlHash::RipeMd160 => "rmd160", + KeyctlHash::RipeMd256 => "rmd256", + KeyctlHash::RipeMd320 => "rmd320", + KeyctlHash::Wp256 => "wp256", + KeyctlHash::Wp384 => "wp384", + KeyctlHash::Wp512 => "wp512", + KeyctlHash::Tgr128 => "tgr128", + KeyctlHash::Tgr160 => "tgr160", + KeyctlHash::Tgr192 => "tgr192", + KeyctlHash::Sm3_256 => "sm3-256", + KeyctlHash::OtherEncoding(ref s) => &s, + } + } +} + impl Key { /// Instantiate a key from an ID. /// @@ -667,6 +737,60 @@ impl Key { unsafe { buffer.set_len(actual_sz as usize) }; Ok(buffer) } + + /// Compute a key from a Diffie-Hellman shared secret. + /// + /// The `base` key contains the remote public key to create a share secret which is then + /// processed using `hash`. + /// + /// See [SP800-56A][] for details. + /// + /// [SP800-56A]: https://csrc.nist.gov/publications/detail/sp/800-56a/revised/archive/2007-03-14 + pub fn compute_dh_kdf( + private: &Key, + prime: &Key, + base: &Key, + hash: KeyctlHash, + other: Option, + ) -> Result> + where + O: AsRef<[u8]>, + { + let hash_cstr = CString::new(hash.hash()).unwrap(); + let (other_ptr, other_sz) = if let Some(other) = other { + let other = other.as_ref(); + (other.as_ptr() as *const libc::c_void, other.len()) + } else { + (ptr::null_mut() as *const libc::c_void, 0) + }; + let sz = check_call_ret(unsafe { + keyctl_dh_compute_kdf( + private.id, + prime.id, + base.id, + hash_cstr.as_ptr(), + other_ptr, + other_sz, + ptr::null_mut() as *mut libc::c_char, + 0, + ) + })?; + let mut buffer = Vec::with_capacity(sz as usize); + let actual_sz = check_call_ret(unsafe { + keyctl_dh_compute_kdf( + private.id, + prime.id, + base.id, + hash_cstr.as_ptr(), + other_ptr, + other_sz, + buffer.as_mut_ptr() as *mut libc::c_char, + sz as usize, + ) + })?; + unsafe { buffer.set_len(actual_sz as usize) }; + Ok(buffer) + } } /// Structure representing the metadata about a key or keyring. From 7022ec83a9030f835765d74a6bdcef7e207eaad2 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Thu, 14 Feb 2019 14:33:58 -0500 Subject: [PATCH 2/3] pkey: expose pkey functionality --- libkeyutils-sys/src/constants.rs | 7 +- libkeyutils-sys/src/functions.rs | 43 ++++++- libkeyutils-sys/src/types.rs | 29 +++++ src/api.rs | 195 ++++++++++++++++++++++++++++++- src/constants.rs | 32 +++++ 5 files changed, 303 insertions(+), 3 deletions(-) diff --git a/libkeyutils-sys/src/constants.rs b/libkeyutils-sys/src/constants.rs index 3a8ea0c..27ecef5 100644 --- a/libkeyutils-sys/src/constants.rs +++ b/libkeyutils-sys/src/constants.rs @@ -27,7 +27,7 @@ // Ignore rustfmt changes in here. The horizontal alignment is too useful to give up. #![cfg_attr(rustfmt, rustfmt_skip)] -use types::{key_perm_t, key_serial_t}; +use types::{key_perm_t, key_serial_t, _keyctl_support_t}; pub const KEY_TYPE_USER: &str = "user"; pub const KEY_TYPE_LOGON: &str = "logon"; @@ -80,3 +80,8 @@ pub const KEY_OTH_SEARCH: key_perm_t = 0x0000_0008; pub const KEY_OTH_LINK: key_perm_t = 0x0000_0010; pub const KEY_OTH_SETATTR: key_perm_t = 0x0000_0020; pub const KEY_OTH_ALL: key_perm_t = 0x0000_003f; + +pub const KEYCTL_SUPPORTS_ENCRYPT: _keyctl_support_t = 0x01; +pub const KEYCTL_SUPPORTS_DECRYPT: _keyctl_support_t = 0x02; +pub const KEYCTL_SUPPORTS_SIGN: _keyctl_support_t = 0x04; +pub const KEYCTL_SUPPORTS_VERIFY: _keyctl_support_t = 0x08; diff --git a/libkeyutils-sys/src/functions.rs b/libkeyutils-sys/src/functions.rs index 31cd144..18b7859 100644 --- a/libkeyutils-sys/src/functions.rs +++ b/libkeyutils-sys/src/functions.rs @@ -26,7 +26,7 @@ use crates::libc; -use types::{key_perm_t, key_serial_t}; +use types::{key_perm_t, key_serial_t, keyctl_pkey_query}; #[rustfmt::skip] extern "C" { @@ -156,4 +156,45 @@ extern "C" { buffer: *mut libc::c_char, buflen: libc::size_t) -> libc::c_long; + pub fn keyctl_pkey_query( + key: key_serial_t, + password: key_serial_t, + info: *mut keyctl_pkey_query) + -> libc::c_long; + pub fn keyctl_pkey_encrypt( + key: key_serial_t, + password: key_serial_t, + info: *const libc::c_char, + data: *const libc::c_void, + data_len: libc::size_t, + enc: *mut libc::c_void, + enc_len: libc::size_t) + -> libc::c_long; + pub fn keyctl_pkey_decrypt( + key: key_serial_t, + password: key_serial_t, + info: *const libc::c_char, + enc: *const libc::c_void, + enc_len: libc::size_t, + data: *mut libc::c_void, + data_len: libc::size_t) + -> libc::c_long; + pub fn keyctl_pkey_sign( + key: key_serial_t, + password: key_serial_t, + info: *const libc::c_char, + data: *const libc::c_void, + data_len: libc::size_t, + sig: *mut libc::c_void, + sig_len: libc::size_t) + -> libc::c_long; + pub fn keyctl_pkey_verify( + key: key_serial_t, + password: key_serial_t, + info: *const libc::c_char, + data: *const libc::c_void, + data_len: libc::size_t, + sig: *const libc::c_void, + sig_len: libc::size_t) + -> libc::c_long; } diff --git a/libkeyutils-sys/src/types.rs b/libkeyutils-sys/src/types.rs index 25b994b..caab558 100644 --- a/libkeyutils-sys/src/types.rs +++ b/libkeyutils-sys/src/types.rs @@ -30,3 +30,32 @@ use crates::libc; pub type key_serial_t = libc::int32_t; pub type key_perm_t = libc::uint32_t; + +// No actual type in the API, but create one for simplicity. +pub type _keyctl_support_t = libc::uint32_t; + +#[rustfmt::skip] +#[repr(C)] +pub struct keyctl_pkey_query { + pub supported_ops: libc::uint32_t, + pub key_size: libc::uint32_t, + pub max_data_size: libc::uint16_t, + pub max_sig_size: libc::uint16_t, + pub max_enc_size: libc::uint16_t, + pub max_dec_size: libc::uint16_t, + __spare: [libc::uint32_t; 10], +} + +impl keyctl_pkey_query { + pub fn new() -> Self { + keyctl_pkey_query { + supported_ops: 0, + key_size: 0, + max_data_size: 0, + max_sig_size: 0, + max_enc_size: 0, + max_dec_size: 0, + __spare: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + } + } +} diff --git a/src/api.rs b/src/api.rs index 18c87a2..ee344a6 100644 --- a/src/api.rs +++ b/src/api.rs @@ -36,7 +36,9 @@ use crates::errno; use crates::libc; use crates::libkeyutils_sys::*; -use constants::{DefaultKeyring, KeyPermissions, KeyringSerial, Permission, SpecialKeyring}; +use constants::{ + DefaultKeyring, KeyPermissions, KeyctlSupportFlags, KeyringSerial, Permission, SpecialKeyring, +}; use keytype::*; use keytypes; @@ -502,6 +504,64 @@ pub struct Key { id: KeyringSerial, } +/// Structure to store results from a query on optional feature support for a key. +#[derive(Debug, Clone, Copy)] +pub struct KeySupportInfo { + /// Features supported by the key. + pub supported_ops: KeyctlSupportFlags, + /// The size of the key (in bits). + pub key_size: u32, + /// The maximum size of a data blob which may be signed. + pub max_data_size: u16, + /// The maximum size of a signature blob. + pub max_sig_size: u16, + /// The maximum size of a blob to be encrypted. + pub max_enc_size: u16, + /// The maximum size of a blob to be decrypted. + pub max_dec_size: u16, +} + +impl KeySupportInfo { + fn from_c(c_info: keyctl_pkey_query) -> Self { + KeySupportInfo { + supported_ops: c_info.supported_ops, + key_size: c_info.key_size, + max_data_size: c_info.max_data_size, + max_sig_size: c_info.max_sig_size, + max_enc_size: c_info.max_enc_size, + max_dec_size: c_info.max_dec_size, + } + } +} + +/// Encodings supported by the kernel. +#[derive(Debug, Clone)] +// #[non_exhaustive] +pub enum KeyctlEncoding { + /// The RSASSA-PKCS1-v1.5 encoding. + RsassaPkcs1V15, + /// The RSAES-PKCS1-v1.5 encoding. + RsaesPkcs1V15, + /// The RSASSA-PSS encoding. + RsassaPss, + /// The RSAES-OAEP encoding. + RsaesOaep, + /// For extensibility. + OtherEncoding(Cow<'static, str>), +} + +impl KeyctlEncoding { + fn encoding(&self) -> &str { + match *self { + KeyctlEncoding::RsassaPkcs1V15 => "pkcs1", + KeyctlEncoding::RsaesPkcs1V15 => "pkcs1", + KeyctlEncoding::RsassaPss => "pss", + KeyctlEncoding::RsaesOaep => "oaep", + KeyctlEncoding::OtherEncoding(ref s) => &s, + } + } +} + /// Hashes supported by the kernel. #[derive(Debug, Clone)] // #[non_exhaustive] @@ -572,6 +632,27 @@ impl KeyctlHash { } } +/// Options for output from public key functions (encryption, decryption, signing, and verifying). +pub struct PublicKeyOptions { + /// The encoding of the encrypted blob or the signature. + pub encoding: Option, + /// Hash algorithm to use (if the encoding uses it). + pub hash: Option, +} + +impl PublicKeyOptions { + fn info(&self) -> String { + let options = [ + ("enc", self.encoding.as_ref().map(KeyctlEncoding::encoding)), + ("hash", self.hash.as_ref().map(KeyctlHash::hash)), + ] + .iter() + .map(|&(key, value)| value.map_or_else(String::new, |v| format!("{}={}", key, v))) + .collect::>(); + options.join(" ").trim().to_owned() + } +} + impl Key { /// Instantiate a key from an ID. /// @@ -791,6 +872,118 @@ impl Key { unsafe { buffer.set_len(actual_sz as usize) }; Ok(buffer) } + + /// Query which optionally supported features may be used by the key. + pub fn query_support(&self, password: &Option) -> Result { + let mut info = keyctl_pkey_query::new(); + let password_id = password.as_ref().map_or(0, |pass| pass.id); + try!(check_call_ret(unsafe { + keyctl_pkey_query(self.id, password_id, &mut info) + })); + Ok(KeySupportInfo::from_c(info)) + } + + /// Encrypt data using the key. + pub fn encrypt( + &self, + password: &Option, + options: &PublicKeyOptions, + data: &[u8], + ) -> Result> { + let password_id = password.as_ref().map_or(0, |pass| pass.id); + let info_cstr = CString::new(options.info()).unwrap(); + let sz = try!(self.query_support(password)).max_enc_size; + let mut buffer = Vec::with_capacity(sz as usize); + let actual_sz = try!(check_call_ret(unsafe { + keyctl_pkey_encrypt( + self.id, + password_id, + info_cstr.as_ptr(), + data.as_ptr() as *const libc::c_void, + data.len(), + buffer.as_mut_ptr() as *mut libc::c_void, + buffer.len(), + ) + })); + unsafe { buffer.set_len(actual_sz as usize) }; + Ok(buffer) + } + + /// Decrypt data using the key. + pub fn decrypt( + &self, + password: &Option, + options: &PublicKeyOptions, + data: &[u8], + ) -> Result> { + let password_id = password.as_ref().map_or(0, |pass| pass.id); + let info_cstr = CString::new(options.info()).unwrap(); + let sz = try!(self.query_support(password)).max_dec_size; + let mut buffer = Vec::with_capacity(sz as usize); + let actual_sz = try!(check_call_ret(unsafe { + keyctl_pkey_decrypt( + self.id, + password_id, + info_cstr.as_ptr(), + data.as_ptr() as *const libc::c_void, + data.len(), + buffer.as_mut_ptr() as *mut libc::c_void, + buffer.len(), + ) + })); + unsafe { buffer.set_len(actual_sz as usize) }; + Ok(buffer) + } + + /// Sign data using the key. + pub fn sign( + &self, + password: &Option, + options: &PublicKeyOptions, + data: &[u8], + ) -> Result> { + let password_id = password.as_ref().map_or(0, |pass| pass.id); + let info_cstr = CString::new(options.info()).unwrap(); + let sz = try!(self.query_support(password)).max_sig_size; + let mut buffer = Vec::with_capacity(sz as usize); + let actual_sz = try!(check_call_ret(unsafe { + keyctl_pkey_sign( + self.id, + password_id, + info_cstr.as_ptr(), + data.as_ptr() as *const libc::c_void, + data.len(), + buffer.as_mut_ptr() as *mut libc::c_void, + buffer.len(), + ) + })); + unsafe { buffer.set_len(actual_sz as usize) }; + Ok(buffer) + } + + /// Verify a signature of the data using the key. + pub fn verify( + &self, + password: &Option, + options: &PublicKeyOptions, + data: &[u8], + signature: &[u8], + ) -> Result { + let password_id = password.as_ref().map_or(0, |pass| pass.id); + let info_cstr = CString::new(options.info()).unwrap(); + let res = try!(check_call_ret(unsafe { + keyctl_pkey_sign( + self.id, + password_id, + info_cstr.as_ptr(), + data.as_ptr() as *const libc::c_void, + data.len(), + signature.as_ptr() as *mut libc::c_void, + signature.len(), + ) + })); + Ok(res == 0) + } } /// Structure representing the metadata about a key or keyring. diff --git a/src/constants.rs b/src/constants.rs index e9eeddf..6a0ebd5 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -218,6 +218,21 @@ bitflags! { } } +/// They kernel type for representing support for optional features. +/// +/// Asymmetric keys might only support a limited set of operations. These flags indicate which +/// operations are available. +pub type KeyctlSupportFlags = u32; + +bitflags! { + struct KeyctlSupportFlag: KeyctlSupportFlags { + const SUPPORTS_ENCRYPT = 0x01; + const SUPPORTS_DECRYPT = 0x02; + const SUPPORTS_SIGN = 0x04; + const SUPPORTS_VERIFY = 0x08; + } +} + #[test] fn test_keyring_ids() { assert_eq!(SpecialKeyring::Thread.serial(), KEY_SPEC_THREAD_KEYRING); @@ -298,3 +313,20 @@ fn test_permission_bits() { assert_eq!(Permission::OTHER_SET_ATTRIBUTE.bits, KEY_OTH_SETATTR); assert_eq!(Permission::OTHER_ALL.bits, KEY_OTH_ALL); } + +#[test] +fn test_support_flags() { + assert_eq!( + KeyctlSupportFlag::SUPPORTS_ENCRYPT.bits, + KEYCTL_SUPPORTS_ENCRYPT, + ); + assert_eq!( + KeyctlSupportFlag::SUPPORTS_DECRYPT.bits, + KEYCTL_SUPPORTS_DECRYPT, + ); + assert_eq!(KeyctlSupportFlag::SUPPORTS_SIGN.bits, KEYCTL_SUPPORTS_SIGN); + assert_eq!( + KeyctlSupportFlag::SUPPORTS_VERIFY.bits, + KEYCTL_SUPPORTS_VERIFY, + ); +} From d9825a80e42e4bfae136bab48f0340886c87ce88 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Thu, 14 Feb 2019 14:44:48 -0500 Subject: [PATCH 3/3] constants: add non_exhaustive attr comments to keyring enums --- src/constants.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/constants.rs b/src/constants.rs index 6a0ebd5..bed00cc 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -27,6 +27,7 @@ use crates::libkeyutils_sys::*; /// Special keyrings predefined for a process. +// #[non_exhaustive] pub enum SpecialKeyring { /// A thread-specific keyring. Thread, @@ -63,6 +64,7 @@ impl SpecialKeyring { /// /// Keys which are implicitly required via syscalls and other operations are placed in the /// default keyring. +// #[non_exhaustive] pub enum DefaultKeyring { /// Do not change the default keyring. ///