Last active
February 20, 2017 22:43
-
-
Save devpolant/a1530e4bdce64cc609514d5cf0368150 to your computer and use it in GitHub Desktop.
Cryptography
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
import Foundation | |
// MARK: - Text + Statistic | |
public extension Character { | |
var isWhiteSpace: Bool { | |
return self == Character(" ") | |
} | |
} | |
struct Letter: CustomStringConvertible { | |
var char: Character | |
var count: Int | |
var percent: Double | |
var description: String { | |
let formatter = NumberFormatter() | |
formatter.maximumFractionDigits = 2 | |
let percentage = formatter.string(for: percent * 100)! | |
return "\(char): count=\(count), \(percentage)%" | |
} | |
} | |
public func analyze(text: String, ofType textType: String) { | |
print("========== \(textType) ==========") | |
var dictionary = [Character: Int]() | |
var nonWhiteScapeCount = 0 | |
for char in text.lowercased().characters { | |
guard !char.isWhiteSpace else { | |
continue | |
} | |
nonWhiteScapeCount += 1 | |
let count = dictionary[char] ?? 0 | |
dictionary[char] = count + 1 | |
} | |
let sortedResult = dictionary.sorted { first, second -> Bool in | |
if first.value != second.value { | |
return first.value > second.value | |
} | |
return first.key < second.key | |
} | |
let statistic = sortedResult.map { pair -> Letter in | |
return Letter(char: pair.key, | |
count: pair.value, | |
percent: Double(pair.value) / Double(nonWhiteScapeCount)) | |
} | |
print(statistic) | |
} | |
//MARK: - XOR | |
public func testXOR() { | |
let key = "12345" | |
let inputText = "Some test text" | |
let encrypted = xor(string: inputText, key: key) | |
let decrypted = xor(string: encrypted!, key: key) | |
print("encrypted: '\(encrypted!)', key: \(key)") | |
print("decrypted: '\(decrypted!)', key: \(key)") | |
} | |
func xor(string: String, key: String) -> String? { | |
let utf8Key = key.utf8 | |
let bytes = string.utf8.enumerated().map { | |
$1 ^ utf8Key[utf8Key.index(utf8Key.startIndex, offsetBy: $0 % utf8Key.count)] | |
} | |
return String(bytes: bytes, encoding: String.Encoding.utf8) | |
} | |
func xor (a: Int, b: Int) -> Int { | |
return (~a & b) | (~b & a) | |
} | |
// MARK: - Cesar | |
public func testCesar() { | |
do { | |
let key = 5 | |
let encrypted = try cesarEncrypt(text: "this is a test message", alphabet: .english, key: key) | |
print("encrypted: '\(encrypted)', key: \(key)") | |
let decrypted = try cesarDecrypt(text: encrypted, alphabet: .english, key: key) | |
print("decrypted: '\(decrypted)', key: \(key)") | |
} catch { | |
print(error) | |
} | |
} | |
public enum Alphabet { | |
case english | |
public var length: Int { | |
switch self { | |
case .english: | |
return 26 | |
} | |
} | |
public var characters: [Character] { | |
return "abcdefghijklmnopqrstuvwxyz".characters.flatMap { $0 } | |
} | |
} | |
public enum CesarError: Error { | |
case encryptionFailed(cause: String) | |
} | |
public func cesarEncrypt(text: String, alphabet: Alphabet, key: Int) throws -> String { | |
var result = "" | |
for char in text.lowercased().characters { | |
guard let index = alphabet.characters.index(of: char) else { | |
if char.isWhiteSpace { | |
result.append(char) | |
continue | |
} | |
throw CesarError.encryptionFailed(cause: "Unrecognized symbol in alphabet") | |
} | |
let encryptedChar = alphabet.characters[(index + key) % alphabet.length] | |
result.append(encryptedChar) | |
} | |
return result | |
} | |
public func cesarDecrypt(text: String, alphabet: Alphabet, key: Int) throws -> String { | |
var result = "" | |
for char in text.lowercased().characters { | |
guard let index = alphabet.characters.index(of: char) else { | |
if char.isWhiteSpace { | |
result.append(char) | |
continue | |
} | |
throw CesarError.encryptionFailed(cause: "Unrecognized symbol in alphabet") | |
} | |
let encryptedChar = alphabet.characters[(index - key >= 0 ? index - key : alphabet.length + index - key) % alphabet.length] | |
result.append(encryptedChar) | |
} | |
return result | |
} | |
// MARK: - Vernam | |
public func testVernam() { | |
let text = "This is a test message" | |
let cipher = VernamCipher() | |
let encrypted = cipher.encrypt(text: text) | |
let decrypted = cipher.decrypt(text: encrypted.text, key: encrypted.key) | |
print("Original: \(text)") | |
print("Encrypted: \(encrypted.text)") | |
print("Decrypted: \(decrypted)") | |
} | |
class VernamCipher { | |
func encrypt(text: String) -> (text: String, key: [Int]) { | |
let text = text.lowercased() | |
let key = self.key(count: text.characters.count) | |
let map = self.map() | |
var output = String() | |
for (index, character) in text.characters.enumerated() { | |
if character == " " { | |
output.append(character) | |
} else if let letterIndex = map.forward[String(character)] { | |
let keyIndex = key[index] | |
let outputIndex = (letterIndex + keyIndex + map.lastCharacterIndex) % map.lastCharacterIndex | |
if let outputCharacter = map.reversed[outputIndex] { | |
output.append(outputCharacter) | |
} | |
} | |
} | |
return (text: output.uppercased(), key: key) | |
} | |
func decrypt(text: String, key:[Int]) -> String { | |
let text = text.lowercased() | |
let map = self.map() | |
var output = String() | |
for (index, character) in text.characters.enumerated() { | |
if character == " " { | |
output.append(character) | |
} else if let letterIndex = map.forward[String(character)] { | |
let keyIndex = key[index] | |
let outputIndex = (letterIndex - keyIndex + map.lastCharacterIndex) % map.lastCharacterIndex | |
if let outputCharacter = map.reversed[outputIndex] { | |
output.append(outputCharacter) | |
} | |
} | |
} | |
return output | |
} | |
let alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] | |
func key(count: Int) -> [Int] { | |
var key = [Int]() | |
for _ in 0..<count { | |
key.append(Int(arc4random() % 26)) | |
} | |
return key | |
} | |
private func map() -> (forward: [String : Int], reversed: [Int : String], lastCharacterIndex: Int) { | |
var forward = [String : Int]() | |
var reversed = [Int : String]() | |
var lastCharacterIndex = 0 | |
for (index, letter) in self.alphabet.enumerated() { | |
forward[letter] = index | |
reversed[index] = letter | |
lastCharacterIndex = index | |
} | |
return (forward: forward, reversed: reversed, lastCharacterIndex: lastCharacterIndex) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment