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.KeyLength;
  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
  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
 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.
 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 
 137     int getHashValue() {
 138         return (id >> 8) & 0xFF;
 139     }
 140 
 141     int getSignatureValue() {
 142         return id & 0xFF;
 143     }
 144 
 145     String getAlgorithmName() {
 146         return algorithm;
 147     }
 148 
 149     // return the size of a SignatureAndHashAlgorithm structure in TLS record
 150     static int sizeInRecord() {
 151         return 2;
 152     }
 153 
 154     // Get local supported algorithm collection complying to
 155     // algorithm constraints
 156     static Collection<SignatureAndHashAlgorithm>
 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
 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 
 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 
 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 
 213     static String getHashAlgorithmName(SignatureAndHashAlgorithm algorithm) {
 214         return algorithm.hash.standardName;
 215     }
 216 
 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 
 234     static SignatureAndHashAlgorithm getPreferableAlgorithm(
 235         Collection<SignatureAndHashAlgorithm> algorithms, String expected) {
 236 
 237         return SignatureAndHashAlgorithm.getPreferableAlgorithm(
 238                 algorithms, expected, null);
 239     }
 240 
 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 = KeyLength.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 
 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 
 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 
 381         private SignatureAlgorithm(String name, int value) {
 382             this.name = name;
 383             this.value = value;
 384         }
 385 
 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;
 415             supports(HashAlgorithm.MD5,         SignatureAlgorithm.RSA,
 416                     "MD5withRSA",           --p);
 417             supports(HashAlgorithm.SHA1,        SignatureAlgorithm.DSA,
 418                     "SHA1withDSA",          --p);
 419             supports(HashAlgorithm.SHA1,        SignatureAlgorithm.RSA,
 420                     "SHA1withRSA",          --p);
 421             supports(HashAlgorithm.SHA1,        SignatureAlgorithm.ECDSA,
 422                     "SHA1withECDSA",        --p);
 423             supports(HashAlgorithm.SHA224,      SignatureAlgorithm.RSA,
 424                     "SHA224withRSA",        --p);
 425             supports(HashAlgorithm.SHA224,      SignatureAlgorithm.ECDSA,
 426                     "SHA224withECDSA",      --p);
 427             supports(HashAlgorithm.SHA256,      SignatureAlgorithm.RSA,
 428                     "SHA256withRSA",        --p);
 429             supports(HashAlgorithm.SHA256,      SignatureAlgorithm.ECDSA,
 430                     "SHA256withECDSA",      --p);
 431             supports(HashAlgorithm.SHA384,      SignatureAlgorithm.RSA,
 432                     "SHA384withRSA",        --p);
 433             supports(HashAlgorithm.SHA384,      SignatureAlgorithm.ECDSA,
 434                     "SHA384withECDSA",      --p);
 435             supports(HashAlgorithm.SHA512,      SignatureAlgorithm.RSA,
 436                     "SHA512withRSA",        --p);
 437             supports(HashAlgorithm.SHA512,      SignatureAlgorithm.ECDSA,
 438                     "SHA512withECDSA",      --p);
 439         }
 440     }
 441 }
 442