Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
This playground shows how to use a RepeatingSequence type to create strong string obfuscation using a multi-byte nonce.
import Foundation
public struct ObfuscationDecoder {
public enum DeobfuscationError: Error {
case invalidStringBytes
/// The multi-byte nonce used to encode strings.
private static let nonce: [UInt8] = [199, 152, 254, 45, 85, 241, 134, 185, 22, 249, 182, 208, 43, 176, 143, 252]
/// The nonce converted into a RepeatingSequence.
private static let nonceSequence = RepeatingSequence(data: AnyCollection(nonce), maxCount: nil)
/// Converts an obfuscated byte array back into a String.
/// - Parameter bytes: The obfuscated string bytes to be decoded.
/// - Returns: The decoded String.
/// - Throws: An .invalidStringBytes if the deobfuscated bytes could not be used to create a String.
public static func decode(bytes: [UInt8]) throws -> String {
let decodingSequence = zip(bytes, nonceSequence)
let decodedBytes ={$0 ^ $1})
guard let string = String(bytes: decodedBytes, encoding: String.Encoding.utf8) else {
throw DeobfuscationError.invalidStringBytes
return string
//: # String Obfuscator
//: This playground allows you to obfuscate sensitive strings such as API keys
//: using a multi-byte nonce.
import Foundation
//: A RepeatingSequence returns the elements of its underlying collection in a cyclical fashion
//: up to an optional maximum count.
//: - Note: You will need to add this type to your project in order to do the decoding step shown
//: at the bottom of the playground.
struct RepeatingSequence<T>: Sequence {
/// The Collection that we base our sequence on. We use a Collection and not
/// another Sequence because Sequences are not guaranteed to be repeatedly iterated.
let data: AnyCollection<T>
/// We can optionally specify a maximum number of iterations. This is necessary
/// to create non-infinite Sequences.
let maxCount: Int?
func makeIterator() -> AnyIterator<T> {
var index: AnyCollection.Index = data.startIndex
var count: Int = 0
return AnyIterator<T> {
if let max = self.maxCount, count >= max {
return nil
defer {
index = index)
if index == {
index =
count += 1
//: Replace this with the string you wish to encode.
let stringToEncode = "aBcDeFgHiJkLmNoPqRsTuVwXyZ"
print("String to encode: \(stringToEncode)\n")
//: Set `userNonce` to a specific value if you want to use a fixed nonce for your encoding.
//: Otherwise, a random one will be generated and printed out.
let userNonce: [UInt8]? = nil
let nonce: [UInt8]
if let un = userNonce {
nonce = un
} else {
/// Change this value up or down to change the length of the generated nonce.
let nonceLength = 16
var nonceBytes: [UInt8] = []
for i in 0..<nonceLength {
// Generate a sequence of random bytes.
print("Make note of this nonce or you will have no way to decode your string!")
print("Nonce: \(nonceBytes)\n")
nonce = nonceBytes
//: Create a repeating sequence and use zip2 to XOR it with the string bytes.
let repeatingNonce = RepeatingSequence(data: AnyCollection(nonce), maxCount: nil)
let encodingSequence = zip(stringToEncode.utf8, repeatingNonce)
let encodedBytes ={$0 ^ $1})
print("Encoded bytes: \(encodedBytes)\n")
//: Now we'll verify that we can reverse the encoding.
let decodingSequence = zip(encodedBytes, repeatingNonce)
let decodedBytes ={ $0 ^ $1})
guard let decodedString = String(bytes: decodedBytes, encoding: String.Encoding.utf8) else {
preconditionFailure("Unable to convert bytes back into a string!")
print("Decoded string: \(decodedString)")
print("Strings match: \(stringToEncode == decodedString)")

This comment has been minimized.

Copy link

@CristiCh CristiCh commented May 6, 2019

Hi. Under what license is this code under?

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