Tasmota/lib/libesp32_div/ESP32-HomeKit/src/mu_srp.c
2021-03-11 14:48:59 +01:00

490 lines
12 KiB
C

#include <string.h>
#include "hkdf-sha.h"
#include "mu_bignum.h"
#include "mu_srp.h"
#ifdef SRP_DEBUG
#include <stdio.h>
#define srp_print printf
static void hex_dbg(char *name, void *buf, int buf_len)
{
char *p = (char *)buf;
int i;
srp_print("%s (%d): ", name, buf_len);
for (i = 0; i < buf_len; i++) {
if (i % 16 == 0)
srp_print("\r\n");
srp_print("%02x ", (unsigned)(unsigned char)p[i]);
}
srp_print("\r\n");
}
static void hex_dbg_bn(char *name, mu_bn_t *bn)
{
int len;
char *str = mu_bn_to_bin(bn, &len);
if (str) {
hex_dbg(name, str, len);
free(str);
}
}
#else
#define srp_print(...)
#define hex_dbg(...)
#define hex_dbg_bn(...)
#endif
static inline void SHA512_hash(const uint8_t *bytes, unsigned int byte_count,
uint8_t digest[SHA512HashSize])
{
SHA512Context ctx;
SHA512Reset(&ctx);
SHA512Input(&ctx, bytes, byte_count);
SHA512Result(&ctx, digest);
}
/************************* SRP Stuff */
char N_3072[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2,
0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67,
0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E,
0x34, 0x04, 0xDD, 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5,
0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF,
0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE,
0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3,
0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3,
0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70,
0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B, 0xE3, 0x9E, 0x77,
0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5,
0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39,
0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D, 0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A,
0x33, 0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64, 0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB,
0xEF, 0x0A, 0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D, 0xB3, 0x97, 0x0F, 0x85, 0xA6,
0xE1, 0xE4, 0xC7, 0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7, 0x1E, 0x8C, 0x94, 0xE0,
0x4A, 0x25, 0x61, 0x9D, 0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B, 0xF1, 0x2F, 0xFA,
0x06, 0xD9, 0x8A, 0x08, 0x64, 0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64, 0x52, 0x1F,
0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C, 0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C, 0x77,
0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2, 0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E, 0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2,
0xCA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
char g_3072[] = { 5 };
int mu_srp_init(mu_srp_handle_t *hd, mu_ng_type_t ng)
{
if (hd->allocated)
mu_srp_free(hd);
memset(hd, 0, sizeof(*hd));
hd->allocated = 1;
hd->ctx = mu_bn_ctx_new();
if (! hd->ctx)
goto error;
if (ng != MU_NG_3072)
goto error;
hd->n = mu_bn_new_from_bin(N_3072, sizeof(N_3072));
hd->bytes_n = N_3072;
hd->len_n = sizeof(N_3072);
if (! hd->n)
goto error;
hd->g = mu_bn_new_from_bin(g_3072, sizeof(g_3072));
hd->bytes_g = g_3072;
hd->len_g = sizeof(g_3072);
if (! hd->g)
goto error;
hd->type = ng;
return 0;
error:
mu_srp_free(hd);
return -1;
}
void mu_srp_free(mu_srp_handle_t *hd)
{
if (hd->allocated != 1)
return;
if (hd->ctx)
mu_bn_ctx_free(hd->ctx);
if (hd->n)
mu_bn_free(hd->n);
if (hd->g)
mu_bn_free(hd->g);
if (hd->s)
mu_bn_free(hd->s);
if (hd->bytes_s)
free(hd->bytes_s);
if (hd->v)
mu_bn_free(hd->v);
if (hd->B)
mu_bn_free(hd->B);
if (hd->bytes_B)
free(hd->bytes_B);
if (hd->b)
mu_bn_free(hd->b);
if (hd->A)
mu_bn_free(hd->A);
if (hd->bytes_A)
free(hd->bytes_A);
if (hd->session_key)
free(hd->session_key);
memset(hd, 0, sizeof(*hd));
}
static mu_bn_t *calculate_x(char *bytes_salt, int salt_len, const char *username, const char *pass, int pass_len)
{
unsigned char digest[SHA512HashSize];
SHA512Context ctx;
srp_print("username:%s:\npass:%s:\npass_len:%d:\n", username, pass, pass_len);
hex_dbg("salt", bytes_salt, salt_len);
SHA512Reset(&ctx);
SHA512Input(&ctx, (unsigned char *)username, strlen(username));
SHA512Input(&ctx, (unsigned char *)":", 1);
SHA512Input(&ctx, (unsigned char *)pass, pass_len);
SHA512Result(&ctx, digest);
SHA512Reset(&ctx);
SHA512Input(&ctx, (unsigned char *)bytes_salt, salt_len);
SHA512Input(&ctx, digest, sizeof(digest));
SHA512Result(&ctx, digest);
hex_dbg("Digest", digest, sizeof(digest));
return mu_bn_new_from_bin((char *)digest, sizeof(digest));
}
static mu_bn_t *calculate_padded_hash(mu_srp_handle_t *hd, char *a, int len_a, char *b, int len_b)
{
unsigned char digest[SHA512HashSize];
SHA512Context ctx;
int pad_len;
char *s = NULL;
if (len_a > len_b) {
pad_len = hd->len_n - len_b;
} else {
pad_len = hd->len_n - len_a;
}
if (pad_len) {
s = malloc(pad_len);
if (s) {
memset(s, 0, pad_len);
}
}
SHA512Reset(&ctx);
/* PAD (a) */
if (s && (len_a != hd->len_n)) {
SHA512Input(&ctx, (unsigned char *)s, hd->len_n - len_a);
}
SHA512Input(&ctx, (unsigned char *)a, len_a);
/* PAD (b) */
if (s && (len_b != hd->len_n)) {
SHA512Input(&ctx, (unsigned char *)s, hd->len_n - len_b);
}
SHA512Input(&ctx, (unsigned char *)b, len_b);
SHA512Result(&ctx, digest);
if (s) {
free(s);
}
hex_dbg("value", digest, sizeof(digest));
return mu_bn_new_from_bin((char *)digest, sizeof(digest));
}
/* k = SHA (N, PAD(g))
*
* https://tools.ietf.org/html/draft-ietf-tls-srp-08
*/
static mu_bn_t *calculate_k(mu_srp_handle_t *hd)
{
srp_print("k-->");
return calculate_padded_hash(hd, hd->bytes_n, hd->len_n, hd->bytes_g, hd->len_g);
}
static mu_bn_t *calculate_u(mu_srp_handle_t *hd, char *A, int len_A)
{
srp_print("u-->");
return calculate_padded_hash(hd, A, len_A, hd->bytes_B, hd->len_B);
}
int __mu_srp_srv_pubkey(mu_srp_handle_t *hd, char **bytes_B, int *len_B)
{
mu_bn_t *k = calculate_k(hd);
mu_bn_t *kv = NULL;
mu_bn_t *gb = NULL;
if (!k)
goto error;
hd->b = mu_bn_new();
if (!hd->b)
goto error;
mu_bn_get_rand(hd->b, 256, -1, 0);
hex_dbg_bn("b", hd->b);
/* B = kv + g^b */
kv = mu_bn_new();
gb = mu_bn_new();
hd->B = mu_bn_new();
if (!kv || !gb || ! hd->B)
goto error;
mu_bn_a_mul_b_mod_c(kv, k, hd->v, hd->n, hd->ctx);
mu_bn_a_exp_b_mod_c(gb, hd->g, hd->b, hd->n, hd->ctx);
mu_bn_a_add_b_mod_c(hd->B, kv, gb, hd->n, hd->ctx);
hd->bytes_B = mu_bn_to_bin(hd->B, len_B);
hd->len_B = *len_B;
*bytes_B = hd->bytes_B;
mu_bn_free(k);
mu_bn_free(kv);
mu_bn_free(gb);
return 0;
error:
if (k)
mu_bn_free(k);
if (kv)
mu_bn_free(kv);
if (gb)
mu_bn_free(gb);
if (hd->B) {
mu_bn_free(hd->B);
hd->B = NULL;
}
if (hd->b) {
mu_bn_free(hd->b);
hd->b = NULL;
}
return -1;
}
int mu_srp_srv_pubkey(mu_srp_handle_t *hd, const char *username, const char *pass, int pass_len, int salt_len,
char **bytes_B, int *len_B, char **bytes_salt)
{
/* Get Salt */
int str_salt_len;
mu_bn_t *x = NULL;
hd->s = mu_bn_new();
if (! hd->s)
goto error;
mu_bn_get_rand(hd->s, 8 * salt_len, -1, 0);
*bytes_salt = mu_bn_to_bin(hd->s, &str_salt_len);
if (! *bytes_salt)
goto error;
hd->bytes_s = *bytes_salt;
hd->len_s = salt_len;
hex_dbg("Salt", *bytes_salt, str_salt_len);
/* Calculate X which is simply a hash for all these things */
x = calculate_x(*bytes_salt, str_salt_len, username, pass, pass_len);
if (! x)
goto error;
hex_dbg_bn("x", x);
/* v = g^x % N */
hd->v = mu_bn_new();
if (! hd->v)
goto error;
mu_bn_a_exp_b_mod_c(hd->v, hd->g, x, hd->n, hd->ctx);
hex_dbg_bn("Verifier", hd->v);
if (__mu_srp_srv_pubkey(hd, bytes_B, len_B) < 0 )
goto error;
mu_bn_free(x);
return 0;
error:
if (hd->s) {
mu_bn_free(hd->s);
hd->s = NULL;
}
if (*bytes_salt) {
free(*bytes_salt);
*bytes_salt = NULL;
hd->bytes_s = NULL;
hd->len_s = 0;
}
if (x) {
mu_bn_free(x);
x = NULL;
}
if (hd->v) {
mu_bn_free(hd->v);
hd->v = NULL;
}
return -1;
}
int mu_srp_srv_pubkey_from_salt_verifier(mu_srp_handle_t *hd, char **bytes_B, int *len_B)
{
return __mu_srp_srv_pubkey(hd, bytes_B, len_B);
}
int mu_srp_set_salt_verifier(mu_srp_handle_t *hd, const char *salt, int salt_len,
const char *verifier, int verifier_len)
{
hd->bytes_s = malloc(salt_len);
if (!hd->bytes_s) {
goto error;
}
memcpy(hd->bytes_s, salt, salt_len);
hd->len_s = salt_len;
hd->s = mu_bn_new_from_bin(salt, salt_len);
if (!hd->s) {
goto error;
}
hd->v = mu_bn_new_from_bin(verifier, verifier_len);
if (!hd->v) {
goto error;
}
return 0;
error:
if (hd->bytes_s) {
free(hd->bytes_s);
hd->bytes_s = NULL;
hd->len_s = 0;
}
if (hd->s) {
mu_bn_free(hd->s);
hd->s = NULL;
}
if (hd->v) {
mu_bn_free(hd->v);
hd->v = NULL;
}
return -1;
}
int mu_srp_get_session_key(mu_srp_handle_t *hd, char *bytes_A, int len_A, char **bytes_key, int *len_key)
{
mu_bn_t *u, *vu, *avu, *S;
char *bytes_S;
int len_S;
u = vu = avu = S = NULL;
bytes_S = NULL;
hd->bytes_A = malloc(len_A);
if (! hd->bytes_A)
goto error;
memcpy(hd->bytes_A, bytes_A, len_A);
hd->len_A = len_A;
hd->A = mu_bn_new_from_bin(bytes_A, len_A);
if (! hd->A)
goto error;
u = calculate_u(hd, bytes_A, len_A);
if (! u)
goto error;
/* S = (A v^u)^b */
vu = mu_bn_new();
avu = mu_bn_new();
S = mu_bn_new();
if (!vu || !avu || !S )
goto error;
mu_bn_a_exp_b_mod_c(vu, hd->v, u, hd->n, hd->ctx);
mu_bn_a_mul_b_mod_c(avu, hd->A, vu, hd->n, hd->ctx);
mu_bn_a_exp_b_mod_c(S, avu, hd->b, hd->n, hd->ctx);
hex_dbg_bn("S", S);
bytes_S = mu_bn_to_bin(S, &len_S);
hd->session_key = malloc(SHA512HashSize);
if (!hd->session_key || ! bytes_S)
goto error;
SHA512_hash((unsigned char *)bytes_S, len_S, (unsigned char *)hd->session_key);
*bytes_key = hd->session_key;
*len_key = SHA512HashSize;
free(bytes_S);
mu_bn_free(vu);
mu_bn_free(avu);
mu_bn_free(S);
mu_bn_free(u);
return 0;
error:
if (bytes_S)
free(bytes_S);
if (vu)
mu_bn_free(vu);
if (avu)
mu_bn_free(avu);
if (S)
mu_bn_free(S);
if (u)
mu_bn_free(u);
if (hd->session_key) {
free(hd->session_key);
hd->session_key = NULL;
}
if (hd->A) {
mu_bn_free(hd->A);
hd->A = NULL;
}
if (hd->bytes_A) {
free(hd->bytes_A);
hd->bytes_A = NULL;
}
return -1;
}
int mu_srp_exchange_proofs(mu_srp_handle_t *hd, char *username, char *bytes_user_proof, char *bytes_host_proof)
{
/* First calculate M */
unsigned char hash_n[SHA512HashSize];
unsigned char hash_g[SHA512HashSize];
unsigned char hash_n_xor_g[SHA512HashSize];
int i;
SHA512_hash((unsigned char *)hd->bytes_n, hd->len_n, (unsigned char *)hash_n);
SHA512_hash((unsigned char *)hd->bytes_g, hd->len_g, (unsigned char *)hash_g);
for (i = 0; i < SHA512HashSize; i++)
hash_n_xor_g[i] = hash_n[i] ^ hash_g[i];
unsigned char hash_I[SHA512HashSize];
SHA512_hash((unsigned char *)username, strlen(username), (unsigned char *)hash_I);
SHA512Context ctx;
unsigned char digest[SHA512HashSize];
SHA512Reset(&ctx);
SHA512Input(&ctx, hash_n_xor_g, SHA512HashSize);
SHA512Input(&ctx, hash_I, SHA512HashSize);
SHA512Input(&ctx, (unsigned char *)hd->bytes_s, hd->len_s);
SHA512Input(&ctx, (unsigned char *)hd->bytes_A, hd->len_A);
SHA512Input(&ctx, (unsigned char *)hd->bytes_B, hd->len_B);
SHA512Input(&ctx, (unsigned char *)hd->session_key, SHA512HashSize);
SHA512Result(&ctx, digest);
hex_dbg("M", digest, sizeof(digest));
if (memcmp(bytes_user_proof, digest, SHA512HashSize) != 0)
return false;
/* M is now validated, let's proceed to H(AMK) */
SHA512Reset(&ctx);
SHA512Input(&ctx, (unsigned char *)hd->bytes_A, hd->len_A);
SHA512Input(&ctx, digest, SHA512HashSize);
SHA512Input(&ctx, (unsigned char *)hd->session_key, SHA512HashSize);
SHA512Result(&ctx, (unsigned char *)bytes_host_proof);
hex_dbg("AMK", bytes_host_proof, SHA512HashSize);
return true;
}
/************************* SRP Stuff Ends */