├── .gitignore ├── LICENSE ├── src ├── pushpop.h ├── crypto_spake.h └── crypto_spake.c ├── test └── test.c └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2017-2021, Frank Denis 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /src/pushpop.h: -------------------------------------------------------------------------------- 1 | static inline void 2 | _push16(unsigned char *out, size_t *i_p, uint16_t v) 3 | { 4 | out[0 + *i_p] = (unsigned char) (v ); 5 | out[1 + *i_p] = (unsigned char) (v >> 8); 6 | (*i_p) += 2; 7 | } 8 | 9 | static inline void 10 | _push64(unsigned char *out, size_t *i_p, uint64_t v) 11 | { 12 | out[0 + *i_p] = (unsigned char) (v ); 13 | out[1 + *i_p] = (unsigned char) (v >> 8); 14 | out[2 + *i_p] = (unsigned char) (v >> 16); 15 | out[3 + *i_p] = (unsigned char) (v >> 24); 16 | out[4 + *i_p] = (unsigned char) (v >> 32); 17 | out[5 + *i_p] = (unsigned char) (v >> 40); 18 | out[6 + *i_p] = (unsigned char) (v >> 48); 19 | out[7 + *i_p] = (unsigned char) (v >> 56); 20 | (*i_p) += 8; 21 | } 22 | 23 | static inline void 24 | _push128(unsigned char *out, size_t *i_p, const unsigned char v[16]) 25 | { 26 | memcpy(&out[*i_p], v, 16); 27 | (*i_p) += 16; 28 | } 29 | 30 | static inline void 31 | _push256(unsigned char *out, size_t *i_p, const unsigned char v[32]) 32 | { 33 | memcpy(&out[*i_p], v, 32); 34 | (*i_p) += 32; 35 | } 36 | 37 | static inline void 38 | _pop16(uint16_t *v, const unsigned char *in, size_t *i_p) 39 | { 40 | *v = (((uint16_t) in[0 + *i_p]) ) 41 | | (((uint16_t) in[1 + *i_p]) << 8); 42 | (*i_p) += 2; 43 | } 44 | 45 | static inline void 46 | _pop64(uint64_t *v, const unsigned char *in, size_t *i_p) 47 | { 48 | *v = (((uint64_t) in[0 + *i_p]) ) 49 | | (((uint64_t) in[1 + *i_p]) << 8) 50 | | (((uint64_t) in[2 + *i_p]) << 16) 51 | | (((uint64_t) in[3 + *i_p]) << 24) 52 | | (((uint64_t) in[4 + *i_p]) << 32) 53 | | (((uint64_t) in[5 + *i_p]) << 40) 54 | | (((uint64_t) in[6 + *i_p]) << 48) 55 | | (((uint64_t) in[7 + *i_p]) << 56); 56 | (*i_p) += 8; 57 | } 58 | 59 | static inline void 60 | _pop128(unsigned char v[32], const unsigned char *in, size_t *i_p) 61 | { 62 | memcpy(v, &in[*i_p], 16); 63 | (*i_p) += 16; 64 | } 65 | 66 | static inline void 67 | _pop256(unsigned char v[32], const unsigned char *in, size_t *i_p) 68 | { 69 | memcpy(v, &in[*i_p], 32); 70 | (*i_p) += 32; 71 | } 72 | -------------------------------------------------------------------------------- /src/crypto_spake.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef crypto_spake_H 3 | #define crypto_spake_H 1 4 | 5 | #define crypto_spake_DUMMYKEYBYTES 32 6 | #define crypto_spake_PUBLICDATABYTES 36 7 | #define crypto_spake_RESPONSE1BYTES 32 8 | #define crypto_spake_RESPONSE2BYTES 64 9 | #define crypto_spake_RESPONSE3BYTES 32 10 | #define crypto_spake_SHAREDKEYBYTES 32 11 | #define crypto_spake_STOREDBYTES 164 12 | 13 | typedef struct crypto_spake_shared_keys_ { 14 | unsigned char client_sk[crypto_spake_SHAREDKEYBYTES]; 15 | unsigned char server_sk[crypto_spake_SHAREDKEYBYTES]; 16 | } crypto_spake_shared_keys; 17 | 18 | typedef struct crypto_spake_client_state_ { 19 | unsigned char h_K[32]; 20 | unsigned char h_L[32]; 21 | unsigned char N[32]; 22 | unsigned char x[32]; 23 | unsigned char X[32]; 24 | } crypto_spake_client_state; 25 | 26 | typedef struct crypto_spake_server_state_ { 27 | unsigned char server_validator[32]; 28 | crypto_spake_shared_keys shared_keys; 29 | } crypto_spake_server_state; 30 | 31 | int crypto_spake_server_store(unsigned char stored_data[crypto_spake_STOREDBYTES], 32 | const char * const passwd, unsigned long long passwdlen, 33 | unsigned long long opslimit, size_t memlimit); 34 | 35 | int crypto_spake_validate_public_data(const unsigned char public_data[crypto_spake_PUBLICDATABYTES], 36 | const int expected_alg, 37 | unsigned long long expected_opslimit, 38 | unsigned long long expected_memlimit); 39 | 40 | int crypto_spake_step0_dummy(crypto_spake_server_state *st, 41 | unsigned char public_data[crypto_spake_PUBLICDATABYTES], 42 | const char *client_id, size_t client_id_len, 43 | const char *server_id, size_t server_id_len, 44 | unsigned long long opslimit, size_t memlimit, 45 | const unsigned char key[crypto_spake_DUMMYKEYBYTES]); 46 | 47 | int crypto_spake_step0(crypto_spake_server_state *st, 48 | unsigned char public_data[crypto_spake_PUBLICDATABYTES], 49 | const unsigned char stored_data[crypto_spake_STOREDBYTES]); 50 | 51 | int crypto_spake_step1(crypto_spake_client_state *st, unsigned char response1[crypto_spake_RESPONSE1BYTES], 52 | const unsigned char public_data[crypto_spake_PUBLICDATABYTES], 53 | const char * const passwd, unsigned long long passwdlen); 54 | 55 | int crypto_spake_step2(crypto_spake_server_state *st, 56 | unsigned char response2[crypto_spake_RESPONSE2BYTES], 57 | const char *client_id, size_t client_id_len, 58 | const char *server_id, size_t server_id_len, 59 | const unsigned char stored_data[crypto_spake_STOREDBYTES], 60 | const unsigned char response1[crypto_spake_RESPONSE1BYTES]); 61 | 62 | int crypto_spake_step3(crypto_spake_client_state *st, 63 | unsigned char response3[crypto_spake_RESPONSE3BYTES], 64 | crypto_spake_shared_keys *shared_keys, 65 | const char *client_id, size_t client_id_len, 66 | const char *server_id, size_t server_id_len, 67 | const unsigned char response2[crypto_spake_RESPONSE2BYTES]); 68 | 69 | int crypto_spake_step4(crypto_spake_server_state *st, 70 | crypto_spake_shared_keys *shared_keys, 71 | const unsigned char response3[crypto_spake_RESPONSE3BYTES]); 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /test/test.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "crypto_spake.h" 8 | 9 | /* A client identifier (username, email address, public key...) */ 10 | #define CLIENT_ID "client" 11 | 12 | /* A server identifier (IP address, host name, public key...) */ 13 | #define SERVER_ID "server" 14 | 15 | int 16 | main(void) 17 | { 18 | int ret; 19 | 20 | if (sodium_init() != 0) { 21 | return 1; 22 | } 23 | 24 | 25 | /* 26 | * Computes a blob to be stored by the server, using the default 27 | * libsodium password hashing function (currently Argon2id) and 28 | * parameters. This operation can also be performed by the 29 | * client, with the result eventually sent to the server over a 30 | * secure channel. 31 | */ 32 | 33 | unsigned char stored_data[crypto_spake_STOREDBYTES]; 34 | 35 | ret = crypto_spake_server_store(stored_data, "password", 8, 36 | crypto_pwhash_OPSLIMIT_INTERACTIVE, 37 | crypto_pwhash_MEMLIMIT_INTERACTIVE); 38 | assert(ret == 0); 39 | 40 | 41 | /* 42 | * `public data` is a subset of the data stored on the server. 43 | * It only contains the parameters of the password hashing function. 44 | */ 45 | 46 | unsigned char public_data[crypto_spake_PUBLICDATABYTES]; 47 | crypto_spake_server_state server_st; 48 | 49 | ret = crypto_spake_step0(&server_st, public_data, stored_data); 50 | assert(ret == 0); 51 | 52 | 53 | /* 54 | * [CLIENT SIDE] 55 | * Computes a packet `response1` using `public_data` and the password. 56 | * This first packet has to be sent to the server. 57 | */ 58 | 59 | unsigned char response1[crypto_spake_RESPONSE1BYTES]; 60 | crypto_spake_client_state client_st; 61 | 62 | ret = crypto_spake_step1(&client_st, response1, public_data, "password", 8); 63 | assert(ret == 0); 64 | 65 | 66 | /* 67 | * [SERVER SIDE] 68 | * Processes `response1` received from the client. 69 | * Returns `response2` to be sent to the client. 70 | */ 71 | 72 | unsigned char response2[crypto_spake_RESPONSE2BYTES]; 73 | crypto_spake_shared_keys shared_keys_from_client; 74 | 75 | ret = crypto_spake_step2(&server_st, response2, CLIENT_ID, 76 | sizeof CLIENT_ID - 1, SERVER_ID, 77 | sizeof SERVER_ID - 1, stored_data, response1); 78 | assert(ret == 0); 79 | 80 | 81 | /* 82 | * [CLIENT SIDE] 83 | * Processes `response2` received from the server. 84 | * Returns a set of shared keys in `shared_keys_from_server`, 85 | * as well as `response3` to be sent to the server for validation. 86 | */ 87 | 88 | unsigned char response3[crypto_spake_RESPONSE3BYTES]; 89 | crypto_spake_shared_keys shared_keys_from_server; 90 | 91 | ret = crypto_spake_step3(&client_st, response3, &shared_keys_from_server, 92 | CLIENT_ID, sizeof CLIENT_ID - 1, SERVER_ID, 93 | sizeof SERVER_ID - 1, response2); 94 | assert(ret == 0); 95 | 96 | 97 | /* 98 | * [SERVER SIDE] 99 | * Processes `response3` received from the client. 100 | * After validation, returns a set of shared key (identical to the one 101 | * computed by the client) in `shared_keys_from_client`. 102 | */ 103 | 104 | ret = crypto_spake_step4(&server_st, &shared_keys_from_client, response3); 105 | assert(ret == 0); 106 | 107 | 108 | /* 109 | * Both parties now share two session keys. 110 | * The first one can be used for server->client communications, 111 | * and the second one can be used in the other direction. 112 | */ 113 | 114 | assert(memcmp(&shared_keys_from_client, &shared_keys_from_server, 115 | sizeof shared_keys_from_client) == 0); 116 | 117 | return 0; 118 | } 119 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SPAKE2+EE 2 | 3 | A SPAKE2+EE (SPAKE2+ Elligator Edition) implementation for libsodium 1.0.17+ 4 | 5 | ## Blurb 6 | 7 | SPAKE2 is a password-authenticated key agreement protocol, allowing two parties 8 | that share a password to securely authenticate each other and derive ephemeral 9 | session keys. It is secure and computationally efficient. 10 | 11 | This is an implementation of the 12 | [SPAKE2+EE](https://moderncrypto.org/mail-archive/curves/2015/000424.html) 13 | variant. It's slightly faster than the original SPAKE2 and has better security 14 | assumptions. It is also augmented, meaning that even if the credentials stored 15 | on the server ever get leaked, this would not be sufficient to log in. 16 | 17 | ## Usage 18 | 19 | The SPAKE2 protocol only requires one round trip to derive shared keys, and 20 | another round trip for mutual authentication. 21 | 22 | ```c 23 | /* A client identifier (username, email address, public key...) */ 24 | #define CLIENT_ID "client" 25 | 26 | /* A server identifier (IP address, host name, public key...) */ 27 | #define SERVER_ID "server" 28 | 29 | 30 | /* 31 | * Computes a blob to be stored by the server, using the default 32 | * libsodium password hashing function (currently Argon2id) and 33 | * parameters. This operation can also be performed by the 34 | * client, with the result eventually sent to the server over a 35 | * secure channel. 36 | */ 37 | 38 | unsigned char stored_data[crypto_spake_STOREDBYTES]; 39 | 40 | ret = crypto_spake_server_store(stored_data, "password", 8, 41 | crypto_pwhash_OPSLIMIT_INTERACTIVE, 42 | crypto_pwhash_MEMLIMIT_INTERACTIVE); 43 | assert(ret == 0); 44 | 45 | 46 | /* 47 | * `public data` is a subset of the data stored on the server. 48 | * It only contains the parameters of the password hashing function. 49 | */ 50 | 51 | unsigned char public_data[crypto_spake_PUBLICDATABYTES]; 52 | crypto_spake_server_state server_st; 53 | 54 | ret = crypto_spake_step0(&server_st, public_data, stored_data); 55 | assert(ret == 0); 56 | 57 | 58 | /* 59 | * [CLIENT SIDE] 60 | * Computes a packet `response1` using `public_data` and the password. 61 | * This first packet has to be sent to the server. 62 | */ 63 | 64 | unsigned char response1[crypto_spake_RESPONSE1BYTES]; 65 | crypto_spake_client_state client_st; 66 | 67 | ret = crypto_spake_step1(&client_st, response1, public_data, "password", 8); 68 | assert(ret == 0); 69 | 70 | 71 | /* 72 | * [SERVER SIDE] 73 | * Processes `response1` received from the client. 74 | * Returns `response2` to be sent to the client. 75 | */ 76 | 77 | unsigned char response2[crypto_spake_RESPONSE2BYTES]; 78 | crypto_spake_shared_keys shared_keys_from_client; 79 | 80 | ret = crypto_spake_step2(&server_st, response2, CLIENT_ID, 81 | sizeof CLIENT_ID - 1, SERVER_ID, 82 | sizeof SERVER_ID - 1, stored_data, response1); 83 | assert(ret == 0); 84 | 85 | 86 | /* 87 | * [CLIENT SIDE] 88 | * Processes `response2` received from the server. 89 | * Returns a set of shared keys in `shared_keys_from_server`, 90 | * as well as `response3` to be sent to the server for validation. 91 | */ 92 | 93 | unsigned char response3[crypto_spake_RESPONSE3BYTES]; 94 | crypto_spake_shared_keys shared_keys_from_server; 95 | 96 | ret = crypto_spake_step3(&client_st, response3, &shared_keys_from_server, 97 | CLIENT_ID, sizeof CLIENT_ID - 1, SERVER_ID, 98 | sizeof SERVER_ID - 1, response2); 99 | assert(ret == 0); 100 | 101 | 102 | /* 103 | * [SERVER SIDE] 104 | * Processes `response3` received from the client. 105 | * After validation, returns a set of shared key (identical to the one 106 | * computed by the client) in `shared_keys_from_client`. 107 | */ 108 | 109 | ret = crypto_spake_step4(&server_st, &shared_keys_from_client, response3); 110 | assert(ret == 0); 111 | 112 | 113 | /* 114 | * Both parties now share two session keys. 115 | * The first one can be used for server->client communications, 116 | * and the second one can be used in the other direction. 117 | */ 118 | 119 | assert(memcmp(&shared_keys_from_client, &shared_keys_from_server, 120 | sizeof shared_keys_from_client) == 0); 121 | ``` 122 | 123 | ## Caveats 124 | 125 | The documentation is terrible. 126 | 127 | The API could be way better. 128 | 129 | The implementation could be faster by using private functions. 130 | 131 | A version using libhydrogen would make more sense. 132 | 133 | But my Starbucks card balance is zero, so this is all you get for now. 134 | -------------------------------------------------------------------------------- /src/crypto_spake.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "pushpop.h" 9 | #include "crypto_spake.h" 10 | 11 | typedef struct spake_keys_ { 12 | unsigned char M[32]; 13 | unsigned char N[32]; 14 | unsigned char L[32]; 15 | unsigned char h_K[32]; 16 | unsigned char h_L[32]; 17 | } spake_keys; 18 | 19 | typedef struct spake_validators_ { 20 | unsigned char client_validator[32]; 21 | unsigned char server_validator[32]; 22 | } spake_validators; 23 | 24 | #define H_VERSION 0x01 25 | #define SER_VERSION 0x0001 26 | 27 | static void 28 | _random_scalar(unsigned char n[32]) 29 | { 30 | do { 31 | randombytes_buf(n, 32); 32 | n[0] &= 248; 33 | n[31] &= 127; 34 | } while (sodium_is_zero(n, 32)); 35 | } 36 | 37 | static int 38 | _create_keys(spake_keys *keys, unsigned char salt[crypto_pwhash_SALTBYTES], 39 | const char * const passwd, unsigned long long passwdlen, 40 | unsigned long long opslimit, size_t memlimit) 41 | { 42 | unsigned char h_MNKL[32 * 4]; 43 | unsigned char *h_M = &h_MNKL[32 * 0]; 44 | unsigned char *h_N = &h_MNKL[32 * 1]; 45 | unsigned char *h_K = &h_MNKL[32 * 2]; 46 | unsigned char *h_L = &h_MNKL[32 * 3]; 47 | 48 | if (crypto_pwhash(h_MNKL, sizeof h_MNKL, passwd, passwdlen, salt, 49 | opslimit, memlimit, crypto_pwhash_alg_default()) != 0) { 50 | return -1; 51 | } 52 | crypto_core_ed25519_from_uniform(keys->M, h_M); 53 | crypto_core_ed25519_from_uniform(keys->N, h_N); 54 | memcpy(keys->h_K, h_K, 32); 55 | memcpy(keys->h_L, h_L, 32); 56 | crypto_scalarmult_ed25519_base(keys->L, keys->h_L); 57 | 58 | return 0; 59 | } 60 | 61 | static int 62 | _shared_keys_and_validators(crypto_spake_shared_keys *shared_keys, 63 | spake_validators *validators, 64 | const char *client_id, size_t client_id_len, 65 | const char *server_id, size_t server_id_len, 66 | const unsigned char X[32], 67 | const unsigned char Y[32], 68 | const unsigned char Z[32], 69 | const unsigned char h_K[32], 70 | const unsigned char V[32]) 71 | { 72 | crypto_generichash_state hst; 73 | unsigned char k0[crypto_kdf_KEYBYTES]; 74 | unsigned char len; 75 | unsigned char h_version; 76 | 77 | if (client_id_len > 255 || server_id_len > 255) { 78 | return -1; 79 | } 80 | crypto_generichash_init(&hst, NULL, 0, sizeof k0); 81 | 82 | h_version = H_VERSION; 83 | crypto_generichash_update(&hst, &h_version, 1); 84 | 85 | len = (unsigned char) client_id_len; 86 | crypto_generichash_update(&hst, &len, 1); 87 | crypto_generichash_update(&hst, (const unsigned char *) client_id, len); 88 | 89 | len = (unsigned char) server_id_len; 90 | crypto_generichash_update(&hst, &len, 1); 91 | crypto_generichash_update(&hst, (const unsigned char *) server_id, len); 92 | 93 | len = 32; 94 | crypto_generichash_update(&hst, X, len); 95 | crypto_generichash_update(&hst, Y, len); 96 | crypto_generichash_update(&hst, Z, len); 97 | crypto_generichash_update(&hst, h_K, len); 98 | crypto_generichash_update(&hst, V, len); 99 | 100 | crypto_generichash_final(&hst, k0, sizeof k0); 101 | 102 | crypto_kdf_derive_from_key(shared_keys->client_sk, 103 | crypto_spake_SHAREDKEYBYTES, 0, "PAKE2+EE", k0); 104 | crypto_kdf_derive_from_key(shared_keys->server_sk, 105 | crypto_spake_SHAREDKEYBYTES, 1, "PAKE2+EE", k0); 106 | crypto_kdf_derive_from_key(validators->client_validator, 107 | 32, 2, "PAKE2+EE", k0); 108 | crypto_kdf_derive_from_key(validators->server_validator, 109 | 32, 3, "PAKE2+EE", k0); 110 | 111 | sodium_memzero(k0, sizeof k0); 112 | 113 | return 0; 114 | } 115 | 116 | int 117 | crypto_spake_server_store(unsigned char stored_data[crypto_spake_STOREDBYTES], 118 | const char * const passwd, 119 | unsigned long long passwdlen, 120 | unsigned long long opslimit, size_t memlimit) 121 | { 122 | spake_keys keys; 123 | unsigned char salt[crypto_pwhash_SALTBYTES]; 124 | size_t i; 125 | 126 | randombytes_buf(salt, sizeof salt); 127 | if (_create_keys(&keys, salt, passwd, passwdlen, opslimit, memlimit) != 0) { 128 | return -1; 129 | } 130 | i = 0; 131 | _push16 (stored_data, &i, SER_VERSION); 132 | _push16 (stored_data, &i, (uint16_t) crypto_pwhash_alg_default()); 133 | _push64 (stored_data, &i, (uint64_t) opslimit); 134 | _push64 (stored_data, &i, (uint64_t) memlimit); 135 | _push128(stored_data, &i, salt); 136 | _push256(stored_data, &i, keys.M); 137 | _push256(stored_data, &i, keys.N); 138 | _push256(stored_data, &i, keys.h_K); 139 | _push256(stored_data, &i, keys.L); 140 | assert(i == crypto_spake_STOREDBYTES); 141 | 142 | return 0; 143 | } 144 | 145 | int 146 | crypto_spake_validate_public_data(const unsigned char public_data[crypto_spake_PUBLICDATABYTES], 147 | const int expected_alg, 148 | unsigned long long expected_opslimit, 149 | unsigned long long expected_memlimit) 150 | { 151 | int alg; 152 | unsigned long long opslimit; 153 | size_t memlimit; 154 | size_t i; 155 | uint16_t v16; 156 | uint64_t v64; 157 | 158 | i = 0; 159 | _pop16 (&v16, public_data, &i); 160 | _pop16 (&v16, public_data, &i); /* alg */ 161 | alg = (int) v16; 162 | _pop64 (&v64, public_data, &i); /* opslimit */ 163 | opslimit = (unsigned long long) v64; 164 | _pop64 (&v64, public_data, &i); /* memlimit */ 165 | memlimit = (size_t) v64; 166 | 167 | if (alg != expected_alg || 168 | opslimit != expected_opslimit || memlimit != expected_memlimit) { 169 | return -1; 170 | } 171 | return 0; 172 | } 173 | 174 | int 175 | crypto_spake_step0_dummy(crypto_spake_server_state *st, 176 | unsigned char public_data[crypto_spake_PUBLICDATABYTES], 177 | const char *client_id, size_t client_id_len, 178 | const char *server_id, size_t server_id_len, 179 | unsigned long long opslimit, size_t memlimit, 180 | const unsigned char key[crypto_spake_DUMMYKEYBYTES]) 181 | { 182 | crypto_generichash_state hst; 183 | unsigned char salt[crypto_pwhash_SALTBYTES]; 184 | size_t i; 185 | unsigned char len; 186 | 187 | memset(st, 0, sizeof *st); 188 | if (client_id_len > 255 || server_id_len > 255) { 189 | return -1; 190 | } 191 | crypto_generichash_init(&hst, key, crypto_spake_DUMMYKEYBYTES, sizeof salt); 192 | len = (unsigned char) client_id_len; 193 | crypto_generichash_update(&hst, &len, 1); 194 | crypto_generichash_update(&hst, (const unsigned char *) client_id, len); 195 | len = (unsigned char) server_id_len; 196 | crypto_generichash_update(&hst, &len, 1); 197 | crypto_generichash_update(&hst, (const unsigned char *) server_id, len); 198 | 199 | i = 0; 200 | _push16 (public_data, &i, SER_VERSION); 201 | _push16 (public_data, &i, (uint16_t) crypto_pwhash_alg_default()); /* alg */ 202 | _push64 (public_data, &i, (uint64_t) opslimit); /* opslimit */ 203 | _push64 (public_data, &i, (uint64_t) memlimit); /* memlimit */ 204 | 205 | crypto_generichash_update(&hst, public_data, i); 206 | crypto_generichash_final(&hst, salt, sizeof salt); 207 | 208 | _push128(public_data, &i, salt); /* salt */ 209 | assert(i == crypto_spake_PUBLICDATABYTES); 210 | 211 | return 0; 212 | } 213 | 214 | int 215 | crypto_spake_step0(crypto_spake_server_state *st, 216 | unsigned char public_data[crypto_spake_PUBLICDATABYTES], 217 | const unsigned char stored_data[crypto_spake_STOREDBYTES]) 218 | { 219 | unsigned char salt[crypto_pwhash_SALTBYTES]; 220 | size_t i, j; 221 | uint16_t v16; 222 | uint64_t v64; 223 | 224 | memset(st, 0, sizeof *st); 225 | i = 0; 226 | j = 0; 227 | _pop16 (&v16, stored_data, &i); /* version */ 228 | if (v16 != SER_VERSION) { 229 | return -1; 230 | } 231 | _push16(public_data, &j, v16); 232 | _pop16 (&v16, stored_data, &i); /* alg */ 233 | _push16(public_data, &j, v16); 234 | _pop64 (&v64, stored_data, &i); /* opslimit */ 235 | _push64(public_data, &j, v64); 236 | _pop64 (&v64, stored_data, &i); /* memlimit */ 237 | _push64(public_data, &j, v64); 238 | _pop128(salt, stored_data, &i); /* salt */ 239 | _push128(public_data, &j, salt); 240 | assert(j == crypto_spake_PUBLICDATABYTES); 241 | 242 | return 0; 243 | } 244 | 245 | int 246 | crypto_spake_step1(crypto_spake_client_state *st, 247 | unsigned char response1[crypto_spake_RESPONSE1BYTES], 248 | const unsigned char public_data[crypto_spake_PUBLICDATABYTES], 249 | const char * const passwd, unsigned long long passwdlen) 250 | { 251 | spake_keys keys; 252 | unsigned char gx[32]; 253 | unsigned char salt[crypto_pwhash_SALTBYTES]; 254 | unsigned char x[32]; 255 | unsigned char *X = response1; 256 | int alg; 257 | unsigned long long opslimit; 258 | size_t memlimit; 259 | size_t i; 260 | uint16_t v16; 261 | uint64_t v64; 262 | 263 | memset(st, 0, sizeof *st); 264 | i = 0; 265 | _pop16 (&v16, public_data, &i); 266 | if (v16 != SER_VERSION) { 267 | return -1; 268 | } 269 | _pop16 (&v16, public_data, &i); /* alg */ 270 | alg = (int) v16; 271 | _pop64 (&v64, public_data, &i); /* opslimit */ 272 | opslimit = (unsigned long long) v64; 273 | _pop64 (&v64, public_data, &i); /* memlimit */ 274 | memlimit = (size_t) v64; 275 | _pop128(salt, public_data, &i); /* salt */ 276 | if (_create_keys(&keys, salt, passwd, passwdlen, opslimit, memlimit) != 0) { 277 | sodium_memzero(st, sizeof *st); 278 | return -1; 279 | } 280 | _random_scalar(x); 281 | crypto_scalarmult_ed25519_base_noclamp(gx, x); 282 | crypto_core_ed25519_add(X, gx, keys.M); 283 | 284 | memcpy(st->h_K, keys.h_K, 32); 285 | memcpy(st->h_L, keys.h_L, 32); 286 | memcpy(st->N, keys.N, 32); 287 | memcpy(st->x, x, 32); 288 | memcpy(st->X, X, 32); 289 | 290 | return 0; 291 | } 292 | 293 | int 294 | crypto_spake_step2(crypto_spake_server_state *st, 295 | unsigned char response2[crypto_spake_RESPONSE2BYTES], 296 | const char *client_id, size_t client_id_len, 297 | const char *server_id, size_t server_id_len, 298 | const unsigned char stored_data[crypto_spake_STOREDBYTES], 299 | const unsigned char response1[crypto_spake_RESPONSE1BYTES]) 300 | { 301 | spake_validators validators; 302 | spake_keys keys; 303 | unsigned char V[32]; 304 | unsigned char Z[32]; 305 | unsigned char gx[32]; 306 | unsigned char gy[32]; 307 | unsigned char salt[crypto_pwhash_SALTBYTES]; 308 | unsigned char y[32]; 309 | unsigned char *Y = response2; 310 | unsigned char *client_validator = response2 + 32; 311 | const unsigned char *X = response1; 312 | size_t i; 313 | uint16_t v16; 314 | uint64_t v64; 315 | 316 | i = 0; 317 | _pop16 (&v16, stored_data, &i); /* version */ 318 | if (v16 != SER_VERSION) { 319 | return -1; 320 | } 321 | _pop16 (&v16, stored_data, &i); /* alg */ 322 | _pop64 (&v64, stored_data, &i); /* opslimit */ 323 | _pop64 (&v64, stored_data, &i); /* memlimit */ 324 | _pop128(salt, stored_data, &i); /* salt */ 325 | _pop256(keys.M, stored_data, &i); 326 | _pop256(keys.N, stored_data, &i); 327 | _pop256(keys.h_K, stored_data, &i); 328 | _pop256(keys.L, stored_data, &i); 329 | 330 | _random_scalar(y); 331 | crypto_scalarmult_ed25519_base_noclamp(gy, y); 332 | crypto_core_ed25519_add(Y, gy, keys.N); 333 | 334 | crypto_core_ed25519_sub(gx, X, keys.M); 335 | if (crypto_scalarmult_ed25519_noclamp(Z, y, gx) != 0 || 336 | crypto_scalarmult_ed25519_noclamp(V, y, keys.L) != 0 || 337 | _shared_keys_and_validators(&st->shared_keys, &validators, client_id, 338 | client_id_len, server_id, server_id_len, 339 | X, Y, Z, keys.h_K, V) != 0) { 340 | sodium_memzero(st, sizeof *st); 341 | return -1; 342 | } 343 | memcpy(client_validator, validators.client_validator, 32); 344 | memcpy(st->server_validator, validators.server_validator, 32); 345 | 346 | return 0; 347 | } 348 | 349 | /* C -> S */ 350 | 351 | int 352 | crypto_spake_step3(crypto_spake_client_state *st, 353 | unsigned char response3[crypto_spake_RESPONSE3BYTES], 354 | crypto_spake_shared_keys *shared_keys, 355 | const char *client_id, size_t client_id_len, 356 | const char *server_id, size_t server_id_len, 357 | const unsigned char response2[crypto_spake_RESPONSE2BYTES]) 358 | { 359 | spake_validators validators; 360 | unsigned char V[32]; 361 | unsigned char Z[32]; 362 | unsigned char gy[32]; 363 | unsigned char *server_validator = response3; 364 | const unsigned char *Y = response2; 365 | const unsigned char *client_validator = response2 + 32; 366 | 367 | crypto_core_ed25519_sub(gy, Y, st->N); 368 | if (crypto_scalarmult_ed25519_noclamp(Z, st->x, gy) != 0 || 369 | crypto_scalarmult_ed25519(V, st->h_L, gy) != 0 || 370 | _shared_keys_and_validators(shared_keys, &validators, client_id, 371 | client_id_len, server_id, server_id_len, 372 | st->X, Y, Z, st->h_K, V) != 0 || 373 | sodium_memcmp(client_validator, validators.client_validator, 32) != 0) { 374 | sodium_memzero(st, sizeof *st); 375 | return -1; 376 | } 377 | memcpy(server_validator, validators.server_validator, 32); 378 | sodium_memzero(st, sizeof *st); 379 | 380 | return 0; 381 | } 382 | 383 | int 384 | crypto_spake_step4(crypto_spake_server_state *st, 385 | crypto_spake_shared_keys *shared_keys, 386 | const unsigned char response3[crypto_spake_RESPONSE3BYTES]) 387 | { 388 | const unsigned char *server_validator = response3; 389 | 390 | if (sodium_memcmp(server_validator, st->server_validator, 32) != 0) { 391 | sodium_memzero(st, sizeof *st); 392 | return -1; 393 | } 394 | memcpy(shared_keys, &st->shared_keys, sizeof *shared_keys); 395 | sodium_memzero(st, sizeof *st); 396 | 397 | return 0; 398 | } 399 | --------------------------------------------------------------------------------