From aaad273ec1ead7e2203e66536b0c80fce8c72e49 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 30 Jan 2020 00:02:27 +0000 Subject: [PATCH] pkcs1: recognize explicit curve parameters Add support for explicit curve parameters according to RFC 3279. This allows an exploitation attempt of CVE-2020-0601 to be detected through the pkcs1.specifiedCurve_element filter name. Be aware though that the certificate is encrypted in TLS 1.3, so a negative match does not imply that no exploitation has happened. While these definitions are technically not part of PKCS #1, the PKIXAlgs module is part of the pkcs1 dissector for historical reasons. It probably makes sense splitting it into a separate pkixalgs dissector, but that would result in field name changes. Defer that for now. Bug: 16340 Change-Id: Ia9d47a8337d6246f52983460580310b12e5709cf Reviewed-on: https://code.wireshark.org/review/35986 Petri-Dish: Peter Wu Tested-by: Petri Dish Buildbot Reviewed-by: Alexis La Goutte --- epan/dissectors/asn1/pkcs1/PKIXAlgs-2009.asn | 57 ++++-- epan/dissectors/asn1/pkcs1/pkcs1.cnf | 8 +- epan/dissectors/packet-pkcs1.c | 195 ++++++++++++++++++- 3 files changed, 245 insertions(+), 15 deletions(-) diff --git a/epan/dissectors/asn1/pkcs1/PKIXAlgs-2009.asn b/epan/dissectors/asn1/pkcs1/PKIXAlgs-2009.asn index 910a674f17..b19dcfbfd0 100644 --- a/epan/dissectors/asn1/pkcs1/PKIXAlgs-2009.asn +++ b/epan/dissectors/asn1/pkcs1/PKIXAlgs-2009.asn @@ -298,19 +298,52 @@ Digest ::= OCTET STRING -- Parameters and Keys for both Restricted and Unrestricted EC ECParameters ::= CHOICE { - namedCurve --CURVE.&id({NamedCurve}) -- OBJECT IDENTIFIER - -- implicitCurve NULL - -- implicitCurve MUST NOT be used in PKIX - -- specifiedCurve SpecifiedCurve - -- specifiedCurve MUST NOT be used in PKIX - -- Details for specifiedCurve can be found in [X9.62] - -- Any future additions to this CHOICE should be coordinated - -- with ANSI X.9. + specifiedCurve SpecifiedECDomain, -- From RFC 3279 / SEC 1 + namedCurve OBJECT IDENTIFIER + -- implicitlyCA NULL + + -- Wireshark note: the PKIXAlgs-2009 module from RFC 5912 only allows + -- namedCurve to be used. This ECParameters type is however a subset of + -- the type defined in X9.62 and RFC 3279 which additionally defines + -- 'specified' and 'implicitlyCA'. Since the explicitly specified curve + -- parameters were spotted in the wild as part of exploiting CVE-2020-0601, + -- we will include it here anyway. We do not include implicitlyCA, it does + -- not appear to be supported by OpenSSL, unlike the other two fields. } - -- If you need to be able to decode ANSI X.9 parameter structures, - -- uncomment the implicitCurve and specifiedCurve above, and also - -- uncomment the following: - -- (WITH COMPONENTS {namedCurve PRESENT}) + (WITH COMPONENTS {namedCurve PRESENT}) + + -- ECParameters from RFC 3279, but renamed to SpecifiedECDomain (RFC 5480). + -- Adapted from https://tools.ietf.org/html/rfc3279#page-14 + + SpecifiedECDomain ::= SEQUENCE { + version ECPVer, -- version is always 1 + fieldID FieldID, -- identifies the finite field over + -- which the curve is defined + curve Curve, -- coefficients a and b of the + -- elliptic curve + base ECPoint, -- specifies the base point P + -- on the elliptic curve + order INTEGER, -- the order n of the base point + cofactor INTEGER OPTIONAL -- The integer h = #E(Fq)/n + } + + ECPVer ::= INTEGER {ecpVer1(1)} + + FieldID ::= SEQUENCE { + fieldType OBJECT IDENTIFIER, + parameters ANY DEFINED BY fieldType } + + Curve ::= SEQUENCE { + a FieldElement, + b FieldElement, + seed BIT STRING OPTIONAL } + + FieldElement ::= OCTET STRING + + -- FieldID.parameters definitions, OIDs are listed in pkcs1.cnf + -- https://tools.ietf.org/html/rfc3279#page-21 + + Prime-p ::= INTEGER -- Finite field F(p), where p is an odd prime -- Sec 2.1.1.1 Named Curve diff --git a/epan/dissectors/asn1/pkcs1/pkcs1.cnf b/epan/dissectors/asn1/pkcs1/pkcs1.cnf index ed0c7e8dd1..55622890e2 100644 --- a/epan/dissectors/asn1/pkcs1/pkcs1.cnf +++ b/epan/dissectors/asn1/pkcs1/pkcs1.cnf @@ -14,6 +14,12 @@ DigestInfo #.FIELD_RENAME +#.FN_PARS FieldID/fieldType + FN_VARIANT = _str VAL_PTR = &actx->external.direct_reference + +#.FN_BODY FieldID/parameters + offset = call_ber_oid_callback(actx->external.direct_reference, tvb, offset, actx->pinfo, tree, NULL); + #.REGISTER DSA-Params B "1.2.840.10040.4.1" "id-dsa" DomainParameters B "1.2.840.10046.2.1" "dhpublicnumber" @@ -23,11 +29,11 @@ ECParameters B "1.3.132.1.12" "id-ecDH" ECParameters B "1.2.840.10045.2.13" "id-ecMQV" RSASSA-PSS-params B "1.2.840.113549.1.1.10" "id-RSASSA-PSS" HashAlgorithm B "1.2.840.113549.1.1.8" "id-mgf1" +Prime-p B "1.2.840.10045.1.1" "prime-field" #.NO_EMIT DSAPublicKey DHPublicKey -ECPoint DSA-Sig-Value ECDSA-Sig-Value diff --git a/epan/dissectors/packet-pkcs1.c b/epan/dissectors/packet-pkcs1.c index 35c2b83e81..1a9fc30105 100644 --- a/epan/dissectors/packet-pkcs1.c +++ b/epan/dissectors/packet-pkcs1.c @@ -45,6 +45,7 @@ static int hf_pkcs1_KEA_Params_Id_PDU = -1; /* KEA_Params_Id */ static int hf_pkcs1_HashAlgorithm_PDU = -1; /* HashAlgorithm */ static int hf_pkcs1_RSASSA_PSS_params_PDU = -1; /* RSASSA_PSS_params */ static int hf_pkcs1_ECParameters_PDU = -1; /* ECParameters */ +static int hf_pkcs1_Prime_p_PDU = -1; /* Prime_p */ static int hf_pkcs1_modulus = -1; /* INTEGER */ static int hf_pkcs1_publicExponent = -1; /* INTEGER */ static int hf_pkcs1_digestAlgorithm = -1; /* DigestAlgorithmIdentifier */ @@ -60,7 +61,18 @@ static int hf_pkcs1_hashAlgorithm = -1; /* HashAlgorithm */ static int hf_pkcs1_maskGenAlgorithm = -1; /* MaskGenAlgorithm */ static int hf_pkcs1_saltLength = -1; /* INTEGER */ static int hf_pkcs1_trailerField = -1; /* INTEGER */ +static int hf_pkcs1_specifiedCurve = -1; /* SpecifiedECDomain */ static int hf_pkcs1_namedCurve = -1; /* OBJECT_IDENTIFIER */ +static int hf_pkcs1_version = -1; /* ECPVer */ +static int hf_pkcs1_fieldID = -1; /* FieldID */ +static int hf_pkcs1_curve = -1; /* Curve */ +static int hf_pkcs1_base = -1; /* ECPoint */ +static int hf_pkcs1_order = -1; /* INTEGER */ +static int hf_pkcs1_cofactor = -1; /* INTEGER */ +static int hf_pkcs1_fieldType = -1; /* T_fieldType */ +static int hf_pkcs1_parameters = -1; /* T_parameters */ +static int hf_pkcs1_a = -1; /* FieldElement */ +static int hf_pkcs1_b = -1; /* FieldElement */ static int hf_pkcs1_r = -1; /* INTEGER */ static int hf_pkcs1_s = -1; /* INTEGER */ @@ -78,6 +90,9 @@ static gint ett_pkcs1_DomainParameters = -1; static gint ett_pkcs1_ValidationParams = -1; static gint ett_pkcs1_RSASSA_PSS_params = -1; static gint ett_pkcs1_ECParameters = -1; +static gint ett_pkcs1_SpecifiedECDomain = -1; +static gint ett_pkcs1_FieldID = -1; +static gint ett_pkcs1_Curve = -1; static gint ett_pkcs1_DSA_Sig_Value = -1; static gint ett_pkcs1_ECDSA_Sig_Value = -1; @@ -255,6 +270,111 @@ dissect_pkcs1_RSASSA_PSS_params(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, in +static int +dissect_pkcs1_ECPoint(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { + offset = dissect_ber_octet_string(implicit_tag, actx, tree, tvb, offset, hf_index, + NULL); + + return offset; +} + + +static const value_string pkcs1_ECPVer_vals[] = { + { 1, "ecpVer1" }, + { 0, NULL } +}; + + +static int +dissect_pkcs1_ECPVer(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { + offset = dissect_ber_integer(implicit_tag, actx, tree, tvb, offset, hf_index, + NULL); + + return offset; +} + + + +static int +dissect_pkcs1_T_fieldType(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { + offset = dissect_ber_object_identifier_str(implicit_tag, actx, tree, tvb, offset, hf_index, &actx->external.direct_reference); + + return offset; +} + + + +static int +dissect_pkcs1_T_parameters(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { +#line 21 "./asn1/pkcs1/pkcs1.cnf" + offset = call_ber_oid_callback(actx->external.direct_reference, tvb, offset, actx->pinfo, tree, NULL); + + + + return offset; +} + + +static const ber_sequence_t FieldID_sequence[] = { + { &hf_pkcs1_fieldType , BER_CLASS_UNI, BER_UNI_TAG_OID, BER_FLAGS_NOOWNTAG, dissect_pkcs1_T_fieldType }, + { &hf_pkcs1_parameters , BER_CLASS_ANY, 0, BER_FLAGS_NOOWNTAG, dissect_pkcs1_T_parameters }, + { NULL, 0, 0, 0, NULL } +}; + +static int +dissect_pkcs1_FieldID(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { + offset = dissect_ber_sequence(implicit_tag, actx, tree, tvb, offset, + FieldID_sequence, hf_index, ett_pkcs1_FieldID); + + return offset; +} + + + +static int +dissect_pkcs1_FieldElement(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { + offset = dissect_ber_octet_string(implicit_tag, actx, tree, tvb, offset, hf_index, + NULL); + + return offset; +} + + +static const ber_sequence_t Curve_sequence[] = { + { &hf_pkcs1_a , BER_CLASS_UNI, BER_UNI_TAG_OCTETSTRING, BER_FLAGS_NOOWNTAG, dissect_pkcs1_FieldElement }, + { &hf_pkcs1_b , BER_CLASS_UNI, BER_UNI_TAG_OCTETSTRING, BER_FLAGS_NOOWNTAG, dissect_pkcs1_FieldElement }, + { &hf_pkcs1_seed , BER_CLASS_UNI, BER_UNI_TAG_BITSTRING, BER_FLAGS_OPTIONAL|BER_FLAGS_NOOWNTAG, dissect_pkcs1_BIT_STRING }, + { NULL, 0, 0, 0, NULL } +}; + +static int +dissect_pkcs1_Curve(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { + offset = dissect_ber_sequence(implicit_tag, actx, tree, tvb, offset, + Curve_sequence, hf_index, ett_pkcs1_Curve); + + return offset; +} + + +static const ber_sequence_t SpecifiedECDomain_sequence[] = { + { &hf_pkcs1_version , BER_CLASS_UNI, BER_UNI_TAG_INTEGER, BER_FLAGS_NOOWNTAG, dissect_pkcs1_ECPVer }, + { &hf_pkcs1_fieldID , BER_CLASS_UNI, BER_UNI_TAG_SEQUENCE, BER_FLAGS_NOOWNTAG, dissect_pkcs1_FieldID }, + { &hf_pkcs1_curve , BER_CLASS_UNI, BER_UNI_TAG_SEQUENCE, BER_FLAGS_NOOWNTAG, dissect_pkcs1_Curve }, + { &hf_pkcs1_base , BER_CLASS_UNI, BER_UNI_TAG_OCTETSTRING, BER_FLAGS_NOOWNTAG, dissect_pkcs1_ECPoint }, + { &hf_pkcs1_order , BER_CLASS_UNI, BER_UNI_TAG_INTEGER, BER_FLAGS_NOOWNTAG, dissect_pkcs1_INTEGER }, + { &hf_pkcs1_cofactor , BER_CLASS_UNI, BER_UNI_TAG_INTEGER, BER_FLAGS_OPTIONAL|BER_FLAGS_NOOWNTAG, dissect_pkcs1_INTEGER }, + { NULL, 0, 0, 0, NULL } +}; + +static int +dissect_pkcs1_SpecifiedECDomain(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { + offset = dissect_ber_sequence(implicit_tag, actx, tree, tvb, offset, + SpecifiedECDomain_sequence, hf_index, ett_pkcs1_SpecifiedECDomain); + + return offset; +} + + static int dissect_pkcs1_OBJECT_IDENTIFIER(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { @@ -265,12 +385,14 @@ dissect_pkcs1_OBJECT_IDENTIFIER(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, in static const value_string pkcs1_ECParameters_vals[] = { - { 0, "namedCurve" }, + { 0, "specifiedCurve" }, + { 1, "namedCurve" }, { 0, NULL } }; static const ber_choice_t ECParameters_choice[] = { - { 0, &hf_pkcs1_namedCurve , BER_CLASS_UNI, BER_UNI_TAG_OID, BER_FLAGS_NOOWNTAG, dissect_pkcs1_OBJECT_IDENTIFIER }, + { 0, &hf_pkcs1_specifiedCurve, BER_CLASS_UNI, BER_UNI_TAG_SEQUENCE, BER_FLAGS_NOOWNTAG, dissect_pkcs1_SpecifiedECDomain }, + { 1, &hf_pkcs1_namedCurve , BER_CLASS_UNI, BER_UNI_TAG_OID, BER_FLAGS_NOOWNTAG, dissect_pkcs1_OBJECT_IDENTIFIER }, { 0, NULL, 0, 0, 0, NULL } }; @@ -285,6 +407,16 @@ dissect_pkcs1_ECParameters(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, int off +static int +dissect_pkcs1_Prime_p(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { + offset = dissect_ber_integer(implicit_tag, actx, tree, tvb, offset, hf_index, + NULL); + + return offset; +} + + + /*--- PDUs ---*/ static int dissect_DSA_Params_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_) { @@ -329,6 +461,13 @@ static int dissect_ECParameters_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, p offset = dissect_pkcs1_ECParameters(FALSE, tvb, offset, &asn1_ctx, tree, hf_pkcs1_ECParameters_PDU); return offset; } +static int dissect_Prime_p_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_) { + int offset = 0; + asn1_ctx_t asn1_ctx; + asn1_ctx_init(&asn1_ctx, ASN1_ENC_BER, TRUE, pinfo); + offset = dissect_pkcs1_Prime_p(FALSE, tvb, offset, &asn1_ctx, tree, hf_pkcs1_Prime_p_PDU); + return offset; +} /*--- End of included file: packet-pkcs1-fn.c ---*/ @@ -366,6 +505,10 @@ void proto_register_pkcs1(void) { { "ECParameters", "pkcs1.ECParameters", FT_UINT32, BASE_DEC, VALS(pkcs1_ECParameters_vals), 0, NULL, HFILL }}, + { &hf_pkcs1_Prime_p_PDU, + { "Prime-p", "pkcs1.Prime_p", + FT_INT32, BASE_DEC, NULL, 0, + NULL, HFILL }}, { &hf_pkcs1_modulus, { "modulus", "pkcs1.modulus", FT_BYTES, BASE_NONE, NULL, 0, @@ -426,10 +569,54 @@ void proto_register_pkcs1(void) { { "trailerField", "pkcs1.trailerField", FT_INT32, BASE_DEC, NULL, 0, "INTEGER", HFILL }}, + { &hf_pkcs1_specifiedCurve, + { "specifiedCurve", "pkcs1.specifiedCurve_element", + FT_NONE, BASE_NONE, NULL, 0, + "SpecifiedECDomain", HFILL }}, { &hf_pkcs1_namedCurve, { "namedCurve", "pkcs1.namedCurve", FT_OID, BASE_NONE, NULL, 0, "OBJECT_IDENTIFIER", HFILL }}, + { &hf_pkcs1_version, + { "version", "pkcs1.version", + FT_INT32, BASE_DEC, VALS(pkcs1_ECPVer_vals), 0, + "ECPVer", HFILL }}, + { &hf_pkcs1_fieldID, + { "fieldID", "pkcs1.fieldID_element", + FT_NONE, BASE_NONE, NULL, 0, + NULL, HFILL }}, + { &hf_pkcs1_curve, + { "curve", "pkcs1.curve_element", + FT_NONE, BASE_NONE, NULL, 0, + NULL, HFILL }}, + { &hf_pkcs1_base, + { "base", "pkcs1.base", + FT_BYTES, BASE_NONE, NULL, 0, + "ECPoint", HFILL }}, + { &hf_pkcs1_order, + { "order", "pkcs1.order", + FT_INT32, BASE_DEC, NULL, 0, + "INTEGER", HFILL }}, + { &hf_pkcs1_cofactor, + { "cofactor", "pkcs1.cofactor", + FT_INT32, BASE_DEC, NULL, 0, + "INTEGER", HFILL }}, + { &hf_pkcs1_fieldType, + { "fieldType", "pkcs1.fieldType", + FT_OID, BASE_NONE, NULL, 0, + NULL, HFILL }}, + { &hf_pkcs1_parameters, + { "parameters", "pkcs1.parameters_element", + FT_NONE, BASE_NONE, NULL, 0, + NULL, HFILL }}, + { &hf_pkcs1_a, + { "a", "pkcs1.a", + FT_BYTES, BASE_NONE, NULL, 0, + "FieldElement", HFILL }}, + { &hf_pkcs1_b, + { "b", "pkcs1.b", + FT_BYTES, BASE_NONE, NULL, 0, + "FieldElement", HFILL }}, { &hf_pkcs1_r, { "r", "pkcs1.r", FT_INT32, BASE_DEC, NULL, 0, @@ -455,6 +642,9 @@ void proto_register_pkcs1(void) { &ett_pkcs1_ValidationParams, &ett_pkcs1_RSASSA_PSS_params, &ett_pkcs1_ECParameters, + &ett_pkcs1_SpecifiedECDomain, + &ett_pkcs1_FieldID, + &ett_pkcs1_Curve, &ett_pkcs1_DSA_Sig_Value, &ett_pkcs1_ECDSA_Sig_Value, @@ -485,6 +675,7 @@ void proto_reg_handoff_pkcs1(void) { register_ber_oid_dissector("1.2.840.10045.2.13", dissect_ECParameters_PDU, proto_pkcs1, "id-ecMQV"); register_ber_oid_dissector("1.2.840.113549.1.1.10", dissect_RSASSA_PSS_params_PDU, proto_pkcs1, "id-RSASSA-PSS"); register_ber_oid_dissector("1.2.840.113549.1.1.8", dissect_HashAlgorithm_PDU, proto_pkcs1, "id-mgf1"); + register_ber_oid_dissector("1.2.840.10045.1.1", dissect_Prime_p_PDU, proto_pkcs1, "prime-field"); /*--- End of included file: packet-pkcs1-dis-tab.c ---*/