1 /*
   2  * Copyright (c) 2010, 2011, 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 
  31 import java.util.Set;
  32 import java.util.HashSet;
  33 import java.util.Map;
  34 import java.util.EnumSet;
  35 import java.util.TreeMap;
  36 import java.util.Collection;
  37 import java.util.Collections;
  38 import java.util.ArrayList;
  39 
  40 /**
  41  * Signature and hash algorithm.
  42  *
  43  * [RFC5246] The client uses the "signature_algorithms" extension to
  44  * indicate to the server which signature/hash algorithm pairs may be
  45  * used in digital signatures.  The "extension_data" field of this
  46  * extension contains a "supported_signature_algorithms" value.
  47  *
  48  *     enum {
  49  *         none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
  50  *         sha512(6), (255)
  51  *     } HashAlgorithm;
  52  *
  53  *     enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) }
  54  *       SignatureAlgorithm;
  55  *
  56  *     struct {
  57  *           HashAlgorithm hash;
  58  *           SignatureAlgorithm signature;
  59  *     } SignatureAndHashAlgorithm;
  60  */
  61 final class SignatureAndHashAlgorithm {
  62 
  63     // minimum priority for default enabled algorithms
  64     final static int SUPPORTED_ALG_PRIORITY_MAX_NUM = 0x00F0;
  65 
  66     // performance optimization
  67     private final static Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET =
  68                                     EnumSet.of(CryptoPrimitive.SIGNATURE);
  69 
  70     // supported pairs of signature and hash algorithm
  71     private final static Map<Integer, SignatureAndHashAlgorithm> supportedMap;
  72     private final static Map<Integer, SignatureAndHashAlgorithm> priorityMap;
  73 
  74     // the hash algorithm
  75     private HashAlgorithm hash;
  76 
  77     // the signature algorithm
  78     private SignatureAlgorithm signature;
  79 
  80     // id in 16 bit MSB format, i.e. 0x0603 for SHA512withECDSA
  81     private int id;
  82 
  83     // the standard algorithm name, for example "SHA512withECDSA"
  84     private String algorithm;
  85 
  86     // Priority for the preference order. The lower the better.
  87     //
  88     // If the algorithm is unsupported, its priority should be bigger
  89     // than SUPPORTED_ALG_PRIORITY_MAX_NUM.
  90     private int priority;
  91 
  92     // constructor for supported algorithm
  93     private SignatureAndHashAlgorithm(HashAlgorithm hash,
  94             SignatureAlgorithm signature, String algorithm, int priority) {
  95         this.hash = hash;
  96         this.signature = signature;
  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.signature = SignatureAlgorithm.valueOf(id & 0xFF);
 106         this.algorithm = algorithm;
 107         this.id = id;
 108 
 109         // add one more to the sequece number, in case that the number is zero
 110         this.priority = SUPPORTED_ALG_PRIORITY_MAX_NUM + sequence + 1;
 111     }
 112 
 113     // Note that we do not use the sequence argument for supported algorithms,
 114     // so please don't sort by comparing the objects read from handshake
 115     // messages.
 116     static SignatureAndHashAlgorithm valueOf(int hash,
 117             int signature, int sequence) {
 118         hash &= 0xFF;
 119         signature &= 0xFF;
 120 
 121         int id = (hash << 8) | signature;
 122         SignatureAndHashAlgorithm signAlg = supportedMap.get(id);
 123         if (signAlg == null) {
 124             // unsupported algorithm
 125             signAlg = new SignatureAndHashAlgorithm(
 126                 "Unknown (hash:0x" + Integer.toString(hash, 16) +
 127                 ", signature:0x" + Integer.toString(signature, 16) + ")",
 128                 id, sequence);
 129         }
 130 
 131         return signAlg;
 132     }
 133 
 134     int getHashValue() {
 135         return (id >> 8) & 0xFF;
 136     }
 137 
 138     int getSignatureValue() {
 139         return id & 0xFF;
 140     }
 141 
 142     String getAlgorithmName() {
 143         return algorithm;
 144     }
 145 
 146     // return the size of a SignatureAndHashAlgorithm structure in TLS record
 147     static int sizeInRecord() {
 148         return 2;
 149     }
 150 
 151     // Get local supported algorithm collection complying to
 152     // algorithm constraints
 153     static Collection<SignatureAndHashAlgorithm>
 154             getSupportedAlgorithms(AlgorithmConstraints constraints) {
 155 
 156         Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>();
 157         synchronized (priorityMap) {
 158             for (SignatureAndHashAlgorithm sigAlg : priorityMap.values()) {
 159                 if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM &&
 160                         constraints.permits(SIGNATURE_PRIMITIVE_SET,
 161                                 sigAlg.algorithm, null)) {
 162                     supported.add(sigAlg);
 163                 }
 164             }
 165         }
 166 
 167         return supported;
 168     }
 169 
 170     // Get supported algorithm collection from an untrusted collection
 171     static Collection<SignatureAndHashAlgorithm> getSupportedAlgorithms(
 172             Collection<SignatureAndHashAlgorithm> algorithms ) {
 173         Collection<SignatureAndHashAlgorithm> supported = new ArrayList<>();
 174         for (SignatureAndHashAlgorithm sigAlg : algorithms) {
 175             if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM) {
 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         if (expected == null && !algorithms.isEmpty()) {
 235             for (SignatureAndHashAlgorithm sigAlg : algorithms) {
 236                 if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM) {
 237                     return sigAlg;
 238                 }
 239             }
 240 
 241             return null;  // no supported algorithm
 242         }
 243 
 244 
 245         for (SignatureAndHashAlgorithm algorithm : algorithms) {
 246             int signValue = algorithm.id & 0xFF;
 247             if ((expected.equalsIgnoreCase("dsa") &&
 248                     signValue == SignatureAlgorithm.DSA.value) ||
 249                 (expected.equalsIgnoreCase("rsa") &&
 250                     signValue == SignatureAlgorithm.RSA.value) ||
 251                 (expected.equalsIgnoreCase("ecdsa") &&
 252                     signValue == SignatureAlgorithm.ECDSA.value) ||
 253                 (expected.equalsIgnoreCase("ec") &&
 254                     signValue == SignatureAlgorithm.ECDSA.value)) {
 255                 return algorithm;
 256             }
 257         }
 258 
 259         return null;
 260     }
 261 
 262     static enum HashAlgorithm {
 263         UNDEFINED("undefined",        "", -1),
 264         NONE(          "none",    "NONE",  0),
 265         MD5(            "md5",     "MD5",  1),
 266         SHA1(          "sha1",   "SHA-1",  2),
 267         SHA224(      "sha224", "SHA-224",  3),
 268         SHA256(      "sha256", "SHA-256",  4),
 269         SHA384(      "sha384", "SHA-384",  5),
 270         SHA512(      "sha512", "SHA-512",  6);
 271 
 272         final String name;  // not the standard signature algorithm name
 273                             // except the UNDEFINED, other names are defined
 274                             // by TLS 1.2 protocol
 275         final String standardName; // the standard MessageDigest algorithm name
 276         final int value;
 277 
 278         private HashAlgorithm(String name, String standardName, int value) {
 279             this.name = name;
 280             this.standardName = standardName;
 281             this.value = value;
 282         }
 283 
 284         static HashAlgorithm valueOf(int value) {
 285             HashAlgorithm algorithm = UNDEFINED;
 286             switch (value) {
 287                 case 0:
 288                     algorithm = NONE;
 289                     break;
 290                 case 1:
 291                     algorithm = MD5;
 292                     break;
 293                 case 2:
 294                     algorithm = SHA1;
 295                     break;
 296                 case 3:
 297                     algorithm = SHA224;
 298                     break;
 299                 case 4:
 300                     algorithm = SHA256;
 301                     break;
 302                 case 5:
 303                     algorithm = SHA384;
 304                     break;
 305                 case 6:
 306                     algorithm = SHA512;
 307                     break;
 308             }
 309 
 310             return algorithm;
 311         }
 312     }
 313 
 314     static enum SignatureAlgorithm {
 315         UNDEFINED("undefined", -1),
 316         ANONYMOUS("anonymous",  0),
 317         RSA(            "rsa",  1),
 318         DSA(            "dsa",  2),
 319         ECDSA(        "ecdsa",  3);
 320 
 321         final String name;  // not the standard signature algorithm name
 322                             // except the UNDEFINED, other names are defined
 323                             // by TLS 1.2 protocol
 324         final int value;
 325 
 326         private SignatureAlgorithm(String name, int value) {
 327             this.name = name;
 328             this.value = value;
 329         }
 330 
 331         static SignatureAlgorithm valueOf(int value) {
 332             SignatureAlgorithm algorithm = UNDEFINED;
 333             switch (value) {
 334                 case 0:
 335                     algorithm = ANONYMOUS;
 336                     break;
 337                 case 1:
 338                     algorithm = RSA;
 339                     break;
 340                 case 2:
 341                     algorithm = DSA;
 342                     break;
 343                 case 3:
 344                     algorithm = ECDSA;
 345                     break;
 346             }
 347 
 348             return algorithm;
 349         }
 350     }
 351 
 352     static {
 353         supportedMap = Collections.synchronizedSortedMap(
 354             new TreeMap<Integer, SignatureAndHashAlgorithm>());
 355         priorityMap = Collections.synchronizedSortedMap(
 356             new TreeMap<Integer, SignatureAndHashAlgorithm>());
 357 
 358         synchronized (supportedMap) {
 359             int p = SUPPORTED_ALG_PRIORITY_MAX_NUM;
 360             supports(HashAlgorithm.MD5,         SignatureAlgorithm.RSA,
 361                     "MD5withRSA",           --p);
 362             supports(HashAlgorithm.SHA1,        SignatureAlgorithm.DSA,
 363                     "SHA1withDSA",          --p);
 364             supports(HashAlgorithm.SHA1,        SignatureAlgorithm.RSA,
 365                     "SHA1withRSA",          --p);
 366             supports(HashAlgorithm.SHA1,        SignatureAlgorithm.ECDSA,
 367                     "SHA1withECDSA",        --p);
 368             supports(HashAlgorithm.SHA224,      SignatureAlgorithm.RSA,
 369                     "SHA224withRSA",        --p);
 370             supports(HashAlgorithm.SHA224,      SignatureAlgorithm.ECDSA,
 371                     "SHA224withECDSA",      --p);
 372             supports(HashAlgorithm.SHA256,      SignatureAlgorithm.RSA,
 373                     "SHA256withRSA",        --p);
 374             supports(HashAlgorithm.SHA256,      SignatureAlgorithm.ECDSA,
 375                     "SHA256withECDSA",      --p);
 376             supports(HashAlgorithm.SHA384,      SignatureAlgorithm.RSA,
 377                     "SHA384withRSA",        --p);
 378             supports(HashAlgorithm.SHA384,      SignatureAlgorithm.ECDSA,
 379                     "SHA384withECDSA",      --p);
 380             supports(HashAlgorithm.SHA512,      SignatureAlgorithm.RSA,
 381                     "SHA512withRSA",        --p);
 382             supports(HashAlgorithm.SHA512,      SignatureAlgorithm.ECDSA,
 383                     "SHA512withECDSA",      --p);
 384         }
 385     }
 386 }
 387