Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Armv8.5 RNDR as prediction resistance #2029

Merged
merged 14 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crypto/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@ if(BUILD_TESTING)
fipsmodule/rand/ctrdrbg_test.cc
fipsmodule/rand/cpu_jitter_test.cc
fipsmodule/rand/new_rand_test.cc
fipsmodule/rand/entropy/entropy_source_tests.cc
fipsmodule/service_indicator/service_indicator_test.cc
fipsmodule/sha/sha_test.cc
fipsmodule/sha/sha3_test.cc
Expand Down
2 changes: 2 additions & 0 deletions crypto/fipsmodule/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ if(ARCH STREQUAL "aarch64")
md5-armv8.${ASM_EXT}
p256-armv8-asm.${ASM_EXT}
p256_beeu-armv8-asm.${ASM_EXT}
rndr-armv8.${ASM_EXT}
sha1-armv8.${ASM_EXT}
sha256-armv8.${ASM_EXT}
sha512-armv8.${ASM_EXT}
Expand Down Expand Up @@ -149,6 +150,7 @@ if(PERL_EXECUTABLE)
perlasm(p256-armv8-asm.${ASM_EXT} ec/asm/p256-armv8-asm.pl)
perlasm(p256_beeu-armv8-asm.${ASM_EXT} ec/asm/p256_beeu-armv8-asm.pl)
perlasm(rdrand-x86_64.${ASM_EXT} rand/asm/rdrand-x86_64.pl)
perlasm(rndr-armv8.${ASM_EXT} rand/asm/rndr-armv8.pl)
torben-hansen marked this conversation as resolved.
Show resolved Hide resolved
perlasm(rsaz-avx2.${ASM_EXT} bn/asm/rsaz-avx2.pl)
perlasm(rsaz-2k-avx512.${ASM_EXT} bn/asm/rsaz-2k-avx512.pl)
perlasm(rsaz-3k-avx512.${ASM_EXT} bn/asm/rsaz-3k-avx512.pl)
Expand Down
7 changes: 7 additions & 0 deletions crypto/fipsmodule/cpucap/cpu_aarch64_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ static uint64_t armv8_cpuid_probe(void) {

void OPENSSL_cpuid_setup(void) {
unsigned long hwcap = getauxval(AT_HWCAP);
unsigned long hwcap2 = getauxval(AT_HWCAP2);

// See /usr/include/asm/hwcap.h on an aarch64 installation for the source of
// these values.
Expand All @@ -47,6 +48,8 @@ void OPENSSL_cpuid_setup(void) {
static const unsigned long kSHA3 = 1 << 17;
static const unsigned long kCPUID = 1 << 11;

static const unsigned long kRNGhwcap2 = 1 << 16;;

uint64_t OPENSSL_arm_midr = 0;

if ((hwcap & kNEON) == 0) {
Expand Down Expand Up @@ -97,6 +100,10 @@ void OPENSSL_cpuid_setup(void) {
OPENSSL_armcap_P |= (ARMV8_DIT | ARMV8_DIT_ALLOWED);
}

if (hwcap2 & kRNGhwcap2) {
OPENSSL_armcap_P |= ARMV8_RNG;
}

// OPENSSL_armcap is a 32-bit, unsigned value which may start with "0x" to
// indicate a hex value. Prior to the 32-bit value, a '~' or '|' may be given.
//
Expand Down
4 changes: 4 additions & 0 deletions crypto/fipsmodule/cpucap/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ OPENSSL_INLINE int CRYPTO_is_ARMv8_DIT_capable(void) {
// This function is used only for testing; hence, not inlined
OPENSSL_EXPORT int CRYPTO_is_ARMv8_DIT_capable_for_testing(void);

OPENSSL_INLINE int CRYPTO_is_ARMv8_RNDR_capable(void) {
return (OPENSSL_armcap_P & ARMV8_RNG) != 0;
}

#endif // OPENSSL_ARM || OPENSSL_AARCH64

#if defined(AARCH64_DIT_SUPPORTED)
Expand Down
60 changes: 60 additions & 0 deletions crypto/fipsmodule/rand/asm/rndr-armv8.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#! /usr/bin/env perl

# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0 OR ISC

# RNDR from ARMv8.5-A.
# System register encoding: s3_3_c2_c4_0.
# see https://developer.arm.com/documentation/ddi0601/2024-09/AArch64-Registers/RNDR--Random-Number

# The first two arguments should always be the flavour and output file path.
if ($#ARGV < 1) { die "Not enough arguments provided.
Two arguments are necessary: the flavour and the output file path."; }

my $flavour = shift;
my $output = shift;

$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
( $xlate="${dir}../../../perlasm/arm-xlate.pl" and -f $xlate) or
die "can't locate arm-xlate.pl";

open OUT,"| \"$^X\" $xlate $flavour $output";
*STDOUT=*OUT;

torben-hansen marked this conversation as resolved.
Show resolved Hide resolved
my ($out, $len, $rndr64) = ("x0", "x1", "x2");

$code.=<<___;
#include <openssl/arm_arch.h>

.arch armv8-a
.text

# int CRYPTO_rndr_multiple8(uint8_t *out, const size_t len)
torben-hansen marked this conversation as resolved.
Show resolved Hide resolved
.globl CRYPTO_rndr_multiple8
.type CRYPTO_rndr_multiple8,%function
.align 4
CRYPTO_rndr_multiple8:
cbz $len, .Lrndr_multiple8_error // len = 0 is not supported
torben-hansen marked this conversation as resolved.
Show resolved Hide resolved

.Lrndr_multiple8_loop:
mrs $rndr64, s3_3_c2_c4_0 // rndr instruction https://developer.arm.com/documentation/ddi0601/2024-09/Index-by-Encoding
torben-hansen marked this conversation as resolved.
Show resolved Hide resolved
cbz $rndr64, .Lrndr_multiple8_error // Check if rndr failed

str $rndr64, [$out], #8 // Copy 8 bytes to *out and increment pointer by 8
sub $len, $len, #8
cbz $len, .Lrndr_multiple8_done // If multiple of 8 this will be 0 eventually
b .Lrndr_multiple8_loop

.Lrndr_multiple8_done:
mov x0, #1 // Return value success
ret

.Lrndr_multiple8_error:
mov x0, #0 // Return value error
ret
.size CRYPTO_rndr_multiple8,.-CRYPTO_rndr_multiple8
___

print $code;
close STDOUT or die "error closing STDOUT: $!"; # enforce flush
40 changes: 40 additions & 0 deletions crypto/fipsmodule/rand/entropy/entropy_source_tests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

#include <gtest/gtest.h>

#include "internal.h"

#define MAX_MULTIPLE_FROM_RNG (16)
torben-hansen marked this conversation as resolved.
Show resolved Hide resolved

// In the future this test can be improved by being able to predict whether the
// test is running on hardware that we expect to support RNDR. This will require
// amending the CI with such information.
// For now, simply ensure we exercise all code-paths in the
// CRYPTO_rndr_multiple8 implementation.
TEST(EntropySupport, Aarch64) {
uint8_t buf[MAX_MULTIPLE_FROM_RNG*8] = { 0 } ;

#if !defined(OPENSSL_AARCH64)
ASSERT_FALSE(have_hw_rng_aarch64_for_testing());
ASSERT_FALSE(rndr_multiple8(buf, 0));
ASSERT_FALSE(rndr_multiple8(buf, 8));
#else
if (have_hw_rng_aarch64_for_testing() != 1) {
GTEST_SKIP() << "Compiled for Arm64, but Aarch64 hw rng is not available in run-time";
}

// Extracting 0 bytes is never supported.
ASSERT_FALSE(rndr_multiple8(buf, 0));

// Multiples of 8 allowed.
for (size_t i = 8; i < MAX_MULTIPLE_FROM_RNG; i += 8) {
ASSERT_TRUE(rndr_multiple8(buf, i));
torben-hansen marked this conversation as resolved.
Show resolved Hide resolved
}

// Must be multiples of 8.
for (size_t i : {1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15}) {
ASSERT_FALSE(rndr_multiple8(buf, i));
}
#endif
}
27 changes: 22 additions & 5 deletions crypto/fipsmodule/rand/entropy/entropy_sources.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@
static int entropy_get_prediction_resistance(
const struct entropy_source_t *entropy_source,
uint8_t pred_resistance[RAND_PRED_RESISTANCE_LEN]) {
if (have_fast_rdrand() == 1 &&
rdrand(pred_resistance, RAND_PRED_RESISTANCE_LEN) != 1) {
return 0;
#if defined(OPENSSL_X86_64)
if (rdrand(pred_resistance, RAND_PRED_RESISTANCE_LEN) == 1) {
return 1;
}
return 1;
#elif defined(OPENSSL_AARCH64)
if (rndr_multiple8(pred_resistance, RAND_PRED_RESISTANCE_LEN) == 1) {
return 1;
}
#endif
return 0;
}

static int entropy_get_extra_entropy(
Expand All @@ -37,7 +42,8 @@ DEFINE_LOCAL_DATA(struct entropy_source_methods, tree_jitter_entropy_source_meth
out->free_thread = tree_jitter_free_thread_drbg;
out->get_seed = tree_jitter_get_seed;
out->get_extra_entropy = entropy_get_extra_entropy;
if (have_fast_rdrand() == 1) {
if (have_hw_rng_x86_64_fast() == 1 ||
have_hw_rng_aarch64() == 1) {
out->get_prediction_resistance = entropy_get_prediction_resistance;
} else {
out->get_prediction_resistance = NULL;
Expand Down Expand Up @@ -68,3 +74,14 @@ struct entropy_source_t * get_entropy_source(void) {

return entropy_source;
}

int rndr_multiple8(uint8_t *buf, const size_t len) {
if (len == 0 || ((len & 0x7) != 0)) {
return 0;
}
return CRYPTO_rndr_multiple8(buf, len);
}

int have_hw_rng_aarch64_for_testing(void) {
return have_hw_rng_aarch64();
}
34 changes: 34 additions & 0 deletions crypto/fipsmodule/rand/entropy/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <openssl/ctrdrbg.h>

#include "../new_rand_internal.h"
#include "../../cpucap/internal.h"

#if defined(__cplusplus)
extern "C" {
Expand Down Expand Up @@ -40,6 +41,39 @@ OPENSSL_EXPORT void tree_jitter_free_thread_drbg(struct entropy_source_t *entrop
OPENSSL_EXPORT int tree_jitter_get_seed(
const struct entropy_source_t *entropy_source, uint8_t seed[CTR_DRBG_ENTROPY_LEN]);

// rndr_multiple8 writes |len| number of bytes to |buf| generated using the
torben-hansen marked this conversation as resolved.
Show resolved Hide resolved
// rndr instruction. |len| must be a multiple of 8.
// Outputs 1 on success, 0 otherwise.
OPENSSL_EXPORT int rndr_multiple8(uint8_t *buf, const size_t len);

// have_hw_rng_aarch64_for_testing wraps |have_hw_rng_aarch64| to allow usage
// in testing.
OPENSSL_EXPORT int have_hw_rng_aarch64_for_testing(void);

#if defined(OPENSSL_AARCH64) && !defined(OPENSSL_NO_ASM)

// CRYPTO_rndr_multiple8 writes |len| number of bytes to |buf| generated using
// the rndr instruction. |len| must be a multiple of 8 and positive.
// Outputs 1 on success, 0 otherwise.
int CRYPTO_rndr_multiple8(uint8_t *out, size_t out_len);

// Returns 1 if Armv8-A instruction rndr is available, 0 otherwise.
OPENSSL_INLINE int have_hw_rng_aarch64(void) {
return CRYPTO_is_ARMv8_RNDR_capable();
}

#else // defined(OPENSSL_AARCH64) && !defined(OPENSSL_NO_ASM)

OPENSSL_INLINE int CRYPTO_rndr_multiple8(uint8_t *out, size_t out_len) {
return 0;
}

OPENSSL_INLINE int have_hw_rng_aarch64(void) {
return 0;
}

#endif // defined(OPENSSL_AARCH64) && !defined(OPENSSL_NO_ASM)

#if defined(__cplusplus)
} // extern C
#endif
Expand Down
14 changes: 8 additions & 6 deletions crypto/fipsmodule/rand/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,19 @@ int rdrand(uint8_t *buf, const size_t len);

#if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM)

OPENSSL_INLINE int have_rdrand(void) {
OPENSSL_INLINE int have_hw_rng_x86_64(void) {
return CRYPTO_is_RDRAND_capable();
}

// have_fast_rdrand returns true if RDRAND is supported and it's reasonably
// fast. Concretely the latter is defined by whether the chip is Intel (fast) or
// not (assumed slow).
OPENSSL_INLINE int have_fast_rdrand(void) {
OPENSSL_INLINE int have_hw_rng_x86_64_fast(void) {
return CRYPTO_is_RDRAND_capable() && CRYPTO_is_intel_cpu();
}

// TODO only allow multiples of 8 from rdrand

// CRYPTO_rdrand writes eight bytes of random data from the hardware RNG to
// |out|. It returns one on success or zero on hardware failure.
int CRYPTO_rdrand(uint8_t out[8]);
Expand All @@ -121,17 +123,17 @@ int CRYPTO_rdrand(uint8_t out[8]);
// one on success and zero on hardware failure.
int CRYPTO_rdrand_multiple8_buf(uint8_t *buf, size_t len);

#else // OPENSSL_X86_64 && !OPENSSL_NO_ASM
#else // defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM)

OPENSSL_INLINE int have_rdrand(void) {
OPENSSL_INLINE int have_hw_rng_x86_64(void) {
return 0;
}

OPENSSL_INLINE int have_fast_rdrand(void) {
OPENSSL_INLINE int have_hw_rng_x86_64_fast(void) {
return 0;
}

#endif // OPENSSL_X86_64 && !OPENSSL_NO_ASM
#endif // defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM)

// Don't retry forever. There is no science in picking this number and can be
// adjusted in the future if need be. We do not backoff forever, because we
Expand Down
6 changes: 3 additions & 3 deletions crypto/fipsmodule/rand/rand.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ void CRYPTO_get_seed_entropy(uint8_t entropy[PASSIVE_ENTROPY_LOAD_LENGTH],
int *out_want_additional_input) {
*out_want_additional_input = 0;

if (have_rdrand() == 1) {
if (have_hw_rng_x86_64() == 1) {
if (rdrand(entropy, PASSIVE_ENTROPY_LOAD_LENGTH) != 1) {
abort();
}
Expand Down Expand Up @@ -400,7 +400,7 @@ void RAND_bytes_with_additional_data(uint8_t *out, size_t out_len,
uint8_t additional_data[32];
// Intel chips have fast RDRAND instructions while, in other cases, RDRAND can
// be _slower_ than a system call.
if (!have_fast_rdrand() ||
if (!have_hw_rng_x86_64_fast() ||
!rdrand(additional_data, sizeof(additional_data))) {
// Without a hardware RNG to save us from address-space duplication, the OS
// entropy is used. This can be expensive (one read per |RAND_bytes| call)
Expand All @@ -411,7 +411,7 @@ void RAND_bytes_with_additional_data(uint8_t *out, size_t out_len,
if ((snapsafe_status != 0 && fork_generation != 0) ||
fork_unsafe_buffering) {
OPENSSL_memset(additional_data, 0, sizeof(additional_data));
} else if (!have_rdrand()) {
} else if (!have_hw_rng_x86_64()) {
// No alternative so block for OS entropy.
CRYPTO_sysrand(additional_data, sizeof(additional_data));
} else if (!CRYPTO_sysrand_if_available(additional_data,
Expand Down
4 changes: 2 additions & 2 deletions crypto/fipsmodule/rand/urandom_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,8 @@ static std::vector<Event> TestFunctionPRNGModel(unsigned flags) {
const size_t kAdditionalDataLength = 32;
const size_t kPersonalizationStringLength = CTR_DRBG_ENTROPY_LEN;
const size_t kPassiveEntropyWithWhitenFactor = PASSIVE_ENTROPY_LOAD_LENGTH;
const bool kHaveRdrand = have_rdrand();
const bool kHaveFastRdrand = have_fast_rdrand();
const bool kHaveRdrand = have_hw_rng_x86_64();
torben-hansen marked this conversation as resolved.
Show resolved Hide resolved
const bool kHaveFastRdrand = have_hw_rng_x86_64_fast();
const bool kHaveForkDetection = have_fork_detection();

// Additional data might be drawn on each invocation of RAND_bytes(). In case
Expand Down
4 changes: 2 additions & 2 deletions crypto/rand_extra/rand_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ TEST(RandTest, Threads) {

#if defined(OPENSSL_X86_64) && defined(SUPPORTS_ABI_TEST)
TEST(RandTest, RdrandABI) {
if (!have_rdrand()) {
if (!have_hw_rng_x86_64()) {
fprintf(stderr, "rdrand not supported. Skipping.\n");
return;
}
Expand Down Expand Up @@ -275,7 +275,7 @@ TEST(RandTest, PassiveEntropyDepletedObviouslyNotBroken) {
// we can only validate the correct value is set on the static build type.
#if !defined(BORINGSSL_SHARED_LIBRARY)
int want_additional_input_expect = 0;
if (have_rdrand()) {
if (have_hw_rng_x86_64()) {
want_additional_input_expect = 1;
}
EXPECT_EQ(out_want_additional_input_false_default, want_additional_input_expect);
Expand Down
37 changes: 37 additions & 0 deletions generated-src/ios-aarch64/crypto/fipsmodule/rndr-armv8.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// This file is generated from a similarly-named Perl script in the BoringSSL
// source tree. Do not edit by hand.

#include <openssl/asm_base.h>

#if !defined(OPENSSL_NO_ASM) && defined(OPENSSL_AARCH64) && defined(__APPLE__)
#include <openssl/arm_arch.h>


.text

# int CRYPTO_rndr_multiple8(uint8_t *out, const size_t len)
.globl _CRYPTO_rndr_multiple8
.private_extern _CRYPTO_rndr_multiple8

.align 4
_CRYPTO_rndr_multiple8:
cbz x1, Lrndr_multiple8_error // len = 0 is not supported

Lrndr_multiple8_loop:
mrs x2, s3_3_c2_c4_0 // rndr instruction https://developer.arm.com/documentation/ddi0601/2024-09/Index-by-Encoding
cbz x2, Lrndr_multiple8_error // Check if rndr failed

str x2, [x0], #8 // Copy 8 bytes to *out and increment pointer by 8
sub x1, x1, #8
cbz x1, Lrndr_multiple8_done // If multiple of 8 this will be 0 eventually
b Lrndr_multiple8_loop

Lrndr_multiple8_done:
mov x0, #1 // Return value success
ret

Lrndr_multiple8_error:
mov x0, #0 // Return value error
ret

#endif // !OPENSSL_NO_ASM && defined(OPENSSL_AARCH64) && defined(__APPLE__)
Loading
Loading