1 /** 2 * Credentials Manager 3 * 4 * Copyright: 5 * (C) 2011,2012 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.tls.credentials_manager; 12 13 import botan.constants; 14 static if (BOTAN_HAS_TLS): 15 import botan.cert.x509.x509cert; 16 import botan.cert.x509.certstor; 17 import botan.math.bigint.bigint; 18 import botan.pubkey.pk_keys; 19 import botan.algo_base.symkey; 20 import botan.tls.credentials_manager; 21 import botan.cert.x509.x509path; 22 import botan.utils.types; 23 import botan.pubkey.algo.ecdsa; 24 25 /** 26 * Interface for a credentials manager. 27 * 28 * A type is a fairly static value that represents the general nature 29 * of the transaction occuring. Currently used values are "tls-client" 30 * and "tls-server". Context represents a hostname, email address, 31 * username, or other identifier. 32 */ 33 abstract class TLSCredentialsManager 34 { 35 public: 36 37 /** 38 * Return a list of the certificates of CAs that we trust in this 39 * type/context. 40 * 41 * Params: 42 * type = specifies the type of operation occuring 43 * 44 * context = specifies a context relative to type. For instance 45 * for type "tls-client", context specifies the servers name. 46 */ 47 abstract Vector!CertificateStore 48 trustedCertificateAuthorities(in string type, in string context) 49 { 50 return Vector!CertificateStore(); 51 } 52 53 /** 54 * Check the certificate chain is valid up to a trusted root, and 55 * optionally (if hostname != "") that the hostname given is 56 * consistent with the leaf certificate. 57 * 58 * This function should throw new an exception derived from 59 * $(D Exception) with an informative what() result if the 60 * certificate chain cannot be verified. 61 62 * Params: 63 * type = specifies the type of operation occuring 64 * purported_hostname = specifies the purported hostname 65 * cert_chain = specifies a certificate chain leading to a 66 * trusted root CA certificate. 67 */ 68 abstract void verifyCertificateChain(in string type, 69 in string purported_hostname, 70 const ref Vector!X509Certificate cert_chain) 71 { 72 if (cert_chain.empty) 73 throw new InvalidArgument("Certificate chain was empty"); 74 75 auto trusted_CAs = trustedCertificateAuthorities(type, purported_hostname); 76 77 PathValidationRestrictions restrictions; 78 79 auto result = x509PathValidate(cert_chain, 80 restrictions, 81 trusted_CAs); 82 83 if (!result.successfulValidation()) 84 throw new Exception("Certificate validation failure: " ~ result.resultString()); 85 86 if (!certInSomeStore(trusted_CAs, result.trustRoot())) 87 throw new Exception("Certificate chain roots in unknown/untrusted CA"); 88 89 if (purported_hostname != "" && !cert_chain[0].matchesDnsName(purported_hostname)) 90 throw new Exception("Certificate did not match hostname"); 91 } 92 93 /** 94 * Return a cert chain we can use, ordered from leaf to root, 95 * or else an empty vector. 96 * 97 * It is assumed that the caller can get the private key of the 98 * leaf with privateKeyFor 99 * 100 * Params: 101 * cert_key_types = specifies the key types desired ("RSA", 102 * "DSA", "ECDSA", etc), or empty if there 103 * is no preference by the caller. 104 * 105 * type = specifies the type of operation occuring 106 * 107 * context = specifies a context relative to type. 108 */ 109 abstract Vector!X509Certificate certChain(const ref Vector!string cert_key_types, 110 in string type, 111 in string context) 112 { 113 return Vector!X509Certificate(); 114 } 115 116 /// ditto 117 final Vector!X509Certificate certChain(T : string[])(auto ref T cert_key_types, in string type, in string context) 118 { 119 return certChain(Vector!string(cert_key_types), type, context); 120 } 121 122 /** 123 * Return a cert chain we can use, ordered from leaf to root, 124 * or else an empty vector. 125 * 126 * It is assumed that the caller can get the private key of the 127 * leaf with privateKeyFor 128 * 129 * Params: 130 * cert_key_type = specifies the type of key requested 131 * ("RSA", "DSA", "ECDSA", etc) 132 * 133 * type = specifies the type of operation occuring 134 * 135 * context = specifies a context relative to type. 136 */ 137 abstract Vector!X509Certificate certChainSingleType(in string cert_key_type, 138 in string type, 139 in string context) 140 { 141 Vector!string cert_types; 142 cert_types.pushBack(cert_key_type); 143 return certChain(cert_types, type, context); 144 } 145 146 /** 147 * 148 * Params: 149 * cert = as returned by cert_chain 150 * type = specifies the type of operation occuring 151 * context = specifies a context relative to type. 152 * 153 * Returns: private key associated with this certificate if we should 154 * use it with this context. 155 * 156 * Notes: this object should retain ownership of the returned key; 157 * it should not be deleted by the caller. 158 */ 159 abstract PrivateKey privateKeyFor(in X509Certificate cert, in string type, in string context) 160 { 161 return null; 162 } 163 164 /** 165 * Params: 166 * type = specifies the type of operation occuring 167 * context = specifies a context relative to type. 168 * Returns: true if we should attempt SRP authentication 169 */ 170 abstract bool attemptSrp(in string type, in string context) 171 { 172 return false; 173 } 174 175 /** 176 * Params: 177 * type = specifies the type of operation occuring 178 * context = specifies a context relative to type. 179 * Returns: identifier for client-side SRP auth, if available 180 for this type/context. Should return empty string 181 if password auth not desired/available. 182 */ 183 abstract string srpIdentifier(in string type, in string context) 184 { 185 return ""; 186 } 187 188 /** 189 * Params: 190 * type = specifies the type of operation occuring 191 * context = specifies a context relative to type. 192 * identifier = specifies what identifier we want the 193 * password for. This will be a value previously returned 194 * by srp_identifier. 195 * Returns: password for client-side SRP auth, if available 196 for this identifier/type/context. 197 */ 198 abstract string srpPassword(in string type, 199 in string context, 200 in string identifier) 201 { 202 return ""; 203 } 204 205 /** 206 * Retrieve SRP verifier parameters 207 */ 208 abstract bool srpVerifier(in string type, 209 in string context, 210 in string identifier, 211 ref string group_name, 212 ref BigInt verifier, 213 ref Vector!ubyte salt, 214 bool generate_fake_on_unknown) 215 { 216 return false; 217 } 218 219 /** 220 * Params: 221 * type = specifies the type of operation occuring 222 * context = specifies a context relative to type. 223 * Returns: the PSK identity hint for this type/context 224 */ 225 abstract string pskIdentityHint(in string type, in string context) 226 { 227 return ""; 228 } 229 230 /** 231 * Params: 232 * type = specifies the type of operation occuring 233 * context = specifies a context relative to type. 234 * identity_hint = was passed by the server (but may be empty) 235 * Returns: the PSK identity we want to use 236 */ 237 abstract string pskIdentity(in string type, in string context, in string identity_hint) 238 { 239 return ""; 240 } 241 242 /// Override and return true to signal PSK usage 243 abstract bool hasPsk() { 244 return false; 245 } 246 247 /// In TLSClient, identifies this machine with the server 248 PrivateKey channelPrivateKey(string hostname) 249 { 250 import botan.rng.auto_rng; 251 static ECDSAPrivateKey[string] pkey_saved; 252 if (hostname !in pkey_saved) { 253 auto rng = scoped!AutoSeededRNG(); 254 pkey_saved[hostname] = ECDSAPrivateKey(rng, ECGroup("secp256r1")); 255 } 256 return cast(PrivateKey) pkey_saved[hostname]; 257 } 258 259 /** 260 * Params: 261 * type = specifies the type of operation occuring 262 * context = specifies a context relative to type. 263 * identity = is a PSK identity previously returned by 264 psk_identity for the same type and context. 265 * Returns: the PSK used for identity, or throw new an exception if no 266 * key exists 267 */ 268 abstract SymmetricKey psk(in string type, in string context, in string identity) 269 { 270 throw new InternalError("No PSK set for identity " ~ identity); 271 } 272 } 273 274 bool certInSomeStore(const ref Vector!CertificateStore trusted_CAs, in X509Certificate trust_root) 275 { 276 foreach (const ref CertificateStore CAs; trusted_CAs[]) 277 if (CAs.certificateKnown(trust_root)) 278 return true; 279 return false; 280 }