David Drysdale | 747e593 | 2023-10-17 14:43:43 +0100 | [diff] [blame] | 1 | // Copyright 2022, The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
David Drysdale | 64e07e3 | 2022-06-10 14:45:45 +0100 | [diff] [blame] | 15 | //! Abstractions and related types for accessing cryptographic primitives |
| 16 | //! and related functionality. |
| 17 | |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 18 | // derive(N) generates a method that is missing a docstring. |
| 19 | #![allow(missing_docs)] |
| 20 | |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 21 | use crate::{km_err, vec_try, vec_try_with_capacity, Error, FallibleAllocExt}; |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 22 | use alloc::{ |
| 23 | format, |
| 24 | string::{String, ToString}, |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 25 | vec::Vec, |
| 26 | }; |
| 27 | use core::convert::{From, TryInto}; |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 28 | use enumn::N; |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 29 | use kmr_derive::AsCborValue; |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 30 | use kmr_wire::keymint::{Algorithm, Digest, EcCurve}; |
| 31 | use kmr_wire::{cbor, cbor_type_error, AsCborValue, CborError, KeySizeInBits, RsaExponent}; |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 32 | use log::error; |
Andrew Walbran | c6dd88f | 2023-12-19 11:09:05 +0000 | [diff] [blame] | 33 | use spki::SubjectPublicKeyInfoRef; |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 34 | use zeroize::ZeroizeOnDrop; |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 35 | |
| 36 | pub mod aes; |
| 37 | pub mod des; |
| 38 | pub mod ec; |
| 39 | pub mod hmac; |
David Drysdale | 64e07e3 | 2022-06-10 14:45:45 +0100 | [diff] [blame] | 40 | pub mod rsa; |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 41 | mod traits; |
| 42 | pub use traits::*; |
| 43 | |
| 44 | /// Size of SHA-256 output in bytes. |
| 45 | pub const SHA256_DIGEST_LEN: usize = 32; |
David Drysdale | 64e07e3 | 2022-06-10 14:45:45 +0100 | [diff] [blame] | 46 | |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 47 | /// Function that mimics `slice.to_vec()` but which detects allocation failures. This version emits |
| 48 | /// `CborError` (instead of the `Error` that `crate::try_to_vec` emits). |
| 49 | #[inline] |
| 50 | pub fn try_to_vec<T: Clone>(s: &[T]) -> Result<Vec<T>, CborError> { |
| 51 | let mut v = vec_try_with_capacity!(s.len()).map_err(|_e| CborError::AllocationFailed)?; |
| 52 | v.extend_from_slice(s); |
| 53 | Ok(v) |
| 54 | } |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 55 | |
| 56 | /// Milliseconds since an arbitrary epoch. |
| 57 | #[repr(transparent)] |
| 58 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 59 | pub struct MillisecondsSinceEpoch(pub i64); |
| 60 | |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 61 | impl From<MillisecondsSinceEpoch> for kmr_wire::secureclock::Timestamp { |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 62 | fn from(value: MillisecondsSinceEpoch) -> Self { |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 63 | kmr_wire::secureclock::Timestamp { milliseconds: value.0 } |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 64 | } |
| 65 | } |
| 66 | |
| 67 | /// Information for key generation. |
| 68 | #[derive(Clone)] |
| 69 | pub enum KeyGenInfo { |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 70 | /// Generate an AES key of the given size. |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 71 | Aes(aes::Variant), |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 72 | /// Generate a 3-DES key. |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 73 | TripleDes, |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 74 | /// Generate an HMAC key of the given size. |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 75 | Hmac(KeySizeInBits), |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 76 | /// Generate an RSA keypair of the given size using the given exponent. |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 77 | Rsa(KeySizeInBits, RsaExponent), |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 78 | /// Generate a NIST EC keypair using the given curve. |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 79 | NistEc(ec::NistCurve), |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 80 | /// Generate an Ed25519 keypair. |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 81 | Ed25519, |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 82 | /// Generate an X25519 keypair. |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 83 | X25519, |
| 84 | } |
| 85 | |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 86 | /// Type of elliptic curve. |
| 87 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, AsCborValue, N)] |
| 88 | #[repr(i32)] |
| 89 | pub enum CurveType { |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 90 | /// NIST curve. |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 91 | Nist = 0, |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 92 | /// EdDSA curve. |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 93 | EdDsa = 1, |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 94 | /// XDH curve. |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 95 | Xdh = 2, |
| 96 | } |
| 97 | |
| 98 | /// Raw key material used for deriving other keys. |
David Drysdale | 3b3840d | 2022-10-30 17:28:14 +0000 | [diff] [blame] | 99 | #[derive(PartialEq, Eq, ZeroizeOnDrop)] |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 100 | pub struct RawKeyMaterial(pub Vec<u8>); |
| 101 | |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 102 | /// Opaque key material whose structure is only known/accessible to the crypto implementation. |
| 103 | /// The contents of this are assumed to be encrypted (and so are not `ZeroizeOnDrop`). |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 104 | #[derive(Clone, PartialEq, Eq)] |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 105 | pub struct OpaqueKeyMaterial(pub Vec<u8>); |
| 106 | |
| 107 | /// Wrapper that holds either a key of explicit type `T`, or an opaque blob of key material. |
| 108 | #[derive(Clone, PartialEq, Eq)] |
| 109 | pub enum OpaqueOr<T> { |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 110 | /// Explicit key material of the given type, available in plaintext. |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 111 | Explicit(T), |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 112 | /// Opaque key material, either encrypted or an opaque key handle. |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 113 | Opaque(OpaqueKeyMaterial), |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 114 | } |
| 115 | |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 116 | /// Macro to provide `impl From<SomeKey> for OpaqueOr<SomeKey>`, so that explicit key material |
| 117 | /// automatically converts into the equivalent `OpaqueOr` variant. |
| 118 | macro_rules! opaque_from_key { |
| 119 | { $t:ty } => { |
| 120 | impl From<$t> for OpaqueOr<$t> { |
| 121 | fn from(k: $t) -> Self { |
| 122 | Self::Explicit(k) |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | opaque_from_key!(aes::Key); |
| 129 | opaque_from_key!(des::Key); |
| 130 | opaque_from_key!(hmac::Key); |
| 131 | opaque_from_key!(rsa::Key); |
| 132 | opaque_from_key!(ec::Key); |
| 133 | |
| 134 | impl<T> From<OpaqueKeyMaterial> for OpaqueOr<T> { |
| 135 | fn from(k: OpaqueKeyMaterial) -> Self { |
| 136 | Self::Opaque(k) |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | /// Key material that is held in plaintext (or is alternatively an opaque blob that is only |
| 141 | /// known/accessible to the crypto implementation, indicated by the `OpaqueOr::Opaque` variant). |
| 142 | #[derive(Clone, PartialEq, Eq)] |
| 143 | pub enum KeyMaterial { |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 144 | /// AES symmetric key. |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 145 | Aes(OpaqueOr<aes::Key>), |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 146 | /// 3-DES symmetric key. |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 147 | TripleDes(OpaqueOr<des::Key>), |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 148 | /// HMAC symmetric key. |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 149 | Hmac(OpaqueOr<hmac::Key>), |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 150 | /// RSA asymmetric key. |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 151 | Rsa(OpaqueOr<rsa::Key>), |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 152 | /// Elliptic curve asymmetric key. |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 153 | Ec(EcCurve, CurveType, OpaqueOr<ec::Key>), |
| 154 | } |
| 155 | |
| 156 | /// Macro that extracts the explicit key from an [`OpaqueOr`] wrapper. |
| 157 | #[macro_export] |
| 158 | macro_rules! explicit { |
| 159 | { $key:expr } => { |
| 160 | if let $crate::crypto::OpaqueOr::Explicit(k) = $key { |
| 161 | Ok(k) |
| 162 | } else { |
Tri Vo | 9e4b447 | 2023-08-30 11:21:48 -0400 | [diff] [blame] | 163 | Err($crate::km_err!(IncompatibleKeyFormat, "Expected explicit key but found opaque key!")) |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 164 | } |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | impl KeyMaterial { |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 169 | /// Indicate whether the key material is for an asymmetric key. |
| 170 | pub fn is_asymmetric(&self) -> bool { |
| 171 | match self { |
| 172 | Self::Aes(_) | Self::TripleDes(_) | Self::Hmac(_) => false, |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 173 | Self::Ec(_, _, _) | Self::Rsa(_) => true, |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 174 | } |
| 175 | } |
| 176 | |
| 177 | /// Indicate whether the key material is for a symmetric key. |
| 178 | pub fn is_symmetric(&self) -> bool { |
| 179 | !self.is_asymmetric() |
| 180 | } |
| 181 | |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 182 | /// Return the public key information as an ASN.1 DER encodable `SubjectPublicKeyInfo`, as |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 183 | /// described in RFC 5280 section 4.1. |
| 184 | /// |
| 185 | /// ```asn1 |
| 186 | /// SubjectPublicKeyInfo ::= SEQUENCE { |
| 187 | /// algorithm AlgorithmIdentifier, |
| 188 | /// subjectPublicKey BIT STRING } |
| 189 | /// |
| 190 | /// AlgorithmIdentifier ::= SEQUENCE { |
| 191 | /// algorithm OBJECT IDENTIFIER, |
| 192 | /// parameters ANY DEFINED BY algorithm OPTIONAL } |
| 193 | /// ``` |
| 194 | /// |
| 195 | /// Returns `None` for a symmetric key. |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 196 | pub fn subject_public_key_info<'a>( |
| 197 | &'a self, |
| 198 | buf: &'a mut Vec<u8>, |
| 199 | ec: &dyn Ec, |
Hasini Gunasinghe | d8518eb | 2022-11-16 23:50:22 +0000 | [diff] [blame] | 200 | rsa: &dyn Rsa, |
Andrew Walbran | c6dd88f | 2023-12-19 11:09:05 +0000 | [diff] [blame] | 201 | ) -> Result<Option<SubjectPublicKeyInfoRef<'a>>, Error> { |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 202 | Ok(match self { |
Hasini Gunasinghe | d8518eb | 2022-11-16 23:50:22 +0000 | [diff] [blame] | 203 | Self::Rsa(key) => Some(key.subject_public_key_info(buf, rsa)?), |
Hasini Gunasinghe | c939a9b | 2022-11-17 02:45:22 +0000 | [diff] [blame] | 204 | Self::Ec(curve, curve_type, key) => { |
| 205 | Some(key.subject_public_key_info(buf, ec, curve, curve_type)?) |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 206 | } |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 207 | _ => None, |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 208 | }) |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 209 | } |
| 210 | } |
| 211 | |
| 212 | /// Manual implementation of [`Debug`] that skips emitting plaintext key material. |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 213 | impl core::fmt::Debug for KeyMaterial { |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 214 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| 215 | match self { |
| 216 | Self::Aes(k) => match k { |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 217 | OpaqueOr::Explicit(aes::Key::Aes128(_)) => f.write_str("Aes128(...)"), |
| 218 | OpaqueOr::Explicit(aes::Key::Aes192(_)) => f.write_str("Aes192(...)"), |
| 219 | OpaqueOr::Explicit(aes::Key::Aes256(_)) => f.write_str("Aes256(...)"), |
| 220 | OpaqueOr::Opaque(_) => f.write_str("Aes(opaque)"), |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 221 | }, |
| 222 | Self::TripleDes(_) => f.write_str("TripleDes(...)"), |
| 223 | Self::Hmac(_) => f.write_str("Hmac(...)"), |
| 224 | Self::Rsa(_) => f.write_str("Rsa(...)"), |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 225 | Self::Ec(c, _, _) => f.write_fmt(format_args!("Ec({:?}, ...)", c)), |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 226 | } |
| 227 | } |
| 228 | } |
| 229 | |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 230 | impl AsCborValue for KeyMaterial { |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 231 | fn from_cbor_value(value: cbor::value::Value) -> Result<Self, CborError> { |
| 232 | let mut a = match value { |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 233 | cbor::value::Value::Array(a) if a.len() == 3 => a, |
| 234 | _ => return cbor_type_error(&value, "arr len 3"), |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 235 | }; |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 236 | let raw_key_value = a.remove(2); |
| 237 | let opaque = match a.remove(1) { |
| 238 | cbor::value::Value::Bool(b) => b, |
| 239 | v => return cbor_type_error(&v, "bool"), |
| 240 | }; |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 241 | let algo: i32 = match a.remove(0) { |
| 242 | cbor::value::Value::Integer(i) => i.try_into()?, |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 243 | v => return cbor_type_error(&v, "uint"), |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 244 | }; |
| 245 | |
| 246 | match algo { |
| 247 | x if x == Algorithm::Aes as i32 => { |
| 248 | let raw_key = <Vec<u8>>::from_cbor_value(raw_key_value)?; |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 249 | if opaque { |
| 250 | Ok(Self::Aes(OpaqueKeyMaterial(raw_key).into())) |
| 251 | } else { |
| 252 | match aes::Key::new(raw_key) { |
| 253 | Ok(k) => Ok(Self::Aes(k.into())), |
| 254 | Err(_e) => Err(CborError::UnexpectedItem("bstr", "bstr len 16/24/32")), |
| 255 | } |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 256 | } |
| 257 | } |
| 258 | x if x == Algorithm::TripleDes as i32 => { |
| 259 | let raw_key = <Vec<u8>>::from_cbor_value(raw_key_value)?; |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 260 | if opaque { |
| 261 | Ok(Self::TripleDes(OpaqueKeyMaterial(raw_key).into())) |
| 262 | } else { |
| 263 | Ok(Self::TripleDes( |
| 264 | des::Key( |
| 265 | raw_key |
| 266 | .try_into() |
| 267 | .map_err(|_e| CborError::UnexpectedItem("bstr", "bstr len 24"))?, |
| 268 | ) |
| 269 | .into(), |
| 270 | )) |
| 271 | } |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 272 | } |
| 273 | x if x == Algorithm::Hmac as i32 => { |
| 274 | let raw_key = <Vec<u8>>::from_cbor_value(raw_key_value)?; |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 275 | if opaque { |
| 276 | Ok(Self::Hmac(OpaqueKeyMaterial(raw_key).into())) |
| 277 | } else { |
| 278 | Ok(Self::Hmac(hmac::Key(raw_key).into())) |
| 279 | } |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 280 | } |
| 281 | x if x == Algorithm::Rsa as i32 => { |
| 282 | let raw_key = <Vec<u8>>::from_cbor_value(raw_key_value)?; |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 283 | if opaque { |
| 284 | Ok(Self::Rsa(OpaqueKeyMaterial(raw_key).into())) |
| 285 | } else { |
| 286 | Ok(Self::Rsa(rsa::Key(raw_key).into())) |
| 287 | } |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 288 | } |
| 289 | x if x == Algorithm::Ec as i32 => { |
| 290 | let mut a = match raw_key_value { |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 291 | cbor::value::Value::Array(a) if a.len() == 3 => a, |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 292 | _ => return cbor_type_error(&raw_key_value, "arr len 2"), |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 293 | }; |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 294 | let raw_key_value = a.remove(2); |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 295 | let raw_key = <Vec<u8>>::from_cbor_value(raw_key_value)?; |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 296 | let curve_type = CurveType::from_cbor_value(a.remove(1))?; |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 297 | let curve = <EcCurve>::from_cbor_value(a.remove(0))?; |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 298 | if opaque { |
| 299 | Ok(Self::Ec(curve, curve_type, OpaqueKeyMaterial(raw_key).into())) |
| 300 | } else { |
| 301 | let key = match (curve, curve_type) { |
| 302 | (EcCurve::P224, CurveType::Nist) => ec::Key::P224(ec::NistKey(raw_key)), |
| 303 | (EcCurve::P256, CurveType::Nist) => ec::Key::P256(ec::NistKey(raw_key)), |
| 304 | (EcCurve::P384, CurveType::Nist) => ec::Key::P384(ec::NistKey(raw_key)), |
| 305 | (EcCurve::P521, CurveType::Nist) => ec::Key::P521(ec::NistKey(raw_key)), |
| 306 | (EcCurve::Curve25519, CurveType::EdDsa) => { |
| 307 | let key = raw_key.try_into().map_err(|_e| { |
| 308 | error!("decoding Ed25519 key of incorrect len"); |
| 309 | CborError::OutOfRangeIntegerValue |
| 310 | })?; |
| 311 | ec::Key::Ed25519(ec::Ed25519Key(key)) |
| 312 | } |
| 313 | (EcCurve::Curve25519, CurveType::Xdh) => { |
| 314 | let key = raw_key.try_into().map_err(|_e| { |
| 315 | error!("decoding X25519 key of incorrect len"); |
| 316 | CborError::OutOfRangeIntegerValue |
| 317 | })?; |
| 318 | ec::Key::X25519(ec::X25519Key(key)) |
| 319 | } |
| 320 | (_, _) => { |
| 321 | error!("Unexpected EC combination ({:?}, {:?})", curve, curve_type); |
| 322 | return Err(CborError::NonEnumValue); |
| 323 | } |
| 324 | }; |
| 325 | Ok(Self::Ec(curve, curve_type, key.into())) |
| 326 | } |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 327 | } |
| 328 | _ => Err(CborError::UnexpectedItem("unknown enum", "algo enum")), |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | fn to_cbor_value(self) -> Result<cbor::value::Value, CborError> { |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 333 | let cbor_alloc_err = |_e| CborError::AllocationFailed; |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 334 | Ok(cbor::value::Value::Array(match self { |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 335 | Self::Aes(OpaqueOr::Opaque(OpaqueKeyMaterial(k))) => vec_try![ |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 336 | cbor::value::Value::Integer((Algorithm::Aes as i32).into()), |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 337 | cbor::value::Value::Bool(true), |
| 338 | cbor::value::Value::Bytes(try_to_vec(&k)?), |
| 339 | ] |
| 340 | .map_err(cbor_alloc_err)?, |
| 341 | Self::TripleDes(OpaqueOr::Opaque(OpaqueKeyMaterial(k))) => vec_try![ |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 342 | cbor::value::Value::Integer((Algorithm::TripleDes as i32).into()), |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 343 | cbor::value::Value::Bool(true), |
| 344 | cbor::value::Value::Bytes(try_to_vec(&k)?), |
| 345 | ] |
| 346 | .map_err(cbor_alloc_err)?, |
| 347 | Self::Hmac(OpaqueOr::Opaque(OpaqueKeyMaterial(k))) => vec_try![ |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 348 | cbor::value::Value::Integer((Algorithm::Hmac as i32).into()), |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 349 | cbor::value::Value::Bool(true), |
| 350 | cbor::value::Value::Bytes(try_to_vec(&k)?), |
| 351 | ] |
| 352 | .map_err(cbor_alloc_err)?, |
| 353 | Self::Rsa(OpaqueOr::Opaque(OpaqueKeyMaterial(k))) => vec_try![ |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 354 | cbor::value::Value::Integer((Algorithm::Rsa as i32).into()), |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 355 | cbor::value::Value::Bool(true), |
| 356 | cbor::value::Value::Bytes(try_to_vec(&k)?), |
| 357 | ] |
| 358 | .map_err(cbor_alloc_err)?, |
| 359 | Self::Ec(curve, curve_type, OpaqueOr::Opaque(OpaqueKeyMaterial(k))) => vec_try![ |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 360 | cbor::value::Value::Integer((Algorithm::Ec as i32).into()), |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 361 | cbor::value::Value::Bool(true), |
| 362 | cbor::value::Value::Array( |
| 363 | vec_try![ |
| 364 | cbor::value::Value::Integer((curve as i32).into()), |
| 365 | cbor::value::Value::Integer((curve_type as i32).into()), |
| 366 | cbor::value::Value::Bytes(try_to_vec(&k)?), |
| 367 | ] |
| 368 | .map_err(cbor_alloc_err)? |
| 369 | ), |
| 370 | ] |
| 371 | .map_err(cbor_alloc_err)?, |
| 372 | |
| 373 | Self::Aes(OpaqueOr::Explicit(k)) => vec_try![ |
| 374 | cbor::value::Value::Integer((Algorithm::Aes as i32).into()), |
| 375 | cbor::value::Value::Bool(false), |
| 376 | match k { |
| 377 | aes::Key::Aes128(k) => cbor::value::Value::Bytes(try_to_vec(&k)?), |
| 378 | aes::Key::Aes192(k) => cbor::value::Value::Bytes(try_to_vec(&k)?), |
| 379 | aes::Key::Aes256(k) => cbor::value::Value::Bytes(try_to_vec(&k)?), |
| 380 | }, |
| 381 | ] |
| 382 | .map_err(cbor_alloc_err)?, |
| 383 | |
| 384 | Self::TripleDes(OpaqueOr::Explicit(k)) => vec_try![ |
| 385 | cbor::value::Value::Integer((Algorithm::TripleDes as i32).into()), |
| 386 | cbor::value::Value::Bool(false), |
| 387 | cbor::value::Value::Bytes(k.0.to_vec()), |
| 388 | ] |
| 389 | .map_err(cbor_alloc_err)?, |
| 390 | Self::Hmac(OpaqueOr::Explicit(k)) => vec_try![ |
| 391 | cbor::value::Value::Integer((Algorithm::Hmac as i32).into()), |
| 392 | cbor::value::Value::Bool(false), |
| 393 | cbor::value::Value::Bytes(k.0.clone()), |
| 394 | ] |
| 395 | .map_err(cbor_alloc_err)?, |
| 396 | Self::Rsa(OpaqueOr::Explicit(k)) => vec_try![ |
| 397 | cbor::value::Value::Integer((Algorithm::Rsa as i32).into()), |
| 398 | cbor::value::Value::Bool(false), |
| 399 | cbor::value::Value::Bytes(k.0.clone()), |
| 400 | ] |
| 401 | .map_err(cbor_alloc_err)?, |
| 402 | Self::Ec(curve, curve_type, OpaqueOr::Explicit(k)) => vec_try![ |
| 403 | cbor::value::Value::Integer((Algorithm::Ec as i32).into()), |
| 404 | cbor::value::Value::Bool(false), |
| 405 | cbor::value::Value::Array( |
| 406 | vec_try![ |
| 407 | cbor::value::Value::Integer((curve as i32).into()), |
| 408 | cbor::value::Value::Integer((curve_type as i32).into()), |
| 409 | cbor::value::Value::Bytes(k.private_key_bytes().to_vec()), |
| 410 | ] |
| 411 | .map_err(cbor_alloc_err)?, |
| 412 | ), |
| 413 | ] |
| 414 | .map_err(cbor_alloc_err)?, |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 415 | })) |
| 416 | } |
| 417 | |
| 418 | fn cddl_typename() -> Option<String> { |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 419 | Some("KeyMaterial".to_string()) |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 420 | } |
| 421 | |
| 422 | fn cddl_schema() -> Option<String> { |
| 423 | Some(format!( |
| 424 | "&( |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 425 | ; For each variant the `bool` second entry indicates whether the bstr for the key material |
| 426 | ; is opaque (true), or explicit (false). |
| 427 | [{}, bool, bstr], ; {} |
| 428 | [{}, bool, bstr], ; {} |
| 429 | [{}, bool, bstr], ; {} |
| 430 | ; An explicit RSA key is in the form of an ASN.1 DER encoding of a PKCS#1 `RSAPrivateKey` |
| 431 | ; structure, as specified by RFC 3447 sections A.1.2 and 3.2. |
| 432 | [{}, bool, bstr], ; {} |
| 433 | ; An explicit EC key for a NIST curve is in the form of an ASN.1 DER encoding of a |
| 434 | ; `ECPrivateKey` structure, as specified by RFC 5915 section 3. |
| 435 | ; An explicit EC key for curve 25519 is the raw key bytes. |
| 436 | [{}, bool, [EcCurve, CurveType, bstr]], ; {} |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 437 | )", |
| 438 | Algorithm::Aes as i32, |
| 439 | "Algorithm_Aes", |
| 440 | Algorithm::TripleDes as i32, |
| 441 | "Algorithm_TripleDes", |
| 442 | Algorithm::Hmac as i32, |
| 443 | "Algorithm_Hmac", |
| 444 | Algorithm::Rsa as i32, |
| 445 | "Algorithm_Rsa", |
| 446 | Algorithm::Ec as i32, |
| 447 | "Algorithm_Ec", |
| 448 | )) |
| 449 | } |
| 450 | } |
| 451 | |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 452 | /// Direction of cipher operation. |
| 453 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 454 | pub enum SymmetricOperation { |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 455 | /// Perform encryption. |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 456 | Encrypt, |
David Drysdale | 63e6ad6 | 2023-05-05 17:41:08 +0100 | [diff] [blame] | 457 | /// Perform decryption. |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 458 | Decrypt, |
| 459 | } |
| 460 | |
| 461 | /// Extract or generate a nonce of the given size. |
| 462 | pub fn nonce( |
| 463 | size: usize, |
| 464 | caller_nonce: Option<&Vec<u8>>, |
| 465 | rng: &mut dyn Rng, |
| 466 | ) -> Result<Vec<u8>, Error> { |
| 467 | match caller_nonce { |
| 468 | Some(n) => match n.len() { |
| 469 | l if l == size => Ok(n.clone()), |
| 470 | _ => Err(km_err!(InvalidNonce, "want {} byte nonce", size)), |
| 471 | }, |
| 472 | None => { |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 473 | let mut n = vec_try![0; size]?; |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 474 | rng.fill_bytes(&mut n); |
| 475 | Ok(n) |
| 476 | } |
| 477 | } |
| 478 | } |
| 479 | |
| 480 | /// Salt value used in HKDF if none provided. |
| 481 | const HKDF_EMPTY_SALT: [u8; SHA256_DIGEST_LEN] = [0; SHA256_DIGEST_LEN]; |
| 482 | |
| 483 | /// Convenience wrapper to perform one-shot HMAC-SHA256. |
Hasini Gunasinghe | 11c2982 | 2022-11-14 02:24:54 +0000 | [diff] [blame] | 484 | pub fn hmac_sha256(hmac: &dyn Hmac, key: &[u8], data: &[u8]) -> Result<Vec<u8>, Error> { |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 485 | let mut op = hmac.begin(hmac::Key(crate::try_to_vec(key)?).into(), Digest::Sha256)?; |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 486 | op.update(data)?; |
| 487 | op.finish() |
| 488 | } |
| 489 | |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 490 | /// Default implementation of [`Hkdf`] for any type implementing [`Hmac`]. |
| 491 | impl<T: Hmac> Hkdf for T { |
Orlando Arbildo | 97ee058 | 2023-01-13 17:05:26 +0000 | [diff] [blame] | 492 | fn extract(&self, mut salt: &[u8], ikm: &[u8]) -> Result<OpaqueOr<hmac::Key>, Error> { |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 493 | if salt.is_empty() { |
| 494 | salt = &HKDF_EMPTY_SALT[..]; |
| 495 | } |
| 496 | let prk = hmac_sha256(self, salt, ikm)?; |
Orlando Arbildo | 97ee058 | 2023-01-13 17:05:26 +0000 | [diff] [blame] | 497 | Ok(OpaqueOr::Explicit(hmac::Key::new(prk))) |
| 498 | } |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 499 | |
Orlando Arbildo | 97ee058 | 2023-01-13 17:05:26 +0000 | [diff] [blame] | 500 | fn expand( |
| 501 | &self, |
| 502 | prk: &OpaqueOr<hmac::Key>, |
| 503 | info: &[u8], |
| 504 | out_len: usize, |
| 505 | ) -> Result<Vec<u8>, Error> { |
| 506 | let prk = &explicit!(prk)?.0; |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 507 | let n = (out_len + SHA256_DIGEST_LEN - 1) / SHA256_DIGEST_LEN; |
| 508 | if n > 256 { |
Tri Vo | 9e4b447 | 2023-08-30 11:21:48 -0400 | [diff] [blame] | 509 | return Err(km_err!(InvalidArgument, "overflow in hkdf")); |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 510 | } |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 511 | let mut t = vec_try_with_capacity!(SHA256_DIGEST_LEN)?; |
| 512 | let mut okm = vec_try_with_capacity!(n * SHA256_DIGEST_LEN)?; |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 513 | let n = n as u8; |
| 514 | for idx in 0..n { |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 515 | let mut input = vec_try_with_capacity!(t.len() + info.len() + 1)?; |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 516 | input.extend_from_slice(&t); |
| 517 | input.extend_from_slice(info); |
| 518 | input.push(idx + 1); |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 519 | |
Orlando Arbildo | 97ee058 | 2023-01-13 17:05:26 +0000 | [diff] [blame] | 520 | t = hmac_sha256(self, prk, &input)?; |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 521 | okm.try_extend_from_slice(&t)?; |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 522 | } |
| 523 | okm.truncate(out_len); |
| 524 | Ok(okm) |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 525 | } |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 526 | } |
| 527 | |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 528 | /// Default implementation of [`Ckdf`] for any type implementing [`AesCmac`]. |
| 529 | impl<T: AesCmac> Ckdf for T { |
| 530 | fn ckdf( |
| 531 | &self, |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 532 | key: &OpaqueOr<aes::Key>, |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 533 | label: &[u8], |
| 534 | chunks: &[&[u8]], |
| 535 | out_len: usize, |
| 536 | ) -> Result<Vec<u8>, Error> { |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 537 | let key = explicit!(key)?; |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 538 | // Note: the variables i and l correspond to i and L in the standard. See page 12 of |
| 539 | // http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf. |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 540 | |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 541 | let blocks: u32 = ((out_len + aes::BLOCK_SIZE - 1) / aes::BLOCK_SIZE) as u32; |
| 542 | let l = (out_len * 8) as u32; // in bits |
| 543 | let net_order_l = l.to_be_bytes(); |
| 544 | let zero_byte: [u8; 1] = [0]; |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 545 | let mut output = vec_try![0; out_len]?; |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 546 | let mut output_pos = 0; |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 547 | |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 548 | for i in 1u32..=blocks { |
| 549 | // Data to mac is (i:u32 || label || 0x00:u8 || context || L:u32), with integers in |
| 550 | // network order. |
David Drysdale | 0b3938d | 2022-09-28 16:49:16 +0100 | [diff] [blame] | 551 | let mut op = self.begin(key.clone().into())?; |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 552 | let net_order_i = i.to_be_bytes(); |
| 553 | op.update(&net_order_i[..])?; |
| 554 | op.update(label)?; |
| 555 | op.update(&zero_byte[..])?; |
| 556 | for chunk in chunks { |
| 557 | op.update(chunk)?; |
| 558 | } |
| 559 | op.update(&net_order_l[..])?; |
| 560 | |
| 561 | let data = op.finish()?; |
| 562 | let copy_len = core::cmp::min(data.len(), output.len() - output_pos); |
| 563 | output[output_pos..output_pos + copy_len].clone_from_slice(&data[..copy_len]); |
| 564 | output_pos += copy_len; |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 565 | } |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 566 | if output_pos != output.len() { |
| 567 | return Err(km_err!( |
Tri Vo | 9e4b447 | 2023-08-30 11:21:48 -0400 | [diff] [blame] | 568 | InvalidArgument, |
David Drysdale | ed33ed1 | 2022-08-23 10:01:39 +0100 | [diff] [blame] | 569 | "finished at {} before end of output at {}", |
| 570 | output_pos, |
| 571 | output.len() |
| 572 | )); |
| 573 | } |
| 574 | Ok(output) |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 575 | } |
David Drysdale | 9f27bc9 | 2022-06-10 15:53:32 +0100 | [diff] [blame] | 576 | } |