Introduction to Upspin on iOS
In February a small team at Google announced Upspin. A framework for naming everyone’s everything. That’s my favorite description of it. From Upspin’s website:
Upspin is an experimental project to build a framework for naming and sharing files and other data securely, uniformly, and globally: a global name system of sorts.
It is not a file system, but a set of protocols and reference implementations that can be used to join things like file systems and other storage services to the name space.
Performance is not a primary goal. Uniformity and security are.
The team’s motivation is personal. They want to help friends and family manage their data.
Documentation exists. There is a fairly thorough dive into it. And a talk given in May describes Upspin and its interfaces. So, I won’t be giving a primer on it here, and will assume some familiarity.
I’ve spoken with a lot of people over the past few months about Upspin. There are a few recurring themes. The one that stands out most for me is that for Upspin to be generally useful it also needs to work on mobile. The reference implementation as it is today works on macOS, Linux, and Windows.
Apple announced enhancements to their File Provider API at WWDC this year. This provides the necessary interfaces to provide cloud backed storage to other apps. iCloud is of course supported, and Apple also revealed Dropbox and Box are adding support. Google Drive supports it as well. If you’re running iOS 11, you can access these through the Files app. This announcement motivated me to start work on an Upspin client for iOS devices.
Disclaimer: There will be a lot of firsts for me here. Swift, Xcode, Apple’s frameworks, and blogging. So expect some iteration as I learn.
There is a lot of material I want to cover. My plan is to break this down into a series of digestible posts. These won’t be step-by-step guides. My hope is to provide color on how Upspin on iOS works, and to provide a guide for building similar apps. My work in progress is at github.com/jngl/JnglMobile.
Upspin and Swift
The Upspin codebase is beautifully constructed Go. If you’re studying Go, this is excellent reading material. This code is a reference implementation, but as far as I know it is the only implementation in existence today. My goal is to make Upspin usable from iOS devices, so the best option is to leverage this work, rather than recreate it in native Objective-C or Swift. Enter gomobile.
Gomobile is an experimental package that can generate bindings for Android and iOS. The heavy lifting is done for us! The output is an Xcode framework which we can add to our project.
There are some limitations. It does not support all the types supported by Go. If you were to try using Gomobile to create bindings for the upspin/client package it would fail. It’s easy enough to wrap the calls and provide appropriate types. There’s an example of that in https://github.com/upspin/exp/tree/master/client/gobind.
The example is missing parts that are important. Asking a user to type in their private key is likely to fail (or worse, cause them to do something risky to paste it in). Being able to regenerate keys from the user’s proquint is important. So we’ll grab some code from upspin/cmd/keygen.go to create a suitable Keygen() function.
For lack of a better name, I’ll call this Go package spinner. You can find it here.
To build the framework we run:
gomobile bind -target=ios -o Spinner.framework github.com/jnglco/JnglMobile/spinner And add the resulting framework into our Xcode project.
To make things easier to work with in Swift and Apple’s frameworks, we’ll wrap this up in a Swift class.
You’ll notice a few things that Gomobile did for us:
- It prepended Spinner to our functions and structs. This is how namespaces work in Objective-C and Swift. (That explains all the class names in Objective-C starting with NS, for NeXTSTEP).
- It created getters and setters for the fields in our Go structs.
- You’ll also notice that when there are multiple return values in Go,
_, err := function()for instance, it’s returned via one of the function parameters (e.g.,
&error) in Swift.
Let’s try using it. This is an example of creating a client, getting a file, and attempting to print it to stdout.
Of course we’ll want to save the user’s configuration. Since the user’s private key is part of the config we’ll want to be careful with how we do that.
If you use a Mac you’re likely familiar with the Keychain. It’s not as visible in iOS, but it’s there. The Keychain has some beautiful features that allow us to store public and private keys. These are then available for use with other cryptographic functions provided by Apple. But that’s more complicated than we need right now.
Why not serialize our instance of the Upspin object into a Keychain value? Apple has given us a nice protocol (interface in Go’s parlance) for this: Codable.
The first thought that ran through my head is JSON. It’s popular, we can all read it. But wait, this is Apple, so let’s put Property Lists to work (stay calm, we won’t be seeing any XML).
We need to make our Upspin class conform to the Codable protocol. This means adding a couple functions, providing coding keys, and telling the compiler we conform to Codable.
Saving and restoring our client is now very easy.
We’ll end this post here. I hope that it’s illuminated the very start of how Upspin on iOS will work. To recap:
1. We went from the Upspin reference implementation in Go to an Xcode framework.
2. Using the framework to create a client.
3. Saving the client by encoding it to and decoding it from the iOS Keychain.
There are many improvements on the above that I’m excited to work on and tell you about in coming posts. We’ll also need to go further into Apple’s iOS frameworks and UI to make this a nice experience for friends and family.
I will leave you with this if you’re curious about iOS’s security posture. Apple publishes a readable and beautiful doc: iOS Security.
If you’re in San Francisco, I’m always happy to meet for a coffee or drink!