1 /*
2  * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.ssl;
27 
28 import java.security.AlgorithmConstraints;
29 import java.security.CryptoPrimitive;
30 import java.security.PrivateKey;
31 
32 import java.util.Set;
33 import java.util.HashSet;
34 import java.util.Map;
35 import java.util.EnumSet;
36 import java.util.TreeMap;
37 import java.util.Collection;
38 import java.util.Collections;
39 import java.util.ArrayList;
40 
41 import sun.security.util.KeyUtil;
42 
43 /**
44  * Signature and hash algorithm.
45  *
46  * [RFC5246] The client uses the "signature_algorithms" extension to
47  * indicate to the server which signature/hash algorithm pairs may be
48  * used in digital signatures.  The "extension_data" field of this
49  * extension contains a "supported_signature_algorithms" value.
50  *
51  *     enum {
52  *         none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
53  *         sha512(6), (255)
54  *     } HashAlgorithm;
55  *
56  *     enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) }
57  *       SignatureAlgorithm;
58  *
59  *     struct {
60  *           HashAlgorithm hash;
61  *           SignatureAlgorithm signature;
62  *     } SignatureAndHashAlgorithm;
63  */
64 final class SignatureAndHashAlgorithm {
65 
66     // minimum priority for default enabled algorithms
67     final static int SUPPORTED_ALG_PRIORITY_MAX_NUM = 0x00F0;
68 
69     // performance optimization
70     private final static Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET =
71                                     EnumSet.of(CryptoPrimitive.SIGNATURE);
72 
73     // supported pairs of signature and hash algorithm
74     private final static Map<Integer, SignatureAndHashAlgorithm> supportedMap;
75     private final static Map<Integer, SignatureAndHashAlgorithm> priorityMap;
76 
77     // the hash algorithm
78     private HashAlgorithm hash;
79 
80     // the signature algorithm
81     private SignatureAlgorithm signature;
82 
83     // id in 16 bit MSB format, i.e. 0x0603 for SHA512withECDSA
84     private int id;
85 
86     // the standard algorithm name, for example "SHA512withECDSA"
87     private String algorithm;
88 
89     // Priority for the preference order. The lower the better.
90     //
91     // If the algorithm is unsupported, its priority should be bigger
92     // than SUPPORTED_ALG_PRIORITY_MAX_NUM.
93     private int priority;
94 
95     // constructor for supported algorithm
SignatureAndHashAlgorithm(HashAlgorithm hash, SignatureAlgorithm signature, String algorithm, int priority)96     private SignatureAndHashAlgorithm(HashAlgorithm hash,
97             SignatureAlgorithm signature, String algorithm, int priority) {
98         this.hash = hash;
99         this.signature = signature;
100         this.algorithm = algorithm;
101         this.id = ((hash.value & 0xFF) << 8) | (signature.value & 0xFF);
102         this.priority = priority;
103     }
104 
105     // constructor for unsupported algorithm
SignatureAndHashAlgorithm(String algorithm, int id, int sequence)106     private SignatureAndHashAlgorithm(String algorithm, int id, int sequence) {
107         this.hash = HashAlgorithm.valueOf((id >> 8) & 0xFF);
108         this.signature = SignatureAlgorithm.valueOf(id & 0xFF);
109         this.algorithm = algorithm;
110         this.id = id;
111 
112         // add one more to the sequece number, in case that the number is zero
113         this.priority = SUPPORTED_ALG_PRIORITY_MAX_NUM + sequence + 1;
114     }
115 
116     // Note that we do not use the sequence argument for supported algorithms,
117     // so please don't sort by comparing the objects read from handshake
118     // messages.
valueOf(int hash, int signature, int sequence)119     static SignatureAndHashAlgorithm valueOf(int hash,
120             int signature, int sequence) {
121         hash &= 0xFF;
122         signature &= 0xFF;
123 
124         int id = (hash << 8) | signature;
125         SignatureAndHashAlgorithm signAlg = supportedMap.get(id);
126         if (signAlg == null) {
127             // unsupported algorithm
128             signAlg = new SignatureAndHashAlgorithm(
129                 "Unknown (hash:0x" + Integer.toString(hash, 16) +
130                 ", signature:0x" + Integer.toString(signature, 16) + ")",
131                 id, sequence);
132         }
133 
134         return signAlg;
135     }
136 
getHashValue()137     int getHashValue() {
138         return (id >> 8) & 0xFF;
139     }
140 
getSignatureValue()141     int getSignatureValue() {
142         return id & 0xFF;
143     }
144 
getAlgorithmName()145     String getAlgorithmName() {
146         return algorithm;
147     }
148 
149     // return the size of a SignatureAndHashAlgorithm structure in TLS record
sizeInRecord()150     static int sizeInRecord() {
151         return 2;
152     }
153 
154     // Get local supported algorithm collection complying to
155     // algorithm constraints
156     static Collection<SignatureAndHashAlgorithm>
getSupportedAlgorithms(AlgorithmConstraints constraints)157             getSupportedAlgorithms(AlgorithmConstraints constraints) {
158 
159         Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>();
160         synchronized (priorityMap) {
161             for (SignatureAndHashAlgorithm sigAlg : priorityMap.values()) {
162                 if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM &&
163                         constraints.permits(SIGNATURE_PRIMITIVE_SET,
164                                 sigAlg.algorithm, null)) {
165                     supported.add(sigAlg);
166                 }
167             }
168         }
169 
170         return supported;
171     }
172 
173     // Get supported algorithm collection from an untrusted collection
getSupportedAlgorithms( Collection<SignatureAndHashAlgorithm> algorithms )174     static Collection<SignatureAndHashAlgorithm> getSupportedAlgorithms(
175             Collection<SignatureAndHashAlgorithm> algorithms ) {
176         Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>();
177         for (SignatureAndHashAlgorithm sigAlg : algorithms) {
178             if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM) {
179                 supported.add(sigAlg);
180             }
181         }
182 
183         return supported;
184     }
185 
getAlgorithmNames( Collection<SignatureAndHashAlgorithm> algorithms)186     static String[] getAlgorithmNames(
187             Collection<SignatureAndHashAlgorithm> algorithms) {
188         ArrayList<String> algorithmNames = new ArrayList<>();
189         if (algorithms != null) {
190             for (SignatureAndHashAlgorithm sigAlg : algorithms) {
191                 algorithmNames.add(sigAlg.algorithm);
192             }
193         }
194 
195         String[] array = new String[algorithmNames.size()];
196         return algorithmNames.toArray(array);
197     }
198 
getHashAlgorithmNames( Collection<SignatureAndHashAlgorithm> algorithms)199     static Set<String> getHashAlgorithmNames(
200             Collection<SignatureAndHashAlgorithm> algorithms) {
201         Set<String> algorithmNames = new HashSet<>();
202         if (algorithms != null) {
203             for (SignatureAndHashAlgorithm sigAlg : algorithms) {
204                 if (sigAlg.hash.value > 0) {
205                     algorithmNames.add(sigAlg.hash.standardName);
206                 }
207             }
208         }
209 
210         return algorithmNames;
211     }
212 
getHashAlgorithmName(SignatureAndHashAlgorithm algorithm)213     static String getHashAlgorithmName(SignatureAndHashAlgorithm algorithm) {
214         return algorithm.hash.standardName;
215     }
216 
supports(HashAlgorithm hash, SignatureAlgorithm signature, String algorithm, int priority)217     private static void supports(HashAlgorithm hash,
218             SignatureAlgorithm signature, String algorithm, int priority) {
219 
220         SignatureAndHashAlgorithm pair =
221             new SignatureAndHashAlgorithm(hash, signature, algorithm, priority);
222         if (supportedMap.put(pair.id, pair) != null) {
223             throw new RuntimeException(
224                 "Duplicate SignatureAndHashAlgorithm definition, id: " +
225                 pair.id);
226         }
227         if (priorityMap.put(pair.priority, pair) != null) {
228             throw new RuntimeException(
229                 "Duplicate SignatureAndHashAlgorithm definition, priority: " +
230                 pair.priority);
231         }
232     }
233 
getPreferableAlgorithm( Collection<SignatureAndHashAlgorithm> algorithms, String expected)234     static SignatureAndHashAlgorithm getPreferableAlgorithm(
235         Collection<SignatureAndHashAlgorithm> algorithms, String expected) {
236 
237         return SignatureAndHashAlgorithm.getPreferableAlgorithm(
238                 algorithms, expected, null);
239     }
240 
getPreferableAlgorithm( Collection<SignatureAndHashAlgorithm> algorithms, String expected, PrivateKey signingKey)241     static SignatureAndHashAlgorithm getPreferableAlgorithm(
242         Collection<SignatureAndHashAlgorithm> algorithms,
243         String expected, PrivateKey signingKey) {
244 
245         if (expected == null && !algorithms.isEmpty()) {
246             for (SignatureAndHashAlgorithm sigAlg : algorithms) {
247                 if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM) {
248                     return sigAlg;
249                 }
250             }
251 
252             return null;  // no supported algorithm
253         }
254 
255         if (expected == null ) {
256             return null;  // no expected algorithm, no supported algorithm
257         }
258 
259         /*
260          * Need to check RSA key length to match the length of hash value
261          */
262         int maxDigestLength = Integer.MAX_VALUE;
263         if (signingKey != null &&
264                 "rsa".equalsIgnoreCase(signingKey.getAlgorithm()) &&
265                 expected.equalsIgnoreCase("rsa")) {
266             /*
267              * RSA keys of 512 bits have been shown to be practically
268              * breakable, it does not make much sense to use the strong
269              * hash algorithm for keys whose key size less than 512 bits.
270              * So it is not necessary to caculate the required max digest
271              * length exactly.
272              *
273              * If key size is greater than or equals to 768, there is no max
274              * digest length limitation in currect implementation.
275              *
276              * If key size is greater than or equals to 512, but less than
277              * 768, the digest length should be less than or equal to 32 bytes.
278              *
279              * If key size is less than 512, the  digest length should be
280              * less than or equal to 20 bytes.
281              */
282             int keySize = KeyUtil.getKeySize(signingKey);
283             if (keySize >= 768) {
284                 maxDigestLength = HashAlgorithm.SHA512.length;
285             } else if ((keySize >= 512) && (keySize < 768)) {
286                 maxDigestLength = HashAlgorithm.SHA256.length;
287             } else if ((keySize > 0) && (keySize < 512)) {
288                 maxDigestLength = HashAlgorithm.SHA1.length;
289             }   // Otherwise, cannot determine the key size, prefer the most
290                 // perferable hash algorithm.
291         }
292 
293         for (SignatureAndHashAlgorithm algorithm : algorithms) {
294             int signValue = algorithm.id & 0xFF;
295             if (expected.equalsIgnoreCase("rsa") &&
296                     signValue == SignatureAlgorithm.RSA.value) {
297                 if (algorithm.hash.length <= maxDigestLength) {
298                     return algorithm;
299                 }
300             } else if (
301                     (expected.equalsIgnoreCase("dsa") &&
302                         signValue == SignatureAlgorithm.DSA.value) ||
303                     (expected.equalsIgnoreCase("ecdsa") &&
304                         signValue == SignatureAlgorithm.ECDSA.value) ||
305                     (expected.equalsIgnoreCase("ec") &&
306                         signValue == SignatureAlgorithm.ECDSA.value)) {
307                 return algorithm;
308             }
309         }
310 
311         return null;
312     }
313 
314     static enum HashAlgorithm {
315         UNDEFINED("undefined",        "", -1, -1),
316         NONE(          "none",    "NONE",  0, -1),
317         MD5(            "md5",     "MD5",  1, 16),
318         SHA1(          "sha1",   "SHA-1",  2, 20),
319         SHA224(      "sha224", "SHA-224",  3, 28),
320         SHA256(      "sha256", "SHA-256",  4, 32),
321         SHA384(      "sha384", "SHA-384",  5, 48),
322         SHA512(      "sha512", "SHA-512",  6, 64);
323 
324         final String name;  // not the standard signature algorithm name
325                             // except the UNDEFINED, other names are defined
326                             // by TLS 1.2 protocol
327         final String standardName; // the standard MessageDigest algorithm name
328         final int value;
329         final int length;   // digest length in bytes, -1 means not applicable
330 
HashAlgorithm(String name, String standardName, int value, int length)331         private HashAlgorithm(String name, String standardName,
332                 int value, int length) {
333             this.name = name;
334             this.standardName = standardName;
335             this.value = value;
336             this.length = length;
337         }
338 
valueOf(int value)339         static HashAlgorithm valueOf(int value) {
340             HashAlgorithm algorithm = UNDEFINED;
341             switch (value) {
342                 case 0:
343                     algorithm = NONE;
344                     break;
345                 case 1:
346                     algorithm = MD5;
347                     break;
348                 case 2:
349                     algorithm = SHA1;
350                     break;
351                 case 3:
352                     algorithm = SHA224;
353                     break;
354                 case 4:
355                     algorithm = SHA256;
356                     break;
357                 case 5:
358                     algorithm = SHA384;
359                     break;
360                 case 6:
361                     algorithm = SHA512;
362                     break;
363             }
364 
365             return algorithm;
366         }
367     }
368 
369     static enum SignatureAlgorithm {
370         UNDEFINED("undefined", -1),
371         ANONYMOUS("anonymous",  0),
372         RSA(            "rsa",  1),
373         DSA(            "dsa",  2),
374         ECDSA(        "ecdsa",  3);
375 
376         final String name;  // not the standard signature algorithm name
377                             // except the UNDEFINED, other names are defined
378                             // by TLS 1.2 protocol
379         final int value;
380 
SignatureAlgorithm(String name, int value)381         private SignatureAlgorithm(String name, int value) {
382             this.name = name;
383             this.value = value;
384         }
385 
valueOf(int value)386         static SignatureAlgorithm valueOf(int value) {
387             SignatureAlgorithm algorithm = UNDEFINED;
388             switch (value) {
389                 case 0:
390                     algorithm = ANONYMOUS;
391                     break;
392                 case 1:
393                     algorithm = RSA;
394                     break;
395                 case 2:
396                     algorithm = DSA;
397                     break;
398                 case 3:
399                     algorithm = ECDSA;
400                     break;
401             }
402 
403             return algorithm;
404         }
405     }
406 
407     static {
408         supportedMap = Collections.synchronizedSortedMap(
409             new TreeMap<Integer, SignatureAndHashAlgorithm>());
410         priorityMap = Collections.synchronizedSortedMap(
411             new TreeMap<Integer, SignatureAndHashAlgorithm>());
412 
413         synchronized (supportedMap) {
414             int p = SUPPORTED_ALG_PRIORITY_MAX_NUM;
supports(HashAlgorithm.MD5, SignatureAlgorithm.RSA, "MD5withRSA", --p)415             supports(HashAlgorithm.MD5,         SignatureAlgorithm.RSA,
416                     "MD5withRSA",           --p);
supports(HashAlgorithm.SHA1, SignatureAlgorithm.DSA, "SHA1withDSA", --p)417             supports(HashAlgorithm.SHA1,        SignatureAlgorithm.DSA,
418                     "SHA1withDSA",          --p);
supports(HashAlgorithm.SHA1, SignatureAlgorithm.RSA, "SHA1withRSA", --p)419             supports(HashAlgorithm.SHA1,        SignatureAlgorithm.RSA,
420                     "SHA1withRSA",          --p);
supports(HashAlgorithm.SHA1, SignatureAlgorithm.ECDSA, "SHA1withECDSA", --p)421             supports(HashAlgorithm.SHA1,        SignatureAlgorithm.ECDSA,
422                     "SHA1withECDSA",        --p);
supports(HashAlgorithm.SHA224, SignatureAlgorithm.RSA, "SHA224withRSA", --p)423             supports(HashAlgorithm.SHA224,      SignatureAlgorithm.RSA,
424                     "SHA224withRSA",        --p);
supports(HashAlgorithm.SHA224, SignatureAlgorithm.ECDSA, "SHA224withECDSA", --p)425             supports(HashAlgorithm.SHA224,      SignatureAlgorithm.ECDSA,
426                     "SHA224withECDSA",      --p);
supports(HashAlgorithm.SHA256, SignatureAlgorithm.RSA, "SHA256withRSA", --p)427             supports(HashAlgorithm.SHA256,      SignatureAlgorithm.RSA,
428                     "SHA256withRSA",        --p);
supports(HashAlgorithm.SHA256, SignatureAlgorithm.ECDSA, "SHA256withECDSA", --p)429             supports(HashAlgorithm.SHA256,      SignatureAlgorithm.ECDSA,
430                     "SHA256withECDSA",      --p);
supports(HashAlgorithm.SHA384, SignatureAlgorithm.RSA, "SHA384withRSA", --p)431             supports(HashAlgorithm.SHA384,      SignatureAlgorithm.RSA,
432                     "SHA384withRSA",        --p);
supports(HashAlgorithm.SHA384, SignatureAlgorithm.ECDSA, "SHA384withECDSA", --p)433             supports(HashAlgorithm.SHA384,      SignatureAlgorithm.ECDSA,
434                     "SHA384withECDSA",      --p);
supports(HashAlgorithm.SHA512, SignatureAlgorithm.RSA, "SHA512withRSA", --p)435             supports(HashAlgorithm.SHA512,      SignatureAlgorithm.RSA,
436                     "SHA512withRSA",        --p);
supports(HashAlgorithm.SHA512, SignatureAlgorithm.ECDSA, "SHA512withECDSA", --p)437             supports(HashAlgorithm.SHA512,      SignatureAlgorithm.ECDSA,
438                     "SHA512withECDSA",      --p);
439         }
440     }
441 }
442 
443