From 6913ab73c284ea43edf3efb96a9ca476ebed61dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20M=C3=B6ller?= <nisse@lysator.liu.se> Date: Mon, 22 Mar 1999 21:58:49 +0100 Subject: [PATCH] Certificate parsing. Rev: lib/modules/Tools.pmod/X509.pmod:1.4 --- lib/modules/Tools.pmod/X509.pmod | 297 ++++++++++++++++++++++++++++++- 1 file changed, 294 insertions(+), 3 deletions(-) diff --git a/lib/modules/Tools.pmod/X509.pmod b/lib/modules/Tools.pmod/X509.pmod index 1b82e1ab19..9746ce5903 100644 --- a/lib/modules/Tools.pmod/X509.pmod +++ b/lib/modules/Tools.pmod/X509.pmod @@ -25,7 +25,58 @@ object make_time(int t) m->sec)); } -object extension_sequence = meta_explicit(3); +/* Returns a mapping similar to that returned by gmtime */ +mapping parse_time(object asn1) +{ + if ((asn1->tag_name != "UTCTime") + || (strlen(asn1->value) != 13)) + return 0; + + sscanf(asn1->value, "%[0-9]s%c", string s, int c); + if ( (strlen(s) != 12) && (c != 'Z') ) + return 0; + + mapping m = mkmapping( ({ "year", "mon", "mday", "hour", "min", "sec" }), + (array(string)) (s/2)); + + if (m->year < 50) + m->year += 50; + if ( (m->mon <= 0 ) || (m->mon > 12) ) + return 0; + m->mon--; + + if ( (m->mday <= 0) || (m->mday >= Calendar.ISO.Year(m->year + 1900) + ->month(m->mon + 1)->number_of_days())) + return 0; + + if ( (m->hour < 0) || (m->hour > 23)) + return 0; + + if ( (m->min < 0) || (m->min > 59)) + return 0; + + /* NOTE: Allows for lead seconds */ + if ( (m->sec < 0) || (m->min > 60)) + return 0; + + return m; +} + +int time_compare(mapping t1, mapping t2) +{ + foreach( ({ "year", "mon", "mday", "hour", "min", "sec" }), string name) + { + if (t1->name < t2->name) + return -1; + if (t1->name > t2->name) + return 1; + } + return 0; +} + + +object extension_sequence = meta_explicit(2, 3); +object version_integer = meta_explicit(2, 0); object make_tbs(object issuer, object algorithm, object subject, object keyinfo, @@ -37,7 +88,7 @@ object make_tbs(object issuer, object algorithm, make_time(now + ttl) }) ); return (extensions - ? asn1_sequence( ({ 2, /* Version 3 */ + ? asn1_sequence( ({ version_integer(asn1_integer(2)), /* Version 3 */ serial, algorithm, issuer, @@ -88,7 +139,16 @@ string rsa_sign_digest(object rsa, object digest_id, string digest) asn1_octet_string(digest) }) ); return rsa->raw_sign(digest_info->get_der())->digits(256); } - + +int rsa_verify_digest(object rsa, object digest_id, string digest, string s) +{ + object digest_info = asn1_sequence( ({ asn1_sequence( ({ digest_id, + asn1_null() }) ), + asn1_octet_string(digest) }) ); + + return rsa->raw_verify(digest_info->get_der(), Gmp.mpz(s, 256)); +} + string make_selfsigned_rsa_certificate(object rsa, int ttl, array name, array|void extensions) { @@ -120,3 +180,234 @@ string make_selfsigned_rsa_certificate(object rsa, int ttl, array name, ->digest())) }) )->get_der(); } +class rsa_verifier +{ + object rsa; + + constant type = "rsa"; + + object init(string key) + { + rsa = RSA.parse_public_key(key); + return rsa && this_object(); + } + + int verify(object algorithm, string msg, string signature) + { + { + if (algorithm->get_der() == Identifiers.rsa_md5_id) + return rsa_verify_digest(rsa, Identifiers.md5_id, + Crypto.md5()->update(msg)->digest(), + signature); + else if (algorithm->get_der() == Identifiers.rsa_sha1_id) + return rsa_verify_digest(rsa, Identifiers.sha1_id, + Crypto.sha()->update(msg)->digest(), + signature); + else + return 0; + } + } +} + +#if 0 +/* FIXME: This is a little more difficult, as the dsa-parameters are + * sometimes taken from the CA, and not present in the keyinfo. */ +class dsa_verifyer +{ + object dsa; + + constant type = "dsa"; + + object init(string key) + { + } +} +#endif + +object make_verifier(object keyinfo) +{ + if ( (keyinfo->type_name != "SEQUENCE") + || (sizeof(keyinfo->elements) != 2) + || (keyinfo->elements[0]->type_name != "SEQUENCE") + || !sizeof(keyinfo->elements[0]->elements) + || (keyinfo->elements[1]->type_name != "BIT STRING") + || keyinfo->elements[1]->unused) + return 0; + + if (keyinfo->elements[0]->elements[0]->get_der() + == Identifiers.rsa_id->get_der()) + { + if ( (sizeof(keyinfo->elements[0]->elements) != 2) + || (keyinfo->elements[0]->elements[1]->get_der() + != asn1_null()->get_der())) + return 0; + + return rsa_verifier(keyinfo->elements[1]->value); + } + else if (keyinfo->elements[0]->elements[0]->get_der() + == Identifiers.dsa_sha_id->get_der()) + { + /* FIXME: Not implemented */ + return 0; + } +} + +class TBSCertificate +{ + string der; + + int version; + object serial; + object algorithm; /* Algorithm Identifier */ + object issuer; + mapping not_after; + mapping not_before; + + object subject; + object public_key; + + /* Optional */ + object issuer_id; + object subject_id; + object extensions; + + object init(object asn1) + { + der = asn1->get_der(); + if (asn1->type_name != "SEQUENCE") + return 0; + + array a = asn1->elements; + if (sizeof(a) < 6) + return 0; + + if (sizeof(a) > 6) + { + /* The optional version field must be present */ + if (!a[0]->constructed + || (a[0]->get_combinded_tag() != make_combined_tag(2, 0)) + || (sizeof(a[0]->elements) != 1) + || (a[0]->elements[0]->tag_name != "INTEGER")) + return 0; + + version = (int) a[0]->elements[0]->value + 1; + if ( (version < 2) || (version > 3)) + return 0; + a = a[1..]; + } else + version = 1; + + if (a[0]->tag_name != "INTEGER") + return 0; + serial = a[0]->value; + + if ((a[1]->tag_name != "SEQUENCE") + || !sizeof(a[1]->elements ) + || (a[1]->elements[0]->tag_name != "OBJECT IDENTIFIER")) + return 0; + + algorithm = a[1]; + + if (a[2]->tag_name != "SEQUENCE") + return 0; + issuer = a[2]; + + if ((a[3]->tag_name != "SEQUENCE") + || (sizeof(a[3]->elements) != 2)) + return 0; + + array validity = a[3]->elements; + + not_before = parse_time(validity[0]); + if (!not_before) + return 0; + + not_after = parse_time(validity[0]); + if (!not_after) + return 0; + + subject = a[4]; + public_key = make_verifier(a[5]); + + if (!public_key) + return 0; + + int i = 6; + if (i == sizeof(a)) + return this_object(); + + if (version < 2) + return 0; + + if (! a[i]->constructed + && (a[i]->combined_tag == make_combined_tag(2, 1))) + { + issuer_id = asn1_bit_string()->decode_primitive(a[i]->raw); + i++; + if (i == sizeof(a)) + return this_object(); + } + if (! a[i]->constructed + && (a[i]->combined_tag == make_combined_tag(2, 2))) + { + subject_id = asn1_bit_string()->decode_primitive(a[i]->raw); + i++; + if (i == sizeof(a)) + return this_object(); + } + if (a[i]->constructed + && (a[i]->combined_tag == make_combined_tag(2, 3))) + { + extensions = a[i]; + i++; + if (i == sizeof(a)) + return this_object(); + } + /* Too many fields */ + return 0; + } +} + +/* Decodes a certificate, checks the signature. Returns the + * TBSCertificate structure, or 0 if decoding or verification failes. + * + * Authorities is a mapping from (DER-encoded) names to a verifiers. */ + +/* NOTE: This function allows self-signed certificates, and it doesn't + * check that names or extensions make sense. */ + +object verify_certificate(string s, mapping authorities) +{ + object cert = Standards.ASN1.Decode.simple_der_decode(s); + + if (!cert + || (cert->type_name != "SEQUENCE") + || (sizeof(cert->elements) != 3) + || (cert->elements[0]->type_name != "SEQUENCE") + || (cert->elements[1]->type_name != "SEQUENCE") + || (!sizeof(cert->elements[1]->elements)) + || (cert->elements[1]->elements[0]->type_name != "OBJECT IDENTIFIER") + || (cert->elements[2]->type_name != "BIT STRING") + || cert->elements[2]->unused) + return 0; + + object(TBSCertificate) tbs = TBSCertificate()->init(cert->elements[0]); + + if (!tbs || cert->elements[1]->get_der() != tbs->algorithm->get_der()) + return 0; + + object v; + + if (tbs->issuer->get_der() == tbs->subject->get_der()) + { + /* A self signed certificate */ + v = tbs->public_key; + } + else + v = authorities[tbs->issuer->get_der()]; + + return v && v->verify(cert->elements[1], + cert->elements[0]->get_der(), + cert->elements[2]->value) + && tbs; +} -- GitLab