//********************************************************* // // Copyright (c) Microsoft. All rights reserved. // This code is licensed under the Apache License Version 2.0. // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. // //********************************************************* package com.microsoft.uprove; import java.math.BigInteger; import java.security.AccessController; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivilegedAction; import java.security.Security; import com.microsoft.uprove.UProveSDKPermission; /* * LOW-LEVEL IMPLEMENTATION CLASS. NOT PART OF PUBLIC API. */ /** * Implementation for SDK configuration options. * <p>For more detail regarding this class's strategy for discovering defaults * and allowing SDK users to get and set configuration options, see * {@link com.microsoft.uprove.Config}.</p> */ final class ConfigImpl { // option names for which we test when we allow outside code to get/set // config options private static final String OPTION_SECURERANDOM_ALGORITHM = "securerandom.algorithm"; private static final String OPTION_SECURERANDOM_PROVIDER = "securerandom.provider"; private static final String OPTION_MESSAGEDIGEST_PROVIDER = "messagedigest.provider"; private static final String OPTION_MATH_PRIMECONFIDENCELEVEL = "math.primeconfidencelevel"; // the base for all security properties we use private static final String SECURITY_PROPERTY_BASE = "com.microsoft.uprove."; // property names we use when calling java.security.Security.getProperty private static final String PROPERTY_SECURERANDOM_ALGORITHM = SECURITY_PROPERTY_BASE + OPTION_SECURERANDOM_ALGORITHM; private static final String PROPERTY_SECURERANDOM_PROVIDER = SECURITY_PROPERTY_BASE + OPTION_SECURERANDOM_PROVIDER; private static final String PROPERTY_MESSAGEDIGEST_PROVIDER = SECURITY_PROPERTY_BASE + OPTION_MESSAGEDIGEST_PROVIDER; private static final String PROPERTY_MATH_PRIMECONFIDENCELEVEL = SECURITY_PROPERTY_BASE + OPTION_MATH_PRIMECONFIDENCELEVEL; // prefixes for permission checks private static final String PREFIX_GET_OPTION = "getOption."; private static final String PREFIX_SET_OPTION = "setOption."; private static final String DEFAULT_SECURE_RANDOM_ALGORITHM = "SHA1PRNG"; /** * We choose 100 as the default, because that's what Java 1.4 chooses for * the default confidence for * {@link BigInteger#probablePrime(int, java.util.Random)}. */ private static final int DEFAULT_PRIME_CONFIDENCE_LEVEL = 100; // configuration settings // note: we create a new string so that our "unset" value is distinct from // any value that we'll possibly get from the User. if we simply set // OPTION_UNSET to "option unset", then we'd end up with an interned // String that would be reference-equal to an "option unset" String // given to us by a User. private static final String OPTION_UNSET = new String("option unset"); private static final int LEVEL_UNSET = -1; private static String secureRandomAlgorithm = OPTION_UNSET; private static String secureRandomProvider = OPTION_UNSET; private static String messageDigestProvider = OPTION_UNSET; private static int primeConfidenceLevel = LEVEL_UNSET; /** * Private constructor to prevent instantiation or subclassing. */ private ConfigImpl() { super(); } /** * Gets the default for an option, as specified in * java.security.Security's property set. * @param propertyName the name of the property holding our option. * @return the default setting, or <code>null</code> if either none * is set or we don't have permission to read the requested property. */ @SuppressWarnings("unchecked") private static String getDefault(final String propertyName) { try { // permission java.security.SecurityPermission // "getProperty.{propertyName}"; return (String) AccessController.doPrivileged( new PrivilegedAction() { public Object run() { return Security.getProperty(propertyName); } }); } catch (SecurityException se) { // we don't have the required permission, so just... return null; } } /** * Returns the configured secure random algorithm, selecting the default * if none is configured. * @return the configured secure random algorithm. */ static synchronized String secureRandomAlgorithm() { if (secureRandomAlgorithm == OPTION_UNSET) { secureRandomAlgorithm = getDefault(PROPERTY_SECURERANDOM_ALGORITHM); // if there's none set or it's empty, use our default if (secureRandomAlgorithm == null || secureRandomAlgorithm.length() == 0) { secureRandomAlgorithm = DEFAULT_SECURE_RANDOM_ALGORITHM; } } return secureRandomAlgorithm; } /** * Returns the name of the * {@link java.security.SecureRandom SecureRandom} algorithm used by the * SDK. * @return a <code>SecureRandom</code> algorithm name. * @throws SecurityException if a security manager exists and its * {@link SecurityManager#checkPermission(java.security.Permission)} * method denies access to retrieve this configuration option's value. */ public static String getSecureRandomAlgorithm() throws SecurityException { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new UProveSDKPermission(PREFIX_GET_OPTION + OPTION_SECURERANDOM_ALGORITHM)); } return secureRandomAlgorithm(); } /** * Sets the name of the * {@link java.security.SecureRandom SecureRandom} algorithm used by the * SDK. * @param algorithm a <code>SecureRandom</code> algorithm name, or * <code>null</code> to select the default according to the site-wide * default. * @throws SecurityException if a security manager exists and its * {@link SecurityManager#checkPermission(java.security.Permission)} * method denies access to set this configuration option's value. */ public static void setSecureRandomAlgorithm(final String algorithm) { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new UProveSDKPermission(PREFIX_SET_OPTION + OPTION_SECURERANDOM_ALGORITHM)); } synchronized (ConfigImpl.class) { secureRandomAlgorithm = algorithm != null && algorithm.length() != 0 ? algorithm : OPTION_UNSET; RandomSourceImpl.reset(); } } /** * Returns the configured secure random provider. * @return the configured secure random provider. */ static synchronized String secureRandomProvider() { if (secureRandomProvider == OPTION_UNSET) { secureRandomProvider = getDefault(PROPERTY_SECURERANDOM_PROVIDER); // if we got the empty string, go with null, meaning that we'll // use the first provider based on the site's java.security config if (secureRandomProvider != null && secureRandomProvider.length() == 0) { secureRandomProvider = null; } } return secureRandomProvider; } /** * Returns the name of the * {@link java.security.SecureRandom SecureRandom} provider used by the * SDK. * @return a <code>SecureRandom</code> provider name. * @throws SecurityException if a security manager exists and its * {@link SecurityManager#checkPermission(java.security.Permission)} * method denies access to retrieve this configuration option's value. */ public static String getSecureRandomProvider() throws SecurityException { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new UProveSDKPermission(PREFIX_GET_OPTION + OPTION_SECURERANDOM_PROVIDER)); } return secureRandomProvider(); } /** * Sets the name of the * {@link java.security.SecureRandom SecureRandom} provider to be used * by the SDK. * @param provider the name of a provider of a <code>SecureRandom</code> * algorithm, or <code>null</code> to use the default according to the * site-wide configuration. * @throws SecurityException if a security manager exists and its * {@link SecurityManager#checkPermission(java.security.Permission)} * method denies access to set this configuration option's value. */ public static void setSecureRandomProvider(final String provider) { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new UProveSDKPermission(PREFIX_SET_OPTION + OPTION_SECURERANDOM_PROVIDER)); } synchronized (ConfigImpl.class) { secureRandomProvider = provider != null && provider.length() != 0 ? provider : OPTION_UNSET; RandomSourceImpl.reset(); } } /** * Returns the configured message digest provider. * @return the configured message digest provider. */ static synchronized String messageDigestProvider() { if (messageDigestProvider == OPTION_UNSET) { messageDigestProvider = getDefault(PROPERTY_MESSAGEDIGEST_PROVIDER); // if we got the empty string, go with null, meaning that we'll // use the first provider based on the site's java.security config if (messageDigestProvider != null && messageDigestProvider.length() == 0) { messageDigestProvider = null; } } return messageDigestProvider; } /** * Returns the name of the * {@link java.security.MessageDigest MessageDigest} provider used by the * SDK. * @return a <code>MessageDigest</code> provider name. * @throws SecurityException if a security manager exists and its * {@link SecurityManager#checkPermission(java.security.Permission)} * method denies access to retrieve this configuration option's value. */ public static String getMessageDigestProvider() throws SecurityException { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new UProveSDKPermission(PREFIX_GET_OPTION + OPTION_MESSAGEDIGEST_PROVIDER)); } return messageDigestProvider(); } /** * Sets the name of the * {@link java.security.MessageDigest MessageDigest} provider used by the * SDK. * @param provider a <code>MessageDigest</code> provider name, or * <code>null</code> to indicate that the SDK should use the * <code>java.security</code> default. * @throws SecurityException if a security manager exists and its * {@link SecurityManager#checkPermission(java.security.Permission)} * method denies access to set this configuration option's value. */ public static void setMessageDigestProvider(final String provider) throws SecurityException { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new UProveSDKPermission(PREFIX_SET_OPTION + OPTION_MESSAGEDIGEST_PROVIDER)); } synchronized (ConfigImpl.class) { messageDigestProvider = provider != null && provider.length() != 0 ? provider : OPTION_UNSET; } } /** * Creates a new <code>MessageDigest</code> instance using the configured * provider. * @param algorithm the name of the message digest algorithm. * @return a digest instance. * @throws NoSuchProviderException if a provider is configured but not * available. * @throws NoSuchAlgorithmException if the desired algorithm is not * available. * @see #messageDigestProvider() */ static MessageDigest getMessageDigest(final String algorithm) throws NoSuchProviderException, NoSuchAlgorithmException { final String provider = messageDigestProvider(); return provider != null ? MessageDigest.getInstance(algorithm, provider) : MessageDigest.getInstance(algorithm); } /* * Prime number generation levels. */ /** * Test a prime confidence level for validity. * <p>Numbers less than <code>1</code> are invalid due to the fact that * you'll never find a prime with such a level. Empirical testing (and * inspection of Sun's implementation) show that for integers over 1024 * bits in length, levels above 3 are equivalent to 3.</p> * @param level a confidence level for testing. * @return <code>true</code> if <code>level</code> is valid. */ private static boolean isValidPrimeConfidenceLevel(final int level) { return level >= 1; } /** * Returns the configured prime number generation confidence level. * @return the configured prime number generation confidence level. */ static synchronized int primeConfidenceLevel() { if (primeConfidenceLevel == LEVEL_UNSET) { final String defaultLevel = getDefault(PROPERTY_MATH_PRIMECONFIDENCELEVEL); // pessimistically choose the default primeConfidenceLevel = DEFAULT_PRIME_CONFIDENCE_LEVEL; // now try to parse the default if (defaultLevel != null && defaultLevel.length() != 0) { try { final int level = Integer.parseInt(defaultLevel); if (isValidPrimeConfidenceLevel(level)) { // we got a good one! primeConfidenceLevel = level; } } catch (NumberFormatException nfe) { // stick with the default } } } return primeConfidenceLevel; } /** * Returns the confidence level for prime number generation. * @return the confidence level for prime number generation. * @throws SecurityException if a security manager exists and its * {@link SecurityManager#checkPermission(java.security.Permission)} * method denies access to retrieve this configuration option's value. */ public static int getPrimeConfidenceLevel() throws SecurityException { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new UProveSDKPermission(PREFIX_GET_OPTION + OPTION_MATH_PRIMECONFIDENCELEVEL)); } return primeConfidenceLevel(); } /** * Sets the confidence level for prime number generation. * @param aLevel the confidence level for prime number generation, or * <code>0</code> to select the SDK's default value. * @throws SecurityException if a security manager exists and its * {@link SecurityManager#checkPermission(java.security.Permission)} * method denies access to set this configuration option's value. */ public static void setPrimeConfidenceLevel(final int aLevel) throws SecurityException { final SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new UProveSDKPermission(PREFIX_SET_OPTION + OPTION_MATH_PRIMECONFIDENCELEVEL)); } final int level; if (aLevel == 0) { level = LEVEL_UNSET; } else if (!isValidPrimeConfidenceLevel(aLevel)) { throw new IllegalArgumentException("Invalid level: " + aLevel); } else { level = aLevel; } synchronized (ConfigImpl.class) { primeConfidenceLevel = level; } } }