Snap for 8485504 from 0015994411735b81575ad76333ac19f3b136aedb to tm-release

Change-Id: Iaa6443f5b36ccf98586ab6c102aea246ad47b88c
diff --git a/remote_provisioning/cert_validator/Android.bp b/remote_provisioning/cert_validator/Android.bp
index e5ee4e2..7c59908 100644
--- a/remote_provisioning/cert_validator/Android.bp
+++ b/remote_provisioning/cert_validator/Android.bp
@@ -8,9 +8,21 @@
     wrapper_src: "libssl_wrapper.h",
     source_stem: "bindings",
     bindgen_flags: [
+        "--size_t-is-usize",
+        "--allowlist-function", "BN_bin2bn",
+        "--allowlist-function", "ECDSA_SIG_free",
+        "--allowlist-function", "ECDSA_SIG_new",
+        "--allowlist-function", "ECDSA_SIG_to_bytes",
+        "--allowlist-function", "ECDSA_verify",
+        "--allowlist-function", "EC_KEY_new_by_curve_name",
+        "--allowlist-function", "EC_KEY_free",
+        "--allowlist-function", "o2i_ECPublicKey",
         "--allowlist-function", "ED25519_verify",
+        "--allowlist-function", "OPENSSL_free",
+        "--allowlist-function", "SHA256",
         "--allowlist-var", "ED25519_PUBLIC_KEY_LEN",
         "--allowlist-var", "ED25519_SIGNATURE_LEN",
+        "--allowlist-var", "NID_X9_62_prime256v1",
     ],
     cflags: ["-I/usr/include/android"],
     shared_libs: ["libcrypto"],
@@ -73,4 +85,4 @@
         "libchrono",
         "libssl_bindgen",
     ],
-}
\ No newline at end of file
+}
diff --git a/remote_provisioning/cert_validator/libssl_wrapper.h b/remote_provisioning/cert_validator/libssl_wrapper.h
index 9516216..9681e81 100644
--- a/remote_provisioning/cert_validator/libssl_wrapper.h
+++ b/remote_provisioning/cert_validator/libssl_wrapper.h
@@ -1 +1,7 @@
-#include <openssl/curve25519.h>
\ No newline at end of file
+#include <openssl/bn.h>
+#include <openssl/curve25519.h>
+#include <openssl/ec_key.h>
+#include <openssl/ecdsa.h>
+#include <openssl/mem.h>
+#include <openssl/nid.h>
+#include <openssl/sha.h>
diff --git a/remote_provisioning/cert_validator/src/bcc.rs b/remote_provisioning/cert_validator/src/bcc.rs
index 5c2245f..f157cf7 100644
--- a/remote_provisioning/cert_validator/src/bcc.rs
+++ b/remote_provisioning/cert_validator/src/bcc.rs
@@ -5,7 +5,7 @@
 use crate::valueas::ValueAs;
 
 use self::entry::SubjectPublicKey;
-use anyhow::{anyhow, ensure, Context, Result};
+use anyhow::{anyhow, bail, ensure, Context, Result};
 use coset::AsCborValue;
 use coset::{
     cbor::value::Value::{self, Array},
@@ -21,7 +21,6 @@
 /// signs the first certificate), followed by a chain of BccEntry certificates. Apart from the
 /// first, the issuer of each cert if the subject of the previous one.
 pub struct Chain {
-    
     public_key: CoseKey,
     entries: Vec<CoseSign1>,
 }
@@ -44,7 +43,8 @@
 
         let mut it = self.entries.iter();
         let entry = it.next().unwrap();
-        let mut payload = entry::Payload::check_sign1_signature(&public_key, entry)?;
+        let mut payload = entry::Payload::check_sign1_signature(&public_key, entry)
+            .context("Failed initial signature check.")?;
         let mut payloads = Vec::with_capacity(self.entries.len());
 
         for entry in it {
@@ -97,6 +97,22 @@
     anyhow!("CoseError: {:?}", ce)
 }
 
+/// Get the value corresponding to the provided label within the supplied CoseKey
+/// or error if it's not present.
+pub fn get_label_value(key: &coset::CoseKey, label: i64) -> Result<&Value> {
+    Ok(&key
+        .params
+        .iter()
+        .find(|(k, _)| k == &coset::Label::Int(label))
+        .ok_or_else(|| anyhow!("Label {:?} not found", label))?
+        .1)
+}
+
+/// Get the byte string for the corresponding label within the key if the label exists
+/// and the value is actually a byte array.
+pub fn get_label_value_as_bytes(key: &coset::CoseKey, label: i64) -> Result<&Vec<u8>> {
+    get_label_value(key, label)?.as_bytes().ok_or_else(|| anyhow!("Value not a bstr."))
+}
 /// This module wraps the certificate validation functions intended for BccEntry.
 pub mod entry {
     use std::fmt::{Display, Formatter, Write};
@@ -278,21 +294,24 @@
         /// Perform validation on the items in the public key.
         pub fn check(&self) -> Result<()> {
             let pkey = &self.0;
-            ensure!(pkey.kty == coset::KeyType::Assigned(iana::KeyType::OKP));
-            // TODO: Follow up cl - add the case for ECDSA.
-            ensure!(pkey.alg == Some(coset::Algorithm::Assigned(iana::Algorithm::EdDSA)));
             if !pkey.key_ops.is_empty() {
                 ensure!(pkey
                     .key_ops
                     .contains(&coset::KeyOperation::Assigned(iana::KeyOperation::Verify)));
             }
-            let crv = &pkey
-                .params
-                .iter()
-                .find(|(k, _)| k == &coset::Label::Int(iana::OkpKeyParameter::Crv as i64))
-                .ok_or_else(|| anyhow!("Curve not found"))?
-                .1;
-            ensure!(crv == &Value::from(iana::EllipticCurve::Ed25519 as i64));
+            match pkey.kty {
+                coset::KeyType::Assigned(iana::KeyType::OKP) => {
+                    ensure!(pkey.alg == Some(coset::Algorithm::Assigned(iana::Algorithm::EdDSA)));
+                    let crv = get_label_value(pkey, iana::OkpKeyParameter::Crv as i64)?;
+                    ensure!(crv == &Value::from(iana::EllipticCurve::Ed25519 as i64));
+                }
+                coset::KeyType::Assigned(iana::KeyType::EC2) => {
+                    ensure!(pkey.alg == Some(coset::Algorithm::Assigned(iana::Algorithm::ES256)));
+                    let crv = get_label_value(pkey, iana::Ec2KeyParameter::Crv as i64)?;
+                    ensure!(crv == &Value::from(iana::EllipticCurve::P_256 as i64));
+                }
+                _ => bail!("Unexpected KeyType value: {:?}", pkey.kty),
+            }
             Ok(())
         }
     }
diff --git a/remote_provisioning/cert_validator/src/lib.rs b/remote_provisioning/cert_validator/src/lib.rs
index 730d976..35cf7f7 100644
--- a/remote_provisioning/cert_validator/src/lib.rs
+++ b/remote_provisioning/cert_validator/src/lib.rs
@@ -42,17 +42,17 @@
             &bcc::entry::read("testdata/open-dice/_CBOR_Ed25519_cert_full_cert_chain_0.cert")
                 .unwrap(),
         );
-        assert!(payload.is_ok());
+        assert!(payload.is_ok(), "Payload not okay: {:?}", payload);
         let payload = payload.unwrap().check_sign1(
             &bcc::entry::read("testdata/open-dice/_CBOR_Ed25519_cert_full_cert_chain_1.cert")
                 .unwrap(),
         );
-        assert!(payload.is_ok());
+        assert!(payload.is_ok(), "Payload not okay: {:?}", payload);
         let payload = payload.unwrap().check_sign1(
             &bcc::entry::read("testdata/open-dice/_CBOR_Ed25519_cert_full_cert_chain_2.cert")
                 .unwrap(),
         );
-        assert!(payload.is_ok());
+        assert!(payload.is_ok(), "Payload not okay: {:?}", payload);
     }
 
     #[test]
@@ -91,6 +91,20 @@
     }
 
     #[test]
+    fn test_check_chain_valid_p256() -> Result<()> {
+        let chain = bcc::Chain::read("testdata/bcc/valid_p256.chain").unwrap();
+        let payloads = chain.check()?;
+        assert_eq!(payloads.len(), 3);
+        Ok(())
+    }
+
+    #[test]
+    fn test_check_chain_bad_p256() {
+        let chain = bcc::Chain::read("testdata/bcc/bad_p256.chain").unwrap();
+        assert!(chain.check().is_err());
+    }
+
+    #[test]
     fn test_check_chain_bad_pub_key() {
         let chain = bcc::Chain::read("testdata/bcc/bad_pub_key.chain").unwrap();
         assert!(chain.check().is_err());
diff --git a/remote_provisioning/cert_validator/src/publickey.rs b/remote_provisioning/cert_validator/src/publickey.rs
index 90f15cc..280e1dd 100644
--- a/remote_provisioning/cert_validator/src/publickey.rs
+++ b/remote_provisioning/cert_validator/src/publickey.rs
@@ -2,53 +2,220 @@
 //! used in the BccPayload. The key itself is stored as a simple byte array in
 //! a vector. For now, only PubKeyEd25519 types of cbor public keys are supported.
 
-use anyhow::{anyhow, ensure, Result};
+use crate::bcc::get_label_value_as_bytes;
+use anyhow::{bail, ensure, Context, Result};
 use coset::{iana, Algorithm, CoseKey};
+use std::ptr;
 
-/// Public key length.
-pub const PUBLIC_KEY_LEN: usize = ssl_bindgen::ED25519_PUBLIC_KEY_LEN as usize;
-/// Signature length.
-pub const SIGNATURE_LEN: usize = ssl_bindgen::ED25519_SIGNATURE_LEN as usize;
+/// Length of an Ed25519 public key.
+pub const ED25519_PUBLIC_KEY_LEN: usize = ssl_bindgen::ED25519_PUBLIC_KEY_LEN as usize;
+/// Length of an Ed25519 signatures.
+pub const ED25519_SIG_LEN: usize = ssl_bindgen::ED25519_SIGNATURE_LEN as usize;
+/// Length of a P256 coordinate.
+pub const P256_COORD_LEN: usize = 32;
+/// Length of a P256 signature.
+pub const P256_SIG_LEN: usize = 64;
 
+enum PubKey {
+    Ed25519 { pub_key: [u8; ED25519_PUBLIC_KEY_LEN] },
+    P256 { x_coord: [u8; P256_COORD_LEN], y_coord: [u8; P256_COORD_LEN] },
+}
 /// Struct wrapping the public key byte array, and the relevant validation methods.
-pub struct PublicKey([u8; PUBLIC_KEY_LEN]);
+pub struct PublicKey {
+    key: PubKey,
+}
 
 impl PublicKey {
     /// Extract the PublicKey from Subject Public Key.
     /// (CertificateRequest.BccEntry.payload[SubjectPublicKey].X)
     pub fn from_cose_key(pkey: &CoseKey) -> Result<Self> {
-        let x = pkey
-            .params
-            .iter()
-            .find(|(k, _)| k == &coset::Label::Int(iana::OkpKeyParameter::X as i64))
-            .ok_or_else(|| anyhow!("X not found"))?
-            .1
-            .as_bytes()
-            .ok_or_else(|| anyhow!("X not bytes"))?;
-
-        PublicKey::new(x)
+        let x = get_label_value_as_bytes(pkey, iana::OkpKeyParameter::X as i64)?;
+        match pkey.alg {
+            Some(coset::Algorithm::Assigned(iana::Algorithm::EdDSA)) => {
+                PublicKey::new(PubKey::Ed25519 {
+                    pub_key: x.as_slice().try_into().context(format!(
+                        "Failed to convert x_coord to array. Len: {:?}",
+                        x.len()
+                    ))?,
+                })
+            }
+            Some(coset::Algorithm::Assigned(iana::Algorithm::ES256)) => {
+                let y = get_label_value_as_bytes(pkey, iana::Ec2KeyParameter::Y as i64)?;
+                PublicKey::new(PubKey::P256 {
+                    x_coord: x.as_slice().try_into().context(format!(
+                        "Failed to convert x_coord to array. Len: {:?}",
+                        x.len()
+                    ))?,
+                    y_coord: y.as_slice().try_into().context(format!(
+                        "Failed to convert y_coord to array. Len: {:?}",
+                        y.len()
+                    ))?,
+                })
+            }
+            _ => bail!("Unsupported signature algorithm: {:?}", pkey.alg),
+        }
     }
 
-    fn new(public_key: &[u8]) -> Result<Self> {
-        Ok(Self(public_key.try_into()?))
+    fn new(key: PubKey) -> Result<Self> {
+        Ok(Self { key })
+    }
+
+    fn sha256(message: &[u8]) -> Result<[u8; 32]> {
+        let mut digest: [u8; 32] = [0; 32];
+        // SAFETY: This function is safe due to message only being read, with the associated length
+        // on the slice passed in to ensure no buffer overreads. Additionally, the digest is sized
+        // accordingly to the output size of SHA256. No memory is allocated.
+        unsafe {
+            if ssl_bindgen::SHA256(message.as_ptr(), message.len(), digest.as_mut_ptr()).is_null() {
+                bail!("Failed to hash the message.");
+            }
+        }
+        Ok(digest)
+    }
+
+    fn raw_p256_sig_to_der(signature: &[u8]) -> Result<Vec<u8>> {
+        ensure!(
+            signature.len() == P256_SIG_LEN,
+            "Unexpected signature length: {:?}",
+            signature.len()
+        );
+        let mut der_sig: *mut u8 = ptr::null_mut();
+        let mut der_sig_len: usize = 0;
+        // SAFETY: The signature slice is verified to contain the expected length before it is
+        // indexed as read only memory for the boringssl code to generate a DER encoded signature.
+        // The final result from the boringssl operations is copied out to a standard vector so
+        // the specific boringSSL deallocators can be used on the memory buffers that were
+        // allocated, and a standard, safe Rust Vec can be returned.
+        unsafe {
+            let der_encoder = ssl_bindgen::ECDSA_SIG_new();
+            if der_encoder.is_null() {
+                bail!("Failed to allocate ECDSA_SIG");
+            }
+            let mut encoder_closure = || {
+                ssl_bindgen::BN_bin2bn(signature.as_ptr(), 32, (*der_encoder).r);
+                ssl_bindgen::BN_bin2bn(signature.as_ptr().offset(32), 32, (*der_encoder).s);
+                if (*der_encoder).r.is_null() || (*der_encoder).s.is_null() {
+                    bail!("Failed to allocate BigNum.");
+                }
+                // ECDSA_SIG_to_bytes takes a uint8_t** and allocates a buffer
+                if ssl_bindgen::ECDSA_SIG_to_bytes(
+                    &mut der_sig,
+                    &mut der_sig_len as *mut usize,
+                    der_encoder,
+                ) == 0
+                {
+                    bail!("Failed to encode ECDSA_SIG into a DER byte array.");
+                }
+                // Copy the data out of der_sig so that the unsafe pointer and associated memory
+                // can be properly freed.
+                let mut safe_copy = Vec::with_capacity(der_sig_len);
+                ptr::copy(der_sig, safe_copy.as_mut_ptr(), der_sig_len);
+                safe_copy.set_len(der_sig_len);
+                Ok(safe_copy)
+            };
+            let safe_copy = encoder_closure();
+            ssl_bindgen::ECDSA_SIG_free(der_encoder);
+            ssl_bindgen::OPENSSL_free(der_sig as *mut std::ffi::c_void);
+            safe_copy
+        }
+    }
+
+    fn verify_p256(signature: &[u8], message: &[u8], ec_point: &[u8]) -> Result<i32> {
+        // len(0x04 || r || s) should be 65 for a p256 public key.
+        ensure!(ec_point.len() == 65);
+        let mut key_bytes: *const u8 = ec_point.as_ptr();
+        let digest = PublicKey::sha256(message)?;
+        let der_sig = PublicKey::raw_p256_sig_to_der(signature)?;
+        // SAFETY: The following unsafe block allocates and creates an EC_KEY, using that struct
+        // in conjunction with read only access to length checked rust slices to verify the
+        // signature. The boringSSL allocated memory is then freed, regardless of failures during
+        // the verification process.
+        unsafe {
+            let mut key = ssl_bindgen::EC_KEY_new_by_curve_name(
+                ssl_bindgen::NID_X9_62_prime256v1.try_into()?,
+            );
+            // Use a closure just to simplify freeing allocated memory and error
+            // handling in the event an error occurs.
+            let mut verifier_closure = || {
+                if key.is_null() {
+                    bail!("Failed to allocate a new EC_KEY.");
+                }
+                if ssl_bindgen::o2i_ECPublicKey(
+                    &mut key,
+                    &mut key_bytes,
+                    ec_point.len().try_into()?,
+                )
+                .is_null()
+                {
+                    bail!("Failed to convert key byte array into an EC_KEY structure.");
+                }
+                Ok(ssl_bindgen::ECDSA_verify(
+                    0, /* type */
+                    digest.as_ptr(),
+                    digest.len(),
+                    der_sig.as_slice().as_ptr(),
+                    der_sig.len(),
+                    key,
+                ))
+            };
+            let result = verifier_closure();
+            ssl_bindgen::EC_KEY_free(key);
+            result
+        }
     }
 
     /// Verify that the signature obtained from signing the given message
     /// with the PublicKey matches the signature provided.
     pub fn verify(&self, signature: &[u8], message: &[u8], alg: &Option<Algorithm>) -> Result<()> {
-        ensure!(signature.len() == SIGNATURE_LEN);
-        // TODO: add match(alg) and handle the case for ECDSA.
-        ensure!(*alg == Some(coset::Algorithm::Assigned(iana::Algorithm::EdDSA)));
-        ensure!(
-            unsafe {
-                ssl_bindgen::ED25519_verify(
-                    message.as_ptr(),
-                    message.len().try_into()?,
-                    signature.as_ptr(),
-                    self.0.as_ptr(),
-                )
-            } == 1
-        );
+        match self.key {
+            PubKey::Ed25519 { pub_key } => {
+                ensure!(
+                    *alg == Some(coset::Algorithm::Assigned(iana::Algorithm::EdDSA)),
+                    "Unexpected algorithm. Ed25519 key, but alg is: {:?}",
+                    *alg
+                );
+                ensure!(
+                    signature.len() == ED25519_SIG_LEN,
+                    "Unexpected signature length: {:?}",
+                    signature.len()
+                );
+                ensure!(
+                    pub_key.len() == ED25519_PUBLIC_KEY_LEN,
+                    "Unexpected public key length {:?}:",
+                    pub_key.len()
+                );
+                ensure!(
+                    // SAFETY: The underlying API only reads from the provided pointers, which are
+                    // themselves standard slices with their corresponding expended lengths checked
+                    // before the function call.
+                    unsafe {
+                        ssl_bindgen::ED25519_verify(
+                            message.as_ptr(),
+                            message.len(),
+                            signature.as_ptr(),
+                            pub_key.as_ptr(),
+                        )
+                    } == 1,
+                    "Signature verification failed."
+                );
+            }
+            PubKey::P256 { x_coord, y_coord } => {
+                ensure!(
+                    *alg == Some(coset::Algorithm::Assigned(iana::Algorithm::ES256)),
+                    "Unexpected algorithm. P256 key, but alg is: {:?}",
+                    *alg
+                );
+                let mut ec_point_uncompressed: Vec<u8> = vec![0x04];
+                ec_point_uncompressed.extend_from_slice(&x_coord);
+                ec_point_uncompressed.extend_from_slice(&y_coord);
+                ensure!(ec_point_uncompressed.len() == 65);
+                let ec_point_slice = ec_point_uncompressed.as_slice();
+                ensure!(
+                    PublicKey::verify_p256(signature, message, ec_point_slice)? == 1,
+                    "Signature verification failed."
+                );
+            }
+        }
         Ok(())
     }
 }
diff --git a/remote_provisioning/cert_validator/testdata/bcc/bad_p256.chain b/remote_provisioning/cert_validator/testdata/bcc/bad_p256.chain
new file mode 100644
index 0000000..822438c
--- /dev/null
+++ b/remote_provisioning/cert_validator/testdata/bcc/bad_p256.chain
Binary files differ
diff --git a/remote_provisioning/cert_validator/testdata/bcc/valid_p256.chain b/remote_provisioning/cert_validator/testdata/bcc/valid_p256.chain
new file mode 100644
index 0000000..9619521
--- /dev/null
+++ b/remote_provisioning/cert_validator/testdata/bcc/valid_p256.chain
Binary files differ