mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-06-05 17:39:29 +02:00
344 lines
11 KiB
Rust
344 lines
11 KiB
Rust
use der::referenced::OwnedToRef;
|
|
use der::{Decode, DecodePem, Encode, Sequence, SliceReader};
|
|
use digest::DynDigest;
|
|
use p256::ecdsa::{
|
|
Signature as P256Signature, SigningKey as P256SigningKey, VerifyingKey as P256VerifyingKey,
|
|
};
|
|
use p256::pkcs8::DecodePrivateKey;
|
|
use p384::ecdsa::{
|
|
Signature as P384Signature, SigningKey as P384SigningKey, VerifyingKey as P384VerifyingKey,
|
|
};
|
|
use p521::ecdsa::{
|
|
Signature as P521Signature, SigningKey as P521SigningKey, VerifyingKey as P521VerifyingKey,
|
|
};
|
|
use rsa::pkcs1v15::{
|
|
Signature as RsaSignature, SigningKey as RsaSigningKey, VerifyingKey as RsaVerifyingKey,
|
|
};
|
|
use rsa::pkcs8::SubjectPublicKeyInfoRef;
|
|
use rsa::signature::hazmat::{PrehashSigner, PrehashVerifier};
|
|
use rsa::signature::SignatureEncoding;
|
|
use rsa::{RsaPrivateKey, RsaPublicKey};
|
|
use sha1::Sha1;
|
|
use sha2::{Sha256, Sha384, Sha512};
|
|
use x509_cert::der::asn1::{OctetString, PrintableString};
|
|
use x509_cert::der::Any;
|
|
use x509_cert::spki::AlgorithmIdentifier;
|
|
use x509_cert::Certificate;
|
|
|
|
use base::libc::c_char;
|
|
use base::{log_err, LoggedResult, MappedFile, ResultExt, StrErr, Utf8CStr};
|
|
|
|
use crate::ffi::BootImage;
|
|
|
|
#[allow(clippy::upper_case_acronyms)]
|
|
pub enum SHA {
|
|
SHA1(Sha1),
|
|
SHA256(Sha256),
|
|
}
|
|
|
|
impl SHA {
|
|
pub fn update(&mut self, data: &[u8]) {
|
|
match self {
|
|
SHA::SHA1(h) => h.update(data),
|
|
SHA::SHA256(h) => h.update(data),
|
|
}
|
|
}
|
|
|
|
pub fn output_size(&self) -> usize {
|
|
match self {
|
|
SHA::SHA1(h) => h.output_size(),
|
|
SHA::SHA256(h) => h.output_size(),
|
|
}
|
|
}
|
|
|
|
pub fn finalize_into(&mut self, out: &mut [u8]) {
|
|
match self {
|
|
SHA::SHA1(h) => h.finalize_into_reset(out),
|
|
SHA::SHA256(h) => h.finalize_into_reset(out),
|
|
}
|
|
.ok();
|
|
}
|
|
}
|
|
|
|
pub fn get_sha(use_sha1: bool) -> Box<SHA> {
|
|
Box::new(if use_sha1 {
|
|
SHA::SHA1(Sha1::default())
|
|
} else {
|
|
SHA::SHA256(Sha256::default())
|
|
})
|
|
}
|
|
|
|
pub fn sha1_hash(data: &[u8], out: &mut [u8]) {
|
|
let mut h = Sha1::default();
|
|
h.update(data);
|
|
DynDigest::finalize_into(h, out).ok();
|
|
}
|
|
|
|
pub fn sha256_hash(data: &[u8], out: &mut [u8]) {
|
|
let mut h = Sha256::default();
|
|
h.update(data);
|
|
DynDigest::finalize_into(h, out).ok();
|
|
}
|
|
|
|
#[allow(clippy::large_enum_variant)]
|
|
enum SigningKey {
|
|
SHA256withRSA(RsaSigningKey<Sha256>),
|
|
SHA256withECDSA(P256SigningKey),
|
|
SHA384withECDSA(P384SigningKey),
|
|
SHA521withECDSA(P521SigningKey),
|
|
}
|
|
|
|
#[allow(clippy::large_enum_variant)]
|
|
enum VerifyingKey {
|
|
SHA256withRSA(RsaVerifyingKey<Sha256>),
|
|
SHA256withECDSA(P256VerifyingKey),
|
|
SHA384withECDSA(P384VerifyingKey),
|
|
SHA521withECDSA(P521VerifyingKey),
|
|
}
|
|
|
|
struct Verifier {
|
|
digest: Box<dyn DynDigest>,
|
|
key: VerifyingKey,
|
|
}
|
|
|
|
impl Verifier {
|
|
fn from_public_key(key: SubjectPublicKeyInfoRef) -> LoggedResult<Verifier> {
|
|
let digest: Box<dyn DynDigest>;
|
|
let key = if let Ok(rsa) = RsaPublicKey::try_from(key.clone()) {
|
|
digest = Box::<Sha256>::default();
|
|
VerifyingKey::SHA256withRSA(RsaVerifyingKey::<Sha256>::new(rsa))
|
|
} else if let Ok(ec) = P256VerifyingKey::try_from(key.clone()) {
|
|
digest = Box::<Sha256>::default();
|
|
VerifyingKey::SHA256withECDSA(ec)
|
|
} else if let Ok(ec) = P384VerifyingKey::try_from(key.clone()) {
|
|
digest = Box::<Sha384>::default();
|
|
VerifyingKey::SHA384withECDSA(ec)
|
|
} else if let Ok(ec) = P521VerifyingKey::try_from(key.clone()) {
|
|
digest = Box::<Sha512>::default();
|
|
VerifyingKey::SHA521withECDSA(ec)
|
|
} else {
|
|
return Err(log_err!("Unsupported private key"));
|
|
};
|
|
Ok(Verifier { digest, key })
|
|
}
|
|
|
|
fn update(&mut self, data: &[u8]) {
|
|
self.digest.update(data)
|
|
}
|
|
|
|
fn verify(mut self, signature: &[u8]) -> LoggedResult<()> {
|
|
let hash = self.digest.finalize_reset();
|
|
return match &self.key {
|
|
VerifyingKey::SHA256withRSA(key) => {
|
|
let sig = RsaSignature::try_from(signature)?;
|
|
key.verify_prehash(hash.as_ref(), &sig).log()
|
|
}
|
|
VerifyingKey::SHA256withECDSA(key) => {
|
|
let sig = P256Signature::from_slice(signature)?;
|
|
key.verify_prehash(hash.as_ref(), &sig).log()
|
|
}
|
|
VerifyingKey::SHA384withECDSA(key) => {
|
|
let sig = P384Signature::from_slice(signature)?;
|
|
key.verify_prehash(hash.as_ref(), &sig).log()
|
|
}
|
|
VerifyingKey::SHA521withECDSA(key) => {
|
|
let sig = P521Signature::from_slice(signature)?;
|
|
key.verify_prehash(hash.as_ref(), &sig).log()
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
struct Signer {
|
|
digest: Box<dyn DynDigest>,
|
|
key: SigningKey,
|
|
}
|
|
|
|
impl Signer {
|
|
fn from_private_key(key: &[u8]) -> LoggedResult<Signer> {
|
|
let digest: Box<dyn DynDigest>;
|
|
let key = if let Ok(rsa) = RsaPrivateKey::from_pkcs8_der(key) {
|
|
digest = Box::<Sha256>::default();
|
|
SigningKey::SHA256withRSA(RsaSigningKey::<Sha256>::new(rsa))
|
|
} else if let Ok(ec) = P256SigningKey::from_pkcs8_der(key) {
|
|
digest = Box::<Sha256>::default();
|
|
SigningKey::SHA256withECDSA(ec)
|
|
} else if let Ok(ec) = P384SigningKey::from_pkcs8_der(key) {
|
|
digest = Box::<Sha384>::default();
|
|
SigningKey::SHA384withECDSA(ec)
|
|
} else if let Ok(ec) = P521SigningKey::from_pkcs8_der(key) {
|
|
digest = Box::<Sha512>::default();
|
|
SigningKey::SHA521withECDSA(ec)
|
|
} else {
|
|
return Err(log_err!("Unsupported private key"));
|
|
};
|
|
Ok(Signer { digest, key })
|
|
}
|
|
|
|
fn update(&mut self, data: &[u8]) {
|
|
self.digest.update(data)
|
|
}
|
|
|
|
fn sign(mut self) -> LoggedResult<Vec<u8>> {
|
|
let hash = self.digest.finalize_reset();
|
|
let v = match &self.key {
|
|
SigningKey::SHA256withRSA(key) => {
|
|
let sig: RsaSignature = key.sign_prehash(hash.as_ref())?;
|
|
sig.to_vec()
|
|
}
|
|
SigningKey::SHA256withECDSA(key) => {
|
|
let sig: P256Signature = key.sign_prehash(hash.as_ref())?;
|
|
sig.to_vec()
|
|
}
|
|
SigningKey::SHA384withECDSA(key) => {
|
|
let sig: P384Signature = key.sign_prehash(hash.as_ref())?;
|
|
sig.to_vec()
|
|
}
|
|
SigningKey::SHA521withECDSA(key) => {
|
|
let sig: P521Signature = key.sign_prehash(hash.as_ref())?;
|
|
sig.to_vec()
|
|
}
|
|
};
|
|
Ok(v)
|
|
}
|
|
}
|
|
|
|
/*
|
|
* BootSignature ::= SEQUENCE {
|
|
* formatVersion ::= INTEGER,
|
|
* certificate ::= Certificate,
|
|
* algorithmIdentifier ::= SEQUENCE {
|
|
* algorithm OBJECT IDENTIFIER,
|
|
* parameters ANY DEFINED BY algorithm OPTIONAL
|
|
* },
|
|
* authenticatedAttributes ::= SEQUENCE {
|
|
* target CHARACTER STRING,
|
|
* length INTEGER
|
|
* },
|
|
* signature ::= OCTET STRING
|
|
* }
|
|
*/
|
|
|
|
#[derive(Sequence)]
|
|
struct AuthenticatedAttributes {
|
|
target: PrintableString,
|
|
length: u64,
|
|
}
|
|
|
|
#[derive(Sequence)]
|
|
struct BootSignature {
|
|
format_version: i32,
|
|
certificate: Certificate,
|
|
algorithm_identifier: AlgorithmIdentifier<Any>,
|
|
authenticated_attributes: AuthenticatedAttributes,
|
|
signature: OctetString,
|
|
}
|
|
|
|
impl BootSignature {
|
|
fn verify(self, payload: &[u8]) -> LoggedResult<()> {
|
|
if self.authenticated_attributes.length as usize != payload.len() {
|
|
return Err(log_err!("Invalid image size"));
|
|
}
|
|
let mut verifier = Verifier::from_public_key(
|
|
self.certificate
|
|
.tbs_certificate
|
|
.subject_public_key_info
|
|
.owned_to_ref(),
|
|
)?;
|
|
verifier.update(payload);
|
|
let attr = self.authenticated_attributes.to_der()?;
|
|
verifier.update(attr.as_slice());
|
|
verifier.verify(self.signature.as_bytes())?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
pub fn verify_boot_image(img: &BootImage, cert: *const c_char) -> bool {
|
|
fn inner(img: &BootImage, cert: *const c_char) -> LoggedResult<()> {
|
|
let tail = img.tail();
|
|
// Don't use BootSignature::from_der because tail might have trailing zeros
|
|
let mut reader = SliceReader::new(tail)?;
|
|
let mut sig = BootSignature::decode(&mut reader)?;
|
|
match unsafe { Utf8CStr::from_ptr(cert) } {
|
|
Ok(s) => {
|
|
let pem = MappedFile::open(s)?;
|
|
sig.certificate = Certificate::from_pem(pem)?;
|
|
}
|
|
Err(StrErr::NullPointerError) => {}
|
|
Err(e) => Err(e)?,
|
|
};
|
|
sig.verify(img.payload())?;
|
|
Ok(())
|
|
}
|
|
inner(img, cert).is_ok()
|
|
}
|
|
|
|
enum Bytes {
|
|
Mapped(MappedFile),
|
|
Slice(&'static [u8]),
|
|
}
|
|
|
|
impl AsRef<[u8]> for Bytes {
|
|
fn as_ref(&self) -> &[u8] {
|
|
match self {
|
|
Bytes::Mapped(m) => m.as_ref(),
|
|
Bytes::Slice(s) => s,
|
|
}
|
|
}
|
|
}
|
|
|
|
const VERITY_PEM: &[u8] = include_bytes!("../../../tools/keys/verity.x509.pem");
|
|
const VERITY_PK8: &[u8] = include_bytes!("../../../tools/keys/verity.pk8");
|
|
|
|
pub fn sign_boot_image(
|
|
payload: &[u8],
|
|
name: *const c_char,
|
|
cert: *const c_char,
|
|
key: *const c_char,
|
|
) -> Vec<u8> {
|
|
fn inner(
|
|
payload: &[u8],
|
|
name: *const c_char,
|
|
cert: *const c_char,
|
|
key: *const c_char,
|
|
) -> LoggedResult<Vec<u8>> {
|
|
// Process arguments
|
|
let name = unsafe { Utf8CStr::from_ptr(name) }?;
|
|
let cert = match unsafe { Utf8CStr::from_ptr(cert) } {
|
|
Ok(s) => Bytes::Mapped(MappedFile::open(s)?),
|
|
Err(StrErr::NullPointerError) => Bytes::Slice(VERITY_PEM),
|
|
Err(e) => Err(e)?,
|
|
};
|
|
let key = match unsafe { Utf8CStr::from_ptr(key) } {
|
|
Ok(s) => Bytes::Mapped(MappedFile::open(s)?),
|
|
Err(StrErr::NullPointerError) => Bytes::Slice(VERITY_PK8),
|
|
Err(e) => Err(e)?,
|
|
};
|
|
|
|
// Parse cert and private key
|
|
let cert = Certificate::from_pem(cert)?;
|
|
let mut signer = Signer::from_private_key(key.as_ref())?;
|
|
|
|
// Sign image
|
|
let attr = AuthenticatedAttributes {
|
|
target: PrintableString::new(name.as_bytes())?,
|
|
length: payload.len() as u64,
|
|
};
|
|
signer.update(payload);
|
|
signer.update(attr.to_der()?.as_slice());
|
|
let sig = signer.sign()?;
|
|
|
|
// Create BootSignature DER
|
|
let alg_id = cert.signature_algorithm.clone();
|
|
let sig = BootSignature {
|
|
format_version: 1,
|
|
certificate: cert,
|
|
algorithm_identifier: alg_id,
|
|
authenticated_attributes: attr,
|
|
signature: OctetString::new(sig)?,
|
|
};
|
|
sig.to_der().log()
|
|
}
|
|
inner(payload, name, cert, key).unwrap_or_default()
|
|
}
|