diff --git a/mongoose.c b/mongoose.c index b9af66e0..2663634c 100644 --- a/mongoose.c +++ b/mongoose.c @@ -17600,7 +17600,6 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert, return 0; } } else { - uint8_t r; const uint8_t *n; size_t nlen; uint8_t sig2[512]; // 4096 bits @@ -17620,15 +17619,7 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert, return 0; } - r = 0; - { - const uint8_t *p, *q; - size_t i; - p = sig2 + nlen - cert->tbshashsz; - q = cert->tbshash; - for (i = 0; i < cert->tbshashsz; i++) r |= (uint8_t)(*p++ ^ *q++); - } - return r == 0; + return mg_rsa_pkcs_verify(sig2, nlen, cert->tbshash, cert->tbshashsz); } } @@ -21721,6 +21712,43 @@ bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash) { return pss_verify_sha256(em, nlen, mhash); } +// Full RSASSA-PKCS1-v1_5-VERIFY for rsa_pkcs1_sha256/sha384 (RFC 8017 9.2) +// em - output of RSA public-key primitive (nlen bytes) +// nlen - modulus byte length +// hash - SHA-256 or SHA-384 of the certificate TBS data +// hashlen - hash length, 32 for SHA-256 or 48 for SHA-384 +static bool pkcs1_v15_verify(const uint8_t *em, size_t nlen, + const uint8_t *hash, size_t hashlen) { + static const uint8_t sha256_di[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; + static const uint8_t sha384_di[] = { + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}; + const uint8_t *di = hashlen == 32 ? sha256_di : sha384_di; + size_t disz = hashlen == 32 ? sizeof(sha256_di) : sizeof(sha384_di); + size_t pslen, i; + uint8_t bad = 0; + + if (hashlen != 32 && hashlen != 48) return false; + if (nlen < 3 + 8 + disz + hashlen) return false; + pslen = nlen - 3 - disz - hashlen; + + bad |= (uint8_t) (em[0] | (em[1] ^ 0x01)); + for (i = 0; i < pslen; i++) bad |= (uint8_t) (em[2 + i] ^ 0xff); + bad |= em[2 + pslen]; + for (i = 0; i < disz; i++) bad |= (uint8_t) (em[3 + pslen + i] ^ di[i]); + for (i = 0; i < hashlen; i++) + bad |= (uint8_t) (em[3 + pslen + disz + i] ^ hash[i]); + + return bad == 0; +} + +bool mg_rsa_pkcs_verify(const uint8_t *em, size_t nlen, const uint8_t *hash, + size_t hashlen) { + return pkcs1_v15_verify(em, nlen, hash, hashlen); +} + #endif /* MG_TLS == MG_TLS_BUILTIN */ #ifdef MG_ENABLE_LINES diff --git a/mongoose.h b/mongoose.h index 4c879fa3..077cdab9 100644 --- a/mongoose.h +++ b/mongoose.h @@ -3479,6 +3479,8 @@ int mg_rsa_crt_sign(const uint8_t *em, size_t em_len, const uint8_t *q, size_t q_len, const uint8_t *qInv, size_t qInv_len, uint8_t *signature, size_t sig_len); +bool mg_rsa_pkcs_verify(const uint8_t *em, size_t nlen, const uint8_t *hash, + size_t hashlen); bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash); #endif // TLS_RSA_H diff --git a/src/tls_builtin.c b/src/tls_builtin.c index 8083981f..c1c40989 100644 --- a/src/tls_builtin.c +++ b/src/tls_builtin.c @@ -1704,7 +1704,6 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert, return 0; } } else { - uint8_t r; const uint8_t *n; size_t nlen; uint8_t sig2[512]; // 4096 bits @@ -1724,15 +1723,7 @@ static int mg_tls_verify_cert_signature(const struct mg_tls_cert *cert, return 0; } - r = 0; - { - const uint8_t *p, *q; - size_t i; - p = sig2 + nlen - cert->tbshashsz; - q = cert->tbshash; - for (i = 0; i < cert->tbshashsz; i++) r |= (uint8_t)(*p++ ^ *q++); - } - return r == 0; + return mg_rsa_pkcs_verify(sig2, nlen, cert->tbshash, cert->tbshashsz); } } diff --git a/src/tls_rsa.c b/src/tls_rsa.c index ff214c49..b5edc47b 100644 --- a/src/tls_rsa.c +++ b/src/tls_rsa.c @@ -950,4 +950,41 @@ bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash) { return pss_verify_sha256(em, nlen, mhash); } +// Full RSASSA-PKCS1-v1_5-VERIFY for rsa_pkcs1_sha256/sha384 (RFC 8017 9.2) +// em - output of RSA public-key primitive (nlen bytes) +// nlen - modulus byte length +// hash - SHA-256 or SHA-384 of the certificate TBS data +// hashlen - hash length, 32 for SHA-256 or 48 for SHA-384 +static bool pkcs1_v15_verify(const uint8_t *em, size_t nlen, + const uint8_t *hash, size_t hashlen) { + static const uint8_t sha256_di[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; + static const uint8_t sha384_di[] = { + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}; + const uint8_t *di = hashlen == 32 ? sha256_di : sha384_di; + size_t disz = hashlen == 32 ? sizeof(sha256_di) : sizeof(sha384_di); + size_t pslen, i; + uint8_t bad = 0; + + if (hashlen != 32 && hashlen != 48) return false; + if (nlen < 3 + 8 + disz + hashlen) return false; + pslen = nlen - 3 - disz - hashlen; + + bad |= (uint8_t) (em[0] | (em[1] ^ 0x01)); + for (i = 0; i < pslen; i++) bad |= (uint8_t) (em[2 + i] ^ 0xff); + bad |= em[2 + pslen]; + for (i = 0; i < disz; i++) bad |= (uint8_t) (em[3 + pslen + i] ^ di[i]); + for (i = 0; i < hashlen; i++) + bad |= (uint8_t) (em[3 + pslen + disz + i] ^ hash[i]); + + return bad == 0; +} + +bool mg_rsa_pkcs_verify(const uint8_t *em, size_t nlen, const uint8_t *hash, + size_t hashlen) { + return pkcs1_v15_verify(em, nlen, hash, hashlen); +} + #endif /* MG_TLS == MG_TLS_BUILTIN */ diff --git a/src/tls_rsa.h b/src/tls_rsa.h index 5beb0b4c..6b9ada27 100644 --- a/src/tls_rsa.h +++ b/src/tls_rsa.h @@ -10,5 +10,7 @@ int mg_rsa_crt_sign(const uint8_t *em, size_t em_len, const uint8_t *q, size_t q_len, const uint8_t *qInv, size_t qInv_len, uint8_t *signature, size_t sig_len); +bool mg_rsa_pkcs_verify(const uint8_t *em, size_t nlen, const uint8_t *hash, + size_t hashlen); bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash); #endif // TLS_RSA_H diff --git a/test/unit_test.c b/test/unit_test.c index c5f3d9b5..ef697646 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -4311,6 +4311,51 @@ static void test_rsa(void) { ASSERT(!mg_rsa_verify(em, 256, tv_mhash)); em[50] = tv_em[50]; } + { + static const uint8_t sha256_di[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; + static const uint8_t sha384_di[] = { + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}; + uint8_t hash[48], em[256]; + size_t i, pslen; + + for (i = 0; i < sizeof(hash); i++) hash[i] = (uint8_t) i; + + memset(em, 0xff, sizeof(em)); + em[0] = 0, em[1] = 1; + pslen = sizeof(em) - 3 - sizeof(sha256_di) - 32; + em[2 + pslen] = 0; + memcpy(em + 3 + pslen, sha256_di, sizeof(sha256_di)); + memcpy(em + 3 + pslen + sizeof(sha256_di), hash, 32); + ASSERT(mg_rsa_pkcs_verify(em, sizeof(em), hash, 32)); + + em[1] = 2; // bad block type + ASSERT(!mg_rsa_pkcs_verify(em, sizeof(em), hash, 32)); + em[1] = 1; + em[10] = 0; // bad padding + ASSERT(!mg_rsa_pkcs_verify(em, sizeof(em), hash, 32)); + em[10] = 0xff; + em[3 + pslen + 15] ^= 1; // bad DigestInfo + ASSERT(!mg_rsa_pkcs_verify(em, sizeof(em), hash, 32)); + em[3 + pslen + 15] ^= 1; + em[sizeof(em) - 1] ^= 1; // bad hash + ASSERT(!mg_rsa_pkcs_verify(em, sizeof(em), hash, 32)); + + memset(em, 0, sizeof(em)); // former suffix-only bypass + memcpy(em + sizeof(em) - 32, hash, 32); + ASSERT(!mg_rsa_pkcs_verify(em, sizeof(em), hash, 32)); + + memset(em, 0xff, sizeof(em)); + em[0] = 0, em[1] = 1; + pslen = sizeof(em) - 3 - sizeof(sha384_di) - 48; + em[2 + pslen] = 0; + memcpy(em + 3 + pslen, sha384_di, sizeof(sha384_di)); + memcpy(em + 3 + pslen + sizeof(sha384_di), hash, 48); + ASSERT(mg_rsa_pkcs_verify(em, sizeof(em), hash, 48)); + ASSERT(!mg_rsa_pkcs_verify(em, sizeof(em), hash, 20)); + } #endif }