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