Bläddra i källkod

Serious bug fixes in CryptoAuth (and probably some new bugs added)

Caleb James DeLisle 8 år sedan
förälder
incheckning
ced280bc17

+ 1 - 7
admin/AuthorizedPasswords.c

@@ -38,10 +38,9 @@ static void add(Dict* args, void* vcontext, String* txid, struct Allocator* allo
 
     String* passwd = Dict_getString(args, String_CONST("password"));
     String* user = Dict_getString(args, String_CONST("user"));
-    String* displayName = Dict_getString(args, String_CONST("displayName"));
     String* ipv6 = Dict_getString(args, String_CONST("ipv6"));
 
-    int32_t ret = CryptoAuth_addUser_ipv6(passwd, user, displayName, ipv6, context->ca);
+    int32_t ret = CryptoAuth_addUser_ipv6(passwd, user, ipv6, context->ca);
 
     switch (ret) {
         case 0:
@@ -51,10 +50,6 @@ static void add(Dict* args, void* vcontext, String* txid, struct Allocator* allo
             sendResponse(String_CONST("Specified auth type is not supported."),
                          context->admin, txid, alloc);
             break;
-        case CryptoAuth_addUser_OUT_OF_SPACE:
-            sendResponse(String_CONST("Out of memory to store password."),
-                         context->admin, txid, alloc);
-            break;
         case CryptoAuth_addUser_DUPLICATE:
             sendResponse(String_CONST("Password already added."), context->admin, txid, alloc);
             break;
@@ -110,7 +105,6 @@ void AuthorizedPasswords_init(struct Admin* admin,
     Admin_registerFunction("AuthorizedPasswords_add", add, context, true,
         ((struct Admin_FunctionArg[]){
             { .name = "password", .required = 1, .type = "String" },
-            { .name = "displayName", .required = 0, .type = "String" },
             { .name = "ipv6", .required = 0, .type = "String" },
             { .name = "user", .required = 0, .type = "String" }
         }), admin);

+ 134 - 149
crypto/CryptoAuth.c

@@ -111,47 +111,25 @@ static inline void getSharedSecret(uint8_t outputSecret[32],
     }
 }
 
-static inline void hashPassword_sha256(struct CryptoAuth_Auth* auth, const String* password)
+static inline void hashPassword(uint8_t secretOut[32],
+                                union CryptoHeader_Challenge* challengeOut,
+                                const String* login,
+                                const String* password,
+                                const uint8_t authType)
 {
+    crypto_hash_sha256(secretOut, (uint8_t*) password->bytes, password->len);
     uint8_t tempBuff[32];
-    crypto_hash_sha256(auth->secret, (uint8_t*) password->bytes, password->len);
-    crypto_hash_sha256(tempBuff, auth->secret, 32);
-    Bits_memcpyConst(auth->challenge.bytes, tempBuff, CryptoHeader_Challenge_SIZE);
-    CryptoHeader_setAuthChallengeDerivations(&auth->challenge, 0);
-    auth->challenge.challenge.type = 1;
-}
-
-static inline void hashPassword_withLogin(struct CryptoAuth_Auth* auth,
-                                          const String* login,
-                                          const String* password)
-{
-    crypto_hash_sha256(auth->secret, (uint8_t*) password->bytes, password->len);
-    uint8_t tempBuff[32];
-    crypto_hash_sha256(tempBuff, (uint8_t*) login->bytes, login->len);
-    Bits_memcpyConst(auth->challenge.bytes, tempBuff, CryptoHeader_Challenge_SIZE);
-    CryptoHeader_setAuthChallengeDerivations(&auth->challenge, 0);
-    auth->challenge.challenge.type = 2;
-}
-
-static inline uint8_t* hashPassword(struct CryptoAuth_Auth* auth,
-                                    const String* login,
-                                    const String* password,
-                                    const uint8_t authType)
-{
-    switch (authType) {
-        case 1: {
-            hashPassword_sha256(auth, password);
-            break;
-        }
-        case 2: {
-            Assert_true(login);
-            hashPassword_withLogin(auth, login, password);
-            break;
-        }
-        default:
-            Assert_failure("Unsupported auth type.");
-    };
-    return auth->secret;
+    if (authType == 1) {
+        crypto_hash_sha256(tempBuff, secretOut, 32);
+    } else if (authType == 2) {
+        crypto_hash_sha256(tempBuff, (uint8_t*) login->bytes, login->len);
+    } else {
+        Assert_failure("Unsupported auth type [%u]", authType);
+    }
+    Bits_memcpyConst(challengeOut->bytes, tempBuff, CryptoHeader_Challenge_SIZE);
+    CryptoHeader_setAuthChallengeDerivations(challengeOut, 0);
+    challengeOut->challenge.type = authType;
+    challengeOut->challenge.additional = 0;
 }
 
 /**
@@ -161,22 +139,29 @@ static inline uint8_t* hashPassword(struct CryptoAuth_Auth* auth,
  * @param context the CryptoAuth engine to search in.
  * @return an Auth struct with a if one is found, otherwise NULL.
  */
-static inline struct CryptoAuth_Auth* getAuth(union CryptoHeader_Challenge auth,
-                                              struct CryptoAuth_pvt* context)
+static inline struct CryptoAuth_User* getAuth(union CryptoHeader_Challenge* auth,
+                                              struct CryptoAuth_pvt* ca)
 {
-    if (auth.challenge.type != 1) {
+    if (auth->challenge.type == 0) {
         return NULL;
     }
-    for (uint32_t i = 0; i < context->passwordCount; i++) {
-        if (Bits_memcmp(auth.bytes, &context->passwords[i], CryptoHeader_Challenge_KEYSIZE) == 0) {
-            return &context->passwords[i];
+    int count = 0;
+    for (struct CryptoAuth_User* u = ca->users; u; u = u->next) {
+        count++;
+        if (auth->challenge.type == 1 &&
+            !Bits_memcmp(auth->bytes, u->passwordHash, CryptoHeader_Challenge_KEYSIZE))
+        {
+            return u;
+        } else if (auth->challenge.type == 2 &&
+            !Bits_memcmp(auth->bytes, u->userNameHash, CryptoHeader_Challenge_KEYSIZE))
+        {
+            return u;
         }
     }
-    Log_debug(context->logger, "Got unrecognized auth, password count = [%d]",
-              context->passwordCount);
+    Log_debug(ca->logger, "Got unrecognized auth, password count = [%d]", count);
     return NULL;
 }
-
+/*
 static inline uint8_t* tryAuth(union CryptoHeader* cauth,
                                uint8_t hashOutput[32],
                                struct CryptoAuth_Session_pvt* session,
@@ -191,7 +176,7 @@ static inline uint8_t* tryAuth(union CryptoHeader* cauth,
 
     return NULL;
 }
-
+*/
 /**
  * Decrypt and authenticate.
  *
@@ -383,15 +368,18 @@ static void encryptHandshake(struct Message* message,
 
     // Password auth
     uint8_t* passwordHash = NULL;
-    struct CryptoAuth_Auth auth;
+    uint8_t passwordHashStore[32];
     if (session->password != NULL) {
-        passwordHash = hashPassword(&auth, session->login, session->password, session->authType);
-        Bits_memcpyConst(header->handshake.auth.bytes,
-                         &auth.challenge,
-                         sizeof(union CryptoHeader_Challenge));
+        hashPassword(passwordHashStore,
+                     &header->handshake.auth,
+                     session->login,
+                     session->password,
+                     session->authType);
+        passwordHash = passwordHashStore;
+    } else {
+        header->handshake.auth.challenge.type = session->authType;
+        header->handshake.auth.challenge.additional = 0;
     }
-    header->handshake.auth.challenge.type = session->authType;
-    header->handshake.auth.challenge.additional = 0;
 
     // Set the session state
     header->nonce = Endian_hostToBigEndian32(session->nextNonce);
@@ -599,26 +587,24 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
         return -1;
     }
 
-    String* user = NULL;
-    struct CryptoAuth_Auth* auth = NULL;
-    uint8_t passwordHashStore[32];
-    uint8_t* passwordHash = tryAuth(header, passwordHashStore, session, &auth);
+    struct CryptoAuth_User* userObj = getAuth(&header->handshake.auth, session->context);
     uint8_t* restrictedToip6 = NULL;
-    if (auth) {
-        user = auth->user;
-        if (auth->restrictedToip6) {
-            restrictedToip6 = auth->restrictedToip6;
-            if (!ip6MatchesKey(auth->restrictedToip6, header->handshake.publicKey)) {
+    uint8_t* passwordHash = NULL;
+    if (userObj) {
+        passwordHash = userObj->secret;
+        if (userObj->restrictedToip6[0]) {
+            restrictedToip6 = userObj->restrictedToip6;
+            if (!ip6MatchesKey(restrictedToip6, header->handshake.publicKey)) {
                 cryptoAuthDebug0(session, "DROP packet with key not matching restrictedToip6");
                 return -1;
             }
         }
     }
-    if (session->requireAuth && !user) {
+    if (session->requireAuth && !userObj) {
         cryptoAuthDebug0(session, "DROP message because auth was not given");
         return -1;
     }
-    if (passwordHash == NULL && header->handshake.auth.challenge.type != 0) {
+    if (!userObj && header->handshake.auth.challenge.type != 0) {
         cryptoAuthDebug0(session, "DROP message with unrecognized authenticator");
         return -1;
     }
@@ -632,7 +618,7 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
     if (nonce < 2) {
         if (nonce == 0) {
             cryptoAuthDebug(session, "Received a hello packet, using auth: %d",
-                            (passwordHash != NULL));
+                            (userObj != NULL));
         } else {
             cryptoAuthDebug0(session, "Received a repeat hello packet");
         }
@@ -641,7 +627,8 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
         if (!knowHerKey(session) || session->nextNonce == 0) {
             herPermKey = header->handshake.publicKey;
             if (Defined(Log_DEBUG) && Bits_isZero(header->handshake.publicKey, 32)) {
-                cryptoAuthDebug0(session, "Node sent public key of ZERO!");
+                cryptoAuthDebug0(session, "DROP Node sent public key of ZERO!");
+                return -1;
             }
         } else {
             herPermKey = session->pub.herPublicKey;
@@ -663,7 +650,8 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
         } else if (nonce == 3) {
             cryptoAuthDebug0(session, "Received a repeat key packet");
         } else {
-            cryptoAuthDebug(session, "Received a packet of unknown type! nonce=%u", nonce);
+            cryptoAuthDebug(session, "DROP Received a packet of unknown type! nonce=%u", nonce);
+            return -1;
         }
         if (Bits_memcmp(header->handshake.publicKey, session->pub.herPublicKey, 32)) {
             cryptoAuthDebug0(session, "DROP packet contains different perminent key");
@@ -710,6 +698,7 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
 
     if (Bits_isZero(header->handshake.encryptedTempKey, 32)) {
         // we need to reject 0 public keys outright because they will be confused with "unknown"
+        cryptoAuthDebug0(session, "DROP message with zero as temp public key");
         return -1;
     }
 
@@ -735,6 +724,7 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
     } else if (nonce == 2 && session->nextNonce >= 4) {
         // we accept a new key packet and let it change the session since the other end might have
         // killed off the session while it was in the midst of setting up.
+        // This is NOT a repeat key packet because it's nonce is 2, not 3
         if (!Bits_memcmp(session->herTempPubKey, header->handshake.encryptedTempKey, 32)) {
             Assert_true(!Bits_isZero(session->herTempPubKey, 32));
             cryptoAuthDebug0(session, "DROP dupe key packet with same temp key");
@@ -760,12 +750,6 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
     if (nextNonce == 4) {
         if (session->nextNonce <= 4) {
             // and have not yet begun sending "run" data
-            Assert_true(session->nextNonce <= nextNonce);
-            session->nextNonce = nextNonce;
-
-            if (!session->pub.displayName) {
-                session->pub.displayName = user;
-            }
             Bits_memcpyConst(session->herTempPubKey, header->handshake.encryptedTempKey, 32);
         } else {
             // It's a (possibly repeat) key packet and we have begun sending run data.
@@ -776,6 +760,7 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
                             header->handshake.encryptedTempKey,
                             NULL,
                             session->context->logger);
+            nextNonce = session->nextNonce + 1;
             cryptoAuthDebug0(session, "New key packet but we are already sending data");
         }
 
@@ -802,14 +787,6 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
             nextNonce = 3;
         }
 
-        Assert_true(session->nextNonce <= nextNonce);
-        session->nextNonce = nextNonce;
-        if (!session->pub.displayName) {
-            session->pub.displayName = user;
-        }
-        if (restrictedToip6) {
-            Bits_memcpyConst(session->pub.herIp6, restrictedToip6, 16);
-        }
         Bits_memcpyConst(session->herTempPubKey, header->handshake.encryptedTempKey, 32);
 
     } else if (Bits_memcmp(header->handshake.publicKey, session->context->pub.publicKey, 32) < 0) {
@@ -819,23 +796,28 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
         cryptoAuthDebug0(session, "Incoming hello from node with lower key, resetting");
         reset(session);
 
-        Assert_true(session->nextNonce <= nextNonce);
-        session->nextNonce = nextNonce;
-        if (!session->pub.displayName) {
-            session->pub.displayName = user;
-        }
-        if (restrictedToip6) {
-            Bits_memcpyConst(session->pub.herIp6, restrictedToip6, 16);
-        }
         Bits_memcpyConst(session->herTempPubKey, header->handshake.encryptedTempKey, 32);
 
     } else {
-        cryptoAuthDebug0(session, "Incoming hello from node with higher key, not resetting");
+        cryptoAuthDebug0(session, "DROP Incoming hello from node with higher key, not resetting");
+        return -1;
     }
 
     if (herPermKey && herPermKey != session->pub.herPublicKey) {
         Bits_memcpyConst(session->pub.herPublicKey, herPermKey, 32);
     }
+    if (!session->pub.displayName && userObj) {
+        session->pub.displayName = userObj->login;
+    }
+    if (restrictedToip6) {
+        Bits_memcpyConst(session->pub.herIp6, restrictedToip6, 16);
+    }
+
+    // Nonces can never go backward and can only "not advance" if they're 0,1,2,3 session state.
+    Assert_true(session->nextNonce < nextNonce ||
+        (session->nextNonce <= 4 && nextNonce == session->nextNonce)
+    );
+    session->nextNonce = nextNonce;
 
     Bits_memset(&session->pub.replayProtector, 0, sizeof(struct ReplayProtector));
 
@@ -936,9 +918,6 @@ struct CryptoAuth* CryptoAuth_new(struct Allocator* allocator,
     struct CryptoAuth_pvt* ca = Allocator_calloc(allocator, sizeof(struct CryptoAuth_pvt), 1);
     Identity_set(ca);
     ca->allocator = allocator;
-    ca->passwords = Allocator_calloc(allocator, sizeof(struct CryptoAuth_Auth), 232);
-    ca->passwordCount = 0;
-    ca->passwordCapacity = 232;
     ca->eventBase = eventBase;
     ca->logger = logger;
     ca->pub.resetAfterInactivitySeconds = CryptoAuth_DEFAULT_RESET_AFTER_INACTIVITY_SECONDS;
@@ -965,87 +944,93 @@ struct CryptoAuth* CryptoAuth_new(struct Allocator* allocator,
     return &ca->pub;
 }
 
-int32_t CryptoAuth_addUser(String* password,
-                           String* login,
-                           String* displayName,
-                           struct CryptoAuth* ca)
+int CryptoAuth_addUser_ipv6(String* password,
+                            String* login,
+                            String* ipv6,
+                            struct CryptoAuth* cryptoAuth)
 {
-     return CryptoAuth_addUser_ipv6(password, login, displayName, NULL, ca);
-}
+    struct CryptoAuth_pvt* ca = Identity_check((struct CryptoAuth_pvt*) cryptoAuth);
 
-int32_t CryptoAuth_addUser_ipv6(String* password,
-                                String* login,
-                                String* displayName,
-                                String* ipv6,
-                                struct CryptoAuth* ca)
-{
-    struct CryptoAuth_pvt* context = Identity_check((struct CryptoAuth_pvt*) ca);
+    struct Allocator* alloc = Allocator_child(ca->allocator);
+    struct CryptoAuth_User* user = Allocator_calloc(alloc, sizeof(struct CryptoAuth_User), 1);
+    user->alloc = alloc;
+    Identity_set(user);
 
-    struct CryptoAuth_Auth a;
-    if (login) {
-        hashPassword_withLogin(&a, login, password);
+    if (!login) {
+        int i = 0;
+        for (struct CryptoAuth_User* u = ca->users; u; u = u->next) { i++; }
+        user->login = login = String_printf(alloc, "Anon #%d", i);
     } else {
-        hashPassword_sha256(&a, password);
+        user->login = String_clone(login, alloc);
     }
-    for (uint32_t i = 0; i < context->passwordCount; i++) {
-        if (!Bits_memcmp(a.secret, context->passwords[i].secret, 32) ||
-            String_equals(displayName, context->passwords[i].user))
-        {
+
+    union CryptoHeader_Challenge ac;
+    // Users specified with a login field might want to use authType 1 still.
+    hashPassword(user->secret, &ac, login, password, 2);
+    Bits_memcpyConst(user->userNameHash, ac.bytes, 8);
+    hashPassword(user->secret, &ac, NULL, password, 1);
+    Bits_memcpyConst(user->passwordHash, ac.bytes, 8);
+
+    for (struct CryptoAuth_User* u = ca->users; u; u = u->next) {
+        if (Bits_memcmp(user->secret, u->secret, 32)) {
+        } else if (!login) {
+        } else if (String_equals(login, u->login)) {
+            Allocator_free(alloc);
             return CryptoAuth_addUser_DUPLICATE;
         }
     }
-    a.user = String_clone(displayName, context->allocator);
+
     if (ipv6) {
-        // TODO(cjd): This could become a memory leak
-        a.restrictedToip6 = Allocator_malloc(context->allocator, 16);
-        if (AddrTools_parseIp(a.restrictedToip6, ipv6->bytes) < 0) {
-            Log_debug(context->logger, "Ipv6 parsing error!");
+        if (AddrTools_parseIp(user->restrictedToip6, ipv6->bytes) < 0) {
+            Log_debug(ca->logger, "Ipv6 parsing error!");
+            Allocator_free(alloc);
             return CryptoAuth_addUser_INVALID_IP;
         }
-    } else {
-        a.restrictedToip6 = NULL;
     }
-    Bits_memcpyConst(&context->passwords[context->passwordCount],
-                     &a,
-                     sizeof(struct CryptoAuth_Auth));
-    context->passwordCount++;
+
+    // Add the user to the *end* of the list
+    for (struct CryptoAuth_User** up = &ca->users; ; up = &(*up)->next) {
+        if (!*up) {
+            *up = user;
+            break;
+        }
+    }
+
     return 0;
 }
 
-int CryptoAuth_removeUsers(struct CryptoAuth* context, String* user)
+int CryptoAuth_removeUsers(struct CryptoAuth* context, String* login)
 {
-    struct CryptoAuth_pvt* ctx = Identity_check((struct CryptoAuth_pvt*) context);
-    if (!user) {
-        int count = ctx->passwordCount;
-        Log_debug(ctx->logger, "Flushing [%d] users", count);
-        ctx->passwordCount = 0;
-        return count;
-    }
+    struct CryptoAuth_pvt* ca = Identity_check((struct CryptoAuth_pvt*) context);
+
     int count = 0;
-    int i = 0;
-    while (i < (int)ctx->passwordCount) {
-        if (String_equals(ctx->passwords[i].user, user)) {
-            Bits_memcpyConst(&ctx->passwords[i],
-                             &ctx->passwords[ctx->passwordCount--],
-                             sizeof(struct CryptoAuth_Auth));
+    struct CryptoAuth_User** up = &ca->users;
+    struct CryptoAuth_User* u = *up;
+    while ((u = *up)) {
+        if (!login || String_equals(login, u->login)) {
+            *up = u->next;
+            Allocator_free(u->alloc);
             count++;
         } else {
-            i++;
+            up = &u->next;
         }
     }
-    Log_debug(ctx->logger, "Removing [%d] user(s) identified by [%s]", count, user->bytes);
+
+    if (!login) {
+        Log_debug(ca->logger, "Flushing [%d] users", count);
+    } else {
+        Log_debug(ca->logger, "Removing [%d] user(s) identified by [%s]", count, login->bytes);
+    }
     return count;
 }
 
 List* CryptoAuth_getUsers(struct CryptoAuth* context, struct Allocator* alloc)
 {
-    struct CryptoAuth_pvt* ctx = Identity_check((struct CryptoAuth_pvt*) context);
-    uint32_t count = ctx->passwordCount;
+    struct CryptoAuth_pvt* ca = Identity_check((struct CryptoAuth_pvt*) context);
 
     List* users = List_new(alloc);
-
-    for (uint32_t i = 0; i < count; i++) {
-        List_addString(users, String_clone(ctx->passwords[i].user, alloc), alloc);
+    for (struct CryptoAuth_User* u = ca->users; u; u = u->next) {
+        List_addString(users, String_clone(u->login, alloc), alloc);
     }
 
     return users;

+ 9 - 12
crypto/CryptoAuth.h

@@ -74,24 +74,21 @@ struct CryptoAuth_Session
  * @param context The CryptoAuth context.
  * @return 0 if all goes well,
  *         CryptoAuth_addUser_INVALID_AUTHTYPE if the authentication method is not supported,
- *         CryptoAuth_addUser_OUT_OF_SPACE if there is not enough space to store the entry,
  *         CryptoAuth_addUser_DUPLICATE if the same *password* already exists.
  *         CryptoAuth_addUser_INVALID_IP if the ipv6 is not valid.
  */
 #define CryptoAuth_addUser_INVALID_AUTHTYPE  -1
-#define CryptoAuth_addUser_OUT_OF_SPACE      -2
 #define CryptoAuth_addUser_DUPLICATE         -3
 #define CryptoAuth_addUser_INVALID_IP        -4
-int32_t CryptoAuth_addUser_ipv6(String* password,
-                                String* login,
-                                String* displayName,
-                                String* ipv6,
-                                struct CryptoAuth* ca);
-
-int32_t CryptoAuth_addUser(String* password,
-                           String* login,
-                           String* displayName,
-                           struct CryptoAuth* ca);
+int CryptoAuth_addUser_ipv6(String* password,
+                            String* login,
+                            String* ipv6,
+                            struct CryptoAuth* ca);
+
+static inline int CryptoAuth_addUser(String* password, String* login, struct CryptoAuth* ca)
+{
+    return CryptoAuth_addUser_ipv6(password, login, NULL, ca);
+}
 
 /**
  * Remove all users registered with this CryptoAuth.

+ 17 - 7
crypto/CryptoAuth_pvt.h

@@ -26,13 +26,25 @@
 
 #include <stdint.h>
 
-struct CryptoAuth_Auth {
-    union CryptoHeader_Challenge challenge;
+struct CryptoAuth_User;
+struct CryptoAuth_User {
+    /** Double-hash of password for authType 1 */
+    uint8_t passwordHash[8];
+
+    /** Hash of username for authType 2 */
+    uint8_t userNameHash[8];
 
     uint8_t secret[32];
 
-    String* user;
-    uint8_t* restrictedToip6;
+    String* login;
+
+    uint8_t restrictedToip6[16];
+
+    struct CryptoAuth_User* next;
+
+    struct Allocator* alloc;
+
+    Identity
 };
 
 struct CryptoAuth_pvt
@@ -41,9 +53,7 @@ struct CryptoAuth_pvt
 
     uint8_t privateKey[32];
 
-    struct CryptoAuth_Auth* passwords;
-    uint32_t passwordCount;
-    uint32_t passwordCapacity;
+    struct CryptoAuth_User* users;
 
     struct Log* logger;
     struct EventBase* eventBase;

+ 20 - 14
crypto/test/CryptoAuth_test.c

@@ -24,13 +24,15 @@
 #include "util/log/FileWriterLog.h"
 #include "wire/CryptoHeader.h"
 
-#define PRIVATEKEY ( \
-    "\x20\xca\x45\xd9\x5b\xbf\xca\xe7\x35\x3c\xd2\xdf\xfa\x12\x84\x4b" \
-    "\x4e\xff\xbe\x7d\x39\xd8\x4d\x8e\x14\x2b\x9d\x21\x89\x5b\x38\x09" )
+#define PRIVATEKEY_A \
+    Constant_stringForHex("53ff22b2eb94ce8c5f1852c0f557eb901f067e5273d541e0a21e143c20dff9da")
+#define PUBLICKEY_A \
+    Constant_stringForHex("e3ff75af6e4414494df22f200ffeaa56e7976d991d33cc87f52427e27f83235d")
 
-#define PUBLICKEY ( \
-    "\x51\xaf\x8d\xd9\x35\xe8\x61\x86\x3e\x94\x2b\x1b\x6d\x21\x22\xe0" \
-    "\x2f\xb2\xd0\x88\x20\xbb\xf3\xf0\x6f\xcd\xe5\x85\x30\xe0\x08\x34" )
+#define PRIVATEKEY_B \
+    Constant_stringForHex("b71c4f43e3d4b1879b5065d44a1cb43eaf07ddba96de6a72ca761c4ef4bd2988")
+#define PUBLICKEY_B \
+    Constant_stringForHex("27c303cdc1f96e4b28d51c75130aff6cad52098f2d752615b7b6509ed6a89477")
 
 #define USEROBJ "This represents a user"
 
@@ -48,7 +50,11 @@ struct Context
     struct EventBase* base;
 };
 
-static struct Context* init(uint8_t* privateKey, uint8_t* herPublicKey, uint8_t* password)
+static struct Context* init(uint8_t* privateKeyA,
+                            uint8_t* publicKeyA,
+                            uint8_t* password,
+                            uint8_t* privateKeyB,
+                            uint8_t* publicKeyB)
 {
     struct Allocator* alloc = MallocAllocator_new(1048576);
     struct Context* ctx = Allocator_calloc(alloc, sizeof(struct Context), 1);
@@ -57,14 +63,14 @@ static struct Context* init(uint8_t* privateKey, uint8_t* herPublicKey, uint8_t*
     struct Random* rand = ctx->rand = Random_new(alloc, logger, NULL);
     struct EventBase* base = ctx->base = EventBase_new(alloc);
 
-    ctx->ca1 = CryptoAuth_new(alloc, NULL, base, logger, rand);
-    ctx->sess1 = CryptoAuth_newSession(ctx->ca1, alloc, herPublicKey, NULL, false, "cif1");
+    ctx->ca1 = CryptoAuth_new(alloc, privateKeyA, base, logger, rand);
+    ctx->sess1 = CryptoAuth_newSession(ctx->ca1, alloc, publicKeyB, NULL, false, "cif1");
 
-    ctx->ca2 = CryptoAuth_new(alloc, privateKey, base, logger, rand);
+    ctx->ca2 = CryptoAuth_new(alloc, privateKeyB, base, logger, rand);
     if (password) {
         String* passStr = String_CONST(password);
         CryptoAuth_setAuth(passStr, NULL, ctx->sess1);
-        CryptoAuth_addUser(passStr, NULL, String_new(USEROBJ, alloc), ctx->ca2);
+        CryptoAuth_addUser(passStr, String_new(USEROBJ, alloc), ctx->ca2);
     }
     ctx->sess2 = CryptoAuth_newSession(ctx->ca2, alloc, NULL, NULL, false, "cif2");
 
@@ -73,7 +79,7 @@ static struct Context* init(uint8_t* privateKey, uint8_t* herPublicKey, uint8_t*
 
 static struct Context* simpleInit()
 {
-    return init(PRIVATEKEY, PUBLICKEY, NULL);
+    return init(PRIVATEKEY_A, PUBLICKEY_A, NULL, PRIVATEKEY_B, PUBLICKEY_B);
 }
 
 static struct Message* encryptMsg(struct Context* ctx,
@@ -177,7 +183,7 @@ static void chatter()
 
 static void auth()
 {
-    struct Context* ctx = init(PRIVATEKEY, PUBLICKEY, "password");
+    struct Context* ctx = init(PRIVATEKEY_A, PUBLICKEY_A, "password", PRIVATEKEY_B, PUBLICKEY_B);
     sendToIf2(ctx, "hello world");
     sendToIf1(ctx, "hello cjdns");
     sendToIf2(ctx, "hai");
@@ -230,7 +236,7 @@ static void hellosCrossedOnTheWire()
     struct Message* hello1 = encryptMsg(ctx, ctx->sess1, "hello1");
 
     decryptMsg(ctx, hello2, ctx->sess1, "hello2");
-    decryptMsg(ctx, hello1, ctx->sess2, "hello1");
+    decryptMsg(ctx, hello1, ctx->sess2, NULL); //"hello1"); // The message is suppressed.
 
     sendToIf2(ctx, "hello world");
     sendToIf1(ctx, "hello cjdns");

+ 2 - 2
crypto/test/CryptoAuth_unit_test.c

@@ -197,12 +197,12 @@ static void testGetUsers()
     users = CryptoAuth_getUsers(ca, allocator);
     Assert_true(List_size(users) == 0);
 
-    CryptoAuth_addUser(String_CONST("pass1"), NULL, String_CONST("user1"), ca);
+    CryptoAuth_addUser(String_CONST("pass1"), String_CONST("user1"), ca);
     users = CryptoAuth_getUsers(ca, allocator);
     Assert_true(List_size(users) == 1);
     Assert_true(String_equals(String_CONST("user1"), List_getString(users,0)));
 
-    CryptoAuth_addUser(String_CONST("pass2"), NULL, String_CONST("user2"), ca);
+    CryptoAuth_addUser(String_CONST("pass2"), String_CONST("user2"), ca);
     users = CryptoAuth_getUsers(ca, allocator);
     Assert_true(List_size(users) == 2);
     Assert_true(String_equals(String_CONST("user2"),List_getString(users,0)));

+ 1 - 1
net/Benchmark.c

@@ -166,7 +166,7 @@ static void switching(struct Context* ctx)
         InterfaceController_newIface(bob->ifController, String_CONST("bob"), alloc);
     Iface_plumb(&sc->bobIf, &bobIci->addrIf);
 
-    CryptoAuth_addUser(String_CONST("abcdefg123"), NULL, String_CONST("TEST"), bob->ca);
+    CryptoAuth_addUser(String_CONST("abcdefg123"), String_CONST("TEST"), bob->ca);
 
     // Client has pubKey and passwd for the server.
     int ret = InterfaceController_bootstrapPeer(alice->ifController,

+ 1 - 1
net/InterfaceController.c

@@ -1018,7 +1018,7 @@ struct InterfaceController* InterfaceController_new(struct CryptoAuth* ca,
     // Add the beaconing password.
     Random_bytes(rand, out->beacon.password, Headers_Beacon_PASSWORD_LEN);
     String strPass = { .bytes=(char*)out->beacon.password, .len=Headers_Beacon_PASSWORD_LEN };
-    int ret = CryptoAuth_addUser(&strPass, NULL, String_CONST("Local Peers"), ca);
+    int ret = CryptoAuth_addUser(&strPass, String_CONST("Local Peers"), ca);
     if (ret) {
         Log_warn(logger, "CryptoAuth_addUser() returned [%d]", ret);
     }

+ 2 - 2
node_build/make.js

@@ -44,7 +44,7 @@ Builder.configure({
     crossCompiling: process.env['CROSS'] !== undefined,
     gcc:            GCC,
     tempDir:        '/tmp',
-    optimizeLevel:  '-O3',
+    optimizeLevel:  '-O0',
     logLevel:       process.env['Log_LEVEL'] || 'DEBUG'
 }, function (builder, waitFor) {
     builder.config.cflags.push(
@@ -56,7 +56,7 @@ Builder.configure({
         '-pedantic',
         '-D', builder.config.systemName + '=1',
         '-Wno-unused-parameter',
-        '-fomit-frame-pointer',
+//        '-fomit-frame-pointer',
 
         '-D', 'Log_' + builder.config.logLevel,
 

+ 791 - 0
patch

@@ -0,0 +1,791 @@
+diff --git a/admin/AuthorizedPasswords.c b/admin/AuthorizedPasswords.c
+index addc04d..9982458 100644
+--- a/admin/AuthorizedPasswords.c
++++ b/admin/AuthorizedPasswords.c
+@@ -38,10 +38,9 @@ static void add(Dict* args, void* vcontext, String* txid, struct Allocator* allo
+ 
+     String* passwd = Dict_getString(args, String_CONST("password"));
+     String* user = Dict_getString(args, String_CONST("user"));
+-    String* displayName = Dict_getString(args, String_CONST("displayName"));
+     String* ipv6 = Dict_getString(args, String_CONST("ipv6"));
+ 
+-    int32_t ret = CryptoAuth_addUser_ipv6(passwd, user, displayName, ipv6, context->ca);
++    int32_t ret = CryptoAuth_addUser_ipv6(passwd, user, ipv6, context->ca);
+ 
+     switch (ret) {
+         case 0:
+@@ -51,10 +50,6 @@ static void add(Dict* args, void* vcontext, String* txid, struct Allocator* allo
+             sendResponse(String_CONST("Specified auth type is not supported."),
+                          context->admin, txid, alloc);
+             break;
+-        case CryptoAuth_addUser_OUT_OF_SPACE:
+-            sendResponse(String_CONST("Out of memory to store password."),
+-                         context->admin, txid, alloc);
+-            break;
+         case CryptoAuth_addUser_DUPLICATE:
+             sendResponse(String_CONST("Password already added."), context->admin, txid, alloc);
+             break;
+@@ -110,7 +105,6 @@ void AuthorizedPasswords_init(struct Admin* admin,
+     Admin_registerFunction("AuthorizedPasswords_add", add, context, true,
+         ((struct Admin_FunctionArg[]){
+             { .name = "password", .required = 1, .type = "String" },
+-            { .name = "displayName", .required = 0, .type = "String" },
+             { .name = "ipv6", .required = 0, .type = "String" },
+             { .name = "user", .required = 0, .type = "String" }
+         }), admin);
+diff --git a/crypto/CryptoAuth.c b/crypto/CryptoAuth.c
+index 858a429..6ae6a3f 100644
+--- a/crypto/CryptoAuth.c
++++ b/crypto/CryptoAuth.c
+@@ -111,47 +111,25 @@ static inline void getSharedSecret(uint8_t outputSecret[32],
+     }
+ }
+ 
+-static inline void hashPassword_sha256(struct CryptoAuth_Auth* auth, const String* password)
++static inline void hashPassword(uint8_t secretOut[32],
++                                union CryptoHeader_Challenge* challengeOut,
++                                const String* login,
++                                const String* password,
++                                const uint8_t authType)
+ {
++    crypto_hash_sha256(secretOut, (uint8_t*) password->bytes, password->len);
+     uint8_t tempBuff[32];
+-    crypto_hash_sha256(auth->secret, (uint8_t*) password->bytes, password->len);
+-    crypto_hash_sha256(tempBuff, auth->secret, 32);
+-    Bits_memcpyConst(auth->challenge.bytes, tempBuff, CryptoHeader_Challenge_SIZE);
+-    CryptoHeader_setAuthChallengeDerivations(&auth->challenge, 0);
+-    auth->challenge.challenge.type = 1;
+-}
+-
+-static inline void hashPassword_withLogin(struct CryptoAuth_Auth* auth,
+-                                          const String* login,
+-                                          const String* password)
+-{
+-    crypto_hash_sha256(auth->secret, (uint8_t*) password->bytes, password->len);
+-    uint8_t tempBuff[32];
+-    crypto_hash_sha256(tempBuff, (uint8_t*) login->bytes, login->len);
+-    Bits_memcpyConst(auth->challenge.bytes, tempBuff, CryptoHeader_Challenge_SIZE);
+-    CryptoHeader_setAuthChallengeDerivations(&auth->challenge, 0);
+-    auth->challenge.challenge.type = 2;
+-}
+-
+-static inline uint8_t* hashPassword(struct CryptoAuth_Auth* auth,
+-                                    const String* login,
+-                                    const String* password,
+-                                    const uint8_t authType)
+-{
+-    switch (authType) {
+-        case 1: {
+-            hashPassword_sha256(auth, password);
+-            break;
+-        }
+-        case 2: {
+-            Assert_true(login);
+-            hashPassword_withLogin(auth, login, password);
+-            break;
+-        }
+-        default:
+-            Assert_failure("Unsupported auth type.");
+-    };
+-    return auth->secret;
++    if (authType == 1) {
++        crypto_hash_sha256(tempBuff, secretOut, 32);
++    } else if (authType == 2) {
++        crypto_hash_sha256(tempBuff, (uint8_t*) login->bytes, login->len);
++    } else {
++        Assert_failure("Unsupported auth type [%u]", authType);
++    }
++    Bits_memcpyConst(challengeOut->bytes, tempBuff, CryptoHeader_Challenge_SIZE);
++    CryptoHeader_setAuthChallengeDerivations(challengeOut, 0);
++    challengeOut->challenge.type = authType;
++    challengeOut->challenge.additional = 0;
+ }
+ 
+ /**
+@@ -161,22 +139,29 @@ static inline uint8_t* hashPassword(struct CryptoAuth_Auth* auth,
+  * @param context the CryptoAuth engine to search in.
+  * @return an Auth struct with a if one is found, otherwise NULL.
+  */
+-static inline struct CryptoAuth_Auth* getAuth(union CryptoHeader_Challenge auth,
+-                                              struct CryptoAuth_pvt* context)
++static inline struct CryptoAuth_User* getAuth(union CryptoHeader_Challenge* auth,
++                                              struct CryptoAuth_pvt* ca)
+ {
+-    if (auth.challenge.type != 1) {
++    if (auth->challenge.type == 0) {
+         return NULL;
+     }
+-    for (uint32_t i = 0; i < context->passwordCount; i++) {
+-        if (Bits_memcmp(auth.bytes, &context->passwords[i], CryptoHeader_Challenge_KEYSIZE) == 0) {
+-            return &context->passwords[i];
++    int count = 0;
++    for (struct CryptoAuth_User* u = ca->users; u; u = u->next) {
++        count++;
++        if (auth->challenge.type == 1 &&
++            !Bits_memcmp(auth->bytes, u->passwordHash, CryptoHeader_Challenge_KEYSIZE))
++        {
++            return u;
++        } else if (auth->challenge.type == 2 &&
++            !Bits_memcmp(auth->bytes, u->userNameHash, CryptoHeader_Challenge_KEYSIZE))
++        {
++            return u;
+         }
+     }
+-    Log_debug(context->logger, "Got unrecognized auth, password count = [%d]",
+-              context->passwordCount);
++    Log_debug(ca->logger, "Got unrecognized auth, password count = [%d]", count);
+     return NULL;
+ }
+-
++/*
+ static inline uint8_t* tryAuth(union CryptoHeader* cauth,
+                                uint8_t hashOutput[32],
+                                struct CryptoAuth_Session_pvt* session,
+@@ -191,7 +176,7 @@ static inline uint8_t* tryAuth(union CryptoHeader* cauth,
+ 
+     return NULL;
+ }
+-
++*/
+ /**
+  * Decrypt and authenticate.
+  *
+@@ -383,15 +368,18 @@ static void encryptHandshake(struct Message* message,
+ 
+     // Password auth
+     uint8_t* passwordHash = NULL;
+-    struct CryptoAuth_Auth auth;
++    uint8_t passwordHashStore[32];
+     if (session->password != NULL) {
+-        passwordHash = hashPassword(&auth, session->login, session->password, session->authType);
+-        Bits_memcpyConst(header->handshake.auth.bytes,
+-                         &auth.challenge,
+-                         sizeof(union CryptoHeader_Challenge));
++        hashPassword(passwordHashStore,
++                     &header->handshake.auth,
++                     session->login,
++                     session->password,
++                     session->authType);
++        passwordHash = passwordHashStore;
++    } else {
++        header->handshake.auth.challenge.type = session->authType;
++        header->handshake.auth.challenge.additional = 0;
+     }
+-    header->handshake.auth.challenge.type = session->authType;
+-    header->handshake.auth.challenge.additional = 0;
+ 
+     // Set the session state
+     header->nonce = Endian_hostToBigEndian32(session->nextNonce);
+@@ -599,26 +587,24 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
+         return -1;
+     }
+ 
+-    String* user = NULL;
+-    struct CryptoAuth_Auth* auth = NULL;
+-    uint8_t passwordHashStore[32];
+-    uint8_t* passwordHash = tryAuth(header, passwordHashStore, session, &auth);
++    struct CryptoAuth_User* userObj = getAuth(&header->handshake.auth, session->context);
+     uint8_t* restrictedToip6 = NULL;
+-    if (auth) {
+-        user = auth->user;
+-        if (auth->restrictedToip6) {
+-            restrictedToip6 = auth->restrictedToip6;
+-            if (!ip6MatchesKey(auth->restrictedToip6, header->handshake.publicKey)) {
++    uint8_t* passwordHash = NULL;
++    if (userObj) {
++        passwordHash = userObj->secret;
++        if (userObj->restrictedToip6[0]) {
++            restrictedToip6 = userObj->restrictedToip6;
++            if (!ip6MatchesKey(restrictedToip6, header->handshake.publicKey)) {
+                 cryptoAuthDebug0(session, "DROP packet with key not matching restrictedToip6");
+                 return -1;
+             }
+         }
+     }
+-    if (session->requireAuth && !user) {
++    if (session->requireAuth && !userObj) {
+         cryptoAuthDebug0(session, "DROP message because auth was not given");
+         return -1;
+     }
+-    if (passwordHash == NULL && header->handshake.auth.challenge.type != 0) {
++    if (!userObj && header->handshake.auth.challenge.type != 0) {
+         cryptoAuthDebug0(session, "DROP message with unrecognized authenticator");
+         return -1;
+     }
+@@ -632,7 +618,7 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
+     if (nonce < 2) {
+         if (nonce == 0) {
+             cryptoAuthDebug(session, "Received a hello packet, using auth: %d",
+-                            (passwordHash != NULL));
++                            (userObj != NULL));
+         } else {
+             cryptoAuthDebug0(session, "Received a repeat hello packet");
+         }
+@@ -641,7 +627,8 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
+         if (!knowHerKey(session) || session->nextNonce == 0) {
+             herPermKey = header->handshake.publicKey;
+             if (Defined(Log_DEBUG) && Bits_isZero(header->handshake.publicKey, 32)) {
+-                cryptoAuthDebug0(session, "Node sent public key of ZERO!");
++                cryptoAuthDebug0(session, "DROP Node sent public key of ZERO!");
++                return -1;
+             }
+         } else {
+             herPermKey = session->pub.herPublicKey;
+@@ -663,7 +650,8 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
+         } else if (nonce == 3) {
+             cryptoAuthDebug0(session, "Received a repeat key packet");
+         } else {
+-            cryptoAuthDebug(session, "Received a packet of unknown type! nonce=%u", nonce);
++            cryptoAuthDebug(session, "DROP Received a packet of unknown type! nonce=%u", nonce);
++            return -1;
+         }
+         if (Bits_memcmp(header->handshake.publicKey, session->pub.herPublicKey, 32)) {
+             cryptoAuthDebug0(session, "DROP packet contains different perminent key");
+@@ -710,6 +698,7 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
+ 
+     if (Bits_isZero(header->handshake.encryptedTempKey, 32)) {
+         // we need to reject 0 public keys outright because they will be confused with "unknown"
++        cryptoAuthDebug0(session, "DROP message with zero as temp public key");
+         return -1;
+     }
+ 
+@@ -735,6 +724,7 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
+     } else if (nonce == 2 && session->nextNonce >= 4) {
+         // we accept a new key packet and let it change the session since the other end might have
+         // killed off the session while it was in the midst of setting up.
++        // This is NOT a repeat key packet because it's nonce is 2, not 3
+         if (!Bits_memcmp(session->herTempPubKey, header->handshake.encryptedTempKey, 32)) {
+             Assert_true(!Bits_isZero(session->herTempPubKey, 32));
+             cryptoAuthDebug0(session, "DROP dupe key packet with same temp key");
+@@ -760,12 +750,6 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
+     if (nextNonce == 4) {
+         if (session->nextNonce <= 4) {
+             // and have not yet begun sending "run" data
+-            Assert_true(session->nextNonce <= nextNonce);
+-            session->nextNonce = nextNonce;
+-
+-            if (!session->pub.displayName) {
+-                session->pub.displayName = user;
+-            }
+             Bits_memcpyConst(session->herTempPubKey, header->handshake.encryptedTempKey, 32);
+         } else {
+             // It's a (possibly repeat) key packet and we have begun sending run data.
+@@ -776,6 +760,7 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
+                             header->handshake.encryptedTempKey,
+                             NULL,
+                             session->context->logger);
++            nextNonce = session->nextNonce + 1;
+             cryptoAuthDebug0(session, "New key packet but we are already sending data");
+         }
+ 
+@@ -802,14 +787,6 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
+             nextNonce = 3;
+         }
+ 
+-        Assert_true(session->nextNonce <= nextNonce);
+-        session->nextNonce = nextNonce;
+-        if (!session->pub.displayName) {
+-            session->pub.displayName = user;
+-        }
+-        if (restrictedToip6) {
+-            Bits_memcpyConst(session->pub.herIp6, restrictedToip6, 16);
+-        }
+         Bits_memcpyConst(session->herTempPubKey, header->handshake.encryptedTempKey, 32);
+ 
+     } else if (Bits_memcmp(header->handshake.publicKey, session->context->pub.publicKey, 32) < 0) {
+@@ -819,23 +796,28 @@ static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
+         cryptoAuthDebug0(session, "Incoming hello from node with lower key, resetting");
+         reset(session);
+ 
+-        Assert_true(session->nextNonce <= nextNonce);
+-        session->nextNonce = nextNonce;
+-        if (!session->pub.displayName) {
+-            session->pub.displayName = user;
+-        }
+-        if (restrictedToip6) {
+-            Bits_memcpyConst(session->pub.herIp6, restrictedToip6, 16);
+-        }
+         Bits_memcpyConst(session->herTempPubKey, header->handshake.encryptedTempKey, 32);
+ 
+     } else {
+-        cryptoAuthDebug0(session, "Incoming hello from node with higher key, not resetting");
++        cryptoAuthDebug0(session, "DROP Incoming hello from node with higher key, not resetting");
++        return -1;
+     }
+ 
+     if (herPermKey && herPermKey != session->pub.herPublicKey) {
+         Bits_memcpyConst(session->pub.herPublicKey, herPermKey, 32);
+     }
++    if (!session->pub.displayName && userObj) {
++        session->pub.displayName = userObj->login;
++    }
++    if (restrictedToip6) {
++        Bits_memcpyConst(session->pub.herIp6, restrictedToip6, 16);
++    }
++
++    // Nonces can never go backward and can only "not advance" if they're 0,1,2,3 session state.
++    Assert_true(session->nextNonce < nextNonce ||
++        (session->nextNonce <= 4 && nextNonce == session->nextNonce)
++    );
++    session->nextNonce = nextNonce;
+ 
+     Bits_memset(&session->pub.replayProtector, 0, sizeof(struct ReplayProtector));
+ 
+@@ -936,9 +918,6 @@ struct CryptoAuth* CryptoAuth_new(struct Allocator* allocator,
+     struct CryptoAuth_pvt* ca = Allocator_calloc(allocator, sizeof(struct CryptoAuth_pvt), 1);
+     Identity_set(ca);
+     ca->allocator = allocator;
+-    ca->passwords = Allocator_calloc(allocator, sizeof(struct CryptoAuth_Auth), 232);
+-    ca->passwordCount = 0;
+-    ca->passwordCapacity = 232;
+     ca->eventBase = eventBase;
+     ca->logger = logger;
+     ca->pub.resetAfterInactivitySeconds = CryptoAuth_DEFAULT_RESET_AFTER_INACTIVITY_SECONDS;
+@@ -965,87 +944,93 @@ struct CryptoAuth* CryptoAuth_new(struct Allocator* allocator,
+     return &ca->pub;
+ }
+ 
+-int32_t CryptoAuth_addUser(String* password,
+-                           String* login,
+-                           String* displayName,
+-                           struct CryptoAuth* ca)
++int CryptoAuth_addUser_ipv6(String* password,
++                            String* login,
++                            String* ipv6,
++                            struct CryptoAuth* cryptoAuth)
+ {
+-     return CryptoAuth_addUser_ipv6(password, login, displayName, NULL, ca);
+-}
++    struct CryptoAuth_pvt* ca = Identity_check((struct CryptoAuth_pvt*) cryptoAuth);
+ 
+-int32_t CryptoAuth_addUser_ipv6(String* password,
+-                                String* login,
+-                                String* displayName,
+-                                String* ipv6,
+-                                struct CryptoAuth* ca)
+-{
+-    struct CryptoAuth_pvt* context = Identity_check((struct CryptoAuth_pvt*) ca);
++    struct Allocator* alloc = Allocator_child(ca->allocator);
++    struct CryptoAuth_User* user = Allocator_calloc(alloc, sizeof(struct CryptoAuth_User), 1);
++    user->alloc = alloc;
++    Identity_set(user);
+ 
+-    struct CryptoAuth_Auth a;
+-    if (login) {
+-        hashPassword_withLogin(&a, login, password);
++    if (!login) {
++        int i = 0;
++        for (struct CryptoAuth_User* u = ca->users; u; u = u->next) { i++; }
++        user->login = login = String_printf(alloc, "Anon #%d", i);
+     } else {
+-        hashPassword_sha256(&a, password);
++        user->login = String_clone(login, alloc);
+     }
+-    for (uint32_t i = 0; i < context->passwordCount; i++) {
+-        if (!Bits_memcmp(a.secret, context->passwords[i].secret, 32) ||
+-            String_equals(displayName, context->passwords[i].user))
+-        {
++
++    union CryptoHeader_Challenge ac;
++    // Users specified with a login field might want to use authType 1 still.
++    hashPassword(user->secret, &ac, login, password, 2);
++    Bits_memcpyConst(user->userNameHash, ac.bytes, 8);
++    hashPassword(user->secret, &ac, NULL, password, 1);
++    Bits_memcpyConst(user->passwordHash, ac.bytes, 8);
++
++    for (struct CryptoAuth_User* u = ca->users; u; u = u->next) {
++        if (Bits_memcmp(user->secret, u->secret, 32)) {
++        } else if (!login) {
++        } else if (String_equals(login, u->login)) {
++            Allocator_free(alloc);
+             return CryptoAuth_addUser_DUPLICATE;
+         }
+     }
+-    a.user = String_clone(displayName, context->allocator);
++
+     if (ipv6) {
+-        // TODO(cjd): This could become a memory leak
+-        a.restrictedToip6 = Allocator_malloc(context->allocator, 16);
+-        if (AddrTools_parseIp(a.restrictedToip6, ipv6->bytes) < 0) {
+-            Log_debug(context->logger, "Ipv6 parsing error!");
++        if (AddrTools_parseIp(user->restrictedToip6, ipv6->bytes) < 0) {
++            Log_debug(ca->logger, "Ipv6 parsing error!");
++            Allocator_free(alloc);
+             return CryptoAuth_addUser_INVALID_IP;
+         }
+-    } else {
+-        a.restrictedToip6 = NULL;
+     }
+-    Bits_memcpyConst(&context->passwords[context->passwordCount],
+-                     &a,
+-                     sizeof(struct CryptoAuth_Auth));
+-    context->passwordCount++;
++
++    // Add the user to the *end* of the list
++    for (struct CryptoAuth_User** up = &ca->users; ; up = &(*up)->next) {
++        if (!*up) {
++            *up = user;
++            break;
++        }
++    }
++
+     return 0;
+ }
+ 
+-int CryptoAuth_removeUsers(struct CryptoAuth* context, String* user)
++int CryptoAuth_removeUsers(struct CryptoAuth* context, String* login)
+ {
+-    struct CryptoAuth_pvt* ctx = Identity_check((struct CryptoAuth_pvt*) context);
+-    if (!user) {
+-        int count = ctx->passwordCount;
+-        Log_debug(ctx->logger, "Flushing [%d] users", count);
+-        ctx->passwordCount = 0;
+-        return count;
+-    }
++    struct CryptoAuth_pvt* ca = Identity_check((struct CryptoAuth_pvt*) context);
++
+     int count = 0;
+-    int i = 0;
+-    while (i < (int)ctx->passwordCount) {
+-        if (String_equals(ctx->passwords[i].user, user)) {
+-            Bits_memcpyConst(&ctx->passwords[i],
+-                             &ctx->passwords[ctx->passwordCount--],
+-                             sizeof(struct CryptoAuth_Auth));
++    struct CryptoAuth_User** up = &ca->users;
++    struct CryptoAuth_User* u = *up;
++    while ((u = *up)) {
++        if (!login || String_equals(login, u->login)) {
++            *up = u->next;
++            Allocator_free(u->alloc);
+             count++;
+         } else {
+-            i++;
++            up = &u->next;
+         }
+     }
+-    Log_debug(ctx->logger, "Removing [%d] user(s) identified by [%s]", count, user->bytes);
++
++    if (!login) {
++        Log_debug(ca->logger, "Flushing [%d] users", count);
++    } else {
++        Log_debug(ca->logger, "Removing [%d] user(s) identified by [%s]", count, login->bytes);
++    }
+     return count;
+ }
+ 
+ List* CryptoAuth_getUsers(struct CryptoAuth* context, struct Allocator* alloc)
+ {
+-    struct CryptoAuth_pvt* ctx = Identity_check((struct CryptoAuth_pvt*) context);
+-    uint32_t count = ctx->passwordCount;
++    struct CryptoAuth_pvt* ca = Identity_check((struct CryptoAuth_pvt*) context);
+ 
+     List* users = List_new(alloc);
+-
+-    for (uint32_t i = 0; i < count; i++) {
+-        List_addString(users, String_clone(ctx->passwords[i].user, alloc), alloc);
++    for (struct CryptoAuth_User* u = ca->users; u; u = u->next) {
++        List_addString(users, String_clone(u->login, alloc), alloc);
+     }
+ 
+     return users;
+diff --git a/crypto/CryptoAuth.h b/crypto/CryptoAuth.h
+index 148326e..b6aefdf 100644
+--- a/crypto/CryptoAuth.h
++++ b/crypto/CryptoAuth.h
+@@ -74,24 +74,21 @@ struct CryptoAuth_Session
+  * @param context The CryptoAuth context.
+  * @return 0 if all goes well,
+  *         CryptoAuth_addUser_INVALID_AUTHTYPE if the authentication method is not supported,
+- *         CryptoAuth_addUser_OUT_OF_SPACE if there is not enough space to store the entry,
+  *         CryptoAuth_addUser_DUPLICATE if the same *password* already exists.
+  *         CryptoAuth_addUser_INVALID_IP if the ipv6 is not valid.
+  */
+ #define CryptoAuth_addUser_INVALID_AUTHTYPE  -1
+-#define CryptoAuth_addUser_OUT_OF_SPACE      -2
+ #define CryptoAuth_addUser_DUPLICATE         -3
+ #define CryptoAuth_addUser_INVALID_IP        -4
+-int32_t CryptoAuth_addUser_ipv6(String* password,
+-                                String* login,
+-                                String* displayName,
+-                                String* ipv6,
+-                                struct CryptoAuth* ca);
+-
+-int32_t CryptoAuth_addUser(String* password,
+-                           String* login,
+-                           String* displayName,
+-                           struct CryptoAuth* ca);
++int CryptoAuth_addUser_ipv6(String* password,
++                            String* login,
++                            String* ipv6,
++                            struct CryptoAuth* ca);
++
++static inline int CryptoAuth_addUser(String* password, String* login, struct CryptoAuth* ca)
++{
++    return CryptoAuth_addUser_ipv6(password, login, NULL, ca);
++}
+ 
+ /**
+  * Remove all users registered with this CryptoAuth.
+diff --git a/crypto/CryptoAuth_pvt.h b/crypto/CryptoAuth_pvt.h
+index b57b2c0..def7fad 100644
+--- a/crypto/CryptoAuth_pvt.h
++++ b/crypto/CryptoAuth_pvt.h
+@@ -26,13 +26,25 @@
+ 
+ #include <stdint.h>
+ 
+-struct CryptoAuth_Auth {
+-    union CryptoHeader_Challenge challenge;
++struct CryptoAuth_User;
++struct CryptoAuth_User {
++    /** Double-hash of password for authType 1 */
++    uint8_t passwordHash[8];
++
++    /** Hash of username for authType 2 */
++    uint8_t userNameHash[8];
+ 
+     uint8_t secret[32];
+ 
+-    String* user;
+-    uint8_t* restrictedToip6;
++    String* login;
++
++    uint8_t restrictedToip6[16];
++
++    struct CryptoAuth_User* next;
++
++    struct Allocator* alloc;
++
++    Identity
+ };
+ 
+ struct CryptoAuth_pvt
+@@ -41,9 +53,7 @@ struct CryptoAuth_pvt
+ 
+     uint8_t privateKey[32];
+ 
+-    struct CryptoAuth_Auth* passwords;
+-    uint32_t passwordCount;
+-    uint32_t passwordCapacity;
++    struct CryptoAuth_User* users;
+ 
+     struct Log* logger;
+     struct EventBase* eventBase;
+diff --git a/crypto/test/CryptoAuth_test.c b/crypto/test/CryptoAuth_test.c
+index 23492f1..dc73e1a 100644
+--- a/crypto/test/CryptoAuth_test.c
++++ b/crypto/test/CryptoAuth_test.c
+@@ -24,13 +24,15 @@
+ #include "util/log/FileWriterLog.h"
+ #include "wire/CryptoHeader.h"
+ 
+-#define PRIVATEKEY ( \
+-    "\x20\xca\x45\xd9\x5b\xbf\xca\xe7\x35\x3c\xd2\xdf\xfa\x12\x84\x4b" \
+-    "\x4e\xff\xbe\x7d\x39\xd8\x4d\x8e\x14\x2b\x9d\x21\x89\x5b\x38\x09" )
++#define PRIVATEKEY_A \
++    Constant_stringForHex("53ff22b2eb94ce8c5f1852c0f557eb901f067e5273d541e0a21e143c20dff9da")
++#define PUBLICKEY_A \
++    Constant_stringForHex("e3ff75af6e4414494df22f200ffeaa56e7976d991d33cc87f52427e27f83235d")
+ 
+-#define PUBLICKEY ( \
+-    "\x51\xaf\x8d\xd9\x35\xe8\x61\x86\x3e\x94\x2b\x1b\x6d\x21\x22\xe0" \
+-    "\x2f\xb2\xd0\x88\x20\xbb\xf3\xf0\x6f\xcd\xe5\x85\x30\xe0\x08\x34" )
++#define PRIVATEKEY_B \
++    Constant_stringForHex("b71c4f43e3d4b1879b5065d44a1cb43eaf07ddba96de6a72ca761c4ef4bd2988")
++#define PUBLICKEY_B \
++    Constant_stringForHex("27c303cdc1f96e4b28d51c75130aff6cad52098f2d752615b7b6509ed6a89477")
+ 
+ #define USEROBJ "This represents a user"
+ 
+@@ -48,7 +50,11 @@ struct Context
+     struct EventBase* base;
+ };
+ 
+-static struct Context* init(uint8_t* privateKey, uint8_t* herPublicKey, uint8_t* password)
++static struct Context* init(uint8_t* privateKeyA,
++                            uint8_t* publicKeyA,
++                            uint8_t* password,
++                            uint8_t* privateKeyB,
++                            uint8_t* publicKeyB)
+ {
+     struct Allocator* alloc = MallocAllocator_new(1048576);
+     struct Context* ctx = Allocator_calloc(alloc, sizeof(struct Context), 1);
+@@ -57,14 +63,14 @@ static struct Context* init(uint8_t* privateKey, uint8_t* herPublicKey, uint8_t*
+     struct Random* rand = ctx->rand = Random_new(alloc, logger, NULL);
+     struct EventBase* base = ctx->base = EventBase_new(alloc);
+ 
+-    ctx->ca1 = CryptoAuth_new(alloc, NULL, base, logger, rand);
+-    ctx->sess1 = CryptoAuth_newSession(ctx->ca1, alloc, herPublicKey, NULL, false, "cif1");
++    ctx->ca1 = CryptoAuth_new(alloc, privateKeyA, base, logger, rand);
++    ctx->sess1 = CryptoAuth_newSession(ctx->ca1, alloc, publicKeyB, NULL, false, "cif1");
+ 
+-    ctx->ca2 = CryptoAuth_new(alloc, privateKey, base, logger, rand);
++    ctx->ca2 = CryptoAuth_new(alloc, privateKeyB, base, logger, rand);
+     if (password) {
+         String* passStr = String_CONST(password);
+         CryptoAuth_setAuth(passStr, NULL, ctx->sess1);
+-        CryptoAuth_addUser(passStr, NULL, String_new(USEROBJ, alloc), ctx->ca2);
++        CryptoAuth_addUser(passStr, String_new(USEROBJ, alloc), ctx->ca2);
+     }
+     ctx->sess2 = CryptoAuth_newSession(ctx->ca2, alloc, NULL, NULL, false, "cif2");
+ 
+@@ -73,7 +79,7 @@ static struct Context* init(uint8_t* privateKey, uint8_t* herPublicKey, uint8_t*
+ 
+ static struct Context* simpleInit()
+ {
+-    return init(PRIVATEKEY, PUBLICKEY, NULL);
++    return init(PRIVATEKEY_A, PUBLICKEY_A, NULL, PRIVATEKEY_B, PUBLICKEY_B);
+ }
+ 
+ static struct Message* encryptMsg(struct Context* ctx,
+@@ -177,7 +183,7 @@ static void chatter()
+ 
+ static void auth()
+ {
+-    struct Context* ctx = init(PRIVATEKEY, PUBLICKEY, "password");
++    struct Context* ctx = init(PRIVATEKEY_A, PUBLICKEY_A, "password", PRIVATEKEY_B, PUBLICKEY_B);
+     sendToIf2(ctx, "hello world");
+     sendToIf1(ctx, "hello cjdns");
+     sendToIf2(ctx, "hai");
+@@ -230,7 +236,7 @@ static void hellosCrossedOnTheWire()
+     struct Message* hello1 = encryptMsg(ctx, ctx->sess1, "hello1");
+ 
+     decryptMsg(ctx, hello2, ctx->sess1, "hello2");
+-    decryptMsg(ctx, hello1, ctx->sess2, "hello1");
++    decryptMsg(ctx, hello1, ctx->sess2, NULL); //"hello1"); // The message is suppressed.
+ 
+     sendToIf2(ctx, "hello world");
+     sendToIf1(ctx, "hello cjdns");
+diff --git a/crypto/test/CryptoAuth_unit_test.c b/crypto/test/CryptoAuth_unit_test.c
+index 986576e..3bf49d0 100644
+--- a/crypto/test/CryptoAuth_unit_test.c
++++ b/crypto/test/CryptoAuth_unit_test.c
+@@ -197,12 +197,12 @@ static void testGetUsers()
+     users = CryptoAuth_getUsers(ca, allocator);
+     Assert_true(List_size(users) == 0);
+ 
+-    CryptoAuth_addUser(String_CONST("pass1"), NULL, String_CONST("user1"), ca);
++    CryptoAuth_addUser(String_CONST("pass1"), String_CONST("user1"), ca);
+     users = CryptoAuth_getUsers(ca, allocator);
+     Assert_true(List_size(users) == 1);
+     Assert_true(String_equals(String_CONST("user1"), List_getString(users,0)));
+ 
+-    CryptoAuth_addUser(String_CONST("pass2"), NULL, String_CONST("user2"), ca);
++    CryptoAuth_addUser(String_CONST("pass2"), String_CONST("user2"), ca);
+     users = CryptoAuth_getUsers(ca, allocator);
+     Assert_true(List_size(users) == 2);
+     Assert_true(String_equals(String_CONST("user2"),List_getString(users,0)));
+diff --git a/net/Benchmark.c b/net/Benchmark.c
+index a3b77eb..100d7d9 100644
+--- a/net/Benchmark.c
++++ b/net/Benchmark.c
+@@ -166,7 +166,7 @@ static void switching(struct Context* ctx)
+         InterfaceController_newIface(bob->ifController, String_CONST("bob"), alloc);
+     Iface_plumb(&sc->bobIf, &bobIci->addrIf);
+ 
+-    CryptoAuth_addUser(String_CONST("abcdefg123"), NULL, String_CONST("TEST"), bob->ca);
++    CryptoAuth_addUser(String_CONST("abcdefg123"), String_CONST("TEST"), bob->ca);
+ 
+     // Client has pubKey and passwd for the server.
+     int ret = InterfaceController_bootstrapPeer(alice->ifController,
+diff --git a/net/InterfaceController.c b/net/InterfaceController.c
+index 39d78db..9a839ef 100644
+--- a/net/InterfaceController.c
++++ b/net/InterfaceController.c
+@@ -1018,7 +1018,7 @@ struct InterfaceController* InterfaceController_new(struct CryptoAuth* ca,
+     // Add the beaconing password.
+     Random_bytes(rand, out->beacon.password, Headers_Beacon_PASSWORD_LEN);
+     String strPass = { .bytes=(char*)out->beacon.password, .len=Headers_Beacon_PASSWORD_LEN };
+-    int ret = CryptoAuth_addUser(&strPass, NULL, String_CONST("Local Peers"), ca);
++    int ret = CryptoAuth_addUser(&strPass, String_CONST("Local Peers"), ca);
+     if (ret) {
+         Log_warn(logger, "CryptoAuth_addUser() returned [%d]", ret);
+     }
+diff --git a/node_build/make.js b/node_build/make.js
+index f85725f..95336eb 100644
+--- a/node_build/make.js
++++ b/node_build/make.js
+@@ -44,7 +44,7 @@ Builder.configure({
+     crossCompiling: process.env['CROSS'] !== undefined,
+     gcc:            GCC,
+     tempDir:        '/tmp',
+-    optimizeLevel:  '-O3',
++    optimizeLevel:  '-O0',
+     logLevel:       process.env['Log_LEVEL'] || 'DEBUG'
+ }, function (builder, waitFor) {
+     builder.config.cflags.push(
+@@ -56,7 +56,7 @@ Builder.configure({
+         '-pedantic',
+         '-D', builder.config.systemName + '=1',
+         '-Wno-unused-parameter',
+-        '-fomit-frame-pointer',
++//        '-fomit-frame-pointer',
+ 
+         '-D', 'Log_' + builder.config.logLevel,
+ 
+diff --git a/test/TestFramework.c b/test/TestFramework.c
+index 3c780ef..e799651 100644
+--- a/test/TestFramework.c
++++ b/test/TestFramework.c
+@@ -185,7 +185,7 @@ void TestFramework_linkNodes(struct TestFramework* client,
+         Assert_true(!ret);
+     } else {
+         // Except that it has an authorizedPassword added.
+-        CryptoAuth_addUser(String_CONST("abcdefg123"), NULL, String_CONST("TEST"), server->nc->ca);
++        CryptoAuth_addUser(String_CONST("abcdefg123"), String_CONST("TEST"), server->nc->ca);
+ 
+         // Client has pubKey and passwd for the server.
+         InterfaceController_bootstrapPeer(client->nc->ifController,
+diff --git a/tools/lib/publicToIp6.js b/tools/lib/publicToIp6.js
+index 5cdaac9..e7a2fb3 100644
+--- a/tools/lib/publicToIp6.js
++++ b/tools/lib/publicToIp6.js
+@@ -62,6 +62,7 @@ var Base32_decode = function (input) {
+ var convert = module.exports.convert = function (pubKey) {
+     if (pubKey.substring(pubKey.length-2) !== ".k") { throw new Error("key does not end with .k"); }
+     keyBytes = Base32_decode(pubKey.substring(0, pubKey.length-2));
++    //console.log(keyBytes.toString('hex'));
+     var hashOneBuff = new Buffer(Crypto.createHash('sha512').update(keyBytes).digest('hex'), 'hex');
+     var hashTwo = Crypto.createHash('sha512').update(hashOneBuff).digest('hex');
+     var first16 = hashTwo.substring(0,32);
+@@ -72,4 +73,7 @@ var convert = module.exports.convert = function (pubKey) {
+     return out.join(':');
+ };
+ 
++if (!module.parent) {
++    console.log(convert(process.argv[process.argv.length - 1]));
++}
+ //console.log(convert('rjndc8rvg194ddf2j5v679cfjcpmsmhv8p022q3lvpym21cqwyh0.k'));
+diff --git a/util/Constant.h b/util/Constant.h
+index 7807764..d348b95 100644
+--- a/util/Constant.h
++++ b/util/Constant.h
+@@ -17,6 +17,8 @@
+ 
+ <?js file.Constant_JS = require("../util/Constant.js"); ?>
+ 
++#define Constant_stringForHex(hex) <?js return file.Constant_JS.stringForHex( hex ) ?>
++
+ #define Constant_base2(num) <?js return file.Constant_JS.base2( #num ) ?>
+ 
+ #define Constant_rand64() <?js return file.Constant_JS.rand64(); ?>
+diff --git a/util/Constant.js b/util/Constant.js
+index 2d03ac0..9d26982 100644
+--- a/util/Constant.js
++++ b/util/Constant.js
+@@ -90,6 +90,15 @@ var log2 = module.exports.log2 = function (val) {
+     throw new Error("not an even power of 2");
+ };
+ 
++var stringForHex = module.exports.stringForHex = function (hex) {
++    var out = [];
++    for (var i = 0; i < hex.length; i+=2) {
++        out.push(hex[i] + hex[i+1]);
++    }
++    if (out.length < 2) { throw new Error(); }
++    return '"\\x' + out.join('\\x') + '"';
++};
++
+ if (!module.parent) {
+     console.log("testing " + __filename);
+     testBase2();

+ 1 - 1
test/TestFramework.c

@@ -185,7 +185,7 @@ void TestFramework_linkNodes(struct TestFramework* client,
         Assert_true(!ret);
     } else {
         // Except that it has an authorizedPassword added.
-        CryptoAuth_addUser(String_CONST("abcdefg123"), NULL, String_CONST("TEST"), server->nc->ca);
+        CryptoAuth_addUser(String_CONST("abcdefg123"), String_CONST("TEST"), server->nc->ca);
 
         // Client has pubKey and passwd for the server.
         InterfaceController_bootstrapPeer(client->nc->ifController,

+ 4 - 0
tools/lib/publicToIp6.js

@@ -62,6 +62,7 @@ var Base32_decode = function (input) {
 var convert = module.exports.convert = function (pubKey) {
     if (pubKey.substring(pubKey.length-2) !== ".k") { throw new Error("key does not end with .k"); }
     keyBytes = Base32_decode(pubKey.substring(0, pubKey.length-2));
+    //console.log(keyBytes.toString('hex'));
     var hashOneBuff = new Buffer(Crypto.createHash('sha512').update(keyBytes).digest('hex'), 'hex');
     var hashTwo = Crypto.createHash('sha512').update(hashOneBuff).digest('hex');
     var first16 = hashTwo.substring(0,32);
@@ -72,4 +73,7 @@ var convert = module.exports.convert = function (pubKey) {
     return out.join(':');
 };
 
+if (!module.parent) {
+    console.log(convert(process.argv[process.argv.length - 1]));
+}
 //console.log(convert('rjndc8rvg194ddf2j5v679cfjcpmsmhv8p022q3lvpym21cqwyh0.k'));

+ 2 - 0
util/Constant.h

@@ -17,6 +17,8 @@
 
 <?js file.Constant_JS = require("../util/Constant.js"); ?>
 
+#define Constant_stringForHex(hex) <?js return file.Constant_JS.stringForHex( hex ) ?>
+
 #define Constant_base2(num) <?js return file.Constant_JS.base2( #num ) ?>
 
 #define Constant_rand64() <?js return file.Constant_JS.rand64(); ?>

+ 9 - 0
util/Constant.js

@@ -90,6 +90,15 @@ var log2 = module.exports.log2 = function (val) {
     throw new Error("not an even power of 2");
 };
 
+var stringForHex = module.exports.stringForHex = function (hex) {
+    var out = [];
+    for (var i = 0; i < hex.length; i+=2) {
+        out.push(hex[i] + hex[i+1]);
+    }
+    if (out.length < 2) { throw new Error(); }
+    return '"\\x' + out.join('\\x') + '"';
+};
+
 if (!module.parent) {
     console.log("testing " + __filename);
     testBase2();