shim: rewrite MoveTestState() to use a serialized representation.
This helps with creating a separate binary to perform split handshakes, in that the test state must be communicated to, and retrieved from, the handshaker binary using a socket. Change-Id: I9d70a9bb3d97dd339aab4f51c6de75f71e4fe72d Reviewed-on: https://boringssl-review.googlesource.com/29704 Commit-Queue: Adam Langley <agl@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org> Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
parent
0cbb1af41f
commit
6d597a34b6
@ -2488,14 +2488,13 @@ uint32_t ssl_hash_session_id(Span<const uint8_t> session_id);
|
||||
|
||||
// SSL_SESSION_parse parses an |SSL_SESSION| from |cbs| and advances |cbs| over
|
||||
// the parsed data.
|
||||
UniquePtr<SSL_SESSION> SSL_SESSION_parse(CBS *cbs,
|
||||
const SSL_X509_METHOD *x509_method,
|
||||
CRYPTO_BUFFER_POOL *pool);
|
||||
OPENSSL_EXPORT UniquePtr<SSL_SESSION> SSL_SESSION_parse(
|
||||
CBS *cbs, const SSL_X509_METHOD *x509_method, CRYPTO_BUFFER_POOL *pool);
|
||||
|
||||
// ssl_session_serialize writes |in| to |cbb| as if it were serialising a
|
||||
// session for Session-ID resumption. It returns one on success and zero on
|
||||
// error.
|
||||
int ssl_session_serialize(const SSL_SESSION *in, CBB *cbb);
|
||||
OPENSSL_EXPORT int ssl_session_serialize(const SSL_SESSION *in, CBB *cbb);
|
||||
|
||||
// ssl_session_is_context_valid returns one if |session|'s session ID context
|
||||
// matches the one set on |hs| and zero otherwise.
|
||||
|
@ -451,7 +451,10 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume,
|
||||
}
|
||||
}
|
||||
|
||||
if (config->is_server && !GetTestState(ssl)->early_callback_called) {
|
||||
// early_callback_called is updated in the handshaker, so we don't see it
|
||||
// here.
|
||||
if (!config->handoff && config->is_server &&
|
||||
!GetTestState(ssl)->early_callback_called) {
|
||||
fprintf(stderr, "early callback not called\n");
|
||||
return false;
|
||||
}
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "../../crypto/internal.h"
|
||||
#include "../internal.h"
|
||||
|
||||
using namespace bssl;
|
||||
|
||||
static CRYPTO_once_t g_once = CRYPTO_ONCE_INIT;
|
||||
static int g_state_index = 0;
|
||||
// Some code treats the zero time special, so initialize the clock to a
|
||||
@ -62,19 +64,9 @@ TestState *GetTestState(const SSL *ssl) {
|
||||
return (TestState *)SSL_get_ex_data(ssl, g_state_index);
|
||||
}
|
||||
|
||||
bool MoveTestState(SSL *dest, SSL *src) {
|
||||
TestState *state = GetTestState(src);
|
||||
if (!SSL_set_ex_data(src, g_state_index, nullptr) ||
|
||||
!SSL_set_ex_data(dest, g_state_index, state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ssl_ctx_add_session(SSL_SESSION *session, void *void_param) {
|
||||
SSL_CTX *ctx = reinterpret_cast<SSL_CTX *>(void_param);
|
||||
bssl::UniquePtr<SSL_SESSION> new_session = bssl::SSL_SESSION_dup(
|
||||
UniquePtr<SSL_SESSION> new_session = SSL_SESSION_dup(
|
||||
session, SSL_SESSION_INCLUDE_NONAUTH | SSL_SESSION_INCLUDE_TICKET);
|
||||
if (new_session != nullptr) {
|
||||
SSL_CTX_add_session(ctx, new_session.get());
|
||||
@ -84,3 +76,115 @@ static void ssl_ctx_add_session(SSL_SESSION *session, void *void_param) {
|
||||
void CopySessions(SSL_CTX *dst, const SSL_CTX *src) {
|
||||
lh_SSL_SESSION_doall_arg(src->sessions, ssl_ctx_add_session, dst);
|
||||
}
|
||||
|
||||
static void push_session(SSL_SESSION *session, void *arg) {
|
||||
auto s = reinterpret_cast<std::vector<SSL_SESSION *> *>(arg);
|
||||
s->push_back(session);
|
||||
}
|
||||
|
||||
bool SerializeContextState(SSL_CTX *ctx, CBB *cbb) {
|
||||
CBB out, ctx_sessions, ticket_keys;
|
||||
uint8_t keys[48];
|
||||
if (!CBB_add_u24_length_prefixed(cbb, &out) ||
|
||||
!CBB_add_u16(&out, 0 /* version */) ||
|
||||
!SSL_CTX_get_tlsext_ticket_keys(ctx, &keys, sizeof(keys)) ||
|
||||
!CBB_add_u8_length_prefixed(&out, &ticket_keys) ||
|
||||
!CBB_add_bytes(&ticket_keys, keys, sizeof(keys)) ||
|
||||
!CBB_add_asn1(&out, &ctx_sessions, CBS_ASN1_SEQUENCE)) {
|
||||
return false;
|
||||
}
|
||||
std::vector<SSL_SESSION *> sessions;
|
||||
lh_SSL_SESSION_doall_arg(ctx->sessions, push_session, &sessions);
|
||||
for (const auto &sess : sessions) {
|
||||
if (!ssl_session_serialize(sess, &ctx_sessions)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return CBB_flush(cbb);
|
||||
}
|
||||
|
||||
bool DeserializeContextState(CBS *cbs, SSL_CTX *ctx) {
|
||||
CBS in, sessions, ticket_keys;
|
||||
uint16_t version;
|
||||
constexpr uint16_t kVersion = 0;
|
||||
if (!CBS_get_u24_length_prefixed(cbs, &in) ||
|
||||
!CBS_get_u16(&in, &version) ||
|
||||
version > kVersion ||
|
||||
!CBS_get_u8_length_prefixed(&in, &ticket_keys) ||
|
||||
!SSL_CTX_set_tlsext_ticket_keys(ctx, CBS_data(&ticket_keys),
|
||||
CBS_len(&ticket_keys)) ||
|
||||
!CBS_get_asn1(&in, &sessions, CBS_ASN1_SEQUENCE)) {
|
||||
return false;
|
||||
}
|
||||
while (CBS_len(&sessions)) {
|
||||
UniquePtr<SSL_SESSION> session =
|
||||
SSL_SESSION_parse(&sessions, ctx->x509_method, ctx->pool);
|
||||
if (!session) {
|
||||
return false;
|
||||
}
|
||||
SSL_CTX_add_session(ctx, session.get());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TestState::Serialize(CBB *cbb) const {
|
||||
CBB out, pending, text;
|
||||
if (!CBB_add_u24_length_prefixed(cbb, &out) ||
|
||||
!CBB_add_u16(&out, 0 /* version */) ||
|
||||
!CBB_add_u24_length_prefixed(&out, &pending) ||
|
||||
(pending_session &&
|
||||
!ssl_session_serialize(pending_session.get(), &pending)) ||
|
||||
!CBB_add_u16_length_prefixed(&out, &text) ||
|
||||
!CBB_add_bytes(
|
||||
&text, reinterpret_cast<const uint8_t *>(msg_callback_text.data()),
|
||||
msg_callback_text.length()) ||
|
||||
!CBB_flush(cbb)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<TestState> TestState::Deserialize(CBS *cbs, SSL_CTX *ctx) {
|
||||
CBS in, pending_session, text;
|
||||
std::unique_ptr<TestState> out_state(new TestState());
|
||||
uint16_t version;
|
||||
constexpr uint16_t kVersion = 0;
|
||||
if (!CBS_get_u24_length_prefixed(cbs, &in) ||
|
||||
!CBS_get_u16(&in, &version) ||
|
||||
version > kVersion ||
|
||||
!CBS_get_u24_length_prefixed(&in, &pending_session) ||
|
||||
!CBS_get_u16_length_prefixed(&in, &text)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (CBS_len(&pending_session)) {
|
||||
out_state->pending_session = SSL_SESSION_parse(
|
||||
&pending_session, ctx->x509_method, ctx->pool);
|
||||
if (!out_state->pending_session) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
out_state->msg_callback_text = std::string(
|
||||
reinterpret_cast<const char *>(CBS_data(&text)), CBS_len(&text));
|
||||
return out_state;
|
||||
}
|
||||
|
||||
bool MoveTestState(SSL *dest, SSL *src) {
|
||||
ScopedCBB out;
|
||||
Array<uint8_t> serialized;
|
||||
if (!CBB_init(out.get(), 512) ||
|
||||
!SerializeContextState(src->ctx.get(), out.get()) ||
|
||||
!GetTestState(src)->Serialize(out.get()) ||
|
||||
!CBBFinishArray(out.get(), &serialized)) {
|
||||
return false;
|
||||
}
|
||||
CBS in;
|
||||
CBS_init(&in, serialized.data(), serialized.size());
|
||||
if (!DeserializeContextState(&in, dest->ctx.get()) ||
|
||||
!SetTestState(dest, TestState::Deserialize(&in, dest->ctx.get())) ||
|
||||
!GetTestState(dest)) {
|
||||
return false;
|
||||
}
|
||||
GetTestState(dest)->async_bio = GetTestState(src)->async_bio;
|
||||
GetTestState(src)->async_bio = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
@ -22,6 +22,17 @@
|
||||
#include <openssl/base.h>
|
||||
|
||||
struct TestState {
|
||||
// Serialize writes |pending_session| and |msg_callback_text| to |out|, for
|
||||
// use in split-handshake tests. We don't try to serialize every bit of test
|
||||
// state, but serializing |pending_session| is necessary to exercise session
|
||||
// resumption, and |msg_callback_text| is especially useful. In the general
|
||||
// case, checks of state updated during the handshake can be skipped when
|
||||
// |config->handoff|.
|
||||
bool Serialize(CBB *out) const;
|
||||
|
||||
// Deserialize returns a new |TestState| from data written by |Serialize|.
|
||||
static std::unique_ptr<TestState> Deserialize(CBS *cbs, SSL_CTX *ctx);
|
||||
|
||||
// async_bio is async BIO which pauses reads and writes.
|
||||
BIO *async_bio = nullptr;
|
||||
// packeted_bio is the packeted BIO which simulates read timeouts.
|
||||
@ -64,4 +75,12 @@ void AdvanceClock(unsigned seconds);
|
||||
|
||||
void CopySessions(SSL_CTX *dest, const SSL_CTX *src);
|
||||
|
||||
// SerializeContextState writes session material (sessions and ticket keys) from
|
||||
// |ctx| into |cbb|.
|
||||
bool SerializeContextState(SSL_CTX *ctx, CBB *cbb);
|
||||
|
||||
// DeserializeContextState updates |out| with material previously serialized by
|
||||
// SerializeContextState.
|
||||
bool DeserializeContextState(CBS *in, SSL_CTX *out);
|
||||
|
||||
#endif // HEADER_TEST_STATE
|
||||
|
Loading…
Reference in New Issue
Block a user