mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-07 16:01:30 +00:00
TLS verification
This commit is contained in:
1 parent
ef8e7419fa
commit
b50081a83b
14 files changed
+5281
-337
No files matched your search
+1
-1
@@ -12,7 +12,7 @@ enum {
|
||||
// Set if PHY LEDs are connected to ground
|
||||
MG_PHY_LEDS_ACTIVE_HIGH = (1 << 0),
|
||||
// Set when PHY clocks MAC. Otherwise, MAC clocks PHY
|
||||
MG_PHY_CLOCKS_MAC = (1 << 1),
|
||||
MG_PHY_CLOCKS_MAC = (1 << 1)
|
||||
};
|
||||
|
||||
enum { MG_PHY_SPEED_10M, MG_PHY_SPEED_100M, MG_PHY_SPEED_1000M };
|
||||
|
||||
+171
@@ -137,6 +137,13 @@ void mg_sha256_final(unsigned char digest[32], mg_sha256_ctx *ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
void mg_sha256(uint8_t dst[32], uint8_t *data, size_t datasz) {
|
||||
mg_sha256_ctx ctx;
|
||||
mg_sha256_init(&ctx);
|
||||
mg_sha256_update(&ctx, data, datasz);
|
||||
mg_sha256_final(dst, &ctx);
|
||||
}
|
||||
|
||||
void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data,
|
||||
size_t datasz) {
|
||||
mg_sha256_ctx ctx;
|
||||
@@ -165,3 +172,167 @@ void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data,
|
||||
mg_sha256_update(&ctx, dst, 32);
|
||||
mg_sha256_final(dst, &ctx);
|
||||
}
|
||||
|
||||
//=====================================
|
||||
// TODO: rename macros
|
||||
#define ROTR64(x, n) (((x) >> (n)) | ((x) << (64 - (n))))
|
||||
#define CH(x, y, z) (((x) & (y)) ^ (~(x) & (z)))
|
||||
#define MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
|
||||
#define EP0(x) (ROTR64(x, 28) ^ ROTR64(x, 34) ^ ROTR64(x, 39))
|
||||
#define EP1(x) (ROTR64(x, 14) ^ ROTR64(x, 18) ^ ROTR64(x, 41))
|
||||
#define SIG0(x) (ROTR64(x, 1) ^ ROTR64(x, 8) ^ ((x) >> 7))
|
||||
#define SIG1(x) (ROTR64(x, 19) ^ ROTR64(x, 61) ^ ((x) >> 6))
|
||||
|
||||
static const uint64_t mg_sha256_k2[80] = {
|
||||
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f,
|
||||
0xe9b5dba58189dbbc, 0x3956c25bf348b538, 0x59f111f1b605d019,
|
||||
0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242,
|
||||
0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
|
||||
0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235,
|
||||
0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3,
|
||||
0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, 0x2de92c6f592b0275,
|
||||
0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
|
||||
0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f,
|
||||
0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725,
|
||||
0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc,
|
||||
0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
|
||||
0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6,
|
||||
0x92722c851482353b, 0xa2bfe8a14cf10364, 0xa81a664bbc423001,
|
||||
0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218,
|
||||
0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
|
||||
0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99,
|
||||
0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb,
|
||||
0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc,
|
||||
0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
|
||||
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915,
|
||||
0xc67178f2e372532b, 0xca273eceea26619c, 0xd186b8c721c0c207,
|
||||
0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba,
|
||||
0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
|
||||
0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
|
||||
0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a,
|
||||
0x5fcb6fab3ad6faec, 0x6c44198c4a475817};
|
||||
|
||||
static void mg_sha384_transform(mg_sha384_ctx *ctx, const uint8_t data[]) {
|
||||
uint64_t m[80];
|
||||
uint64_t a, b, c, d, e, f, g, h;
|
||||
int i, j;
|
||||
|
||||
for (i = 0, j = 0; i < 16; ++i, j += 8)
|
||||
m[i] = ((uint64_t) data[j] << 56) | ((uint64_t) data[j + 1] << 48) |
|
||||
((uint64_t) data[j + 2] << 40) | ((uint64_t) data[j + 3] << 32) |
|
||||
((uint64_t) data[j + 4] << 24) | ((uint64_t) data[j + 5] << 16) |
|
||||
((uint64_t) data[j + 6] << 8) | ((uint64_t) data[j + 7]);
|
||||
for (; i < 80; ++i)
|
||||
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
|
||||
|
||||
a = ctx->state[0];
|
||||
b = ctx->state[1];
|
||||
c = ctx->state[2];
|
||||
d = ctx->state[3];
|
||||
e = ctx->state[4];
|
||||
f = ctx->state[5];
|
||||
g = ctx->state[6];
|
||||
h = ctx->state[7];
|
||||
|
||||
for (i = 0; i < 80; ++i) {
|
||||
uint64_t t1 = h + EP1(e) + CH(e, f, g) + mg_sha256_k2[i] + m[i];
|
||||
uint64_t t2 = EP0(a) + MAJ(a, b, c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + t1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1 + t2;
|
||||
}
|
||||
|
||||
ctx->state[0] += a;
|
||||
ctx->state[1] += b;
|
||||
ctx->state[2] += c;
|
||||
ctx->state[3] += d;
|
||||
ctx->state[4] += e;
|
||||
ctx->state[5] += f;
|
||||
ctx->state[6] += g;
|
||||
ctx->state[7] += h;
|
||||
}
|
||||
|
||||
void mg_sha384_init(mg_sha384_ctx *ctx) {
|
||||
ctx->datalen = 0;
|
||||
ctx->bitlen[0] = 0;
|
||||
ctx->bitlen[1] = 0;
|
||||
ctx->state[0] = 0xcbbb9d5dc1059ed8;
|
||||
ctx->state[1] = 0x629a292a367cd507;
|
||||
ctx->state[2] = 0x9159015a3070dd17;
|
||||
ctx->state[3] = 0x152fecd8f70e5939;
|
||||
ctx->state[4] = 0x67332667ffc00b31;
|
||||
ctx->state[5] = 0x8eb44a8768581511;
|
||||
ctx->state[6] = 0xdb0c2e0d64f98fa7;
|
||||
ctx->state[7] = 0x47b5481dbefa4fa4;
|
||||
}
|
||||
|
||||
void mg_sha384_update(mg_sha384_ctx *ctx, const uint8_t *data, size_t len) {
|
||||
size_t i;
|
||||
for (i = 0; i < len; ++i) {
|
||||
ctx->buffer[ctx->datalen] = data[i];
|
||||
ctx->datalen++;
|
||||
if (ctx->datalen == 128) {
|
||||
mg_sha384_transform(ctx, ctx->buffer);
|
||||
ctx->bitlen[1] += 1024;
|
||||
if (ctx->bitlen[1] < 1024) ctx->bitlen[0]++;
|
||||
ctx->datalen = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mg_sha384_final(uint8_t hash[48], mg_sha384_ctx *ctx) {
|
||||
size_t i = ctx->datalen;
|
||||
|
||||
if (ctx->datalen < 112) {
|
||||
ctx->buffer[i++] = 0x80;
|
||||
while (i < 112) ctx->buffer[i++] = 0x00;
|
||||
} else {
|
||||
ctx->buffer[i++] = 0x80;
|
||||
while (i < 128) ctx->buffer[i++] = 0x00;
|
||||
mg_sha384_transform(ctx, ctx->buffer);
|
||||
memset(ctx->buffer, 0, 112);
|
||||
}
|
||||
|
||||
ctx->bitlen[1] += ctx->datalen * 8;
|
||||
if (ctx->bitlen[1] < ctx->datalen * 8) ctx->bitlen[0]++;
|
||||
ctx->buffer[127] = (uint8_t) (ctx->bitlen[1]);
|
||||
ctx->buffer[126] = (uint8_t) (ctx->bitlen[1] >> 8);
|
||||
ctx->buffer[125] = (uint8_t) (ctx->bitlen[1] >> 16);
|
||||
ctx->buffer[124] = (uint8_t) (ctx->bitlen[1] >> 24);
|
||||
ctx->buffer[123] = (uint8_t) (ctx->bitlen[1] >> 32);
|
||||
ctx->buffer[122] = (uint8_t) (ctx->bitlen[1] >> 40);
|
||||
ctx->buffer[121] = (uint8_t) (ctx->bitlen[1] >> 48);
|
||||
ctx->buffer[120] = (uint8_t) (ctx->bitlen[1] >> 56);
|
||||
ctx->buffer[119] = (uint8_t) (ctx->bitlen[0]);
|
||||
ctx->buffer[118] = (uint8_t) (ctx->bitlen[0] >> 8);
|
||||
ctx->buffer[117] = (uint8_t) (ctx->bitlen[0] >> 16);
|
||||
ctx->buffer[116] = (uint8_t) (ctx->bitlen[0] >> 24);
|
||||
ctx->buffer[115] = (uint8_t) (ctx->bitlen[0] >> 32);
|
||||
ctx->buffer[114] = (uint8_t) (ctx->bitlen[0] >> 40);
|
||||
ctx->buffer[113] = (uint8_t) (ctx->bitlen[0] >> 48);
|
||||
ctx->buffer[112] = (uint8_t) (ctx->bitlen[0] >> 56);
|
||||
mg_sha384_transform(ctx, ctx->buffer);
|
||||
|
||||
for (i = 0; i < 6; ++i) {
|
||||
hash[i * 8] = (uint8_t) ((ctx->state[i] >> 56) & 0xff);
|
||||
hash[i * 8 + 1] = (uint8_t) ((ctx->state[i] >> 48) & 0xff);
|
||||
hash[i * 8 + 2] = (uint8_t) ((ctx->state[i] >> 40) & 0xff);
|
||||
hash[i * 8 + 3] = (uint8_t) ((ctx->state[i] >> 32) & 0xff);
|
||||
hash[i * 8 + 4] = (uint8_t) ((ctx->state[i] >> 24) & 0xff);
|
||||
hash[i * 8 + 5] = (uint8_t) ((ctx->state[i] >> 16) & 0xff);
|
||||
hash[i * 8 + 6] = (uint8_t) ((ctx->state[i] >> 8) & 0xff);
|
||||
hash[i * 8 + 7] = (uint8_t) (ctx->state[i] & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
void mg_sha384(uint8_t dst[48], uint8_t *data, size_t datasz) {
|
||||
mg_sha384_ctx ctx;
|
||||
mg_sha384_init(&ctx);
|
||||
mg_sha384_update(&ctx, data, datasz);
|
||||
mg_sha384_final(dst, &ctx);
|
||||
}
|
||||
@@ -15,8 +15,22 @@ typedef struct {
|
||||
unsigned char buffer[64];
|
||||
} mg_sha256_ctx;
|
||||
|
||||
|
||||
void mg_sha256_init(mg_sha256_ctx *);
|
||||
void mg_sha256_update(mg_sha256_ctx *, const unsigned char *data, size_t len);
|
||||
void mg_sha256_final(unsigned char digest[32], mg_sha256_ctx *);
|
||||
void mg_sha256(uint8_t dst[32], uint8_t *data, size_t datasz);
|
||||
void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data,
|
||||
size_t datasz);
|
||||
|
||||
typedef struct {
|
||||
uint64_t state[8];
|
||||
uint8_t buffer[128];
|
||||
uint64_t bitlen[2];
|
||||
uint32_t datalen;
|
||||
} mg_sha384_ctx;
|
||||
void mg_sha384_init(mg_sha384_ctx *ctx);
|
||||
void mg_sha384_update(mg_sha384_ctx *ctx, const uint8_t *data, size_t len);
|
||||
void mg_sha384_final(uint8_t digest[48], mg_sha384_ctx *ctx);
|
||||
void mg_sha384(uint8_t dst[48], uint8_t *data, size_t datasz);
|
||||
|
||||
+478
-146
@@ -5,6 +5,7 @@
|
||||
#include "tls.h"
|
||||
#include "tls_aes128.h"
|
||||
#include "tls_chacha20.h"
|
||||
#include "tls_rsa.h"
|
||||
#include "tls_uecc.h"
|
||||
#include "tls_x25519.h"
|
||||
#include "util.h"
|
||||
@@ -81,12 +82,15 @@ struct tls_data {
|
||||
int skip_verification; // perform checks on server certificate?
|
||||
int cert_requested; // client received a CertificateRequest?
|
||||
struct mg_str cert_der; // certificate in DER format
|
||||
struct mg_str ca_der; // CA certificate
|
||||
uint8_t ec_key[32]; // EC private key
|
||||
char hostname[254]; // server hostname (client extension)
|
||||
|
||||
uint8_t certhash[32]; // certificate message hash
|
||||
uint8_t pubkey[64]; // server EC public key to verify cert
|
||||
uint8_t sighash[32]; // server EC public key to verify cert
|
||||
int is_ec_pubkey; // EC or RSA?
|
||||
uint8_t pubkey[512 + 16]; // server EC (64) or RSA (512+exp) public key to
|
||||
// verify cert
|
||||
size_t pubkeysz; // size of the server public key
|
||||
uint8_t sighash[32]; // calculated signature verification hash
|
||||
|
||||
struct tls_enc enc;
|
||||
};
|
||||
@@ -140,6 +144,66 @@ struct mg_der_tlv {
|
||||
uint8_t *value;
|
||||
};
|
||||
|
||||
static int mg_der_parse(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) {
|
||||
size_t header_len = 2;
|
||||
uint32_t len = dersz < 2 ? 0 : der[1];
|
||||
if (dersz < 2) return -1; // Invalid DER
|
||||
tlv->type = der[0];
|
||||
if (len > 0x7F) { // long-form length
|
||||
uint8_t len_bytes = len & 0x7F;
|
||||
if (dersz < (size_t) (2 + len_bytes)) return -1;
|
||||
len = 0;
|
||||
for (uint8_t i = 0; i < len_bytes; i++) {
|
||||
len = (len << 8) | der[2 + i];
|
||||
}
|
||||
header_len += len_bytes;
|
||||
}
|
||||
if (dersz < header_len + len) return -1;
|
||||
tlv->len = len;
|
||||
tlv->value = der + header_len;
|
||||
return (int) (header_len + len);
|
||||
}
|
||||
|
||||
static int mg_der_next(struct mg_der_tlv *parent, struct mg_der_tlv *child) {
|
||||
if (parent->len == 0) return 0;
|
||||
int consumed = mg_der_parse(parent->value, parent->len, child);
|
||||
if (consumed < 0) return -1;
|
||||
parent->value += consumed;
|
||||
parent->len -= (uint32_t) consumed;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mg_der_find_oid(struct mg_der_tlv *tlv, const uint8_t *oid,
|
||||
size_t oid_len, struct mg_der_tlv *found) {
|
||||
struct mg_der_tlv parent, child;
|
||||
parent = *tlv;
|
||||
while (mg_der_next(&parent, &child) > 0) {
|
||||
if (child.type == 0x06 && child.len == oid_len &&
|
||||
memcmp(child.value, oid, oid_len) == 0) {
|
||||
return mg_der_next(&parent, found);
|
||||
} else if (child.type & 0x20) {
|
||||
struct mg_der_tlv sub_parent = child;
|
||||
if (mg_der_find_oid(&sub_parent, oid, oid_len, found)) return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void mg_der_debug(struct mg_der_tlv *tlv, int depth) {
|
||||
MG_DEBUG(("> %.*sd=%d Type: 0x%02X, Length: %u\n", depth * 4, " ", depth,
|
||||
tlv->type, tlv->len));
|
||||
|
||||
if (tlv->type & 0x20) { // Constructed: recurse into children
|
||||
struct mg_der_tlv child;
|
||||
struct mg_der_tlv parent = *tlv;
|
||||
while (mg_der_next(&parent, &child) > 0) {
|
||||
mg_der_debug(&child, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// parse DER into a TLV record
|
||||
static int mg_der_to_tlv(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) {
|
||||
if (dersz < 2) {
|
||||
@@ -162,31 +226,6 @@ static int mg_der_to_tlv(uint8_t *der, size_t dersz, struct mg_der_tlv *tlv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mg_der_find(uint8_t *der, size_t dersz, uint8_t *oid, size_t oidsz,
|
||||
struct mg_der_tlv *tlv) {
|
||||
uint8_t *p, *end;
|
||||
struct mg_der_tlv child = {0, 0, NULL};
|
||||
if (mg_der_to_tlv(der, dersz, tlv) < 0) {
|
||||
return -1; // invalid DER
|
||||
} else if (tlv->type == 6) { // found OID, check value
|
||||
return (tlv->len == oidsz && memcmp(tlv->value, oid, oidsz) == 0);
|
||||
} else if ((tlv->type & 0x20) == 0) {
|
||||
return 0; // Primitive, but not OID: not found
|
||||
}
|
||||
// Constructed object: scan children
|
||||
p = tlv->value;
|
||||
end = tlv->value + tlv->len;
|
||||
while (end > p) {
|
||||
int r;
|
||||
mg_der_to_tlv(p, (size_t) (end - p), &child);
|
||||
r = mg_der_find(p, (size_t) (end - p), oid, oidsz, tlv);
|
||||
if (r < 0) return -1; // error
|
||||
if (r > 0) return 1; // found OID!
|
||||
p = child.value + child.len;
|
||||
}
|
||||
return 0; // not found
|
||||
}
|
||||
|
||||
// Did we receive a full TLS record in the c->rtls buffer?
|
||||
static bool mg_tls_got_record(struct mg_connection *c) {
|
||||
return c->rtls.len >= (size_t) TLS_RECHDR_SIZE &&
|
||||
@@ -385,7 +424,7 @@ static void mg_tls_encrypt(struct mg_connection *c, const uint8_t *msg,
|
||||
nonce[8] ^= (uint8_t) ((seq >> 24) & 255U);
|
||||
nonce[9] ^= (uint8_t) ((seq >> 16) & 255U);
|
||||
nonce[10] ^= (uint8_t) ((seq >> 8) & 255U);
|
||||
nonce[11] ^= (uint8_t) ((seq) &255U);
|
||||
nonce[11] ^= (uint8_t) ((seq) & 255U);
|
||||
|
||||
mg_iobuf_add(wio, wio->len, hdr, sizeof(hdr));
|
||||
mg_iobuf_resize(wio, wio->len + encsz);
|
||||
@@ -464,8 +503,7 @@ static int mg_tls_recv_record(struct mg_connection *c) {
|
||||
nonce[8] ^= (uint8_t) ((seq >> 24) & 255U);
|
||||
nonce[9] ^= (uint8_t) ((seq >> 16) & 255U);
|
||||
nonce[10] ^= (uint8_t) ((seq >> 8) & 255U);
|
||||
nonce[11] ^= (uint8_t) ((seq) &255U);
|
||||
|
||||
nonce[11] ^= (uint8_t) ((seq) & 255U);
|
||||
#if CHACHA20
|
||||
{
|
||||
uint8_t *dec = (uint8_t *) calloc(1, msgsz);
|
||||
@@ -553,15 +591,14 @@ static int mg_tls_server_recv_hello(struct mg_connection *c) {
|
||||
uint16_t key_exchange_len;
|
||||
uint8_t *key_exchange;
|
||||
uint16_t n = MG_LOAD_BE16(ext + j + 2);
|
||||
if (ext[j] != 0x00 ||
|
||||
ext[j + 1] != 0x33) { // not a key share extension, ignore
|
||||
if (MG_LOAD_BE16(ext + j) != 0x0033) { // not a key share extension, ignore
|
||||
j += (uint16_t) (n + 4);
|
||||
continue;
|
||||
}
|
||||
key_exchange_len = MG_LOAD_BE16(ext + j + 4);
|
||||
key_exchange = ext + j + 6;
|
||||
if (key_exchange_len >
|
||||
rio->len - (uint16_t) ((size_t) key_exchange - (size_t) rio->buf) - 2)
|
||||
rio->len - (uint16_t) ((size_t) key_exchange - (size_t) rio->buf))
|
||||
goto fail;
|
||||
for (k = 0; k < key_exchange_len;) {
|
||||
uint16_t m = MG_LOAD_BE16(key_exchange + k + 2);
|
||||
@@ -615,7 +652,7 @@ static void mg_tls_server_send_hello(struct mg_connection *c) {
|
||||
// calculate keyshare
|
||||
uint8_t x25519_pub[X25519_BYTES];
|
||||
uint8_t x25519_prv[X25519_BYTES];
|
||||
if (!mg_random(x25519_prv, sizeof(x25519_prv))) mg_error(c, "RNG");
|
||||
if (!mg_random(x25519_prv, sizeof(x25519_prv))) mg_error(c, "RNG");
|
||||
mg_tls_x25519(x25519_pub, x25519_prv, X25519_BASE_POINT, 1);
|
||||
mg_tls_x25519(tls->x25519_sec, x25519_prv, tls->x25519_cli, 1);
|
||||
mg_tls_hexdump("s x25519 sec", tls->x25519_sec, sizeof(tls->x25519_sec));
|
||||
@@ -644,28 +681,30 @@ static void mg_tls_server_send_ext(struct mg_connection *c) {
|
||||
|
||||
static void mg_tls_server_send_cert(struct mg_connection *c) {
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
// server DER certificate (empty)
|
||||
size_t n = tls->cert_der.len;
|
||||
int send_ca = !c->is_client && tls->ca_der.len > 0;
|
||||
// server DER certificate + CA (optional)
|
||||
size_t n = tls->cert_der.len + (send_ca ? tls->ca_der.len + 5 : 0);
|
||||
uint8_t *cert = (uint8_t *) calloc(1, 13 + n);
|
||||
if (cert == NULL) {
|
||||
mg_error(c, "tls cert oom");
|
||||
return;
|
||||
}
|
||||
cert[0] = 0x0b; // handshake header
|
||||
cert[1] = (uint8_t) (((n + 9) >> 16) & 255U); // 3 bytes: payload length
|
||||
cert[2] = (uint8_t) (((n + 9) >> 8) & 255U);
|
||||
cert[3] = (uint8_t) ((n + 9) & 255U);
|
||||
cert[4] = 0; // request context
|
||||
cert[5] = (uint8_t) (((n + 5) >> 16) & 255U); // 3 bytes: cert (s) length
|
||||
cert[6] = (uint8_t) (((n + 5) >> 8) & 255U);
|
||||
cert[7] = (uint8_t) ((n + 5) & 255U);
|
||||
cert[8] =
|
||||
(uint8_t) (((n) >> 16) & 255U); // 3 bytes: first (and only) cert len
|
||||
cert[9] = (uint8_t) (((n) >> 8) & 255U);
|
||||
cert[10] = (uint8_t) (n & 255U);
|
||||
cert[0] = 0x0b; // handshake header
|
||||
MG_STORE_BE24(cert + 1, n + 9);
|
||||
cert[4] = 0; // request context
|
||||
MG_STORE_BE24(cert + 5, n + 5); // 3 bytes: cert (s) length
|
||||
MG_STORE_BE24(cert + 8, tls->cert_der.len); // 3 bytes: first cert len
|
||||
// bytes 11+ are certificate in DER format
|
||||
memmove(cert + 11, tls->cert_der.buf, n);
|
||||
cert[11 + n] = cert[12 + n] = 0; // certificate extensions (none)
|
||||
memmove(cert + 11, tls->cert_der.buf, tls->cert_der.len);
|
||||
MG_STORE_BE16(cert + 11 + tls->cert_der.len,
|
||||
0); // certificate extensions (none)
|
||||
if (send_ca) {
|
||||
size_t offset = 13 + tls->cert_der.len;
|
||||
MG_STORE_BE24(cert + offset, tls->ca_der.len); // 3 bytes: CA cert length
|
||||
memmove(cert + offset + 3, tls->ca_der.buf,
|
||||
tls->ca_der.len); // CA cert data
|
||||
MG_STORE_BE16(cert + 11 + n, 0); // certificate extensions (none)
|
||||
}
|
||||
mg_sha256_update(&tls->sha256, cert, 13 + n);
|
||||
mg_tls_encrypt(c, cert, 13 + n, MG_TLS_HANDSHAKE);
|
||||
free(cert);
|
||||
@@ -774,9 +813,10 @@ static void mg_tls_client_send_hello(struct mg_connection *c) {
|
||||
|
||||
uint8_t x25519_pub[X25519_BYTES];
|
||||
|
||||
// the only signature algorithm we actually support
|
||||
uint8_t secp256r1_sig_algs[8] = {
|
||||
0x00, 0x0d, 0x00, 0x04, 0x00, 0x02, 0x04, 0x03,
|
||||
// signature algorithms we actually support:
|
||||
// rsa_pkcs1_sha256, rsa_pss_rsae_sha256 and ecdsa_secp256r1_sha256
|
||||
uint8_t secp256r1_sig_algs[12] = {
|
||||
0x00, 0x0d, 0x00, 0x08, 0x00, 0x06, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01,
|
||||
};
|
||||
// all popular signature algorithms (if we don't care about verification)
|
||||
uint8_t all_sig_algs[34] = {
|
||||
@@ -947,106 +987,351 @@ static int mg_tls_client_recv_ext(struct mg_connection *c) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct mg_tls_cert {
|
||||
int is_ec_pubkey;
|
||||
struct mg_str sn;
|
||||
struct mg_str pubkey;
|
||||
struct mg_str sig; // signature
|
||||
uint8_t tbshash[48]; // 32b for sha256/secp256, 48b for sha384/secp384
|
||||
size_t tbshashsz; // actual TBS hash size
|
||||
};
|
||||
|
||||
static void mg_der_debug_cert_name(const char *name, struct mg_der_tlv *tlv) {
|
||||
struct mg_der_tlv v;
|
||||
struct mg_str cn, c, o, ou;
|
||||
cn = c = o = ou = mg_str("");
|
||||
if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x03", 3, &v))
|
||||
cn = mg_str_n((const char *) v.value, v.len);
|
||||
if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x06", 3, &v))
|
||||
c = mg_str_n((const char *) v.value, v.len);
|
||||
if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x0a", 3, &v))
|
||||
o = mg_str_n((const char *) v.value, v.len);
|
||||
if (mg_der_find_oid(tlv, (uint8_t *) "\x55\x04\x0b", 3, &v))
|
||||
ou = mg_str_n((const char *) v.value, v.len);
|
||||
MG_VERBOSE(("%s: CN=%.*s, C=%.*s, O=%.*s, OU=%.*s", name, cn.len, cn.buf,
|
||||
c.len, c.buf, o.len, o.buf, ou.len, ou.buf));
|
||||
}
|
||||
|
||||
static int mg_tls_parse_cert_der(void *buf, size_t dersz,
|
||||
struct mg_tls_cert *cert) {
|
||||
uint8_t *tbs, *der = (uint8_t *) buf;
|
||||
size_t tbssz;
|
||||
struct mg_der_tlv root, tbs_cert, field, algo; // pubkey, signature;
|
||||
struct mg_der_tlv pki, pki_algo, pki_key, pki_curve, raw_sig;
|
||||
|
||||
// Parse outermost SEQUENCE
|
||||
if (mg_der_parse(der, dersz, &root) <= 0 || root.type != 0x30) return -1;
|
||||
|
||||
// Parse TBSCertificate SEQUENCE
|
||||
tbs = root.value;
|
||||
if (mg_der_next(&root, &tbs_cert) <= 0 || tbs_cert.type != 0x30) return -1;
|
||||
tbssz = (size_t) (tbs_cert.value + tbs_cert.len - tbs);
|
||||
|
||||
// Parse Version (optional field)
|
||||
if (mg_der_next(&tbs_cert, &field) <= 0) return -1;
|
||||
if (field.type == 0xa0) { // v3
|
||||
if (mg_der_parse(field.value, field.len, &field) <= 0 || field.len != 1 ||
|
||||
field.value[0] != 2)
|
||||
return -1;
|
||||
if (mg_der_next(&tbs_cert, &field) <= 0) return -1;
|
||||
}
|
||||
|
||||
// Parse Serial Number
|
||||
if (field.type != 2) return -1;
|
||||
cert->sn = mg_str_n((char *) field.value, field.len);
|
||||
MG_VERBOSE(("cert s/n: %M", mg_print_hex, cert->sn.len, cert->sn.buf));
|
||||
|
||||
// Parse signature algorithm (first occurrence)
|
||||
if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1;
|
||||
if (mg_der_next(&field, &algo) <= 0 || algo.type != 0x06) return -1;
|
||||
|
||||
MG_VERBOSE(("sig algo (oid): %M", mg_print_hex, algo.len, algo.value));
|
||||
// Signature algorithm OID mapping
|
||||
if (memcmp(algo.value, "\x2A\x86\x48\xCE\x3D\x04\x03\x02", algo.len) == 0) {
|
||||
MG_VERBOSE(("sig algo: ECDSA with SHA256"));
|
||||
mg_sha256(cert->tbshash, tbs, tbssz);
|
||||
cert->tbshashsz = 32;
|
||||
} else if (memcmp(algo.value, "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0B",
|
||||
algo.len) == 0) {
|
||||
MG_VERBOSE(("sig algo: RSA with SHA256"));
|
||||
mg_sha256(cert->tbshash, tbs, tbssz);
|
||||
cert->tbshashsz = 32;
|
||||
} else if (memcmp(algo.value, "\x2A\x86\x48\xCE\x3D\x04\x03\x03", algo.len) ==
|
||||
0) {
|
||||
MG_VERBOSE(("sig algo: ECDSA with SHA384"));
|
||||
mg_sha384(cert->tbshash, tbs, tbssz);
|
||||
cert->tbshashsz = 48;
|
||||
} else if (memcmp(algo.value, "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0C",
|
||||
algo.len) == 0) {
|
||||
MG_VERBOSE(("sig algo: RSA with SHA384"));
|
||||
mg_sha384(cert->tbshash, tbs, tbssz);
|
||||
cert->tbshashsz = 48;
|
||||
} else {
|
||||
MG_ERROR(
|
||||
("sig algo: unsupported OID: %M", mg_print_hex, algo.len, algo.value));
|
||||
return -1;
|
||||
}
|
||||
MG_VERBOSE(("tbs hash: %M", mg_print_hex, cert->tbshashsz, cert->tbshash));
|
||||
|
||||
// issuer
|
||||
if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1;
|
||||
mg_der_debug_cert_name("issuer", &field);
|
||||
|
||||
// validity dates (before/after)
|
||||
if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1;
|
||||
if (1) {
|
||||
struct mg_der_tlv before, after;
|
||||
mg_der_next(&field, &before);
|
||||
mg_der_next(&field, &after);
|
||||
if (memcmp(after.value, "250101000000Z", after.len) < 0) {
|
||||
MG_ERROR(("invalid validity dates: before=%M after=%M", mg_print_hex,
|
||||
before.len, before.value, mg_print_hex, after.len,
|
||||
after.value));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// subject
|
||||
if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1;
|
||||
mg_der_debug_cert_name("subject", &field);
|
||||
|
||||
// subject public key info
|
||||
if (mg_der_next(&tbs_cert, &field) <= 0 || field.type != 0x30) return -1;
|
||||
|
||||
if (mg_der_next(&field, &pki) <= 0 || pki.type != 0x30) return -1;
|
||||
if (mg_der_next(&pki, &pki_algo) <= 0 || pki_algo.type != 0x06) return -1;
|
||||
|
||||
// public key algorithm
|
||||
MG_VERBOSE(("pk algo (oid): %M", mg_print_hex, pki_algo.len, pki_algo.value));
|
||||
if (memcmp(pki_algo.value, "\x2A\x86\x48\xCE\x3D\x03\x01\x07",
|
||||
pki_algo.len) == 0) {
|
||||
cert->is_ec_pubkey = 1;
|
||||
MG_VERBOSE(("pk algo: ECDSA secp256r1"));
|
||||
} else if (memcmp(pki_algo.value, "\x2A\x86\x48\xCE\x3D\x03\x01\x08",
|
||||
pki_algo.len) == 0) {
|
||||
cert->is_ec_pubkey = 1;
|
||||
MG_VERBOSE(("pk algo: ECDSA secp384r1"));
|
||||
} else if (memcmp(pki_algo.value, "\x2A\x86\x48\xCE\x3D\x02\x01",
|
||||
pki_algo.len) == 0) {
|
||||
cert->is_ec_pubkey = 1;
|
||||
MG_VERBOSE(("pk algo: EC public key"));
|
||||
} else if (memcmp(pki_algo.value, "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01",
|
||||
pki_algo.len) == 0) {
|
||||
cert->is_ec_pubkey = 0;
|
||||
MG_VERBOSE(("pk algo: RSA"));
|
||||
} else {
|
||||
MG_ERROR(("unsupported pk algo: %M", mg_print_hex, pki_algo.len,
|
||||
pki_algo.value));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Parse public key
|
||||
if (cert->is_ec_pubkey) {
|
||||
if (mg_der_next(&pki, &pki_curve) <= 0 || pki_curve.type != 0x06) return -1;
|
||||
}
|
||||
if (mg_der_next(&field, &pki_key) <= 0 || pki_key.type != 0x03) return -1;
|
||||
|
||||
if (cert->is_ec_pubkey) { // Skip leading 0x00 and 0x04 (=uncompressed)
|
||||
cert->pubkey = mg_str_n((char *) pki_key.value + 2, pki_key.len - 2);
|
||||
} else { // Skip leading 0x00 byte
|
||||
cert->pubkey = mg_str_n((char *) pki_key.value + 1, pki_key.len - 1);
|
||||
}
|
||||
|
||||
// Parse signature
|
||||
if (mg_der_next(&root, &field) <= 0 || field.type != 0x30) return -1;
|
||||
if (mg_der_next(&root, &raw_sig) <= 0 || raw_sig.type != 0x03) return -1;
|
||||
if (raw_sig.len < 1 || raw_sig.value[0] != 0x00) return -1;
|
||||
|
||||
cert->sig = mg_str_n((char *) raw_sig.value + 1, raw_sig.len - 1);
|
||||
MG_VERBOSE(("sig: %M", mg_print_hex, cert->sig.len, cert->sig.buf));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mg_tls_verify_cert_san(const uint8_t *der, size_t dersz,
|
||||
const char *server_name) {
|
||||
struct mg_der_tlv root, field, name;
|
||||
if (mg_der_parse((uint8_t *) der, dersz, &root) < 0 ||
|
||||
mg_der_find_oid(&root, (uint8_t *) "\x55\x1d\x11", 3, &field) < 0) {
|
||||
MG_ERROR(("failed to parse certificate to extract SAN"));
|
||||
return -1;
|
||||
}
|
||||
if (mg_der_parse(field.value, field.len, &field) < 0) {
|
||||
MG_ERROR(
|
||||
("certificate subject alternative names is not a constructed object"));
|
||||
return -1;
|
||||
}
|
||||
while (mg_der_next(&field, &name) > 0) {
|
||||
if (mg_match(mg_str(server_name),
|
||||
mg_str_n((const char *) name.value, name.len), NULL)) {
|
||||
// Found SAN that matches the host name
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert,
|
||||
const struct mg_tls_cert *issuer) {
|
||||
if (issuer->is_ec_pubkey) {
|
||||
uint8_t sig[128];
|
||||
struct mg_der_tlv seq = {0, 0, 0}, a = {0, 0, 0}, b = {0, 0, 0};
|
||||
mg_der_parse((uint8_t *) cert->sig.buf, cert->sig.len, &seq);
|
||||
mg_der_next(&seq, &a);
|
||||
mg_der_next(&seq, &b);
|
||||
if (a.len == 0 || b.len == 0) {
|
||||
MG_ERROR(("cert verification error"));
|
||||
return 0;
|
||||
}
|
||||
if (issuer->pubkey.len == 64) {
|
||||
const uint32_t N = 32;
|
||||
if (a.len > N) a.value += (a.len - N), a.len = N;
|
||||
if (b.len > N) b.value += (b.len - N), b.len = N;
|
||||
memmove(sig, a.value, N);
|
||||
memmove(sig + N, b.value, N);
|
||||
return mg_uecc_verify((uint8_t *) issuer->pubkey.buf, cert->tbshash,
|
||||
(unsigned) cert->tbshashsz, sig,
|
||||
mg_uecc_secp256r1());
|
||||
} else if (issuer->pubkey.len == 96) {
|
||||
MG_DEBUG(("ignore secp386 for now"));
|
||||
return 1;
|
||||
} else {
|
||||
MG_ERROR(("unsupported public key length: %d", issuer->pubkey.len));
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
int r;
|
||||
uint8_t sig2[256]; // 2048 bits
|
||||
struct mg_der_tlv seq, modulus, exponent;
|
||||
if (mg_der_parse((uint8_t *) issuer->pubkey.buf, issuer->pubkey.len,
|
||||
&seq) <= 0 ||
|
||||
mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 ||
|
||||
mg_der_next(&seq, &exponent) <= 0 || exponent.type != 2) {
|
||||
return -1;
|
||||
}
|
||||
mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value, exponent.len,
|
||||
(uint8_t *) cert->sig.buf, cert->sig.len, sig2,
|
||||
sizeof(sig2));
|
||||
|
||||
r = memcmp(sig2 + sizeof(sig2) - cert->tbshashsz, cert->tbshash,
|
||||
cert->tbshashsz);
|
||||
return r == 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int mg_tls_client_recv_cert(struct mg_connection *c) {
|
||||
uint8_t *cert;
|
||||
uint32_t certsz;
|
||||
struct mg_der_tlv oid, pubkey, seq, subj;
|
||||
int subj_match = 0;
|
||||
struct tls_data *tls = (struct tls_data *) c->tls;
|
||||
unsigned char *recv_buf;
|
||||
(void) subj_match;
|
||||
|
||||
if (mg_tls_recv_record(c) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
recv_buf = &c->rtls.buf[tls->recv_offset];
|
||||
|
||||
if (recv_buf[0] == MG_TLS_CERTIFICATE_REQUEST) {
|
||||
MG_VERBOSE(("got certificate request"));
|
||||
mg_tls_drop_message(c);
|
||||
tls->cert_requested = 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (recv_buf[0] != MG_TLS_CERTIFICATE) {
|
||||
mg_error(c, "expected server certificate but got msg 0x%02x", recv_buf[0]);
|
||||
return -1;
|
||||
}
|
||||
if (tls->skip_verification) {
|
||||
mg_tls_drop_message(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (tls->recv_len < 11) {
|
||||
mg_error(c, "certificate list too short");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cert = recv_buf + 11;
|
||||
certsz = MG_LOAD_BE24(recv_buf + 8);
|
||||
if (certsz > tls->recv_len - 11) {
|
||||
mg_error(c, "certificate too long: %d vs %d", certsz, tls->recv_len - 11);
|
||||
uint32_t full_cert_chain_len = MG_LOAD_BE24(recv_buf + 1);
|
||||
uint32_t cert_chain_len = MG_LOAD_BE24(recv_buf + 5);
|
||||
if (cert_chain_len != full_cert_chain_len - 4) {
|
||||
MG_ERROR(("full chain length: %d, chain length: %d", full_cert_chain_len,
|
||||
cert_chain_len));
|
||||
mg_error(c, "certificate chain length mismatch");
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
// secp256r1 public key
|
||||
if (mg_der_find(cert, certsz,
|
||||
(uint8_t *) "\x2A\x86\x48\xCE\x3D\x03\x01\x07", 8,
|
||||
&oid) < 0) {
|
||||
mg_error(c, "certificate secp256r1 public key OID not found");
|
||||
// Normally, there are 2-3 certs in a chain
|
||||
struct mg_tls_cert certs[8];
|
||||
int certnum = 0;
|
||||
uint8_t *p = recv_buf + 8;
|
||||
//uint8_t *endp = recv_buf + tls->recv_len;
|
||||
uint8_t *endp = recv_buf + cert_chain_len;
|
||||
|
||||
int found_ca = 0;
|
||||
struct mg_tls_cert ca;
|
||||
|
||||
memset(certs, 0, sizeof(certs));
|
||||
memset(&ca, 0, sizeof(ca));
|
||||
|
||||
if (tls->ca_der.len > 0) {
|
||||
if (mg_tls_parse_cert_der(tls->ca_der.buf, tls->ca_der.len, &ca) < 0) {
|
||||
mg_error(c, "failed to parse CA certificate");
|
||||
return -1;
|
||||
}
|
||||
if (mg_der_to_tlv(oid.value + oid.len,
|
||||
(size_t) (cert + certsz - (oid.value + oid.len)),
|
||||
&pubkey) < 0) {
|
||||
mg_error(c, "certificate secp256r1 public key not found");
|
||||
MG_VERBOSE(("CA serial: %M", mg_print_hex, ca.sn.len, ca.sn.buf));
|
||||
}
|
||||
|
||||
while (p < endp) {
|
||||
struct mg_tls_cert *ci = &certs[certnum++];
|
||||
uint32_t certsz = MG_LOAD_BE24(p);
|
||||
uint8_t *cert = p + 3;
|
||||
uint16_t certext = MG_LOAD_BE16(cert + certsz);
|
||||
if (certext != 0) {
|
||||
mg_error(c, "certificate extensions are not supported");
|
||||
return -1;
|
||||
}
|
||||
p = cert + certsz + 2;
|
||||
|
||||
if (mg_tls_parse_cert_der(cert, certsz, ci) < 0) {
|
||||
mg_error(c, "failed to parse certificate");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// expect BIT STRING, unpadded, uncompressed: [0]+[4]+32+32 content bytes
|
||||
if (pubkey.type != 3 || pubkey.len != 66 || pubkey.value[0] != 0 ||
|
||||
pubkey.value[1] != 4) {
|
||||
mg_error(c, "unsupported public key bitstring encoding");
|
||||
return -1;
|
||||
}
|
||||
memmove(tls->pubkey, pubkey.value + 2, pubkey.len - 2);
|
||||
} while (0);
|
||||
|
||||
// Subject Alternative Names
|
||||
do {
|
||||
if (mg_der_find(cert, certsz, (uint8_t *) "\x55\x1d\x11", 3, &oid) < 0) {
|
||||
mg_error(c, "certificate does not contain subject alternative names");
|
||||
return -1;
|
||||
}
|
||||
if (mg_der_to_tlv(oid.value + oid.len,
|
||||
(size_t) (cert + certsz - (oid.value + oid.len)),
|
||||
&seq) < 0) {
|
||||
mg_error(c, "certificate subject alternative names not found");
|
||||
return -1;
|
||||
}
|
||||
if (mg_der_to_tlv(seq.value, seq.len, &seq) < 0) {
|
||||
mg_error(
|
||||
c,
|
||||
"certificate subject alternative names is not a constructed object");
|
||||
return -1;
|
||||
}
|
||||
MG_VERBOSE(("verify hostname %s", tls->hostname));
|
||||
while (seq.len > 0) {
|
||||
if (mg_der_to_tlv(seq.value, seq.len, &subj) < 0) {
|
||||
mg_error(c, "bad subject alternative name");
|
||||
if (ci == certs) {
|
||||
// First certificate in the chain is peer cert, check SAN and store
|
||||
// public key for further CertVerify step
|
||||
if (mg_tls_verify_cert_san(cert, certsz, tls->hostname) <= 0) {
|
||||
mg_error(c, "failed to verify hostname");
|
||||
return -1;
|
||||
}
|
||||
MG_VERBOSE(("subj=%.*s", subj.len, subj.value));
|
||||
if (mg_match(mg_str((const char *) tls->hostname),
|
||||
mg_str_n((const char *) subj.value, subj.len), NULL)) {
|
||||
subj_match = 1;
|
||||
break;
|
||||
memmove(tls->pubkey, ci->pubkey.buf, ci->pubkey.len);
|
||||
tls->pubkeysz = ci->pubkey.len;
|
||||
} else {
|
||||
if (!mg_tls_verify_cert_signature(ci - 1, ci)) {
|
||||
mg_error(c, "failed to verify certificate chain");
|
||||
return -1;
|
||||
}
|
||||
seq.len = (uint32_t) (seq.value + seq.len - (subj.value + subj.len));
|
||||
seq.value = subj.value + subj.len;
|
||||
}
|
||||
if (!subj_match) {
|
||||
mg_error(c, "certificate did not match the hostname");
|
||||
|
||||
if (ca.pubkey.len == ci->pubkey.len &&
|
||||
memcmp(ca.pubkey.buf, ci->pubkey.buf, ca.pubkey.len) == 0) {
|
||||
found_ca = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (certnum == sizeof(certs) / sizeof(certs[0]) - 1) {
|
||||
mg_error(c, "too many certificates in the chain");
|
||||
return -1;
|
||||
}
|
||||
} while (0);
|
||||
}
|
||||
|
||||
if (!found_ca && tls->ca_der.len > 0) {
|
||||
if (certnum < 1 ||
|
||||
!mg_tls_verify_cert_signature(&certs[certnum - 1], &ca)) {
|
||||
mg_error(c, "failed to verify CA");
|
||||
return -1;
|
||||
} else {
|
||||
MG_VERBOSE(
|
||||
("CA was not in the chain, but verification with builtin CA passed"));
|
||||
}
|
||||
}
|
||||
|
||||
mg_tls_drop_message(c);
|
||||
mg_tls_calc_cert_verify_hash(c, tls->sighash, 0);
|
||||
@@ -1061,51 +1346,94 @@ static int mg_tls_client_recv_cert_verify(struct mg_connection *c) {
|
||||
}
|
||||
recv_buf = &c->rtls.buf[tls->recv_offset];
|
||||
if (recv_buf[0] != MG_TLS_CERTIFICATE_VERIFY) {
|
||||
mg_error(c, "expected server certificate verify but got msg 0x%02x", recv_buf[0]);
|
||||
mg_error(c, "expected server certificate verify but got msg 0x%02x",
|
||||
recv_buf[0]);
|
||||
return -1;
|
||||
}
|
||||
if (tls->recv_len < 8) {
|
||||
mg_error(c, "server certificate verify is too short: %d bytes",
|
||||
tls->recv_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Ignore CertificateVerify is strict checks are not required
|
||||
if (tls->skip_verification) {
|
||||
mg_tls_drop_message(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Extract certificate signature and verify it using pubkey and sighash
|
||||
do {
|
||||
uint16_t sigalg = MG_LOAD_BE16(recv_buf + 4);
|
||||
uint16_t siglen = MG_LOAD_BE16(recv_buf + 6);
|
||||
uint8_t *sigbuf = recv_buf + 8;
|
||||
if (siglen > tls->recv_len - 8) {
|
||||
mg_error(c, "invalid certverify signature length: %d, expected %d", siglen,
|
||||
tls->recv_len - 8);
|
||||
return -1;
|
||||
}
|
||||
MG_VERBOSE(("certificate verification, algo=%04x, siglen=%d", sigalg, siglen));
|
||||
|
||||
if (sigalg == 0x0804) { // rsa_pss_rsae_sha256
|
||||
uint8_t sig2[512]; // 2048 or 4096 bits
|
||||
struct mg_der_tlv seq, modulus, exponent;
|
||||
|
||||
if (mg_der_parse(tls->pubkey, tls->pubkeysz, &seq) <= 0 ||
|
||||
mg_der_next(&seq, &modulus) <= 0 || modulus.type != 2 ||
|
||||
mg_der_next(&seq, &exponent) <= 0 || exponent.type != 2) {
|
||||
mg_error(c, "invalid public key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mg_rsa_mod_pow(modulus.value, modulus.len, exponent.value, exponent.len,
|
||||
sigbuf, siglen, sig2, sizeof(sig2));
|
||||
|
||||
if (sig2[sizeof(sig2) - 1] != 0xbc) {
|
||||
mg_error(c, "failed to verify RSA certificate (certverify)");
|
||||
return -1;
|
||||
}
|
||||
MG_DEBUG(("certificate verification successful (RSA)"));
|
||||
} else if (sigalg == 0x0403) { // ecdsa_secp256r1_sha256
|
||||
// Extract certificate signature and verify it using pubkey and sighash
|
||||
uint8_t sig[64];
|
||||
struct mg_der_tlv seq, a, b;
|
||||
if (mg_der_to_tlv(recv_buf + 8, tls->recv_len - 8, &seq) < 0) {
|
||||
struct mg_der_tlv seq, r, s;
|
||||
if (mg_der_to_tlv(sigbuf, siglen, &seq) < 0) {
|
||||
mg_error(c, "verification message is not an ASN.1 DER sequence");
|
||||
return -1;
|
||||
}
|
||||
if (mg_der_to_tlv(seq.value, seq.len, &a) < 0) {
|
||||
if (mg_der_to_tlv(seq.value, seq.len, &r) < 0) {
|
||||
mg_error(c, "missing first part of the signature");
|
||||
return -1;
|
||||
}
|
||||
if (mg_der_to_tlv(a.value + a.len, seq.len - a.len, &b) < 0) {
|
||||
if (mg_der_to_tlv(r.value + r.len, seq.len - r.len, &s) < 0) {
|
||||
mg_error(c, "missing second part of the signature");
|
||||
return -1;
|
||||
}
|
||||
// Integers may be padded with zeroes
|
||||
if (a.len > 32) {
|
||||
a.value = a.value + (a.len - 32);
|
||||
a.len = 32;
|
||||
}
|
||||
if (b.len > 32) {
|
||||
b.value = b.value + (b.len - 32);
|
||||
b.len = 32;
|
||||
}
|
||||
if (r.len > 32) r.value = r.value + (r.len - 32), r.len = 32;
|
||||
if (s.len > 32) s.value = s.value + (s.len - 32), s.len = 32;
|
||||
|
||||
memmove(sig, a.value, a.len);
|
||||
memmove(sig + 32, b.value, b.len);
|
||||
memmove(sig, r.value, r.len);
|
||||
memmove(sig + 32, s.value, s.len);
|
||||
|
||||
if (mg_uecc_verify(tls->pubkey, tls->sighash, sizeof(tls->sighash), sig,
|
||||
mg_uecc_secp256r1()) != 1) {
|
||||
mg_error(c, "failed to verify certificate");
|
||||
mg_error(c, "failed to verify EC certificate (certverify)");
|
||||
return -1;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
MG_DEBUG(("certificate verification successful (EC)"));
|
||||
} else {
|
||||
// From
|
||||
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml:
|
||||
// 0805 = rsa_pss_rsae_sha384
|
||||
// 0806 = rsa_pss_rsae_sha512
|
||||
// 0807 = ed25519
|
||||
// 0808 = ed448
|
||||
// 0809 = rsa_pss_pss_sha256
|
||||
// 080A = rsa_pss_pss_sha384
|
||||
// 080B = rsa_pss_pss_sha512
|
||||
MG_ERROR(("unsupported certverify signature scheme: %x of %d bytes", sigalg,
|
||||
siglen));
|
||||
return -1;
|
||||
}
|
||||
mg_tls_drop_message(c);
|
||||
return 0;
|
||||
}
|
||||
@@ -1195,9 +1523,7 @@ static void mg_tls_client_handshake(struct mg_connection *c) {
|
||||
c->is_tls_hs = 0;
|
||||
mg_call(c, MG_EV_TLS_HS, NULL);
|
||||
break;
|
||||
default:
|
||||
mg_error(c, "unexpected client state: %d", tls->state);
|
||||
break;
|
||||
default: mg_error(c, "unexpected client state: %d", tls->state); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1224,9 +1550,7 @@ static void mg_tls_server_handshake(struct mg_connection *c) {
|
||||
tls->state = MG_TLS_STATE_SERVER_CONNECTED;
|
||||
c->is_tls_hs = 0;
|
||||
return;
|
||||
default:
|
||||
mg_error(c, "unexpected server state: %d", tls->state);
|
||||
break;
|
||||
default: mg_error(c, "unexpected server state: %d", tls->state); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1283,7 +1607,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
||||
c->is_client ? MG_TLS_STATE_CLIENT_START : MG_TLS_STATE_SERVER_START;
|
||||
|
||||
tls->skip_verification = opts->skip_verification;
|
||||
//tls->send.align = MG_IO_SIZE;
|
||||
// tls->send.align = MG_IO_SIZE;
|
||||
|
||||
c->tls = tls;
|
||||
c->is_tls = c->is_tls_hs = 1;
|
||||
@@ -1298,9 +1622,16 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
||||
strncpy((char *) tls->hostname, opts->name.buf, sizeof(tls->hostname) - 1);
|
||||
tls->hostname[opts->name.len] = 0;
|
||||
}
|
||||
// server CA certificate, store serial number
|
||||
if (opts->ca.len > 0) {
|
||||
if (mg_parse_pem(opts->ca, mg_str_s("CERTIFICATE"), &tls->ca_der) < 0) {
|
||||
MG_ERROR(("Failed to load certificate"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->cert.buf == NULL) {
|
||||
MG_VERBOSE(("no certificate provided"));
|
||||
MG_VERBOSE(("No certificate provided"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1312,7 +1643,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
||||
|
||||
// parse PEM or DER EC key
|
||||
if (opts->key.buf == NULL) {
|
||||
mg_error(c, "certificate provided without a private key");
|
||||
mg_error(c, "Certificate provided without a private key");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1335,7 +1666,7 @@ void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts) {
|
||||
} else if (mg_parse_pem(opts->key, mg_str_s("PRIVATE KEY"), &key) == 0) {
|
||||
mg_error(c, "PKCS8 private key format is not supported");
|
||||
} else {
|
||||
mg_error(c, "expected EC PRIVATE KEY or PRIVATE KEY");
|
||||
mg_error(c, "Expected EC PRIVATE KEY or PRIVATE KEY");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1344,6 +1675,7 @@ void mg_tls_free(struct mg_connection *c) {
|
||||
if (tls != NULL) {
|
||||
mg_iobuf_free(&tls->send);
|
||||
free((void *) tls->cert_der.buf);
|
||||
free((void *) tls->ca_der.buf);
|
||||
}
|
||||
free(c->tls);
|
||||
c->tls = NULL;
|
||||
|
||||
@@ -104,10 +104,12 @@ static void initialize_state(uint32_t state[CHACHA20_STATE_WORDS],
|
||||
const uint8_t key[CHACHA20_KEY_SIZE],
|
||||
const uint8_t nonce[CHACHA20_NONCE_SIZE],
|
||||
uint32_t counter) {
|
||||
#if 0
|
||||
#ifdef static_assert
|
||||
static_assert(sizeof(uint32_t) == 4,
|
||||
"We don't support systems that do not conform to standard of "
|
||||
"uint32_t being exact 32bit wide");
|
||||
#endif
|
||||
#endif
|
||||
state[0] = 0x61707865;
|
||||
state[1] = 0x3320646e;
|
||||
|
||||
+1579
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,7 @@
|
||||
#ifndef TLS_RSA_H
|
||||
#define TLS_RSA_H
|
||||
|
||||
#include "arch.h"
|
||||
int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t expsz, const uint8_t *msg, size_t msgsz, uint8_t *out, size_t outsz);
|
||||
|
||||
#endif // TLS_RSA_H
|
||||
@@ -42,6 +42,12 @@ bool mg_path_is_sane(const struct mg_str path);
|
||||
MG_U8P(p)[0] = ((n) >> 8U) & 255; \
|
||||
MG_U8P(p)[1] = (n) &255; \
|
||||
} while (0)
|
||||
#define MG_STORE_BE24(p, n) \
|
||||
do { \
|
||||
MG_U8P(p)[0] = ((n) >> 16U) & 255; \
|
||||
MG_U8P(p)[1] = ((n) >> 8U) & 255; \
|
||||
MG_U8P(p)[2] = (n) &255; \
|
||||
} while (0)
|
||||
|
||||
#define MG_REG(x) ((volatile uint32_t *) (x))[0]
|
||||
#define MG_BIT(x) (((uint32_t) 1U) << (x))
|
||||
|
||||
Reference in new issue
Block a user