Last active
January 12, 2016 21:01
-
-
Save sjbodzo/72128f1cf1c6edaf0d11 to your computer and use it in GitHub Desktop.
128 bit WEP key generation written natively in Swift, using an MD5 hash under the hood.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: Wifi Encryption For 128 Bit WEP key generation using an MD5 hash | |
// Created by: Jess Bodzo | |
/** | |
This program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
See a copy of the GNU General Public License at <http://www.gnu.org/licenses/>. | |
**/ | |
import UIKit | |
import Foundation | |
var psk = "meterking1" | |
// 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) & 0xFFFFFFFF) | (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 & Z) | (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 and return the resulting md5 generated key | |
return (md5_key[0..<13].reduce(String("")) { (key, piece) -> String in | |
return key + String(piece, radix: 16, uppercase: true) | |
}) | |
} | |
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 reversed 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 00000010 00000000 00000000 00000000 00000000 00000000 00000000 when reversed... | |
var length = [UInt8](count: 8, repeatedValue: 0) | |
length[length.endIndex.advancedBy(-2)] = 2 | |
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] | |
// Each round has 16 operations, thus 4 * 16 = 64 operations | |
for (var l = 0; l < 64; l++) { | |
// 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 and shift variables | |
/* Explanation: z = {a,b,c,d} are used in calculations at each round, where one of them stores the result of the computation; | |
In the MD5 algorithm, which variable in z is used to store the result shifts; To accomodate that shift we shuffle | |
the data between the variables after each iteration | |
*/ | |
// Compute the shifted result of the nonlinear function summed with the message subtext data and the corresponding sinusoidal-based integer; | |
// Note: this result would be stored into a | |
let nonlinear_shifted = round_vars[1] &+ ((round_vars[0] &+ op(round_vars[1], round_vars[2], round_vars[3]) &+ M[m_i] &+ t[l]) <<< s[l]) | |
let temp = round_vars[3] // Store d for copying | |
round_vars[3] = round_vars[2] // d = c | |
round_vars[2] = round_vars[1] // c = b | |
round_vars[1] = nonlinear_shifted // b = a | |
round_vars[0] = temp // a = d | |
} | |
// 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) == "FAE93BD77058E8EDFDE8BE5C39" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment