Secure Remote Password in Swift

Secure 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.

From: Secure Remote Password protocol

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.

ifaddrs.h in Swift 3

ifaddrs.h contains some useful information if you want to work with sockets. Using getifaddrs will enumerate all interfaces and the IP addresses they are bound to. However Swift does not include this header in either Darwin or Glibc.

Using Swift 3’s SPM, a first attempt would be to define a system module package like this:

module Cifaddrs [system] [extern_c] {
    header "/usr/include/ifaddrs.h"
    export *
}

However, this doesn’t work and gives the following error message in both Xcode 8 and SPM:

/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/include/module.modulemap:7:8: error: redefinition of module 'Compression'
module Compression [system] [extern_c] {
^

A second attempt might be to define a new module that simply imports ifaddrs.h:

Sources/Cifaddrs/include/Cifaddrs.h:
#import <ifaddrs.h>

Sources/Cifaddrs/Cifaddrs.c:
(empty)

While this is OK for SPM, Xcode will give the following error message:

[…]Sources/Cifaddrs/include/Cifaddrs.h:1:9: Include of non-modular header inside framework module 'Cifaddrs'

Note that setting CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES doesn’t affect Swift compiler.

That got me thinking. In the header file I’m apparently not allowed to import non-modular header files. However in the implementation file, I could just forward to the actual implementation.

Sources/Cifaddrs/include/Cifaddrs.h:
/*  $FreeBSD: src/include/ifaddrs.h,v 1.3.32.1.4.1 2010/06/14 02:09:06 kensmith Exp $   */

/*
 + Copyright (c) 1995, 1999
 +  Berkeley Software Design, Inc.  All rights reserved.
 *
 + Redistribution and use in source and binary forms, with or without
 + modification, are permitted provided that the following conditions
 + are met:
 + 1. Redistributions of source code must retain the above copyright
 +    notice, this list of conditions and the following disclaimer.
 *
 + THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
 + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 + ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
 + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 + SUCH DAMAGE.
 *
 +  BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp
 */

#ifndef _IFADDRS_H_
#define _IFADDRS_H_

struct ifaddrs {
    struct ifaddrs  *ifa_next;
    char            *ifa_name;
    unsigned int     ifa_flags;
    struct sockaddr *ifa_addr;
    struct sockaddr *ifa_netmask;
    struct sockaddr *ifa_dstaddr;
    void            *ifa_data;
};

extern int getifaddrs(struct ifaddrs **);
extern void freeifaddrs(struct ifaddrs *);

#endif
Sources/Cifaddrs/Cifaddrs.c:
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>

From Swift, enumerating the interfaces looks like this:

var addrs: UnsafeMutablePointer<ifaddrs>?
getifaddrs(&addrs)

And with Swift 3’s sequence the result can be transformed in a sequence:

for ifaddrs in sequence(first: addrs!, next: { $0.pointee.ifa_next }) {
    //...
}

Except where otherwise noted, this work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.