Skip to content

Commit

Permalink
Also use reflection to get Conscrypt provider to generate randomness.
Browse files Browse the repository at this point in the history
This makes sure that Conscrypt gets used even if Randomness is requested before installing Conscrypt.

This will not work with ProGuard, since ProGuard changes class names. But ProGuard is usually only used on Android, where Conscrypt is anyways already installed.

Also, we let setFipsRestricted fail if Conscrypt is not available. This should be fine because it is not possible to use Tink in FIPS-restricted mode if Conscrypt is not avaialble anyways. And it makes sure that Random doesn't return non-FIPS restricted randomness if setFipsRestricted is enabled.

PiperOrigin-RevId: 557786519
Change-Id: I11c4e6a12c6d6a5f41159acaca22039a5c0e8501
  • Loading branch information
juergw authored and copybara-github committed Aug 17, 2023
1 parent f80a2c7 commit 496703e
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 25 deletions.
10 changes: 8 additions & 2 deletions src/main/java/com/google/crypto/tink/config/internal/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@ android_library(
java_library(
name = "tink_fips_util",
srcs = ["TinkFipsUtil.java"],
deps = [":tink_fips_status"],
deps = [
":tink_fips_status",
"//src/main/java/com/google/crypto/tink/internal:random",
],
)

android_library(
name = "tink_fips_util-android",
srcs = ["TinkFipsUtil.java"],
deps = [":tink_fips_status-android"],
deps = [
":tink_fips_status-android",
"//src/main/java/com/google/crypto/tink/internal:random-android",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
////////////////////////////////////////////////////////////////////////////////
package com.google.crypto.tink.config.internal;

import com.google.crypto.tink.internal.Random;
import java.lang.reflect.Method;
import java.security.GeneralSecurityException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;

Expand Down Expand Up @@ -52,7 +54,13 @@ public boolean isCompatible() {
public abstract boolean isCompatible();
}

public static void setFipsRestricted() {
/** Sets the binary to FIPS-only restricted mode. Fails if no FIPS-mode is available. */
public static void setFipsRestricted() throws GeneralSecurityException {
if (!checkConscryptIsAvailableAndUsesFipsBoringSsl()) {
throw new GeneralSecurityException(
"Conscrypt is not available or does not support checking for FIPS build.");
}
Random.validateUsesConscrypt();
isRestrictedToFips.set(true);
}

Expand All @@ -74,8 +82,8 @@ public static boolean fipsModuleAvailable() {
static Boolean checkConscryptIsAvailableAndUsesFipsBoringSsl() {
try {
Class<?> cls = Class.forName("org.conscrypt.Conscrypt");
Method isBoringSslFIPSBuild = cls.getMethod("isBoringSslFIPSBuild");
return (Boolean) isBoringSslFIPSBuild.invoke(null);
Method isBoringSslFipsBuild = cls.getMethod("isBoringSslFIPSBuild");
return (Boolean) isBoringSslFipsBuild.invoke(null);
} catch (Exception e) {
// For older versions of Conscrypt we get a NoSuchMethodException. But no matter what goes
// wrong, we cannot guarantee that Conscrypt uses BoringCrypto, so we will return false.
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/com/google/crypto/tink/internal/Random.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@

package com.google.crypto.tink.internal;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.GeneralSecurityException;
import java.security.Provider;
import java.security.SecureRandom;

/** Provides secure randomness using {@link SecureRandom}. */
Expand All @@ -29,6 +32,27 @@ protected SecureRandom initialValue() {
}
};

/**
* Tries to get the Conscrypt provider using reflection.
*
* <p>Note that this will typically fail on Android, because after ProGuard renames all class and
* method names. However, on Android Conscrypt is installed by default and so this code is not
* executed.
*/
private static Provider getConscryptProviderWithReflection() throws GeneralSecurityException {
try {
Class<?> conscrypt = Class.forName("org.conscrypt.Conscrypt");
Method getProvider = conscrypt.getMethod("newProvider");
return (Provider) getProvider.invoke(null);
} catch (ClassNotFoundException
| NoSuchMethodException
| IllegalArgumentException
| InvocationTargetException
| IllegalAccessException e) {
throw new GeneralSecurityException("Failed to get Conscrypt provider", e);
}
}

private static SecureRandom create() {
// Use Conscrypt if possible. Conscrypt may have three different provider names.
// For legacy compatibility reasons it uses the algorithm name "SHA1PRNG".
Expand All @@ -47,6 +71,12 @@ private static SecureRandom create() {
} catch (GeneralSecurityException e) {
// ignore
}
// Conscrypt might be present in the binary, but not (yet) installed.
try {
return SecureRandom.getInstance("SHA1PRNG", getConscryptProviderWithReflection());
} catch (GeneralSecurityException e) {
// ignore
}
return new SecureRandom();
}

Expand Down
17 changes: 13 additions & 4 deletions src/test/java/com/google/crypto/tink/RegistryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1772,14 +1772,23 @@ public Class<Mac> getInputPrimitiveClass() {
}

@Test
public void testFips_succeedsOnEmptyRegistry() throws Exception {
public void testRestrictToFips_fipsModuleAvailable_succeedsOnEmptyRegistry() throws Exception {
Assume.assumeTrue(TinkFipsUtil.fipsModuleAvailable());
Registry.reset();
Registry.restrictToFipsIfEmpty();
assertTrue(TinkFipsUtil.useOnlyFips());
}

@Test
public void testFips_succeedsOnSuccessiveRestrictToFips() throws Exception {
public void test_fipsModuleNotAvailable_fails() throws Exception {
Assume.assumeFalse(TinkFipsUtil.fipsModuleAvailable());
Registry.reset();
assertThrows(GeneralSecurityException.class, Registry::restrictToFipsIfEmpty);
}

@Test
public void testSuccessiveRestrictToFips_works() throws Exception {
Assume.assumeTrue(TinkFipsUtil.fipsModuleAvailable());
Registry.reset();
Registry.restrictToFipsIfEmpty();
Registry.restrictToFipsIfEmpty();
Expand All @@ -1788,14 +1797,14 @@ public void testFips_succeedsOnSuccessiveRestrictToFips() throws Exception {
}

@Test
public void testFips_succeedsOnRestrictToFipsWhenBuiltInFipsMode() throws Exception {
public void testRestrictToFips_builtInFipsMode_works() throws Exception {
Assume.assumeTrue(TinkFipsUtil.useOnlyFips());
Registry.restrictToFipsIfEmpty();
assertTrue(TinkFipsUtil.useOnlyFips());
}

@Test
public void testFips_failsOnNonEmptyRegistry() throws Exception {
public void testRestrictToFips_failsOnNonEmptyRegistry() throws Exception {
Assume.assumeFalse(TinkFipsUtil.useOnlyFips());
assertThrows(GeneralSecurityException.class, Registry::restrictToFipsIfEmpty);
}
Expand Down
14 changes: 13 additions & 1 deletion src/test/java/com/google/crypto/tink/config/TinkFipsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.google.crypto.tink.config;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

import com.google.crypto.tink.config.internal.TinkFipsUtil;
import java.security.GeneralSecurityException;
Expand Down Expand Up @@ -50,13 +51,24 @@ public void testFipsOnlyModeConsistentEnabled() {
}

@Test
public void testFipsEnablingAtRuntime() throws GeneralSecurityException {
public void testFipsEnablingAtRuntime_worksWhenFipsModuleIsAvailable()
throws GeneralSecurityException {
// If Tink has not been built in FIPS-mode, then the useOnlyFips() call should only return
// true after the restrictions have been enabled.
Assume.assumeFalse(TinkFipsUtil.useOnlyFips());
Assume.assumeTrue(TinkFipsUtil.fipsModuleAvailable());

assertThat(TinkFips.useOnlyFips()).isFalse();
TinkFips.restrictToFips();
assertThat(TinkFips.useOnlyFips()).isTrue();
}

@Test
public void testFipsEnablingAtRuntime_failsWhenFipsModuleIsNotAvailable()
throws GeneralSecurityException {
Assume.assumeFalse(TinkFipsUtil.useOnlyFips());
Assume.assumeFalse(TinkFipsUtil.fipsModuleAvailable());

assertThrows(GeneralSecurityException.class, TinkFips::restrictToFips);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
package com.google.crypto.tink.config.internal;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

import java.security.GeneralSecurityException;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -70,34 +72,29 @@ public void testFipsOnlyModeEnabledAlgorithmCompatibilityNoBoringCrypto() {
}

@Test
public void testFipsOnlyModeEnabledAtRuntimeAlgorithmCompatibility() {
public void testFipsOnlyModeEnabledAtRuntimeWithBoringCrypto() throws GeneralSecurityException {
// Test behavior when FIPS-only mode is set at runtime.
Assume.assumeFalse(TinkFipsUtil.useOnlyFips());
TinkFipsUtil.setFipsRestricted();

assertThat(TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_NOT_FIPS.isCompatible()).isFalse();

// BoringCrypto is available, therefore an algorithm which has a FIPS validated
// implementation is compatible.
Assume.assumeTrue(TinkFipsUtil.fipsModuleAvailable());

TinkFipsUtil.setFipsRestricted();

assertThat(TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_NOT_FIPS.isCompatible()).isFalse();
assertThat(
TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_REQUIRES_BORINGCRYPTO.isCompatible())
.isTrue();
}

@Test
public void testFipsOnlyModeEnabledAtRuntimeAlgorithmCompatibilityNoBoringCrypto() {
public void testFipsOnlyModeEnabledAtRuntimeWithoutBoringCrypto()
throws GeneralSecurityException {
// Test behavior when FIPS-only mode is set at runtime.
Assume.assumeFalse(TinkFipsUtil.useOnlyFips());
TinkFipsUtil.setFipsRestricted();
// BoringCrypto is not available.
Assume.assumeFalse(TinkFipsUtil.fipsModuleAvailable());

assertThat(TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_NOT_FIPS.isCompatible()).isFalse();

// BoringCrypto is not available, therefore no validated implementation is available and
// the compatibility check must fail.
Assume.assumeTrue(!TinkFipsUtil.fipsModuleAvailable());
assertThat(
TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_REQUIRES_BORINGCRYPTO.isCompatible())
.isFalse();
assertThrows(GeneralSecurityException.class, TinkFipsUtil::setFipsRestricted);
}
}
13 changes: 13 additions & 0 deletions src/test/java/com/google/crypto/tink/internal/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -518,3 +518,16 @@ java_test(
"@maven//:junit_junit",
],
)

java_test(
name = "RandomWithoutInstallingConscryptTest",
size = "small",
srcs = ["RandomWithoutInstallingConscryptTest.java"],
deps = [
"//src/main/java/com/google/crypto/tink/internal:random",
"//src/main/java/com/google/crypto/tink/testing:test_util",
"@maven//:com_google_truth_truth",
"@maven//:junit_junit",
"@maven//:org_conscrypt_conscrypt_openjdk_uber",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package com.google.crypto.tink.internal;

import static com.google.common.truth.Truth.assertThat;

import com.google.crypto.tink.testing.TestUtil;
import org.conscrypt.Conscrypt;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public final class RandomWithoutInstallingConscryptTest {

@Test
public void randBytes_usesConscrypt() throws Exception {
assertThat(Random.randBytes(10)).hasLength(10);

Random.validateUsesConscrypt();

if (!TestUtil.isAndroid()) {
// Make a call to Conscrypt to make sure it is present. But don't install it.
Conscrypt.checkAvailability();
}
}
}

0 comments on commit 496703e

Please sign in to comment.