1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
//! Legacy IOS7Crypt encryptor/decryptor library

extern crate rand;
use rand::Rng;

use std::str;
use std::i64;
use std::iter::Iterator;

// Finite IOS7Crypt constant key
pub fn xlat_prime() -> [u8; 53] {
  return [
    0x64, 0x73, 0x66, 0x64, 0x3b, 0x6b, 0x66, 0x6f,
    0x41, 0x2c, 0x2e, 0x69, 0x79, 0x65, 0x77, 0x72,
    0x6b, 0x6c, 0x64, 0x4a, 0x4b, 0x44, 0x48, 0x53,
    0x55, 0x42, 0x73, 0x67, 0x76, 0x63, 0x61, 0x36,
    0x39, 0x38, 0x33, 0x34, 0x6e, 0x63, 0x78, 0x76,
    0x39, 0x38, 0x37, 0x33, 0x32, 0x35, 0x34, 0x6b,
    0x3b, 0x66, 0x67, 0x38, 0x37
  ];
}

// Wraparound IOS7Crypt constant key
pub fn xlat(i : usize, len : usize) -> Vec<u8> {
  let xs : [u8; 53] = xlat_prime();
  let xs_len : usize = xs.len();

  if len < 1 {
    return Vec::new();
  }
  else {
    let mut head : Vec<u8> = Vec::new();
    head.push(xs[i % xs_len]);

    let mut tail : Vec<u8> = xlat(i + 1, len - 1);

    head.append(&mut tail);
    return head;
  }
}

// Bitwise XOR convenience function
pub fn xor(tp : (&u8, &u8)) -> u8 {
  let (a, b) : (&u8, &u8) = tp;
  return (*a) ^ (*b);
}

// Encode an ASCII password with IOS7Crypt
pub fn encrypt(password : &str) -> String {
  let mut rng = rand::thread_rng();

  let seed : usize = rng.gen_range(0, 16);

  let plaintext : &[u8] = password.as_bytes();

  let keys : Vec<u8> = xlat(seed, password.len());

  assert_eq!(plaintext.len(), keys.len());

  let zipped : Vec<(&u8, &u8)> = plaintext.iter().zip(keys.iter()).collect();

  let ciphertext : Vec<u8> = zipped.iter().map(|pair| xor(*pair)).collect();

  let hexpairs : Vec<String> = ciphertext.iter().map(|cipherbyte| format!("{:02x}", cipherbyte)).collect();

  let hexdata : String = hexpairs.concat();

  return format!("{:02}{}", seed, hexdata);
}

// Decrypt valid IOS7Crypt hashes
pub fn decrypt(hash : &str) -> String {
  if hash.len() < 2 {
    return "".to_string();
  }
  else {
    let (seed_str, hash_str) : (&str, &str) = hash.split_at(2);

    let seed : usize = seed_str.parse().expect("Invalid seed");

    let codepoints : Vec<u8> = String::from(hash_str).bytes().collect();

    let hexpairs : Vec<&[u8]> = codepoints.chunks(2).collect();

    let ciphertext : Vec<u8> = hexpairs.iter().map(|hexpair| match str::from_utf8(*hexpair) {
      Ok(v) => match i64::from_str_radix(v, 16) {
        Ok(w) => w as u8,
        Err(err) => panic!(err)
      },
      Err(err) => panic!(err)
    }).collect();

    let keys : Vec<u8> = xlat(seed, ciphertext.len());

    assert_eq!(ciphertext.len(), keys.len());

    let zipped : Vec<(&u8, &u8)> = ciphertext.iter().zip(keys.iter()).collect();

    let plainbytes : Vec<u8> = zipped.iter().map(|pair| xor(*pair)).collect();

    match String::from_utf8(plainbytes) {
      Ok(password) => return password,
      Err(err) => panic!(err)
    };
  }
}

#[test]
fn smoketest() {
  assert_eq!(decrypt("1308181c00091d"), "monkey");
}