Upgrade to Pro — share decks privately, control downloads, hide ads and more …

IDZCommonCrypto

Avatar for iOS Dev Zone iOS Dev Zone
November 20, 2015

 IDZCommonCrypto

A presentation given describing a "thin" wrapper around Apple's CommonCrypto library at the Swift Language Users Group 11/19/15

Avatar for iOS Dev Zone

iOS Dev Zone

November 20, 2015

Other Decks in Programming

Transcript

  1. Outline • Intro to CommonCrypto • How to Access CommonCrypto

    in Swift • IDZSwiftCommonCrypto Design Goals • IDZSwiftCommonCrypto API • Some words of caution • Other Libraries/Projects • Summary
  2. What is CommonCrypto? • CommonCrypto is Apple’s Open Source cryptography

    library that is included in iOS & OS X • It is a C library • It is part of System.framework • It is not (directly) accessible from Swift (!)
  3. CommonCrypto Provides • Message Digests • Hash-based Message Authentication Codes

    (HMACs) • Cryptors • Key Derivation • Random Number Generation
  4. Design Goals • Implementation Independent • There is an IDZSwiftOpenSSL

    • Thin — The minimum code to “Swiftify” • Don’t introduce security issues • Easy to use
  5. Message Digests • Given m easy to calculate h •

    Given h difficult to find m • Given m1 difficult to find m2 | hash(m1) == hash(m2) • Difficult to find pair (m1, m2) | hash(m1) == hash(m2) https://en.wikipedia.org/wiki/Cryptographic_hash_function SHA1 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog m 2fd4 e1c6 7a2d 28fc ed84 9ee1 bb76 e739 1b93 eb12 408d 9438 4216 f890 ff7a 0c35 28e8 bed1 e0b0 1621 h
  6. 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
  7. 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 public func CC_MD4_Init(c: UnsafeMutablePointer<CC_MD4_CTX>) -> Int32 public func CC_MD4_Update(c: UnsafeMutablePointer<CC_MD4_CTX>, _ data: UnsafePointer<Void>, _ len: CC_LONG) -> Int32 public func CC_MD4_Final(md: UnsafeMutablePointer<UInt8>, _ c: UnsafeMutablePointer<CC_MD4_CTX>) - > Int32
  8. 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 public func CC_MD4_Init(c: UnsafeMutablePointer<CC_MD4_CTX>) -> Int32 public func CC_MD4_Update(c: UnsafeMutablePointer<CC_MD4_CTX>, _ data: UnsafePointer<Void>, _ len: CC_LONG) -> Int32 public func CC_MD4_Final(md: UnsafeMutablePointer<UInt8>, _ c: UnsafeMutablePointer<CC_MD4_CTX>) - > Int32 public func CC_MD5_Init(c: UnsafeMutablePointer<CC_MD5_CTX>) -> Int32 public func CC_MD5_Update(c: UnsafeMutablePointer<CC_MD5_CTX>, _ data: UnsafePointer<Void>, _ len: CC_LONG) -> Int32 public func CC_MD5_Final(md: UnsafeMutablePointer<UInt8>, _ c: UnsafeMutablePointer<CC_MD5_CTX>) - > Int32
  9. CommonDigest.h • And so on… for each of the 8

    algorithms • We can do better
  10. typealias Initializer = (Context) -> (Int32) typealias Updater = (Context,

    Buffer, CC_LONG) -> (Int32) typealias Finalizer = (Digest, Context) -> (Int32) Digest.swift
  11. 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) Digest.swift
  12. 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] } Digest.swift
  13. The Digest API 1.2 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 }
  14. public protocol Updateable { var status : Status { get

    } func update(buffer : UnsafePointer<Void>, _ byteCount : size_t) -> Self? } Updateable.swift 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 } }
  15. The Digest API 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] } Digest.swift
  16. Using 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()
  17. • Message Digest + Key = HMAC • Let’s you

    verify that the message is what the person in control of the key sent you and has not been tampered with HMAC HMAC The quick brown fox jumps over the lazy dog. 05ca 7ed1 5cdd 4a95 ba67 4845 abe3 570c f61d 3fbb 408d 9438 4216 f890 ff7a 0c35 28e8 bed1 e0b0 1621 The quick brown fox jumps over the lazy dog. KEX 408d 9438 4216 f890 ff7a 0c35 28e8 bed1 e0b0 1621
  18. CommonHMAC.h 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>) /usr/include/CommonCrypto/CommonHMAC.h algorithm: CCHmacAlgorithm
  19. 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] } }} HMAC.swift
  20. The HMAC API 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] } HMAC.swift
  21. Using HMAC 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()
  22. Cryptor The quick brown fox jumps over the lazy dog.

    Decrypt KEX Encrypt 16fa 6587 3100 2ad6 e34a 2fa0 0f29 0d9f 974f 7bac 1045 574b 74c2 049e 65d2 IV KEY KEY The quick brown fox jumps over the lazy dog. IV Sender Receiver Channel
  23. CommonCryptor.h 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 /usr/include/CommonCrypto/CommonCryptor.h _ options: CCOptions public func CCCryptorRelease(cryptorRef: CCCryptorRef) -> CCCryptorStatus
  24. Swift 2.0 OptionSetType 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) } StreanCryptor.swift
  25. The StreamCryptor API 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) } StreamCryptor.swift
  26. The Cryptor API public class Cryptor : StreamCryptor, Updateable {

    internal var accumulator: [UInt8] public func final() -> [UInt8]? public func update(buffer: UnsafePointer<Void>, _ byteCount: Int) -> Self? } Crypto.swift
  27. Using Cryptor 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()
  28. The PBKDF API 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] } KeyDerivation.swift
  29. The Random API public class Random { public class func

    generateBytes(byteCount : Int) throws -> [UInt8] } Random.swift
  30. Some Cautionary Words • IDZSwiftCommonCrypto should be considered beta •

    Don’t try to invent your own protocol; there be dragons! • Don’t write your crypto own libraries public func ==(lhs: [UInt8], rhs: [UInt8]) -> Bool { guard lhs.count == rhs.count else { return false } for i in 0..<lhs.count { if lhs[i] != rhs [i] { return false } } return true } • Do check local jurisdiction for laws governing use/sale/export of products containing/using cryptography
  31. Other Projects • 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
  32. Summary • 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
  33. Digest HMAC Cryptor StreamCryptor MD2, MD4, MD5, SHA1, SHA224, SHA256,

    SHA384, SHA512 MD5, SHA1, SHA224, SHA256, SHA384, SHA512 AES, DES, TripleDES, CAST, RC2, Blowfish Operations: Encrypt, Decrypt Options: None, PKCS7Padding, ECBMode init(algorithm: Algorithm) init(algorithm: Algorithm, key: NSData) init(algorithm: Algorithm, key: [UInt8]) init(algorithm: Algorithm, key: String) init(operation: Operation, algorithm: Algorithm, options: Options, key: NSData, iv: NSData) init(operation: Operation, algorithm: Algorithm, options: Options, key: [UInt8], iv: [UInt8]) init(operation: Operation, algorithm: Algorithm, options: Options, key: String, iv: String) update(data: NSData) -> Self? update(byteArray : [UInt8]) -> Self? update(string: String) -> Self? update(dataIn: NSData, inout byteArrayOut: [UInt8]) -> (Int, Status) update(byteArrayIn: [UInt8], inout byteArrayOut: [UInt8]) -> (Int, Status) update(stringIn: String, inout byteArrayOut: [UInt8]) -> (Int, Status) final() -> [UInt8] final(inout byteArrayOut: [UInt8]) -> (Int, Status) PBKDF calibrate(passwordLength: Int, saltLength: Int, algorithm: PseudoRandomAlgorithm, derivedKeyLength: Int, msec : UInt32) -> UInt deriveKey(password : String, salt : String, prf: PseudoRandomAlgorithm, rounds: uint, derivedKeyLength: UInt) -> [UInt8] Random generateBytes(byteCount : Int ) throws -> [UInt8]