use keyhog_scanner::checksum::{
validate_checksum, ChecksumResult, ChecksumValidator, GithubClassicPatValidator,
};
const BASE62_DIGITS: &[u8; 62] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
fn ref_crc32(data: &[u8]) -> u32 {
let mut table = [0u32; 256];
let mut i = 0usize;
while i < 256 {
let mut crc = i as u32;
let mut j = 0;
while j < 8 {
if crc & 1 != 0 {
crc = 0xEDB8_8320 ^ (crc >> 1);
} else {
crc >>= 1;
}
j += 1;
}
table[i] = crc;
i += 1;
}
let mut crc: u32 = 0xFFFF_FFFF;
for &byte in data {
crc = table[((crc ^ (byte as u32)) & 0xFF) as usize] ^ (crc >> 8);
}
crc ^ 0xFFFF_FFFF
}
fn ref_base62(mut value: u32, width: usize) -> String {
if value == 0 {
return "0".repeat(width);
}
let mut rev = Vec::with_capacity(width.max(6));
while value > 0 {
rev.push(BASE62_DIGITS[(value % 62) as usize] as char);
value /= 62;
}
while rev.len() < width {
rev.push('0');
}
rev.reverse();
rev.into_iter().collect()
}
fn checksum_for(entropy: &str) -> String {
ref_base62(ref_crc32(entropy.as_bytes()), 6)
}
fn valid_token(entropy: &str) -> String {
assert_eq!(entropy.len(), 30, "entropy must be exactly 30 chars");
format!("ghp_{}{}", entropy, checksum_for(entropy))
}
fn classic() -> GithubClassicPatValidator {
GithubClassicPatValidator
}
const E_ALPHA: &str = "abcdefghijklmnopqrstuvwxyz1234"; const E_ZERO: &str = "000000000000000000000000000000"; const E_MIX: &str = "A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5";
#[test]
fn ref_replica_matches_golden_checksums() {
assert_eq!(checksum_for(E_ALPHA), "3Tcn6I");
assert_eq!(checksum_for(E_ZERO), "2C8GjS");
assert_eq!(checksum_for(E_MIX), "0Zb5Hm");
}
#[test]
fn ref_crc32_known_vectors() {
assert_eq!(ref_crc32(b""), 0x0000_0000);
assert_eq!(ref_crc32(b"a"), 0xE8B7_BE43);
assert_eq!(ref_crc32(b"abc"), 0x3524_41C2);
assert_eq!(
ref_crc32(b"The quick brown fox jumps over the lazy dog"),
0x414F_A339
);
}
#[test]
fn ref_base62_padding_and_zero() {
assert_eq!(ref_base62(0, 6), "000000");
assert_eq!(ref_base62(1, 6), "000001");
assert_eq!(ref_base62(61, 6), "00000z");
assert_eq!(ref_base62(62, 6), "000010");
assert_eq!(ref_base62(u32::MAX, 6), ref_base62(u32::MAX, 6));
assert!(ref_base62(u32::MAX, 6).len() >= 6);
}
#[test]
fn golden_alpha_token_is_valid() {
let tok = "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";
assert_eq!(tok.len(), 40);
assert_eq!(classic().validate(tok), ChecksumResult::Valid);
}
#[test]
fn golden_zero_entropy_token_is_valid() {
let tok = "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";
assert_eq!(tok.len(), 40);
assert_eq!(classic().validate(tok), ChecksumResult::Valid);
}
#[test]
fn golden_mixed_entropy_token_is_valid() {
let tok = "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";
assert_eq!(tok.len(), 40);
assert_eq!(classic().validate(tok), ChecksumResult::Valid);
}
#[test]
fn constructed_valid_token_roundtrips() {
let tok = valid_token(E_ALPHA);
assert_eq!(tok, "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");
assert_eq!(classic().validate(&tok), ChecksumResult::Valid);
}
#[test]
fn valid_token_through_registry_is_valid() {
let tok = valid_token(E_ALPHA);
assert_eq!(validate_checksum(&tok), ChecksumResult::Valid);
}
#[test]
fn entropy_last_char_flipped_is_invalid() {
let tok = "ghp_abcdefghijklmnopqrstuvwxyz12353Tcn6I";
assert_eq!(tok.len(), 40);
assert_eq!(classic().validate(tok), ChecksumResult::Invalid);
}
#[test]
fn entropy_first_char_flipped_is_invalid() {
let mut entropy: Vec<char> = E_ALPHA.chars().collect();
entropy[0] = if entropy[0] == 'x' { 'y' } else { 'x' };
let corrupted: String = entropy.into_iter().collect();
let tok = format!("ghp_{}{}", corrupted, checksum_for(E_ALPHA));
assert_eq!(tok.len(), 40);
assert_ne!(checksum_for(&corrupted), checksum_for(E_ALPHA));
assert_eq!(classic().validate(&tok), ChecksumResult::Invalid);
}
#[test]
fn checksum_first_char_flipped_is_invalid() {
let tok = "ghp_abcdefghijklmnopqrstuvwxyz1234ZTcn6I"; assert_eq!(tok.len(), 40);
assert_eq!(classic().validate(tok), ChecksumResult::Invalid);
}
#[test]
fn checksum_last_char_flipped_is_invalid() {
let cs = checksum_for(E_ALPHA); let mut bytes: Vec<char> = cs.chars().collect();
let last = bytes.len() - 1;
bytes[last] = if bytes[last] == 'Z' { 'Y' } else { 'Z' };
let bad_cs: String = bytes.into_iter().collect();
let tok = format!("ghp_{}{}", E_ALPHA, bad_cs);
assert_eq!(tok.len(), 40);
assert_ne!(bad_cs, cs);
assert_eq!(classic().validate(&tok), ChecksumResult::Invalid);
}
#[test]
fn all_zero_checksum_on_real_body_is_invalid() {
let tok = format!("ghp_{}000000", E_ALPHA);
assert_eq!(tok.len(), 40);
assert_ne!(checksum_for(E_ALPHA), "000000");
assert_eq!(classic().validate(&tok), ChecksumResult::Invalid);
}
#[test]
fn wrong_checksum_through_registry_is_invalid() {
let tok = "ghp_abcdefghijklmnopqrstuvwxyz1234ZTcn6I";
assert_eq!(validate_checksum(tok), ChecksumResult::Invalid);
}
#[test]
fn payload_one_short_is_not_applicable() {
let tok = format!("ghp_{}", "a".repeat(35)); assert_eq!(classic().validate(&tok), ChecksumResult::NotApplicable);
}
#[test]
fn payload_one_long_is_not_applicable() {
let tok = format!("ghp_{}", "a".repeat(37)); assert_eq!(classic().validate(&tok), ChecksumResult::NotApplicable);
}
#[test]
fn empty_payload_is_not_applicable() {
assert_eq!(classic().validate("ghp_"), ChecksumResult::NotApplicable);
}
#[test]
fn payload_far_too_long_is_not_applicable() {
let tok = format!("ghp_{}", "a".repeat(100));
assert_eq!(classic().validate(&tok), ChecksumResult::NotApplicable);
}
#[test]
fn payload_36_minus_and_plus_one_boundary() {
let body36 = "a".repeat(36);
let body35 = "a".repeat(35);
let body37 = "a".repeat(37);
assert_eq!(
classic().validate(&format!("ghp_{}", body36)),
ChecksumResult::Invalid
);
assert_eq!(
classic().validate(&format!("ghp_{}", body35)),
ChecksumResult::NotApplicable
);
assert_eq!(
classic().validate(&format!("ghp_{}", body37)),
ChecksumResult::NotApplicable
);
}
#[test]
fn length_check_precedes_alnum_check() {
let tok = "ghp_aaaa-aaaa"; assert_eq!(classic().validate(tok), ChecksumResult::NotApplicable);
}
#[test]
fn non_alnum_hyphen_in_payload_is_invalid() {
let payload = format!("{}-", "a".repeat(35));
assert_eq!(payload.len(), 36);
let tok = format!("ghp_{}", payload);
assert_eq!(classic().validate(&tok), ChecksumResult::Invalid);
}
#[test]
fn non_alnum_underscore_in_payload_is_invalid() {
let payload = format!("{}_{}", "a".repeat(17), "a".repeat(18));
assert_eq!(payload.len(), 36);
let tok = format!("ghp_{}", payload);
assert_eq!(classic().validate(&tok), ChecksumResult::Invalid);
}
#[test]
fn non_alnum_space_in_payload_is_invalid() {
let payload = format!("{} {}", "a".repeat(17), "a".repeat(18));
assert_eq!(payload.len(), 36);
assert_eq!(
classic().validate(&format!("ghp_{}", payload)),
ChecksumResult::Invalid
);
}
#[test]
fn non_ascii_alnum_unicode_digit_is_not_applicable() {
let payload = format!("{}\u{0660}", "a".repeat(35));
assert_eq!(payload.chars().count(), 36);
assert_eq!(payload.len(), 37); let tok = format!("ghp_{}", payload);
assert_eq!(classic().validate(&tok), ChecksumResult::NotApplicable);
}
#[test]
fn non_ascii_alnum_at_36_bytes_is_invalid() {
let payload = format!("{}\u{00E9}", "a".repeat(34));
assert_eq!(payload.len(), 36); assert!(!payload.chars().all(|c| c.is_ascii_alphanumeric()));
let tok = format!("ghp_{}", payload);
assert_eq!(classic().validate(&tok), ChecksumResult::Invalid);
}
#[test]
fn non_alnum_in_checksum_region_is_invalid() {
let bad_tail = "3Tcn6-"; let payload = format!("{}{}", E_ALPHA, bad_tail);
assert_eq!(payload.len(), 36);
assert_eq!(
classic().validate(&format!("ghp_{}", payload)),
ChecksumResult::Invalid
);
}
#[test]
fn missing_prefix_is_not_applicable() {
let tok = "abcdefghijklmnopqrstuvwxyz12343Tcn6I";
assert_eq!(classic().validate(tok), ChecksumResult::NotApplicable);
}
#[test]
fn uppercase_prefix_is_not_applicable() {
let tok = "GHP_abcdefghijklmnopqrstuvwxyz12343Tcn6I";
assert_eq!(classic().validate(tok), ChecksumResult::NotApplicable);
}
#[test]
fn fine_grained_prefix_not_claimed_by_classic() {
let tok = "github_pat_aaaaaaaaaaaaaaaaaaaaaa_bbbbbbb";
assert_eq!(classic().validate(tok), ChecksumResult::NotApplicable);
}
#[test]
fn empty_string_is_not_applicable() {
assert_eq!(classic().validate(""), ChecksumResult::NotApplicable);
}
#[test]
fn prefix_only_substring_is_not_applicable() {
assert_eq!(classic().validate("ghp"), ChecksumResult::NotApplicable);
assert_eq!(classic().validate("gh"), ChecksumResult::NotApplicable);
assert_eq!(classic().validate("ghp_"), ChecksumResult::NotApplicable);
}
#[test]
fn prefix_in_middle_is_not_applicable() {
let tok = format!("XXghp_{}", "a".repeat(36));
assert_eq!(classic().validate(&tok), ChecksumResult::NotApplicable);
}
#[test]
fn validator_id_is_stable() {
assert_eq!(classic().validator_id(), "github-classic-pat");
}
#[test]
fn registry_not_applicable_for_unknown_token() {
assert_eq!(
validate_checksum("totally-random-not-a-token"),
ChecksumResult::NotApplicable
);
}
#[test]
fn registry_valid_matches_direct_validator_for_many_tokens() {
for entropy in CORPUS {
let tok = valid_token(entropy);
assert_eq!(classic().validate(&tok), ChecksumResult::Valid, "{tok}");
assert_eq!(validate_checksum(&tok), ChecksumResult::Valid, "{tok}");
}
}
#[test]
fn registry_invalid_matches_direct_validator_for_corrupted_tokens() {
for entropy in CORPUS {
let cs = checksum_for(entropy);
let mut chars: Vec<char> = cs.chars().collect();
chars[0] = if chars[0] == 'A' { 'B' } else { 'A' };
let bad: String = chars.into_iter().collect();
let tok = format!("ghp_{}{}", entropy, bad);
assert_eq!(classic().validate(&tok), ChecksumResult::Invalid, "{tok}");
assert_eq!(validate_checksum(&tok), ChecksumResult::Invalid, "{tok}");
}
}
const CORPUS: &[&str] = &[
"abcdefghijklmnopqrstuvwxyz1234",
"000000000000000000000000000000",
"A1b2C3d4E5f6G7h8I9j0K1l2M3n4O5",
"AUa00Zt2x7mrKBkxh3BqJOlNJlwV9U",
"fHVALGkjnUem1vubDvb9GSVAaa8Q3k",
"XLlsgPAg4RKlVyo9HMT60vZcoh3FnV",
"ZWJQlqLBH69ickt31aj9owIvkyuadd",
"TCfyZ6lYaXTxnRGWhENdaOFuXN9A78",
"JmxXQShJjfFk59vonkksjIfHSajaQF",
"4l1M7Z2gqGWzeYW9DAlp64lbxvlzll",
"KR0H5p9AGSAuhePDVmONKmViH5ovKs",
];
#[test]
fn proptest_constructed_tokens_are_valid() {
for entropy in CORPUS {
let tok = valid_token(entropy);
assert_eq!(tok.len(), 40, "{tok}");
assert_eq!(
classic().validate(&tok),
ChecksumResult::Valid,
"expected Valid for {tok}"
);
}
}
#[test]
fn proptest_every_single_entropy_byte_flip_breaks_checksum() {
for entropy in CORPUS {
let original_cs = checksum_for(entropy);
let body: Vec<char> = entropy.chars().collect();
for i in 0..body.len() {
let mut mutated = body.clone();
let repl = if mutated[i] == 'a' { 'b' } else { 'a' };
mutated[i] = repl;
let mutated_body: String = mutated.into_iter().collect();
if checksum_for(&mutated_body) == original_cs {
continue;
}
let tok = format!("ghp_{}{}", mutated_body, original_cs);
assert_eq!(tok.len(), 40);
assert_eq!(
classic().validate(&tok),
ChecksumResult::Invalid,
"byte flip at {i} of {entropy} should invalidate"
);
}
}
}
#[test]
fn proptest_appended_or_truncated_payload_is_not_applicable() {
for entropy in CORPUS {
let valid = valid_token(entropy); let too_long = format!("{valid}a"); let too_short = &valid[..valid.len() - 1]; assert_eq!(
classic().validate(&too_long),
ChecksumResult::NotApplicable,
"{too_long}"
);
assert_eq!(
classic().validate(too_short),
ChecksumResult::NotApplicable,
"{too_short}"
);
}
}
#[test]
fn proptest_idempotent_and_pure() {
let tok = valid_token(E_ALPHA);
let a = classic().validate(&tok);
let b = classic().validate(&tok);
let c = GithubClassicPatValidator.validate(&tok);
assert_eq!(a, ChecksumResult::Valid);
assert_eq!(a, b);
assert_eq!(b, c);
}
#[test]
fn checksum_result_variants_are_distinct() {
assert_ne!(ChecksumResult::Valid, ChecksumResult::Invalid);
assert_ne!(ChecksumResult::Valid, ChecksumResult::NotApplicable);
assert_ne!(ChecksumResult::Invalid, ChecksumResult::NotApplicable);
assert_eq!(ChecksumResult::Valid, ChecksumResult::Valid);
}