Skip to content

Instantly share code, notes, and snippets.

@sjbodzo
Last active January 12, 2016 21:01
Show Gist options
  • Save sjbodzo/72128f1cf1c6edaf0d11 to your computer and use it in GitHub Desktop.
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.
//: 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