Secure Remote Password in Swift
06 Mar 2017Secure Remote Password is a neat authentication protocol to prove your identity to another party, using a password, but without ever revealing that password to other parties. Not even the party you are proving your identity. A short summary from Wikipedia:
The SRP protocol has a number of desirable properties: it allows a user to authenticate themselves to a server, it is resistant to dictionary attacks mounted by an eavesdropper, and it does not require a trusted third party. It effectively conveys a zero-knowledge password proof from the user to the server. In revision 6 of the protocol only one password can be guessed per connection attempt. One of the interesting properties of the protocol is that even if one or two of the cryptographic primitives it uses are attacked, it is still secure. The SRP protocol has been revised several times, and is currently at revision 6a.
The SRP protocol creates a large private key shared between the two parties in a manner similar to Diffie–Hellman key exchange based on the client side having the user password and the server side having a cryptographic verifier derived from the password. The shared public key is derived from two random numbers, one generated by the client, and the other generated by the server, which are unique to the login attempt. In cases where encrypted communications as well as authentication are required, the SRP protocol is more secure than the alternative SSH protocol and faster than using Diffie–Hellman key exchange with signed messages. It is also independent of third parties, unlike Kerberos. The SRP protocol, version 3 is described in RFC 2945. SRP version 6 is also used for strong password authentication in SSL/TLS (in TLS-SRP) and other standards such as EAP and SAML, and is being standardized in IEEE P1363 and ISO/IEC 11770-4.
I’ve written an implementation of this protocol in Swift. I wrote it because I needed it to get my Homekit Accessory Protocol implementation working. Now that it is nearing completion I found some time to polish the API and make it publicly available as a library.
API Usage
The API was designed with great usability in mind.
// 0. Initial setup; Alice creates a salted password verifier she will
// share these values with the server.
let (salt, verificationKey) = createSaltedVerificationKey(username: "alice", password: "password123")
// 1. Now Alice wants to authenticate with the server, she sends her
// username to the server.
let client = Client(username: "alice", password: "password123")
let (username, clientPublicKey) = client.startAuthentication()
let server = Server(username: username, salt: salt, verificationKey: verificationKey)
// 2. The server shares Alice's salt and its public key (the challenge).
let (salt, serverPublicKey) = server.getChallenge()
// 3. Alice generates a sessionKey and proofs she generated the correct
// session key based on her password and the challenge.
let clientKeyProof = try client.processChallenge(salt: salt, publicKey: serverPublicKey)
// 4. The server verifies Alices' proof and generates their proof.
let serverKeyProof = try server.verifySession(publicKey: clientPublicKey, keyProof: clientKeyProof)
// 5. The client verifies the server's proof.
try client.verifySession(keyProof: serverKeyProof)
// At this point, authentication has completed.
assert(server.isAuthenticated)
assert(client.isAuthenticated)
// Both now have the same session key. This key can be used to encrypt
// further communication between client and server.
assert(server.sessionKey == client.sessionKey)
Bignum library
As with anything related to encryption, you’ll work with large numbers. As of this moment, the Swift standard library doesn’t include a “bignum” library. Which is a pity, so I’d have to rely on some other library.
Initial attempt: OpenSSL
Intially I went for a wrapper on top of OpenSSL. OpenSSL was already a dependency somewhere else in the codebase, so it made sense to use this implementation. In order to call a C library from Swift, a system module needs to be defined. There were already some libraries available, but none of them included "openssl/bn.h"
, so I defined my own: COpenSSL.
On top of this system module, I wrote a small library called Bignum that uses the bignum methods exposed by OpenSSL. As Swift allows operator overloading, the API is rather simple to use:
let result = Bignum("2") + Bignum("3") * Bignum("4") / Bignum("2")
assert(result == Bignum("8"))
Second attempt: BigInt
While my Bignum worked great, it made my SRP implementation on OpenSSL. In order to drop OpenSSL as a dependency I needed a different bignum implementation. Since my initial attempt a pure Swift implementation was written by Károly Lőrentey: BigInt. It has a great API, has all the functions I need and comes with documentation and tests.
Most of the calculations done in SRP were easily replaced; however I found the debug builds to be quite a bit slower as SwiftPM builds all targets, including BigInt, in debug mode. In release mode the library’s much faster, but still a multifold slower than OpenSSL’s implementation. I think the performance is sufficient for my use-case, but might need to be revisited if better performance is required.
For more information on the SRP library, see the project page on Github, or the documentation.