Branch: Tag:

2014-07-14

2014-07-14 17:04:00 by Martin Nilsson <nilsson@opera.com>

Sort Context items into Global, Cryptography, Certificates/authentication and Sessions

42:   import ".";   import Constants;    + protected void create() + { +  SSL3_DEBUG_MSG("SSL.Context->create\n"); +  +  /* Backwards compatibility */ +  preferred_suites = get_suites(128, 1); + } +    //! The minimum supported protocol version.   //!   //! Defaults to @[PROTOCOL_SSL_3_0].
58:   //! This value should not be less than @[min_version].   ProtocolVersion max_version = PROTOCOL_TLS_MAX;    + //! List of advertised protocols using using TLS application level + //! protocol negotiation. + array(string(8bit)) advertised_protocols; +  + //! The maximum amount of data that is sent in each SSL packet by + //! @[File]. A value between 1 and @[Constants.PACKET_MAX_SIZE]. + int packet_max_size = PACKET_MAX_SIZE; +  + //! Lists the supported compression algorithms in order of preference. + //! + //! Defaults to @expr{({ COMPRESSION_null, COMPRESSION_deflate })@} + //! (ie avoid compression unless the client requires it) due to + //! SSL attacks that target compression. + array(int) preferred_compressors = ({ +  COMPRESSION_null, + #if constant(Gz) +  COMPRESSION_deflate, + #endif +  }); +    //! @decl Alert alert_factory(SSL.Connection con, int level, int description, @   //! ProtocolVersion version, @   //! string|void message, mixed|void trace)
89:    return Alert(level, description, version, message);   }    - //! Policy for client authentication. One of - //! @[SSL.Constants.AUTHLEVEL_none], @[SSL.Constants.AUTHLEVEL_ask] - //! and @[SSL.Constants.AUTHLEVEL_require]. - int auth_level; +     - //! Array of authorities that are accepted for client certificates. - //! The server will only accept connections from clients whose - //! certificate is signed by one of these authorities. The string is a - //! DER-encoded certificate, which typically must be decoded using - //! @[MIME.decode_base64] or @[Standards.PEM.Messages] first. - //! - //! Note that it is presumed that the issuer will also be trusted by - //! the server. See @[trusted_issuers] for details on specifying - //! trusted issuers. - //! - //! If empty, the server will accept any client certificate whose - //! issuer is trusted by the server. - void set_authorities(array(string) a) - { -  authorities = a; -  update_authorities(); - } + // + // --- Cryptography + //    - //! When set, require the chain to be known, even if the root is self - //! signed. - //! - //! Note that if set, and certificates are set to be verified, trusted - //! issuers must be provided, or no connections will be accepted. - int require_trust=0; -  - //! Get the list of allowed authorities. See @[set_authorities]. - array(string) get_authorities() - { -  return authorities; - } -  - protected array(string) authorities = ({}); - array(string(8bit)) authorities_cache = ({}); -  - //! Sets the list of trusted certificate issuers. - //! - //! @param a - //! - //! An array of certificate chains whose root is self signed (ie a - //! root issuer), and whose final certificate is an issuer that we - //! trust. The root of the certificate should be first certificate in - //! the chain. The string is a DER-encoded certificate, which - //! typically must be decoded using @[MIME.decode_base64] or - //! @[Standards.PEM.Messages] first. - //! - //! If this array is left empty, and the context is set to verify - //! certificates, a certificate chain must have a root that is self - //! signed. - void set_trusted_issuers(array(array(string)) i) - { -  trusted_issuers = i; -  update_trusted_issuers(); - } -  - //! Get the list of trusted issuers. See @[set_trusted_issuers]. - array(array(string)) get_trusted_issuers() - { -  return trusted_issuers; - } -  - protected array(array(string)) trusted_issuers = ({}); - mapping(string:array(Standards.X509.Verifier)) trusted_issuers_cache = ([]); -  - //! Determines whether certificates presented by the peer are - //! verified, or just accepted as being valid. - int verify_certificates = 0; -  +    //! Temporary, non-certified, private keys, used for RSA key exchange   //! in export mode. They are used as follows:   //!
188:   //! default set to @[Crypto.Random.random_string].   function(int(0..):string(8bit)) random = Crypto.Random.random_string;    - //! Certificates and their corresponding keys. - array(CertificatePair) cert_pairs = ({}); + //! Attempt to enable encrypt-then-mac mode. + int encrypt_then_mac = 1;    - protected int(0..1) cert_pairs_sorted = 0; -  - //! Lookup from SNI (Server Name Indication) (server), or Issuer DER - //! (client) to an array of suitable @[CertificatePair]s. - //! - //! Generated on demand from @[cert_pairs]. - mapping(string(8bit):array(CertificatePair)) cert_cache = ([]); -  - //! For client authentication. Used only if auth_level is AUTH_ask or - //! AUTH_require. - array(int) preferred_auth_methods = - ({ AUTH_rsa_sign }); -  +    //! Cipher suites we want to support, in order of preference, best   //! first.   array(int) preferred_suites;
211:   //! Supported elliptical curve cipher curves in order of preference.   array(int) ecc_curves = reverse(sort(indices(ECC_CURVES)));    - //! List of advertised protocols using using TLS application level - //! protocol negotiation. - array(string(8bit)) advertised_protocols; -  - //! The maximum amount of data that is sent in each SSL packet by - //! @[File]. A value between 1 and @[Constants.PACKET_MAX_SIZE]. - int packet_max_size = PACKET_MAX_SIZE; -  +    //! The set of <hash, signature> combinations to use by us.   //!   //! Only used with TLS 1.2 and later.
303:   #endif   }    - protected int cert_sort_key(CertificatePair cp) - { -  array(HashAlgorithm|SignatureAlgorithm) sign_alg = cp->sign_algs[0]; -  int bits = cp->key->key_size(); -  -  // Adjust the bits to be comparable for the different algorithms. -  switch(sign_alg[1]) { -  case SIGNATURE_rsa: -  // The normative size. -  break; -  case SIGNATURE_dsa: -  // The consensus seems to be that DSA keys are about -  // the same strength as the corresponding RSA length. -  break; -  case SIGNATURE_ecdsa: -  // ECDSA size: NIST says: Our approximation: -  // 160 bits ~1024 bits RSA 960 bits RSA -  // 224 bits ~2048 bits RSA 2240 bits RSA -  // 256 bits ~4096 bits RSA 3072 bits RSA -  // 384 bits ~7680 bits RSA 7680 bits RSA -  // 521 bits ~15360 bits RSA 14881 bits RSA -  bits = (bits * (bits - 64))>>4; -  if (bits < 0) bits = 128; -  break; -  } -  -  // NB: Returns negative to get the largest values sorted first. -  return -((bits<<16)|(sign_alg[1]<<8)|sign_alg[0]); - } -  - //! Order the @[cps] in priority order. - protected array(CertificatePair) sort_certs(array(CertificatePair) cps) - { -  if (sizeof(cps) > 1) { -  sort(map(cps, cert_sort_key), cps); -  } -  return cps; - } -  - //! Look up a suitable set of certificates for the specified SNI (server) - //! or issuer (client). - //! - //! @param is_issuer - //! Indicates whether to @[glob]-match against the common name (server), - //! or against the DER for the issuer (client). - array(CertificatePair) find_cert(array(string)|void sni_or_issuer, -  int(0..1)|void is_issuer) - { -  if (!cert_pairs_sorted) { -  cert_pairs = sort_certs(cert_pairs); -  cert_pairs_sorted = 1; -  } -  -  if (!sizeof(sni_or_issuer || ({}))) { -  // Either no/empty SNI, or empty certificate_authorities list. -  -  if (!is_issuer) { -  // First check if there's a set of default certs. -  // Note: This doubles as a cache lookup of the -  // fallback entry set further below. -  array(CertificatePair) res = find_cert(({ "" }), is_issuer); -  if (res && sizeof(res)) return res; -  -  // Fall back to returning the entire set, since -  // they are presumably all valid for the server. -  } -  -  // RFC 4346 7.4.4 -  // If the certificate_authorities list is empty then the client MAY -  // send any certificate of the appropriate ClientCertificateType, -  // unless there is some external arrangement to the contrary. -  return cert_cache[""] = cert_pairs; -  } -  -  mapping(string(8bit):array(CertificatePair)) certs = ([]); -  array(string(8bit)) maybes = ({}); -  -  if (!is_issuer) { -  sni_or_issuer = [array(string(8bit))]map(sni_or_issuer, lower_case); -  } -  -  foreach(sni_or_issuer, string name) { -  array(CertificatePair) res; -  if (res = cert_cache[name]) { -  certs[name] = res; -  continue; -  } -  if (zero_type(res)) { -  // Not known bad. -  certs[name] = ({}); -  maybes += ({ name }); -  } -  } -  -  if (sizeof(maybes)) { -  // There were some unknown names. Check them. -  if (is_issuer) { -  foreach(cert_pairs, CertificatePair cp) { -  foreach(maybes, string(8bit) i) { -  if (has_value(cp->issuers, i)) { -  certs[i] += ({ cp }); -  } -  } -  } -  } else { -  foreach(cert_pairs, CertificatePair cp) { -  foreach(cp->globs, string(8bit) g) { -  foreach(glob(g, maybes), string name) { -  certs[name] += ({ cp }); -  } -  } -  } -  } -  -  if (sizeof(cert_cache) > (sizeof(cert_pairs) * 10 + 10)) { -  // It seems the cache has been poisoned. Clean it. -  cert_cache = ([]); -  } -  -  // Update the cache. -  foreach(maybes, string name) { -  cert_cache[name] = certs[name]; -  } -  } -  -  // No certificate found. -  if (!sizeof(certs)) return UNDEFINED; -  -  if (sizeof(certs) == 1) { -  // Just a single matching name. -  return values(certs)[0]; -  } -  -  return sort_certs(values(certs) * ({})); - } -  - //! Add a certificate. - //! - //! This function is used on both servers and clients to add - //! a key and chain of certificates to the set of certificate - //! candidates to use in @[find_cert()]. - //! - //! On a server these are used in the normal initial handshake, - //! while on a client they are only used if a server requests - //! client certificate authentication. - //! - //! @param key - //! Private key matching the first certificate in @[certs]. - //! - //! Supported key types are currently: - //! @mixed - //! @type Crypto.RSA - //! Rivest-Shamir-Adelman. - //! @type Crypto.DSA - //! Digital Signing Algorithm. - //! @type Crypto.ECC.Curve.ECDSA - //! Elliptic Curve Digital Signing Algorithm. - //! @endmixed - //! - //! This key MUST match the public key in the first certificate - //! in @[certs]. - //! - //! @param certs - //! A chain of X509.v1 or X509.v3 certificates, with the local - //! certificate first and root-most certificate last. - //! - //! @param extra_name_globs - //! Further SNI globs (than the ones in the first certificate), that - //! this certificate should be selected for. Typically used to set - //! the default certificate(s) by specifying @expr{({ "*" })@}. - //! - //! The SNI globs are only relevant for server-side certificates. - //! - //! @param cp - //! An alternative is to send an initialized @[CertificatePair]. - //! - //! @throws - //! The function performs various validation of the @[key] - //! and @[certs], and throws errors if the validation fails. - //! - //! @seealso - //! @[find_cert()] - void add_cert(Crypto.Sign key, array(string(8bit)) certs, -  array(string(8bit))|void extra_name_globs) - { -  CertificatePair cp = CertificatePair(key, certs, extra_name_globs); -  -  cert_pairs += ({ cp }); -  -  cert_pairs_sorted = 0; -  -  cert_cache = ([]); - } - variant void add_cert(CertificatePair cp) - { -  cert_pairs += ({ cp }); -  -  cert_pairs_sorted = 0; -  -  cert_cache = ([]); - } -  +    // Generate a sort key for a cipher suite.   //   // The larger the value, the stronger the cipher suite.
818:      #endif /* Crypto.ECC.Curve && Crypto.AES.GCM && Crypto.SHA384 */    - //! Lists the supported compression algorithms in order of preference. +  + // + // --- Certificates and authentication + // +  + //! Policy for client authentication. One of + //! @[SSL.Constants.AUTHLEVEL_none], @[SSL.Constants.AUTHLEVEL_ask] + //! and @[SSL.Constants.AUTHLEVEL_require]. + int auth_level; +  + //! Array of authorities that are accepted for client certificates. + //! The server will only accept connections from clients whose + //! certificate is signed by one of these authorities. The string is a + //! DER-encoded certificate, which typically must be decoded using + //! @[MIME.decode_base64] or @[Standards.PEM.Messages] first.   //! - //! Defaults to @expr{({ COMPRESSION_null, COMPRESSION_deflate })@} - //! (ie avoid compression unless the client requires it) due to - //! SSL attacks that target compression. - array(int) preferred_compressors = ({ -  COMPRESSION_null, - #if constant(Gz) -  COMPRESSION_deflate, - #endif -  }); + //! Note that it is presumed that the issuer will also be trusted by + //! the server. See @[trusted_issuers] for details on specifying + //! trusted issuers. + //! + //! If empty, the server will accept any client certificate whose + //! issuer is trusted by the server. + void set_authorities(array(string) a) + { +  authorities = a; +  update_authorities(); + }    - //! Attempt to enable encrypt-then-mac mode. - int encrypt_then_mac = 1; + //! When set, require the chain to be known, even if the root is self + //! signed. + //! + //! Note that if set, and certificates are set to be verified, trusted + //! issuers must be provided, or no connections will be accepted. + int require_trust=0;    -  + //! Get the list of allowed authorities. See @[set_authorities]. + array(string) get_authorities() + { +  return authorities; + } +  + protected array(string) authorities = ({}); + array(string(8bit)) authorities_cache = ({}); +  + //! Sets the list of trusted certificate issuers. + //! + //! @param a + //! + //! An array of certificate chains whose root is self signed (ie a + //! root issuer), and whose final certificate is an issuer that we + //! trust. The root of the certificate should be first certificate in + //! the chain. The string is a DER-encoded certificate, which + //! typically must be decoded using @[MIME.decode_base64] or + //! @[Standards.PEM.Messages] first. + //! + //! If this array is left empty, and the context is set to verify + //! certificates, a certificate chain must have a root that is self + //! signed. + void set_trusted_issuers(array(array(string)) i) + { +  trusted_issuers = i; +  update_trusted_issuers(); + } +  + //! Get the list of trusted issuers. See @[set_trusted_issuers]. + array(array(string)) get_trusted_issuers() + { +  return trusted_issuers; + } +  + protected array(array(string)) trusted_issuers = ({}); + mapping(string:array(Standards.X509.Verifier)) trusted_issuers_cache = ([]); +  + //! Determines whether certificates presented by the peer are + //! verified, or just accepted as being valid. + int verify_certificates = 0; +  + //! Certificates and their corresponding keys. + array(CertificatePair) cert_pairs = ({}); +  + protected int(0..1) cert_pairs_sorted = 0; +  + //! Lookup from SNI (Server Name Indication) (server), or Issuer DER + //! (client) to an array of suitable @[CertificatePair]s. + //! + //! Generated on demand from @[cert_pairs]. + mapping(string(8bit):array(CertificatePair)) cert_cache = ([]); +  + //! For client authentication. Used only if auth_level is AUTH_ask or + //! AUTH_require. + array(int) preferred_auth_methods = + ({ AUTH_rsa_sign }); +  + protected int cert_sort_key(CertificatePair cp) + { +  array(HashAlgorithm|SignatureAlgorithm) sign_alg = cp->sign_algs[0]; +  int bits = cp->key->key_size(); +  +  // Adjust the bits to be comparable for the different algorithms. +  switch(sign_alg[1]) { +  case SIGNATURE_rsa: +  // The normative size. +  break; +  case SIGNATURE_dsa: +  // The consensus seems to be that DSA keys are about +  // the same strength as the corresponding RSA length. +  break; +  case SIGNATURE_ecdsa: +  // ECDSA size: NIST says: Our approximation: +  // 160 bits ~1024 bits RSA 960 bits RSA +  // 224 bits ~2048 bits RSA 2240 bits RSA +  // 256 bits ~4096 bits RSA 3072 bits RSA +  // 384 bits ~7680 bits RSA 7680 bits RSA +  // 521 bits ~15360 bits RSA 14881 bits RSA +  bits = (bits * (bits - 64))>>4; +  if (bits < 0) bits = 128; +  break; +  } +  +  // NB: Returns negative to get the largest values sorted first. +  return -((bits<<16)|(sign_alg[1]<<8)|sign_alg[0]); + } +  + //! Order the @[cps] in priority order. + protected array(CertificatePair) sort_certs(array(CertificatePair) cps) + { +  if (sizeof(cps) > 1) { +  sort(map(cps, cert_sort_key), cps); +  } +  return cps; + } +  + //! Look up a suitable set of certificates for the specified SNI (server) + //! or issuer (client). + //! + //! @param is_issuer + //! Indicates whether to @[glob]-match against the common name (server), + //! or against the DER for the issuer (client). + array(CertificatePair) find_cert(array(string)|void sni_or_issuer, +  int(0..1)|void is_issuer) + { +  if (!cert_pairs_sorted) { +  cert_pairs = sort_certs(cert_pairs); +  cert_pairs_sorted = 1; +  } +  +  if (!sizeof(sni_or_issuer || ({}))) { +  // Either no/empty SNI, or empty certificate_authorities list. +  +  if (!is_issuer) { +  // First check if there's a set of default certs. +  // Note: This doubles as a cache lookup of the +  // fallback entry set further below. +  array(CertificatePair) res = find_cert(({ "" }), is_issuer); +  if (res && sizeof(res)) return res; +  +  // Fall back to returning the entire set, since +  // they are presumably all valid for the server. +  } +  +  // RFC 4346 7.4.4 +  // If the certificate_authorities list is empty then the client MAY +  // send any certificate of the appropriate ClientCertificateType, +  // unless there is some external arrangement to the contrary. +  return cert_cache[""] = cert_pairs; +  } +  +  mapping(string(8bit):array(CertificatePair)) certs = ([]); +  array(string(8bit)) maybes = ({}); +  +  if (!is_issuer) { +  sni_or_issuer = [array(string(8bit))]map(sni_or_issuer, lower_case); +  } +  +  foreach(sni_or_issuer, string name) { +  array(CertificatePair) res; +  if (res = cert_cache[name]) { +  certs[name] = res; +  continue; +  } +  if (zero_type(res)) { +  // Not known bad. +  certs[name] = ({}); +  maybes += ({ name }); +  } +  } +  +  if (sizeof(maybes)) { +  // There were some unknown names. Check them. +  if (is_issuer) { +  foreach(cert_pairs, CertificatePair cp) { +  foreach(maybes, string(8bit) i) { +  if (has_value(cp->issuers, i)) { +  certs[i] += ({ cp }); +  } +  } +  } +  } else { +  foreach(cert_pairs, CertificatePair cp) { +  foreach(cp->globs, string(8bit) g) { +  foreach(glob(g, maybes), string name) { +  certs[name] += ({ cp }); +  } +  } +  } +  } +  +  if (sizeof(cert_cache) > (sizeof(cert_pairs) * 10 + 10)) { +  // It seems the cache has been poisoned. Clean it. +  cert_cache = ([]); +  } +  +  // Update the cache. +  foreach(maybes, string name) { +  cert_cache[name] = certs[name]; +  } +  } +  +  // No certificate found. +  if (!sizeof(certs)) return UNDEFINED; +  +  if (sizeof(certs) == 1) { +  // Just a single matching name. +  return values(certs)[0]; +  } +  +  return sort_certs(values(certs) * ({})); + } +  + //! Add a certificate. + //! + //! This function is used on both servers and clients to add + //! a key and chain of certificates to the set of certificate + //! candidates to use in @[find_cert()]. + //! + //! On a server these are used in the normal initial handshake, + //! while on a client they are only used if a server requests + //! client certificate authentication. + //! + //! @param key + //! Private key matching the first certificate in @[certs]. + //! + //! Supported key types are currently: + //! @mixed + //! @type Crypto.RSA + //! Rivest-Shamir-Adelman. + //! @type Crypto.DSA + //! Digital Signing Algorithm. + //! @type Crypto.ECC.Curve.ECDSA + //! Elliptic Curve Digital Signing Algorithm. + //! @endmixed + //! + //! This key MUST match the public key in the first certificate + //! in @[certs]. + //! + //! @param certs + //! A chain of X509.v1 or X509.v3 certificates, with the local + //! certificate first and root-most certificate last. + //! + //! @param extra_name_globs + //! Further SNI globs (than the ones in the first certificate), that + //! this certificate should be selected for. Typically used to set + //! the default certificate(s) by specifying @expr{({ "*" })@}. + //! + //! The SNI globs are only relevant for server-side certificates. + //! + //! @param cp + //! An alternative is to send an initialized @[CertificatePair]. + //! + //! @throws + //! The function performs various validation of the @[key] + //! and @[certs], and throws errors if the validation fails. + //! + //! @seealso + //! @[find_cert()] + void add_cert(Crypto.Sign key, array(string(8bit)) certs, +  array(string(8bit))|void extra_name_globs) + { +  CertificatePair cp = CertificatePair(key, certs, extra_name_globs); +  +  cert_pairs += ({ cp }); +  +  cert_pairs_sorted = 0; +  +  cert_cache = ([]); + } + variant void add_cert(CertificatePair cp) + { +  cert_pairs += ({ cp }); +  +  cert_pairs_sorted = 0; +  +  cert_cache = ([]); + } +  + // update the cached decoded authorities list + private void update_authorities() + { +  authorities_cache = ({}); +  foreach(authorities, string a) +  authorities_cache += ({ Standards.X509.decode_certificate(a)-> +  subject->get_der() }); + } +  + // update the cached decoded issuers list + private void update_trusted_issuers() + { +  trusted_issuers_cache=([]); +  foreach(trusted_issuers, array(string) i) +  { +  // make sure the chain is valid and intact. +  mapping result = Standards.X509.verify_certificate_chain(i, ([]), 0); +  +  if(!result->verified) +  error("Broken trusted issuer chain!\n"); +  +  // FIXME: The pathLenConstraint does not survive the cache. +  +  // The leaf of the trusted issuer is the root to validate +  // certificate chains against. +  Standards.X509.TBSCertificate cert = +  ([array(object(Standards.X509.TBSCertificate))]result->certificates)[-1]; +  +  if( !cert->ext_basicConstraints_cA || +  !(cert->ext_keyUsage & Standards.X509.KU_keyCertSign) ) +  error("Trusted issuer not allowed to sign other certificates.\n"); +  +  trusted_issuers_cache[cert->subject->get_der()] += ({ cert->public_key }); +  } + } +  +  + // + // --- Sessions + // +    //! Non-zero to enable caching of sessions   int use_cache = 1;   
916:    /* There's no need to remove the id from the active_sessions queue */   }    - protected void create() - { -  SSL3_DEBUG_MSG("SSL.Context->create\n"); +     -  /* Backwards compatibility */ -  preferred_suites = get_suites(128, 1); - } -  - // update the cached decoded authorities list - private void update_authorities() - { -  authorities_cache = ({}); -  foreach(authorities, string a) -  authorities_cache += ({ Standards.X509.decode_certificate(a)-> -  subject->get_der() }); - } -  - // update the cached decoded issuers list - private void update_trusted_issuers() - { -  trusted_issuers_cache=([]); -  foreach(trusted_issuers, array(string) i) -  { -  // make sure the chain is valid and intact. -  mapping result = Standards.X509.verify_certificate_chain(i, ([]), 0); -  -  if(!result->verified) -  error("Broken trusted issuer chain!\n"); -  -  // FIXME: The pathLenConstraint does not survive the cache. -  -  // The leaf of the trusted issuer is the root to validate -  // certificate chains against. -  Standards.X509.TBSCertificate cert = -  ([array(object(Standards.X509.TBSCertificate))]result->certificates)[-1]; -  -  if( !cert->ext_basicConstraints_cA || -  !(cert->ext_keyUsage & Standards.X509.KU_keyCertSign) ) -  error("Trusted issuer not allowed to sign other certificates.\n"); -  -  trusted_issuers_cache[cert->subject->get_der()] += ({ cert->public_key }); -  } - } -  -  +    //   // --- Compat code below   //