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

Added u16 endian loading/storing functions, SSL_CIPHER_find, and SSL_client_hello_get0_ciphers #1482

Merged
merged 19 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5957723
added SSL_client_hello_get0_ciphers
smittals2 Mar 5, 2024
6e8338d
added SSL_CIPHER_find and fixed error in get_cipher
smittals2 Mar 6, 2024
2faa40a
fixed SSL_CIPHER_find and added a test for it
smittals2 Mar 7, 2024
5a822be
reworked logic to write cipher to *out, wrote a test for SSL_client_h…
smittals2 Mar 8, 2024
7bb08b7
removed extra line
smittals2 Mar 8, 2024
f1d06cd
added u16 endian load and store functions, fixed SSL_client_hello_get…
smittals2 Mar 8, 2024
72caa20
small test change
smittals2 Mar 8, 2024
8631d05
fixed documentation and endian u16 test
smittals2 Mar 12, 2024
1d19ab7
changed null check convention and fixed comments
smittals2 Mar 12, 2024
cafcd0c
reduced redundant cipher count calculation
smittals2 Mar 12, 2024
3434d45
used CRYPTO library function to maintain consistency instead of expli…
smittals2 Mar 12, 2024
069f33d
fixed tests and added reset for cipher_suites array in SSL_clear
smittals2 Mar 12, 2024
8b93da5
Merge branch 'aws:main' into cipherfuncs
smittals2 Mar 12, 2024
4beaaaa
removed memcpy check from test and fixed var name
smittals2 Mar 12, 2024
83ff135
changed init check and added debug check
smittals2 Mar 13, 2024
a983b3b
made variable init explicit to avoid test failure
smittals2 Mar 14, 2024
a00c196
added ssl null check for cipher_find and made logic clearer for get_c…
smittals2 Mar 26, 2024
ff3a8ab
Merge branch 'main' into cipherfuncs
justsmth Apr 4, 2024
e853951
Merge branch 'aws:main' into cipherfuncs
smittals2 Apr 15, 2024
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
14 changes: 14 additions & 0 deletions crypto/endian_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@
#include "openssl/bn.h"
#include "test/test_util.h"

TEST(EndianTest, u16Operations) {
uint8_t buffer[2];
uint16_t val = 0x1234;
uint8_t expected_be[2] = {0x12, 0x34};
uint8_t expected_le[2] = {0x34, 0x12};

CRYPTO_store_u16_le(buffer, val);
EXPECT_EQ(Bytes(expected_le), Bytes(buffer));
EXPECT_EQ(val, CRYPTO_load_u16_le(buffer));

CRYPTO_store_u16_be(buffer, val);
EXPECT_EQ(Bytes(expected_be), Bytes(buffer));
EXPECT_EQ(val, CRYPTO_load_u16_be(buffer));
}

TEST(EndianTest, u32Operations) {
uint8_t buffer[4];
Expand Down
34 changes: 34 additions & 0 deletions crypto/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,40 @@ static inline void *OPENSSL_memset(void *dst, int c, size_t n) {
// endianness. They use |memcpy|, and so avoid alignment or strict aliasing
// requirements on the input and output pointers.

static inline uint16_t CRYPTO_load_u16_le(const void *in) {
uint16_t v;
OPENSSL_memcpy(&v, in, sizeof(v));
#if defined(OPENSSL_BIG_ENDIAN)
return CRYPTO_bswap2(v);
#else
return v;
#endif
}

static inline void CRYPTO_store_u16_le(void *out, uint16_t v) {
#if defined(OPENSSL_BIG_ENDIAN)
v = CRYPTO_bswap2(v);
#endif
OPENSSL_memcpy(out, &v, sizeof(v));
}

static inline uint16_t CRYPTO_load_u16_be(const void *in) {
uint16_t v;
OPENSSL_memcpy(&v, in, sizeof(v));
#if defined(OPENSSL_BIG_ENDIAN)
return v;
#else
return CRYPTO_bswap2(v);
#endif
}

static inline void CRYPTO_store_u16_be(void *out, uint16_t v) {
#if !defined(OPENSSL_BIG_ENDIAN)
v = CRYPTO_bswap2(v);
#endif
OPENSSL_memcpy(out, &v, sizeof(v));
}

static inline uint32_t CRYPTO_load_u32_le(const void *in) {
uint32_t v;
OPENSSL_memcpy(&v, in, sizeof(v));
Expand Down
11 changes: 11 additions & 0 deletions include/openssl/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1469,6 +1469,12 @@ DEFINE_CONST_STACK_OF(SSL_CIPHER)
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4.
OPENSSL_EXPORT const SSL_CIPHER *SSL_get_cipher_by_value(uint16_t value);

// SSL_CIPHER_find returns a SSL_CIPHER structure which has the cipher ID stored in ptr or
// NULL if unknown. The ptr parameter is a two element array of char, which stores the
// two-byte TLS cipher ID (as allocated by IANA) in network byte order. SSL_CIPHER_find re-casts
// |ptr| to uint16_t and calls |SSL_get_cipher_by_value| to get the SSL_CIPHER structure.
OPENSSL_EXPORT const SSL_CIPHER *SSL_CIPHER_find(SSL *ssl, const unsigned char *ptr);

// SSL_CIPHER_get_id returns |cipher|'s non-IANA id. This is not its
// IANA-assigned number, which is called the "value" here, although it may be
// cast to a |uint16_t| to get it.
Expand Down Expand Up @@ -1850,6 +1856,11 @@ OPENSSL_EXPORT const SSL_CIPHER *SSL_get_current_cipher(const SSL *ssl);
// performed a new handshake.
OPENSSL_EXPORT STACK_OF(SSL_CIPHER) *SSL_get_client_ciphers(const SSL *ssl);

// SSL_client_hello_get0_ciphers provides access to the client ciphers field from the
// Client Hello, optionally writing the result to an out pointer. It returns the field
// length if successful, or 0 if |ssl| is a client or the handshake hasn't occurred yet.
OPENSSL_EXPORT size_t SSL_client_hello_get0_ciphers(SSL *ssl, const unsigned char **out);

// SSL_session_reused returns one if |ssl| performed an abbreviated handshake
// and zero otherwise.
//
Expand Down
5 changes: 5 additions & 0 deletions ssl/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -4068,6 +4068,11 @@ struct ssl_st {
// serialized and is only populated if used in a server context.
bssl::UniquePtr<STACK_OF(SSL_CIPHER)> client_cipher_suites;

// client_cipher_suites_arr is initialized when |SSL_client_hello_get0_ciphers|
// is called with a valid |out| param. It holds the cipher suites offered by the
// client from |client_cipher_suites| in an array.
bssl::Array<uint16_t> client_cipher_suites_arr;
smittals2 marked this conversation as resolved.
Show resolved Hide resolved

void (*info_callback)(const SSL *ssl, int type, int value) = nullptr;

bssl::UniquePtr<SSL_CTX> ctx;
Expand Down
9 changes: 9 additions & 0 deletions ssl/ssl_cipher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1470,6 +1470,15 @@ const SSL_CIPHER *SSL_get_cipher_by_value(uint16_t value) {
ssl_cipher_id_cmp));
}

const SSL_CIPHER *SSL_CIPHER_find(SSL *ssl, const unsigned char *ptr) {
smittals2 marked this conversation as resolved.
Show resolved Hide resolved
if (ssl != nullptr && ptr != nullptr) {
uint16_t cipher_id = CRYPTO_load_u16_be(ptr);
return SSL_get_cipher_by_value(cipher_id);
}

return NULL;
}

uint32_t SSL_CIPHER_get_id(const SSL_CIPHER *cipher) { return cipher->id; }

uint16_t SSL_CIPHER_get_protocol_id(const SSL_CIPHER *cipher) {
Expand Down
33 changes: 33 additions & 0 deletions ssl/ssl_lib.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3096,6 +3096,7 @@ int SSL_clear(SSL *ssl) {
}

ssl->client_cipher_suites.reset();
ssl->client_cipher_suites_arr.Reset();

// In OpenSSL, reusing a client |SSL| with |SSL_clear| causes the previously
// established session to be offered the next time around. wpa_supplicant
Expand Down Expand Up @@ -3319,3 +3320,35 @@ int SSL_CTX_set1_curves_list(SSL_CTX *ctx, const char *curves) {
int SSL_set1_curves_list(SSL *ssl, const char *curves) {
return SSL_set1_groups_list(ssl, curves);
}

size_t SSL_client_hello_get0_ciphers(SSL *ssl, const unsigned char **out) {
STACK_OF(SSL_CIPHER) *client_cipher_suites = SSL_get_client_ciphers(ssl);
if (client_cipher_suites == nullptr) {
return 0;
}

size_t num_ciphers = sk_SSL_CIPHER_num(client_cipher_suites);
if (out != nullptr) {
// Hasn't been called before
if(ssl->client_cipher_suites_arr.empty()) {
if (!ssl->client_cipher_suites_arr.Init(num_ciphers)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
smittals2 marked this conversation as resolved.
Show resolved Hide resolved
return 0;
}

// Construct list of cipherIDs
for (size_t i = 0; i < num_ciphers; i++) {
const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(client_cipher_suites, i);
uint16_t iana_id = SSL_CIPHER_get_protocol_id(cipher);

CRYPTO_store_u16_be(&ssl->client_cipher_suites_arr[i], iana_id);
}
}

assert(ssl->client_cipher_suites_arr.size() == num_ciphers);
*out = reinterpret_cast<unsigned char*>(ssl->client_cipher_suites_arr.data());
justsmth marked this conversation as resolved.
Show resolved Hide resolved
}

// Return the size
return num_ciphers;
}
77 changes: 77 additions & 0 deletions ssl/ssl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2378,6 +2378,83 @@ static bool ConnectClientAndServer(bssl::UniquePtr<SSL> *out_client,
return true;
}

// Correct ID and name
#define TLS13_AES_128_GCM_SHA256_BYTES ((const unsigned char *)"\x13\x01")
#define TLS13_CHACHA20_POLY1305_SHA256_BYTES ((const unsigned char *)"\x13\x03")
// Invalid ID
#define TLS13_AES_256_GCM_SHA384_BYTES ((const unsigned char *)"\x13\x13")
TEST(SSLTest, FindingCipher) {
bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
bssl::UniquePtr<SSL_CTX> server_ctx =
CreateContextWithTestCertificate(TLS_method());
ASSERT_TRUE(client_ctx);
ASSERT_TRUE(server_ctx);
// Configure only TLS 1.3.
ASSERT_TRUE(SSL_CTX_set_min_proto_version(client_ctx.get(), TLS1_3_VERSION));
ASSERT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), TLS1_3_VERSION));

bssl::UniquePtr<SSL> client, server;
ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
server_ctx.get()));

SCOPED_TRACE("TLS_AES_128_GCM_SHA256");
const SSL_CIPHER *cipher1 = SSL_CIPHER_find(server.get(), TLS13_AES_128_GCM_SHA256_BYTES);
ASSERT_TRUE(cipher1);
EXPECT_STREQ("TLS_AES_128_GCM_SHA256", SSL_CIPHER_standard_name(cipher1));

SCOPED_TRACE("TLS_CHACHA20_POLY1305_SHA256");
const SSL_CIPHER *cipher2 = SSL_CIPHER_find(server.get(), TLS13_CHACHA20_POLY1305_SHA256_BYTES);
ASSERT_TRUE(cipher2);
EXPECT_STREQ("TLS_CHACHA20_POLY1305_SHA256", SSL_CIPHER_standard_name(cipher2));

SCOPED_TRACE("TLS_AES_256_GCM_SHA384");
const SSL_CIPHER *cipher3 = SSL_CIPHER_find(client.get(), TLS13_AES_256_GCM_SHA384_BYTES);
ASSERT_FALSE(cipher3);
}

TEST(SSLTest, GetClientCiphers) {
bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
bssl::UniquePtr<SSL_CTX> server_ctx =
CreateContextWithTestCertificate(TLS_method());

ASSERT_TRUE(SSL_CTX_set_ciphersuites(client_ctx.get(),
"TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"));
ASSERT_TRUE(client_ctx);
ASSERT_TRUE(server_ctx);
// Configure only TLS 1.3.
ASSERT_TRUE(SSL_CTX_set_min_proto_version(client_ctx.get(), TLS1_3_VERSION));
ASSERT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), TLS1_3_VERSION));

bssl::UniquePtr<SSL> client, server;
ASSERT_TRUE(CreateClientAndServer(&client, &server,
client_ctx.get(), server_ctx.get()));

const unsigned char *tmp = nullptr;
// Handshake not completed, getting ciphers should fail
ASSERT_FALSE(SSL_client_hello_get0_ciphers(client.get(), &tmp));
ASSERT_FALSE(SSL_client_hello_get0_ciphers(server.get(), &tmp));
ASSERT_FALSE(tmp);

ASSERT_TRUE(CompleteHandshakes(client.get(), server.get()));

// Client calling, should return 0
ASSERT_EQ(SSL_client_hello_get0_ciphers(client.get(), nullptr), (size_t) 0);

const unsigned char expected_cipher_bytes[] = {0x13, 0x01, 0x13, 0x02, 0x13, 0x03};

const unsigned char *p;
// Get client ciphers and ensure written to out in appropriate format
ASSERT_EQ(SSL_client_hello_get0_ciphers(server.get(), &p), (size_t) 3);
ASSERT_EQ(Bytes(expected_cipher_bytes, sizeof(expected_cipher_bytes)),
Bytes(p, sizeof(expected_cipher_bytes)));

// When calling again, should reuse value from ssl_st
ASSERT_FALSE(server.get()->client_cipher_suites_arr.empty());
ASSERT_EQ(SSL_client_hello_get0_ciphers(server.get(), &p), (size_t) 3);
ASSERT_EQ(Bytes(expected_cipher_bytes, sizeof(expected_cipher_bytes)),
Bytes(p, sizeof(expected_cipher_bytes)));
}

static bssl::UniquePtr<SSL_SESSION> g_last_session;

static int SaveLastSession(SSL *ssl, SSL_SESSION *session) {
Expand Down
Loading