Skip to content

Latest commit

 

History

History
121 lines (94 loc) · 5.69 KB

File metadata and controls

121 lines (94 loc) · 5.69 KB

title: CommonCrypto and the Swift Bridging Header

pub_date: 2016-08-21

author: Kyle Crawshaw

twitter_handle: kylecrawshaw

body:

The Backstory

Recently I started to learn a little bit of Swift in an attempt to build ImagrAdmin, but I ran into a road block pretty quickly. How do I create a sha512 encoded string? Imagr uses a sha512 encoded string for the password component at the root level of imagr_config.plist. You can check out the Imagr wiki on GitHub for more details about this component.

A lot of organizations use this feature of Imagr since they likely have multiple people using their imaging tool or even to protect their end users from accidentally wiping their computers. As I mentioned before, I had just started to learn Swift so I could build this, but I had literally just started to look at it the week before. Trying to learn how Swift does cryptography was not something I had any idea about how to approach or where to begin. With this in mind I opted to replicate what Imagr instructs you to do with Python in the wiki, which meant using NSTask to run a single line of python and then parse the results.

Creating the Bridging Header

Ok, now to the fun stuff. So I had seen previously that I might be able to use some the functionality from the CommonCrypto framework, but Apple has not made that available via Swift. This is where the bridging header can be pretty helpful since it will allow a Swift developer to call specific functions and constants from C/Objective-C only frameworks. The first thing that I needed to do is create a bridging header file. I used the name ImagrAdmin-Bridging-Header.h so you may want to use something in the form of <ProjectName>-Bridging-Header.h if you were to create on in your project.

1. Create a New File

You can name the file pretty much anything you want, but I went with ImagrAdmin-Bridging-Header.h since it makes sense to use names that are clear.

New file dialog screenshot

2. Add Imports to the Bridging Header

When you create that file it'll have the contents below and you can import any other header files here. All I added for my app was #import <CommonCrypto/CommonCrypto.h>. There are likely some limitations to the bridging header, but it has proved helpful for this task at least.

ImagrAdmin-Bridging-Header.h

#ifndef ImagrAdmin_Bridging_Header_h
#define ImagrAdmin_Bridging_Header_h

#import <CommonCrypto/CommonCrypto.h>

#endif /* ImagrAdmin_Bridging_Header_h */
3. Update the Application's Build Settings

This is the most important step and really the part that actually makes this work. Your application needs to know that the header file that you've created has a specific purpose, otherwise it just looks like a normal header file.

Navigate to the Build Settings and then find the section called Swift Compiler - Code Generation. Here you can add the path to your bridging header and then when the application is compiled it will include frameworks listed in that file. Pretty neat!

The Not So Amazing Way

This seemed silly, but I figured it worked and all of the examples for accomplishing the same thing in Swift were either unnecessarily complex or just made no sense to me. Luckily, I have one of the best resources I could ask for at my disposal, the #macadmins Slack team and @groob kindly pointed me toward a post on Stack Overflow. I had been hoping that noone would notice my "temporary" workaround, but he noticed this immediately within hours of me posting my code. This was awesome and was yet another reason why open source software and the community around it can be such an amazing thing. My code was visible for others to critique and instead of scoffing at me for taking a shortcut he pointed me in the right direction.

import Foundation

func hashPassword(password: String) -> String {
   let task = NSTask()
   task.launchPath = "/usr/bin/python"
   task.arguments = ["-c", "import hashlib; print hashlib.sha512(\"\(password)\").hexdigest()"]

   let stdOutput = NSPipe()
   let outputHandler = stdOutput.fileHandleForReading
   task.standardOutput = stdOutput

   task.launch()

   // Process the task stdout
   let outputString = outputHandler.availableData
   let stringRead = NSString(data: outputString, encoding: NSUTF8StringEncoding) as! String
   let output = stringRead.stringByReplacingOccurrencesOfString(
       "\n", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)

   return output
}
Creating an Extension for Swift String Objects
extension String {
    func sha512() -> String {
        let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
        var digest = [UInt8](count:Int(CC_SHA512_DIGEST_LENGTH), repeatedValue: 0)
        CC_SHA512(data.bytes, CC_LONG(data.length), &digest)
        let hexBytes = digest.map { String(format: "%02hhx", $0) }
        return hexBytes.joinWithSeparator("")
    }
}

The approach above is far simpler and it uses an extension to the String class. The usage between the two approaches does change all that much. Instead of using let hashedPass = hashPassword("supersecretpassword") I would use let hashedPass = "supersecretpassword".sha512(). This just feels like a nicer interface.

If there are errors or things you think should be changed about this post, you can open up a PR on Github by clicking the in the navigation bar.