Skip to content

Instantly share code, notes, and snippets.

@alskipp
Last active November 1, 2018 11:10
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save alskipp/016c8ba96352e5c74bf2 to your computer and use it in GitHub Desktop.
Save alskipp/016c8ba96352e5c74bf2 to your computer and use it in GitHub Desktop.
Swift encrypt/decrypt string using XOR
import Foundation
extension Character {
func utf8() -> UInt8 {
let utf8 = String(self).utf8
return utf8[utf8.startIndex]
}
}
func encrypt(c:Character, key:Character) -> String {
let byte = [c.utf8() ^ key.utf8()]
return String(bytes: byte, encoding: NSUTF8StringEncoding)! // forced unwrapping alert!
}
func encrypt(message:String, #key:String) -> String {
return reduce(Zip2(message, key), "") { $0 + encrypt($1) }
}
let message = "Hello world!"
let secretKey = "(:.,?P!9@PAz" // really should be randomly generated!
let encryptedMessage = encrypt(message, key: secretKey) // > "`_B@PpVV2<%["
let decryptedMessage = encrypt(encryptedMessage, key: secretKey) // > "Hello world!"
/* (Requires Swift 1.2)
In the version above you'll notice that there's use of forced Optional unwrapping.
This is potentially hazardous. Here's a version that deals with Optional values safely.
Having to 'reduce' Optional Strings adds significant complexity.
Below, I've shown 2 ways of dealing with this (Monadic bind & 'if let' syntax).
*/
import Foundation
// Monadic bind for Optionals
infix operator >>= {associativity left}
func >>= <A,B> (m: A?, f: A -> B?) -> B? {
if let x = m {return f(x)}
return .None
}
extension Character {
func utf8() -> UInt8 {
let utf8 = String(self).utf8
return utf8[utf8.startIndex]
}
}
func encrypt(key:Character, c:Character) -> String? {
let byte = [key.utf8() ^ c.utf8()]
return String(bytes: byte, encoding: NSUTF8StringEncoding)
}
// Curried func for convenient use with map
func encryptKey(key:String)(message:String) -> String? {
return reduce(zip(key, message), Optional("")) { str, c in str >>= { s in encrypt(c).map {s + $0} }}
}
let message = "Hello world!"
let secretKey = "(:.,?P!9@PAz" // really should be randomly generated!
let encryptedMessage = encryptKey(secretKey)(message: message) // > .Some("`_B@PpVV2<%[")
// As the encryptKey func is curried it can be passed directly to map
let decryptedMessage = encryptedMessage.map(encryptKey(secretKey)) // > .Some("Hello world!")
/* here's how the encrypt function would look like using 'if let' syntax
func encryptKey_if_let(key:String)(message:String) -> String? {
return reduce(zip(key, message), Optional("")) { if let str = $0, c = encrypt($1) {
return str + c
} else {
return .None}
}
}
*/
@alskipp
Copy link
Author

alskipp commented Oct 28, 2014

This started as a Twitter conversation with https://twitter.com/sketchyTech and resulted in the following blog post: http://sketchytech.blogspot.co.uk/2014/10/bytes-for-beginners-xor-encryption-and.html

The idea was to show a simple way of using XOR to encrypt a string in Swift. The version presented here does essentially the same thing, though written in a functional style.

Using Swift it's possible to access a UTF8 representation of a String, but not a Character, therefore an extension has been added to make this possible. I'd consider the most interesting implementation detail of this gist to be the use of Zip2. It initialises a struct from two SequenceTypes and allows the two sequences to be iterated simultaneously as pairs. In this case reduce iterates through the two Strings as tuple values (Character, Character). Each tuple is passed to encrypt(s:Character, key:Character) -> String which returns an encrypted String that is then appended to the previous result.

For more details about the use of XOR take a look at the linked blog post above.

@kristoferdoman
Copy link

My message is just a string object when returned from the server. How come I cannot call .map on a string? Sorry, I'm still relatively new to Swift. The only difference I see between your code and mine is that your string is an optional String.

@freak4pc
Copy link

freak4pc commented May 1, 2016

I'm wondering if there's a library around this solution or was this dropped back then?

Thanks :)
Shai

@robinclark
Copy link

robinclark commented Feb 1, 2017

could someone explain encryptKey? I'm guessing that its reducing zipped tuples of the key and message characters. However it looks like its calling the encrypt function with a tuple, instead of 2 characters. Can/How could this work in Swift 3?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment