1

我正在用 Swift 编写自己的简单 MD5 实现。我正在使用它来生成 WEP 密钥。(要生成 128 位 WEP 密钥,您可以使用 MD5 散列函数。通过将密码短语与自身连接,然后通过 MD5 散列算法运行它,将密码短语扩展为 64 字节的长度。)我知道 WEP 是不安全的,但是约束是我必须从密码生成一个 WEP 加密密钥。不幸的是,我没有选择使用 WPA2。

下面是我的实现。我想我在某个地方有一个小错误,它正在抛弃整个事情。如果我尝试从给定的字符串生成 md5 哈希,我不会生成正确的哈希。

有谁知道我的错误在哪里?谢谢你的时间。

下面的代码注释:
- 我创建了一个运算符来在 md5 算法的主循环期间执行循环移位。
- 我使用索引变量 j 来跟踪您在主循环的每个子循环中传递给函数的 {a,b,c,d} 中的变量。(另一种方法是在每次迭代后交换存储在每个变量中的值。)

// MD5 Implementation
/** Circular Shift implementation for 32 bit unsigned integers **/
protocol CircularShiftable {
    func leftCircularShift(value: UInt32, amount: UInt32) -> UInt32
}
extension UInt32: CircularShiftable {
    func leftCircularShift(value: UInt32, amount: UInt32) -> UInt32 {
        print(String((value << UInt32(amount)), radix: 2))
        print(String((value >> (UInt32(32 - amount))), radix: 2))
        return (amount == 0) ? value : ((value << UInt32(amount)) | (value >> (UInt32(32 - amount))))
    }
}
infix operator <<< { associativity left precedence 160 }
func <<< <T: CircularShiftable>(lhs: T, rhs: UInt32) -> UInt32 {
    return lhs.leftCircularShift(lhs as! UInt32, amount: rhs)
}

    // t[i] indicates the integer part of 2^32*abs(sin(i)) where i is in radians
    private let t: [UInt32] =
        // Round 1
       [0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
    0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
    0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,
    0x6b901122,0xfd987193,0xa679438e,0x49b40821,
    // Round 2
    0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,
    0xd62f105d,0x2441453,0xd8a1e681,0xe7d3fbc8,
    0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,
    0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
    // Round 3
    0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,
    0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
    0x289b7ec6,0xeaa127fa,0xd4ef3085,0x4881d05,
    0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
    // Round 4
    0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,
    0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
    0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,
    0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391]

// Each round uses a sequence of shift amounts in each operation
//       Operation number: 1  2   3   4   5  6   7   8   9  10  11  12  13 14  15  16
private let s: [UInt32] = [7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
                           5, 9,  14, 20, 5, 9,  14, 20, 5, 9,  14, 20, 5, 9,  14, 20,
                           4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
                           6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]

// Each of the four rounds uses a specific nonlinear function in the set S = { F, G, H, I }
let F = { (X: UInt32, Y: UInt32, Z: UInt32) -> UInt32 in
    return (X & Y) | ((~X) & Z)
}
let G = { (X: UInt32, Y: UInt32, Z: UInt32) -> UInt32 in
    return (X & Y) | (Y & (~Z))
}
let H = { (X: UInt32, Y: UInt32, Z: UInt32) -> UInt32 in
    return X ^ Y ^ Z
}
let I = { (X: UInt32, Y: UInt32, Z: UInt32) -> UInt32 in
    return Y ^ (X | (~Z))
}

func wep_encrypt_128(passphrase: String) -> String {
    // Pad message length to 64 bytes in length & convert to 32 bit unsigned integer array
    let msg = pad_msg(passphrase)

    // For each chunk of 512 in resulting msg, process in the main loop of the algorithm
    let blocks = chunk_msg(msg)
    let md5_key = md5_main_loop(blocks)

    // Format the resulting md5 generated key
    // Testing key: FAE93BD77058E8EDFDE8BE5C39
    return (md5_key.reduce(String("")) { (key, piece) -> String in
        return key + String(format: "%2X", piece)
    })
}

func chunk_msg(msg: [UInt8]) -> [[UInt8]] {
    var blocks: [[UInt8]] = []
    for (var i = 0; i < msg.count/64; i++) {
       blocks.append([UInt8](msg[64*i..<(64*i+64)]))
    }
    return blocks
}

func pad_msg(passphrase: String) -> [UInt8] {
    // For WEP you take the initial passphrase and add it to itself until you end up at 64 bytes in length
    var msg = passphrase
    for (var i = 0; msg.characters.count < 64; i = (i+1) % passphrase.characters.count) {
        msg.append(passphrase[passphrase.startIndex.advancedBy(i)])
    }

    // For MD5 you want a msg padded such that the length is 64 bits shy of a multiple of 512 (1 bit, then as many 0s as needed)
    var unicode_byte_values: [UInt8] = msg.utf8.map({$0 as UInt8})
    unicode_byte_values.append(0x80)    // MD5 uses big endian for encoding of bits into bytes
    // Note: (x % 64) == 56 is true when x is 8 bytes less than a multiple of 512 bits
    while ((unicode_byte_values.count % 64) != 56) {
        unicode_byte_values.append(0x00)
    }

    // Append little endian representation of the pre-MD5 padded String length (should always be 64 bytes for WEP encryption)
    //64 * 8 = 512 bits = 00000000 00000000 00000000 00000000 00000000 00000000 00000010 00000000 in big endian...
    //64 * 8 = 512 bits = 00000000 10000000 00000000 00000000 00000000 00000000 00000000 00000000 in little endian...
    var length = [UInt8](count: 8, repeatedValue: 0)
    length[length.endIndex.advancedBy(-7)] = 0x80
    length = length.reverse()
    unicode_byte_values += length

    return unicode_byte_values
}

// Convert each block of 4 bytes to a 32 bit word (using little endian notation)
func convert_to_sub_blocks(b: [UInt8]) -> [UInt32] {
    var m_block: [UInt32] = []
    for (var i = 0; i < b.count; i+=4) {
        let BLK_A = UInt32(b[b.startIndex.advancedBy(i)])
        let BLK_B = UInt32(b[b.startIndex.advancedBy(i+1)]) << 8
        let BLK_C = UInt32(b[b.startIndex.advancedBy(i+2)]) << 16
        let BLK_D = UInt32(b[b.startIndex.advancedBy(i+3)]) << 24
        m_block.append(UInt32(BLK_A | BLK_B | BLK_C | BLK_D))
    }
    return m_block
}

func md5_main_loop(blocks: [[UInt8]]) -> [UInt8] {
    // Chaining variables for the MD5 algorithm
    var A: UInt32 = 0x67452301
    var B: UInt32 = 0xEFCDAB89
    var C: UInt32 = 0x98BADCFE
    var D: UInt32 = 0x10325476

    // FF(a,b,c,d,Mj,s,ti) => a = b + ((a + F(b,c,d) + Mj + ti) <<< s)
    var M = [UInt32]()
    for (var i = 0; i < blocks.count; i++) {

        // There are 16 32-bit message sub-blocks per each 512-bit block of input text
        M = convert_to_sub_blocks(blocks[i])

        // Initialize vars for round
        var round_vars: [UInt32] = [A, B, C, D]
        var j = 0

        // Each round has 16 operations, thus 4 * 16 = 64 operations
        for (var l = 0; l < 64; l++) {

            // Set indexing relative to j
            let a = j
            let b = (a == 3) ? 0 : j+1
            let c = (b == 3) ? 0 : b+1
            let d = (c == 3) ? 0 : c+1

            // Set operation to apply and index into message structure to access based off round position
            var op: (UInt32, UInt32, UInt32) -> UInt32
            var m_i: Int
            if (l < 16) {       // Round 1
                op = F
                m_i = l
            }
            else if (l < 32) {  // Round 2
                op = G
                m_i = (5 * l + 1) % 16
            }
            else if (l < 48) {  // Round 3
                op = H
                m_i = (3 * l + 5) % 16
            }
            else {              // Round 4
                op = I
                m_i = (7 * l) % 16
            }

            // Compute value for variable for sub-round
            round_vars[a] = round_vars[b] &+ (round_vars[a] &+ op(round_vars[b], round_vars[c], round_vars[d]) &+ M[m_i] &+ t[l] <<< s[l])

            // Shift index for cycling through round_vars
            if ((l % 16 == 0) && (l != 0)) { j = 0 }
            else { j = (j == 0) ? 3 : --j }
        }

        // Update chaining variables for next block of data
        A = A &+ round_vars[0]
        B = B &+ round_vars[1]
        C = C &+ round_vars[2]
        D = D &+ round_vars[3]
    }

    return form_hash([A,B,C,D])
}

// Forming the hash requires concatenating the final values stored in A,B,C,D
func form_hash(sub_blocks: [UInt32]) -> [UInt8] {
    var key_hash: [UInt8] = []
    for b in sub_blocks {
        key_hash += [UInt8((b      )  & 0xFF)]
        key_hash += [UInt8((b >>  8)  & 0xFF)]
        key_hash += [UInt8((b >> 16) & 0xFF)]
        key_hash += [UInt8((b >> 24) & 0xFF)]
    }
    return key_hash
}

// Testing key: FAE93BD77058E8EDFDE8BE5C39
wep_encrypt_128(psk)
4

1 回答 1

0

我注意到一些错别字在这里和那里把这个东西放在一起:

  • 我的长度计算错误。我应该在末尾的第 2 个字节中添加一个 2,然后将其反转。(我在用字节顺序和字节交换来写这个问题时完全混淆了自己,所以它在最初的实现中离我们很远。当我在纸上为算法执行一些步骤时我注意到了它。)
  • G 输入错误:它应该是 (X & Z ) | (Y & (~Z)),我不小心用 Y 代替了粗体 Z

谢谢大家的帮助!它现在可以工作了:)

于 2016-01-12T15:50:23.740 回答