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 }