Apple’s open-source CommonCrypto isn’t shabby for anyone looking to implement encryption in their app, but it isn’t very “Swifty” to use. Luckily, Danny Keogan wrote a nice wrapper called IDZSwiftCommonCrypto, which renders Swift encryption a much friendlier beast.
Introduction (0:00)
In this post, I’m going to discuss a wrapper I wrote around CommonCrypto called IDZSwiftCommonCrypto, which makes it a lot Swiftier to use. Upfront, I’ll mention that you can find all the below code on GitHub, and if you have any questions about it, you can find me @iOSDevZone on Twitter.
For a quick outline, this post will cover:
- Intro to CommonCrypto
- How to Access CommonCrypto in Swift
- IDZSwiftCommonCrypto Design Goals
- IDZSwiftCommonCrypto API
- Some words of caution
- Other Libraries/Projects
- Summary
What is CommonCrypto? (1:26)
CommonCrypto is Apple’s Open Source cryptography library that is included in iOS & OS X. You can find it at opensource.apple.com. When you’re choosing a crypto library, it’s important to choose one that’s open source, because otherwise you don’t really know what’s going on in there.
CommonCrypto is a C library, so that makes it a little bit unpalatable to use in Swift. It is part of System.framework and, unfortunately, it’s not directly accessible by default in Swift. But we can work around that.
It provides a number of features: message digests, hash-based message authentication codes, cryptors (which are basically a catch-all for encryptors and decryptors), and then a couple of utility routines, such as key derivation routines and random number generation routines.
API Design Goals (2:23)
When designing my API, I had several goals in mind.
First of all, I wanted it to be implementation independent. I didn’t want to be tied into CommonCrypto because there are certain things it can’t do, and certain things that other libraries like OpenSSL does do. The user-facing API of it doesn’t actually bleed through any of CommonCrypto; it’s pretty much independent. There is also an IDZSwiftOpenSSL, but that’s not quite ready for prime time yet.
I also wanted to make the layer as thin as possible to make it Swifty, while avoiding the introduction of any security issues. It’s extremely easy to introduce security problems if you’re meddling with a crypto library.
Finally, I wanted it be easy to use. There are a lot of inconsistencies in Apple’s CommonCrypto API, and even in C, it’s not that pleasant to use.
IDZSwiftCommonCrypto API (3:32)
The first problem I ran up against when trying to use CommonCrypto was that Apple does not provide a module map. I kept getting a “No such module” error. You might say “it’s in the system.framework module!”, but somehow they don’t export the right symbols.
After a bit of digging about, I found that the solution to this was to create a fake module map for the CommonCrypto library.
module CommonCrypto [system] {
header "/Applications/Xcode-7.0-7A218.app/Contents/Developer/Platforms/MacOSX.platform/
Developer/SDKs/MacOSX10.11.sdk/usr/include/CommonCrypto/CommonCrypto.h"
header "/Applications/Xcode-7.0-7A218.app/Contents/Developer/Platforms/MacOSX.platform/
Developer/SDKs/MacOSX10.11.sdk/usr/include/CommonCrypto/CommonRandom.h"
export *
}
This turns out to be a pain when you’re sending out a library because the header entries require absolute library names. The example here is from my Mac and you can see that I put my Xcode in a non-standard place because, like every Swift developer, I have three or four different versions going at the same time.
This is basically something that you want to write a script to do. Originally, I wrote a Bash script. There exists a newer version, as I’ve added support for tvOS and watchOS, that’s all written in Swift.
Once you’ve got access to the various routines, we can look and find out what they are, what we can do with them, and how I wrapped them up in Swift.
Message Digests (4:52)
Basically, this is a cryptographic hash function. It takes a message in and produces digests on the other side.
It has a few of properties that make it a cryptographic hash function:
- Given
m
easy to calculateh
- Given
h
difficult to findm
- Given
m1
difficult to findm2
such thathash(m1) == hash(m2)
- Difficult to find pair (
m1
,m2
) such thathash(m1) == hash(m2)
So, given a message, it should be quick and easy to calculate the hash.h. Also, given the hash.h, it should be difficult (or “infeasible”) to find “m”, the message. Furthermore, if you have a message, m1, it should be extremely difficult to find another message m2 that hashes to the same value. The final thing is that it should be very difficult to come up with two messages, m1 and m2, that hash to the same value.
There are fancy names for all of those, like “preimage resistance” and “second preimage resistance,” but that’s the gist of what they are.
CommonDigest.h
(6:04)
If we were trying to do this in C, what would this look like? Here is a quick excerpt from CommonDigest.h:
public func CC_MD2_Init(c: UnsafeMutablePointer<CC_MD2_CTX>) -> Int32
public func CC_MD2_Update(c: UnsafeMutablePointer<CC_MD2_CTX>, _ data: UnsafePointer<Void>, _ len: CC_LONG) -> Int32
public func CC_MD2_Final(md: UnsafeMutablePointer<UInt8>, _ c: UnsafeMutablePointer<CC_MD2_CTX>) -> Int32
There’s an initialization routine that initializes some context, there’s an update which takes in a buffer and updates the calculation, and then there’s a final routine, which gives you the actual digest. Notice that the context is the first argument of the first two routines and the last one in the third. In general, you’ll see that they’re completely inconsistent about where they put their arguments. For digest, though, they’re particularly bad because this is for an old digest function called MD2, which you probably shouldn’t use anymore.
Of course, they add more for MD4 and for MD5. Eventually, you’ve got eight algorithms, each with three different routines, and it’s a mess.
We can definitely do better when we’re making this Swifty. We don’t want to bring this in all as it is.
Simplify the Types (7:04)
One thing that I found that Swift is doing for me is that I think a lot more about types. If we step back from those functions a bit, we can see that there were basically three functions:
typealias Initializer = (Context) -> (Int32)
typealias Updater = (Context, Buffer, CC_LONG) -> (Int32)
typealias Finalizer = (Digest, Context) -> (Int32)
And if we simplify the types, we can see that this is what they look like. This just gives a much clearer view of what’s going on:
class DigestEngineCC<C> : DigestEngine {
typealias Context = UnsafeMutablePointer<C>
typealias Buffer = UnsafePointer<Void>
typealias Digest = UnsafeMutablePointer<UInt8>
typealias Initializer = (Context) -> (Int32)
typealias Updater = (Context, Buffer, CC_LONG) -> (Int32)
typealias Finalizer = (Digest, Context) -> (Int32)
/* . . . */
init(initializer : Initializer, updater : Updater, finalizer : Finalizer, length : Int32)
func update(buffer: Buffer, _ byteCount: CC_LONG)
func final() -> [UInt8]
}
We have to peel it back a little bit, of course, because there are those nasty pointers and such. Then, if we wrap that up in a class, this all becomes rather nice. This is only parameterized by the context structure of the individual algorithm. Otherwise, we can abstract away all that complexity.
If we create one of these for each of the algorithms, we’re then going to be able to use it.
protocol DigestEngine
{
func update(buffer: UnsafePointer<Void>, _ byteCount: CC_LONG)
func final() -> [UInt8]
}
And if we create a protocol, as I have called DigestEngine, then the rest of the code can speak without having to worry about generics or the particular algorithm to our engine. This gave me the first view of my Digest API.
The Digest API 1.2 and 2.0 (8:08)
public class Digest
{
public enum Algorithm
{
case MD2, MD4, MD5, SHA1, SHA224, SHA256, SHA384, SHA512
}
public init(algorithm: Algorithm)
public func update(data : NSData) -> Digest?
public func update(byteArray : [UInt8]) -> Digest?
public func update(string : String) -> Digest?
public func final() -> [UInt8]
var engine: DigestEngine
}
I think you’ll agree, it looks a little bit nicer than the C version. Basically, the init
routine populates the engine based on what algorithm you pass in. From then on, it’s able to talk to the digest engine protocol and it doesn’t care what algorithm you’re using.
Above is the version as it was in Swift 1.2, but we can actually tidy things up a little bit more, because in Swift 2.0, they introduced protocol extensions. If we define a protocol Updateable
, we can then factor out all that code that was talking about updating based on different types.
public protocol Updateable {
var status : Status { get }
func update(buffer : UnsafePointer<Void>, _ byteCount : size_t) -> Self?
}
extension Updateable {
public func update(data: NSData) -> Self?
{
update(data.bytes, size_t(data.length))
return self.status == Status.Success ? self : nil
}
public func update(byteArray : [UInt8]) -> Self?
{
update(byteArray, size_t(byteArray.count))
return self.status == Status.Success ? self : nil
}
public func update(string: String) -> Self?
{
update(string, size_t(string.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)))
return self.status == Status.Success ? self : nil
}
}
You have all these different types, and it becomes annoying to use it if you’ve got some stuff that NSData, some stuff that’s UInt arrays, or some stuff that’s string. This just makes it a little bit nicer. With the protocols extensions, I don’t have to repeat this code in every single class, which is what I had to do in Swift 1.2. So win for Swift 2.0.
public class Digest : Updateable {
public enum Algorithm {
case MD2, MD4, MD5, SHA1, SHA224, SHA256, SHA384, SHA512 }
public init(algorithm: Algorithm)
public func update(buffer: UnsafePointer<Void>, _ byteCount: size_t) -> Self?
public func final() -> [UInt8]
}
Using Digest (9:34)
What does it look like now if I want to calculate a digest?
let m1 = "The quick brown fox jumps over the lazy dog."
let sha1 = Digest(algorithm: .SHA1).update(m1)?.final()
let d = Digest(algorithm: .SHA1)
d.update(m1)
let sha1 = d.final()
It’s fairly straightforward. The top version shows if you were just trying to calculate on a short buffer what the digest is. You can use optional chaining to put it all on one line.
For the lower view, if you were perhaps trying to generate a digest over something that was coming in off the network, although I only call update
once, you would call update
as each block comes in and update it and eventually check that your digest is okay at the end.
So those are message digests; they’re great for storing passwords in a database, as long as you add Salt to it, and they’re great for testing whether something has changed. If you use Git, of course, you’re familiar with this. One thing that they’re not good for, though, is that they can’t detect if somebody else has intercepted the message and modified it. All you need in order to calculate the digest is the message and knowledge of the algorithm. If you want protection, then you need to use hash-based message authentication codes, or HMAC.
HMAC (10:51)
Basically, an HMAC takes a message digest and permutes it with a key, such that to calculate the HMAC, you must also be in possession of the key. To verify it, you also have to be in possession of the key.
That in itself causes a little bit of a problem, because you have to make sure that the keys are securely transmitted. Luckily, there are key exchange protocols. (Key exchange protocols fall outside the scope of this present post, but if you’re interested in cryptography, these are amazing to look into. The most common one is called Diffie-Hellman key exchange, and it’s absolutely mind-blowing.)
A key exchange protocol the additional ability to verify that the message is at it was intended by the person who had control of the key. If you have a good trust that the key belongs to the person, or that only you and the other person can share the key, then you can know that the message came from them and it is the message that they sent.
Alright, let’s have a look at the header file:
public func CCHmacInit(ctx: UnsafeMutablePointer<CCHmacContext>,
_ algorithm: CCHmacAlgorithm,
_ key: UnsafePointer<Void>, _ keyLength: Int)
public func CCHmacUpdate(ctx: UnsafeMutablePointer<CCHmacContext>, _ data:
UnsafePointer<Void>, _ dataLength: Int)
public func CCHmacFinal(ctx: UnsafeMutablePointer<CCHmacContext>, _ macOut:
UnsafeMutablePointer<Void>)
The interesting thing here is that it’s completely inconsistent with the other API. In fact, it looks a little bit more like the message digest API. In particular, there’s this algorithm
argument, so it looks like it should be much easier to Swiftify.
However, there’s one problem: when Swift imports constants from C, it doesn’t do it as literals, and RawRepresentable enums can’t handle non-literals.
Luckily, because Swift enums can have methods attached to them, we can just solve it. It’s not that pretty, but if you just ignore that, you can bury all the complexity down a level.
public class HMAC {
public enum Algorithm { case MD5, SHA1, SHA224, SHA256, SHA384, SHA512 }
static let fromNative : [CCHmacAlgorithm: Algorithm] = [
CCHmacAlgorithm(kCCHmacAlgSHA1):.SHA1,
CCHmacAlgorithm(kCCHmacAlgSHA1):.MD5,
CCHmacAlgorithm(kCCHmacAlgSHA256):.SHA256,
CCHmacAlgorithm(kCCHmacAlgSHA384):.SHA384,
CCHmacAlgorithm(kCCHmacAlgSHA512):.SHA512,
CCHmacAlgorithm(kCCHmacAlgSHA224):.SHA224
]
func nativeValue() -> CCHmacAlgorithm {
switch self {
case .SHA1: return CCHmacAlgorithm(kCCHmacAlgSHA1)
case .MD5: return CCHmacAlgorithm(kCCHmacAlgMD5)
case .SHA224: return CCHmacAlgorithm(kCCHmacAlgSHA224)
case .SHA256: return CCHmacAlgorithm(kCCHmacAlgSHA256)
case .SHA384: return CCHmacAlgorithm(kCCHmacAlgSHA384)
case .SHA512: return CCHmacAlgorithm(kCCHmacAlgSHA512)
}
}
static func fromNativeValue(nativeAlg : CCHmacAlgorithm) -> Algorithm? {
return fromNative[nativeAlg]
}
}
Once you do that, then the HMAC API looks like this below. As you can see, it’s beginning to look extremely similar to the digest one.
public class HMAC : Updateable {
public enum Algorithm {
case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
}
public init(algorithm: Algorithm, key: NSData)
public init(algorithm: Algorithm, key: [UInt8])
public init(algorithm: Algorithm, key: String)
public func update(buffer: UnsafePointer<Void>, _ byteCount: size_t) -> Self?
public func final() -> [UInt8]
}
The only bit that makes it a little bit different is that I’ve given some convenience initializers so that you don’t have to cast stuff if your key which happens to be coming from Objective-C as NSData, or if you want to use a string as your key.
Using HMAC (13:41)
Using it is fairly straightforward. In fact, using it is exactly the same as the message digest, except there’s the additional initialization parameter of the key:
let key = arrayFromHexString("408d94384216f890ff7a0c3528e8bed1e0b01621")
let m1 = "The quick brown fox jumps over the lazy dog."
let hmac1 = HMAC(algorithm: .SHA1, key: key).update(m1)?.final()
let hmac2 = HMAC(algorithm: .SHA1, key: key)
hmac2.update(m1)
let hmac2final = hmac2.final()
Hopefully you agree that this is a bit better than the C interface.
Cryptor (13:58)
When you think of cryptography, you generally don’t think much of message digests and message authentication code. Most people think of sending secret messages with keys, and all that sort of thing. The class that looks after that part is the cryptor classes. Adopting CommonCrypto’s terminology, they’re just using cryptor to encompass a decryptor or an encryptor.
When sending a message with cryptography, we start with a sender has a message and a key. Similar to the message authentication codes, both the sender and the receiver have to have a shared key that’s shared by some means (we assume it’s by a key exchange protocol, so any eavesdropper can see and it’s not a problem). For some modes of operation, there will be also an intialization vector, which is just a random block used to start off the transmission.
The key, the plaintext, and the initialization vector go into the encryption. Out the other side comes the encrypted message, or ciphertext, and you transmit the initialization vector and the ciphertext. On the receiving side, the receiver passes in the key, the initialization vector, and the ciphertext and if everything works out well, they get the plaintext back. As I mentioned, an eavesdropper can see the initialization vector and it wouldn’t matter.
The initialization vector serves two purposes. The first one is that, in certain modes, the ciphertext for the current block depends on the previous block. Obviously, that begs the question of what the first block uses, since there’s no previous block. It uses the initialization vector. However, because it’s random, it also serves another purpose: if all your messages were to start off with the same thing, then an attacker might notice the first block is always the same ciphertext. Whereas, if you choose your initialization vector randomly, they won’t be able to notice that.
Cryptor in CommonCrypto (16:25)
public func CCCryptorCreate(op: CCOperation, _ alg: CCAlgorithm,
_ options: CCOptions,
_ key: UnsafePointer<Void>, _ keyLength: Int,
_ iv: UnsafePointer<Void>,
_ cryptorRef: UnsafeMutablePointer<CCCryptorRef>) -> CCCryptorStatus
public func CCCryptorUpdate(cryptorRef: CCCryptorRef,
_ dataIn: UnsafePointer<Void>, _ dataInLength: Int,
_ dataOut: UnsafeMutablePointer<Void>, _ dataOutAvailable: Int,
_ dataOutMoved: UnsafeMutablePointer<Int>) -> CCCryptorStatus
public func CCCryptorFinal(cryptorRef: CCCryptorRef,
_ dataOut: UnsafeMutablePointer<Void>, _ dataOutAvailable: Int,
_ dataOutMoved: UnsafeMutablePointer<Int>) -> CCCryptorStatus
public func CCCryptorRelease(cryptorRef: CCCryptorRef) -> CCCryptorStatus
It’s a little more complicated now, because when we create the cryptor, not only is there a key as there was with the HMAC, but we also have the initialization vector. We have to tell it whether we’re encrypting or decrypting. Also, unlike HMACs and digests, we’re not just trying to calculate a single answer here; as we feed in data, we’re going to be getting data out, so each of the updates now has both input buffers and output buffers. Then, when we get to the end, there could still be some stuff in the buffers, so we get some additional data out at the end.
The other thing to note is that there’s also this CCCryptorRelease
. To make sure that we clean up all the resources and everything, we have to have to Swift deinit that calls this to make sure that everything’s looked after correctly.
Swift 2.0 OptionSetType (17:34)
Now for the options parameter. In C, this is a set of flags that can be Bitwise OR’d together. In Swift 1.2, this was a nightmare to deal with because you might think that you could use enums, but you can’t Bitwise OR together enums. (There was a way of doing it, but it took endless amounts of code to get it to work correctly.) I’m not going to go through the Swift 1.2 version here, it was so horrible, but in Swift 2.0, there’s OptionSetType
which makes it really easy to do.
public struct Options : OptionSetType {
public typealias RawValue = Int
public let rawValue: RawValue
public init(rawValue: RawValue) {
self.rawValue = rawValue
}
public init(_ rawValue: RawValue) {
self.init(rawValue: rawValue)
}
public static let None = Options(rawValue: 0)
public static let PKCS7Padding = Options(rawValue:kCCOptionPKCS7Padding)
public static let ECBMode = Options(rawValue:kCCOptionECBMode)
}
At the bottom, those are the three options, and that’s all the code you have to do to bring them in. When you call it from Swift, though, you don’t Bitwise OR them together. Instead, you actually create an array containing the flags that you need.
Now, the padding flag. All the currently implemented cryptography algorithms in CommonCrypto are what are called “block-based algorithms”, which means that you pass in a block of a particular size and you get a block out. If you pass in less than that block, it can’t produce anything, and if your input’s not an integral number of blocks long, obviously the final block’s going to get truncated. To work around that, there’s a particular sort of padding that’s also designed so that it doesn’t leak too much information about how long your message is. Unless you’re coding to a particular protocol and you know that you’re going to have an integral number of block lengths, you probably want to specify this flag.
The other flag is the complete opposite. There are two modes that CommonCrypto or cryptors can operate in.
The first one (the default) is called Chain Block Cipher. That’s the one that uses the initialization vector I mentioned previously, where the ciphertext of the current block depends not only on the current plaintext, but also on the ciphertext of the previous block. Why is this important?
Suppose I have a highly secret message, and I’m going to encrypt it. If I use Chain Block Cipher, it will end up essentially being garbled white noise, and will be unintelliglble. If I’d specified Electronic CodeBook Mode, it would come out as readable.
In Electronic CodeBook Mode, if you put in the same plaintext, you’re going to get the same ciphertext out for a given key. So, although it’s taking blocks of pixels and encrypting them, lots of the blocks are the same, and there’s enough information leakage that you can make out what the original was.
The StreamCryptor API (20:56)
Okay, well if we put it all together then, what does the StreamCryptor look like?
public class StreamCryptor {
public enum Operation { case Encrypt, Decrypt }
public enum Algorithm { case AES, DES, TripleDES, CAST, RC2, Blowfish }
public struct Options : OptionSetType {
static public let None, PKCS7Padding, ECBMode: Options
}
public convenience init(operation: Operation, algorithm: Algorithm, options: Options, key: [UInt8], iv: [UInt8])
public convenience init(operation: Operation, algorithm: Algorithm, options: Options, key: String, iv: String)
public func update(dataIn: NSData, inout byteArrayOut: [UInt8]) -> (Int, Status)
public func update(byteArrayIn: [UInt8], inout byteArrayOut: [UInt8]) -> (Int, Status)
public func update(stringIn: String, inout byteArrayOut: [UInt8]) -> (Int, Status)
public func final(inout byteArrayOut: [UInt8]) -> (Int, IDZSwiftCommonCrypto.Status)
}
This class is quite complicated, and it has to be that way. First of all, you have to pass in all the parameters for the initialization, and then also, as you pass in each block, you’ve got to get a block out as well. If it happens that you’re not getting data nicely aligned, it’ll correctly handle it as long as you specified the padding. It’ll apply the padding in the final block and make sure that everything gets through correctly. You’ll notice as well in this one that I’m using the Swift idea of being able to return a tuple.
There are two things that you might care about here: there’s a Status
because unlike the message digest and HMACs, according to the documentation, there are cases under which this can fail. I’ve never actually seen it do so, but I suppose we should put it in there.
The other thing is that it may not produce a full block, depending on what’s going on. The other return value is a count of how much ciphertext was produced in that iteration.
So, if you’re decrypting data coming over the network or if it’s a big file, this is the interface you want to use. If you’re only doing a small block of data, I also provided a simpler version:
public class Cryptor : StreamCryptor, Updateable {
internal var accumulator: [UInt8]
public func final() -> [UInt8]?
public func update(buffer: UnsafePointer<Void>, _ byteCount: Int) -> Self?
}
You can see that cryptor, if it’s short, is actually pretty straightforward.
Using Cryptor (22:56)
var aesKey1Bytes = arrayFromHexString("2b7e151628aed2a6abf7158809cf4f3c")
var aesIV = arrayFromHexString("deadfacedeadfacedeadfacedeadface")
let encryptor = Cryptor(operation: .Encrypt, algorithm: .AES, options: [.PKCS7Padding], key: aesKey1Bytes, iv: aesIV)
let ciphertext = encryptor.update(m1)?.final()
let decryptor = Cryptor(operation:.Decrypt, algorithm: .AES, options: [.PKCS7Padding], key: aesKey1Bytes, iv: aesIV)
let plaintext = decryptor.update(ciphertext!)?.final()
This is how you would encrypt to a ciphertext and then decrypt the ciphertext back to plaintext. The plaintext, in this case, will be an array of bytes, so you’ll have to do a little bit more munging to get it into a string to prove that it is equal to what you originally passed in.
The PBKDF API (23:16)
It might be tempting when you’re trying to come up with a key for something, particularly if you’re just encrypting a file to disk, to use a pass phrase and to use the ASCII encoding of the pass phrase or something like that. In the past, that may well have been done, but with modern computers, you probably don’t want to do that because they can guess too many thousands or millions of keys a second. A better way to do it is to use a password-based key derivation function, or PBKDF.
public class PBKDF
{
public enum PseudoRandomAlgorithm { case SHA1, SHA224, SHA256, SHA384, SHA512 }
public class func calibrate(passwordLength: Int, saltLength: Int, algorithm: PseudoRandomAlgorithm, derivedKeyLength: Int, msec : UInt32) -> UInt
public class func deriveKey(password : String, salt : String, prf: PseudoRandomAlgorithm, rounds: uint, derivedKeyLength: UInt) -> [UInt8]
}
This is a function specifically designed to be expensive to compute. Normally, they have a balance between computationally expensive and memory expensive so that it slows down ability to see what the key that came out from that was.
I’m not going to bother looking at the header file here, but these are basically Swifty translations of the two functions related to this functionality. The first one is calibrate
: given a specific password length, a specific Salt length, a desired derived key length, and the amount of time that you’re willing to wait on a device, it tells you how many rounds of the function you can run. Basically, the higher the number of rounds, the longer it’s going to take an attacker to try and guess any one password.
Now, you’re probably not going to do this as part of an app, but it’s useful to have it so that when you bring out the next version of your app, you can up the number of rounds if your amount of computational devices has gone up enough, without inconveniencing your user.
Once you’ve decided on the number of rounds, you use deriveKey
to guess key material from the password. It takes the password itself, the Salt, which is a bit like the initialization vector that I mentioned for the cryptors. Essenntially, Salt is a random buffer that’s used to permute your password. It’s normally stored with the password or transmitted with it, but the main thing is that it means that if two people have the same password, it won’t appear the same.
The Random API (26:34)
public class Random
{
public class func generateBytes(byteCount : Int) throws -> [UInt8]
}
For both Salts and initialization vectors, it’s important that they’re random. It’s also important that these random numbers are of sufficiently high quality, because low quality randomization could mean that an attacker could have an easier time gaining information about your system.
Some Cautionary Words (27:15)
IDZSwiftCommonCrypto should be considered beta
I’ve tested it reasonably well, but I think there are still some problems in a few places that I’m looking for good tests for. If you’re using it, please treat it as a beta.
Don’t try to invent your own protocol; there be dragons!
Instead, use one of the existing ones. Use a peer-reviewed one. It’s incredibly easy to shoot yourself in the foot with this. Even some of the existing, well-tested, well-reviewed algorithms like SSH have their problems. Many of the implementations of OpenSSH use a limited version of Diffie-Hellman key exchange, and recent papers have suggested that this is exploitable. If professionals in the area can occasionally screw things up then the chances are, if you’re rolling your own, you’re going to make a mess of it. If the stuff you’re dealing with is very sensitive for your users, that could be a disaster.
Don’t write your own crypto libraries
I should say that didn’t write any crypto algorithms here, I just wrapped it. I’ve said this to few people and they said, “Well, why? I mean, what could possibly go wrong?” Anybody who knows this field will giggle to themselves.
Check local jurisdiction for laws governing use/sale/export of products containing/using cryptography
There are generally laws governing particularly strong cryptography. If you’re submitting to the U.S. App Store and you’ve crossed a certain threshold, you’ll have to provide the information. You’ll need to deal with the Bureau of Industry and Security to get your export license.
Other Projects (32:56)
- RNCryptor (Rob Napier) - Supports C++, C#, Java, PHP, Python, Javascript, and Ruby
- CryptoSwift (Marcin Krzyżanowski) - Pure Swift Implementation
- Crypto (Sam Soffes) - Extensions to NSData and NSString
Summary (34:01)
- Use module maps to generate Swift prototypes for forgotten functions
- Use generic classes to unify related functions and structures
- Use protocol extensions to factor out repetitive code
- Use protocols to bridge non-generic to generic classes
- Use customized enums to work around
RawRepresentable
limitations - Use
OptionSetType
to wrap bitwise flags
Q&A (35:36)
_Q: One thing I’m curious about is when submitting things to the App Store, there’s a “Does your app contain cryptography?” question, but I’ve never been in a position where I’ve needed to check Yes on that. What is that process like, say, here in the United States? How painful is it if you want to get something approved to the App Store that has cryptography in it?
Danny: I can only speak to part of that. One of my apps in the App Store does have cryptography in it, but doesn’t have a level of cryptography that requires me to have a registration number. When you click the Yes button, it asks you a number of other questions. In particular, and this is one of the deciding factors, is something like, “Is it only used to keep user information private?” I was using it for protecting user passwords and there was a small amount of cryptography, and I think Diffie-Hellman key exchange as well. Mine was below the threshold, where I simply said “Yes, it’s in there, but it’s below this threshold, and this is what it does.” That was sufficient for the App Store.
If that’s not enough, the first stage is you register with the Bureau of Industry and Security, then you have to fill out a SNAP-R application with a whole bunch of information about how strong the cryptography is and what it’s used for. Depending on that, you get either an ERN or a CCATS. I think you have to be doing really high-level stuff to get a CCATS.
I haven’t really had to go through the full process. My initial registration took very little time and was very painless.
Q: Did you consider writing this as a bunch of extensions for NSData and NSString? It looks like you’ve written it as a standalone. Also, did you consider using sequence type or generator, because it looks like random gives you a sequence of bytes? It might be possible to implement it using a sequence type.
Danny: Basically, I was trying to provide building blocks. It is really easy to implement the extensions using Sam Soffes’s library. It’s like three lines of code for each one. The one big reason you may not want to do it, though, is that some of this stuff takes a while, especially if your data is any way big. It can take quite a few seconds to encrypt, so you wouldn’t want that happening on your main thread. You’d have all sorts of synchronization and things, but it would be fairly easy to do.
As for the he sequence type thing, you could, but the underlying mechanism expects you to be requesting a specific length, and you normally know a priori what that length is because you’re asking for it for an initialization vector for Salt. You’re not going to really ask for an endless stream of random numbers. However, there would be no reason why you couldn’t build up such a thing quite easily from this.
Receive news and updates from Realm straight to your inbox