apksigner 1.0.0

APK signer and verifier CLI — a faithful Rust port of AOSP apksigner
//! Mirrors `com.android.apksigner.HexEncoding`.
//!
//! Hexadecimal encoding where each byte is represented by two lowercase hexadecimal digits.

/// Namespace mirroring the Java `HexEncoding` class, whose methods are static.
///
/// Kept so call sites read like the Java (`HexEncoding::encode(...)`) rather than as free
/// functions.
pub struct HexEncoding;

impl HexEncoding {
    /// Mirrors `HexEncoding.encode(byte[])`.
    pub fn encode(data: &[u8]) -> String {
        encode(data)
    }
}

const HEX_DIGITS: &[u8; 16] = b"0123456789abcdef";

/// Encodes `length` bytes of `data`, starting at `offset`, as a hexadecimal string.
///
/// Mirrors `HexEncoding.encode(byte[], int, int)`.
pub fn encode_range(data: &[u8], offset: usize, length: usize) -> String {
    let mut result = String::with_capacity(length * 2);
    for i in 0..length {
        let b = data[offset + i];
        result.push(HEX_DIGITS[((b >> 4) & 0x0f) as usize] as char);
        result.push(HEX_DIGITS[(b & 0x0f) as usize] as char);
    }
    result
}

/// Encodes all of `data` as a hexadecimal string.
///
/// Mirrors `HexEncoding.encode(byte[])`.
pub fn encode(data: &[u8]) -> String {
    encode_range(data, 0, data.len())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn encodes_bytes_as_two_lowercase_digits_each() {
        assert_eq!(encode(&[0x00]), "00");
        assert_eq!(encode(&[0x0f]), "0f");
        assert_eq!(encode(&[0xff]), "ff");
        assert_eq!(encode(&[0xde, 0xad, 0xbe, 0xef]), "deadbeef");
    }

    #[test]
    fn empty_input_yields_empty_string() {
        assert_eq!(encode(&[]), "");
        assert_eq!(encode_range(&[1, 2, 3], 0, 0), "");
    }

    #[test]
    fn matches_known_digest_encoding() {
        // SHA-256 of the empty string.
        let digest: [u8; 32] = [
            0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
            0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
            0x78, 0x52, 0xb8, 0x55,
        ];
        assert_eq!(
            encode(&digest),
            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
        );
    }

    #[test]
    fn encodes_a_sub_range() {
        let data = [0xaa, 0xbb, 0xcc, 0xdd];
        assert_eq!(encode_range(&data, 1, 2), "bbcc");
        assert_eq!(encode_range(&data, 3, 1), "dd");
    }
}