Skip to content

Commit

Permalink
Add support for XPIs signed with AMO local dev (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
willdurand committed May 13, 2024
1 parent af3d08b commit e87260f
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 29 deletions.
10 changes: 7 additions & 3 deletions src/wasm_bindings.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{SignatureKind, XPI as InnerXPI};
use crate::{Environment, SignatureKind, XPI as InnerXPI};
use std::io::Cursor;
use wasm_bindgen::prelude::*;
use zip::ZipArchive;
Expand Down Expand Up @@ -43,8 +43,12 @@ impl XPI {
}

#[wasm_bindgen(getter)]
pub fn is_staging(&self) -> bool {
self.xpi.signatures.pkcs7.is_staging()
pub fn env(&self) -> String {
match self.xpi.signatures.pkcs7.env() {
Environment::Development => "development".to_string(),
Environment::Staging => "staging".to_string(),
Environment::Production => "production".to_string(),
}
}

#[wasm_bindgen(getter)]
Expand Down
41 changes: 32 additions & 9 deletions src/xpi/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,27 @@ impl fmt::Display for Date {
}
}

#[derive(Debug, PartialEq)]
pub enum Environment {
Development,
Staging,
Production,
}

impl fmt::Display for Environment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Environment::Development => "DEVELOPMENT",
Environment::Staging => "STAGING",
Environment::Production => "PRODUCTION",
}
)
}
}

#[derive(Default, Serialize)]
/// Represents some of the information found in a certificate.
pub struct CertificateInfo {
Expand All @@ -66,8 +87,14 @@ pub struct CertificateInfo {
}

impl CertificateInfo {
fn is_staging(&self) -> bool {
self.common_name.contains("staging")
fn env(&self) -> Environment {
if self.common_name.contains("dev.amo.root.ca") {
Environment::Development
} else if self.common_name.contains("staging") {
Environment::Staging
} else {
Environment::Production
}
}
}

Expand Down Expand Up @@ -164,8 +191,8 @@ impl Signature {
self.present
}

pub fn is_staging(&self) -> bool {
self.certificates.iter().any(|cert| cert.is_staging())
pub fn env(&self) -> Environment {
self.certificates[0].env()
}

pub fn kind(&self) -> SignatureKind {
Expand Down Expand Up @@ -193,11 +220,7 @@ impl fmt::Display for Signature {
f,
" └── {} / {} / {} / {}\n └── Certificates:",
if self.present { "PRESENT" } else { "ABSENT" },
if self.is_staging() {
"STAGING"
} else {
"PRODUCTION"
},
self.env(),
self.algorithm.as_deref().unwrap_or("N/A"),
self.kind(),
)?;
Expand Down
Binary file added tests/fixtures/amo-localdev.xpi
Binary file not shown.
94 changes: 79 additions & 15 deletions tests/xpi_test.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::io::Cursor;
use std::time::Duration;
use xpidump::{Date, RecommendationState, Signature, SignatureKind, XPI};
use xpidump::{Date, Environment, RecommendationState, Signature, SignatureKind, XPI};
use zip::ZipArchive;

fn assert_signature(signature: &Signature, kind: SignatureKind, is_staging: bool, algorithm: &str) {
fn assert_signature(signature: &Signature, kind: SignatureKind, env: Environment, algorithm: &str) {
assert!(signature.exists());
assert_eq!(kind, signature.kind());
assert_eq!(is_staging, signature.is_staging());
assert_eq!(env, signature.env());
assert_eq!(
algorithm,
signature.algorithm.as_ref().expect("expect algorithm")
Expand Down Expand Up @@ -43,15 +43,20 @@ fn test_prod_regular_addon() {
assert_signature(
&xpi.signatures.pkcs7,
SignatureKind::Regular,
false,
Environment::Production,
"SHA-1",
);
assert_eq!(
Date::utc_time_from_duration(Duration::from_secs(1743724800)),
xpi.signatures.pkcs7.certificates[0].end_date
);

assert_signature(&xpi.signatures.cose, SignatureKind::Regular, false, "ES256");
assert_signature(
&xpi.signatures.cose,
SignatureKind::Regular,
Environment::Production,
"ES256",
);
assert_eq!(
Date::utc_time_from_duration(Duration::from_secs(1743724800)),
xpi.signatures.cose.certificates[0].end_date
Expand All @@ -74,7 +79,7 @@ fn test_prod_old_regular_addon() {
assert_signature(
&xpi.signatures.pkcs7,
SignatureKind::Regular,
false,
Environment::Production,
"SHA-1",
);
// Verify TeletexString values.
Expand Down Expand Up @@ -111,13 +116,13 @@ fn test_prod_privileged_addon() {
assert_signature(
&xpi.signatures.pkcs7,
SignatureKind::Privileged,
false,
Environment::Production,
"SHA-256",
);
assert_signature(
&xpi.signatures.cose,
SignatureKind::Privileged,
false,
Environment::Production,
"ES256",
);
}
Expand All @@ -138,8 +143,18 @@ fn test_staging_regular_addon() {
);
assert_eq!("16.0", xpi.manifest.version.expect("expect add-on version"));

assert_signature(&xpi.signatures.pkcs7, SignatureKind::Regular, true, "SHA-1");
assert_signature(&xpi.signatures.cose, SignatureKind::Regular, true, "ES256");
assert_signature(
&xpi.signatures.pkcs7,
SignatureKind::Regular,
Environment::Staging,
"SHA-1",
);
assert_signature(
&xpi.signatures.cose,
SignatureKind::Regular,
Environment::Staging,
"ES256",
);
}

#[test]
Expand All @@ -162,7 +177,12 @@ fn test_staging_old_recommended_addon() {
assert_eq!("alex3@mail.com", xpi.manifest.id.expect("expect add-on ID"));
assert_eq!("1.1", xpi.manifest.version.expect("expect add-on version"));

assert_signature(&xpi.signatures.pkcs7, SignatureKind::Regular, true, "SHA-1");
assert_signature(
&xpi.signatures.pkcs7,
SignatureKind::Regular,
Environment::Staging,
"SHA-1",
);
assert!(xpi.signatures.cose.exists());
}

Expand All @@ -188,10 +208,15 @@ fn test_staging_system_addon() {
assert_signature(
&xpi.signatures.pkcs7,
SignatureKind::System,
true,
Environment::Staging,
"SHA-256",
);
assert_signature(&xpi.signatures.cose, SignatureKind::System, true, "ES256");
assert_signature(
&xpi.signatures.cose,
SignatureKind::System,
Environment::Staging,
"ES256",
);
}

#[test]
Expand Down Expand Up @@ -263,7 +288,12 @@ fn test_staging_line_extension() {
xpi.manifest.version.expect("expect add-on version")
);

assert_signature(&xpi.signatures.pkcs7, SignatureKind::Regular, true, "SHA-1");
assert_signature(
&xpi.signatures.pkcs7,
SignatureKind::Regular,
Environment::Staging,
"SHA-1",
);
assert_eq!(
Date::utc_time_from_duration(Duration::from_secs(1741910400)),
xpi.signatures.pkcs7.certificates[0].end_date
Expand All @@ -273,7 +303,12 @@ fn test_staging_line_extension() {
xpi.signatures.pkcs7.certificates[1].end_date
);

assert_signature(&xpi.signatures.cose, SignatureKind::Regular, true, "ES256");
assert_signature(
&xpi.signatures.cose,
SignatureKind::Regular,
Environment::Staging,
"ES256",
);
assert_eq!(
Date::utc_time_from_duration(Duration::from_secs(1741910400)),
xpi.signatures.cose.certificates[0].end_date
Expand All @@ -283,3 +318,32 @@ fn test_staging_line_extension() {
xpi.signatures.cose.certificates[1].end_date
);
}

#[test]
fn test_amo_localdev() {
let bytes = include_bytes!("fixtures/amo-localdev.xpi");
let reader = Cursor::new(bytes);
let mut archive = ZipArchive::new(reader).unwrap();

let xpi = XPI::new(&mut archive);

assert!(xpi.manifest.exists());
assert!(!xpi.is_recommended());
assert_eq!(
"a-test-extension@will.drnd.me",
xpi.manifest.id.expect("expect add-on ID")
);

assert_signature(
&xpi.signatures.pkcs7,
SignatureKind::Regular,
Environment::Development,
"SHA-1",
);
assert_signature(
&xpi.signatures.cose,
SignatureKind::Regular,
Environment::Development,
"ES256",
);
}
4 changes: 2 additions & 2 deletions web/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const updateUI = (xpi) => {
cose_algorithm,
has_cose_sig,
has_pkcs7_sig,
is_staging,
env,
kind,
manifest,
pkcs7_algorithm,
Expand All @@ -29,7 +29,7 @@ const updateUI = (xpi) => {
${manifest.id ? `This ${prettyKind} has the following ID in its manifest: <code>${manifest.id}</code>` : `This ${prettyKind} does not have an ID in its manifest`}. Its version is: <code>${manifest.version}</code>.
<br>
<br>
${has_pkcs7_sig ? `${has_cose_sig ? "🔐" : "🔓"} It has been signed with the <strong>${is_staging ? "staging" : "production"}</strong> root certificate. ${has_cose_sig ? "This add-on is dual-signed (PKCS#7 and COSE)" : "This add-on is <strong>not</strong> signed with COSE"}. The PKCS#7 digest algorithm is: <strong>${pkcs7_algorithm}</strong>. ${has_cose_sig ? `The COSE algorithm is: <strong>${cose_algorithm}</strong>.` : ""}` : `❌ It doesn't appear to be signed.`}
${has_pkcs7_sig ? `${has_cose_sig ? "🔐" : "🔓"} It has been signed with the <strong>${env}</strong> root certificate. ${has_cose_sig ? "This add-on is dual-signed (PKCS#7 and COSE)" : "This add-on is <strong>not</strong> signed with COSE"}. The PKCS#7 digest algorithm is: <strong>${pkcs7_algorithm}</strong>. ${has_cose_sig ? `The COSE algorithm is: <strong>${cose_algorithm}</strong>.` : ""}` : `❌ It doesn't appear to be signed.`}
`;

$outputRaw.textContent = JSON.stringify(xpi.to_js(), null, 2);
Expand Down

0 comments on commit e87260f

Please sign in to comment.