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
15 //! BoringSSL-based implementation of elliptic curve functionality.
16 use crate::types::{EvpMdCtx, EvpPkeyCtx};
17 use crate::{cvt, cvt_p, digest_into_openssl, openssl_err, openssl_last_err, ossl};
18 use alloc::boxed::Box;
19 use alloc::vec::Vec;
20 #[cfg(soong)]
21 use bssl_sys as ffi;
22 use core::ops::DerefMut;
23 use core::ptr;
24 use foreign_types::ForeignType;
25 use kmr_common::{
26 crypto,
27 crypto::{ec, ec::Key, AccumulatingOperation, CurveType, OpaqueOr},
28 explicit, km_err, vec_try, Error, FallibleAllocExt,
29 };
30 use kmr_wire::{
31 keymint,
32 keymint::{Digest, EcCurve},
33 };
34 use openssl::hash::MessageDigest;
35
36 #[cfg(soong)]
private_key_from_der_for_group( der: &[u8], group: &openssl::ec::EcGroupRef, ) -> Result<openssl::ec::EcKey<openssl::pkey::Private>, openssl::error::ErrorStack>37 fn private_key_from_der_for_group(
38 der: &[u8],
39 group: &openssl::ec::EcGroupRef,
40 ) -> Result<openssl::ec::EcKey<openssl::pkey::Private>, openssl::error::ErrorStack> {
41 // This method is an Android modification to the rust-openssl crate.
42 openssl::ec::EcKey::private_key_from_der_for_group(der, group)
43 }
44
45 #[cfg(not(soong))]
private_key_from_der_for_group( der: &[u8], _group: &openssl::ec::EcGroupRef, ) -> Result<openssl::ec::EcKey<openssl::pkey::Private>, openssl::error::ErrorStack>46 fn private_key_from_der_for_group(
47 der: &[u8],
48 _group: &openssl::ec::EcGroupRef,
49 ) -> Result<openssl::ec::EcKey<openssl::pkey::Private>, openssl::error::ErrorStack> {
50 // This doesn't work if the encoded data is missing the curve.
51 openssl::ec::EcKey::private_key_from_der(der)
52 }
53
54 /// [`crypto::Ec`] implementation based on BoringSSL.
55 pub struct BoringEc {
56 /// Zero-sized private field to force use of [`default()`] for initialization.
57 _priv: core::marker::PhantomData<()>,
58 }
59
60 impl core::default::Default for BoringEc {
default() -> Self61 fn default() -> Self {
62 ffi::init();
63 Self { _priv: core::marker::PhantomData }
64 }
65 }
66
67 impl crypto::Ec for BoringEc {
generate_nist_key( &self, _rng: &mut dyn crypto::Rng, curve: ec::NistCurve, _params: &[keymint::KeyParam], ) -> Result<crypto::KeyMaterial, Error>68 fn generate_nist_key(
69 &self,
70 _rng: &mut dyn crypto::Rng,
71 curve: ec::NistCurve,
72 _params: &[keymint::KeyParam],
73 ) -> Result<crypto::KeyMaterial, Error> {
74 let ec_key = ossl!(openssl::ec::EcKey::<openssl::pkey::Private>::generate(
75 nist_curve_to_group(curve)?.as_ref()
76 ))?;
77 let nist_key = ec::NistKey(ossl!(ec_key.private_key_to_der())?);
78 let key = match curve {
79 ec::NistCurve::P224 => Key::P224(nist_key),
80 ec::NistCurve::P256 => Key::P256(nist_key),
81 ec::NistCurve::P384 => Key::P384(nist_key),
82 ec::NistCurve::P521 => Key::P521(nist_key),
83 };
84 Ok(crypto::KeyMaterial::Ec(curve.into(), CurveType::Nist, key.into()))
85 }
86
generate_ed25519_key( &self, _rng: &mut dyn crypto::Rng, _params: &[keymint::KeyParam], ) -> Result<crypto::KeyMaterial, Error>87 fn generate_ed25519_key(
88 &self,
89 _rng: &mut dyn crypto::Rng,
90 _params: &[keymint::KeyParam],
91 ) -> Result<crypto::KeyMaterial, Error> {
92 let pkey = ossl!(openssl::pkey::PKey::generate_ed25519())?;
93 let key = ossl!(pkey.raw_private_key())?;
94 let key: [u8; ec::CURVE25519_PRIV_KEY_LEN] = key.try_into().map_err(|e| {
95 km_err!(UnsupportedKeySize, "generated Ed25519 key of unexpected size: {:?}", e)
96 })?;
97 let key = Key::Ed25519(ec::Ed25519Key(key));
98 Ok(crypto::KeyMaterial::Ec(EcCurve::Curve25519, CurveType::EdDsa, key.into()))
99 }
100
generate_x25519_key( &self, _rng: &mut dyn crypto::Rng, _params: &[keymint::KeyParam], ) -> Result<crypto::KeyMaterial, Error>101 fn generate_x25519_key(
102 &self,
103 _rng: &mut dyn crypto::Rng,
104 _params: &[keymint::KeyParam],
105 ) -> Result<crypto::KeyMaterial, Error> {
106 let pkey = ossl!(openssl::pkey::PKey::generate_x25519())?;
107 let key = ossl!(pkey.raw_private_key())?;
108 let key: [u8; ec::CURVE25519_PRIV_KEY_LEN] = key.try_into().map_err(|e| {
109 km_err!(UnsupportedKeySize, "generated X25519 key of unexpected size: {:?}", e)
110 })?;
111 let key = Key::X25519(ec::X25519Key(key));
112 Ok(crypto::KeyMaterial::Ec(EcCurve::Curve25519, CurveType::Xdh, key.into()))
113 }
114
nist_public_key(&self, key: &ec::NistKey, curve: ec::NistCurve) -> Result<Vec<u8>, Error>115 fn nist_public_key(&self, key: &ec::NistKey, curve: ec::NistCurve) -> Result<Vec<u8>, Error> {
116 let group = nist_curve_to_group(curve)?;
117 let ec_key = ossl!(private_key_from_der_for_group(&key.0, group.as_ref()))?;
118 let pt = ec_key.public_key();
119 let mut bn_ctx = ossl!(openssl::bn::BigNumContext::new())?;
120 ossl!(pt.to_bytes(
121 group.as_ref(),
122 openssl::ec::PointConversionForm::UNCOMPRESSED,
123 bn_ctx.deref_mut()
124 ))
125 }
126
ed25519_public_key(&self, key: &ec::Ed25519Key) -> Result<Vec<u8>, Error>127 fn ed25519_public_key(&self, key: &ec::Ed25519Key) -> Result<Vec<u8>, Error> {
128 let pkey = ossl!(openssl::pkey::PKey::private_key_from_raw_bytes(
129 &key.0,
130 openssl::pkey::Id::ED25519
131 ))?;
132 ossl!(pkey.raw_public_key())
133 }
134
x25519_public_key(&self, key: &ec::X25519Key) -> Result<Vec<u8>, Error>135 fn x25519_public_key(&self, key: &ec::X25519Key) -> Result<Vec<u8>, Error> {
136 let pkey = ossl!(openssl::pkey::PKey::private_key_from_raw_bytes(
137 &key.0,
138 openssl::pkey::Id::X25519
139 ))?;
140 ossl!(pkey.raw_public_key())
141 }
142
begin_agree(&self, key: OpaqueOr<Key>) -> Result<Box<dyn AccumulatingOperation>, Error>143 fn begin_agree(&self, key: OpaqueOr<Key>) -> Result<Box<dyn AccumulatingOperation>, Error> {
144 let key = explicit!(key)?;
145 // Maximum size for a `SubjectPublicKeyInfo` that holds an EC public key is:
146 //
147 // 30 LL SEQUENCE + len (SubjectPublicKeyInfo)
148 // 30 LL SEQUENCE + len (AlgorithmIdentifier)
149 // 06 07 OID + len
150 // 2a8648ce3d0201 (ecPublicKey OID)
151 // 06 08 OID + len
152 // 2a8648ce3d030107 (P-256 curve OID, which is the longest)
153 // 03 42 BIT STRING + len
154 // 00 zero pad bits
155 // 04 uncompressed
156 // ... 66 bytes of P-521 X coordinate
157 // ... 66 bytes of P-521 Y coordinate
158 //
159 // Round up a bit just in case.
160 let max_size = 164;
161 Ok(Box::new(BoringEcAgreeOperation { key, pending_input: Vec::new(), max_size }))
162 }
163
begin_sign( &self, key: OpaqueOr<Key>, digest: Digest, ) -> Result<Box<dyn AccumulatingOperation>, Error>164 fn begin_sign(
165 &self,
166 key: OpaqueOr<Key>,
167 digest: Digest,
168 ) -> Result<Box<dyn AccumulatingOperation>, Error> {
169 let key = explicit!(key)?;
170 let curve = key.curve();
171 match key {
172 Key::P224(key) | Key::P256(key) | Key::P384(key) | Key::P521(key) => {
173 let curve = ec::NistCurve::try_from(curve)?;
174 if let Some(digest) = digest_into_openssl(digest) {
175 Ok(Box::new(BoringEcDigestSignOperation::new(key, curve, digest)?))
176 } else {
177 Ok(Box::new(BoringEcUndigestSignOperation::new(key, curve)?))
178 }
179 }
180 Key::Ed25519(key) => Ok(Box::new(BoringEd25519SignOperation::new(key)?)),
181 Key::X25519(_) => {
182 Err(km_err!(IncompatibleAlgorithm, "X25519 key not valid for signing"))
183 }
184 }
185 }
186 }
187
188 /// [`crypto::EcAgreeOperation`] based on BoringSSL.
189 pub struct BoringEcAgreeOperation {
190 key: Key,
191 pending_input: Vec<u8>, // Limited to `max_size` below.
192 // Size of a `SubjectPublicKeyInfo` holding peer public key.
193 max_size: usize,
194 }
195
196 impl crypto::AccumulatingOperation for BoringEcAgreeOperation {
max_input_size(&self) -> Option<usize>197 fn max_input_size(&self) -> Option<usize> {
198 Some(self.max_size)
199 }
200
update(&mut self, data: &[u8]) -> Result<(), Error>201 fn update(&mut self, data: &[u8]) -> Result<(), Error> {
202 self.pending_input.try_extend_from_slice(data)?;
203 Ok(())
204 }
205
finish(self: Box<Self>) -> Result<Vec<u8>, Error>206 fn finish(self: Box<Self>) -> Result<Vec<u8>, Error> {
207 let peer_key = ossl!(openssl::pkey::PKey::public_key_from_der(&self.pending_input))?;
208 match &self.key {
209 Key::P224(key) | Key::P256(key) | Key::P384(key) | Key::P521(key) => {
210 let group = nist_key_to_group(&self.key)?;
211 let ec_key = ossl!(private_key_from_der_for_group(&key.0, group.as_ref()))?;
212 let pkey = ossl!(openssl::pkey::PKey::from_ec_key(ec_key))?;
213 let mut deriver = ossl!(openssl::derive::Deriver::new(&pkey))?;
214 ossl!(deriver.set_peer(&peer_key))
215 .map_err(|e| km_err!(InvalidArgument, "peer key invalid: {:?}", e))?;
216 let derived = ossl!(deriver.derive_to_vec())?;
217 Ok(derived)
218 }
219 #[cfg(soong)]
220 Key::X25519(key) => {
221 // The BoringSSL `EVP_PKEY` interface does not support X25519, so need to invoke the
222 // `ffi:X25519()` method directly. First need to extract the raw peer key from the
223 // `SubjectPublicKeyInfo` it arrives in.
224 let peer_key =
225 ossl!(openssl::pkey::PKey::public_key_from_der(&self.pending_input))?;
226 let peer_key_type = peer_key.id();
227 if peer_key_type != openssl::pkey::Id::X25519 {
228 return Err(km_err!(
229 InvalidArgument,
230 "peer key for {:?} not supported with X25519",
231 peer_key_type
232 ));
233 }
234 let peer_key_data = ossl!(peer_key.raw_public_key())?;
235 if peer_key_data.len() != ffi::X25519_PUBLIC_VALUE_LEN as usize {
236 return Err(km_err!(
237 UnsupportedKeySize,
238 "peer raw key invalid length {}",
239 peer_key_data.len()
240 ));
241 }
242
243 let mut sig = vec_try![0; ffi::X25519_SHARED_KEY_LEN as usize]?;
244 // Safety: all pointer arguments need to point to 32-byte memory areas, enforced
245 // above and in the definition of [`ec::X25519Key`].
246 let result = unsafe {
247 ffi::X25519(sig.as_mut_ptr(), &key.0 as *const u8, peer_key_data.as_ptr())
248 };
249 if result == 1 {
250 Ok(sig)
251 } else {
252 Err(super::openssl_last_err())
253 }
254 }
255 #[cfg(not(soong))]
256 Key::X25519(_) => Err(km_err!(UnsupportedEcCurve, "X25519 not supported in cargo")),
257 Key::Ed25519(_) => {
258 Err(km_err!(IncompatibleAlgorithm, "Ed25519 key not valid for agreement"))
259 }
260 }
261 }
262 }
263
264 /// [`crypto::EcSignOperation`] based on BoringSSL, when an external digest is used.
265 pub struct BoringEcDigestSignOperation {
266 // Safety: `pkey` internally holds a pointer to BoringSSL-allocated data (`EVP_PKEY`),
267 // as do both of the raw pointers. This means that this item stays valid under moves,
268 // because the FFI-allocated data doesn't move.
269 pkey: openssl::pkey::PKey<openssl::pkey::Private>,
270
271 // Safety invariant: both `pctx` and `md_ctx` are non-`nullptr` and valid once item is
272 // constructed.
273 md_ctx: EvpMdCtx,
274 pctx: EvpPkeyCtx,
275 }
276
277 impl Drop for BoringEcDigestSignOperation {
drop(&mut self)278 fn drop(&mut self) {
279 // Safety: `EVP_MD_CTX_free()` handles `nullptr`, so it's fine to drop a partly-constructed
280 // item. `pctx` is owned by the `md_ctx`, so no need to explicitly free it.
281 unsafe {
282 ffi::EVP_MD_CTX_free(self.md_ctx.0);
283 }
284 }
285 }
286
287 impl BoringEcDigestSignOperation {
new(key: ec::NistKey, curve: ec::NistCurve, digest: MessageDigest) -> Result<Self, Error>288 fn new(key: ec::NistKey, curve: ec::NistCurve, digest: MessageDigest) -> Result<Self, Error> {
289 let group = nist_curve_to_group(curve)?;
290 let ec_key = ossl!(private_key_from_der_for_group(&key.0, group.as_ref()))?;
291 let pkey = ossl!(openssl::pkey::PKey::from_ec_key(ec_key))?;
292
293 // Safety: each of the raw pointers have to be non-nullptr (and thus valid, as BoringSSL
294 // emits only valid pointers or nullptr) to get to the point where they're used.
295 unsafe {
296 let mut op = BoringEcDigestSignOperation {
297 pkey,
298 md_ctx: EvpMdCtx(cvt_p(ffi::EVP_MD_CTX_new())?),
299 pctx: EvpPkeyCtx(ptr::null_mut()),
300 };
301
302 let r = ffi::EVP_DigestSignInit(
303 op.md_ctx.0,
304 &mut op.pctx.0,
305 digest.as_ptr(),
306 ptr::null_mut(),
307 op.pkey.as_ptr(),
308 );
309 if r != 1 {
310 return Err(openssl_last_err());
311 }
312 if op.pctx.0.is_null() {
313 return Err(km_err!(BoringSslError, "no PCTX!"));
314 }
315 // Safety invariant: both `pctx` and `md_ctx` are non-`nullptr` and valid on success.
316 Ok(op)
317 }
318 }
319 }
320
321 impl crypto::AccumulatingOperation for BoringEcDigestSignOperation {
update(&mut self, data: &[u8]) -> Result<(), Error>322 fn update(&mut self, data: &[u8]) -> Result<(), Error> {
323 // Safety: `data` is a valid slice, and `self.md_ctx` is non-`nullptr` and valid.
324 unsafe {
325 cvt(ffi::EVP_DigestUpdate(self.md_ctx.0, data.as_ptr() as *const _, data.len()))?;
326 }
327 Ok(())
328 }
329
finish(self: Box<Self>) -> Result<Vec<u8>, Error>330 fn finish(self: Box<Self>) -> Result<Vec<u8>, Error> {
331 let mut max_siglen = 0;
332 // Safety: `self.md_ctx` is non-`nullptr` and valid.
333 unsafe {
334 cvt(ffi::EVP_DigestSignFinal(self.md_ctx.0, ptr::null_mut(), &mut max_siglen))?;
335 }
336 let mut buf = vec_try![0; max_siglen]?;
337 let mut actual_siglen = max_siglen;
338 // Safety: `self.md_ctx` is non-`nullptr` and valid, and `buf` does have `actual_siglen`
339 // bytes.
340 unsafe {
341 cvt(ffi::EVP_DigestSignFinal(
342 self.md_ctx.0,
343 buf.as_mut_ptr() as *mut _,
344 &mut actual_siglen,
345 ))?;
346 }
347 buf.truncate(actual_siglen);
348 Ok(buf)
349 }
350 }
351
352 /// [`crypto::EcSignOperation`] based on BoringSSL, when data is undigested.
353 pub struct BoringEcUndigestSignOperation {
354 ec_key: openssl::ec::EcKey<openssl::pkey::Private>,
355 pending_input: Vec<u8>,
356 max_size: usize,
357 }
358
359 impl BoringEcUndigestSignOperation {
new(key: ec::NistKey, curve: ec::NistCurve) -> Result<Self, Error>360 fn new(key: ec::NistKey, curve: ec::NistCurve) -> Result<Self, Error> {
361 let group = nist_curve_to_group(curve)?;
362 let ec_key = ossl!(private_key_from_der_for_group(&key.0, group.as_ref()))?;
363 // Input to an undigested EC signing operation must be smaller than key size.
364 Ok(Self { ec_key, pending_input: Vec::new(), max_size: curve.coord_len() })
365 }
366 }
367
368 impl crypto::AccumulatingOperation for BoringEcUndigestSignOperation {
update(&mut self, data: &[u8]) -> Result<(), Error>369 fn update(&mut self, data: &[u8]) -> Result<(), Error> {
370 // For ECDSA signing, extra data beyond the maximum size is ignored (rather than being
371 // rejected via the `max_input_size()` trait method).
372 let max_extra_data = self.max_size - self.pending_input.len();
373 if max_extra_data > 0 {
374 let len = core::cmp::min(max_extra_data, data.len());
375 self.pending_input.try_extend_from_slice(&data[..len])?;
376 }
377 Ok(())
378 }
379
finish(self: Box<Self>) -> Result<Vec<u8>, Error>380 fn finish(self: Box<Self>) -> Result<Vec<u8>, Error> {
381 // BoringSSL doesn't support `EVP_PKEY` use without digest, so use low-level ECDSA
382 // functionality.
383 let sig = ossl!(openssl::ecdsa::EcdsaSig::sign(&self.pending_input, &self.ec_key))?;
384 let sig = ossl!(sig.to_der())?;
385 Ok(sig)
386 }
387 }
388
389 /// [`crypto::EcSignOperation`] based on BoringSSL for Ed25519.
390 pub struct BoringEd25519SignOperation {
391 pkey: openssl::pkey::PKey<openssl::pkey::Private>,
392 pending_input: Vec<u8>,
393 }
394
395 impl BoringEd25519SignOperation {
new(key: ec::Ed25519Key) -> Result<Self, Error>396 fn new(key: ec::Ed25519Key) -> Result<Self, Error> {
397 let pkey = ossl!(openssl::pkey::PKey::private_key_from_raw_bytes(
398 &key.0,
399 openssl::pkey::Id::ED25519
400 ))?;
401 Ok(Self { pkey, pending_input: Vec::new() })
402 }
403 }
404
405 impl crypto::AccumulatingOperation for BoringEd25519SignOperation {
max_input_size(&self) -> Option<usize>406 fn max_input_size(&self) -> Option<usize> {
407 // Ed25519 has an internal digest so could theoretically take arbitrary amounts of
408 // data. However, BoringSSL does not support incremental data feeding for Ed25519 so
409 // instead impose a message size limit (as required by the KeyMint HAL spec).
410 Some(ec::MAX_ED25519_MSG_SIZE)
411 }
412
update(&mut self, data: &[u8]) -> Result<(), Error>413 fn update(&mut self, data: &[u8]) -> Result<(), Error> {
414 // OK to accumulate data as there is a size limit.
415 self.pending_input.try_extend_from_slice(data)?;
416 Ok(())
417 }
418
finish(self: Box<Self>) -> Result<Vec<u8>, Error>419 fn finish(self: Box<Self>) -> Result<Vec<u8>, Error> {
420 let mut signer = ossl!(openssl::sign::Signer::new_without_digest(&self.pkey))?;
421 let sig = ossl!(signer.sign_oneshot_to_vec(&self.pending_input))?;
422 Ok(sig)
423 }
424 }
425
nist_curve_to_group(curve: ec::NistCurve) -> Result<openssl::ec::EcGroup, Error>426 fn nist_curve_to_group(curve: ec::NistCurve) -> Result<openssl::ec::EcGroup, Error> {
427 use openssl::nid::Nid;
428 openssl::ec::EcGroup::from_curve_name(match curve {
429 ec::NistCurve::P224 => Nid::SECP224R1,
430 ec::NistCurve::P256 => Nid::X9_62_PRIME256V1,
431 ec::NistCurve::P384 => Nid::SECP384R1,
432 ec::NistCurve::P521 => Nid::SECP521R1,
433 })
434 .map_err(openssl_err!("failed to determine EcGroup"))
435 }
436
nist_key_to_group(key: &ec::Key) -> Result<openssl::ec::EcGroup, Error>437 fn nist_key_to_group(key: &ec::Key) -> Result<openssl::ec::EcGroup, Error> {
438 use openssl::nid::Nid;
439 openssl::ec::EcGroup::from_curve_name(match key {
440 ec::Key::P224(_) => Nid::SECP224R1,
441 ec::Key::P256(_) => Nid::X9_62_PRIME256V1,
442 ec::Key::P384(_) => Nid::SECP384R1,
443 ec::Key::P521(_) => Nid::SECP521R1,
444 ec::Key::Ed25519(_) | ec::Key::X25519(_) => {
445 return Err(km_err!(UnsupportedEcCurve, "no NIST group for curve25519 key"))
446 }
447 })
448 .map_err(openssl_err!("failed to determine EcGroup"))
449 }
450