Merge "Re-connect after adb root"
diff --git a/apex/Android.bp b/apex/Android.bp
index a9fad55..fade6c5 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -102,11 +102,7 @@
"sign_virt_apex.py",
],
version: {
- py2: {
- enabled: false,
- },
py3: {
- enabled: true,
embedded_launcher: true,
},
},
@@ -170,11 +166,7 @@
"replace_bytes.py",
],
version: {
- py2: {
- enabled: false,
- },
py3: {
- enabled: true,
embedded_launcher: true,
},
},
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index 5b7a4f4..93a788b 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -26,12 +26,13 @@
mod fsverity;
use anyhow::{bail, Result};
+use clap::Parser;
use log::debug;
use nix::sys::stat::{umask, Mode};
use rpcbinder::run_rpc_server;
use std::collections::BTreeMap;
use std::fs::File;
-use std::os::unix::io::FromRawFd;
+use std::os::unix::io::{FromRawFd, OwnedFd};
use aidl::{FdConfig, FdService};
use authfs_fsverity_metadata::parse_fsverity_metadata;
@@ -72,86 +73,52 @@
))
}
-fn parse_arg_rw_fds(arg: &str) -> Result<(i32, FdConfig)> {
- let fd = arg.parse::<i32>()?;
- let file = fd_to_owned::<File>(fd)?;
- if file.metadata()?.len() > 0 {
- bail!("File is expected to be empty");
- }
- Ok((fd, FdConfig::ReadWrite(file)))
-}
-
-fn parse_arg_ro_dirs(arg: &str) -> Result<(i32, FdConfig)> {
- let fd = arg.parse::<i32>()?;
- Ok((fd, FdConfig::InputDir(fd_to_owned(fd)?)))
-}
-
-fn parse_arg_rw_dirs(arg: &str) -> Result<(i32, FdConfig)> {
- let fd = arg.parse::<i32>()?;
- Ok((fd, FdConfig::OutputDir(fd_to_owned(fd)?)))
-}
-
+#[derive(Parser)]
struct Args {
- fd_pool: BTreeMap<i32, FdConfig>,
- ready_fd: Option<File>,
+ /// Read-only FD of file, with optional FD of corresponding .fsv_meta, joined with a ':'.
+ /// Example: "1:2", "3".
+ #[clap(long)]
+ ro_fds: Vec<String>,
+
+ /// Read-writable FD of file
+ #[clap(long)]
+ rw_fds: Vec<i32>,
+
+ /// Read-only FD of directory
+ #[clap(long)]
+ ro_dirs: Vec<i32>,
+
+ /// Read-writable FD of directory
+ #[clap(long)]
+ rw_dirs: Vec<i32>,
+
+ /// A pipe FD for signaling the other end once ready
+ #[clap(long)]
+ ready_fd: Option<i32>,
}
-fn parse_args() -> Result<Args> {
- #[rustfmt::skip]
- let matches = clap::App::new("fd_server")
- .arg(clap::Arg::with_name("ro-fds")
- .long("ro-fds")
- .multiple(true)
- .number_of_values(1))
- .arg(clap::Arg::with_name("rw-fds")
- .long("rw-fds")
- .multiple(true)
- .number_of_values(1))
- .arg(clap::Arg::with_name("ro-dirs")
- .long("ro-dirs")
- .multiple(true)
- .number_of_values(1))
- .arg(clap::Arg::with_name("rw-dirs")
- .long("rw-dirs")
- .multiple(true)
- .number_of_values(1))
- .arg(clap::Arg::with_name("ready-fd")
- .long("ready-fd")
- .takes_value(true))
- .get_matches();
-
+/// Convert argument strings and integers to a form that is easier to use and handles ownership.
+fn convert_args(args: Args) -> Result<(BTreeMap<i32, FdConfig>, Option<OwnedFd>)> {
let mut fd_pool = BTreeMap::new();
- if let Some(args) = matches.values_of("ro-fds") {
- for arg in args {
- let (fd, config) = parse_arg_ro_fds(arg)?;
- fd_pool.insert(fd, config);
- }
+ for arg in args.ro_fds {
+ let (fd, config) = parse_arg_ro_fds(&arg)?;
+ fd_pool.insert(fd, config);
}
- if let Some(args) = matches.values_of("rw-fds") {
- for arg in args {
- let (fd, config) = parse_arg_rw_fds(arg)?;
- fd_pool.insert(fd, config);
+ for fd in args.rw_fds {
+ let file = fd_to_owned::<File>(fd)?;
+ if file.metadata()?.len() > 0 {
+ bail!("File is expected to be empty");
}
+ fd_pool.insert(fd, FdConfig::ReadWrite(file));
}
- if let Some(args) = matches.values_of("ro-dirs") {
- for arg in args {
- let (fd, config) = parse_arg_ro_dirs(arg)?;
- fd_pool.insert(fd, config);
- }
+ for fd in args.ro_dirs {
+ fd_pool.insert(fd, FdConfig::InputDir(fd_to_owned(fd)?));
}
- if let Some(args) = matches.values_of("rw-dirs") {
- for arg in args {
- let (fd, config) = parse_arg_rw_dirs(arg)?;
- fd_pool.insert(fd, config);
- }
+ for fd in args.rw_dirs {
+ fd_pool.insert(fd, FdConfig::OutputDir(fd_to_owned(fd)?));
}
- let ready_fd = if let Some(arg) = matches.value_of("ready-fd") {
- let fd = arg.parse::<i32>()?;
- Some(fd_to_owned(fd)?)
- } else {
- None
- };
- Ok(Args { fd_pool, ready_fd })
+ let ready_fd = args.ready_fd.map(fd_to_owned).transpose()?;
+ Ok((fd_pool, ready_fd))
}
fn main() -> Result<()> {
@@ -159,7 +126,8 @@
android_logger::Config::default().with_tag("fd_server").with_min_level(log::Level::Debug),
);
- let args = parse_args()?;
+ let args = Args::parse();
+ let (fd_pool, mut ready_fd) = convert_args(args)?;
// Allow open/create/mkdir from authfs to create with expecting mode. It's possible to still
// use a custom mask on creation, then report the actual file mode back to authfs. But there
@@ -167,9 +135,8 @@
let old_umask = umask(Mode::empty());
debug!("Setting umask to 0 (old: {:03o})", old_umask.bits());
- let service = FdService::new_binder(args.fd_pool).as_binder();
+ let service = FdService::new_binder(fd_pool).as_binder();
debug!("fd_server is starting as a rpc service.");
- let mut ready_fd = args.ready_fd;
let retval = run_rpc_server(service, RPC_SERVICE_PORT, || {
debug!("fd_server is ready");
// Close the ready-fd if we were given one to signal our readiness.
diff --git a/avmd/Android.bp b/avmd/Android.bp
index 7237f5f..6d91b59 100644
--- a/avmd/Android.bp
+++ b/avmd/Android.bp
@@ -11,6 +11,7 @@
rustlibs: [
"libserde",
"libapexutil_rust", // TODO(b/239413416): Remove this after adding hex
+ "libapkverify",
],
}
diff --git a/avmd/src/avmd.rs b/avmd/src/avmd.rs
index 50cdfdf..05fc201 100644
--- a/avmd/src/avmd.rs
+++ b/avmd/src/avmd.rs
@@ -19,6 +19,7 @@
vec::Vec,
};
use apexutil::to_hex_string;
+use apkverify::SignatureAlgorithmID;
use core::fmt;
use serde::{Deserialize, Serialize};
@@ -120,7 +121,7 @@
/// It should be one of the algorithms in the [list][].
///
/// [list]: https://source.android.com/security/apksigning/v2#signature-algorithm-ids
- pub signature_algorithm_id: u32,
+ pub signature_algorithm_id: SignatureAlgorithmID,
/// Digest of the APK's v3 signing block. TODO: fix
pub apk_digest: Vec<u8>,
}
@@ -130,7 +131,7 @@
writeln!(f, " APK descriptor:")?;
writeln!(f, " namespace: {}", self.resource.namespace)?;
writeln!(f, " name: {}", self.resource.name)?;
- writeln!(f, " Signing algorithm ID: {:#x}", self.signature_algorithm_id)?;
+ writeln!(f, " Signing algorithm ID: {:#04x}", self.signature_algorithm_id.to_u32())?;
writeln!(f, " APK digest: {}", to_hex_string(&self.apk_digest))?;
Ok(())
}
diff --git a/avmd/tests/data/test.avmd b/avmd/tests/data/test.avmd
index 52e634f..e567125 100644
--- a/avmd/tests/data/test.avmd
+++ b/avmd/tests/data/test.avmd
Binary files differ
diff --git a/libs/apkverify/Android.bp b/libs/apkverify/Android.bp
index 9bb8f8e..78192d2 100644
--- a/libs/apkverify/Android.bp
+++ b/libs/apkverify/Android.bp
@@ -15,6 +15,7 @@
"liblog_rust",
"libnum_traits",
"libopenssl",
+ "libserde",
"libzip",
],
proc_macros: ["libnum_derive"],
diff --git a/libs/apkverify/src/algorithms.rs b/libs/apkverify/src/algorithms.rs
index edfa946..a1cf368 100644
--- a/libs/apkverify/src/algorithms.rs
+++ b/libs/apkverify/src/algorithms.rs
@@ -17,52 +17,82 @@
//! Algorithms used for APK Signature Scheme.
use anyhow::{ensure, Result};
-use num_derive::FromPrimitive;
+use bytes::{Buf, Bytes};
+use num_derive::{FromPrimitive, ToPrimitive};
+use num_traits::{FromPrimitive, ToPrimitive};
use openssl::hash::MessageDigest;
use openssl::pkey::{self, PKey};
use openssl::rsa::Padding;
use openssl::sign::Verifier;
-use std::cmp::Ordering;
+use serde::{Deserialize, Serialize};
+
+use crate::bytes_ext::ReadFromBytes;
/// [Signature Algorithm IDs]: https://source.android.com/docs/security/apksigning/v2#signature-algorithm-ids
+/// [SignatureAlgorithm.java]: (tools/apksig/src/main/java/com/android/apksig/internal/apk/SignatureAlgorithm.java)
///
/// Some of the algorithms are not implemented. See b/197052981.
-#[derive(Clone, Debug, Eq, FromPrimitive)]
+#[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)]
#[repr(u32)]
pub enum SignatureAlgorithmID {
+ /// RSASSA-PSS with SHA2-256 digest, SHA2-256 MGF1, 32 bytes of salt, trailer: 0xbc, content
+ /// digested using SHA2-256 in 1 MB chunks.
RsaPssWithSha256 = 0x0101,
+
+ /// RSASSA-PSS with SHA2-512 digest, SHA2-512 MGF1, 64 bytes of salt, trailer: 0xbc, content
+ /// digested using SHA2-512 in 1 MB chunks.
RsaPssWithSha512 = 0x0102,
+
+ /// RSASSA-PKCS1-v1_5 with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks.
RsaPkcs1V15WithSha256 = 0x0103,
+
+ /// RSASSA-PKCS1-v1_5 with SHA2-512 digest, content digested using SHA2-512 in 1 MB chunks.
RsaPkcs1V15WithSha512 = 0x0104,
+
+ /// ECDSA with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks.
EcdsaWithSha256 = 0x0201,
+
+ /// ECDSA with SHA2-512 digest, content digested using SHA2-512 in 1 MB chunks.
EcdsaWithSha512 = 0x0202,
+
+ /// DSA with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks.
+ /// Signing is done deterministically according to RFC 6979.
DsaWithSha256 = 0x0301,
+
+ /// RSASSA-PKCS1-v1_5 with SHA2-256 digest, content digested using SHA2-256 in 4 KB
+ /// chunks, in the same way fsverity operates. This digest and the content length
+ /// (before digestion, 8 bytes in little endian) construct the final digest.
VerityRsaPkcs1V15WithSha256 = 0x0421,
+
+ /// ECDSA with SHA2-256 digest, content digested using SHA2-256 in 4 KB chunks, in the
+ /// same way fsverity operates. This digest and the content length (before digestion,
+ /// 8 bytes in little endian) construct the final digest.
VerityEcdsaWithSha256 = 0x0423,
+
+ /// DSA with SHA2-256 digest, content digested using SHA2-256 in 4 KB chunks, in the
+ /// same way fsverity operates. This digest and the content length (before digestion,
+ /// 8 bytes in little endian) construct the final digest.
VerityDsaWithSha256 = 0x0425,
}
-impl Ord for SignatureAlgorithmID {
- /// Ranks the signature algorithm according to the corresponding content
- /// digest algorithm's rank.
- fn cmp(&self, other: &Self) -> Ordering {
- self.to_content_digest_algorithm().cmp(&other.to_content_digest_algorithm())
+impl Default for SignatureAlgorithmID {
+ fn default() -> Self {
+ SignatureAlgorithmID::DsaWithSha256
}
}
-impl PartialOrd for SignatureAlgorithmID {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl PartialEq for SignatureAlgorithmID {
- fn eq(&self, other: &Self) -> bool {
- self.cmp(other) == Ordering::Equal
+impl ReadFromBytes for Option<SignatureAlgorithmID> {
+ fn read_from_bytes(buf: &mut Bytes) -> Result<Self> {
+ Ok(SignatureAlgorithmID::from_u32(buf.get_u32_le()))
}
}
impl SignatureAlgorithmID {
+ /// Converts the signature algorithm ID to the corresponding u32.
+ pub fn to_u32(&self) -> u32 {
+ ToPrimitive::to_u32(self).expect("Unsupported algorithm for to_u32.")
+ }
+
pub(crate) fn new_verifier<'a>(
&self,
public_key: &'a PKey<pkey::Public>,
@@ -72,7 +102,7 @@
self,
SignatureAlgorithmID::DsaWithSha256 | SignatureAlgorithmID::VerityDsaWithSha256
),
- "TODO(b/197052981): Algorithm '{:#?}' is not implemented.",
+ "TODO(b/197052981): Algorithm '{:?}' is not implemented.",
self
);
ensure!(public_key.id() == self.pkey_id(), "Public key has the wrong ID");
@@ -132,7 +162,7 @@
}
}
- fn to_content_digest_algorithm(&self) -> ContentDigestAlgorithm {
+ pub(crate) fn content_digest_algorithm(&self) -> ContentDigestAlgorithm {
match self {
SignatureAlgorithmID::RsaPssWithSha256
| SignatureAlgorithmID::RsaPkcs1V15WithSha256
@@ -160,7 +190,7 @@
/// [apk digest]: https://source.android.com/docs/security/features/apksigning/v4#apk-digest
/// [v3 verification]: https://source.android.com/docs/security/apksigning/v3#v3-verification
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
-enum ContentDigestAlgorithm {
+pub(crate) enum ContentDigestAlgorithm {
ChunkedSha256 = 1,
VerityChunkedSha256,
ChunkedSha512,
diff --git a/libs/apkverify/src/lib.rs b/libs/apkverify/src/lib.rs
index 040c304..084a910 100644
--- a/libs/apkverify/src/lib.rs
+++ b/libs/apkverify/src/lib.rs
@@ -25,4 +25,5 @@
mod ziputil;
// TODO(b/197052981) fallback to v2 when v3 not found
+pub use algorithms::SignatureAlgorithmID;
pub use v3::{get_public_key_der, pick_v4_apk_digest, verify};
diff --git a/libs/apkverify/src/sigutil.rs b/libs/apkverify/src/sigutil.rs
index 3832c09..bfa51c1 100644
--- a/libs/apkverify/src/sigutil.rs
+++ b/libs/apkverify/src/sigutil.rs
@@ -16,14 +16,9 @@
//! Utilities for Signature Verification
-// TODO(b/246254355): Remove this once we migrate all the usages of
-// raw signature algorithm id to the enum.
-#![allow(dead_code)]
-
use anyhow::{anyhow, ensure, Error, Result};
use byteorder::{LittleEndian, ReadBytesExt};
use bytes::{Buf, BufMut, Bytes, BytesMut};
-use num_traits::FromPrimitive;
use openssl::hash::{DigestBytes, Hasher, MessageDigest};
use std::cmp::min;
use std::io::{self, Cursor, ErrorKind, Read, Seek, SeekFrom, Take};
@@ -34,19 +29,9 @@
const APK_SIG_BLOCK_MIN_SIZE: u32 = 32;
const APK_SIG_BLOCK_MAGIC: u128 = 0x3234206b636f6c4220676953204b5041;
-// TODO(b/246254355): Migrates usages of raw signature algorithm id to the enum.
-pub const SIGNATURE_RSA_PSS_WITH_SHA256: u32 = 0x0101;
-pub const SIGNATURE_RSA_PSS_WITH_SHA512: u32 = 0x0102;
-pub const SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA256: u32 = 0x0103;
-pub const SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512: u32 = 0x0104;
-pub const SIGNATURE_ECDSA_WITH_SHA256: u32 = 0x0201;
-pub const SIGNATURE_ECDSA_WITH_SHA512: u32 = 0x0202;
-pub const SIGNATURE_DSA_WITH_SHA256: u32 = 0x0301;
-pub const SIGNATURE_VERITY_RSA_PKCS1_V1_5_WITH_SHA256: u32 = 0x0421;
-pub const SIGNATURE_VERITY_ECDSA_WITH_SHA256: u32 = 0x0423;
-pub const SIGNATURE_VERITY_DSA_WITH_SHA256: u32 = 0x0425;
-
const CHUNK_SIZE_BYTES: u64 = 1024 * 1024;
+const CHUNK_HEADER_TOP: &[u8] = &[0x5a];
+const CHUNK_HEADER_MID: &[u8] = &[0xa5];
/// The [APK structure] has four major sections:
///
@@ -95,12 +80,11 @@
/// chunks (little-endian uint32), and the concatenation of digests of the chunks in the
/// order the chunks appear in the APK.
/// (see https://source.android.com/security/apksigning/v2#integrity-protected-contents)
- pub fn compute_digest(&mut self, signature_algorithm_id: u32) -> Result<Vec<u8>> {
- // TODO(b/246254355): Passes the enum SignatureAlgorithmID directly to this method.
- let signature_algorithm_id = SignatureAlgorithmID::from_u32(signature_algorithm_id)
- .ok_or_else(|| anyhow!("Unsupported algorithm ID: {}", signature_algorithm_id))?;
+ pub(crate) fn compute_digest(
+ &mut self,
+ signature_algorithm_id: SignatureAlgorithmID,
+ ) -> Result<Vec<u8>> {
let digester = Digester { message_digest: signature_algorithm_id.new_message_digest() };
-
let mut digests_of_chunks = BytesMut::new();
let mut chunk_count = 0u32;
let mut chunk = vec![0u8; CHUNK_SIZE_BYTES as usize];
@@ -168,9 +152,6 @@
message_digest: MessageDigest,
}
-const CHUNK_HEADER_TOP: &[u8] = &[0x5a];
-const CHUNK_HEADER_MID: &[u8] = &[0xa5];
-
impl Digester {
// v2/v3 digests are computed after prepending "header" byte and "size" info.
fn digest(&self, data: &[u8], header: &[u8], size: u32) -> Result<DigestBytes> {
@@ -293,7 +274,7 @@
fn test_apk_digest() {
let apk_file = File::open("tests/data/v3-only-with-dsa-sha256-1024.apk").unwrap();
let mut apk_sections = ApkSections::new(apk_file).unwrap();
- let digest = apk_sections.compute_digest(SIGNATURE_DSA_WITH_SHA256).unwrap();
+ let digest = apk_sections.compute_digest(SignatureAlgorithmID::DsaWithSha256).unwrap();
assert_eq!(
"0DF2426EA33AEDAF495D88E5BE0C6A1663FF0A81C5ED12D5B2929AE4B4300F2F",
to_hex_string(&digest[..])
diff --git a/libs/apkverify/src/v3.rs b/libs/apkverify/src/v3.rs
index 5313a9b..a3e738b 100644
--- a/libs/apkverify/src/v3.rs
+++ b/libs/apkverify/src/v3.rs
@@ -20,7 +20,6 @@
use anyhow::{ensure, Context, Result};
use bytes::Bytes;
-use num_traits::FromPrimitive;
use openssl::pkey::{self, PKey};
use openssl::x509::X509;
use std::fs::File;
@@ -70,13 +69,13 @@
#[derive(Debug)]
struct Signature {
- /// TODO(b/246254355): Change the type of signature_algorithm_id to SignatureAlgorithmID
- signature_algorithm_id: u32,
+ /// Option is used here to allow us to ignore unsupported algorithm.
+ signature_algorithm_id: Option<SignatureAlgorithmID>,
signature: LengthPrefixed<Bytes>,
}
struct Digest {
- signature_algorithm_id: u32,
+ signature_algorithm_id: Option<SignatureAlgorithmID>,
digest: LengthPrefixed<Bytes>,
}
@@ -85,9 +84,9 @@
/// Verifies APK Signature Scheme v3 signatures of the provided APK and returns the public key
/// 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)?;
+pub fn verify<P: AsRef<Path>>(apk_path: P) -> Result<Box<[u8]>> {
+ let apk = File::open(apk_path.as_ref())?;
+ let mut sections = ApkSections::new(apk)?;
find_signer_and_then(&mut sections, |(signer, sections)| signer.verify(sections))
}
@@ -116,9 +115,9 @@
}
/// 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]>> {
- let f = File::open(path.as_ref())?;
- let mut sections = ApkSections::new(f)?;
+pub fn get_public_key_der<P: AsRef<Path>>(apk_path: P) -> Result<Box<[u8]>> {
+ let apk = File::open(apk_path.as_ref())?;
+ let mut sections = ApkSections::new(apk)?;
find_signer_and_then(&mut sections, |(signer, _)| {
Ok(signer.public_key.to_vec().into_boxed_slice())
})
@@ -127,7 +126,7 @@
/// Gets the v4 [apk_digest].
///
/// [apk_digest]: https://source.android.com/docs/security/apksigning/v4#apk-digest
-pub fn pick_v4_apk_digest<R: Read + Seek>(apk: R) -> Result<(u32, Box<[u8]>)> {
+pub fn pick_v4_apk_digest<R: Read + Seek>(apk: R) -> Result<(SignatureAlgorithmID, Box<[u8]>)> {
let mut sections = ApkSections::new(apk)?;
let mut block = sections.find_signature(APK_SIGNATURE_SCHEME_V3_BLOCK_ID)?;
let signers = block.read::<Signers>()?;
@@ -142,12 +141,12 @@
Ok(self
.signatures
.iter()
- .filter(|sig| SignatureAlgorithmID::from_u32(sig.signature_algorithm_id).is_some())
- .max_by_key(|sig| SignatureAlgorithmID::from_u32(sig.signature_algorithm_id).unwrap())
+ .filter(|sig| sig.signature_algorithm_id.is_some())
+ .max_by_key(|sig| sig.signature_algorithm_id.unwrap().content_digest_algorithm())
.context("No supported signatures found")?)
}
- fn pick_v4_apk_digest(&self) -> Result<(u32, Box<[u8]>)> {
+ fn pick_v4_apk_digest(&self) -> Result<(SignatureAlgorithmID, Box<[u8]>)> {
let strongest = self.strongest_signature()?;
let signed_data: SignedData = self.signed_data.slice(..).read()?;
let digest = signed_data
@@ -155,7 +154,10 @@
.iter()
.find(|&dig| dig.signature_algorithm_id == strongest.signature_algorithm_id)
.context("Digest not found")?;
- Ok((digest.signature_algorithm_id, digest.digest.as_ref().to_vec().into_boxed_slice()))
+ Ok((
+ digest.signature_algorithm_id.context("Unsupported algorithm")?,
+ digest.digest.as_ref().to_vec().into_boxed_slice(),
+ ))
}
/// The steps in this method implements APK Signature Scheme v3 verification step 3.
@@ -195,7 +197,8 @@
.iter()
.find(|&dig| dig.signature_algorithm_id == strongest.signature_algorithm_id)
.unwrap(); // ok to unwrap since we check if two lists are the same above
- let computed = sections.compute_digest(digest.signature_algorithm_id)?;
+ let computed = sections
+ .compute_digest(digest.signature_algorithm_id.context("Unsupported algorithm")?)?;
// 6. Verify that the computed digest is identical to the corresponding digest from digests.
ensure!(
@@ -226,7 +229,8 @@
signature: &Signature,
public_key: &PKey<pkey::Public>,
) -> Result<()> {
- let mut verifier = SignatureAlgorithmID::from_u32(signature.signature_algorithm_id)
+ let mut verifier = signature
+ .signature_algorithm_id
.context("Unsupported algorithm")?
.new_verifier(public_key)?;
verifier.update(data)?;
@@ -278,36 +282,3 @@
pub(crate) fn to_hex_string(buf: &[u8]) -> String {
buf.iter().map(|b| format!("{:02X}", b)).collect()
}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use std::fs::File;
-
- #[test]
- fn test_pick_v4_apk_digest_only_with_v3_dsa_sha256() {
- check_v4_apk_digest(
- "tests/data/v3-only-with-dsa-sha256-1024.apk",
- SIGNATURE_DSA_WITH_SHA256,
- "0DF2426EA33AEDAF495D88E5BE0C6A1663FF0A81C5ED12D5B2929AE4B4300F2F",
- );
- }
-
- #[test]
- fn test_pick_v4_apk_digest_only_with_v3_pkcs1_sha512() {
- check_v4_apk_digest(
- "tests/data/v3-only-with-rsa-pkcs1-sha512-1024.apk",
- SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512,
- "9B9AE02DA60B18999BF541790F00D380006FDF0655C3C482AA0BB0AF17CF7A42\
- ECF56B973518546C9080B2FEF83027E895ED2882BFC88EA19790BBAB29AF53B3",
- );
- }
-
- fn check_v4_apk_digest(apk_filename: &str, expected_algorithm: u32, expected_digest: &str) {
- let apk_file = File::open(apk_filename).unwrap();
- let (signature_algorithm_id, apk_digest) = pick_v4_apk_digest(apk_file).unwrap();
-
- assert_eq!(expected_algorithm, signature_algorithm_id);
- assert_eq!(expected_digest, to_hex_string(apk_digest.as_ref()));
- }
-}
diff --git a/libs/apkverify/tests/apkverify_test.rs b/libs/apkverify/tests/apkverify_test.rs
index 3818259..5bd901d 100644
--- a/libs/apkverify/tests/apkverify_test.rs
+++ b/libs/apkverify/tests/apkverify_test.rs
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-use apkverify::{testing::assert_contains, verify};
-use std::matches;
+use apkverify::{
+ get_public_key_der, pick_v4_apk_digest, testing::assert_contains, verify, SignatureAlgorithmID,
+};
+use std::{fs, matches, path::Path};
const KEY_NAMES_DSA: &[&str] = &["1024", "2048", "3072"];
const KEY_NAMES_ECDSA: &[&str] = &["p256", "p384", "p521"];
@@ -33,8 +35,8 @@
}
#[test]
-fn test_verify_v3() {
- assert!(verify("tests/data/test.apex").is_ok());
+fn apex_signed_with_v3_rsa_pkcs1_sha512_is_valid() {
+ validate_apk("tests/data/test.apex", SignatureAlgorithmID::RsaPkcs1V15WithSha512);
}
#[test]
@@ -46,34 +48,53 @@
}
}
+/// TODO(b/197052981): DSA algorithm is not yet supported.
#[test]
-fn test_verify_v3_ecdsa_sha256() {
- for key_name in KEY_NAMES_ECDSA.iter() {
- assert!(verify(format!("tests/data/v3-only-with-ecdsa-sha256-{}.apk", key_name)).is_ok());
- }
-}
-
-#[test]
-fn test_verify_v3_ecdsa_sha512() {
- for key_name in KEY_NAMES_ECDSA.iter() {
- assert!(verify(format!("tests/data/v3-only-with-ecdsa-sha512-{}.apk", key_name)).is_ok());
- }
-}
-
-#[test]
-fn test_verify_v3_rsa_sha256() {
- for key_name in KEY_NAMES_RSA.iter() {
- assert!(
- verify(format!("tests/data/v3-only-with-rsa-pkcs1-sha256-{}.apk", key_name)).is_ok()
+fn apks_signed_with_v3_dsa_sha256_have_valid_apk_digest() {
+ for key_name in KEY_NAMES_DSA.iter() {
+ validate_apk_digest(
+ format!("tests/data/v3-only-with-dsa-sha256-{}.apk", key_name),
+ SignatureAlgorithmID::DsaWithSha256,
);
}
}
#[test]
-fn test_verify_v3_rsa_sha512() {
+fn apks_signed_with_v3_ecdsa_sha256_are_valid() {
+ for key_name in KEY_NAMES_ECDSA.iter() {
+ validate_apk(
+ format!("tests/data/v3-only-with-ecdsa-sha256-{}.apk", key_name),
+ SignatureAlgorithmID::EcdsaWithSha256,
+ );
+ }
+}
+
+#[test]
+fn apks_signed_with_v3_ecdsa_sha512_are_valid() {
+ for key_name in KEY_NAMES_ECDSA.iter() {
+ validate_apk(
+ format!("tests/data/v3-only-with-ecdsa-sha512-{}.apk", key_name),
+ SignatureAlgorithmID::EcdsaWithSha512,
+ );
+ }
+}
+
+#[test]
+fn apks_signed_with_v3_rsa_pkcs1_sha256_are_valid() {
for key_name in KEY_NAMES_RSA.iter() {
- assert!(
- verify(format!("tests/data/v3-only-with-rsa-pkcs1-sha512-{}.apk", key_name)).is_ok()
+ validate_apk(
+ format!("tests/data/v3-only-with-rsa-pkcs1-sha256-{}.apk", key_name),
+ SignatureAlgorithmID::RsaPkcs1V15WithSha256,
+ );
+ }
+}
+
+#[test]
+fn apks_signed_with_v3_rsa_pkcs1_sha512_are_valid() {
+ for key_name in KEY_NAMES_RSA.iter() {
+ validate_apk(
+ format!("tests/data/v3-only-with-rsa-pkcs1-sha512-{}.apk", key_name),
+ SignatureAlgorithmID::RsaPkcs1V15WithSha512,
);
}
}
@@ -166,21 +187,88 @@
}
#[test]
-fn test_verify_v3_unknown_additional_attr() {
- assert!(verify("tests/data/v3-only-unknown-additional-attr.apk").is_ok());
+fn apk_signed_with_v3_unknown_additional_attr_is_valid() {
+ validate_apk(
+ "tests/data/v3-only-unknown-additional-attr.apk",
+ SignatureAlgorithmID::RsaPkcs1V15WithSha256,
+ );
}
#[test]
-fn test_verify_v3_unknown_pair_in_apk_sig_block() {
- assert!(verify("tests/data/v3-only-unknown-pair-in-apk-sig-block.apk").is_ok());
+fn apk_signed_with_v3_unknown_pair_in_apk_sig_block_is_valid() {
+ validate_apk(
+ "tests/data/v3-only-unknown-pair-in-apk-sig-block.apk",
+ SignatureAlgorithmID::RsaPkcs1V15WithSha256,
+ );
}
#[test]
-fn test_verify_v3_ignorable_unsupported_sig_algs() {
- assert!(verify("tests/data/v3-only-with-ignorable-unsupported-sig-algs.apk").is_ok());
+fn apk_signed_with_v3_ignorable_unsupported_sig_algs_is_valid() {
+ validate_apk(
+ "tests/data/v3-only-with-ignorable-unsupported-sig-algs.apk",
+ SignatureAlgorithmID::RsaPkcs1V15WithSha256,
+ );
}
#[test]
-fn test_verify_v3_stamp() {
- assert!(verify("tests/data/v3-only-with-stamp.apk").is_ok());
+fn apk_signed_with_v3_stamp_is_valid() {
+ validate_apk("tests/data/v3-only-with-stamp.apk", SignatureAlgorithmID::EcdsaWithSha256);
+}
+
+fn validate_apk<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID) {
+ validate_apk_public_key(&apk_path);
+ validate_apk_digest(&apk_path, expected_algorithm_id);
+}
+
+/// Validates that the following public keys are equal:
+/// * public key from verification
+/// * public key extracted from apk without verification
+/// * expected public key from the corresponding .der file
+fn validate_apk_public_key<P: AsRef<Path>>(apk_path: P) {
+ let public_key_from_verification = verify(&apk_path);
+ let public_key_from_verification =
+ public_key_from_verification.expect("Error in verification result");
+
+ let expected_public_key_path = format!("{}.der", apk_path.as_ref().to_str().unwrap());
+ assert_bytes_eq_to_data_in_file(&public_key_from_verification, expected_public_key_path);
+
+ let public_key_from_apk = get_public_key_der(&apk_path);
+ let public_key_from_apk =
+ public_key_from_apk.expect("Error when extracting public key from apk");
+ assert_eq!(
+ public_key_from_verification, public_key_from_apk,
+ "Public key extracted directly from apk does not match the public key from verification."
+ );
+}
+
+/// Validates that the following apk_digest are equal:
+/// * apk_digest directly extracted from apk without computation
+/// * expected apk digest from the corresponding .apk_digest file
+fn validate_apk_digest<P: AsRef<Path>>(apk_path: P, expected_algorithm_id: SignatureAlgorithmID) {
+ let apk = fs::File::open(&apk_path).expect("Unabled to open apk file");
+
+ let (signature_algorithm_id, digest_from_apk) =
+ pick_v4_apk_digest(apk).expect("Error when extracting apk digest.");
+
+ assert_eq!(expected_algorithm_id, signature_algorithm_id);
+ let expected_digest_path = format!("{}.apk_digest", apk_path.as_ref().to_str().unwrap());
+ assert_bytes_eq_to_data_in_file(&digest_from_apk, expected_digest_path);
+}
+
+fn assert_bytes_eq_to_data_in_file<P: AsRef<Path> + std::fmt::Display>(
+ bytes_data: &[u8],
+ expected_data_path: P,
+) {
+ assert!(
+ fs::metadata(&expected_data_path).is_ok(),
+ "File does not exist. You can re-create it with:\n$ echo -en {} > {}\n",
+ bytes_data.iter().map(|b| format!("\\\\x{:02x}", b)).collect::<String>(),
+ expected_data_path
+ );
+ let expected_data = fs::read(&expected_data_path).unwrap();
+ assert_eq!(
+ expected_data, bytes_data,
+ "Actual data does not match the data from: {}",
+ expected_data_path
+ );
}
diff --git a/libs/apkverify/tests/data/README.md b/libs/apkverify/tests/data/README.md
index 7556921..7ba5d8e 100644
--- a/libs/apkverify/tests/data/README.md
+++ b/libs/apkverify/tests/data/README.md
@@ -1,6 +1,10 @@
+# About test data
+
+## .apk
+
test.apex is copied from ADBD apex built in AOSP.
-```sh
+```bash
$ apksigner verify -v test.apex
Verifies
Verified using v1 scheme (JAR signing): false
@@ -11,4 +15,20 @@
Number of signers: 1
```
-APK files are copied from tools/apksig/src/test/resources/com/android/apksig/.
+APK files are copied from [tools/apksig/src/test/resources/com/android/apksig/](https://cs.android.com/android/platform/superproject/+/master:tools/apksig/src/test/resources/com/android/apksig/;l=1;drc=c2a8da1913d7fb359b023bf200e31d75ff22a5c3).
+
+## .der
+
+`.der` files contain the expected public keys. When validating the public keys in tests, if the corresponding `.der` file is missing, there will be some text as follows in the failure message:
+
+```bash
+$ echo -en \\x30\\x59\\x30\\x13\\x06\\x07\\x2a\\x86\\x48\\xce\\x3d\\x02\\x01\\x06\\x08\\x2a\\x86\\x48\\xce\\x3d\\x03\\x01\\x07\\x03\\x42\\x00\\x04\\xa6\\x5f\\x11\\x3d\\x22\\xcb\\x49\\x13\\x90\\x83\\x07\\xac\\x31\\xee\\x2b\\xa0\\xe9\\x13\\x8b\\x78\\x5f\\xac\\x65\\x36\\xd1\\x4e\\xa2\\xce\\x90\\xd2\\xb4\\xbf\\xe1\\x94\\xb5\\x0c\\xdc\\x8e\\x16\\x9f\\x54\\xa7\\x3a\\x99\\x1e\\xf0\\xfa\\x76\\x32\\x98\\x25\\xbe\\x07\\x8c\\xc7\\x82\\x74\\x07\\x03\\xda\\x44\\xb4\\xd7\\xeb > tests/data/v3-only-with-stamp.apk.der
+```
+
+You just need to execute this command in the folder `libs/apkverify` to generate the corresponding `.der` file. After it, you can run the test again, and the test will pass.
+
+Otherwise, you can also use the command `apksigner verify --min-sdk-version 24 --print-certs-pem tests/data/v3-only-with-stamp.apk` to get the public key in `.pem` format and then convert it to `.der` with openssl.
+
+## .apk_digest
+
+`.apk_digest` files contain the expected [apk_digest](https://source.android.com/docs/security/features/apksigning/v4#apk-digest). They are generated the same way as `.der` when validating the apk_digest.
\ No newline at end of file
diff --git a/libs/apkverify/tests/data/test.apex.apk_digest b/libs/apkverify/tests/data/test.apex.apk_digest
new file mode 100644
index 0000000..09a7340
--- /dev/null
+++ b/libs/apkverify/tests/data/test.apex.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/test.apex.der b/libs/apkverify/tests/data/test.apex.der
new file mode 100644
index 0000000..abeb1eb
--- /dev/null
+++ b/libs/apkverify/tests/data/test.apex.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-unknown-additional-attr.apk.apk_digest b/libs/apkverify/tests/data/v3-only-unknown-additional-attr.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-unknown-additional-attr.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-unknown-additional-attr.apk.der b/libs/apkverify/tests/data/v3-only-unknown-additional-attr.apk.der
new file mode 100644
index 0000000..27535ca
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-unknown-additional-attr.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-unknown-pair-in-apk-sig-block.apk.apk_digest b/libs/apkverify/tests/data/v3-only-unknown-pair-in-apk-sig-block.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-unknown-pair-in-apk-sig-block.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-unknown-pair-in-apk-sig-block.apk.der b/libs/apkverify/tests/data/v3-only-unknown-pair-in-apk-sig-block.apk.der
new file mode 100644
index 0000000..6aafd09
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-unknown-pair-in-apk-sig-block.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-dsa-sha256-1024.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-dsa-sha256-1024.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-dsa-sha256-1024.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-dsa-sha256-2048.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-dsa-sha256-2048.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-dsa-sha256-2048.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-dsa-sha256-3072.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-dsa-sha256-3072.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-dsa-sha256-3072.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p256.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p256.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p256.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p256.apk.der b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p256.apk.der
new file mode 100644
index 0000000..01927af
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p256.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p384.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p384.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p384.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p384.apk.der b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p384.apk.der
new file mode 100644
index 0000000..95baf40
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p384.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p521.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p521.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p521.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p521.apk.der b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p521.apk.der
new file mode 100644
index 0000000..b68f925
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha256-p521.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p256.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p256.apk.apk_digest
new file mode 100644
index 0000000..4fe1b65
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p256.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p256.apk.der b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p256.apk.der
new file mode 100644
index 0000000..01927af
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p256.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p384.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p384.apk.apk_digest
new file mode 100644
index 0000000..4fe1b65
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p384.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p384.apk.der b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p384.apk.der
new file mode 100644
index 0000000..95baf40
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p384.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p521.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p521.apk.apk_digest
new file mode 100644
index 0000000..4fe1b65
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p521.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p521.apk.der b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p521.apk.der
new file mode 100644
index 0000000..b68f925
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ecdsa-sha512-p521.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ignorable-unsupported-sig-algs.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-ignorable-unsupported-sig-algs.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ignorable-unsupported-sig-algs.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-ignorable-unsupported-sig-algs.apk.der b/libs/apkverify/tests/data/v3-only-with-ignorable-unsupported-sig-algs.apk.der
new file mode 100644
index 0000000..96dc543
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-ignorable-unsupported-sig-algs.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-1024.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-1024.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-1024.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-1024.apk.der b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-1024.apk.der
new file mode 100644
index 0000000..6aafd09
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-1024.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-16384.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-16384.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-16384.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-16384.apk.der b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-16384.apk.der
new file mode 100644
index 0000000..31abdc7
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-16384.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-2048.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-2048.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-2048.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-2048.apk.der b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-2048.apk.der
new file mode 100644
index 0000000..96dc543
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-2048.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-3072.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-3072.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-3072.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-3072.apk.der b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-3072.apk.der
new file mode 100644
index 0000000..bd70f5f
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-3072.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-4096.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-4096.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-4096.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-4096.apk.der b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-4096.apk.der
new file mode 100644
index 0000000..951648e
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-4096.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-8192.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-8192.apk.apk_digest
new file mode 100644
index 0000000..c5aec18
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-8192.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-8192.apk.der b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-8192.apk.der
new file mode 100644
index 0000000..15e5edf
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha256-8192.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-1024.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-1024.apk.apk_digest
new file mode 100644
index 0000000..4fe1b65
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-1024.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-1024.apk.der b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-1024.apk.der
new file mode 100644
index 0000000..6aafd09
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-1024.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-16384.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-16384.apk.apk_digest
new file mode 100644
index 0000000..4fe1b65
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-16384.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-16384.apk.der b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-16384.apk.der
new file mode 100644
index 0000000..31abdc7
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-16384.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-2048.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-2048.apk.apk_digest
new file mode 100644
index 0000000..4fe1b65
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-2048.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-2048.apk.der b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-2048.apk.der
new file mode 100644
index 0000000..96dc543
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-2048.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-3072.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-3072.apk.apk_digest
new file mode 100644
index 0000000..4fe1b65
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-3072.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-3072.apk.der b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-3072.apk.der
new file mode 100644
index 0000000..bd70f5f
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-3072.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-4096.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-4096.apk.apk_digest
new file mode 100644
index 0000000..4fe1b65
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-4096.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-4096.apk.der b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-4096.apk.der
new file mode 100644
index 0000000..951648e
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-4096.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-8192.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-8192.apk.apk_digest
new file mode 100644
index 0000000..4fe1b65
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-8192.apk.apk_digest
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-8192.apk.der b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-8192.apk.der
new file mode 100644
index 0000000..15e5edf
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-rsa-pkcs1-sha512-8192.apk.der
Binary files differ
diff --git a/libs/apkverify/tests/data/v3-only-with-stamp.apk.apk_digest b/libs/apkverify/tests/data/v3-only-with-stamp.apk.apk_digest
new file mode 100644
index 0000000..a29c2a9
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-stamp.apk.apk_digest
@@ -0,0 +1 @@
+bk¶GÀ§ÿ¥/ØèEù@=^'÷¥¨u.G³4_¸/\
\ No newline at end of file
diff --git a/libs/apkverify/tests/data/v3-only-with-stamp.apk.der b/libs/apkverify/tests/data/v3-only-with-stamp.apk.der
new file mode 100644
index 0000000..01927af
--- /dev/null
+++ b/libs/apkverify/tests/data/v3-only-with-stamp.apk.der
Binary files differ
diff --git a/libs/idsig/src/apksigv4.rs b/libs/idsig/src/apksigv4.rs
index f8ca184..c1b6495 100644
--- a/libs/idsig/src/apksigv4.rs
+++ b/libs/idsig/src/apksigv4.rs
@@ -15,7 +15,7 @@
*/
use anyhow::{anyhow, bail, Context, Result};
-use apkverify::pick_v4_apk_digest;
+use apkverify::{pick_v4_apk_digest, SignatureAlgorithmID};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive};
@@ -69,7 +69,7 @@
/// Public key of the signer in ASN.1 DER form. This must match the `x509_certificate` field.
pub public_key: Box<[u8]>,
/// Signature algorithm used to sign this file.
- pub signature_algorithm_id: SignatureAlgorithmId,
+ pub signature_algorithm_id: SignatureAlgorithmID,
/// The signature of this file.
pub signature: Box<[u8]>,
}
@@ -114,40 +114,6 @@
}
}
-/// Signature algorithm that can be used for idsig file
-#[derive(Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
-#[allow(non_camel_case_types)]
-#[repr(u32)]
-pub enum SignatureAlgorithmId {
- /// RSASSA-PSS with SHA2-256 digest, SHA2-256 MGF1, 32 bytes of salt, trailer: 0xbc
- RSASSA_PSS_SHA2_256 = 0x0101,
- /// RSASSA-PSS with SHA2-512 digest, SHA2-512 MGF1, 64 bytes of salt, trailer: 0xbc
- RSASSA_PSS_SHA2_512 = 0x0102,
- /// RSASSA-PKCS1-v1_5 with SHA2-256 digest.
- RSASSA_PKCS1_SHA2_256 = 0x0103,
- /// RSASSA-PKCS1-v1_5 with SHA2-512 digest.
- RSASSA_PKCS1_SHA2_512 = 0x0104,
- /// ECDSA with SHA2-256 digest.
- ECDSA_SHA2_256 = 0x0201,
- /// ECDSA with SHA2-512 digest.
- ECDSA_SHA2_512 = 0x0202,
- /// DSA with SHA2-256 digest
- DSA_SHA2_256 = 0x0301,
-}
-
-impl SignatureAlgorithmId {
- fn from(val: u32) -> Result<SignatureAlgorithmId> {
- Self::from_u32(val)
- .with_context(|| format!("{:#06x} is an unsupported signature algorithm", val))
- }
-}
-
-impl Default for SignatureAlgorithmId {
- fn default() -> Self {
- SignatureAlgorithmId::DSA_SHA2_256
- }
-}
-
impl<R: Read + Seek> V4Signature<R> {
/// Consumes a stream for an idsig file into a `V4Signature` struct.
pub fn from(mut r: R) -> Result<V4Signature<R>> {
@@ -193,8 +159,7 @@
apk.seek(SeekFrom::Start(start))?;
let (signature_algorithm_id, apk_digest) = pick_v4_apk_digest(apk)?;
- ret.signing_info.signature_algorithm_id =
- SignatureAlgorithmId::from(signature_algorithm_id)?;
+ ret.signing_info.signature_algorithm_id = signature_algorithm_id;
ret.signing_info.apk_digest = apk_digest;
// TODO(jiyong): add a signature to the signing_info struct
@@ -276,7 +241,8 @@
x509_certificate: read_sized_array(&mut r)?,
additional_data: read_sized_array(&mut r)?,
public_key: read_sized_array(&mut r)?,
- signature_algorithm_id: SignatureAlgorithmId::from(r.read_u32::<LittleEndian>()?)?,
+ signature_algorithm_id: SignatureAlgorithmID::from_u32(r.read_u32::<LittleEndian>()?)
+ .context("Unsupported signature algorithm")?,
signature: read_sized_array(&mut r)?,
})
}
@@ -291,7 +257,7 @@
write_sized_array(&mut w, &self.x509_certificate)?;
write_sized_array(&mut w, &self.additional_data)?;
write_sized_array(&mut w, &self.public_key)?;
- w.write_u32::<LittleEndian>(self.signature_algorithm_id.to_u32().unwrap())?;
+ w.write_u32::<LittleEndian>(self.signature_algorithm_id.to_u32())?;
write_sized_array(&mut w, &self.signature)?;
// Determine the size of signing_info, and write it in front of the struct where the value
@@ -358,7 +324,7 @@
a8585c38d7f654835eb219ae9e176b44e86dcb23153e3d9d6",
hexstring_from(si.signature.as_ref())
);
- assert_eq!(SignatureAlgorithmId::DSA_SHA2_256, si.signature_algorithm_id);
+ assert_eq!(SignatureAlgorithmID::DsaWithSha256, si.signature_algorithm_id);
assert_eq!(36864, parsed.merkle_tree_size);
assert_eq!(2251, parsed.merkle_tree_offset);
diff --git a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
index eeadae1..e5eee27 100644
--- a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
+++ b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
@@ -39,6 +39,7 @@
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
@@ -56,6 +57,11 @@
private static final String SYSTEM_SERVER_COMPILER_FILTER_PROP_NAME =
"dalvik.vm.systemservercompilerfilter";
+ private static final String BOOTLOADER_TIME_PROP_NAME = "ro.boot.boottime";
+ private static final String BOOTLOADER_PREFIX = "bootloader-";
+ private static final String BOOTLOADER_TIME = "bootloader_time";
+ private static final String BOOTLOADER_PHASE_SW = "SW";
+
/** Boot time test related variables */
private static final int REINSTALL_APEX_RETRY_INTERVAL_MS = 5 * 1000;
private static final int REINSTALL_APEX_TIMEOUT_SEC = 15;
@@ -89,72 +95,124 @@
}
@Test
- public void testBootEnableAndDisablePKVM() throws Exception {
- skipIfPKVMStatusSwitchNotSupported();
-
- List<Double> bootWithPKVMEnableTime = new ArrayList<>(ROUND_COUNT);
- List<Double> bootWithoutPKVMEnableTime = new ArrayList<>(ROUND_COUNT);
-
- for (int round = 0; round < ROUND_COUNT; ++round) {
-
- setPKVMStatusWithRebootToBootloader(true);
- long start = System.nanoTime();
- rebootFromBootloaderAndWaitBootCompleted();
- long elapsedWithPKVMEnable = System.nanoTime() - start;
- double elapsedSec = elapsedWithPKVMEnable / NANOS_IN_SEC;
- bootWithPKVMEnableTime.add(elapsedSec);
- CLog.i("Boot time with PKVM enable took " + elapsedSec + "s");
-
- setPKVMStatusWithRebootToBootloader(false);
- start = System.nanoTime();
- rebootFromBootloaderAndWaitBootCompleted();
- long elapsedWithoutPKVMEnable = System.nanoTime() - start;
- elapsedSec = elapsedWithoutPKVMEnable / NANOS_IN_SEC;
- bootWithoutPKVMEnableTime.add(elapsedSec);
- CLog.i("Boot time with PKVM disable took " + elapsedSec + "s");
- }
-
- reportMetric(bootWithPKVMEnableTime, "boot_time_with_pkvm_enable", "s");
- reportMetric(bootWithoutPKVMEnableTime, "boot_time_with_pkvm_disable", "s");
+ public void testBootEnablePKVM() throws Exception {
+ enableDisablePKVMTestHelper(true);
}
@Test
- public void testBootWithAndWithoutCompOS() throws Exception {
- assumeFalse(isCuttlefish());
+ public void testBootDisablePKVM() throws Exception {
+ enableDisablePKVMTestHelper(false);
+ }
- List<Double> bootWithCompOsTime = new ArrayList<>(ROUND_COUNT);
- List<Double> bootWithoutCompOsTime = new ArrayList<>(ROUND_COUNT);
+ @Test
+ public void testBootWithCompOS() throws Exception {
+ composTestHelper(true);
+ }
+ @Test
+ public void testBootWithoutCompOS() throws Exception {
+ composTestHelper(false);
+ }
+
+ private void updateBootloaderTimeInfo(Map<String, List<Double>> bootloaderTime)
+ throws Exception {
+
+ String bootLoaderVal = getDevice().getProperty(BOOTLOADER_TIME_PROP_NAME);
+ // Sample Output : 1BLL:89,1BLE:590,2BLL:0,2BLE:1344,SW:6734,KL:1193
+ if (bootLoaderVal != null) {
+ String[] bootLoaderPhases = bootLoaderVal.split(",");
+ double bootLoaderTotalTime = 0d;
+ for (String bootLoaderPhase : bootLoaderPhases) {
+ String[] bootKeyVal = bootLoaderPhase.split(":");
+ String key = String.format("%s%s", BOOTLOADER_PREFIX, bootKeyVal[0]);
+
+ bootloaderTime.computeIfAbsent(key,
+ k -> new ArrayList<>()).add(Double.parseDouble(bootKeyVal[1]));
+ // SW is the time spent on the warning screen. So ignore it in
+ // final boot time calculation.
+ if (BOOTLOADER_PHASE_SW.equalsIgnoreCase(bootKeyVal[0])) {
+ continue;
+ }
+ bootLoaderTotalTime += Double.parseDouble(bootKeyVal[1]);
+ }
+ bootloaderTime.computeIfAbsent(BOOTLOADER_TIME,
+ k -> new ArrayList<>()).add(bootLoaderTotalTime);
+ }
+ }
+
+ private Double getDmesgBootTime() throws Exception {
+
+ CommandRunner android = new CommandRunner(getDevice());
+ String result = android.run("dmesg");
+ Pattern pattern = Pattern.compile("\\[(.*)\\].*sys.boot_completed=1.*");
+ for (String line : result.split("[\r\n]+")) {
+ Matcher matcher = pattern.matcher(line);
+ if (matcher.find()) {
+ return Double.valueOf(matcher.group(1));
+ }
+ }
+ throw new IllegalArgumentException("Failed to get boot time info.");
+ }
+
+ private void enableDisablePKVMTestHelper(boolean isEnable) throws Exception {
+ skipIfPKVMStatusSwitchNotSupported();
+
+ List<Double> bootDmesgTime = new ArrayList<>(ROUND_COUNT);
+ Map<String, List<Double>> bootloaderTime = new HashMap<>();
+
+ setPKVMStatusWithRebootToBootloader(isEnable);
+ rebootFromBootloaderAndWaitBootCompleted();
for (int round = 0; round < ROUND_COUNT; ++round) {
-
- // Boot time with compilation OS test.
- reInstallApex(REINSTALL_APEX_TIMEOUT_SEC);
- compileStagedApex(COMPILE_STAGED_APEX_TIMEOUT_SEC);
getDevice().nonBlockingReboot();
- long start = System.nanoTime();
waitForBootCompleted();
- long elapsedWithCompOS = System.nanoTime() - start;
- double elapsedSec = elapsedWithCompOS / NANOS_IN_SEC;
- bootWithCompOsTime.add(elapsedSec);
- CLog.i("Boot time with compilation OS took " + elapsedSec + "s");
- // Boot time without compilation OS test.
- reInstallApex(REINSTALL_APEX_TIMEOUT_SEC);
- getDevice().nonBlockingReboot();
- start = System.nanoTime();
- waitForBootCompleted();
- long elapsedWithoutCompOS = System.nanoTime() - start;
- elapsedSec = elapsedWithoutCompOS / NANOS_IN_SEC;
- bootWithoutCompOsTime.add(elapsedSec);
- CLog.i("Boot time without compilation OS took " + elapsedSec + "s");
+ updateBootloaderTimeInfo(bootloaderTime);
+
+ double elapsedSec = getDmesgBootTime();
+ bootDmesgTime.add(elapsedSec);
}
- reportMetric(bootWithCompOsTime, "boot_time_with_compos", "s");
- reportMetric(bootWithoutCompOsTime, "boot_time_without_compos", "s");
+ String suffix = "";
+ if (isEnable) {
+ suffix = "enable";
+ } else {
+ suffix = "disable";
+ }
+
+ reportMetric(bootDmesgTime, "dmesg_boot_time_with_pkvm_" + suffix, "s");
+ reportAggregatedMetrics(bootloaderTime,
+ "bootloader_time_with_pkvm_" + suffix, "ms");
+ }
+
+ private void composTestHelper(boolean isWithCompos) throws Exception {
+ assumeFalse("Skip on CF; too slow", isCuttlefish());
+
+ List<Double> bootDmesgTime = new ArrayList<>(ROUND_COUNT);
+
+ for (int round = 0; round < ROUND_COUNT; ++round) {
+ reInstallApex(REINSTALL_APEX_TIMEOUT_SEC);
+ if (isWithCompos) {
+ compileStagedApex(COMPILE_STAGED_APEX_TIMEOUT_SEC);
+ }
+ getDevice().nonBlockingReboot();
+ waitForBootCompleted();
+
+ double elapsedSec = getDmesgBootTime();
+ bootDmesgTime.add(elapsedSec);
+ }
+
+ String suffix = "";
+ if (isWithCompos) {
+ suffix = "with_compos";
+ } else {
+ suffix = "without_compos";
+ }
+
+ reportMetric(bootDmesgTime, "dmesg_boot_time_" + suffix, "s");
}
private void skipIfPKVMStatusSwitchNotSupported() throws Exception {
- assumeFalse(isCuttlefish());
+ assumeFalse("Skip on CF; can't reboot to bootloader", isCuttlefish());
if (!getDevice().isStateBootloaderOrFastbootd()) {
getDevice().rebootIntoBootloader();
@@ -171,12 +229,22 @@
}
private void reportMetric(List<Double> data, String name, String unit) {
+ CLog.d("Report metric " + name + "(" + unit + ") : " + data.toString());
Map<String, Double> stats = mMetricsProcessor.computeStats(data, name, unit);
for (Map.Entry<String, Double> entry : stats.entrySet()) {
+ CLog.d("Add test metrics " + entry.getKey() + " : " + entry.getValue().toString());
mMetrics.addTestMetric(entry.getKey(), entry.getValue().toString());
}
}
+ private void reportAggregatedMetrics(Map<String, List<Double>> bootloaderTime,
+ String prefix, String unit) {
+
+ for (Map.Entry<String, List<Double>> entry : bootloaderTime.entrySet()) {
+ reportMetric(entry.getValue(), prefix + "_" + entry.getKey(), unit);
+ }
+ }
+
private void setPKVMStatusWithRebootToBootloader(boolean isEnable) throws Exception {
if (!getDevice().isStateBootloaderOrFastbootd()) {