diff --git a/include/gmssl/ocsp.h b/include/gmssl/ocsp.h index 3569f8e9..a2586e14 100644 --- a/include/gmssl/ocsp.h +++ b/include/gmssl/ocsp.h @@ -240,20 +240,33 @@ int ocsp_response_print(FILE *fp, int fmt, int ind, const char *label, /* - * OCSPResponse signing context + * OCSP context for signing and verification */ #define OCSP_MAX_REQUEST_SIZE 65536 #define OCSP_MAX_CERT_SIZE 65536 #define OCSP_MAX_EXTS_SIZE 4096 #define OCSP_MAX_CERTS_SIZE 65536 +enum { + OCSP_VERIFY_REASON_NONE = 0, + OCSP_VERIFY_REASON_REVOKED, + OCSP_VERIFY_REASON_UNKNOWN, + OCSP_VERIFY_REASON_MALFORMED_RESPONSE, + OCSP_VERIFY_REASON_RESPONSE_STATUS_NOT_SUCCESSFUL, + OCSP_VERIFY_REASON_UNSUPPORTED_RESPONSE_TYPE, + OCSP_VERIFY_REASON_BAD_SIGNATURE, + OCSP_VERIFY_REASON_BAD_RESPONDER_ID, + OCSP_VERIFY_REASON_NO_MATCHING_SINGLE_RESPONSE, + OCSP_VERIFY_REASON_THIS_UPDATE_IN_FUTURE, + OCSP_VERIFY_REASON_NEXT_UPDATE_EXPIRED, +}; + typedef struct { const uint8_t *req; size_t reqlen; const uint8_t *issuer_cert; size_t issuer_cert_len; - int response_status; int responder_id_type; time_t produced_at; time_t next_update; @@ -265,13 +278,16 @@ typedef struct { size_t response_exts_len; const uint8_t *certs; size_t certs_len; + + time_t verify_time; + int max_clock_skew; + int reason; } OCSP_SIGN_CTX; int ocsp_sign_init(OCSP_SIGN_CTX *ctx, const uint8_t *req, size_t reqlen, const uint8_t *issuer_cert, size_t issuer_cert_len); -int ocsp_sign_set_response_status(OCSP_SIGN_CTX *ctx, int response_status); int ocsp_sign_set_responder_id_type(OCSP_SIGN_CTX *ctx, int responder_id_type); int ocsp_sign_set_produced_at(OCSP_SIGN_CTX *ctx, time_t produced_at); int ocsp_sign_set_next_update(OCSP_SIGN_CTX *ctx, time_t next_update); @@ -286,6 +302,18 @@ int ocsp_sign(OCSP_SIGN_CTX *ctx, X509_KEY *sign_key, const char *signer_id, size_t signer_id_len, uint8_t **out, size_t *outlen); +int ocsp_verify_init(OCSP_SIGN_CTX *ctx, + const uint8_t *req, size_t reqlen, + const uint8_t *issuer_cert, size_t issuer_cert_len); +int ocsp_verify_set_time(OCSP_SIGN_CTX *ctx, time_t verify_time); +int ocsp_verify_set_clock_skew(OCSP_SIGN_CTX *ctx, int seconds); +int ocsp_verify_set_certs(OCSP_SIGN_CTX *ctx, const uint8_t *certs, size_t certs_len); +int ocsp_verify(OCSP_SIGN_CTX *ctx, + const uint8_t *resp, size_t resplen, + const uint8_t *signer_cert, size_t signer_cert_len, + const char *signer_id, size_t signer_id_len, + int *reason); + #ifdef __cplusplus } diff --git a/src/ocsp.c b/src/ocsp.c index 024a9667..b07e26e8 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -1872,7 +1872,6 @@ int ocsp_sign_init(OCSP_SIGN_CTX *ctx, ctx->reqlen = reqlen; ctx->issuer_cert = issuer_cert; ctx->issuer_cert_len = issuer_cert_len; - ctx->response_status = OCSP_response_status_successful; ctx->responder_id_type = OCSP_responder_id_by_name; ctx->produced_at = time(NULL); ctx->next_update = (time_t)-1; @@ -1881,16 +1880,6 @@ int ocsp_sign_init(OCSP_SIGN_CTX *ctx, return 1; } -int ocsp_sign_set_response_status(OCSP_SIGN_CTX *ctx, int response_status) -{ - if (!ctx || !ocsp_response_status_name(response_status)) { - error_print(); - return -1; - } - ctx->response_status = response_status; - return 1; -} - int ocsp_sign_set_responder_id_type(OCSP_SIGN_CTX *ctx, int responder_id_type) { if (!ctx) { @@ -2009,10 +1998,6 @@ int ocsp_sign(OCSP_SIGN_CTX *ctx, return -1; } - if (ctx->response_status != OCSP_response_status_successful) { - return ocsp_response_to_der(ctx->response_status, NULL, 0, out, outlen); - } - if (!signer_cert || !signer_cert_len || !sign_key || this_update == (time_t)-1 || !ocsp_cert_status_name(cert_status) @@ -2146,3 +2131,421 @@ int ocsp_sign(OCSP_SIGN_CTX *ctx, } return 1; } + +static void ocsp_verify_set_reason(OCSP_SIGN_CTX *ctx, int *reason, int value) +{ + if (ctx) { + ctx->reason = value; + } + if (reason) { + *reason = value; + } +} + +static int ocsp_verify_responder_id(int responder_id_type, + const uint8_t *responder_id, size_t responder_id_len, + const uint8_t *signer_cert, size_t signer_cert_len) +{ + uint8_t expected[OCSP_MAX_RESPONDER_ID_SIZE]; + size_t expected_len = 0; + + if (!responder_id || !responder_id_len || !signer_cert || !signer_cert_len) { + error_print(); + return -1; + } + if (ocsp_sign_get_responder_id(responder_id_type, signer_cert, signer_cert_len, + expected, &expected_len, sizeof(expected)) != 1) { + error_print(); + return -1; + } + if (expected_len != responder_id_len + || memcmp(expected, responder_id, responder_id_len) != 0) { + return 0; + } + return 1; +} + +static int ocsp_verify_signature(const uint8_t *response_data, size_t response_data_len, + int signature_algor, const uint8_t *signature, size_t signature_len, + const uint8_t *signer_cert, size_t signer_cert_len, + const char *signer_id, size_t signer_id_len) +{ + X509_KEY public_key; + X509_SIGN_CTX verify_ctx; + int sign_algor; + const void *sign_args = signer_id; + size_t sign_args_len = signer_id_len; + + if (!response_data || !response_data_len + || !signature || !signature_len + || !signer_cert || !signer_cert_len + || (!signer_id && signer_id_len) + || signer_id_len > SM2_MAX_ID_LENGTH) { + error_print(); + return -1; + } + if (x509_cert_get_subject_public_key(signer_cert, signer_cert_len, &public_key) != 1 + || x509_key_get_sign_algor(&public_key, &sign_algor) != 1) { + error_print(); + return -1; + } + if (signature_algor != sign_algor) { + error_print(); + return -1; + } + if (!sign_args && public_key.algor == OID_ec_public_key && public_key.algor_param == OID_sm2) { + sign_args = SM2_DEFAULT_ID; + sign_args_len = SM2_DEFAULT_ID_LENGTH; + } + if (x509_verify_init(&verify_ctx, &public_key, sign_args, sign_args_len, + signature, signature_len) != 1 + || x509_verify_update(&verify_ctx, response_data, response_data_len) != 1 + || x509_verify_finish(&verify_ctx) != 1) { + error_print(); + return -1; + } + return 1; +} + +static int ocsp_single_response_match_request(const uint8_t *single_response, size_t single_response_len, + int req_hash_algor, + const uint8_t *req_issuer_name_hash, size_t req_issuer_name_hash_len, + const uint8_t *req_issuer_key_hash, size_t req_issuer_key_hash_len, + const uint8_t *req_serial_number, size_t req_serial_number_len, + int *cert_status, time_t *revocation_time, int *revocation_reason, + time_t *this_update, time_t *next_update) +{ + const uint8_t *p = single_response; + size_t len = single_response_len; + int hash_algor; + const uint8_t *issuer_name_hash; + size_t issuer_name_hash_len; + const uint8_t *issuer_key_hash; + size_t issuer_key_hash_len; + const uint8_t *serial_number; + size_t serial_number_len; + const uint8_t *exts; + size_t extslen; + + if (!single_response || !single_response_len + || !req_issuer_name_hash || !req_issuer_name_hash_len + || !req_issuer_key_hash || !req_issuer_key_hash_len + || !req_serial_number || !req_serial_number_len + || !cert_status || !revocation_time || !revocation_reason + || !this_update || !next_update) { + error_print(); + return -1; + } + if (ocsp_single_response_from_der(&hash_algor, + &issuer_name_hash, &issuer_name_hash_len, + &issuer_key_hash, &issuer_key_hash_len, + &serial_number, &serial_number_len, + cert_status, revocation_time, revocation_reason, + this_update, next_update, + &exts, &extslen, &p, &len) != 1 + || asn1_length_is_zero(len) != 1) { + error_print(); + return -1; + } + if (hash_algor != req_hash_algor + || issuer_name_hash_len != req_issuer_name_hash_len + || memcmp(issuer_name_hash, req_issuer_name_hash, issuer_name_hash_len) != 0 + || issuer_key_hash_len != req_issuer_key_hash_len + || memcmp(issuer_key_hash, req_issuer_key_hash, issuer_key_hash_len) != 0 + || serial_number_len != req_serial_number_len + || memcmp(serial_number, req_serial_number, serial_number_len) != 0) { + return 0; + } + return 1; +} + +static int ocsp_verify_find_single_response( + const uint8_t *single_response_first, size_t single_response_first_len, + const uint8_t *single_response_others, size_t single_response_others_len, + int req_hash_algor, + const uint8_t *req_issuer_name_hash, size_t req_issuer_name_hash_len, + const uint8_t *req_issuer_key_hash, size_t req_issuer_key_hash_len, + const uint8_t *req_serial_number, size_t req_serial_number_len, + int *cert_status, time_t *revocation_time, int *revocation_reason, + time_t *this_update, time_t *next_update) +{ + const uint8_t *single_response; + size_t single_response_len; + int ret; + + ret = ocsp_single_response_match_request(single_response_first, single_response_first_len, + req_hash_algor, + req_issuer_name_hash, req_issuer_name_hash_len, + req_issuer_key_hash, req_issuer_key_hash_len, + req_serial_number, req_serial_number_len, + cert_status, revocation_time, revocation_reason, + this_update, next_update); + if (ret != 0) { + return ret; + } + while (single_response_others_len) { + if (asn1_any_from_der(&single_response, &single_response_len, + &single_response_others, &single_response_others_len) != 1) { + error_print(); + return -1; + } + ret = ocsp_single_response_match_request(single_response, single_response_len, + req_hash_algor, + req_issuer_name_hash, req_issuer_name_hash_len, + req_issuer_key_hash, req_issuer_key_hash_len, + req_serial_number, req_serial_number_len, + cert_status, revocation_time, revocation_reason, + this_update, next_update); + if (ret != 0) { + return ret; + } + } + return 0; +} + +int ocsp_verify_init(OCSP_SIGN_CTX *ctx, + const uint8_t *req, size_t reqlen, + const uint8_t *issuer_cert, size_t issuer_cert_len) +{ + int hash_algor; + const uint8_t *issuer_name_hash; + size_t issuer_name_hash_len; + const uint8_t *issuer_key_hash; + size_t issuer_key_hash_len; + const uint8_t *serial_number; + size_t serial_number_len; + + if (!ctx || !req || !reqlen || !issuer_cert || !issuer_cert_len) { + error_print(); + return -1; + } + memset(ctx, 0, sizeof(*ctx)); + + if (ocsp_sign_get_request_item(req, reqlen, + &hash_algor, + &issuer_name_hash, &issuer_name_hash_len, + &issuer_key_hash, &issuer_key_hash_len, + &serial_number, &serial_number_len) != 1 + || ocsp_sign_check_issuer_cert(issuer_cert, issuer_cert_len, + hash_algor, + issuer_name_hash, issuer_name_hash_len, + issuer_key_hash, issuer_key_hash_len) != 1) { + error_print(); + return -1; + } + + ctx->req = req; + ctx->reqlen = reqlen; + ctx->issuer_cert = issuer_cert; + ctx->issuer_cert_len = issuer_cert_len; + ctx->verify_time = time(NULL); + ctx->max_clock_skew = 300; + ctx->reason = OCSP_VERIFY_REASON_NONE; + + return 1; +} + +int ocsp_verify_set_time(OCSP_SIGN_CTX *ctx, time_t verify_time) +{ + if (!ctx || verify_time == (time_t)-1) { + error_print(); + return -1; + } + ctx->verify_time = verify_time; + return 1; +} + +int ocsp_verify_set_clock_skew(OCSP_SIGN_CTX *ctx, int seconds) +{ + if (!ctx || seconds < 0) { + error_print(); + return -1; + } + ctx->max_clock_skew = seconds; + return 1; +} + +int ocsp_verify_set_certs(OCSP_SIGN_CTX *ctx, const uint8_t *certs, size_t certs_len) +{ + if (!ctx || (!certs && certs_len) || certs_len > OCSP_MAX_CERTS_SIZE) { + error_print(); + return -1; + } + ctx->certs = certs; + ctx->certs_len = certs_len; + return 1; +} + +int ocsp_verify(OCSP_SIGN_CTX *ctx, + const uint8_t *resp, size_t resplen, + const uint8_t *signer_cert, size_t signer_cert_len, + const char *signer_id, size_t signer_id_len, + int *reason) +{ + const uint8_t *p; + size_t len; + int response_status; + const uint8_t *basic_response; + size_t basic_response_len; + const uint8_t *response_data; + size_t response_data_len; + int signature_algor; + const uint8_t *signature; + size_t signature_len; + const uint8_t *certs; + size_t certs_len; + int responder_id_type; + const uint8_t *responder_id; + size_t responder_id_len; + time_t produced_at; + const uint8_t *single_response_first; + size_t single_response_first_len; + const uint8_t *single_response_others; + size_t single_response_others_len; + const uint8_t *response_exts; + size_t response_exts_len; + int hash_algor; + const uint8_t *issuer_name_hash; + size_t issuer_name_hash_len; + const uint8_t *issuer_key_hash; + size_t issuer_key_hash_len; + const uint8_t *serial_number; + size_t serial_number_len; + int cert_status; + time_t revocation_time; + int revocation_reason; + time_t this_update; + time_t next_update; + int ret; + + if (!ctx || !ctx->req || !ctx->reqlen + || !ctx->issuer_cert || !ctx->issuer_cert_len + || !resp || !resplen + || !signer_cert || !signer_cert_len + || (!signer_id && signer_id_len) + || signer_id_len > SM2_MAX_ID_LENGTH) { + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_MALFORMED_RESPONSE); + error_print(); + return -1; + } + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_NONE); + + p = resp; + len = resplen; + if (ocsp_response_from_der(&response_status, &basic_response, &basic_response_len, + &p, &len) != 1 + || asn1_length_is_zero(len) != 1) { + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_MALFORMED_RESPONSE); + error_print(); + return -1; + } + if (response_status != OCSP_response_status_successful) { + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_RESPONSE_STATUS_NOT_SUCCESSFUL); + return -1; + } + + p = basic_response; + len = basic_response_len; + if (ocsp_basic_response_from_der(&response_data, &response_data_len, + &signature_algor, &signature, &signature_len, + &certs, &certs_len, &p, &len) != 1 + || asn1_length_is_zero(len) != 1) { + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_MALFORMED_RESPONSE); + error_print(); + return -1; + } + + p = response_data; + len = response_data_len; + if (ocsp_response_data_from_der(&responder_id_type, + &responder_id, &responder_id_len, + &produced_at, + &single_response_first, &single_response_first_len, + &single_response_others, &single_response_others_len, + &response_exts, &response_exts_len, + &p, &len) != 1 + || asn1_length_is_zero(len) != 1) { + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_MALFORMED_RESPONSE); + error_print(); + return -1; + } + + ret = ocsp_verify_responder_id(responder_id_type, + responder_id, responder_id_len, + signer_cert, signer_cert_len); + if (ret < 0) { + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_BAD_RESPONDER_ID); + error_print(); + return -1; + } + if (ret == 0) { + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_BAD_RESPONDER_ID); + return -1; + } + if (ocsp_verify_signature(response_data, response_data_len, + signature_algor, signature, signature_len, + signer_cert, signer_cert_len, + signer_id, signer_id_len) != 1) { + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_BAD_SIGNATURE); + error_print(); + return -1; + } + + if (ocsp_sign_get_request_item(ctx->req, ctx->reqlen, + &hash_algor, + &issuer_name_hash, &issuer_name_hash_len, + &issuer_key_hash, &issuer_key_hash_len, + &serial_number, &serial_number_len) != 1 + || ocsp_sign_check_issuer_cert(ctx->issuer_cert, ctx->issuer_cert_len, + hash_algor, + issuer_name_hash, issuer_name_hash_len, + issuer_key_hash, issuer_key_hash_len) != 1) { + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_MALFORMED_RESPONSE); + error_print(); + return -1; + } + + ret = ocsp_verify_find_single_response(single_response_first, single_response_first_len, + single_response_others, single_response_others_len, + hash_algor, + issuer_name_hash, issuer_name_hash_len, + issuer_key_hash, issuer_key_hash_len, + serial_number, serial_number_len, + &cert_status, &revocation_time, &revocation_reason, + &this_update, &next_update); + if (ret < 0) { + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_MALFORMED_RESPONSE); + error_print(); + return -1; + } + if (ret == 0) { + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_NO_MATCHING_SINGLE_RESPONSE); + return -1; + } + + if (this_update > ctx->verify_time + ctx->max_clock_skew) { + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_THIS_UPDATE_IN_FUTURE); + return -1; + } + if (next_update != (time_t)-1 + && ctx->verify_time - ctx->max_clock_skew > next_update) { + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_NEXT_UPDATE_EXPIRED); + return -1; + } + + switch (cert_status) { + case OCSP_cert_status_good: + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_NONE); + return 1; + case OCSP_cert_status_revoked: + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_REVOKED); + return 0; + case OCSP_cert_status_unknown: + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_UNKNOWN); + return 0; + default: + ocsp_verify_set_reason(ctx, reason, OCSP_VERIFY_REASON_MALFORMED_RESPONSE); + error_print(); + return -1; + } +}