< prev index next >

src/share/classes/sun/security/ssl/CipherSuite.java

Print this page
rev 11548 : 8133070: Hot lock on BulkCipher.isAvailable
Reviewed-by: mullan
Contributed-by: xuelei.fan@oracle.com, kungu.mjh@alibaba-inc.com


  58  *    as well). [A minor exception are *unsupported* CipherSuites read from a
  59  *    handshake message, but this is usually irrelevant]
  60  *
  61  *  . instances are obtained using the static valueOf() factory methods.
  62  *
  63  *  . properties are defined as final variables and made available as
  64  *    package private variables without method accessors
  65  *
  66  *  . if the member variable allowed is false, the given algorithm is either
  67  *    unavailable or disabled at compile time
  68  *
  69  */
  70 final class CipherSuite implements Comparable<CipherSuite> {
  71 
  72     // minimum priority for supported CipherSuites
  73     final static int SUPPORTED_SUITES_PRIORITY = 1;
  74 
  75     // minimum priority for default enabled CipherSuites
  76     final static int DEFAULT_SUITES_PRIORITY = 300;
  77 
  78     // Flag indicating if CipherSuite availability can change dynamically.
  79     // This is the case when we rely on a JCE cipher implementation that
  80     // may not be available in the installed JCE providers.
  81     // It is true because we might not have an ECC implementation.
  82     final static boolean DYNAMIC_AVAILABILITY = true;
  83 
  84     private final static boolean ALLOW_ECC = Debug.getBooleanProperty
  85         ("com.sun.net.ssl.enableECC", true);
  86 
  87     // Map Integer(id) -> CipherSuite
  88     // contains all known CipherSuites
  89     private final static Map<Integer,CipherSuite> idMap;
  90 
  91     // Map String(name) -> CipherSuite
  92     // contains only supported CipherSuites (i.e. allowed == true)
  93     private final static Map<String,CipherSuite> nameMap;
  94 
  95     // Protocol defined CipherSuite name, e.g. SSL_RSA_WITH_RC4_128_MD5
  96     // we use TLS_* only for new CipherSuites, still SSL_* for old ones
  97     final String name;
  98 
  99     // id in 16 bit MSB format, i.e. 0x0004 for SSL_RSA_WITH_RC4_128_MD5
 100     final int id;
 101 
 102     // priority for the internal default preference order. the higher the
 103     // better. Each supported CipherSuite *must* have a unique priority.


 169      */
 170     private CipherSuite(String name, int id) {
 171         this.name = name;
 172         this.id = id;
 173         this.allowed = false;
 174 
 175         this.priority = 0;
 176         this.keyExchange = null;
 177         this.cipher = null;
 178         this.macAlg = null;
 179         this.exportable = false;
 180         this.obsoleted = ProtocolVersion.LIMIT_MAX_VALUE;
 181         this.supported = ProtocolVersion.LIMIT_MIN_VALUE;
 182         this.prfAlg = P_NONE;
 183     }
 184 
 185     /**
 186      * Return whether this CipherSuite is available for use. A
 187      * CipherSuite may be unavailable even if it is supported
 188      * (i.e. allowed == true) if the required JCE cipher is not installed.
 189      * In some configuration, this situation may change over time, call
 190      * CipherSuiteList.clearAvailableCache() before this method to obtain
 191      * the most current status.
 192      */
 193     boolean isAvailable() {
 194         return allowed && keyExchange.isAvailable() && cipher.isAvailable();
 195     }
 196 
 197     boolean isNegotiable() {
 198         return this != C_SCSV && isAvailable();
 199     }
 200 
 201     /**
 202      * Compares CipherSuites based on their priority. Has the effect of
 203      * sorting CipherSuites when put in a sorted collection, which is
 204      * used by CipherSuiteList. Follows standard Comparable contract.
 205      *
 206      * Note that for unsupported CipherSuites parsed from a handshake
 207      * message we violate the equals() contract.
 208      */
 209     @Override
 210     public int compareTo(CipherSuite o) {
 211         return o.priority - priority;


 387         public String toString() {
 388             return name;
 389         }
 390     }
 391 
 392     static enum CipherType {
 393         STREAM_CIPHER,         // null or stream cipher
 394         BLOCK_CIPHER,          // block cipher in CBC mode
 395         AEAD_CIPHER            // AEAD cipher
 396     }
 397 
 398     /**
 399      * An SSL/TLS bulk cipher algorithm. One instance per combination of
 400      * cipher and key length.
 401      *
 402      * Also contains a factory method to obtain in initialized CipherBox
 403      * for this algorithm.
 404      */
 405     final static class BulkCipher {
 406 
 407         // Map BulkCipher -> Boolean(available)
 408         private final static Map<BulkCipher,Boolean> availableCache =
 409                                             new HashMap<>(8);
 410 
 411         // descriptive name including key size, e.g. AES/128
 412         final String description;
 413 
 414         // JCE cipher transformation string, e.g. AES/CBC/NoPadding
 415         final String transformation;
 416 
 417         // algorithm name, e.g. AES
 418         final String algorithm;
 419 
 420         // supported and compile time enabled. Also see isAvailable()
 421         final boolean allowed;
 422 
 423         // number of bytes of entropy in the key
 424         final int keySize;
 425 
 426         // length of the actual cipher key in bytes.
 427         // for non-exportable ciphers, this is the same as keySize
 428         final int expandedKeySize;
 429 
 430         // size of the IV


 434         //
 435         // record_iv_length = ivSize - fixedIvSize
 436         final int fixedIvSize;
 437 
 438         // exportable under 512/40 bit rules
 439         final boolean exportable;
 440 
 441         // Is the cipher algorithm of Cipher Block Chaining (CBC) mode?
 442         final CipherType cipherType;
 443 
 444         // size of the authentication tag, only applicable to cipher suites in
 445         // Galois Counter Mode (GCM)
 446         //
 447         // As far as we know, all supported GCM cipher suites use 128-bits
 448         // authentication tags.
 449         final int tagSize = 16;
 450 
 451         // The secure random used to detect the cipher availability.
 452         private final static SecureRandom secureRandom;
 453 



 454         static {
 455             try {
 456                 secureRandom = JsseJce.getSecureRandom();
 457             } catch (KeyManagementException kme) {
 458                 throw new RuntimeException(kme);
 459             }
 460         }
 461 
 462         BulkCipher(String transformation, CipherType cipherType, int keySize,
 463                 int expandedKeySize, int ivSize,
 464                 int fixedIvSize, boolean allowed) {
 465 
 466             this.transformation = transformation;
 467             String[] splits = transformation.split("/");
 468             this.algorithm = splits[0];
 469             this.cipherType = cipherType;
 470             this.description = this.algorithm + "/" + (keySize << 3);
 471             this.keySize = keySize;
 472             this.ivSize = ivSize;
 473             this.fixedIvSize = fixedIvSize;
 474             this.allowed = allowed;
 475 
 476             this.expandedKeySize = expandedKeySize;
 477             this.exportable = true;











 478         }
 479 
 480         BulkCipher(String transformation, CipherType cipherType, int keySize,
 481                 int ivSize, int fixedIvSize, boolean allowed) {
 482             this.transformation = transformation;
 483             String[] splits = transformation.split("/");
 484             this.algorithm = splits[0];
 485             this.cipherType = cipherType;
 486             this.description = this.algorithm + "/" + (keySize << 3);
 487             this.keySize = keySize;
 488             this.ivSize = ivSize;
 489             this.fixedIvSize = fixedIvSize;
 490             this.allowed = allowed;
 491 
 492             this.expandedKeySize = keySize;
 493             this.exportable = false;











 494         }
 495 
 496         /**
 497          * Return an initialized CipherBox for this BulkCipher.
 498          * IV must be null for stream ciphers.
 499          *
 500          * @exception NoSuchAlgorithmException if anything goes wrong
 501          */
 502         CipherBox newCipher(ProtocolVersion version, SecretKey key,
 503                 IvParameterSpec iv, SecureRandom random,
 504                 boolean encrypt) throws NoSuchAlgorithmException {
 505             return CipherBox.newCipherBox(version, this,
 506                                             key, iv, random, encrypt);
 507         }
 508 
 509         /**
 510          * Test if this bulk cipher is available. For use by CipherSuite.
 511          *
 512          * Currently all supported ciphers except AES are always available
 513          * via the JSSE internal implementations. We also assume AES/128 of
 514          * CBC mode is always available since it is shipped with the SunJCE
 515          * provider.  However, AES/256 is unavailable when the default JCE
 516          * policy jurisdiction files are installed because of key length
 517          * restrictions, and AEAD is unavailable when the underlying providers
 518          * do not support AEAD/GCM mode.
 519          */
 520         boolean isAvailable() {
 521             if (allowed == false) {
 522                 return false;
 523             }
 524 
 525             if ((this == B_AES_256) ||
 526                     (this.cipherType == CipherType.AEAD_CIPHER)) {
 527                 return isAvailable(this);
 528             }
 529 
 530             // always available
 531             return true;
 532         }
 533 
 534         // for use by CipherSuiteList.clearAvailableCache();
 535         static synchronized void clearAvailableCache() {
 536             if (DYNAMIC_AVAILABILITY) {
 537                 availableCache.clear();
 538             }
 539         }
 540 
 541         private static synchronized boolean isAvailable(BulkCipher cipher) {
 542             Boolean b = availableCache.get(cipher);
 543             if (b == null) {
 544                 int keySizeInBits = cipher.keySize * 8;
 545                 if (keySizeInBits > 128) {    // need the JCE unlimited
 546                                                // strength jurisdiction policy
 547                     try {
 548                         if (Cipher.getMaxAllowedKeyLength(
 549                                 cipher.transformation) < keySizeInBits) {
 550                             b = Boolean.FALSE;
 551                         }
 552                     } catch (Exception e) {
 553                         b = Boolean.FALSE;
 554                     }
 555                 }
 556 
 557                 if (b == null) {
 558                     b = Boolean.FALSE;          // may be reset to TRUE if
 559                                                 // the cipher is available
 560                     CipherBox temporary = null;
 561                     try {
 562                         SecretKey key = new SecretKeySpec(
 563                                             new byte[cipher.expandedKeySize],
 564                                             cipher.algorithm);
 565                         IvParameterSpec iv;
 566                         if (cipher.cipherType == CipherType.AEAD_CIPHER) {
 567                             iv = new IvParameterSpec(
 568                                             new byte[cipher.fixedIvSize]);
 569                         } else {
 570                             iv = new IvParameterSpec(new byte[cipher.ivSize]);
 571                         }
 572                         temporary = cipher.newCipher(
 573                                             ProtocolVersion.DEFAULT,
 574                                             key, iv, secureRandom, true);
 575                         b = temporary.isAvailable();
 576                     } catch (NoSuchAlgorithmException e) {
 577                         // not available
 578                     } finally {
 579                         if (temporary != null) {
 580                             temporary.dispose();
 581                         }
 582                     }


 583                 }
 584 
 585                 availableCache.put(cipher, b);
 586             }
 587 
 588             return b.booleanValue();
 589         }
 590 
 591         @Override
 592         public String toString() {
 593             return description;
 594         }
 595     }
 596 
 597     /**
 598      * An SSL/TLS key MAC algorithm.
 599      *
 600      * Also contains a factory method to obtain an initialized MAC
 601      * for this algorithm.
 602      */
 603     final static class MacAlg {
 604 
 605         // descriptive name, e.g. MD5
 606         final String name;
 607 
 608         // size of the MAC value (and MAC key) in bytes




  58  *    as well). [A minor exception are *unsupported* CipherSuites read from a
  59  *    handshake message, but this is usually irrelevant]
  60  *
  61  *  . instances are obtained using the static valueOf() factory methods.
  62  *
  63  *  . properties are defined as final variables and made available as
  64  *    package private variables without method accessors
  65  *
  66  *  . if the member variable allowed is false, the given algorithm is either
  67  *    unavailable or disabled at compile time
  68  *
  69  */
  70 final class CipherSuite implements Comparable<CipherSuite> {
  71 
  72     // minimum priority for supported CipherSuites
  73     final static int SUPPORTED_SUITES_PRIORITY = 1;
  74 
  75     // minimum priority for default enabled CipherSuites
  76     final static int DEFAULT_SUITES_PRIORITY = 300;
  77 






  78     private final static boolean ALLOW_ECC = Debug.getBooleanProperty
  79         ("com.sun.net.ssl.enableECC", true);
  80 
  81     // Map Integer(id) -> CipherSuite
  82     // contains all known CipherSuites
  83     private final static Map<Integer,CipherSuite> idMap;
  84 
  85     // Map String(name) -> CipherSuite
  86     // contains only supported CipherSuites (i.e. allowed == true)
  87     private final static Map<String,CipherSuite> nameMap;
  88 
  89     // Protocol defined CipherSuite name, e.g. SSL_RSA_WITH_RC4_128_MD5
  90     // we use TLS_* only for new CipherSuites, still SSL_* for old ones
  91     final String name;
  92 
  93     // id in 16 bit MSB format, i.e. 0x0004 for SSL_RSA_WITH_RC4_128_MD5
  94     final int id;
  95 
  96     // priority for the internal default preference order. the higher the
  97     // better. Each supported CipherSuite *must* have a unique priority.


 163      */
 164     private CipherSuite(String name, int id) {
 165         this.name = name;
 166         this.id = id;
 167         this.allowed = false;
 168 
 169         this.priority = 0;
 170         this.keyExchange = null;
 171         this.cipher = null;
 172         this.macAlg = null;
 173         this.exportable = false;
 174         this.obsoleted = ProtocolVersion.LIMIT_MAX_VALUE;
 175         this.supported = ProtocolVersion.LIMIT_MIN_VALUE;
 176         this.prfAlg = P_NONE;
 177     }
 178 
 179     /**
 180      * Return whether this CipherSuite is available for use. A
 181      * CipherSuite may be unavailable even if it is supported
 182      * (i.e. allowed == true) if the required JCE cipher is not installed.



 183      */
 184     boolean isAvailable() {
 185         return allowed && keyExchange.isAvailable() && cipher.isAvailable();
 186     }
 187 
 188     boolean isNegotiable() {
 189         return this != C_SCSV && isAvailable();
 190     }
 191 
 192     /**
 193      * Compares CipherSuites based on their priority. Has the effect of
 194      * sorting CipherSuites when put in a sorted collection, which is
 195      * used by CipherSuiteList. Follows standard Comparable contract.
 196      *
 197      * Note that for unsupported CipherSuites parsed from a handshake
 198      * message we violate the equals() contract.
 199      */
 200     @Override
 201     public int compareTo(CipherSuite o) {
 202         return o.priority - priority;


 378         public String toString() {
 379             return name;
 380         }
 381     }
 382 
 383     static enum CipherType {
 384         STREAM_CIPHER,         // null or stream cipher
 385         BLOCK_CIPHER,          // block cipher in CBC mode
 386         AEAD_CIPHER            // AEAD cipher
 387     }
 388 
 389     /**
 390      * An SSL/TLS bulk cipher algorithm. One instance per combination of
 391      * cipher and key length.
 392      *
 393      * Also contains a factory method to obtain in initialized CipherBox
 394      * for this algorithm.
 395      */
 396     final static class BulkCipher {
 397 




 398         // descriptive name including key size, e.g. AES/128
 399         final String description;
 400 
 401         // JCE cipher transformation string, e.g. AES/CBC/NoPadding
 402         final String transformation;
 403 
 404         // algorithm name, e.g. AES
 405         final String algorithm;
 406 
 407         // supported and compile time enabled. Also see isAvailable()
 408         final boolean allowed;
 409 
 410         // number of bytes of entropy in the key
 411         final int keySize;
 412 
 413         // length of the actual cipher key in bytes.
 414         // for non-exportable ciphers, this is the same as keySize
 415         final int expandedKeySize;
 416 
 417         // size of the IV


 421         //
 422         // record_iv_length = ivSize - fixedIvSize
 423         final int fixedIvSize;
 424 
 425         // exportable under 512/40 bit rules
 426         final boolean exportable;
 427 
 428         // Is the cipher algorithm of Cipher Block Chaining (CBC) mode?
 429         final CipherType cipherType;
 430 
 431         // size of the authentication tag, only applicable to cipher suites in
 432         // Galois Counter Mode (GCM)
 433         //
 434         // As far as we know, all supported GCM cipher suites use 128-bits
 435         // authentication tags.
 436         final int tagSize = 16;
 437 
 438         // The secure random used to detect the cipher availability.
 439         private final static SecureRandom secureRandom;
 440 
 441         // runtime availability
 442         private final boolean isAvailable;
 443 
 444         static {
 445             try {
 446                 secureRandom = JsseJce.getSecureRandom();
 447             } catch (KeyManagementException kme) {
 448                 throw new RuntimeException(kme);
 449             }
 450         }
 451 
 452         BulkCipher(String transformation, CipherType cipherType, int keySize,
 453                 int expandedKeySize, int ivSize,
 454                 int fixedIvSize, boolean allowed) {
 455 
 456             this.transformation = transformation;
 457             String[] splits = transformation.split("/");
 458             this.algorithm = splits[0];
 459             this.cipherType = cipherType;
 460             this.description = this.algorithm + "/" + (keySize << 3);
 461             this.keySize = keySize;
 462             this.ivSize = ivSize;
 463             this.fixedIvSize = fixedIvSize;
 464             this.allowed = allowed;
 465 
 466             this.expandedKeySize = expandedKeySize;
 467             this.exportable = true;
 468 
 469             // availability of this bulk cipher
 470             //
 471             // Currently all supported ciphers except AES are always available
 472             // via the JSSE internal implementations. We also assume AES/128 of
 473             // CBC mode is always available since it is shipped with the SunJCE
 474             // provider.  However, AES/256 is unavailable when the default JCE
 475             // policy jurisdiction files are installed because of key length
 476             // restrictions.
 477             this.isAvailable =
 478                     allowed ? isUnlimited(keySize, transformation) : false;
 479         }
 480 
 481         BulkCipher(String transformation, CipherType cipherType, int keySize,
 482                 int ivSize, int fixedIvSize, boolean allowed) {
 483             this.transformation = transformation;
 484             String[] splits = transformation.split("/");
 485             this.algorithm = splits[0];
 486             this.cipherType = cipherType;
 487             this.description = this.algorithm + "/" + (keySize << 3);
 488             this.keySize = keySize;
 489             this.ivSize = ivSize;
 490             this.fixedIvSize = fixedIvSize;
 491             this.allowed = allowed;
 492 
 493             this.expandedKeySize = keySize;
 494             this.exportable = false;
 495 
 496             // availability of this bulk cipher
 497             //
 498             // Currently all supported ciphers except AES are always available
 499             // via the JSSE internal implementations. We also assume AES/128 of
 500             // CBC mode is always available since it is shipped with the SunJCE
 501             // provider.  However, AES/256 is unavailable when the default JCE
 502             // policy jurisdiction files are installed because of key length
 503             // restrictions.
 504             this.isAvailable =
 505                     allowed ? isUnlimited(keySize, transformation) : false;
 506         }
 507 
 508         /**
 509          * Return an initialized CipherBox for this BulkCipher.
 510          * IV must be null for stream ciphers.
 511          *
 512          * @exception NoSuchAlgorithmException if anything goes wrong
 513          */
 514         CipherBox newCipher(ProtocolVersion version, SecretKey key,
 515                 IvParameterSpec iv, SecureRandom random,
 516                 boolean encrypt) throws NoSuchAlgorithmException {
 517             return CipherBox.newCipherBox(version, this,
 518                                             key, iv, random, encrypt);
 519         }
 520 
 521         /**
 522          * Test if this bulk cipher is available. For use by CipherSuite.








 523          */
 524         boolean isAvailable() {
 525             return this.isAvailable;










 526         }
 527 
 528         private static boolean isUnlimited(int keySize, String transformation) {
 529             int keySizeInBits = keySize * 8;









 530             if (keySizeInBits > 128) {    // need the JCE unlimited
 531                                           // strength jurisdiction policy
 532                 try {
 533                     if (Cipher.getMaxAllowedKeyLength(
 534                             transformation) < keySizeInBits) {






 535 
 536                         return false;
























 537                     }
 538                 } catch (Exception e) {
 539                     return false;
 540                 }


 541             }
 542 
 543             return true;
 544         }
 545 
 546         @Override
 547         public String toString() {
 548             return description;
 549         }
 550     }
 551 
 552     /**
 553      * An SSL/TLS key MAC algorithm.
 554      *
 555      * Also contains a factory method to obtain an initialized MAC
 556      * for this algorithm.
 557      */
 558     final static class MacAlg {
 559 
 560         // descriptive name, e.g. MD5
 561         final String name;
 562 
 563         // size of the MAC value (and MAC key) in bytes


< prev index next >