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