b68b462013-08-15Martin Nilsson #pike __REAL_VERSION__
0a00252014-04-05Martin Nilsson #require constant(Crypto.Hash)
b68b462013-08-15Martin Nilsson //#pragma strict_types
dc93732015-08-22Martin Nilsson //! Functions to generate and validate @rfc{2459@} style X.509 v3
84d85d2013-08-16Martin Nilsson //! certificates.
b68b462013-08-15Martin Nilsson  constant dont_dump_module = 1; import Standards.ASN1.Types; #ifdef X509_DEBUG
84d85d2013-08-16Martin Nilsson #define DBG(X ...) werror(X)
7715522014-09-30Martin Nilsson #define NULL(X ...) werror(X) && 0
b68b462013-08-15Martin Nilsson #else
84d85d2013-08-16Martin Nilsson #define DBG(X ...)
7715522014-09-30Martin Nilsson #define NULL(X ...) 0
b68b462013-08-15Martin Nilsson #endif
1453212014-04-27Martin Nilsson enum CertFailure { //! CERT_TOO_OLD = 1<<0,
b68b462013-08-15Martin Nilsson 
1453212014-04-27Martin Nilsson  //! CERT_TOO_NEW = 1<<1,
b68b462013-08-15Martin Nilsson 
1453212014-04-27Martin Nilsson  //! CERT_INVALID = 1<<2,
b68b462013-08-15Martin Nilsson 
1453212014-04-27Martin Nilsson  //! CERT_CHAIN_BROKEN = 1<<3,
b68b462013-08-15Martin Nilsson 
1453212014-04-27Martin Nilsson  //! CERT_ROOT_UNTRUSTED = 1<<4,
b68b462013-08-15Martin Nilsson 
1453212014-04-27Martin Nilsson  //! CERT_BAD_SIGNATURE = 1<<5,
b68b462013-08-15Martin Nilsson 
1453212014-04-27Martin Nilsson  //! A CA certificate is not allowed by basic constraints to sign //! another certificate. CERT_UNAUTHORIZED_CA = 1<<6,
ba33982014-04-27Martin Nilsson  //! The certificate is not allowed by it's key usage to sign data. CERT_UNAUTHORIZED_SIGNING = 1<<7, //! The certificate chain is longer than allowed by a certificate in //! the chain. CERT_EXCEEDED_PATH_LENGTH = 1<<8,
1453212014-04-27Martin Nilsson }
0a00252014-04-05Martin Nilsson 
b68b462013-08-15Martin Nilsson 
8bcf872014-04-03Martin Nilsson // Bit 0 is the first bit in the BitString.
62e7c62014-04-29Martin Nilsson enum keyUsage { KU_digitalSignature = 1<<0,
7a6bf02014-09-29Martin Nilsson  KU_nonRepudiation = 1<<1, // contentCommitment
62e7c62014-04-29Martin Nilsson  KU_keyEncipherment = 1<<2, KU_dataEncipherment = 1<<3, KU_keyAgreement = 1<<4, KU_keyCertSign = 1<<5, KU_cRLSign = 1<<6, KU_encipherOnly = 1<<7, KU_decipherOnly = 1<<8, KU_last_keyUsage = 1<<9, // end marker
ee04e22014-03-29Martin Nilsson };
3217712014-04-26Martin Nilsson // Generates the reverse int for keyUsage. protected BitString build_keyUsage(keyUsage i) { string v = ""; int pos=7, char; while(i) { if(i&1) char |= 1<<pos; if( --pos < 0 ) { pos = 7; v += sprintf("%c", char); char = 0; } i >>= 1; } if( char ) v += sprintf("%c", char); BitString b = BitString(v); b->unused = pos==7 ? 0 : pos+1; return b; }
ee04e22014-03-29Martin Nilsson 
11380f2014-02-13Henrik Grubbström (Grubba) //! Unique identifier for the certificate issuer. //! //! X.509v2 (deprecated). class IssuerId { inherit BitString;
39fddc2014-06-09Martin Nilsson  int cls = 2; int tag = 1;
11380f2014-02-13Henrik Grubbström (Grubba) } //! Unique identifier for the certificate subject. //! //! X.509v2 (deprecated). class SubjectId { inherit BitString;
39fddc2014-06-09Martin Nilsson  int cls = 2; int tag = 2;
11380f2014-02-13Henrik Grubbström (Grubba) }
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)
5303e82014-10-12Martin Nilsson  .PKCS.Identifiers.rsa_md2_id : Crypto.MD2,
e8ef062013-11-21Martin Nilsson #endif
5303e82014-10-12Martin Nilsson  .PKCS.Identifiers.rsa_md5_id : Crypto.MD5, .PKCS.Identifiers.rsa_sha1_id : Crypto.SHA1, .PKCS.Identifiers.rsa_sha256_id : Crypto.SHA256,
e699882013-11-21Martin Nilsson #if constant(Crypto.SHA384)
5303e82014-10-12Martin Nilsson  .PKCS.Identifiers.rsa_sha384_id : Crypto.SHA384,
e699882013-11-21Martin Nilsson #endif #if constant(Crypto.SHA512)
5303e82014-10-12Martin Nilsson  .PKCS.Identifiers.rsa_sha512_id : Crypto.SHA512,
e699882013-11-21Martin Nilsson #endif
f7b5bc2013-11-22Martin Nilsson 
5303e82014-10-12Martin Nilsson  .PKCS.Identifiers.dsa_sha_id : Crypto.SHA1,
468f482013-11-22Martin Nilsson #if constant(Crypto.SHA224)
5303e82014-10-12Martin Nilsson  .PKCS.Identifiers.dsa_sha224_id : Crypto.SHA224,
468f482013-11-22Martin Nilsson #endif
5303e82014-10-12Martin Nilsson  .PKCS.Identifiers.dsa_sha256_id : Crypto.SHA256,
42e0422014-01-13Henrik Grubbström (Grubba) 
5303e82014-10-12Martin Nilsson  .PKCS.Identifiers.ecdsa_sha1_id : Crypto.SHA1,
42e0422014-01-13Henrik Grubbström (Grubba) #if constant(Crypto.SHA224)
5303e82014-10-12Martin Nilsson  .PKCS.Identifiers.ecdsa_sha224_id : Crypto.SHA224,
42e0422014-01-13Henrik Grubbström (Grubba) #endif
5303e82014-10-12Martin Nilsson  .PKCS.Identifiers.ecdsa_sha256_id : Crypto.SHA256,
42e0422014-01-13Henrik Grubbström (Grubba) #if constant(Crypto.SHA384)
5303e82014-10-12Martin Nilsson  .PKCS.Identifiers.ecdsa_sha384_id : Crypto.SHA384,
42e0422014-01-13Henrik Grubbström (Grubba) #endif #if constant(Crypto.SHA512)
5303e82014-10-12Martin Nilsson  .PKCS.Identifiers.ecdsa_sha512_id : Crypto.SHA512,
42e0422014-01-13Henrik Grubbström (Grubba) #endif
e8ef062013-11-21Martin Nilsson  ]);
ee6ea12013-08-16Martin Nilsson }
b68b462013-08-15Martin Nilsson  class Verifier { constant type = "none";
8d8ad82014-08-14Henrik Grubbström (Grubba)  Crypto.Sign.State pkc;
468f482013-11-22Martin Nilsson  //! Verifies the @[signature] of the certificate @[msg] using the
db9a2d2013-11-22Martin Nilsson  //! indicated hash @[algorithm].
8f9e2b2014-04-28Martin Nilsson  int(0..1) verify(Sequence algorithm, string(8bit) msg, string(8bit) signature)
468f482013-11-22Martin Nilsson  {
8ceb292014-02-21Martin Nilsson  DBG("Verify hash %O\n", algorithm[0]);
9777652014-04-22Martin Nilsson  Crypto.Hash hash = algorithms[algorithm[0]];
468f482013-11-22Martin Nilsson  if (!hash) return 0;
fc51612013-12-04Martin Nilsson  return pkc && pkc->pkcs_verify(msg, hash, signature);
468f482013-11-22Martin Nilsson  }
8ceb292014-02-21Martin Nilsson 
18f2752014-04-30Martin Nilsson  protected int(0..1) `==(mixed o) { return objectp(o) && o->pkc?->name && pkc->name()==o->pkc->name() && pkc->public_key_equal(o->pkc); }
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) {
5303e82014-10-12Martin Nilsson  pkc = .PKCS.RSA.parse_public_key(key);
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) {
5303e82014-10-12Martin Nilsson  pkc = .PKCS.DSA.parse_public_key(key, p, q, g);
17da1c2013-11-19Martin Nilsson  }
b68b462013-08-15Martin Nilsson }
42e0422014-01-13Henrik Grubbström (Grubba) #if constant(Crypto.ECC.Curve) protected class ECDSAVerifier { inherit Verifier; constant type = "ecdsa";
9777652014-04-22Martin Nilsson  protected void create(string(8bit) key, Identifier curve_id)
42e0422014-01-13Henrik Grubbström (Grubba)  { Crypto.ECC.Curve curve; foreach(values(Crypto.ECC), mixed c) { if (objectp(c) && c->pkcs_named_curve_id &&
9777652014-04-22Martin Nilsson  (c->pkcs_named_curve_id() == curve_id)) {
42e0422014-01-13Henrik Grubbström (Grubba)  curve = [object(Crypto.ECC.Curve)]c; break; } }
9777652014-04-22Martin Nilsson  DBG("ECC Curve: %O (%O)\n", curve, curve_id);
42e0422014-01-13Henrik Grubbström (Grubba)  pkc = curve->ECDSA()->set_public_key(key); } } #endif
ee6ea12013-08-16Martin Nilsson protected Verifier make_verifier(Object _keyinfo)
b68b462013-08-15Martin Nilsson { if( _keyinfo->type_name != "SEQUENCE" )
7715522014-09-30Martin Nilsson  return NULL("keyinfo isn't a SEQUENCE.\n");
b68b462013-08-15Martin Nilsson  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)
7715522014-09-30Martin Nilsson  return NULL("Illegal keyinfo ASN.1\n");
d01fbe2013-08-30Martin Nilsson  Sequence seq = [object(Sequence)]keyinfo[0]; String str = [object(String)]keyinfo[1];
17da1c2013-11-19Martin Nilsson 
7715522014-09-30Martin Nilsson  if(sizeof(seq)==0) return NULL("Empty keyinfo algorithm identifier.\n");
17da1c2013-11-19Martin Nilsson 
5303e82014-10-12Martin Nilsson  if (seq[0]->get_der() == .PKCS.Identifiers.rsa_id->get_der())
b68b462013-08-15Martin Nilsson  {
7715522014-09-30Martin Nilsson  if ( (sizeof(seq) > 2) ||
5536682014-09-30Martin Nilsson  // Strictly there should always be a Null parameter member // here, but there has been a lot of confusion about 1 // element sequence vs. 2 element sequence with Null. Allow // both for compatibility.
0dcaba2014-09-30Martin Nilsson  (sizeof(seq)==2 && seq[1]->get_der() != Null()->get_der()) )
7715522014-09-30Martin Nilsson  return NULL("Illegal RSA ASN.1\n");
17da1c2013-11-19Martin Nilsson 
769a6a2013-11-19Martin Nilsson  return RSAVerifier(str->value);
b68b462013-08-15Martin Nilsson  }
5303e82014-10-12Martin Nilsson  if(seq[0]->get_der() == .PKCS.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" )
7715522014-09-30Martin Nilsson  return NULL("Illegal DSA ASN.1\n");
17da1c2013-11-19Martin Nilsson  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)
5303e82014-10-12Martin Nilsson  if(seq[0]->get_der() == .PKCS.Identifiers.ec_id->get_der())
42e0422014-01-13Henrik Grubbström (Grubba)  {
8fe84f2014-01-13Henrik Grubbström (Grubba)  if( sizeof(seq)!=2 || seq[1]->type_name!="OBJECT IDENTIFIER" )
7715522014-09-30Martin Nilsson  return NULL("Illegal ECDSA ASN.1\n");
42e0422014-01-13Henrik Grubbström (Grubba) 
9777652014-04-22Martin Nilsson  Identifier params = seq[1]; return ECDSAVerifier(str->value, params);
42e0422014-01-13Henrik Grubbström (Grubba)  } #endif
7715522014-09-30Martin Nilsson  return NULL("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]
eb16ad2014-05-14Henrik Grubbström (Grubba) //! prior to Pike 8.0.
b68b462013-08-15Martin Nilsson class TBSCertificate {
3b33c82014-02-08Henrik Grubbström (Grubba)  inherit Sequence;
b68b462013-08-15Martin Nilsson 
01891a2014-08-27Martin Nilsson  void _decode(array(int|array(Object)) x) { ::_decode(x); init(this); }
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;
1591ba2014-09-30Martin Nilsson  if (init(Standards.ASN1.Decode.secure_der_decode(asn1, x509_types))) {
3b33c82014-02-08Henrik Grubbström (Grubba)  internal_der = asn1; } } string `der() { if (internal_der) return internal_der;
775a1d2015-03-05Martin Nilsson  return internal_der = build_der(get_der_content());
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 
8f9e2b2014-04-28Martin Nilsson  protected Sequence low_get_sequence(int index) { return [object(Sequence)]low_get(index); } protected Integer low_get_integer(int index) { return [object(Integer)]low_get(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() {
8f9e2b2014-04-28Martin Nilsson  return low_get_integer(0)->value;
3b33c82014-02-08Henrik Grubbström (Grubba)  } //! Algorithm Identifier. void `algorithm=(Sequence a) { low_set(1, a); } Sequence `algorithm() {
8f9e2b2014-04-28Martin Nilsson  return low_get_sequence(1);
3b33c82014-02-08Henrik Grubbström (Grubba)  }
4797722015-07-15Henrik Grubbström (Grubba)  //! Algorithm hash if known and supported. //! Otherwise @[UNDEFINED]. Crypto.Hash `hash() { Sequence a = low_get_sequence(1); if (sizeof(a) < 1) return UNDEFINED; return algorithms[a[0]]; }
3b33c82014-02-08Henrik Grubbström (Grubba)  //! Certificate issuer. void `issuer=(Sequence i) { low_set(2, i); } Sequence `issuer() {
8f9e2b2014-04-28Martin Nilsson  return low_get_sequence(2);
3b33c82014-02-08Henrik Grubbström (Grubba)  }
b68b462013-08-15Martin Nilsson  //!
34689e2014-02-09Henrik Grubbström (Grubba)  void `validity=(Sequence v) { // FIXME: Validate? low_set(3, v); } Sequence `validity() {
8f9e2b2014-04-28Martin Nilsson  return low_get_sequence(3);
34689e2014-02-09Henrik Grubbström (Grubba)  }
3a035f2014-09-29Martin Nilsson  protected UTC time_object(int t) { if( !t ) { // This is the "no well-known expiration date". return GeneralizedTime("99991231235959Z"); } Calendar.ISO_UTC.Second s = Calendar.ISO_UTC.Second(t); if( s->year_no()<1950 || s->year_no()>=2050 ) return GeneralizedTime(s); return UTC(s); }
34689e2014-02-09Henrik Grubbström (Grubba)  //!
3b33c82014-02-08Henrik Grubbström (Grubba)  void `not_before=(int t) {
8f9e2b2014-04-28Martin Nilsson  Sequence validity = low_get_sequence(3);
3a035f2014-09-29Martin Nilsson  validity->elements[0] = time_object(t);
3b33c82014-02-08Henrik Grubbström (Grubba)  internal_der = UNDEFINED; } int `not_before() {
8f9e2b2014-04-28Martin Nilsson  Sequence validity = low_get_sequence(3);
3b33c82014-02-08Henrik Grubbström (Grubba)  return validity[0]->get_posix(); }
b68b462013-08-15Martin Nilsson  //!
3b33c82014-02-08Henrik Grubbström (Grubba)  void `not_after=(int t) {
8f9e2b2014-04-28Martin Nilsson  Sequence validity = low_get_sequence(3);
3a035f2014-09-29Martin Nilsson  validity->elements[1] = time_object(t);
3b33c82014-02-08Henrik Grubbström (Grubba)  internal_der = UNDEFINED; } int `not_after() {
8f9e2b2014-04-28Martin Nilsson  Sequence validity = low_get_sequence(3);
3b33c82014-02-08Henrik Grubbström (Grubba)  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() {
8f9e2b2014-04-28Martin Nilsson  return low_get_sequence(4);
3b33c82014-02-08Henrik Grubbström (Grubba)  }
57c8ac2014-04-21Martin Nilsson  // Attempt to create a presentable string from the subject DER. string subject_str() {
8f9e2b2014-04-28Martin Nilsson  Sequence subj = low_get_sequence(4);
57c8ac2014-04-21Martin Nilsson  mapping ids = ([]); foreach(subj->elements, Compound pair) { if(pair->type_name!="SET" || !sizeof(pair)) continue; pair = pair[0]; if(pair->type_name!="SEQUENCE" || sizeof(pair)!=2) continue; if(pair[0]->type_name=="OBJECT IDENTIFIER" &&
9777652014-04-22Martin Nilsson  pair[1]->value && !ids[pair[0]]) ids[pair[0]] = pair[1]->value;
57c8ac2014-04-21Martin Nilsson  }
9777652014-04-22Martin Nilsson  string res = ids[.PKCS.Identifiers.at_ids.commonName] || ids[.PKCS.Identifiers.at_ids.organizationName] || ids[.PKCS.Identifiers.at_ids.organizationUnitName];
57c8ac2014-04-21Martin Nilsson  return res; }
3b33c82014-02-08Henrik Grubbström (Grubba)  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() {
8f9e2b2014-04-28Martin Nilsson  return low_get_sequence(5);
34689e2014-02-09Henrik Grubbström (Grubba)  } //!
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 
c7978a2014-04-28Martin Nilsson  protected mapping extension_types = ([
3f33cf2014-06-10Martin Nilsson  .PKCS.Identifiers.ce_ids.authorityKeyIdentifier : ([ make_combined_tag(2,0) : OctetString, // keyIdentifier make_combined_tag(2,2) : Integer, // certSerialNo ]), .PKCS.Identifiers.ce_ids.subjectAltName : ([ make_combined_tag(2,1) : IA5String, // rfc822Name make_combined_tag(2,2) : IA5String, // dNSName make_combined_tag(2,6) : IA5String, // URI make_combined_tag(2,7) : OctetString, // iPAddress make_combined_tag(2,8) : Identifier, // registeredID ]),
c7978a2014-04-28Martin 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 = (<>);
9777652014-04-22Martin Nilsson  mapping(Identifier:Object) extensions = ([]);
3b33c82014-02-08Henrik Grubbström (Grubba)  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 ) {
7715522014-09-30Martin Nilsson  DBG("TBSCertificate: Bad extensions ASN1.\n"); return;
3b33c82014-02-08Henrik Grubbström (Grubba)  }
7715522014-09-30Martin Nilsson 
3b33c82014-02-08Henrik Grubbström (Grubba)  Sequence ext = [object(Sequence)]_ext; if( ext[0]->type_name != "OBJECT IDENTIFIER" || ext[-1]->type_name != "OCTET STRING" ) { DBG("TBSCertificate: Bad extensions structure.\n");
7715522014-09-30Martin Nilsson  return;
3b33c82014-02-08Henrik Grubbström (Grubba)  }
7715522014-09-30Martin Nilsson  DBG("TBSCertificate: Extension: %O\n", ext[0]);
9777652014-04-22Martin Nilsson  Identifier id = ext[0];
3b33c82014-02-08Henrik Grubbström (Grubba)  if( extensions[id] ) {
7715522014-09-30Martin Nilsson  DBG("TBSCertificate: Extension %O sent twice.\n"); return;
3b33c82014-02-08Henrik Grubbström (Grubba)  } extensions[ id ] =
1591ba2014-09-30Martin Nilsson  Standards.ASN1.Decode.secure_der_decode(ext->elements[-1]->value,
c7978a2014-04-28Martin Nilsson  extension_types[id]);
3b33c82014-02-08Henrik Grubbström (Grubba)  if(sizeof(ext)==3) {
7715522014-09-30Martin Nilsson  if( ext[1]->type_name != "BOOLEAN" ) { DBG("Illegal extension critical ASN.1\n"); return; }
3b33c82014-02-08Henrik Grubbström (Grubba)  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
9777652014-04-22Martin Nilsson  protected mapping(Identifier:Object) internal_extensions = ([]); mapping(Identifier:Object) `extensions()
3b33c82014-02-08Henrik Grubbström (Grubba)  { 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
ac7d632014-07-16Martin Nilsson  Sequence(({ UTC(-0x8000000), UTC(0x7fffffff) })), // validity
3b33c82014-02-08Henrik Grubbström (Grubba)  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:
ec1a0f2014-08-16Martin Nilsson  return UNDEFINED;
eb96042013-10-29Martin Nilsson  break; } }
769a6a2013-11-19Martin Nilsson  protected array fmt_asn1(object asn) { array i = ({}); mapping m = ([]); foreach(asn->elements;; object o) { o = o[0];
8c31862014-04-26Martin Nilsson  string id = .PKCS.Identifiers.reverse_name_ids[o[0]] ||
6067742014-05-12Martin Nilsson  .PKCS.Identifiers.reverse_attribute_ids[o[0]] ||
8c31862014-04-26Martin Nilsson  (array(string))o[0]->id*".";
769a6a2013-11-19Martin Nilsson  i += ({ ([ id : o[1]->value]) }); if( m ) { if(m[id]) { m = 0; continue; } m[id] = o[1]->value; } } return m || i; }
3f33cf2014-06-10Martin Nilsson  string fmt_extensions() { foreach(extensions; Identifier i; Object o) write("%O : %O\n", .PKCS.Identifiers.reverse_ce_ids[i]||i, o); }
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].
01891a2014-08-27Martin Nilsson  this_program init(array|Object asn1)
b68b462013-08-15Martin Nilsson  {
7715522014-09-30Martin Nilsson  if (!objectp(asn1) || asn1->type_name != "SEQUENCE") return NULL("init: Argument not an Sequence object. %O\n", asn1);
01891a2014-08-27Martin Nilsson  array(Object) a = ([object(Sequence)]asn1)->elements;
b68b462013-08-15Martin Nilsson  if (sizeof(a) < 6)
7715522014-09-30Martin Nilsson  return NULL("init: Incorrect ASN.1. %O\n", a);
b68b462013-08-15Martin Nilsson 
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"))
7715522014-09-30Martin Nilsson  return NULL("init: Incorrect ASN.1. %O\n", a);
b68b462013-08-15Martin Nilsson 
d01fbe2013-08-30Martin Nilsson  version = (int) a[0][0]->value + 1;
b68b462013-08-15Martin Nilsson  if ( (version < 2) || (version > 3))
7715522014-09-30Martin Nilsson  return NULL("init: Unsupported version %O.\n", version);
b68b462013-08-15Martin Nilsson  a = a[1..];
3b33c82014-02-08Henrik Grubbström (Grubba)  }
8e06a32014-09-30Martin Nilsson  this::version = version;
3b33c82014-02-08Henrik Grubbström (Grubba) 
b68b462013-08-15Martin Nilsson  if (a[0]->type_name != "INTEGER")
7715522014-09-30Martin Nilsson  return NULL("init: Illegal serial ASN.1. %O\n", a[0]);
b68b462013-08-15Martin Nilsson  serial = a[0]->value;
7715522014-09-30Martin Nilsson 
b68b462013-08-15Martin Nilsson  if ((a[1]->type_name != "SEQUENCE")
d01fbe2013-08-30Martin Nilsson  || !sizeof(a[1]) || (a[1][0]->type_name != "OBJECT IDENTIFIER"))
7715522014-09-30Martin Nilsson  return NULL("init: Illegal algorithm ASN.1. %O\n", a[1]);
b68b462013-08-15Martin Nilsson  algorithm = a[1];
7715522014-09-30Martin Nilsson  // FIXME: Verify this more strictly.
b68b462013-08-15Martin Nilsson  if (a[2]->type_name != "SEQUENCE")
7715522014-09-30Martin Nilsson  return NULL("init: Illegal issuer ASN.1. %O\n", a[2]);
b68b462013-08-15Martin Nilsson  issuer = a[2]; if ((a[3]->type_name != "SEQUENCE")
d01fbe2013-08-30Martin Nilsson  || (sizeof(a[3]) != 2))
7715522014-09-30Martin Nilsson  return NULL("init: Illegal validity ASN.1. %O\n", a[3]);
b68b462013-08-15Martin Nilsson  array validity = a[3]->elements;
7715522014-09-30Martin Nilsson  if (mixed err = catch {
3b33c82014-02-08Henrik Grubbström (Grubba)  not_before = validity[0]->get_posix(); })
7715522014-09-30Martin Nilsson  return NULL("init: Failed to decode not_before. %O\n%s\n", validity[0], describe_backtrace(err));
b68b462013-08-15Martin Nilsson 
7715522014-09-30Martin Nilsson  if (mixed err = catch {
3b33c82014-02-08Henrik Grubbström (Grubba)  not_after = validity[1]->get_posix(); })
7715522014-09-30Martin Nilsson  return NULL("init: Failed to decode not_after. %O\n%s\n", validity[1], describe_backtrace(err));
b68b462013-08-15Martin Nilsson 
7715522014-09-30Martin Nilsson  // FIXME: Verify this more strictly.
b68b462013-08-15Martin Nilsson  if (a[4]->type_name != "SEQUENCE")
7715522014-09-30Martin Nilsson  return NULL("init: Illegal subject ASN.1. %O\n", a[4]);
b68b462013-08-15Martin Nilsson  subject = a[4];
17ed402014-10-20Martin Nilsson  Verifier key = make_verifier(a[5]); if (!key)
7715522014-09-30Martin Nilsson  return NULL("init: Failed to decode public key. %O\n", a[5]);
17ed402014-10-20Martin Nilsson  public_key = key;
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)  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)  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++;
33b0282014-04-26Martin Nilsson 
ba33982014-04-27Martin Nilsson #define EXT(X) do { \ Object o = internal_extensions[.PKCS.Identifiers.ce_ids.##X]; \ if(o && !parse_##X(o)) \ DBG("TBSCertificate: Failed to parse extension %O.\n", #X); \ } while (0)
0d013a2014-04-28Martin Nilsson  EXT(basicConstraints); // 2.5.29.19 EXT(authorityKeyIdentifier); // 2.5.29.35 EXT(subjectKeyIdentifier); // 2.5.29.14 EXT(keyUsage); // 2.5.29.15 EXT(extKeyUsage); // 2.5.29.37
cf384f2014-04-28Martin Nilsson  EXT(subjectAltName); // 2.5.29.17
33b0282014-04-26Martin Nilsson #undef EXT
3b33c82014-02-08Henrik Grubbström (Grubba)  } } internal_der = asn1->get_der(); if (i == sizeof(a)) return this;
7715522014-09-30Martin Nilsson  return NULL("init: Too many fields. %O\n", sizeof(a));
b68b462013-08-15Martin Nilsson  }
33b0282014-04-26Martin Nilsson  // // --- Extension code // //! Set if the certificate contains a valid basicConstraints
dc93732015-08-22Martin Nilsson  //! extension. @rfc{3280@} 4.2.1.10.
33b0282014-04-26Martin Nilsson  int(0..1) ext_basicConstraints; //! If set, the certificate may be used as a CA certificate, i.e. //! sign other certificates. int(0..1) ext_basicConstraints_cA;
3f1bdd2014-04-27Martin Nilsson  //! The maximum number of certificates that may follow this
dc93732015-08-22Martin Nilsson  //! certificate in a certificate chain. @expr{0@} in case no limit //! is imposed. Note that this variable is off by one compared to //! the @rfc{3280@} definition, which only counts intermediate //! certificates (i.e. 0 intermediates means this variable would be //! 1, as in one following certificate).
3f1bdd2014-04-27Martin Nilsson  int ext_basicConstraints_pathLenConstraint;
33b0282014-04-26Martin Nilsson  protected int(0..1) parse_basicConstraints(Object o) { // FIXME: This extension must be critical if certificate contains // public keys use usage is to validate signatures on // certificates.
ba33982014-04-27Martin Nilsson  if( o->type_name!="SEQUENCE" )
33b0282014-04-26Martin Nilsson  return 0; Sequence s = [object(Sequence)]o;
a505da2014-05-27Martin Nilsson  if( sizeof(s)==0 ) { ext_basicConstraints = 1; ext_basicConstraints_cA = 0; return 1; } if( sizeof(s)>2 || s[0]->type_name!="BOOLEAN" )
33b0282014-04-26Martin Nilsson  return 0;
a505da2014-05-27Martin Nilsson 
33b0282014-04-26Martin Nilsson  if( sizeof(s)==2 ) { if( s[1]->type_name!="INTEGER" || s[0]->value==0 || s[1]->value<0 ) return 0;
3f1bdd2014-04-27Martin Nilsson  ext_basicConstraints_pathLenConstraint = s[1]->value + 1;
33b0282014-04-26Martin Nilsson  // FIXME: pathLenConstraint is not permitted if keyCertSign
c7978a2014-04-28Martin Nilsson  // isn't set in key usage. We need to check that at a higher // level though.
33b0282014-04-26Martin Nilsson  }
a505da2014-05-27Martin Nilsson  else ext_basicConstraints_pathLenConstraint = 0;
33b0282014-04-26Martin Nilsson  ext_basicConstraints = 1; ext_basicConstraints_cA = s[0]->value; return 1; } //! Set if the certificate contains a valid authorityKeyIdentifier
dc93732015-08-22Martin Nilsson  //! extension. @rfc{3280@} 4.2.1.1.
33b0282014-04-26Martin Nilsson  int(0..1) ext_authorityKeyIdentifier;
c7978a2014-04-28Martin Nilsson  //! Set to the KeyIdentifier, if set in the extension. string ext_authorityKeyIdentifier_keyIdentifier;
6854e52014-04-28Martin Nilsson  //! Set to the CertificateSerialNumber, if set in the extension. Gmp.mpz ext_authorityKeyIdentifier_authorityCertSerialNumber;
33b0282014-04-26Martin Nilsson  protected int(0..1) parse_authorityKeyIdentifier(Object o) { if( o->type_name!="SEQUENCE" ) return 0;
0d013a2014-04-28Martin Nilsson  Sequence s = [object(Sequence)]o;
33b0282014-04-26Martin Nilsson 
6854e52014-04-28Martin Nilsson  // Let's assume you can only have one unique identifier of each // kind.
3f33cf2014-06-10Martin Nilsson  array list = filter(s->elements, lambda(Object o) { return o->cls==2; }); if( sizeof(list) != sizeof(Array.uniq(list->tag)) )
6854e52014-04-28Martin Nilsson  return 0; foreach(list, Object o) {
3f33cf2014-06-10Martin Nilsson  switch(o->tag)
c7978a2014-04-28Martin Nilsson  {
6854e52014-04-28Martin Nilsson  case 0:
c7978a2014-04-28Martin Nilsson  ext_authorityKeyIdentifier_keyIdentifier = o->value;
6854e52014-04-28Martin Nilsson  break; case 2: ext_authorityKeyIdentifier_authorityCertSerialNumber = o->value; break; }}
c7978a2014-04-28Martin Nilsson 
6854e52014-04-28Martin Nilsson  // FIXME: We don't parse authorityCertIssuer yet.
c7978a2014-04-28Martin Nilsson 
33b0282014-04-26Martin Nilsson  ext_authorityKeyIdentifier = 1; return 1; } //! Set to the value of the SubjectKeyIdentifier if the certificate
dc93732015-08-22Martin Nilsson  //! contains the subjectKeyIdentifier extension. @rfc{3280@} //! 4.2.1.2.
33b0282014-04-26Martin Nilsson  string ext_subjectKeyIdentifier; protected int(0..1) parse_subjectKeyIdentifier(Object o) { if( o->type_name!="OCTET STRING" ) return 0; ext_subjectKeyIdentifier = o->value; return 1; }
dc93732015-08-22Martin Nilsson  //! Set to the value of the KeyUsage if the certificate contains the //! keyUsage extension. @rfc{3280@} 4.2.1.3.
3217712014-04-26Martin Nilsson  keyUsage ext_keyUsage;
33b0282014-04-26Martin Nilsson  protected int(0..1) parse_keyUsage(Object o) { if( o->type_name!="BIT STRING" ) return 0;
3217712014-04-26Martin Nilsson  int pos;
33b0282014-04-26Martin Nilsson  foreach(o->value;; int char) for(int i; i<8; i++) { int bit = !!(char & 0x80);
3217712014-04-26Martin Nilsson  ext_keyUsage |= (bit << pos);
33b0282014-04-26Martin Nilsson  pos++; char <<= 1; }
3217712014-04-26Martin Nilsson 
33b0282014-04-26Martin Nilsson  return 1; }
0d013a2014-04-28Martin Nilsson  //! Set to the list of extended key usages from anyExtendedKeyUsage, //! if the certificate contains the extKeyUsage extensions. These //! Identifier objects are typically found in
dc93732015-08-22Martin Nilsson  //! @[.PKCS.Identifiers.reverse_kp_ids]. @rfc{3280@} 4.2.1.13.
0d013a2014-04-28Martin Nilsson  array(Identifier) ext_extKeyUsage; protected int(0..1) parse_extKeyUsage(Object o) { if( o->type_name!="SEQUENCE" ) return 0;
cf384f2014-04-28Martin Nilsson  Sequence s = [object(Sequence)]o; ext_extKeyUsage = s->elements; return 1; }
6854e52014-04-28Martin Nilsson  array(string) ext_subjectAltName_rfc822Name;
cf384f2014-04-28Martin Nilsson  array(string) ext_subjectAltName_dNSName;
6854e52014-04-28Martin Nilsson  array(string) ext_subjectAltName_uniformResourceIdentifier;
cf384f2014-04-28Martin Nilsson  array(string) ext_subjectAltName_iPAddress;
6854e52014-04-28Martin Nilsson  array(Identifier) ext_subjectAltName_registeredID;
cf384f2014-04-28Martin Nilsson  protected int(0..1) parse_subjectAltName(Object o) { if( o->type_name!="SEQUENCE" ) return 0; Sequence s = [object(Sequence)]o; foreach(s->elements, Object o) {
3f33cf2014-06-10Martin Nilsson  if( o->cls!=2 ) continue;
6854e52014-04-28Martin Nilsson #define CASE(X) do { if(!ext_subjectAltName_##X) ext_subjectAltName_##X=0; \ ext_subjectAltName_##X += ({ o->value }); } while(0)
3f33cf2014-06-10Martin Nilsson  switch(o->tag)
cf384f2014-04-28Martin Nilsson  {
6854e52014-04-28Martin Nilsson  case 1: CASE(rfc822Name); break; case 2: CASE(dNSName); break; case 6: CASE(uniformResourceIdentifier);
cf384f2014-04-28Martin Nilsson  break;
6854e52014-04-28Martin Nilsson  case 7: CASE(iPAddress); break; case 8: CASE(registeredID);
cf384f2014-04-28Martin Nilsson  break; } }
0d013a2014-04-28Martin Nilsson 
6854e52014-04-28Martin Nilsson  // FIXME: otherName, x400Address, directoryName and ediPartyName // not supported.
0d013a2014-04-28Martin Nilsson  return 1; }
b68b462013-08-15Martin Nilsson }
dc93732015-08-22Martin Nilsson //! Creates the ASN.1 TBSCertificate sequence (see @rfc{2459@} section
6d86c22014-02-11Henrik Grubbström (Grubba) //! 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; }
dc93732015-08-22Martin Nilsson //! Creates the ASN.1 TBSCertificate sequence (see @rfc{2459@} section
6d86c22014-02-11Henrik Grubbström (Grubba) //! 4.1) to be signed (TBS) by the CA. version is explicitly set to //! v3, validity is calculated based on time and @[ttl], and
dc93732015-08-22Martin Nilsson //! @[extensions] is optionally added to the sequence. issuerUniqueID //! and subjectUniqueID are not supported.
6d86c22014-02-11Henrik Grubbström (Grubba) //! //! @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();
ac7d632014-07-16Martin Nilsson  Sequence validity = Sequence( ({ UTC(now), UTC(now + ttl) }) );
6d86c22014-02-11Henrik Grubbström (Grubba)  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,
8d8ad82014-08-14Henrik Grubbström (Grubba)  Crypto.Sign.State sign, Crypto.Hash hash)
6d86c22014-02-11Henrik Grubbström (Grubba) { return Sequence(({ [object(Sequence)]tbs, sign->pkcs_signature_algorithm_id(hash), BitString(sign->pkcs_sign(tbs->get_der(), hash)), })); }
1945062014-05-18Martin Nilsson //! Low-level function for creating a signed certificate.
6d86c22014-02-11Henrik Grubbström (Grubba) //! //! @param issuer //! Distinguished name for the issuer. //! See @[Standards.PKCS.Certificate.build_distinguished_name]. //! //! @param c
1945062014-05-18Martin Nilsson //! RSA, DSA or ECDSA parameters for the subject. Only the public //! key needs to be set. See @[Crypto.RSA], @[Crypto.DSA] and //! @[Crypto.ECC.Curve.ECDSA]. //! //! @param ca //! RSA, DSA or ECDSA parameters for the issuer. Only the private //! key needs to be set. See @[Crypto.RSA], @[Crypto.DSA] and //! @[Crypto.ECC.Curve.ECDSA].
6d86c22014-02-11Henrik Grubbström (Grubba) //! //! @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
94114f2014-05-21Martin Nilsson //! Distinguished name for the subject.
6d86c22014-02-11Henrik Grubbström (Grubba) //! 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()]
8d8ad82014-08-14Henrik Grubbström (Grubba) string sign_key(Sequence issuer, Crypto.Sign.State c, Crypto.Sign.State ca, Crypto.Hash h, Sequence subject, int serial, int ttl, array|mapping|void extensions)
6d86c22014-02-11Henrik Grubbström (Grubba) { Sequence algorithm_id = c->pkcs_signature_algorithm_id(h); if(!algorithm_id) error("Can't use %O for %O.\n", h, c);
e3d3552014-07-14Martin Nilsson  if(serial<=0) error("Conforming CA serial number needs to be >0.\n"); if(serial>1<<142) error("Serial needs to be less than 20 bytes encoded.\n");
7b2a1b2014-05-16Martin Nilsson  if( mappingp(extensions) ) { mapping(Identifier:Sequence) m = [mapping]extensions; array(Sequence) a = ({}); foreach( sort(indices(m)), Identifier i ) a += ({ m[i] }); extensions = a; }
6d86c22014-02-11Henrik Grubbström (Grubba)  return sign_tbs(make_tbs(issuer, algorithm_id, subject, c->pkcs_public_key(), Integer(serial), ttl, extensions),
1945062014-05-18Martin Nilsson  ca, h)->get_der();
6d86c22014-02-11Henrik Grubbström (Grubba) }
ee04e22014-03-29Martin Nilsson //! Creates a certificate extension with the @[id] as identifier and //! @[ext] as the extension payload. If the @[critical] flag is set //! the extension will be marked as critical. Sequence make_extension(Identifier id, Object ext, void|int critical) { array seq = ({ id }); if( critical ) seq += ({ Boolean(1) }); return Sequence( seq+({ OctetString(ext->get_der()) }) ); }
72b52b2014-09-29Martin Nilsson protected int make_key_usage_flags(Crypto.Sign.State c)
7a6bf02014-09-29Martin Nilsson { int flags = KU_digitalSignature|KU_keyEncipherment; // ECDSA certificates can be used for ECDH exchanges, which requires // keyAgreement. Potentially we should make a nicer API than name // prefix. if( has_prefix(c->name(), "ECDSA") ) flags |= KU_keyAgreement; return flags; }
6d86c22014-02-11Henrik Grubbström (Grubba) //! 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
7b2a1b2014-05-16Martin Nilsson //! Mapping with extensions as ASN.1 structures, as produced by //! @[make_extension]. The extensions subjectKeyIdentifier, keyUsage //! (flagged critical) and basicConstraints (flagged critical) will //! automatically be added if not present.
6d86c22014-02-11Henrik Grubbström (Grubba) //! //! @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()]
8d8ad82014-08-14Henrik Grubbström (Grubba) string make_selfsigned_certificate(Crypto.Sign.State c, int ttl,
7b2a1b2014-05-16Martin Nilsson  mapping|array name, mapping(Identifier:Sequence)|void extensions,
6d86c22014-02-11Henrik Grubbström (Grubba)  void|Crypto.Hash h, void|int serial) { if(!serial) serial = (int)Gmp.mpz(Standards.UUID.make_version1(-1)->encode(), 256);
8bcf872014-04-03Martin Nilsson 
5303e82014-10-12Martin Nilsson  Sequence dn = .PKCS.Certificate.build_distinguished_name(name);
ee04e22014-03-29Martin Nilsson 
7b2a1b2014-05-16Martin Nilsson  void add(string name, Object data, void|int critical) {
5303e82014-10-12Martin Nilsson  Identifier id = .PKCS.Identifiers.ce_ids[name];
7b2a1b2014-05-16Martin Nilsson  if(!extensions[id]) extensions[id] = make_extension(id, data, critical); };
ee04e22014-03-29Martin Nilsson 
7b2a1b2014-05-16Martin Nilsson  if(!extensions) extensions = ([]);
ee04e22014-03-29Martin Nilsson  // While RFC 3280 section 4.2.1.2 suggest to only hash the BIT // STRING part of the subjectPublicKey, it is only a suggestion.
7b2a1b2014-05-16Martin Nilsson  add("subjectKeyIdentifier", OctetString( Crypto.SHA1.hash(c->pkcs_public_key()->get_der()) ));
7a6bf02014-09-29Martin Nilsson  add("keyUsage", build_keyUsage(make_key_usage_flags(c)), 1);
a505da2014-05-27Martin Nilsson  add("basicConstraints", Sequence(({})), 1);
ee04e22014-03-29Martin Nilsson 
1945062014-05-18Martin Nilsson  return sign_key(dn, c, c, h||Crypto.SHA256, dn, serial, ttl, extensions); }
8d8ad82014-08-14Henrik Grubbström (Grubba) string make_site_certificate(TBSCertificate ca, Crypto.Sign.State ca_key, Crypto.Sign.State c, int ttl, mapping|array name,
1945062014-05-18Martin Nilsson  mapping|void extensions, void|Crypto.Hash h, void|int serial) { if(!serial) serial = (int)Gmp.mpz(Standards.UUID.make_version1(-1)->encode(), 256);
5303e82014-10-12Martin Nilsson  Sequence dn = .PKCS.Certificate.build_distinguished_name(name);
1945062014-05-18Martin Nilsson  void add(string name, Object data, void|int critical) {
5303e82014-10-12Martin Nilsson  Identifier id = .PKCS.Identifiers.ce_ids[name];
1945062014-05-18Martin Nilsson  if(!extensions[id]) extensions[id] = make_extension(id, data, critical); }; if(!extensions) extensions = ([]); // FIXME: authorityKeyIdentifier
7a6bf02014-09-29Martin Nilsson  add("keyUsage", build_keyUsage(make_key_usage_flags(c)), 1);
a505da2014-05-27Martin Nilsson  add("basicConstraints", Sequence(({})), 1);
94114f2014-05-21Martin Nilsson  return sign_key(ca->subject, c, ca_key, h||Crypto.SHA256, dn, serial, ttl, extensions);
1945062014-05-18Martin Nilsson }
8d8ad82014-08-14Henrik Grubbström (Grubba) string make_root_certificate(Crypto.Sign.State c, int ttl, mapping|array name, mapping(Identifier:Sequence)|void extensions, void|Crypto.Hash h, void|int serial)
1945062014-05-18Martin Nilsson { if(!serial) serial = (int)Gmp.mpz(Standards.UUID.make_version1(-1)->encode(), 256);
5303e82014-10-12Martin Nilsson  Sequence dn = .PKCS.Certificate.build_distinguished_name(name);
1945062014-05-18Martin Nilsson  void add(string name, Object data, void|int critical) {
5303e82014-10-12Martin Nilsson  Identifier id = .PKCS.Identifiers.ce_ids[name];
1945062014-05-18Martin Nilsson  if(!extensions[id]) extensions[id] = make_extension(id, data, critical); }; if(!extensions) extensions = ([]); // While RFC 3280 section 4.2.1.2 suggest to only hash the BIT // STRING part of the subjectPublicKey, it is only a suggestion. // FIXME: authorityKeyIdentifier add("subjectKeyIdentifier", OctetString( Crypto.SHA1.hash(c->pkcs_public_key()->get_der()) )); add("keyUsage", build_keyUsage(KU_keyCertSign|KU_cRLSign), 1); add("basicConstraints", Sequence(({Boolean(1)})), 1); return sign_key(dn, c, c, h||Crypto.SHA256, dn, serial, ttl, extensions);
6d86c22014-02-11Henrik Grubbström (Grubba) }
84d85d2013-08-16Martin Nilsson //! Decodes a certificate and verifies that it is structually sound. //! Returns a @[TBSCertificate] object if ok, otherwise @expr{0@}.
3926cb2015-02-27Martin Nilsson TBSCertificate decode_certificate(string|.PKCS.Signature.Signed cert)
b68b462013-08-15Martin Nilsson {
3926cb2015-02-27Martin Nilsson  if (stringp (cert))
2bed8e2015-02-27Martin Nilsson  cert = .PKCS.Signature.decode_signed(cert, x509_types);
b68b462013-08-15Martin Nilsson 
9532b22015-02-27Martin Nilsson  TBSCertificate tbs=TBSCertificate([object(.PKCS.Signature.Signed)]cert->tbs);
b68b462013-08-15Martin Nilsson 
481f6d2015-02-01Martin Nilsson  // FIXME: The re-encoding and algorithm checks are more appropriate // in verify_certificate, but the full certificate doesn't reach // there.
3926cb2015-02-27Martin Nilsson  if (!tbs)
7715522014-09-30Martin Nilsson  return NULL("Failed to generate TBSCertificate.\n");
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.
ea8b5c2014-03-04Henrik Grubbström (Grubba) TBSCertificate verify_certificate(string s, mapping(string:Verifier|array(Verifier)) authorities)
b68b462013-08-15Martin Nilsson {
2bed8e2015-02-27Martin Nilsson  .PKCS.Signature.Signed cert = .PKCS.Signature.decode_signed(s, x509_types);
b68b462013-08-15Martin Nilsson  TBSCertificate tbs = decode_certificate(cert); if (!tbs) return 0;
2bed8e2015-02-27Martin Nilsson  if(tbs->algorithm->get_der() != cert->algorithm->get_der()) return NULL("Mismatching algorithm identifiers.\n");
ea8b5c2014-03-04Henrik Grubbström (Grubba)  array(Verifier)|Verifier verifiers;
cdd85c2015-02-27Martin Nilsson 
b68b462013-08-15Martin Nilsson  if (tbs->issuer->get_der() == tbs->subject->get_der()) {
8ceb292014-02-21Martin Nilsson  DBG("Self signed certificate: %O\n", tbs->public_key);
ea8b5c2014-03-04Henrik Grubbström (Grubba)  verifiers = ({ tbs->public_key }); } else { verifiers = authorities[tbs->issuer->get_der()]; if (objectp(verifiers)) verifiers = ({ verifiers });
b68b462013-08-15Martin Nilsson  }
ea8b5c2014-03-04Henrik Grubbström (Grubba)  foreach(verifiers || ({}), Verifier v) { if (v->verify(cert[1], cert[0]->get_der(), cert[2]->value)) return tbs; } return 0;
b68b462013-08-15Martin Nilsson }
72a5202014-03-24Martin Nilsson //! Verifies that all extensions mandated for certificate signing //! certificates are present and valid.
57c8ac2014-04-21Martin Nilsson TBSCertificate verify_ca_certificate(string|TBSCertificate tbs)
2c73f72013-11-28Martin Nilsson {
57c8ac2014-04-21Martin Nilsson  if(stringp(tbs)) tbs = decode_certificate(tbs);
2c73f72013-11-28Martin Nilsson  if(!tbs) return 0;
8c31862014-04-26Martin Nilsson  int t = time();
7715522014-09-30Martin Nilsson  if( tbs->not_after < t ) return NULL("verify ca: not after check failed\n"); if( tbs->not_before > t ) return NULL("verify ca: not before check failed\n");
8c31862014-04-26Martin Nilsson 
607cad2014-04-25Martin Nilsson  multiset crit = tbs->critical + (<>);
ee04e22014-03-29Martin Nilsson  int self_signed = (tbs->issuer->get_der() == tbs->subject->get_der());
2c73f72013-11-28Martin Nilsson  // id-ce-basicConstraints is required for certificates with public
33b0282014-04-26Martin Nilsson  // key used to validate certificate signatures. crit[.PKCS.Identifiers.ce_ids.basicConstraints]=0; if( tbs->ext_basicConstraints )
2c73f72013-11-28Martin Nilsson  {
33b0282014-04-26Martin Nilsson  if( !tbs->ext_basicConstraints_cA )
7715522014-09-30Martin Nilsson  return NULL("vertify ca: id-ce-basicContraints-cA is false.\n");
2c73f72013-11-28Martin Nilsson  }
33b0282014-04-26Martin Nilsson  else
7715522014-09-30Martin Nilsson  return NULL("verify ca: Bad or missing id-ce-basicConstraints.\n");
2c73f72013-11-28Martin Nilsson 
57c8ac2014-04-21Martin Nilsson  // id-ce-authorityKeyIdentifier is required by RFC 5759, unless self // signed. Defined in RFC 3280 4.2.1.1, but there only as // recommended.
33b0282014-04-26Martin Nilsson  crit[.PKCS.Identifiers.ce_ids.authorityKeyIdentifier]=0; if( !tbs->ext_authorityKeyIdentifier && !self_signed )
7715522014-09-30Martin Nilsson  return NULL("verify ca: Missing id-ce-authorityKeyIdentifier.\n");
2c73f72013-11-28Martin Nilsson 
33b0282014-04-26Martin Nilsson  // id-ce-keyUsage is required. crit[.PKCS.Identifiers.ce_ids.keyUsage]=0;
62e7c62014-04-29Martin Nilsson  if( !(tbs->ext_keyUsage & KU_keyCertSign) )
7715522014-09-30Martin Nilsson  return NULL("verify ca: id-ce-keyUsage doesn't allow keyCertSign.\n");
57c8ac2014-04-21Martin Nilsson  // FIXME: RFC 5759 also requires CRLSign set.
33b0282014-04-26Martin Nilsson  if( tbs->ext_keyUsage &
62e7c62014-04-29Martin Nilsson  (~(KU_keyCertSign | KU_cRLSign | KU_digitalSignature | KU_nonRepudiation)&(KU_last_keyUsage-1)) )
7715522014-09-30Martin Nilsson  return NULL("verify ca: illegal CA uses in id-ce-keyUsage.\n");
ee04e22014-03-29Martin Nilsson 
57c8ac2014-04-21Martin Nilsson  // FIXME: In addition RFC 5759 requires policyMappings, // policyConstraints and inhibitAnyPolicy to be processed in // accordance with RFC 5280.
2c73f72013-11-28Martin Nilsson  // One or more critical extensions have not been processed. if( sizeof(crit) )
7715522014-09-30Martin Nilsson  return NULL("verify ca: Critical unknown extensions %O.\n", crit);
2c73f72013-11-28Martin Nilsson  return tbs; }
867fea2014-03-19Martin Nilsson //! Convenience function for loading known root certificates.
a20f492014-03-05Henrik Grubbström (Grubba) //! //! @param root_cert_dirs //! Directory/directories containing the PEM-encoded root certificates //! to load. Defaults to a rather long list of directories, including //! @expr{"/etc/ssl/certs"@}, @expr{"/etc/pki/tls/certs"@} and //! @expr{"/System/Library/OpenSSL/certs"@}, which seem to be the most //! common locations. //! //! @returns //! Returns a mapping from DER-encoded issuer to @[Verifier]s //! compatible with eg @[verify_certificate()] //! //! @note //! If a certificate directory contains a file named //! @expr{"ca-certificates.crt"@}, it is assumed to //! contain a concatenation of all the certificates //! in the directory. //! //! @seealso //! @[verify_certificate()], @[verify_certificate_chain()] mapping(string:array(Verifier)) load_authorities(string|array(string)|void root_cert_dirs) { root_cert_dirs = root_cert_dirs || ({ // These directories have with some minor modifications // been taken from the list at // http://gagravarr.org/writing/openssl-certs/others.shtml "/etc/ssl/certs", // This seems to be the default for most current installations // with the exception of RHEL and MacOS X. // // Debian Woody (3.0), OpenSSL 0.9.6 // Debian Sarge (3.1), OpenSSL 0.9.7 // Debian Etch (4.0), OpenSSL 0.9.8 // Debian Lenny (5.0), OpenSSL 0.9.8 // Debian Squeeze (6.0), OpenSSL 0.9.8o // FreeBSD, OpenSSL 0.9.8 // Gentoo, OpenSSL 0.9.7 // Nokia N900 Maemo 5, OpenSSL 0.9.8n // OpenBSD, OpenSSL 0.9.x // Slackware, OpenSSL 0.9.6 // SuSE 8.1 / 8.2, OpenSSL 0.9.6 // Ubuntu Maverick (10.10), OpenSSL 0.9.8o // Ubuntu Precise (12.04), OpenSSL 1.0.1 "/etc/pki/tls/certs", // Redhat Enterprise 6, OpenSSL 1.0.0 // Redhat Fedora Core 4, OpenSSL 0.9.7 // Redhat Fedora Core 5 / 6, OpenSSL 0.9.8 "/System/Library/OpenSSL/certs", // Mac OS X 10.1.2, OpenSSL 0.9.6b "/etc/openssl/certs", // NetBSD, OpenSSL 0.9.x // From this point on the operation systems start getting // a bit old. "/usr/share/ssl/certs", // Centos 3 / 4, OpenSSL 0.9.7 // Redhat 6.2 / 7.x / 8.0 / 9, OpenSSL 0.9.6 // Redhat Enterprise 3 / 4, OpenSSL 0.9.7 // Redhat Fedora Core 2 / 3, OpenSSL 0.9.7 // SuSE 7.3 / 8.0, OpenSSL 0.9.6 "/usr/lib/ssl/certs", // Gentoo, OpenSSL 0.9.6 // Mandrake 7.1 -> 8.2, OpenSSL 0.9.6 "/var/ssl/certs", // AIX, OpenSSL 0.9.6 (from OpenSSH support packages) "/usr/ssl/certs", // Cygwin, OpenSSL 0.9.6 "/usr/local/openssl/certs", // FreeBSD, OpenSSL 0.9.x (custom complile) "/usr/local/ssl/certs", // Normal OpenSSL Tarball Build, OpenSSL 0.9.6 "/opt/local/ssl/certs", // Common alternative to /usr/local/. }); if (!arrayp(root_cert_dirs)) { root_cert_dirs = ({ root_cert_dirs }); } mapping(string:array(Verifier)) res = ([]); foreach(root_cert_dirs, string dir) { string pem = Stdio.read_bytes(combine_path(dir, "ca-certificates.crt")); if (pem) { Standards.PEM.Messages messages = Standards.PEM.Messages(pem);
49c4c02014-11-03Martin Nilsson  foreach(messages->get_certificates(), string m) { TBSCertificate tbs = verify_ca_certificate(m);
a20f492014-03-05Henrik Grubbström (Grubba)  if (!tbs) continue;
18f2752014-04-30Martin Nilsson  string subj = tbs->subject->get_der(); if( !res[subj] || !has_value(res[subj], tbs->public_key ) ) res[subj] += ({ tbs->public_key });
a20f492014-03-05Henrik Grubbström (Grubba)  } continue; } foreach(get_dir(dir) || ({}), string fname) { if (has_suffix(fname, ".0")) { // Skip OpenSSL hash files for now (as they are duplicates). continue; } fname = combine_path(dir, fname); if (!Stdio.is_file(fname)) continue; pem = Stdio.read_bytes(fname); if (!pem) continue; string cert = Standards.PEM.simple_decode(pem); if (!cert) continue;
57c8ac2014-04-21Martin Nilsson  TBSCertificate tbs = verify_ca_certificate(cert);
a20f492014-03-05Henrik Grubbström (Grubba)  if (!tbs) continue;
18f2752014-04-30Martin Nilsson  string subj = tbs->subject->get_der(); if( !res[subj] || !has_value(res[subj], tbs->public_key ) ) res[subj] += ({ tbs->public_key });
a20f492014-03-05Henrik Grubbström (Grubba)  } } return res; }
33a9982014-04-28Martin Nilsson //! Decodes a certificate chain, oredered from leaf to root, and //! checks the signatures. Verifies that the chain can be decoded //! correctly, is unbroken, and that all certificates are in effect //! (time-wise.) and allowed to sign it's child certificate. //! //! No verifications are done on the leaf certificate to determine //! what it can and can not be used for.
b68b462013-08-15Martin Nilsson //!
cdd85c2015-02-27Martin Nilsson //! Returns a mapping with the following contents, depending
b68b462013-08-15Martin Nilsson //! on the verification of the certificate chain: //! //! @mapping //! @member int "error_code"
1453212014-04-27Martin Nilsson //! Error describing type of verification failurew, if //! verification failed. May be one of the following, OR:ed //! together: @[CERT_TOO_NEW], @[CERT_TOO_OLD], //! @[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.
33a9982014-04-28Martin Nilsson //! @member Standards.ASN1.Sequence "authority" //! The authority RDN that verified the chain. //! @member Standards.ASN1.Sequence "cn" //! The common name RDN of the leaf certificate. //! @member array(TBSCertificate) "certificates" //! An array with the decoded certificates, ordered from root to leaf.
b68b462013-08-15Martin Nilsson //! @endmapping //! //! @param cert_chain //! An array of certificates, with the relative-root last. Each
9532b22015-02-27Martin Nilsson //! certificate should be a DER-encoded certificate, or decoded as a //! @[Standards.PKCS.Signature.Signed] object.
b68b462013-08-15Martin Nilsson //! @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.
9532b22015-02-27Martin Nilsson mapping verify_certificate_chain(array(string|.PKCS.Signature.Signed) cert_chain,
ea8b5c2014-03-04Henrik Grubbström (Grubba)  mapping(string:Verifier|array(Verifier)) authorities,
8b05432013-11-24Martin Nilsson  int|void require_trust)
b68b462013-08-15Martin Nilsson { mapping m = ([ ]);
1453212014-04-27Martin Nilsson 
8b05432013-11-24Martin Nilsson #define ERROR(X) do { \ DBG("Error " #X "\n"); \
1453212014-04-27Martin Nilsson  m->verified=0; m->error_code|=(X); m->error_cert=idx; \
8b05432013-11-24Martin Nilsson  } while(0)
1453212014-04-27Martin Nilsson #define FATAL(X) do { ERROR(X); return m; } while(0)
b68b462013-08-15Martin Nilsson 
72a5202014-03-24Martin Nilsson  // Decode all certificates in the chain. Leaf is first and root is // last.
b68b462013-08-15Martin Nilsson  int len = sizeof(cert_chain); array chain_obj = allocate(len); array chain_cert = allocate(len);
9532b22015-02-27Martin Nilsson  foreach(cert_chain; int idx; string|.PKCS.Signature.Signed c)
b68b462013-08-15Martin Nilsson  {
9532b22015-02-27Martin Nilsson  .PKCS.Signature.Signed cert; if( stringp(c) ) cert = .PKCS.Signature.decode_signed(c); TBSCertificate tbs = decode_certificate(c);
b68b462013-08-15Martin Nilsson  if(!tbs)
1453212014-04-27Martin Nilsson  FATAL(CERT_INVALID);
b68b462013-08-15Martin Nilsson  int idx = len-idx-1; chain_cert[idx] = cert; chain_obj[idx] = tbs; }
33a9982014-04-28Martin Nilsson  m->certificates = chain_obj;
b68b462013-08-15Martin Nilsson 
72a5202014-03-24Martin Nilsson  // Chain is now reversed so root is first and leaf is last.
93182b2014-04-21Martin Nilsson  int my_time = time();
b68b462013-08-15Martin Nilsson  foreach(chain_obj; int idx; TBSCertificate tbs) {
ea8b5c2014-03-04Henrik Grubbström (Grubba)  array(Verifier)|Verifier verifiers;
b68b462013-08-15Martin Nilsson 
6e7bab2014-05-18Martin Nilsson  // Check not_before. We want the current time to be later. if(my_time < tbs->not_before) ERROR(CERT_TOO_NEW); // Check not_after. We want the current time to be earlier. if(my_time > tbs->not_after) ERROR(CERT_TOO_OLD);
0a00252014-04-05Martin Nilsson  if(idx != len-1) // Not the leaf { // id-ce-basicConstraints is required for certificates with
33b0282014-04-26Martin Nilsson  // public key used to validate certificate signatures. if( !tbs->ext_basicConstraints )
0a00252014-04-05Martin Nilsson  ERROR(CERT_INVALID);
33b0282014-04-26Martin Nilsson  if( !tbs->ext_basicConstraints_cA )
0a00252014-04-05Martin Nilsson  ERROR(CERT_UNAUTHORIZED_CA);
3f1bdd2014-04-27Martin Nilsson  if( tbs->ext_basicConstraints_pathLenConstraint )
0a00252014-04-05Martin Nilsson  {
3f1bdd2014-04-27Martin Nilsson  // len-1-idx is the number of following certificates. if( len-1-idx > tbs->ext_basicConstraints_pathLenConstraint )
0a00252014-04-05Martin Nilsson  { // The error was later in the chain though, so maybe a // different error should be sent.
ba33982014-04-27Martin Nilsson  ERROR(CERT_EXCEEDED_PATH_LENGTH);
0a00252014-04-05Martin Nilsson  } }
ba33982014-04-27Martin Nilsson 
62e7c62014-04-29Martin Nilsson  if( !(tbs->ext_keyUsage & KU_keyCertSign) )
ba33982014-04-27Martin Nilsson  ERROR(CERT_UNAUTHORIZED_CA); }
0a00252014-04-05Martin Nilsson 
b68b462013-08-15Martin Nilsson  if(idx == 0) // The root cert {
ea8b5c2014-03-04Henrik Grubbström (Grubba)  verifiers = authorities[tbs->issuer->get_der()];
b68b462013-08-15Martin Nilsson  // if we don't know the issuer of the root certificate, and we // require trust, we're done.
ea8b5c2014-03-04Henrik Grubbström (Grubba)  if(!verifiers && 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.
6e7bab2014-05-18Martin Nilsson  if(!verifiers)
ea8b5c2014-03-04Henrik Grubbström (Grubba)  verifiers = ({ tbs->public_key });
b68b462013-08-15Martin Nilsson  }
df34e62014-05-18Martin Nilsson  if (objectp(verifiers)) verifiers = ({ verifiers });
b68b462013-08-15Martin Nilsson  } else // otherwise, we make sure the chain is unbroken. { // 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.
ea8b5c2014-03-04Henrik Grubbström (Grubba)  verifiers = ({ chain_obj[idx-1]->public_key });
b68b462013-08-15Martin Nilsson  }
a646162015-05-28Martin Nilsson  m->verified = 0;
ea8b5c2014-03-04Henrik Grubbström (Grubba)  int verified; foreach(verifiers || ({}), Verifier 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");
ea8b5c2014-03-04Henrik Grubbström (Grubba)  m->verified = verified = 1;
b68b462013-08-15Martin Nilsson  // if we're the root of the chain and we've verified, this is // the authority. if(idx == 0) m->authority = tbs->issuer;
cdd85c2015-02-27Martin Nilsson 
b68b462013-08-15Martin Nilsson  if(idx == sizeof(chain_cert)-1) m->cn = tbs->subject;
ea8b5c2014-03-04Henrik Grubbström (Grubba)  break;
b68b462013-08-15Martin Nilsson  } }
ea8b5c2014-03-04Henrik Grubbström (Grubba)  if (!verified)
c02ad02015-05-28Tobias S. Josefowitz  {
ea8b5c2014-03-04Henrik Grubbström (Grubba)  ERROR(CERT_BAD_SIGNATURE);
c02ad02015-05-28Tobias S. Josefowitz  return m; }
b68b462013-08-15Martin Nilsson  } return m;
8b05432013-11-24Martin Nilsson  #undef ERROR
1453212014-04-27Martin Nilsson #undef FATAL
b68b462013-08-15Martin Nilsson }
5ece3a2014-05-13Henrik Grubbström (Grubba)  //! DWIM-parse the ASN.1-sequence for a private key.
8d8ad82014-08-14Henrik Grubbström (Grubba) Crypto.Sign.State parse_private_key(Sequence seq)
5ece3a2014-05-13Henrik Grubbström (Grubba) { switch(sizeof(seq)) { case 5: return Standards.PKCS.DSA.parse_private_key(seq); case 9: return Standards.PKCS.RSA.parse_private_key(seq); #if constant(Nettle.ECC_Curve) case 2: // ECDSA, implicit curve. Not supported yet. return UNDEFINED; case 3: case 4: return Standards.PKCS.ECDSA.parse_private_key(seq); #endif } return UNDEFINED; } //! DWIM-parse the DER-sequence for a private key.
8d8ad82014-08-14Henrik Grubbström (Grubba) variant Crypto.Sign.State parse_private_key(string private_key)
5ece3a2014-05-13Henrik Grubbström (Grubba) {
1591ba2014-09-30Martin Nilsson  Object seq = Standards.ASN1.Decode.secure_der_decode(private_key);
5ece3a2014-05-13Henrik Grubbström (Grubba)  if (!seq || (seq->type_name != "SEQUENCE")) return UNDEFINED; return parse_private_key([object(Sequence)]seq); }