Skip to content

Instantly share code, notes, and snippets.

@op183
Created May 4, 2016 11:53
Show Gist options
  • Save op183/bb3f296e26586e986d5d1a44a3f0d200 to your computer and use it in GitHub Desktop.
Save op183/bb3f296e26586e986d5d1a44a3f0d200 to your computer and use it in GitHub Desktop.
func crypt(arr: [UInt8], key: Rabbit.Key? = nil, iv: Rabbit.IV? = nil)->[UInt8] {
let r = Rabbit()
if let k = key {
r.keySetup(k)
}
if let v = iv {
r.ivSetup(v)
}
return arr.map { $0 ^ r.next() }
}
func cryptGenerator(key: Rabbit.Key? = nil, iv: Rabbit.IV? = nil)->[UInt8]->[UInt8] {
let r = Rabbit()
if let k = key {
r.keySetup(k)
}
if let v = iv {
r.ivSetup(v)
}
return { arr in
return arr.map { $0 ^ r.next() }
}
}
import Foundation
class FStream {
let f: UnsafeMutablePointer<FILE>
init(path: String, mode: String) {
f = fopen(path, mode)
}
deinit {
fclose(f)
}
func write(data: [UInt8])->Bool { // returns false on error
guard f != nil else { return false }
var data = data
return fwrite(&data, sizeof(UInt8.self), data.count, f) == data.count
}
func read(size: Int)->[UInt8]? { //return nil on error
guard f != nil else { return nil }
var data = [UInt8](count: size, repeatedValue: 0)
let r = fread(&data, sizeof(UInt8.self), size, f)
if r < size {
if ferror(f) != 0 {
return nil
} else {
return Array(data[0..<r])
}
}
return data
}
func seek(offset: Int)->Bool {
guard f != nil else { return false }
let s = fseek(f, offset, SEEK_SET)
if s < 0 {
return false
} else {
return true
}
}
}
let testFile = "/Users/ivovacek/Documents/test.data"
let encrypted = "/Users/ivovacek/Documents/testEncrypted.data"
let decrypted = "/Users/ivovacek/Documents/testDecrypted.data"
// generate random bytes and save in the testFile
let rnd = Rabbit()
var fileStreamWriter: FStream? = FStream(path: testFile, mode: "w")
var chunks = 0
let size = 100_000 // 1024 times chunk size == file size in bytes (102,4 MB)
while chunks < size {
var chunk = [UInt8](count: 1024, repeatedValue: 0)
for c in chunk.enumerate() {
chunk[c.index] = rnd.next() // generate 1024 random bytes
}
if let success = fileStreamWriter?.write(chunk) where success == true {
chunks += 1
} else {
print("error to write data")
break
}
}
fileStreamWriter = nil // close the file
var fileStreamReader: FStream? = FStream(path: testFile, mode: "r")
fileStreamWriter = FStream(path: encrypted, mode: "w")
// encrypt random data
let encrypt = cryptGenerator(iv: (221, 22))
repeat {
// do it in 1024 bytes long chunks, to save memory usage
if let data = fileStreamReader?.read(1024) where !data.isEmpty {
if let success = fileStreamWriter?.write(encrypt(data)) where success == false {
print("error to write data")
break
}
} else {
break
}
} while true
// reuse streams
fileStreamReader = FStream(path: encrypted, mode: "r")
fileStreamWriter = FStream(path: decrypted, mode: "w")
// decrypt it back (with the same settings as for encryption)
var decrypt = cryptGenerator(iv: (221, 22))
repeat {
// do it in 2048 bytes long chunks now :-)
if let data = fileStreamReader?.read(2048) where !data.isEmpty {
if let success = fileStreamWriter?.write(decrypt(data)) where success == false {
print("error to write data")
break
}
} else {
break
}
} while true
// close both files
fileStreamReader = nil
fileStreamWriter = nil
// compare some part of files, return false if found some difference
func compareFiles(path0: String, path1: String, from: Int, bytes: Int)->Bool {
let fr0 = FStream(path: path0, mode: "r")
let fr1 = FStream(path: path1, mode: "r")
let s0 = fr0.seek(from)
let s1 = fr1.seek(from)
if s0 == false || s1 == false {
print("error to seek")
return false
}
if let b0 = fr0.read(bytes),
let b1 = fr1.read(bytes) {
for byte in b0.enumerate() {
if byte.element != b1[byte.index] {
return false
}
}
} else {
print("error to read data")
}
return true // the same data in both files at the same position
}
// try to compere 64 bytes from testFile and decrypted at offset 100
let mirror = compareFiles(testFile, path1: decrypted, from: 100, bytes: 64)
print("is mirror:", mirror)
// generator with offset
func cryptGenerator(key: Rabbit.Key? = nil, iv: Rabbit.IV? = nil, offset: Int)->[UInt8]->[UInt8] {
let r = Rabbit()
if let k = key {
r.keySetup(k)
}
if let v = iv {
r.ivSetup(v)
}
// skip offset bytes from 'one time pad'
let blockSize = sizeof(Rabbit.Key.self) // for Rabbit, the block state size is 16 bytes
var blockOffset = offset / blockSize
while blockOffset > 0 {
r.nextState(master: false)
blockOffset -= 1
}
var byteOffset = offset % blockSize // remainding bytes to skip
while byteOffset > 0 {
r.next()
byteOffset -= 1
}
// and return correct state for further encryption / decryption
return { arr in
return arr.map { $0 ^ r.next() }
}
}
fileStreamReader = FStream(path: testFile, mode: "r")
var fileStreamReader2: FStream? = FStream(path: encrypted, mode: "r")
var offset = 100_000_000 // in bytes ( cca 100 MB from the beginning :-)
fileStreamReader?.seek(offset)
fileStreamReader2?.seek(offset)
// read from the testFile and from the encrypted 100 bytes from offset position
var start = NSDate()
decrypt = cryptGenerator(iv: (221, 22), offset: offset) // will decrypt data from offset in file
if let data = fileStreamReader?.read(100),
let data2 = fileStreamReader2?.read(100) {
// decrypt 100 bytes
let decryptedChunk = decrypt(data2)
var mirror = true
for byte in data.enumerate() {
if byte.element != decryptedChunk[byte.index] {
mirror = false
}
}
let time = NSDate().timeIntervalSinceDate(start)
print("is mirror: \(mirror) from offset: \(offset) decrypted in: \(time) seconds")
}
// seek back :-)
offset = 10_000_000 // in bytes ( cca 10 MB from the beginning :-)
fileStreamReader?.seek(offset)
fileStreamReader2?.seek(offset)
// read from the testFile and from the encrypted 100 bytes from offset position
start = NSDate()
decrypt = cryptGenerator(iv: (221, 22), offset: offset) // will decrypt data from offset in file
if let data = fileStreamReader?.read(100),
let data2 = fileStreamReader2?.read(100) {
// decrypt 100 bytes
let decryptedChunk = decrypt(data2)
var mirror = true
for byte in data.enumerate() {
if byte.element != decryptedChunk[byte.index] {
mirror = false
}
}
let time = NSDate().timeIntervalSinceDate(start)
print("is mirror: \(mirror) from offset: \(offset) decrypted in: \(time) seconds")
}
/*
is mirror: true
is mirror: true from offset: 100000000 decrypted in: 0.657293021678925 seconds
is mirror: true from offset: 10000000 decrypted in: 0.0662170052528381 seconds
Program ended with exit code: 0
*/
public class Rabbit {
typealias Vect = (UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32, UInt32)
static let vect0: Vect = (0,0,0,0,0,0,0,0)
typealias State = (x: Vect, c: Vect, carry: UInt32)
static let state0: State = (x: vect0, c: vect0, carry: 0)
public typealias Key = (UInt32, UInt32, UInt32, UInt32)
public static let key0: Key = (0,0,0,0)
public typealias IV = (UInt32,UInt32)
public static let iv0: IV = (0,0)
typealias Ctx = (master: State, work: State)
var ctx: Ctx = (master: state0, work: state0)
private var byteStream: [UInt8] = []
public init() {}
public init(key: Key, iv: IV = Rabbit.iv0) {
keySetup(key)
ivSetup(iv)
}
private let rabbitG = { (p: UInt32)->UInt32 in
let p = UInt64(p)
let t2 = p * p
let r = t2 ^ (t2 >> 32)
return UInt32(truncatingBitPattern: r)//UInt32(r % 0x1_0000_0000)
}
public func keySetup(key: Key) {
// setup initial state
ctx.master.x.0 = key.0
ctx.master.x.2 = key.1
ctx.master.x.4 = key.2
ctx.master.x.6 = key.3
ctx.master.x.1 = key.3 << 16 | key.2 >> 16
ctx.master.x.3 = key.0 << 16 | key.3 >> 16
ctx.master.x.5 = key.1 << 16 | key.0 >> 16
ctx.master.x.7 = key.2 << 16 | key.1 >> 16
ctx.master.c.0 = key.2 << 16 | key.2 >> 16
ctx.master.c.2 = key.3 << 16 | key.3 >> 16
ctx.master.c.4 = key.0 << 16 | key.0 >> 16
ctx.master.c.6 = key.1 << 16 | key.1 >> 16
ctx.master.c.1 = (key.0 & 0xFFFF_0000) | (key.1 & 0xFFFF)
ctx.master.c.3 = (key.1 & 0xFFFF_0000) | (key.2 & 0xFFFF)
ctx.master.c.5 = (key.2 & 0xFFFF_0000) | (key.3 & 0xFFFF)
ctx.master.c.7 = (key.3 & 0xFFFF_0000) | (key.0 & 0xFFFF)
ctx.master.carry = 0
// iterate system master ctx 4 times
(0..<4).forEach {_ in
nextState(master: true)
}
// Modify the counters
//
ctx.master.c.0 ^= ctx.master.x.4
ctx.master.c.1 ^= ctx.master.x.5
ctx.master.c.2 ^= ctx.master.x.6
ctx.master.c.3 ^= ctx.master.x.7
ctx.master.c.4 ^= ctx.master.x.0
ctx.master.c.5 ^= ctx.master.x.1
ctx.master.c.6 ^= ctx.master.x.2
ctx.master.c.7 ^= ctx.master.x.3
// copy master to work (x,c,carry)
ctx.work = ctx.master
byteStream = []
}
public func ivSetup(iv: IV) {
let v = (
iv.0, // IV[31..0]
(iv.1 & 0xFFFF_0000) | (iv.0 >> 16 & 0xFFFF), // (IV[63..48] || IV[31..16])
iv.1, // IV[63..32]
iv.1 << 16 | (iv.0 & 0x0000_FFFF) // (IV[47..32] || IV[15..0])
)
// mofify the counters
ctx.work.c.0 = ctx.master.c.0 ^ v.0
ctx.work.c.1 = ctx.master.c.1 ^ v.1
ctx.work.c.2 = ctx.master.c.2 ^ v.2
ctx.work.c.3 = ctx.master.c.3 ^ v.3
ctx.work.c.4 = ctx.master.c.4 ^ v.0
ctx.work.c.5 = ctx.master.c.5 ^ v.1
ctx.work.c.6 = ctx.master.c.6 ^ v.2
ctx.work.c.7 = ctx.master.c.7 ^ v.3
// copy rest of master to work (x, carry)
ctx.work.x = ctx.master.x
ctx.work.carry = ctx.master.carry
// iterate system work ctx 4 times
(0..<4).forEach {_ in
nextState(master: false)
}
byteStream = []
}
func nextState(master master: Bool) {
var p = master ? ctx.master: ctx.work
let c = p.c
p.c.0 = p.c.0 &+ 0x4D34D34D &+ p.carry
p.c.1 = p.c.1 &+ 0xD34D34D3 &+ (p.c.0 < c.0 ? 1: 0)
p.c.2 = p.c.2 &+ 0x34D34D34 &+ (p.c.1 < c.1 ? 1: 0)
p.c.3 = p.c.3 &+ 0x4D34D34D &+ (p.c.2 < c.2 ? 1: 0)
p.c.4 = p.c.4 &+ 0xD34D34D3 &+ (p.c.3 < c.3 ? 1: 0)
p.c.5 = p.c.5 &+ 0x34D34D34 &+ (p.c.4 < c.4 ? 1: 0)
p.c.6 = p.c.6 &+ 0x4D34D34D &+ (p.c.5 < c.5 ? 1: 0)
p.c.7 = p.c.7 &+ 0xD34D34D3 &+ (p.c.6 < c.6 ? 1: 0)
p.carry = p.c.7 < c.7 ? 1: 0
// calculate g vector
let g: Vect = (
rabbitG(p.x.0 &+ p.c.0),
rabbitG(p.x.1 &+ p.c.1),
rabbitG(p.x.2 &+ p.c.2),
rabbitG(p.x.3 &+ p.c.3),
rabbitG(p.x.4 &+ p.c.4),
rabbitG(p.x.5 &+ p.c.5),
rabbitG(p.x.6 &+ p.c.6),
rabbitG(p.x.7 &+ p.c.7)
)
// calculate new state values
p.x.0 = g.0 &+ (g.7 << 16 | g.7 >> 16) &+ (g.6 << 16 | g.6 >> 16)
p.x.1 = g.1 &+ (g.0 << 8 | g.0 >> 24) &+ g.7
p.x.2 = g.2 &+ (g.1 << 16 | g.1 >> 16) &+ (g.0 << 16 | g.0 >> 16)
p.x.3 = g.3 &+ (g.2 << 8 | g.2 >> 24) &+ g.1
p.x.4 = g.4 &+ (g.3 << 16 | g.3 >> 16) &+ (g.2 << 16 | g.2 >> 16)
p.x.5 = g.5 &+ (g.4 << 8 | g.4 >> 24) &+ g.3
p.x.6 = g.6 &+ (g.5 << 16 | g.5 >> 16) &+ (g.4 << 16 | g.4 >> 16)
p.x.7 = g.7 &+ (g.6 << 8 | g.6 >> 24) &+ g.5
// update counters????
// update state
if master {
ctx.master = p
} else {
ctx.work = p
}
}
public func next()->UInt8 {
if byteStream.isEmpty {
nextState(master: false)
var stream: Key = (
(ctx.work.x.0 ^ ctx.work.x.5 >> 16 ^ ctx.work.x.3 << 16),
(ctx.work.x.2 ^ ctx.work.x.7 >> 16 ^ ctx.work.x.5 << 16),
(ctx.work.x.4 ^ ctx.work.x.1 >> 16 ^ ctx.work.x.7 << 16),
(ctx.work.x.6 ^ ctx.work.x.3 >> 16 ^ ctx.work.x.1 << 16)
)
byteStream = withUnsafePointer(&stream) { (pt) -> [UInt8] in
var arr: [UInt8] = []
let pa = UnsafePointer<UInt8>(pt)
for i in (0..<sizeof(Key)) {
arr.append((pa + i).memory)
}
return arr
}
}
return byteStream.removeFirst()
}
}
public extension UTF8 {
static func decode(arr: [UInt8])->String {
var d = UTF8()
var g = arr.generate()
var str0 = ""
loop: while true {
switch d.decode(&g) {
case .Result(let c): str0.append(c)
case .Error:
return "ERROR"
case .EmptyInput:
break loop
}
}
return str0
}
}
public extension UnsignedIntegerType {
var hex: String {
var str = String(self, radix: 16, uppercase: true)
while str.characters.count < 2 * sizeof(Self.self) {
str.insert("0", atIndex: str.startIndex)
}
return str
}
}
public extension Array where Element: UnsignedIntegerType {
var hex: String {
var str = ""
self.forEach { (u) in
str.appendContentsOf(u.hex)
}
return str
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment