Merge "Factor out Rust client library for VirtualizationService."
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index a1e81d2..1c0714e 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -104,7 +104,7 @@
         help='the directory having files to be packaged')
     args = parser.parse_args(argv)
     # preprocess --key_override into a map
-    args.key_overrides = dict()
+    args.key_overrides = {}
     if args.key_override:
         for pair in args.key_override:
             name, key = pair.split('=')
@@ -159,7 +159,7 @@
         - a list of descriptors.
     """
     if not os.path.exists(image_path):
-        raise ValueError('Failed to find image: {}'.format(image_path))
+        raise ValueError(f'Failed to find image: {image_path}')
 
     output, ret_code = RunCommand(
         args, ['avbtool', 'info_image', '--image', image_path], expected_return_values={0, 1})
@@ -283,8 +283,7 @@
                 part_key = chained_partitions[part_name]
                 avbpubkey = os.path.join(work_dir, part_name + '.avbpubkey')
                 ExtractAvbPubkey(args, part_key, avbpubkey)
-                cmd.extend(['--chain_partition', '%s:%s:%s' %
-                           (part_name, ril, avbpubkey)])
+                cmd.extend(['--chain_partition', f'{part_name}:{ril}:{avbpubkey}'])
 
         if args.signing_args:
             cmd.extend(shlex.split(args.signing_args))
@@ -292,7 +291,7 @@
         RunCommand(args, cmd)
         # libavb expects to be able to read the maximum vbmeta size, so we must provide a partition
         # which matches this or the read will fail.
-        with open(vbmeta_img, 'a') as f:
+        with open(vbmeta_img, 'a', encoding='utf8') as f:
             f.truncate(65536)
 
 
@@ -311,9 +310,8 @@
             tmp_img = os.path.join(work_dir, part)
             RunCommand(args, ['img2simg', img, tmp_img])
 
-            image_arg = '--image=%s=%s' % (part, img)
-            partition_arg = '--partition=%s:readonly:%d:default' % (
-                part, os.path.getsize(img))
+            image_arg = f'--image={part}={img}'
+            partition_arg = f'--partition={part}:readonly:{os.path.getsize(img)}:default'
             cmd.extend([image_arg, partition_arg])
 
         RunCommand(args, cmd)
@@ -448,15 +446,15 @@
             return f.read()
 
     def check_equals_pubkey(file):
-        assert contents(file) == pubkey, 'pubkey mismatch: %s' % file
+        assert contents(file) == pubkey, f'pubkey mismatch: {file}'
 
     def check_contains_pubkey(file):
-        assert contents(file).find(pubkey) != -1, 'pubkey missing: %s' % file
+        assert contents(file).find(pubkey) != -1, f'pubkey missing: {file}'
 
     def check_avb_pubkey(file):
         info, _ = AvbInfo(args, file)
-        assert info is not None, 'no avbinfo: %s' % file
-        assert info['Public key (sha1)'] == pubkey_digest, 'pubkey mismatch: %s' % file
+        assert info is not None, f'no avbinfo: {file}'
+        assert info['Public key (sha1)'] == pubkey_digest, f'pubkey mismatch: {file}'
 
     for f in files.values():
         if f == files['bootloader.pubkey']:
diff --git a/libs/apkverify/Android.bp b/libs/apkverify/Android.bp
index df1cac6..2445dd5 100644
--- a/libs/apkverify/Android.bp
+++ b/libs/apkverify/Android.bp
@@ -13,7 +13,7 @@
         "libbyteorder",
         "libbytes",
         "liblog_rust",
-        "libring",
+        "libopenssl",
         "libx509_parser",
         "libzip",
     ],
@@ -22,6 +22,8 @@
 rust_library {
     name: "libapkverify",
     defaults: ["libapkverify.defaults"],
+    // TODO(b/204562227): move to host_supported to the defaults to include tests
+    host_supported: true,
 }
 
 rust_test {
diff --git a/libs/apkverify/src/lib.rs b/libs/apkverify/src/lib.rs
index 71ea857..290a79a 100644
--- a/libs/apkverify/src/lib.rs
+++ b/libs/apkverify/src/lib.rs
@@ -23,17 +23,5 @@
 mod v3;
 mod ziputil;
 
-use anyhow::Result;
-use std::path::Path;
-
-/// Verifies APK/APEX signing with v2/v3 scheme. On success, the public key (in DER format) is
-/// returned.
-pub fn verify<P: AsRef<Path>>(path: P) -> Result<Box<[u8]>> {
-    // TODO(jooyung) fallback to v2 when v3 not found
-    v3::verify(path)
-}
-
-/// Gets the public key (in DER format) that was used to sign the given APK/APEX file
-pub fn get_public_key_der<P: AsRef<Path>>(path: P) -> Result<Box<[u8]>> {
-    v3::get_public_key_der(path)
-}
+// TODO(jooyung) fallback to v2 when v3 not found
+pub use v3::{get_public_key_der, verify};
diff --git a/libs/apkverify/src/sigutil.rs b/libs/apkverify/src/sigutil.rs
index 23dd91e..009154f 100644
--- a/libs/apkverify/src/sigutil.rs
+++ b/libs/apkverify/src/sigutil.rs
@@ -19,7 +19,7 @@
 use anyhow::{anyhow, bail, Result};
 use byteorder::{LittleEndian, ReadBytesExt};
 use bytes::{Buf, BufMut, Bytes, BytesMut};
-use ring::digest;
+use openssl::hash::{DigestBytes, Hasher, MessageDigest};
 use std::cmp::min;
 use std::io::{Cursor, Read, Seek, SeekFrom, Take};
 
@@ -107,12 +107,12 @@
                 let slice = &mut chunk[..(chunk_size as usize)];
                 data.read_exact(slice)?;
                 digests_of_chunks.put_slice(
-                    digester.digest(slice, CHUNK_HEADER_MID, chunk_size as u32).as_ref(),
+                    digester.digest(slice, CHUNK_HEADER_MID, chunk_size as u32)?.as_ref(),
                 );
                 chunk_count += 1;
             }
         }
-        Ok(digester.digest(&digests_of_chunks, CHUNK_HEADER_TOP, chunk_count).as_ref().into())
+        Ok(digester.digest(&digests_of_chunks, CHUNK_HEADER_TOP, chunk_count)?.as_ref().into())
     }
 
     fn zip_entries(&mut self) -> Result<Take<Box<dyn Read + '_>>> {
@@ -157,7 +157,7 @@
 }
 
 struct Digester {
-    algorithm: &'static digest::Algorithm,
+    algorithm: MessageDigest,
 }
 
 const CHUNK_HEADER_TOP: &[u8] = &[0x5a];
@@ -167,8 +167,8 @@
     fn new(signature_algorithm_id: u32) -> Result<Digester> {
         let digest_algorithm_id = to_content_digest_algorithm(signature_algorithm_id)?;
         let algorithm = match digest_algorithm_id {
-            CONTENT_DIGEST_CHUNKED_SHA256 => &digest::SHA256,
-            CONTENT_DIGEST_CHUNKED_SHA512 => &digest::SHA512,
+            CONTENT_DIGEST_CHUNKED_SHA256 => MessageDigest::sha256(),
+            CONTENT_DIGEST_CHUNKED_SHA512 => MessageDigest::sha512(),
             // TODO(jooyung): implement
             CONTENT_DIGEST_VERITY_CHUNKED_SHA256 => {
                 bail!("TODO(b/190343842): CONTENT_DIGEST_VERITY_CHUNKED_SHA256: not implemented")
@@ -179,12 +179,12 @@
     }
 
     // v2/v3 digests are computed after prepending "header" byte and "size" info.
-    fn digest(&self, data: &[u8], header: &[u8], size: u32) -> digest::Digest {
-        let mut ctx = digest::Context::new(self.algorithm);
-        ctx.update(header);
-        ctx.update(&size.to_le_bytes());
-        ctx.update(data);
-        ctx.finish()
+    fn digest(&self, data: &[u8], header: &[u8], size: u32) -> Result<DigestBytes> {
+        let mut ctx = Hasher::new(self.algorithm)?;
+        ctx.update(header)?;
+        ctx.update(&size.to_le_bytes())?;
+        ctx.update(data)?;
+        Ok(ctx.finish()?)
     }
 }
 
diff --git a/libs/apkverify/src/v3.rs b/libs/apkverify/src/v3.rs
index 797911b..16530be 100644
--- a/libs/apkverify/src/v3.rs
+++ b/libs/apkverify/src/v3.rs
@@ -19,12 +19,12 @@
 // TODO(jooyung) remove this
 #![allow(dead_code)]
 
-use anyhow::{anyhow, bail, Context, Result};
+use anyhow::{anyhow, bail, ensure, Context, Result};
 use bytes::Bytes;
-use ring::signature::{
-    UnparsedPublicKey, VerificationAlgorithm, ECDSA_P256_SHA256_ASN1, RSA_PKCS1_2048_8192_SHA256,
-    RSA_PKCS1_2048_8192_SHA512, RSA_PSS_2048_8192_SHA256, RSA_PSS_2048_8192_SHA512,
-};
+use openssl::hash::MessageDigest;
+use openssl::pkey::{self, PKey};
+use openssl::rsa::Padding;
+use openssl::sign::Verifier;
 use std::fs::File;
 use std::io::{Read, Seek};
 use std::ops::Range;
@@ -87,7 +87,7 @@
 type AdditionalAttributes = Bytes;
 
 /// Verifies APK Signature Scheme v3 signatures of the provided APK and returns the public key
-/// associated with the signer.
+/// associated with the signer in DER format.
 pub fn verify<P: AsRef<Path>>(path: P) -> Result<Box<[u8]>> {
     let f = File::open(path.as_ref())?;
     let mut sections = ApkSections::new(f)?;
@@ -200,14 +200,22 @@
     signature: &Signature,
     key_info: &SubjectPublicKeyInfo,
 ) -> Result<()> {
-    let verification_alg: &dyn VerificationAlgorithm = match signature.signature_algorithm_id {
-        SIGNATURE_RSA_PSS_WITH_SHA256 => &RSA_PSS_2048_8192_SHA256,
-        SIGNATURE_RSA_PSS_WITH_SHA512 => &RSA_PSS_2048_8192_SHA512,
-        SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256 | SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 => {
-            &RSA_PKCS1_2048_8192_SHA256
+    let (pkey_id, padding, digest) = match signature.signature_algorithm_id {
+        SIGNATURE_RSA_PSS_WITH_SHA256 => {
+            (pkey::Id::RSA, Padding::PKCS1_PSS, MessageDigest::sha256())
         }
-        SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512 => &RSA_PKCS1_2048_8192_SHA512,
-        SIGNATURE_ECDSA_WITH_SHA256 | SIGNATURE_VERITY_ECDSA_WITH_SHA256 => &ECDSA_P256_SHA256_ASN1,
+        SIGNATURE_RSA_PSS_WITH_SHA512 => {
+            (pkey::Id::RSA, Padding::PKCS1_PSS, MessageDigest::sha512())
+        }
+        SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256 | SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256 => {
+            (pkey::Id::RSA, Padding::PKCS1, MessageDigest::sha256())
+        }
+        SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512 => {
+            (pkey::Id::RSA, Padding::PKCS1, MessageDigest::sha512())
+        }
+        SIGNATURE_ECDSA_WITH_SHA256 | SIGNATURE_VERITY_ECDSA_WITH_SHA256 => {
+            (pkey::Id::EC, Padding::NONE, MessageDigest::sha256())
+        }
         // TODO(b/190343842) not implemented signature algorithm
         SIGNATURE_ECDSA_WITH_SHA512
         | SIGNATURE_DSA_WITH_SHA256
@@ -219,8 +227,15 @@
         }
         _ => bail!("Unsupported signature algorithm: {:#x}", signature.signature_algorithm_id),
     };
-    let key = UnparsedPublicKey::new(verification_alg, &key_info.subject_public_key);
-    key.verify(data.as_ref(), signature.signature.as_ref())?;
+    let key = PKey::public_key_from_der(key_info.raw)?;
+    ensure!(key.id() == pkey_id, "Public key has the wrong ID");
+    let mut verifier = Verifier::new(digest, &key)?;
+    if pkey_id == pkey::Id::RSA {
+        verifier.set_rsa_padding(padding)?;
+    }
+    verifier.update(data)?;
+    let verified = verifier.verify(&signature.signature)?;
+    ensure!(verified, "Signature is invalid ");
     Ok(())
 }
 
diff --git a/libs/apkverify/tests/apkverify_test.rs b/libs/apkverify/tests/apkverify_test.rs
index ade4468..22faba4 100644
--- a/libs/apkverify/tests/apkverify_test.rs
+++ b/libs/apkverify/tests/apkverify_test.rs
@@ -23,6 +23,11 @@
 }
 
 #[test]
+fn test_verify_v3_ecdsa_sha256_p256() {
+    assert!(verify("tests/data/v3-only-with-ecdsa-sha256-p256.apk").is_ok());
+}
+
+#[test]
 fn test_verify_v3_digest_mismatch() {
     let res = verify("tests/data/v3-only-with-rsa-pkcs1-sha512-8192-digest-mismatch.apk");
     assert!(res.is_err());
diff --git a/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p256.apk b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p256.apk
new file mode 100644
index 0000000..5ef4fec
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p256.apk
Binary files differ