b68b462013-08-15Martin Nilsson #pike __REAL_VERSION__ //#pragma strict_types
84d85d2013-08-16Martin Nilsson //! Functions to generate and validate RFC2459 style X.509 v3 //! certificates.
b68b462013-08-15Martin Nilsson  constant dont_dump_module = 1;
84d85d2013-08-16Martin Nilsson #if constant(Crypto.Hash)
b68b462013-08-15Martin Nilsson  import Standards.ASN1.Types; import Standards.PKCS; #ifdef X509_DEBUG
84d85d2013-08-16Martin Nilsson #define DBG(X ...) werror(X)
b68b462013-08-15Martin Nilsson #else
84d85d2013-08-16Martin Nilsson #define DBG(X ...)
b68b462013-08-15Martin Nilsson #endif //! constant CERT_TOO_OLD = 1; //! constant CERT_TOO_NEW = 2; //! constant CERT_INVALID = 3; //! constant CERT_CHAIN_BROKEN = 4; //! constant CERT_ROOT_UNTRUSTED = 5; //! constant CERT_BAD_SIGNATURE = 6;
8b05432013-11-24Martin Nilsson #if 0 // A CA certificate does not have the CA basic constraint.
b68b462013-08-15Martin Nilsson constant CERT_UNAUTHORIZED_CA = 7;
8b05432013-11-24Martin Nilsson #endif
b68b462013-08-15Martin Nilsson 
11380f2014-02-13Henrik Grubbström (Grubba) //! Unique identifier for the certificate issuer. //! //! X.509v2 (deprecated). class IssuerId { inherit BitString; constant cls = 2; constant tag = 1; } //! Unique identifier for the certificate subject. //! //! X.509v2 (deprecated). class SubjectId { inherit BitString; constant cls = 2; constant tag = 2; }
ee6ea12013-08-16Martin Nilsson protected { MetaExplicit extension_sequence = MetaExplicit(2, 3); MetaExplicit version_integer = MetaExplicit(2, 0);
b68b462013-08-15Martin Nilsson 
e8ef062013-11-21Martin Nilsson  mapping algorithms = ([ #if constant(Crypto.MD2)
468f482013-11-22Martin Nilsson  Identifiers.rsa_md2_id->get_der() : Crypto.MD2,
e8ef062013-11-21Martin Nilsson #endif
468f482013-11-22Martin Nilsson  Identifiers.rsa_md5_id->get_der() : Crypto.MD5, Identifiers.rsa_sha1_id->get_der() : Crypto.SHA1, Identifiers.rsa_sha256_id->get_der() : Crypto.SHA256,
e699882013-11-21Martin Nilsson #if constant(Crypto.SHA384)
468f482013-11-22Martin Nilsson  Identifiers.rsa_sha384_id->get_der() : Crypto.SHA384,
e699882013-11-21Martin Nilsson #endif #if constant(Crypto.SHA512)
468f482013-11-22Martin Nilsson  Identifiers.rsa_sha512_id->get_der() : Crypto.SHA512,
e699882013-11-21Martin Nilsson #endif
f7b5bc2013-11-22Martin Nilsson 
468f482013-11-22Martin Nilsson  Identifiers.dsa_sha_id->get_der() : Crypto.SHA1, #if constant(Crypto.SHA224) Identifiers.dsa_sha224_id->get_der() : Crypto.SHA224, #endif Identifiers.dsa_sha256_id->get_der() : Crypto.SHA256,
42e0422014-01-13Henrik Grubbström (Grubba) 
8ceb292014-02-21Martin Nilsson  Identifiers.ecdsa_sha1_id->get_der() : Crypto.SHA1,
42e0422014-01-13Henrik Grubbström (Grubba) #if constant(Crypto.SHA224) Identifiers.ecdsa_sha224_id->get_der() : Crypto.SHA224, #endif Identifiers.ecdsa_sha256_id->get_der() : Crypto.SHA256, #if constant(Crypto.SHA384) Identifiers.ecdsa_sha384_id->get_der() : Crypto.SHA384, #endif #if constant(Crypto.SHA512) Identifiers.ecdsa_sha512_id->get_der() : Crypto.SHA512, #endif
e8ef062013-11-21Martin Nilsson  ]);
ee6ea12013-08-16Martin Nilsson }
b68b462013-08-15Martin Nilsson  class Verifier { constant type = "none";
42e0422014-01-13Henrik Grubbström (Grubba)  Crypto.Sign pkc;
bb532d2013-11-21Martin Nilsson  optional Crypto.RSA rsa; optional Crypto.DSA dsa;
42e0422014-01-13Henrik Grubbström (Grubba) #if constant(Crypto.ECC.Curve) optional Crypto.ECC.SECP_521R1.ECDSA ecdsa; #endif
468f482013-11-22Martin Nilsson  //! Verifies the @[signature] of the certificate @[msg] using the
db9a2d2013-11-22Martin Nilsson  //! indicated hash @[algorithm].
468f482013-11-22Martin Nilsson  int(0..1) verify(Sequence algorithm, string msg, string signature) {
8ceb292014-02-21Martin Nilsson  DBG("Verify hash %O\n", algorithm[0]);
468f482013-11-22Martin Nilsson  Crypto.Hash hash = algorithms[algorithm[0]->get_der()]; if (!hash) return 0;
fc51612013-12-04Martin Nilsson  return pkc && pkc->pkcs_verify(msg, hash, signature);
468f482013-11-22Martin Nilsson  }
8ceb292014-02-21Martin Nilsson  protected string _sprintf(int t) { return t=='O' && sprintf("%O(%O)", this_program, pkc); }
b68b462013-08-15Martin Nilsson }
769a6a2013-11-19Martin Nilsson protected class RSAVerifier
b68b462013-08-15Martin Nilsson { inherit Verifier; constant type = "rsa";
769a6a2013-11-19Martin Nilsson  protected void create(string key) {
fc51612013-12-04Martin Nilsson  pkc = RSA.parse_public_key(key);
b68b462013-08-15Martin Nilsson  }
fc51612013-12-04Martin Nilsson  Crypto.RSA `rsa() { return [object(Crypto.RSA)]pkc; }
b68b462013-08-15Martin Nilsson }
17da1c2013-11-19Martin Nilsson protected class DSAVerifier
b68b462013-08-15Martin Nilsson { inherit Verifier; constant type = "dsa";
17da1c2013-11-19Martin Nilsson  protected void create(string key, Gmp.mpz p, Gmp.mpz q, Gmp.mpz g) {
fc51612013-12-04Martin Nilsson  pkc = DSA.parse_public_key(key, p, q, g);
17da1c2013-11-19Martin Nilsson  }
fc51612013-12-04Martin Nilsson  Crypto.DSA `dsa() { return [object(Crypto.DSA)]pkc; }
b68b462013-08-15Martin Nilsson }
42e0422014-01-13Henrik Grubbström (Grubba) #if constant(Crypto.ECC.Curve) protected class ECDSAVerifier { inherit Verifier; constant type = "ecdsa"; protected void create(string(8bit) key, string(8bit) curve_der) { Crypto.ECC.Curve curve; foreach(values(Crypto.ECC), mixed c) { if (objectp(c) && c->pkcs_named_curve_id && (c->pkcs_named_curve_id()->get_der() == curve_der)) { curve = [object(Crypto.ECC.Curve)]c; break; } } DBG("ECC Curve: %O (DER: %O)\n", curve, curve_der); pkc = curve->ECDSA()->set_public_key(key); } Crypto.ECC.SECP_521R1.ECDSA `ecdsa() { return [object(Crypto.ECC.SECP_521R1.ECDSA)]pkc; } } #endif
ee6ea12013-08-16Martin Nilsson protected Verifier make_verifier(Object _keyinfo)
b68b462013-08-15Martin Nilsson { if( _keyinfo->type_name != "SEQUENCE" ) return 0; Sequence keyinfo = [object(Sequence)]_keyinfo;
a79ded2013-08-16Martin Nilsson 
b68b462013-08-15Martin Nilsson  if ( (keyinfo->type_name != "SEQUENCE")
d01fbe2013-08-30Martin Nilsson  || (sizeof(keyinfo) != 2) || (keyinfo[0]->type_name != "SEQUENCE") || !sizeof( [object(Sequence)]keyinfo[0] ) || (keyinfo[1]->type_name != "BIT STRING") || keyinfo[1]->unused)
b68b462013-08-15Martin Nilsson  return 0;
d01fbe2013-08-30Martin Nilsson  Sequence seq = [object(Sequence)]keyinfo[0]; String str = [object(String)]keyinfo[1];
17da1c2013-11-19Martin Nilsson  if(sizeof(seq)==0) return 0;
d01fbe2013-08-30Martin Nilsson  if (seq[0]->get_der() == Identifiers.rsa_id->get_der())
b68b462013-08-15Martin Nilsson  {
d01fbe2013-08-30Martin Nilsson  if ( (sizeof(seq) != 2) || (seq[1]->get_der() != Null()->get_der()) )
b68b462013-08-15Martin Nilsson  return 0;
17da1c2013-11-19Martin Nilsson 
769a6a2013-11-19Martin Nilsson  return RSAVerifier(str->value);
b68b462013-08-15Martin Nilsson  }
17da1c2013-11-19Martin Nilsson  if(seq[0]->get_der() == Identifiers.dsa_id->get_der())
b68b462013-08-15Martin Nilsson  {
17da1c2013-11-19Martin Nilsson  if( sizeof(seq)!=2 || seq[1]->type_name!="SEQUENCE" || sizeof(seq[1])!=3 || seq[1][0]->type_name!="INTEGER" || seq[1][1]->type_name!="INTEGER" || seq[1][2]->type_name!="INTEGER" ) return 0; Sequence params = seq[1]; return DSAVerifier(str->value, params[0]->value, params[1]->value, params[2]->value);
b68b462013-08-15Martin Nilsson  }
42e0422014-01-13Henrik Grubbström (Grubba)  #if constant(Crypto.ECC.Curve) if(seq[0]->get_der() == Identifiers.ec_id->get_der()) {
8fe84f2014-01-13Henrik Grubbström (Grubba)  if( sizeof(seq)!=2 || seq[1]->type_name!="OBJECT IDENTIFIER" )
42e0422014-01-13Henrik Grubbström (Grubba)  return 0; Sequence params = seq[1];
8fe84f2014-01-13Henrik Grubbström (Grubba)  return ECDSAVerifier(str->value, params->get_der());
42e0422014-01-13Henrik Grubbström (Grubba)  } #endif DBG("make_verifier: Unknown algorithm identifier: %O\n", seq[0]);
b68b462013-08-15Martin Nilsson }
11380f2014-02-13Henrik Grubbström (Grubba) protected mapping(int:program(Object)) x509_types = ([ make_combined_tag(2, 1):IssuerId, make_combined_tag(2, 2):SubjectId, ]);
84d85d2013-08-16Martin Nilsson //! Represents a TBSCertificate.
34689e2014-02-09Henrik Grubbström (Grubba) //! //! @note //! Was not compatible with @[Standards.ASN1.Types.Sequence] //! Prior to Pike 8.0.
b68b462013-08-15Martin Nilsson class TBSCertificate {
3b33c82014-02-08Henrik Grubbström (Grubba)  inherit Sequence;
b68b462013-08-15Martin Nilsson 
3b33c82014-02-08Henrik Grubbström (Grubba)  protected string internal_der;
b68b462013-08-15Martin Nilsson  //!
3b33c82014-02-08Henrik Grubbström (Grubba)  void `der=(string asn1) { internal_der = UNDEFINED;
11380f2014-02-13Henrik Grubbström (Grubba)  if (init(Standards.ASN1.Decode.simple_der_decode(asn1, x509_types))) {
3b33c82014-02-08Henrik Grubbström (Grubba)  internal_der = asn1; } } string `der() { if (internal_der) return internal_der;
bd514e2014-02-08Henrik Grubbström (Grubba)  return internal_der = der_encode();
3b33c82014-02-08Henrik Grubbström (Grubba)  }
b68b462013-08-15Martin Nilsson  //!
3b33c82014-02-08Henrik Grubbström (Grubba)  void `version=(int v) { internal_der = UNDEFINED; if (v == 1) { if (sizeof(elements) > 6) {
391fb42014-02-10Henrik Grubbström (Grubba)  DBG("Reducing version to %d\n", v);
3b33c82014-02-08Henrik Grubbström (Grubba)  elements = elements[1..6]; issuer_pos = subject_pos = extensions_pos = 0;
391fb42014-02-10Henrik Grubbström (Grubba)  internal_extensions = ([]); internal_critical = (<>);
3b33c82014-02-08Henrik Grubbström (Grubba)  } } else if (sizeof(elements) == 6) {
391fb42014-02-10Henrik Grubbström (Grubba)  DBG("Bumping version to %d\n", v);
3b33c82014-02-08Henrik Grubbström (Grubba)  elements = ({ version_integer(Integer(v-1)) }) + elements; } else {
391fb42014-02-10Henrik Grubbström (Grubba)  if ((v < 3) && extensions_pos) { DBG("Reducing version to %d\n", v); elements = elements[..extensions_pos-1]; extensions_pos = 0; internal_extensions = ([]); internal_critical = (<>); } else { DBG("Bumping version to %d\n", v); }
3b33c82014-02-08Henrik Grubbström (Grubba)  elements[0] = version_integer(Integer(v-1)); } } int `version() { if (sizeof(elements) == 6) return 1; return (int)elements[0][0]->value + 1; } //! @param index //! Index in a v1 certificate. //! //! @param val //! New value for index. protected void low_set(int index, Sequence|Integer val) { internal_der = UNDEFINED; if (sizeof(elements) > 6) index++; elements[index] = val; } protected Sequence|Integer low_get(int index) { if (sizeof(elements) > 6) index++; return elements[index]; }
b68b462013-08-15Martin Nilsson  //!
3b33c82014-02-08Henrik Grubbström (Grubba)  void `serial=(Gmp.mpz|int s) { low_set(0, Integer(s)); } Gmp.mpz `serial() { Integer s = low_get(0); return s->value; } //! Algorithm Identifier. void `algorithm=(Sequence a) { low_set(1, a); } Sequence `algorithm() { return low_get(1); } //! Certificate issuer. void `issuer=(Sequence i) { low_set(2, i); } Sequence `issuer() { return low_get(2); }
b68b462013-08-15Martin Nilsson  //!
34689e2014-02-09Henrik Grubbström (Grubba)  void `validity=(Sequence v) { // FIXME: Validate? low_set(3, v); } Sequence `validity() { return low_get(3); } //!
3b33c82014-02-08Henrik Grubbström (Grubba)  void `not_before=(int t) { Sequence validity = low_get(3); validity->elements[0] = UTC()->set_posix(t); internal_der = UNDEFINED; } int `not_before() { Sequence validity = low_get(3); return validity[0]->get_posix(); }
b68b462013-08-15Martin Nilsson  //!
3b33c82014-02-08Henrik Grubbström (Grubba)  void `not_after=(int t) { Sequence validity = low_get(3); validity->elements[1] = UTC()->set_posix(t); internal_der = UNDEFINED; } int `not_after() { Sequence validity = low_get(3); return validity[1]->get_posix(); }
b68b462013-08-15Martin Nilsson  //!
3b33c82014-02-08Henrik Grubbström (Grubba)  void `subject=(Sequence s) { low_set(4, s); } Sequence `subject() { return low_get(4); } protected Verifier internal_public_key;
b68b462013-08-15Martin Nilsson  //!
34689e2014-02-09Henrik Grubbström (Grubba)  void `keyinfo=(Sequence ki) { internal_public_key = make_verifier(ki); low_set(5, ki); } Sequence `keyinfo() { return low_get(5); } //!
3b33c82014-02-08Henrik Grubbström (Grubba)  void `public_key=(Verifier v) { internal_public_key = v; low_set(5, v->pkc->pkcs_public_key()); } Verifier `public_key() { return internal_public_key; }
b68b462013-08-15Martin Nilsson  /* Optional */
3b33c82014-02-08Henrik Grubbström (Grubba)  protected int issuer_pos; protected int subject_pos; protected int extensions_pos;
b68b462013-08-15Martin Nilsson  //! @note //! optional
11380f2014-02-13Henrik Grubbström (Grubba)  void `issuer_id=(IssuerId|BitString i)
3b33c82014-02-08Henrik Grubbström (Grubba)  { internal_der = UNDEFINED; if (!i) { if (!issuer_pos) return; elements = elements[..6] + elements[8..]; issuer_pos = 0; if (subject_pos) subject_pos--; if (extensions_pos) extensions_pos--; return; }
11380f2014-02-13Henrik Grubbström (Grubba)  if (i->cls != 2) { // Convert BitString to IssuerId. i = IssuerId(i->value); } else if (i->raw) { // Convert Primitive to IssuerId.
daf8e22014-02-17Henrik Grubbström (Grubba)  (i = IssuerId())->decode_primitive(i->raw);
11380f2014-02-13Henrik Grubbström (Grubba)  }
3b33c82014-02-08Henrik Grubbström (Grubba)  if (!issuer_pos) { if (version < 2) version = 2; issuer_pos = 7;
11380f2014-02-13Henrik Grubbström (Grubba)  elements = elements[..6] + ({ i }) + elements[7..];
3b33c82014-02-08Henrik Grubbström (Grubba)  if (subject_pos) subject_pos++; if (extensions_pos) extensions_pos++; return; }
11380f2014-02-13Henrik Grubbström (Grubba)  elements[issuer_pos] = i;
3b33c82014-02-08Henrik Grubbström (Grubba)  }
11380f2014-02-13Henrik Grubbström (Grubba)  IssuerId `issuer_id()
3b33c82014-02-08Henrik Grubbström (Grubba)  {
11380f2014-02-13Henrik Grubbström (Grubba)  if (issuer_pos) return elements[issuer_pos];
3b33c82014-02-08Henrik Grubbström (Grubba)  return UNDEFINED; }
b68b462013-08-15Martin Nilsson  //! @note //! optional
11380f2014-02-13Henrik Grubbström (Grubba)  void `subject_id=(SubjectId|BitString s)
3b33c82014-02-08Henrik Grubbström (Grubba)  { internal_der = UNDEFINED; if (!s) { if (!subject_pos) return; elements = elements[..subject_pos -1] + elements[subject_pos+1..]; subject_pos = 0; if (extensions_pos) extensions_pos--; return; }
11380f2014-02-13Henrik Grubbström (Grubba)  if (s->cls != 2) { // Convert BitString to SubjectId. s = SubjectId()->decode_primitive(s->raw);
daf8e22014-02-17Henrik Grubbström (Grubba)  } else if (s->raw) {
11380f2014-02-13Henrik Grubbström (Grubba)  // Convert Primitive to SubjectId.
daf8e22014-02-17Henrik Grubbström (Grubba)  (s = SubjectId())->decode_primitive(s->raw);
11380f2014-02-13Henrik Grubbström (Grubba)  }
3b33c82014-02-08Henrik Grubbström (Grubba)  if (!subject_pos) { if (version < 2) version = 2; subject_pos = (issuer_pos || 6) + 1;
11380f2014-02-13Henrik Grubbström (Grubba)  elements = elements[..subject_pos-1] + ({ s }) +
3b33c82014-02-08Henrik Grubbström (Grubba)  elements[subject_pos..]; if (extensions_pos) extensions_pos++; return; }
11380f2014-02-13Henrik Grubbström (Grubba)  elements[subject_pos] = s;
3b33c82014-02-08Henrik Grubbström (Grubba)  }
11380f2014-02-13Henrik Grubbström (Grubba)  SubjectId `subject_id()
3b33c82014-02-08Henrik Grubbström (Grubba)  {
11380f2014-02-13Henrik Grubbström (Grubba)  if (subject_pos) return elements[subject_pos];
3b33c82014-02-08Henrik Grubbström (Grubba)  return UNDEFINED; }
b68b462013-08-15Martin Nilsson 
81badb2014-02-06Henrik Grubbström (Grubba)  //! The raw ASN.1 objects from which @[extensions] and @[critical] //! have been generated. //! //! @note //! optional
3b33c82014-02-08Henrik Grubbström (Grubba)  void `raw_extensions=(Sequence r) { internal_der = UNDEFINED; internal_extensions = ([]); internal_critical = (<>); mapping(string:Object) extensions = ([]); multiset critical = (<>); if (!r) { if (!extensions_pos) return; elements = elements[..extensions_pos-1]; extensions_pos = 0; return; } foreach(r->elements, Object _ext) { if( _ext->type_name != "SEQUENCE" || sizeof(_ext)<2 || sizeof(_ext)>3 ) { DBG("TBSCertificate: Bad extensions structure.\n"); return 0; } Sequence ext = [object(Sequence)]_ext; if( ext[0]->type_name != "OBJECT IDENTIFIER" || ext[-1]->type_name != "OCTET STRING" ) { DBG("TBSCertificate: Bad extensions structure.\n"); return 0; } DBG("TBSCertificate: extension: %O\n", ext[0]); string id = ext[0]->get_der(); if( extensions[id] ) { DBG("TBSCertificate: extension %O sent twice.\n"); return 0; } extensions[ id ] = Standards.ASN1.Decode.simple_der_decode(ext->elements[-1]->value); if(sizeof(ext)==3) { if( ext[1]->type_name != "BOOLEAN" ) return 0; if( ext[1]->value ) critical[id]=1; } }
391fb42014-02-10Henrik Grubbström (Grubba)  if (!extensions_pos) {
3b33c82014-02-08Henrik Grubbström (Grubba)  if (version < 3) version = 3; extensions_pos = sizeof(elements); elements = elements + ({ TaggedType3(r) }); } else { elements[extensions_pos] = TaggedType3(r); } internal_extensions = extensions; internal_critical = critical; } Sequence `raw_extensions() { if (extensions_pos) return elements[extensions_pos][0]; return UNDEFINED; }
81badb2014-02-06Henrik Grubbström (Grubba) 
b68b462013-08-15Martin Nilsson  //! @note //! optional
3b33c82014-02-08Henrik Grubbström (Grubba)  protected mapping(string:Object) internal_extensions = ([]); mapping(string:Object) `extensions() { return internal_extensions; }
2c73f72013-11-28Martin Nilsson  //! @note //! optional
3b33c82014-02-08Henrik Grubbström (Grubba)  protected multiset internal_critical = (<>); multiset `critical() { return internal_critical; }
b68b462013-08-15Martin Nilsson 
3b33c82014-02-08Henrik Grubbström (Grubba)  protected void create(Sequence|void asn1)
81badb2014-02-06Henrik Grubbström (Grubba)  {
3b33c82014-02-08Henrik Grubbström (Grubba)  // Initialize to defaults. elements = ({ Integer(0), // serialNumber Null, // signature Sequence(({})), // issuer Sequence(({ UTC()->set_posix(-0x8000000), UTC()->set_posix(0x7fffffff) })), // validity Sequence(({})), // subject Null, // subjectPublicKeyInfo }); if (asn1) { if (!init(asn1)) { error("Invalid ASN.1 structure for a TBSCertificate.\n"); } }
81badb2014-02-06Henrik Grubbström (Grubba)  }
eb96042013-10-29Martin Nilsson  protected mixed cast(string to) { switch(to) { case "mapping": return ([ "version" : version, "algorithm" : algorithm, "issuer" : issuer, "subject" : subject, ]); break; default: error("Can't case %O to %O\n", this_program, to); break; } }
769a6a2013-11-19Martin Nilsson  protected string get_id(object asn) { foreach(.PKCS.Identifiers.name_ids; string name; object id) if( asn==id ) return name; return (array(string))asn->id*"."; } protected array fmt_asn1(object asn) { array i = ({}); mapping m = ([]); foreach(asn->elements;; object o) { o = o[0]; string id = get_id(o[0]); i += ({ ([ id : o[1]->value]) }); if( m ) { if(m[id]) { m = 0; continue; } m[id] = o[1]->value; } } return m || i; }
eb96042013-10-29Martin Nilsson  protected string _sprintf(int t) {
769a6a2013-11-19Martin Nilsson  if( t!='O' ) return UNDEFINED; mapping m = cast("mapping"); catch { m->issuer = fmt_asn1(m->issuer); m->subject = fmt_asn1(m->subject); }; return sprintf("%O(%O)", this_program, m);
eb96042013-10-29Martin Nilsson  }
84d85d2013-08-16Martin Nilsson  //! Populates the object from a certificate decoded into an ASN.1 //! Object. Returns the object on success, otherwise @expr{0@}. You //! probably want to call @[decode_certificate] or even //! @[verify_certificate].
3b33c82014-02-08Henrik Grubbström (Grubba)  this_program init(array(Object)|Object asn1)
b68b462013-08-15Martin Nilsson  {
3b33c82014-02-08Henrik Grubbström (Grubba)  array(Object) a; if (objectp(asn1)) { if (asn1->type_name != "SEQUENCE") return 0;
b68b462013-08-15Martin Nilsson 
3b33c82014-02-08Henrik Grubbström (Grubba)  a = ([object(Sequence)]asn1)->elements; } else { a = [array(Object)]asn1; }
84d85d2013-08-16Martin Nilsson  DBG("TBSCertificate: sizeof(a) = %d\n", sizeof(a));
b68b462013-08-15Martin Nilsson  if (sizeof(a) < 6) return 0;
3b33c82014-02-08Henrik Grubbström (Grubba)  int version = 1;
b68b462013-08-15Martin Nilsson  if (sizeof(a) > 6) { /* The optional version field must be present */ if (!a[0]->constructed || (a[0]->get_combined_tag() != make_combined_tag(2, 0))
d01fbe2013-08-30Martin Nilsson  || (sizeof(a[0]) != 1) || (a[0][0]->type_name != "INTEGER"))
b68b462013-08-15Martin Nilsson  return 0;
d01fbe2013-08-30Martin Nilsson  version = (int) a[0][0]->value + 1;
b68b462013-08-15Martin Nilsson  if ( (version < 2) || (version > 3)) return 0; a = a[1..];
3b33c82014-02-08Henrik Grubbström (Grubba)  }
84d85d2013-08-16Martin Nilsson  DBG("TBSCertificate: version = %d\n", version);
ee6ea12013-08-16Martin Nilsson 
3b33c82014-02-08Henrik Grubbström (Grubba)  this_program::version = version;
b68b462013-08-15Martin Nilsson  if (a[0]->type_name != "INTEGER") return 0; serial = a[0]->value;
84d85d2013-08-16Martin Nilsson  DBG("TBSCertificate: serial = %s\n", (string) serial);
b68b462013-08-15Martin Nilsson  if ((a[1]->type_name != "SEQUENCE")
d01fbe2013-08-30Martin Nilsson  || !sizeof(a[1]) || (a[1][0]->type_name != "OBJECT IDENTIFIER"))
b68b462013-08-15Martin Nilsson  return 0; algorithm = a[1];
3e88822013-08-30Martin Nilsson  DBG("TBSCertificate: algorithm = %O\n", algorithm);
b68b462013-08-15Martin Nilsson  if (a[2]->type_name != "SEQUENCE") return 0; issuer = a[2];
3e88822013-08-30Martin Nilsson  DBG("TBSCertificate: issuer = %O\n", issuer);
b68b462013-08-15Martin Nilsson  if ((a[3]->type_name != "SEQUENCE")
d01fbe2013-08-30Martin Nilsson  || (sizeof(a[3]) != 2))
b68b462013-08-15Martin Nilsson  return 0; array validity = a[3]->elements;
3b33c82014-02-08Henrik Grubbström (Grubba)  if (catch { not_before = validity[0]->get_posix(); })
b68b462013-08-15Martin Nilsson  return 0;
84d85d2013-08-16Martin Nilsson  DBG("TBSCertificate: not_before = %O\n", not_before);
b68b462013-08-15Martin Nilsson 
3b33c82014-02-08Henrik Grubbström (Grubba)  if (catch { not_after = validity[1]->get_posix(); })
b68b462013-08-15Martin Nilsson  return 0;
84d85d2013-08-16Martin Nilsson  DBG("TBSCertificate: not_after = %O\n", not_after);
b68b462013-08-15Martin Nilsson  if (a[4]->type_name != "SEQUENCE") return 0; subject = a[4];
3e88822013-08-30Martin Nilsson  DBG("TBSCertificate: keyinfo = %O\n", a[5]);
b68b462013-08-15Martin Nilsson  public_key = make_verifier(a[5]); if (!public_key) return 0;
84d85d2013-08-16Martin Nilsson  DBG("TBSCertificate: parsed public key. type = %s\n", public_key->type);
b68b462013-08-15Martin Nilsson  int i = 6;
3b33c82014-02-08Henrik Grubbström (Grubba)  if (version >= 2)
b68b462013-08-15Martin Nilsson  {
3b33c82014-02-08Henrik Grubbström (Grubba)  if ((i < sizeof(a)) && !a[i]->constructed &&
11380f2014-02-13Henrik Grubbström (Grubba)  (a[i]->get_combined_tag() == make_combined_tag(2, 1)))
2c73f72013-11-28Martin Nilsson  {
11380f2014-02-13Henrik Grubbström (Grubba)  if (a[i]->raw) { (issuer_id = IssuerId())->decode_primitive(a[i]->raw); } else { issuer_id = a[i]; }
3b33c82014-02-08Henrik Grubbström (Grubba)  DBG("TBSCertificate: issuer_id = %O\n", issuer_id); i++; } if ((i < sizeof(a)) && !a[i]->constructed &&
11380f2014-02-13Henrik Grubbström (Grubba)  (a[i]->get_combined_tag() == make_combined_tag(2, 2)))
3b33c82014-02-08Henrik Grubbström (Grubba)  {
11380f2014-02-13Henrik Grubbström (Grubba)  if (a[i]->raw) { (subject_id = SubjectId())->decode_primitive(a[i]->raw); } else { subject_id = a[i]; }
3b33c82014-02-08Henrik Grubbström (Grubba)  DBG("TBSCertificate: subject_id = %O\n", subject_id); i++;
2c73f72013-11-28Martin Nilsson  }
b68b462013-08-15Martin Nilsson  }
3b33c82014-02-08Henrik Grubbström (Grubba)  if (version >= 3) { if ((i < sizeof(a)) && a[i]->constructed && (a[i]->combined_tag == make_combined_tag(2, 3)) && sizeof(a[i])==1 && a[i][0]->type_name == "SEQUENCE") { raw_extensions = a[i][0];
391fb42014-02-10Henrik Grubbström (Grubba)  i++;
3b33c82014-02-08Henrik Grubbström (Grubba)  } } internal_der = asn1->get_der(); if (i == sizeof(a)) return this;
b68b462013-08-15Martin Nilsson  /* Too many fields */ return 0; } }
6d86c22014-02-11Henrik Grubbström (Grubba) //! Creates the ASN.1 TBSCertificate sequence (see RFC2459 section //! 4.1) to be signed (TBS) by the CA. version is explicitly set to //! v3, and @[extensions] is optionally added to the sequence. //! issuerUniqueID and subjectUniqueID are not supported. TBSCertificate make_tbs(Sequence issuer, Sequence algorithm, Sequence subject, Sequence keyinfo, Integer serial, Sequence validity,
c5fe922014-02-17Henrik Grubbström (Grubba)  array|int(0..0)|void extensions)
6d86c22014-02-11Henrik Grubbström (Grubba) { TBSCertificate tbs = TBSCertificate(); tbs->serial = serial->value; tbs->algorithm = algorithm; tbs->issuer = issuer; tbs->validity = validity; tbs->subject = subject; tbs->keyinfo = keyinfo; tbs->raw_extensions = extensions && Sequence(extensions); return tbs; } //! Creates the ASN.1 TBSCertificate sequence (see RFC2459 section //! 4.1) to be signed (TBS) by the CA. version is explicitly set to //! v3, validity is calculated based on time and @[ttl], and //! @[extensions] is optionally added to the sequence. //! issuerUniqueID and subjectUniqueID are not supported. //! //! @note //! Prior to Pike 8.0 this function returned a plain @[Sequence] object. variant TBSCertificate make_tbs(Sequence issuer, Sequence algorithm, Sequence subject, Sequence keyinfo, Integer serial, int ttl,
c5fe922014-02-17Henrik Grubbström (Grubba)  array|int(0..0)|void extensions)
6d86c22014-02-11Henrik Grubbström (Grubba) { int now = time(); Sequence validity = Sequence( ({ UTC()->set_posix(now), UTC()->set_posix(now + ttl) }) ); return make_tbs(issuer, algorithm, subject, keyinfo, serial, validity, extensions); } //! Sign the provided TBSCertificate. //! //! @param tbs //! A @[TBSCertificate] as returned by @[decode_certificate()] //! or @[make_tbs()]. //! //! @param sign //! RSA, DSA or ECDSA parameters for the issuer. //! See @[Crypto.RSA], @[Crypto.DSA] and @[Crypto.ECC.Curve.ECDSA]. //! Must be initialized with the private key. //! //! @param hash //! The hash function to use for the certificate. Must be one of the //! standardized PKCS hashes to be used with the given Crypto. //! //! @seealso //! @[decode_certificate()], @[make_tbs()] Sequence sign_tbs(TBSCertificate tbs, Crypto.Sign sign, Crypto.Hash hash) { return Sequence(({ [object(Sequence)]tbs, sign->pkcs_signature_algorithm_id(hash), BitString(sign->pkcs_sign(tbs->get_der(), hash)), })); } //! Low-level function for creating a self-signed certificate. //! //! @param issuer //! Distinguished name for the issuer. //! See @[Standards.PKCS.Certificate.build_distinguished_name]. //! //! @param c //! RSA, DSA or ECDSA parameters for the issuer. //! Both the public and the private keys need to be set. //! See @[Crypto.RSA], @[Crypto.DSA] and @[Crypto.ECC.Curve.ECDSA]. //! //! @param h //! The hash function to use for the certificate. Must be one of the //! standardized PKCS hashes to be used with the given Crypto. //! //! @param subject //! Distinguished name for the issuer. //! See @[Standards.PKCS.Certificate.build_distinguished_name]. //! //! @param public_key //! DER-encoded RSAPublicKey structure. //! See @[Standards.PKCS.RSA.public_key()]. //! //! @param serial //! Serial number for this key and subject. //! //! @param ttl //! Validity time in seconds for this signature to be valid. //! //! @param extensions //! Set of extensions. //! //! @returns //! Returns a DER-encoded certificate. //! //! @seealso //! @[make_selfsigned_certificate()], @[make_tbs()], @[sign_tbs()] string sign_key(Sequence issuer, Crypto.Sign c, Crypto.Hash h, Sequence subject, int serial, int ttl, array|void extensions) { Sequence algorithm_id = c->pkcs_signature_algorithm_id(h); if(!algorithm_id) error("Can't use %O for %O.\n", h, c); return sign_tbs(make_tbs(issuer, algorithm_id, subject, c->pkcs_public_key(), Integer(serial), ttl, extensions), c, h)->get_der(); } //! Creates a selfsigned certificate, i.e. where issuer and subject //! are the same entity. This entity is derived from the list of pairs //! in @[name], which is encoded into an distinguished_name by //! @[Standards.PKCS.Certificate.build_distinguished_name]. //! //! @param c //! The public key cipher used for the certificate, @[Crypto.RSA], //! @[Crypto.DSA] or @[Crypto.ECC.Curve.ECDSA]. The object should be //! initialized with both public and private keys. //! //! @param ttl //! The validity of the certificate, in seconds, starting from //! creation date. //! //! @param name //! List of properties to create distinguished name from. //! //! @param extensions //! List of extensions as ASN.1 structures. //! //! @param h //! The hash function to use for the certificate. Must be one of the //! standardized PKCS hashes to be used with the given Crypto. By //! default @[Crypto.SHA256] is selected for both RSA and DSA. //! //! @param serial //! Serial number of the certificate. Defaults to generating a UUID //! version1 value with random node. Some browsers will refuse //! different certificates from the same signer with the same serial //! number. //! //! @seealso //! @[sign_key()], @[sign_tbs()] string make_selfsigned_certificate(Crypto.Sign c, int ttl, mapping|array name, array|void extensions, void|Crypto.Hash h, void|int serial) { if(!serial) serial = (int)Gmp.mpz(Standards.UUID.make_version1(-1)->encode(), 256); Sequence dn = Certificate.build_distinguished_name(name); return sign_key(dn, c, h||Crypto.SHA256, dn, serial, ttl, extensions); }
84d85d2013-08-16Martin Nilsson //! Decodes a certificate and verifies that it is structually sound. //! Returns a @[TBSCertificate] object if ok, otherwise @expr{0@}.
b68b462013-08-15Martin Nilsson TBSCertificate decode_certificate(string|object cert) {
11380f2014-02-13Henrik Grubbström (Grubba)  if (stringp (cert)) { cert = Standards.ASN1.Decode.simple_der_decode(cert, x509_types); }
b68b462013-08-15Martin Nilsson  if (!cert || (cert->type_name != "SEQUENCE")
d01fbe2013-08-30Martin Nilsson  || (sizeof(cert) != 3) || (cert[0]->type_name != "SEQUENCE") || (cert[1]->type_name != "SEQUENCE") || (!sizeof(cert[1])) || (cert[1][0]->type_name != "OBJECT IDENTIFIER") || (cert[2]->type_name != "BIT STRING") || cert[2]->unused)
2c73f72013-11-28Martin Nilsson  { DBG("Certificate has the wrong ASN.1 structure.\n");
b68b462013-08-15Martin Nilsson  return 0;
2c73f72013-11-28Martin Nilsson  }
b68b462013-08-15Martin Nilsson 
d01fbe2013-08-30Martin Nilsson  TBSCertificate tbs = TBSCertificate()->init(cert[0]);
b68b462013-08-15Martin Nilsson 
d01fbe2013-08-30Martin Nilsson  if (!tbs || (cert[1]->get_der() != tbs->algorithm->get_der()))
2c73f72013-11-28Martin Nilsson  { DBG("Failed to generate TBSCertificate.\n");
b68b462013-08-15Martin Nilsson  return 0;
2c73f72013-11-28Martin Nilsson  }
b68b462013-08-15Martin Nilsson  return tbs; } //! Decodes a certificate, checks the signature. Returns the //! TBSCertificate structure, or 0 if decoding or verification failes.
ea73142013-11-19Martin Nilsson //! The valid time range for the certificate is not checked.
b68b462013-08-15Martin Nilsson //! //! 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.
8b05432013-11-24Martin Nilsson TBSCertificate verify_certificate(string s, mapping(string:Verifier) authorities)
b68b462013-08-15Martin Nilsson { object cert = Standards.ASN1.Decode.simple_der_decode(s); TBSCertificate tbs = decode_certificate(cert); if (!tbs) return 0; object v; if (tbs->issuer->get_der() == tbs->subject->get_der()) {
8ceb292014-02-21Martin Nilsson  DBG("Self signed certificate: %O\n", tbs->public_key);
b68b462013-08-15Martin Nilsson  v = tbs->public_key; } else v = authorities[tbs->issuer->get_der()];
d01fbe2013-08-30Martin Nilsson  return v && v->verify(cert[1], cert[0]->get_der(), cert[2]->value)
b68b462013-08-15Martin Nilsson  && tbs; }
2c73f72013-11-28Martin Nilsson //! Decodes a root certificate using @[decode_certificate] and //! verifies that all extensions mandated for root certificates are //! present and valid. TBSCertificate verify_root_certificate(string s) { TBSCertificate tbs = decode_certificate(s); if(!tbs) return 0; multiset crit = tbs->critical + (<>); Object lookup(int num) { string id = Identifiers.ce_id->append(num)->get_der(); crit[id]=0; return tbs->extensions[id]; }; // id-ce-basicConstraints is required for certificates with public // key used to validate certificate signatures. RFC 3280, 4.2.1.10. Object c = lookup(19); if( !c || c->type_name!="SEQUENCE" || sizeof(c)<1 || sizeof(c)>2 || c[0]->type_name!="BOOLEAN" || !c[0]->value ) { DBG("verify root: Bad or missing id-ce-basicConstraints.\n"); return 0; } // id-ce-authorityKeyIdentifier is required, unless self signed. RFC // 3280 4.2.1.1 if( !lookup(35) && tbs->issuer->get_der() != tbs->subject->get_der() ) { DBG("verify root: Missing id-ce-authorityKeyIdentifier.\n"); return 0; } // id-ce-subjectKeyIdentifier is required. RFC 3280 4.2.1.2 if( !lookup(14) ) { DBG("verify root: Missing id-ce-subjectKeyIdentifier.\n"); return 0; } // id-ce-keyUsage is required. RFC 3280 4.2.1.3 if( !lookup(15) ) // FIXME: Look at usage bits { DBG("verify root: Missing id-ce-keyUsage.\n"); return 0; } // One or more critical extensions have not been processed. if( sizeof(crit) ) { DBG("verify root: Critical unknown extensions %O.\n", crit); return 0; } return tbs; }
b68b462013-08-15Martin Nilsson //! Decodes a certificate chain, checks the signatures. Verifies that the //! chain is unbroken, and that all certificates are in effect //! (time-wise.) //! //! Returns a mapping with the following contents, depending //! on the verification of the certificate chain: //! //! @mapping //! @member int "error_code" //! Error describing type of verification failure, if verification failed. //! May be one of the following: @[CERT_TOO_NEW], @[CERT_TOO_OLD],
8b05432013-11-24Martin Nilsson //! @[CERT_ROOT_UNTRUSTED], @[CERT_BAD_SIGNATURE], @[CERT_INVALID] //! or @[CERT_CHAIN_BROKEN]
b68b462013-08-15Martin Nilsson //! @member int "error_cert" //! Index number of the certificate that caused the verification failure. //! @member int(0..1) "self_signed" //! Non-zero if the certificate is self-signed. //! @member int(0..1) "verified" //! Non-zero if the certificate is verified. //! @member string "authority" //! @[Standards.ASN1.Sequence] of the authority RDN that verified //! the chain. //! @member string "cn" //! @[Standards.ASN1.Sequence] of the common name RDN of the leaf //! certificate. //! @endmapping //! //! @param cert_chain //! An array of certificates, with the relative-root last. Each //! certificate should be a DER-encoded certificate. //! @param authorities //! A mapping from (DER-encoded) names to verifiers. //! @param require_trust //! Require that the certificate be traced to an authority, even if //! it is self signed. //! //! See @[Standards.PKCS.Certificate.get_dn_string] for converting the //! RDN to an X500 style string. mapping verify_certificate_chain(array(string) cert_chain,
8b05432013-11-24Martin Nilsson  mapping(string:Verifier) authorities, int|void require_trust)
b68b462013-08-15Martin Nilsson { mapping m = ([ ]);
8b05432013-11-24Martin Nilsson #define ERROR(X) do { \ DBG("Error " #X "\n"); \ m->verified=0; m->error_code=(X); m->error_cert=idx; \ return m; \ } while(0)
b68b462013-08-15Martin Nilsson  int len = sizeof(cert_chain); array chain_obj = allocate(len); array chain_cert = allocate(len); foreach(cert_chain; int idx; string c) { object cert = Standards.ASN1.Decode.simple_der_decode(c); TBSCertificate tbs = decode_certificate(cert); if(!tbs)
8b05432013-11-24Martin Nilsson  ERROR(CERT_INVALID);
b68b462013-08-15Martin Nilsson  int idx = len-idx-1; chain_cert[idx] = cert; chain_obj[idx] = tbs; } foreach(chain_obj; int idx; TBSCertificate tbs) {
8b05432013-11-24Martin Nilsson  Verifier v;
b68b462013-08-15Martin Nilsson  if(idx == 0) // The root cert { v = authorities[tbs->issuer->get_der()]; // if we don't know the issuer of the root certificate, and we // require trust, we're done. if(!v && require_trust)
8b05432013-11-24Martin Nilsson  ERROR(CERT_ROOT_UNTRUSTED);
b68b462013-08-15Martin Nilsson 
8b05432013-11-24Martin Nilsson  // Is the root self signed?
b68b462013-08-15Martin Nilsson  if (tbs->issuer->get_der() == tbs->subject->get_der()) {
84d85d2013-08-16Martin Nilsson  DBG("Self signed certificate\n");
8b05432013-11-24Martin Nilsson  m->self_signed = 1;
b68b462013-08-15Martin Nilsson  // always trust our own authority first, even if it is self signed. if(!v) v = tbs->public_key; } } else // otherwise, we make sure the chain is unbroken. { // is the certificate in effect (time-wise)? int my_time = time();
2c73f72013-11-28Martin Nilsson  // Check not_before. We want the current time to be later.
30e5f32013-11-22Martin Nilsson  if(my_time < tbs->not_before)
8b05432013-11-24Martin Nilsson  ERROR(CERT_TOO_NEW);
b68b462013-08-15Martin Nilsson 
8b05432013-11-24Martin Nilsson  // Check not_after. We want the current time to be earlier.
30e5f32013-11-22Martin Nilsson  if(my_time > tbs->not_after)
8b05432013-11-24Martin Nilsson  ERROR(CERT_TOO_OLD);
b68b462013-08-15Martin Nilsson  // is the issuer of this certificate the subject of the previous // (more rootward) certificate? if(tbs->issuer->get_der() != chain_obj[idx-1]->subject->get_der())
8b05432013-11-24Martin Nilsson  ERROR(CERT_CHAIN_BROKEN);
b68b462013-08-15Martin Nilsson  // the verifier for this certificate should be the public key of // the previous certificate in the chain. v = chain_obj[idx-1]->public_key; } if (v) {
d01fbe2013-08-30Martin Nilsson  if( v->verify(chain_cert[idx][1], chain_cert[idx][0]->get_der(), chain_cert[idx][2]->value)
b68b462013-08-15Martin Nilsson  && tbs) {
84d85d2013-08-16Martin Nilsson  DBG("signature is verified..\n");
b68b462013-08-15Martin Nilsson  m->verified = 1; // if we're the root of the chain and we've verified, this is // the authority. if(idx == 0) m->authority = tbs->issuer; if(idx == sizeof(chain_cert)-1) m->cn = tbs->subject; } else
8b05432013-11-24Martin Nilsson  ERROR(CERT_BAD_SIGNATURE);
b68b462013-08-15Martin Nilsson  } } return m;
8b05432013-11-24Martin Nilsson  #undef ERROR
b68b462013-08-15Martin Nilsson } #endif