2017-06-19 21:52:01 +02:00
|
|
|
/*
|
|
|
|
*
|
2020-11-27 22:46:38 +01:00
|
|
|
* The Secure Remote Password 6a implementation is adapted from:
|
2017-06-19 21:52:01 +02:00
|
|
|
* - Tom Cocagne
|
|
|
|
* <https://github.com/cocagne/csrp>
|
|
|
|
*
|
|
|
|
* The MIT License (MIT)
|
2020-11-27 22:46:38 +01:00
|
|
|
*
|
2017-06-19 21:52:01 +02:00
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
|
|
* this software and associated documentation files (the "Software"), to deal in
|
|
|
|
* the Software without restriction, including without limitation the rights to
|
|
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
|
|
* of the Software, and to permit persons to whom the Software is furnished to do
|
|
|
|
* so, subject to the following conditions:
|
2020-11-27 22:46:38 +01:00
|
|
|
*
|
2017-06-19 21:52:01 +02:00
|
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
|
|
* copies or substantial portions of the Software.
|
2020-11-27 22:46:38 +01:00
|
|
|
*
|
2017-06-19 21:52:01 +02:00
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
* SOFTWARE.
|
2020-11-27 22:46:38 +01:00
|
|
|
*
|
2017-06-19 21:52:01 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <plist/plist.h>
|
|
|
|
#include <sodium.h>
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
#include "pair-internal.h"
|
2017-06-19 21:52:01 +02:00
|
|
|
|
|
|
|
/* ----------------------------- DEFINES ETC ------------------------------- */
|
|
|
|
|
|
|
|
#define USERNAME "12:34:56:78:90:AB"
|
|
|
|
#define EPK_LENGTH 32
|
|
|
|
#define AUTHTAG_LENGTH 16
|
|
|
|
#define AES_SETUP_KEY "Pair-Setup-AES-Key"
|
|
|
|
#define AES_SETUP_IV "Pair-Setup-AES-IV"
|
|
|
|
#define AES_VERIFY_KEY "Pair-Verify-AES-Key"
|
|
|
|
#define AES_VERIFY_IV "Pair-Verify-AES-IV"
|
|
|
|
|
|
|
|
|
|
|
|
/* ---------------------------------- SRP ---------------------------------- */
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
SRP_NG_2048,
|
|
|
|
SRP_NG_CUSTOM
|
|
|
|
} SRP_NGType;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
bnum N;
|
|
|
|
bnum g;
|
|
|
|
} NGConstant;
|
|
|
|
|
|
|
|
struct SRPUser
|
|
|
|
{
|
|
|
|
enum hash_alg alg;
|
|
|
|
NGConstant *ng;
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
bnum a;
|
|
|
|
bnum A;
|
|
|
|
bnum S;
|
|
|
|
|
|
|
|
const unsigned char *bytes_A;
|
2020-11-27 22:46:38 +01:00
|
|
|
int authenticated;
|
|
|
|
|
|
|
|
char *username;
|
|
|
|
unsigned char *password;
|
|
|
|
int password_len;
|
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
unsigned char M [SHA512_DIGEST_LENGTH];
|
|
|
|
unsigned char H_AMK [SHA512_DIGEST_LENGTH];
|
|
|
|
unsigned char session_key [2 * SHA512_DIGEST_LENGTH]; // See hash_session_key()
|
|
|
|
int session_key_len;
|
|
|
|
};
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
struct NGHex
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
const char *n_hex;
|
|
|
|
const char *g_hex;
|
|
|
|
};
|
|
|
|
|
|
|
|
// We only need 2048 right now, but keep the array in case we want to add others later
|
|
|
|
// All constants here were pulled from Appendix A of RFC 5054
|
|
|
|
static struct NGHex global_Ng_constants[] =
|
|
|
|
{
|
|
|
|
{ /* 2048 */
|
|
|
|
"AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4"
|
|
|
|
"A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60"
|
|
|
|
"95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF"
|
|
|
|
"747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907"
|
|
|
|
"8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861"
|
|
|
|
"60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB"
|
|
|
|
"FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73",
|
|
|
|
"2"
|
|
|
|
},
|
|
|
|
{0,0} /* null sentinel */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static NGConstant *
|
|
|
|
new_ng(SRP_NGType ng_type, const char *n_hex, const char *g_hex)
|
|
|
|
{
|
|
|
|
NGConstant *ng = calloc(1, sizeof(NGConstant));
|
|
|
|
|
|
|
|
if ( ng_type != SRP_NG_CUSTOM )
|
|
|
|
{
|
|
|
|
n_hex = global_Ng_constants[ ng_type ].n_hex;
|
|
|
|
g_hex = global_Ng_constants[ ng_type ].g_hex;
|
|
|
|
}
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
bnum_hex2bn(ng->N, n_hex);
|
|
|
|
bnum_hex2bn(ng->g, g_hex);
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
return ng;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
free_ng(NGConstant * ng)
|
|
|
|
{
|
|
|
|
if (!ng)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bnum_free(ng->N);
|
|
|
|
bnum_free(ng->g);
|
|
|
|
free(ng);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bnum
|
|
|
|
calculate_x(enum hash_alg alg, const bnum salt, const char *username, const unsigned char *password, int password_len)
|
|
|
|
{
|
|
|
|
unsigned char ucp_hash[SHA512_DIGEST_LENGTH];
|
|
|
|
HashCTX ctx;
|
|
|
|
|
|
|
|
hash_init( alg, &ctx );
|
|
|
|
hash_update( alg, &ctx, username, strlen(username) );
|
|
|
|
hash_update( alg, &ctx, ":", 1 );
|
|
|
|
hash_update( alg, &ctx, password, password_len );
|
|
|
|
hash_final( alg, &ctx, ucp_hash );
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
return H_ns( alg, salt, ucp_hash, hash_length(alg) );
|
2017-06-19 21:52:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
hash_session_key(enum hash_alg alg, const bnum n, unsigned char *dest)
|
|
|
|
{
|
|
|
|
int nbytes = bnum_num_bytes(n);
|
|
|
|
unsigned char *bin = malloc(nbytes);
|
|
|
|
unsigned char fourbytes[4] = { 0 }; // Only God knows the reason for this, and perhaps some poor soul at Apple
|
2017-06-20 22:07:49 +02:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
bnum_bn2bin(n, bin, nbytes);
|
|
|
|
|
|
|
|
hash_ab(alg, dest, bin, nbytes, fourbytes, sizeof(fourbytes));
|
|
|
|
|
|
|
|
fourbytes[3] = 1; // Again, only ...
|
|
|
|
|
|
|
|
hash_ab(alg, dest + hash_length(alg), bin, nbytes, fourbytes, sizeof(fourbytes));
|
|
|
|
|
|
|
|
free(bin);
|
|
|
|
|
|
|
|
return (2 * hash_length(alg));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
calculate_M(enum hash_alg alg, NGConstant *ng, unsigned char *dest, const char *I, const bnum s,
|
|
|
|
const bnum A, const bnum B, const unsigned char *K, int K_len)
|
|
|
|
{
|
|
|
|
unsigned char H_N[ SHA512_DIGEST_LENGTH ];
|
|
|
|
unsigned char H_g[ SHA512_DIGEST_LENGTH ];
|
|
|
|
unsigned char H_I[ SHA512_DIGEST_LENGTH ];
|
|
|
|
unsigned char H_xor[ SHA512_DIGEST_LENGTH ];
|
|
|
|
HashCTX ctx;
|
|
|
|
int i = 0;
|
|
|
|
int hash_len = hash_length(alg);
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
hash_num( alg, ng->N, H_N );
|
|
|
|
hash_num( alg, ng->g, H_g );
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
hash(alg, (const unsigned char *)I, strlen(I), H_I);
|
2020-11-27 22:46:38 +01:00
|
|
|
|
|
|
|
for (i=0; i < hash_len; i++ )
|
2017-06-19 21:52:01 +02:00
|
|
|
H_xor[i] = H_N[i] ^ H_g[i];
|
|
|
|
|
|
|
|
hash_init( alg, &ctx );
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
hash_update( alg, &ctx, H_xor, hash_len );
|
|
|
|
hash_update( alg, &ctx, H_I, hash_len );
|
|
|
|
update_hash_n( alg, &ctx, s );
|
|
|
|
update_hash_n( alg, &ctx, A );
|
|
|
|
update_hash_n( alg, &ctx, B );
|
|
|
|
hash_update( alg, &ctx, K, K_len );
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
hash_final( alg, &ctx, dest );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
calculate_H_AMK(enum hash_alg alg, unsigned char *dest, const bnum A, const unsigned char * M, const unsigned char * K, int K_len)
|
|
|
|
{
|
|
|
|
HashCTX ctx;
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
hash_init( alg, &ctx );
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
update_hash_n( alg, &ctx, A );
|
|
|
|
hash_update( alg, &ctx, M, hash_length(alg) );
|
|
|
|
hash_update( alg, &ctx, K, K_len );
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
hash_final( alg, &ctx, dest );
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct SRPUser *
|
2020-11-27 22:46:38 +01:00
|
|
|
srp_user_new(enum hash_alg alg, SRP_NGType ng_type, const char *username,
|
2017-06-19 21:52:01 +02:00
|
|
|
const unsigned char *bytes_password, int len_password,
|
|
|
|
const char *n_hex, const char *g_hex)
|
|
|
|
{
|
|
|
|
struct SRPUser *usr = calloc(1, sizeof(struct SRPUser));
|
|
|
|
int ulen = strlen(username) + 1;
|
|
|
|
|
|
|
|
if (!usr)
|
|
|
|
goto err_exit;
|
|
|
|
|
|
|
|
usr->alg = alg;
|
|
|
|
usr->ng = new_ng( ng_type, n_hex, g_hex );
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
bnum_new(usr->a);
|
|
|
|
bnum_new(usr->A);
|
|
|
|
bnum_new(usr->S);
|
|
|
|
|
|
|
|
if (!usr->ng || !usr->a || !usr->A || !usr->S)
|
|
|
|
goto err_exit;
|
2020-11-27 22:46:38 +01:00
|
|
|
|
|
|
|
usr->username = malloc(ulen);
|
|
|
|
usr->password = malloc(len_password);
|
2017-06-19 21:52:01 +02:00
|
|
|
usr->password_len = len_password;
|
|
|
|
|
|
|
|
if (!usr->username || !usr->password)
|
|
|
|
goto err_exit;
|
2020-11-27 22:46:38 +01:00
|
|
|
|
|
|
|
memcpy(usr->username, username, ulen);
|
|
|
|
memcpy(usr->password, bytes_password, len_password);
|
2017-06-19 21:52:01 +02:00
|
|
|
|
|
|
|
usr->authenticated = 0;
|
|
|
|
usr->bytes_A = 0;
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
return usr;
|
|
|
|
|
|
|
|
err_exit:
|
|
|
|
if (!usr)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
bnum_free(usr->a);
|
|
|
|
bnum_free(usr->A);
|
|
|
|
bnum_free(usr->S);
|
2020-11-27 22:46:38 +01:00
|
|
|
|
|
|
|
free(usr->username);
|
2017-06-19 21:52:01 +02:00
|
|
|
if (usr->password)
|
|
|
|
{
|
2020-11-27 22:46:38 +01:00
|
|
|
memset(usr->password, 0, usr->password_len);
|
|
|
|
free(usr->password);
|
2017-06-19 21:52:01 +02:00
|
|
|
}
|
|
|
|
free(usr);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-11-27 22:46:38 +01:00
|
|
|
srp_user_free(struct SRPUser *usr)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
if(!usr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bnum_free(usr->a);
|
|
|
|
bnum_free(usr->A);
|
|
|
|
bnum_free(usr->S);
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
free_ng(usr->ng);
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
memset(usr->password, 0, usr->password_len);
|
|
|
|
|
|
|
|
free(usr->username);
|
|
|
|
free(usr->password);
|
|
|
|
free((char *)usr->bytes_A);
|
2017-06-19 21:52:01 +02:00
|
|
|
|
|
|
|
memset(usr, 0, sizeof(*usr));
|
|
|
|
free(usr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
srp_user_is_authenticated(struct SRPUser *usr)
|
|
|
|
{
|
|
|
|
return usr->authenticated;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const unsigned char *
|
|
|
|
srp_user_get_session_key(struct SRPUser *usr, int *key_length)
|
|
|
|
{
|
|
|
|
if (key_length)
|
|
|
|
*key_length = usr->session_key_len;
|
|
|
|
return usr->session_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Output: username, bytes_A, len_A */
|
|
|
|
static void
|
|
|
|
srp_user_start_authentication(struct SRPUser *usr, const char **username,
|
|
|
|
const unsigned char **bytes_A, int *len_A)
|
|
|
|
{
|
|
|
|
bnum_random(usr->a, 256);
|
|
|
|
bnum_modexp(usr->A, usr->ng->g, usr->a, usr->ng->N);
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
*len_A = bnum_num_bytes(usr->A);
|
|
|
|
*bytes_A = malloc(*len_A);
|
|
|
|
|
|
|
|
if (!*bytes_A)
|
|
|
|
{
|
|
|
|
*len_A = 0;
|
|
|
|
*bytes_A = 0;
|
|
|
|
*username = 0;
|
|
|
|
return;
|
|
|
|
}
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
bnum_bn2bin(usr->A, (unsigned char *) *bytes_A, *len_A);
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
usr->bytes_A = *bytes_A;
|
|
|
|
*username = usr->username;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Output: bytes_M. Buffer length is SHA512_DIGEST_LENGTH */
|
|
|
|
static void
|
|
|
|
srp_user_process_challenge(struct SRPUser *usr, const unsigned char *bytes_s, int len_s,
|
|
|
|
const unsigned char *bytes_B, int len_B,
|
|
|
|
const unsigned char **bytes_M, int *len_M )
|
|
|
|
{
|
|
|
|
bnum s, B, k, v;
|
|
|
|
bnum tmp1, tmp2, tmp3;
|
|
|
|
bnum u, x;
|
|
|
|
|
|
|
|
*len_M = 0;
|
|
|
|
*bytes_M = 0;
|
|
|
|
|
|
|
|
bnum_bin2bn(s, bytes_s, len_s);
|
|
|
|
bnum_bin2bn(B, bytes_B, len_B);
|
|
|
|
k = H_nn_pad(usr->alg, usr->ng->N, usr->ng->g);
|
|
|
|
bnum_new(v);
|
|
|
|
bnum_new(tmp1);
|
|
|
|
bnum_new(tmp2);
|
|
|
|
bnum_new(tmp3);
|
|
|
|
|
|
|
|
if (!s || !B || !k || !v || !tmp1 || !tmp2 || !tmp3)
|
|
|
|
goto cleanup1;
|
|
|
|
|
|
|
|
u = H_nn_pad(usr->alg, usr->A, B);
|
|
|
|
x = calculate_x(usr->alg, s, usr->username, usr->password, usr->password_len);
|
|
|
|
if (!u || !x)
|
|
|
|
goto cleanup2;
|
|
|
|
|
|
|
|
// SRP-6a safety check
|
|
|
|
if (!bnum_is_zero(B) && !bnum_is_zero(u))
|
|
|
|
{
|
|
|
|
bnum_modexp(v, usr->ng->g, x, usr->ng->N);
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
// S = (B - k*(g^x)) ^ (a + ux)
|
|
|
|
bnum_mul(tmp1, u, x);
|
|
|
|
bnum_add(tmp2, usr->a, tmp1); // tmp2 = (a + ux)
|
|
|
|
bnum_modexp(tmp1, usr->ng->g, x, usr->ng->N);
|
|
|
|
bnum_mul(tmp3, k, tmp1); // tmp3 = k*(g^x)
|
|
|
|
bnum_sub(tmp1, B, tmp3); // tmp1 = (B - K*(g^x))
|
|
|
|
bnum_modexp(usr->S, tmp1, tmp2, usr->ng->N);
|
|
|
|
|
|
|
|
usr->session_key_len = hash_session_key(usr->alg, usr->S, usr->session_key);
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
calculate_M(usr->alg, usr->ng, usr->M, usr->username, s, usr->A, B, usr->session_key, usr->session_key_len);
|
|
|
|
calculate_H_AMK(usr->alg, usr->H_AMK, usr->A, usr->M, usr->session_key, usr->session_key_len);
|
2020-11-27 22:46:38 +01:00
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
*bytes_M = usr->M;
|
2020-11-27 22:46:38 +01:00
|
|
|
if (len_M)
|
|
|
|
*len_M = hash_length(usr->alg);
|
2017-06-19 21:52:01 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*bytes_M = NULL;
|
2020-11-27 22:46:38 +01:00
|
|
|
if (len_M)
|
|
|
|
*len_M = 0;
|
2017-06-19 21:52:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
cleanup2:
|
|
|
|
bnum_free(x);
|
|
|
|
bnum_free(u);
|
|
|
|
cleanup1:
|
|
|
|
bnum_free(tmp3);
|
|
|
|
bnum_free(tmp2);
|
|
|
|
bnum_free(tmp1);
|
|
|
|
bnum_free(v);
|
|
|
|
bnum_free(k);
|
|
|
|
bnum_free(B);
|
|
|
|
bnum_free(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
srp_user_verify_session(struct SRPUser *usr, const unsigned char *bytes_HAMK)
|
|
|
|
{
|
|
|
|
if (memcmp(usr->H_AMK, bytes_HAMK, hash_length(usr->alg)) == 0)
|
|
|
|
usr->authenticated = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* -------------------------------- HELPERS -------------------------------- */
|
|
|
|
|
|
|
|
static int
|
|
|
|
encrypt_gcm(unsigned char *ciphertext, int ciphertext_len, unsigned char *tag, unsigned char *plaintext, int plaintext_len, unsigned char *key, unsigned char *iv, const char **errmsg)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_OPENSSL
|
|
|
|
EVP_CIPHER_CTX *ctx;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
*errmsg = NULL;
|
|
|
|
|
|
|
|
if ( !(ctx = EVP_CIPHER_CTX_new()) ||
|
|
|
|
(EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL) != 1) ||
|
|
|
|
(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL) != 1) ||
|
|
|
|
(EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv) != 1) )
|
|
|
|
{
|
|
|
|
*errmsg = "Error initialising AES 128 GCM encryption";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len) != 1)
|
|
|
|
{
|
|
|
|
*errmsg = "Error GCM encrypting";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len > ciphertext_len)
|
|
|
|
{
|
|
|
|
*errmsg = "Bug! Buffer overflow";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (EVP_EncryptFinal_ex(ctx, ciphertext + len, &len) != 1)
|
|
|
|
{
|
|
|
|
*errmsg = "Error finalising GCM encryption";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, AUTHTAG_LENGTH, tag) != 1)
|
|
|
|
{
|
|
|
|
*errmsg = "Error getting authtag";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
return -1;
|
|
|
|
#elif CONFIG_GCRYPT
|
|
|
|
gcry_cipher_hd_t hd;
|
2018-02-22 22:36:50 +01:00
|
|
|
gcry_error_t err;
|
2017-06-19 21:52:01 +02:00
|
|
|
|
2018-02-22 22:36:50 +01:00
|
|
|
err = gcry_cipher_open(&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, 0);
|
|
|
|
if (err)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
*errmsg = "Error initialising AES 128 GCM encryption";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-02-22 22:36:50 +01:00
|
|
|
err = gcry_cipher_setkey(hd, key, gcry_cipher_get_algo_keylen(GCRY_CIPHER_AES128));
|
|
|
|
if (err)
|
|
|
|
{
|
|
|
|
*errmsg = "Could not set key for AES 128 GCM";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = gcry_cipher_setiv(hd, iv, gcry_cipher_get_algo_blklen(GCRY_CIPHER_AES128));
|
|
|
|
if (err)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
2018-02-22 22:36:50 +01:00
|
|
|
*errmsg = "Could not set iv for AES 128 GCM";
|
2017-06-19 21:52:01 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-02-22 22:36:50 +01:00
|
|
|
err = gcry_cipher_encrypt(hd, ciphertext, ciphertext_len, plaintext, plaintext_len);
|
|
|
|
if (err)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
*errmsg = "Error GCM encrypting";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-02-22 22:36:50 +01:00
|
|
|
err = gcry_cipher_gettag(hd, tag, AUTHTAG_LENGTH);
|
|
|
|
if (err)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
*errmsg = "Error getting authtag";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
gcry_cipher_close(hd);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
gcry_cipher_close(hd);
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
encrypt_ctr(unsigned char *ciphertext, int ciphertext_len,
|
|
|
|
unsigned char *plaintext1, int plaintext1_len, unsigned char *plaintext2, int plaintext2_len,
|
|
|
|
unsigned char *key, unsigned char *iv, const char **errmsg)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_OPENSSL
|
|
|
|
EVP_CIPHER_CTX *ctx;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
*errmsg = NULL;
|
|
|
|
|
|
|
|
if ( !(ctx = EVP_CIPHER_CTX_new()) || (EVP_EncryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, key, iv) != 1) )
|
|
|
|
{
|
|
|
|
*errmsg = "Error initialising AES 128 CTR encryption";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( (EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext1, plaintext1_len) != 1) ||
|
|
|
|
(EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext2, plaintext2_len) != 1) )
|
|
|
|
{
|
|
|
|
*errmsg = "Error CTR encrypting";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (EVP_EncryptFinal_ex(ctx, ciphertext + len, &len) != 1)
|
|
|
|
{
|
|
|
|
*errmsg = "Error finalising encryption";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
|
|
return -1;
|
|
|
|
#elif CONFIG_GCRYPT
|
|
|
|
gcry_cipher_hd_t hd;
|
2018-02-22 22:36:50 +01:00
|
|
|
gcry_error_t err;
|
2017-06-19 21:52:01 +02:00
|
|
|
|
2018-02-22 22:36:50 +01:00
|
|
|
err = gcry_cipher_open(&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, 0);
|
|
|
|
if (err)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
*errmsg = "Error initialising AES 128 CTR encryption";
|
|
|
|
return -1;
|
|
|
|
}
|
2018-02-22 22:36:50 +01:00
|
|
|
err = gcry_cipher_setkey(hd, key, gcry_cipher_get_algo_keylen(GCRY_CIPHER_AES128));
|
|
|
|
if (err)
|
|
|
|
{
|
|
|
|
*errmsg = "Could not set key for AES 128 CTR";
|
|
|
|
goto error;
|
|
|
|
}
|
2017-06-19 21:52:01 +02:00
|
|
|
|
2018-02-22 22:36:50 +01:00
|
|
|
err = gcry_cipher_setctr(hd, iv, gcry_cipher_get_algo_blklen(GCRY_CIPHER_AES128));
|
|
|
|
if (err)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
2018-02-22 22:36:50 +01:00
|
|
|
*errmsg = "Could not set iv for AES 128 CTR";
|
2017-06-19 21:52:01 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2018-02-22 22:36:50 +01:00
|
|
|
err = gcry_cipher_encrypt(hd, ciphertext, ciphertext_len, plaintext1, plaintext1_len);
|
|
|
|
if (err)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
2018-02-22 22:36:50 +01:00
|
|
|
*errmsg = "Error CTR encrypting plaintext 1";
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = gcry_cipher_encrypt(hd, ciphertext, ciphertext_len, plaintext2, plaintext2_len);
|
|
|
|
if (err)
|
|
|
|
{
|
|
|
|
*errmsg = "Error CTR encrypting plaintext 2";
|
2017-06-19 21:52:01 +02:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
gcry_cipher_close(hd);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
|
|
|
gcry_cipher_close(hd);
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
/* -------------------------- IMPLEMENTATION -------------------------------- */
|
2017-06-19 21:52:01 +02:00
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
static struct pair_setup_context *
|
|
|
|
pair_setup_new(struct pair_definition *type, const char *pin, const char *device_id)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
2020-11-27 22:46:38 +01:00
|
|
|
struct pair_setup_context *sctx;
|
2017-06-19 21:52:01 +02:00
|
|
|
|
|
|
|
if (sodium_init() == -1)
|
|
|
|
return NULL;
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
if (!pin || strlen(pin) < 4)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
sctx = calloc(1, sizeof(struct pair_setup_context));
|
2017-06-19 21:52:01 +02:00
|
|
|
if (!sctx)
|
|
|
|
return NULL;
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
sctx->type = type;
|
|
|
|
|
2017-06-19 21:52:01 +02:00
|
|
|
memcpy(sctx->pin, pin, sizeof(sctx->pin));
|
|
|
|
|
|
|
|
return sctx;
|
|
|
|
}
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
static void
|
|
|
|
pair_setup_free(struct pair_setup_context *sctx)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
if (!sctx)
|
|
|
|
return;
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
srp_user_free(sctx->user);
|
2017-06-19 21:52:01 +02:00
|
|
|
|
|
|
|
free(sctx->pkB);
|
|
|
|
free(sctx->M2);
|
|
|
|
free(sctx->salt);
|
|
|
|
free(sctx->epk);
|
|
|
|
free(sctx->authtag);
|
|
|
|
|
|
|
|
free(sctx);
|
|
|
|
}
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
static uint8_t *
|
2021-01-04 00:05:04 +01:00
|
|
|
pair_setup_request1(size_t *len, struct pair_setup_context *sctx)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
plist_t dict;
|
|
|
|
plist_t method;
|
|
|
|
plist_t user;
|
2021-01-04 00:05:04 +01:00
|
|
|
uint32_t uint32;
|
2017-06-19 21:52:01 +02:00
|
|
|
char *data = NULL; // Necessary to initialize because plist_to_bin() uses value
|
|
|
|
|
|
|
|
sctx->user = srp_user_new(HASH_SHA1, SRP_NG_2048, USERNAME, (unsigned char *)sctx->pin, sizeof(sctx->pin), 0, 0);
|
|
|
|
|
|
|
|
dict = plist_new_dict();
|
|
|
|
|
|
|
|
method = plist_new_string("pin");
|
|
|
|
user = plist_new_string(USERNAME);
|
|
|
|
|
|
|
|
plist_dict_set_item(dict, "method", method);
|
|
|
|
plist_dict_set_item(dict, "user", user);
|
2021-01-04 00:05:04 +01:00
|
|
|
plist_to_bin(dict, &data, &uint32);
|
2017-06-19 21:52:01 +02:00
|
|
|
plist_free(dict);
|
|
|
|
|
2021-01-04 00:05:04 +01:00
|
|
|
*len = (size_t)uint32;
|
2017-06-19 21:52:01 +02:00
|
|
|
return (uint8_t *)data;
|
|
|
|
}
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
static uint8_t *
|
2021-01-04 00:05:04 +01:00
|
|
|
pair_setup_request2(size_t *len, struct pair_setup_context *sctx)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
plist_t dict;
|
|
|
|
plist_t pk;
|
|
|
|
plist_t proof;
|
|
|
|
const char *auth_username = NULL;
|
2021-01-04 00:05:04 +01:00
|
|
|
uint32_t uint32;
|
2017-06-19 21:52:01 +02:00
|
|
|
char *data = NULL;
|
|
|
|
|
|
|
|
// Calculate A
|
|
|
|
srp_user_start_authentication(sctx->user, &auth_username, &sctx->pkA, &sctx->pkA_len);
|
|
|
|
|
|
|
|
// Calculate M1 (client proof)
|
|
|
|
srp_user_process_challenge(sctx->user, (const unsigned char *)sctx->salt, sctx->salt_len, (const unsigned char *)sctx->pkB, sctx->pkB_len, &sctx->M1, &sctx->M1_len);
|
|
|
|
|
|
|
|
pk = plist_new_data((char *)sctx->pkA, sctx->pkA_len);
|
|
|
|
proof = plist_new_data((char *)sctx->M1, sctx->M1_len);
|
|
|
|
|
|
|
|
dict = plist_new_dict();
|
|
|
|
plist_dict_set_item(dict, "pk", pk);
|
|
|
|
plist_dict_set_item(dict, "proof", proof);
|
2021-01-04 00:05:04 +01:00
|
|
|
plist_to_bin(dict, &data, &uint32);
|
2017-06-19 21:52:01 +02:00
|
|
|
plist_free(dict);
|
|
|
|
|
2021-01-04 00:05:04 +01:00
|
|
|
*len = (size_t)uint32;
|
2017-06-19 21:52:01 +02:00
|
|
|
return (uint8_t *)data;
|
|
|
|
}
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
static uint8_t *
|
2021-01-04 00:05:04 +01:00
|
|
|
pair_setup_request3(size_t *len, struct pair_setup_context *sctx)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
plist_t dict;
|
|
|
|
plist_t epk;
|
|
|
|
plist_t authtag;
|
2021-01-04 00:05:04 +01:00
|
|
|
uint32_t uint32;
|
2017-06-19 21:52:01 +02:00
|
|
|
char *data = NULL;
|
|
|
|
const unsigned char *session_key;
|
|
|
|
int session_key_len;
|
|
|
|
unsigned char key[SHA512_DIGEST_LENGTH];
|
|
|
|
unsigned char iv[SHA512_DIGEST_LENGTH];
|
|
|
|
unsigned char encrypted[128]; // Alloc a bit extra - should only need 2*16
|
|
|
|
unsigned char tag[16];
|
|
|
|
const char *errmsg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
session_key = srp_user_get_session_key(sctx->user, &session_key_len);
|
|
|
|
if (!session_key)
|
|
|
|
{
|
|
|
|
sctx->errmsg = "Setup request 3: No valid session key";
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = hash_ab(HASH_SHA512, key, (unsigned char *)AES_SETUP_KEY, strlen(AES_SETUP_KEY), session_key, session_key_len);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
sctx->errmsg = "Setup request 3: Hashing of key string and shared secret failed";
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = hash_ab(HASH_SHA512, iv, (unsigned char *)AES_SETUP_IV, strlen(AES_SETUP_IV), session_key, session_key_len);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
sctx->errmsg = "Setup request 3: Hashing of iv string and shared secret failed";
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
iv[15]++; // Nonce?
|
2017-06-19 21:52:01 +02:00
|
|
|
/*
|
|
|
|
if (iv[15] == 0x00 || iv[15] == 0xff)
|
|
|
|
printf("- note that value of last byte is %d!\n", iv[15]);
|
|
|
|
*/
|
|
|
|
crypto_sign_keypair(sctx->public_key, sctx->private_key);
|
|
|
|
|
|
|
|
ret = encrypt_gcm(encrypted, sizeof(encrypted), tag, sctx->public_key, sizeof(sctx->public_key), key, iv, &errmsg);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
sctx->errmsg = errmsg;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
epk = plist_new_data((char *)encrypted, EPK_LENGTH);
|
|
|
|
authtag = plist_new_data((char *)tag, AUTHTAG_LENGTH);
|
|
|
|
|
|
|
|
dict = plist_new_dict();
|
|
|
|
plist_dict_set_item(dict, "epk", epk);
|
|
|
|
plist_dict_set_item(dict, "authTag", authtag);
|
2021-01-04 00:05:04 +01:00
|
|
|
plist_to_bin(dict, &data, &uint32);
|
2017-06-19 21:52:01 +02:00
|
|
|
plist_free(dict);
|
|
|
|
|
2021-01-04 00:05:04 +01:00
|
|
|
*len = (size_t)uint32;
|
2017-06-19 21:52:01 +02:00
|
|
|
return (uint8_t *)data;
|
|
|
|
}
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
static int
|
2021-01-04 00:05:04 +01:00
|
|
|
pair_setup_response1(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
plist_t dict;
|
|
|
|
plist_t pk;
|
|
|
|
plist_t salt;
|
|
|
|
|
|
|
|
plist_from_bin((const char *)data, data_len, &dict);
|
|
|
|
|
|
|
|
pk = plist_dict_get_item(dict, "pk");
|
|
|
|
salt = plist_dict_get_item(dict, "salt");
|
|
|
|
if (!pk || !salt)
|
|
|
|
{
|
|
|
|
sctx->errmsg = "Setup response 1: Missing pk or salt";
|
|
|
|
plist_free(dict);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
plist_get_data_val(pk, (char **)&sctx->pkB, &sctx->pkB_len); // B
|
|
|
|
plist_get_data_val(salt, (char **)&sctx->salt, &sctx->salt_len);
|
|
|
|
|
|
|
|
plist_free(dict);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
static int
|
2021-01-04 00:05:04 +01:00
|
|
|
pair_setup_response2(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
plist_t dict;
|
|
|
|
plist_t proof;
|
|
|
|
|
|
|
|
plist_from_bin((const char *)data, data_len, &dict);
|
|
|
|
|
|
|
|
proof = plist_dict_get_item(dict, "proof");
|
|
|
|
if (!proof)
|
|
|
|
{
|
|
|
|
sctx->errmsg = "Setup response 2: Missing proof";
|
|
|
|
plist_free(dict);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
plist_get_data_val(proof, (char **)&sctx->M2, &sctx->M2_len); // M2
|
|
|
|
|
|
|
|
plist_free(dict);
|
|
|
|
|
|
|
|
// Check M2
|
|
|
|
srp_user_verify_session(sctx->user, (const unsigned char *)sctx->M2);
|
|
|
|
if (!srp_user_is_authenticated(sctx->user))
|
|
|
|
{
|
|
|
|
sctx->errmsg = "Setup response 2: Server authentication failed";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
static int
|
2021-01-04 00:05:04 +01:00
|
|
|
pair_setup_response3(struct pair_setup_context *sctx, const uint8_t *data, size_t data_len)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
plist_t dict;
|
|
|
|
plist_t epk;
|
|
|
|
plist_t authtag;
|
|
|
|
|
|
|
|
plist_from_bin((const char *)data, data_len, &dict);
|
|
|
|
|
|
|
|
epk = plist_dict_get_item(dict, "epk");
|
|
|
|
if (!epk)
|
|
|
|
{
|
|
|
|
sctx->errmsg = "Setup response 3: Missing epk";
|
|
|
|
plist_free(dict);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
plist_get_data_val(epk, (char **)&sctx->epk, &sctx->epk_len);
|
|
|
|
|
|
|
|
authtag = plist_dict_get_item(dict, "authTag");
|
|
|
|
if (!authtag)
|
|
|
|
{
|
|
|
|
sctx->errmsg = "Setup response 3: Missing authTag";
|
|
|
|
plist_free(dict);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
plist_get_data_val(authtag, (char **)&sctx->authtag, &sctx->authtag_len);
|
|
|
|
|
|
|
|
plist_free(dict);
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
sctx->setup_is_completed = 1;
|
2017-06-19 21:52:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
static int
|
|
|
|
pair_setup_result(const uint8_t **key, size_t *key_len, struct pair_setup_context *sctx)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
2020-11-27 22:46:38 +01:00
|
|
|
// Last 32 bytes of private key should match public key, but check assumption
|
|
|
|
if (memcmp(sctx->private_key + sizeof(sctx->private_key) - sizeof(sctx->public_key), sctx->public_key, sizeof(sctx->public_key)) != 0)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
2020-11-27 22:46:38 +01:00
|
|
|
sctx->errmsg = "Pair setup result: Unexpected keys, private key does not match public key";
|
2017-06-19 21:52:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
*key = sctx->private_key;
|
|
|
|
*key_len = sizeof(sctx->private_key);
|
2017-06-19 21:52:01 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
static uint8_t *
|
2021-01-04 00:05:04 +01:00
|
|
|
pair_verify_request1(size_t *len, struct pair_verify_context *vctx)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
const uint8_t basepoint[32] = {9};
|
|
|
|
uint8_t *data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = crypto_scalarmult(vctx->client_eph_public_key, vctx->client_eph_private_key, basepoint);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
vctx->errmsg = "Verify request 1: Curve 25519 returned an error";
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*len = 4 + sizeof(vctx->client_eph_public_key) + sizeof(vctx->client_public_key);
|
|
|
|
data = calloc(1, *len);
|
|
|
|
if (!data)
|
|
|
|
{
|
|
|
|
vctx->errmsg = "Verify request 1: Out of memory";
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
data[0] = 1; // Magic
|
|
|
|
memcpy(data + 4, vctx->client_eph_public_key, sizeof(vctx->client_eph_public_key));
|
|
|
|
memcpy(data + 4 + sizeof(vctx->client_eph_public_key), vctx->client_public_key, sizeof(vctx->client_public_key));
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
static uint8_t *
|
2021-01-04 00:05:04 +01:00
|
|
|
pair_verify_request2(size_t *len, struct pair_verify_context *vctx)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
|
|
|
uint8_t shared_secret[crypto_scalarmult_BYTES];
|
|
|
|
uint8_t key[SHA512_DIGEST_LENGTH];
|
|
|
|
uint8_t iv[SHA512_DIGEST_LENGTH];
|
|
|
|
uint8_t encrypted[128]; // Alloc a bit extra, should only really need size of public key len
|
|
|
|
uint8_t signature[crypto_sign_BYTES];
|
|
|
|
uint8_t *data;
|
|
|
|
int ret;
|
|
|
|
const char *errmsg;
|
|
|
|
|
|
|
|
*len = sizeof(vctx->client_eph_public_key) + sizeof(vctx->server_eph_public_key);
|
|
|
|
data = calloc(1, *len);
|
|
|
|
if (!data)
|
|
|
|
{
|
|
|
|
vctx->errmsg = "Verify request 2: Out of memory";
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(data, vctx->client_eph_public_key, sizeof(vctx->client_eph_public_key));
|
|
|
|
memcpy(data + sizeof(vctx->client_eph_public_key), vctx->server_eph_public_key, sizeof(vctx->server_eph_public_key));
|
|
|
|
|
|
|
|
crypto_sign_detached(signature, NULL, data, *len, vctx->client_private_key);
|
|
|
|
|
|
|
|
free(data);
|
|
|
|
|
|
|
|
ret = crypto_scalarmult(shared_secret, vctx->client_eph_private_key, vctx->server_eph_public_key);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
vctx->errmsg = "Verify request 2: Curve 25519 returned an error";
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = hash_ab(HASH_SHA512, key, (unsigned char *)AES_VERIFY_KEY, strlen(AES_VERIFY_KEY), shared_secret, sizeof(shared_secret));
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
vctx->errmsg = "Verify request 2: Hashing of key string and shared secret failed";
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = hash_ab(HASH_SHA512, iv, (unsigned char *)AES_VERIFY_IV, strlen(AES_VERIFY_IV), shared_secret, sizeof(shared_secret));
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
vctx->errmsg = "Verify request 2: Hashing of iv string and shared secret failed";
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = encrypt_ctr(encrypted, sizeof(encrypted), vctx->server_public_key, sizeof(vctx->server_public_key), signature, sizeof(signature), key, iv, &errmsg);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
vctx->errmsg = errmsg;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*len = 4 + sizeof(vctx->server_public_key);
|
|
|
|
data = calloc(1, *len);
|
|
|
|
if (!data)
|
|
|
|
{
|
|
|
|
vctx->errmsg = "Verify request 2: Out of memory";
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(data + 4, encrypted, sizeof(vctx->server_public_key));
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
2020-11-27 22:46:38 +01:00
|
|
|
static int
|
2021-01-04 00:05:04 +01:00
|
|
|
pair_verify_response1(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len)
|
2017-06-19 21:52:01 +02:00
|
|
|
{
|
2021-01-04 00:05:04 +01:00
|
|
|
size_t wanted;
|
2017-06-19 21:52:01 +02:00
|
|
|
|
|
|
|
wanted = sizeof(vctx->server_eph_public_key) + sizeof(vctx->server_public_key);
|
|
|
|
if (data_len < wanted)
|
|
|
|
{
|
|
|
|
vctx->errmsg = "Verify response 2: Unexpected response (too short)";
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(vctx->server_eph_public_key, data, sizeof(vctx->server_eph_public_key));
|
|
|
|
memcpy(vctx->server_public_key, data + sizeof(vctx->server_eph_public_key), sizeof(vctx->server_public_key));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2020-11-27 22:46:38 +01:00
|
|
|
|
|
|
|
static int
|
2021-01-04 00:05:04 +01:00
|
|
|
pair_verify_response2(struct pair_verify_context *vctx, const uint8_t *data, size_t data_len)
|
2020-11-27 22:46:38 +01:00
|
|
|
{
|
|
|
|
// TODO actually check response
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct pair_definition pair_fruit =
|
|
|
|
{
|
|
|
|
.pair_setup_new = pair_setup_new,
|
|
|
|
.pair_setup_free = pair_setup_free,
|
|
|
|
.pair_setup_result = pair_setup_result,
|
|
|
|
|
|
|
|
.pair_setup_request1 = pair_setup_request1,
|
|
|
|
.pair_setup_request2 = pair_setup_request2,
|
|
|
|
.pair_setup_request3 = pair_setup_request3,
|
|
|
|
|
|
|
|
.pair_setup_response1 = pair_setup_response1,
|
|
|
|
.pair_setup_response2 = pair_setup_response2,
|
|
|
|
.pair_setup_response3 = pair_setup_response3,
|
|
|
|
|
|
|
|
.pair_verify_request1 = pair_verify_request1,
|
|
|
|
.pair_verify_request2 = pair_verify_request2,
|
|
|
|
|
|
|
|
.pair_verify_response1 = pair_verify_response1,
|
|
|
|
.pair_verify_response2 = pair_verify_response2,
|
|
|
|
};
|