fix: file hash & apk cert hash
This commit is contained in:
@ -35,20 +35,20 @@ impl ApkSigningBlock {
|
||||
for (k, v) in &self.data {
|
||||
match *k {
|
||||
V2_SIG_BLOCK_ID => {
|
||||
let v2 = get_length_prefixed_u32_sequence(v)?;
|
||||
let v2_block = remove_prefix_layers(v)?;
|
||||
let v2_block = get_lv_sequence(v2_block)?;
|
||||
ensure!(
|
||||
v2.len() == 1,
|
||||
"Expected 1 element in signing block got {}",
|
||||
v2.len()
|
||||
v2_block.len() == 3,
|
||||
"Expected 3 elements in signing block got {}",
|
||||
v2_block.len()
|
||||
);
|
||||
|
||||
let v2 = get_length_prefixed_u32_sequence(v2[0])?;
|
||||
let signed_data = get_sequence(v2[0])?;
|
||||
let signed_data = get_lv_sequence(v2_block[0])?;
|
||||
let digests = get_sequence_kv(signed_data[0])?;
|
||||
let certificates = get_sequence(signed_data[1])?;
|
||||
let certificates = get_lv_sequence(signed_data[1])?;
|
||||
let attributes = get_sequence_kv(signed_data[2])?;
|
||||
let signatures = get_sequence_kv(v2[1])?;
|
||||
let public_key = v2[2];
|
||||
|
||||
let signatures = get_sequence_kv(v2_block[1])?;
|
||||
let public_key = v2_block[2];
|
||||
let digests: HashMap<u32, &[u8]> = HashMap::from_iter(digests);
|
||||
sigs.push(ApkSignatureBlock::V2 {
|
||||
attributes: HashMap::from_iter(
|
||||
@ -60,23 +60,20 @@ impl ApkSigningBlock {
|
||||
});
|
||||
}
|
||||
V3_SIG_BLOCK_ID => {
|
||||
let v3 = get_length_prefixed_u32_sequence(v)?;
|
||||
ensure!(
|
||||
v3.len() == 1,
|
||||
"Expected 1 element in signing block got {}",
|
||||
v3.len()
|
||||
);
|
||||
let mut v = &v[4..];
|
||||
let mut v3_block = take_lv_u32(&mut v)?;
|
||||
|
||||
let v3 = get_length_prefixed_u32_sequence(v3[0])?;
|
||||
let signed_data = get_sequence(v3[0])?;
|
||||
let digests = get_sequence_kv(signed_data[0])?;
|
||||
let certificates = get_sequence(signed_data[1])?;
|
||||
let min_sdk_signed = u32::from_le_bytes(signed_data[2].try_into()?);
|
||||
let max_sdk_signed = u32::from_le_bytes(signed_data[3].try_into()?);
|
||||
let attributes = get_sequence_kv(signed_data[4])?;
|
||||
let mut signed_data = take_lv_u32(&mut v3_block)?;
|
||||
let digests = get_sequence_kv(take_lv_u32(&mut signed_data)?)?;
|
||||
let certificates = get_lv_sequence(take_lv_u32(&mut signed_data)?)?;
|
||||
let min_sdk_signed = u32::from_le_bytes(signed_data[..4].try_into()?);
|
||||
let max_sdk_signed = u32::from_le_bytes(signed_data[4..8].try_into()?);
|
||||
signed_data = &signed_data[8..];
|
||||
let attributes = get_sequence_kv(take_lv_u32(&mut signed_data)?)?;
|
||||
|
||||
let min_sdk = u32::from_le_bytes(v3[1].try_into()?);
|
||||
let max_sdk = u32::from_le_bytes(v3[2].try_into()?);
|
||||
let min_sdk = u32::from_le_bytes(v3_block[..4].try_into()?);
|
||||
let max_sdk = u32::from_le_bytes(v3_block[4..8].try_into()?);
|
||||
v3_block = &v3_block[8..];
|
||||
|
||||
ensure!(
|
||||
min_sdk_signed == min_sdk,
|
||||
@ -91,8 +88,8 @@ impl ApkSigningBlock {
|
||||
max_sdk
|
||||
);
|
||||
|
||||
let signatures = get_sequence_kv(v3[3])?;
|
||||
let public_key = v3[4];
|
||||
let signatures = get_sequence_kv(take_lv_u32(&mut v3_block)?)?;
|
||||
let public_key = take_lv_u32(&mut v3_block)?;
|
||||
let digests: HashMap<u32, &[u8]> = HashMap::from_iter(digests);
|
||||
|
||||
sigs.push(ApkSignatureBlock::V3 {
|
||||
@ -257,7 +254,7 @@ where
|
||||
let mut magic_buf = [0u8; 16];
|
||||
loop {
|
||||
let magic_pos = zip.seek(SeekFrom::Current(-17))?;
|
||||
if magic_pos <= 4 {
|
||||
if magic_pos <= 16 {
|
||||
bail!("Failed to find signing block");
|
||||
}
|
||||
|
||||
@ -276,19 +273,19 @@ where
|
||||
size2
|
||||
);
|
||||
|
||||
let mut data_bytes = size1 - 8 - 16;
|
||||
let mut sigs = Vec::new();
|
||||
let mut data_bytes = size2 - 16 - 8;
|
||||
let mut blocks = Vec::new();
|
||||
loop {
|
||||
let (k, v) = read_u64_length_prefixed_kv(zip)?;
|
||||
data_bytes -= (v.len() + 4 + 8) as u64;
|
||||
sigs.push((k, v));
|
||||
blocks.push((k, v));
|
||||
if data_bytes == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
zip.seek(SeekFrom::Start(0))?;
|
||||
return Ok(ApkSigningBlock { data: sigs });
|
||||
return Ok(ApkSigningBlock { data: blocks });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -302,41 +299,70 @@ where
|
||||
let k = file.read_u32::<LittleEndian>()?;
|
||||
let v_len = kv_len as usize - 4;
|
||||
let mut v = vec![0; v_len];
|
||||
file.read_exact(v.as_mut_slice())?;
|
||||
file.read_exact(&mut v)?;
|
||||
Ok((k, v))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_u64_length_prefixed_kv(slice: &[u8]) -> Result<(u32, &[u8])> {
|
||||
let kv_len = u64::from_le_bytes(slice[..8].try_into()?);
|
||||
let k = u32::from_le_bytes(slice[8..12].try_into()?);
|
||||
Ok((k, &slice[12..(kv_len as usize - 12)]))
|
||||
fn get_lv_u32_kv(slice: &[u8]) -> Result<(u32, &[u8])> {
|
||||
let data = get_lv_u32(slice)?;
|
||||
let k = u32::from_le_bytes(data[0..4].try_into()?);
|
||||
Ok((k, &data[4..]))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_u32_length_prefixed_kv(slice: &[u8]) -> Result<(u32, &[u8])> {
|
||||
let kv_len = u32::from_le_bytes(slice[..4].try_into()?);
|
||||
let k = u32::from_le_bytes(slice[4..8].try_into()?);
|
||||
Ok((k, &slice[8..(kv_len as usize - 8)]))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_length_prefixed_u32(slice: &[u8]) -> Result<&[u8]> {
|
||||
let len = u32::from_le_bytes(slice[..4].try_into()?);
|
||||
fn get_lv_u32(slice: &[u8]) -> Result<&[u8]> {
|
||||
let len = u32::from_le_bytes(slice[0..4].try_into()?);
|
||||
ensure!(
|
||||
len <= (slice.len() - 4) as u32,
|
||||
"Invalid LV sequence {} > {}",
|
||||
len,
|
||||
slice.len(),
|
||||
);
|
||||
Ok(&slice[4..4 + len as usize])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_length_prefixed_u32_sequence(slice: &[u8]) -> Result<Vec<&[u8]>> {
|
||||
let sequence_len = u32::from_le_bytes(slice[..4].try_into()?);
|
||||
get_sequence(&slice[4..4 + sequence_len as usize])
|
||||
fn take_lv_u32<'a>(slice: &mut &'a[u8]) -> Result<&'a [u8]> {
|
||||
let len = u32::from_le_bytes(slice[..4].try_into()?);
|
||||
ensure!(
|
||||
len <= (slice.len() - 4) as u32,
|
||||
"Invalid LV sequence {} > {}",
|
||||
len,
|
||||
slice.len(),
|
||||
);
|
||||
let (a, b) = slice[4..].split_at(len as usize);
|
||||
*slice = b;
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
/// Remove 1 or more prefix layers
|
||||
/// IDK why android needs 3 layers of prefixed lengths, maybe its just how it was written in Java,
|
||||
/// but it makes no logical sense:
|
||||
/// ```yaml
|
||||
/// L: (1000)
|
||||
/// L: (996)
|
||||
/// L: V (740) - SEQ
|
||||
/// L: V (252) - SEQ
|
||||
/// ```
|
||||
#[inline]
|
||||
fn get_sequence(mut slice: &[u8]) -> Result<Vec<&[u8]>> {
|
||||
fn remove_prefix_layers(mut slice: &[u8]) -> Result<&[u8]> {
|
||||
let l1 = u32::from_le_bytes(slice[..4].try_into()?);
|
||||
loop {
|
||||
slice = &slice[4..];
|
||||
let l2 = u32::from_le_bytes(slice[..4].try_into()?);
|
||||
if l1 != l2 + 4 {
|
||||
return Ok(slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read 0 or more length prefixed values until the slice is empty
|
||||
#[inline]
|
||||
fn get_lv_sequence(mut slice: &[u8]) -> Result<Vec<&[u8]>> {
|
||||
let mut ret = Vec::new();
|
||||
while slice.len() >= 4 {
|
||||
let data = get_length_prefixed_u32(slice)?;
|
||||
let data = get_lv_u32(&slice[..])?;
|
||||
let r_len = data.len() + 4;
|
||||
slice = &slice[r_len..];
|
||||
ret.push(data);
|
||||
@ -346,7 +372,7 @@ fn get_sequence(mut slice: &[u8]) -> Result<Vec<&[u8]>> {
|
||||
|
||||
#[inline]
|
||||
fn get_sequence_kv(slice: &[u8]) -> Result<Vec<(u32, &[u8])>> {
|
||||
let seq = get_sequence(slice)?;
|
||||
let seq = get_lv_sequence(slice)?;
|
||||
Ok(seq
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
|
@ -71,26 +71,26 @@ impl TryInto<EventBuilder> for RepoArtifact {
|
||||
match self.metadata {
|
||||
ArtifactMetadata::APK {
|
||||
manifest,
|
||||
signatures,
|
||||
signature_blocks: signatures,
|
||||
} => {
|
||||
for signature in signatures {
|
||||
match signature {
|
||||
ApkSignatureBlock::Unknown { .. } => {
|
||||
warn!("No signature found in metadata");
|
||||
}
|
||||
ApkSignatureBlock::V2 { signatures, .. } => {
|
||||
for signature in signatures {
|
||||
ApkSignatureBlock::V2 { certificates, .. } => {
|
||||
for certificate in certificates {
|
||||
b = b.tag(Tag::parse([
|
||||
"apk_signature_hash",
|
||||
&hex::encode(signature.digest),
|
||||
&hex::encode(Sha256::digest(certificate)),
|
||||
])?);
|
||||
}
|
||||
}
|
||||
ApkSignatureBlock::V3 { signatures, .. } => {
|
||||
for signature in signatures {
|
||||
ApkSignatureBlock::V3 { certificates, .. } => {
|
||||
for certificate in certificates {
|
||||
b = b.tag(Tag::parse([
|
||||
"apk_signature_hash",
|
||||
&hex::encode(signature.digest),
|
||||
&hex::encode(Sha256::digest(certificate)),
|
||||
])?);
|
||||
}
|
||||
}
|
||||
@ -124,7 +124,7 @@ impl TryInto<EventBuilder> for RepoArtifact {
|
||||
pub enum ArtifactMetadata {
|
||||
APK {
|
||||
manifest: AndroidManifest,
|
||||
signatures: Vec<ApkSignatureBlock>,
|
||||
signature_blocks: Vec<ApkSignatureBlock>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -133,7 +133,7 @@ impl Display for ArtifactMetadata {
|
||||
match self {
|
||||
ArtifactMetadata::APK {
|
||||
manifest,
|
||||
signatures,
|
||||
signature_blocks: signatures,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
@ -173,6 +173,7 @@ impl Display for Platform {
|
||||
Architecture::ARM64 => "arm64-v8a",
|
||||
Architecture::X86 => "x86",
|
||||
Architecture::X86_64 => "x86_64",
|
||||
Architecture::Universal => "universal",
|
||||
}
|
||||
),
|
||||
Platform::IOS { arch } => write!(
|
||||
@ -220,6 +221,7 @@ impl Display for Platform {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Architecture {
|
||||
Universal,
|
||||
ARMv7,
|
||||
ARM64,
|
||||
X86,
|
||||
@ -233,6 +235,7 @@ impl Display for Architecture {
|
||||
Architecture::ARM64 => write!(f, "arm64-v8a"),
|
||||
Architecture::X86 => write!(f, "x86"),
|
||||
Architecture::X86_64 => write!(f, "x86_64"),
|
||||
Architecture::Universal => write!(f, "universal"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -396,8 +399,6 @@ fn load_apk_artifact(path: &Path) -> Result<RepoArtifact> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
ensure!(lib_arch.len() == 1, "Unknown library architecture");
|
||||
|
||||
Ok(RepoArtifact {
|
||||
name: path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||
size: path.metadata()?.len(),
|
||||
@ -405,17 +406,21 @@ fn load_apk_artifact(path: &Path) -> Result<RepoArtifact> {
|
||||
hash: hash_file(path)?,
|
||||
content_type: "application/vnd.android.package-archive".to_string(),
|
||||
platform: Platform::Android {
|
||||
arch: match lib_arch.iter().next().unwrap().as_str() {
|
||||
"arm64-v8a" => Architecture::ARM64,
|
||||
"armeabi-v7a" => Architecture::ARMv7,
|
||||
"x86_64" => Architecture::X86_64,
|
||||
"x86" => Architecture::X86,
|
||||
v => bail!("unknown architecture: {v}"),
|
||||
arch: if lib_arch.is_empty() {
|
||||
Architecture::Universal
|
||||
} else {
|
||||
match lib_arch.iter().next().unwrap().as_str() {
|
||||
"arm64-v8a" => Architecture::ARM64,
|
||||
"armeabi-v7a" => Architecture::ARMv7,
|
||||
"x86_64" => Architecture::X86_64,
|
||||
"x86" => Architecture::X86,
|
||||
v => bail!("unknown architecture: {v}"),
|
||||
}
|
||||
},
|
||||
},
|
||||
metadata: ArtifactMetadata::APK {
|
||||
manifest,
|
||||
signatures: sig_block.get_signatures()?,
|
||||
signature_blocks: sig_block.get_signatures()?,
|
||||
},
|
||||
})
|
||||
}
|
||||
@ -423,7 +428,7 @@ fn load_apk_artifact(path: &Path) -> Result<RepoArtifact> {
|
||||
fn hash_file(path: &Path) -> Result<Vec<u8>> {
|
||||
let mut file = File::open(path)?;
|
||||
let mut hash = Sha256::default();
|
||||
let mut buf = Vec::with_capacity(4096);
|
||||
let mut buf = vec![0; 4096];
|
||||
while let Ok(r) = file.read(&mut buf) {
|
||||
if r == 0 {
|
||||
break;
|
||||
@ -471,7 +476,11 @@ mod tests {
|
||||
let path = "/home/kieran/Downloads/snort-arm64-v8a-v0.3.0.apk";
|
||||
|
||||
let apk = load_apk_artifact(&PathBuf::from(path))?;
|
||||
eprint!("{}", apk);
|
||||
Ok(())
|
||||
|
||||
eprintln!("{:?}", apk);
|
||||
if let ArtifactMetadata::APK { .. } = apk.metadata {
|
||||
return Ok(());
|
||||
}
|
||||
bail!("missing apk metadata");
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user