|
|
|
/* Copyright (c) 2014, Google Inc.
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
|
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
|
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
|
|
|
|
|
|
|
#include "test_config.h"
|
|
|
|
|
|
|
|
#include <assert.h>
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
#include <algorithm>
|
|
|
|
#include <functional>
|
|
|
|
#include <limits>
|
|
|
|
#include <memory>
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
#include <type_traits>
|
|
|
|
|
|
|
|
#include <openssl/base64.h>
|
|
|
|
#include <openssl/hmac.h>
|
|
|
|
#include <openssl/hpke.h>
|
|
|
|
#include <openssl/rand.h>
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
#include <openssl/span.h>
|
|
|
|
#include <openssl/ssl.h>
|
|
|
|
|
|
|
|
#include "../../crypto/internal.h"
|
|
|
|
#include "../internal.h"
|
|
|
|
#include "handshake_util.h"
|
|
|
|
#include "mock_quic_transport.h"
|
|
|
|
#include "test_state.h"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
template <typename Config>
|
|
|
|
struct Flag {
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
const char *name;
|
|
|
|
bool has_param;
|
Pass IPv6 vs IPv4 down to the shim
The runner currently tries to listen on IPv6 and then falls back to IPv4
on error. The shim does the same. If they pick different ones, this
breaks down.
Normally, fallback happens because the system doesn't have IPv6, and
both sides will make the same decision. But if binding to IPv6 fails for
other reasons, they may mismatch. We're observing them fail due to what
seems to port exhaustion. When this happens, shim and runner don't
agree on the same address family.
Instead, just tell the shim which address to connect to. This doesn't
fix the underlying port exhaustion problem, but it does seem to fix the
flakes. Although given we are still exhausting ports and falling back to
IPv4, it doesn't truly fix it. Later CLs will address port exhaustion by
using a single server port.
This changes the runner <-> shim protocol, but hopefully in a fairly
obvious way that others using BoGo can easily follow. It shouldn't break
our cross-version tests because we keep runner and shim at the same
versio there.
To avoid needing to make an incompatible change to the shim <->
handshaker protocol, which would impact our cross-version tests, this
introduces a mechanism for the shim omit flags when talking to the
handshaker. The handshaker doesn't need to know how to connect to the
runner.
Also print the error string on Windows. Sadly this is a bit tedious.
Change-Id: Ic8bda9a854a115c206c05a659a2e34f544b844a6
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60885
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
1 year ago
|
|
|
// skip_handshaker, if true, causes this flag to be skipped when
|
|
|
|
// forwarding flags to the handshaker. This should be used with flags
|
|
|
|
// that only impact connecting to the runner.
|
|
|
|
bool skip_handshaker;
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
// If |has_param| is false, |param| will be nullptr.
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
std::function<bool(Config *config, const char *param)> set_param;
|
|
|
|
};
|
|
|
|
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
template <typename Config>
|
|
|
|
Flag<Config> BoolFlag(const char *name, bool Config::*field,
|
|
|
|
bool skip_handshaker = false) {
|
|
|
|
return Flag<Config>{name, false, skip_handshaker,
|
|
|
|
[=](Config *config, const char *) -> bool {
|
|
|
|
config->*field = true;
|
|
|
|
return true;
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
template <typename T>
|
|
|
|
bool StringToInt(T *out, const char *str) {
|
|
|
|
static_assert(std::is_integral<T>::value, "not an integral type");
|
|
|
|
|
|
|
|
// |strtoull| allows leading '-' with wraparound. Additionally, both
|
|
|
|
// functions accept empty strings and leading whitespace.
|
|
|
|
if (!OPENSSL_isdigit(static_cast<unsigned char>(*str)) &&
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
(!std::is_signed<T>::value || *str != '-')) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
errno = 0;
|
|
|
|
char *end;
|
|
|
|
if (std::is_signed<T>::value) {
|
|
|
|
static_assert(sizeof(T) <= sizeof(long long),
|
|
|
|
"type too large for long long");
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
long long value = strtoll(str, &end, 10);
|
|
|
|
if (value < static_cast<long long>(std::numeric_limits<T>::min()) ||
|
|
|
|
value > static_cast<long long>(std::numeric_limits<T>::max())) {
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*out = static_cast<T>(value);
|
|
|
|
} else {
|
|
|
|
static_assert(sizeof(T) <= sizeof(unsigned long long),
|
|
|
|
"type too large for unsigned long long");
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
unsigned long long value = strtoull(str, &end, 10);
|
|
|
|
if (value >
|
|
|
|
static_cast<unsigned long long>(std::numeric_limits<T>::max())) {
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*out = static_cast<T>(value);
|
|
|
|
}
|
|
|
|
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
// Check for overflow and that the whole input was consumed.
|
|
|
|
return errno != ERANGE && *end == '\0';
|
|
|
|
}
|
|
|
|
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
template <typename Config, typename T>
|
|
|
|
Flag<Config> IntFlag(const char *name, T Config::*field,
|
|
|
|
bool skip_handshaker = false) {
|
|
|
|
return Flag<Config>{name, true, skip_handshaker,
|
|
|
|
[=](Config *config, const char *param) -> bool {
|
|
|
|
return StringToInt(&(config->*field), param);
|
|
|
|
}};
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
}
|
|
|
|
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
template <typename Config, typename T>
|
|
|
|
Flag<Config> IntVectorFlag(const char *name, std::vector<T> Config::*field,
|
|
|
|
bool skip_handshaker = false) {
|
|
|
|
return Flag<Config>{name, true, skip_handshaker,
|
|
|
|
[=](Config *config, const char *param) -> bool {
|
|
|
|
T value;
|
|
|
|
if (!StringToInt(&value, param)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
(config->*field).push_back(value);
|
|
|
|
return true;
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Config>
|
|
|
|
Flag<Config> StringFlag(const char *name, std::string Config::*field,
|
|
|
|
bool skip_handshaker = false) {
|
|
|
|
return Flag<Config>{name, true, skip_handshaker,
|
|
|
|
[=](Config *config, const char *param) -> bool {
|
|
|
|
config->*field = param;
|
|
|
|
return true;
|
|
|
|
}};
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
}
|
|
|
|
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
// TODO(davidben): When we can depend on C++17 or Abseil, switch this to
|
|
|
|
// std::optional or absl::optional.
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
template <typename Config>
|
|
|
|
Flag<Config> OptionalStringFlag(const char *name,
|
|
|
|
std::unique_ptr<std::string> Config::*field,
|
|
|
|
bool skip_handshaker = false) {
|
|
|
|
return Flag<Config>{name, true, skip_handshaker,
|
|
|
|
[=](Config *config, const char *param) -> bool {
|
|
|
|
(config->*field) = std::make_unique<std::string>(param);
|
|
|
|
return true;
|
|
|
|
}};
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
}
|
|
|
|
|
|
|
|
bool DecodeBase64(std::string *out, const std::string &in) {
|
|
|
|
size_t len;
|
|
|
|
if (!EVP_DecodedLength(&len, in.size())) {
|
|
|
|
fprintf(stderr, "Invalid base64: %s.\n", in.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
std::vector<uint8_t> buf(len);
|
|
|
|
if (!EVP_DecodeBase64(buf.data(), &len, buf.size(),
|
|
|
|
reinterpret_cast<const uint8_t *>(in.data()),
|
|
|
|
in.size())) {
|
|
|
|
fprintf(stderr, "Invalid base64: %s.\n", in.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
out->assign(reinterpret_cast<const char *>(buf.data()), len);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
template <typename Config>
|
|
|
|
Flag<Config> Base64Flag(const char *name, std::string Config::*field,
|
|
|
|
bool skip_handshaker = false) {
|
|
|
|
return Flag<Config>{name, true, skip_handshaker,
|
|
|
|
[=](Config *config, const char *param) -> bool {
|
|
|
|
return DecodeBase64(&(config->*field), param);
|
|
|
|
}};
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
}
|
|
|
|
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
template <typename Config>
|
|
|
|
Flag<Config> Base64VectorFlag(const char *name,
|
|
|
|
std::vector<std::string> Config::*field,
|
|
|
|
bool skip_handshaker = false) {
|
|
|
|
return Flag<Config>{name, true, skip_handshaker,
|
|
|
|
[=](Config *config, const char *param) -> bool {
|
|
|
|
std::string value;
|
|
|
|
if (!DecodeBase64(&value, param)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
(config->*field).push_back(std::move(value));
|
|
|
|
return true;
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Config>
|
|
|
|
Flag<Config> StringPairVectorFlag(
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
const char *name,
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
std::vector<std::pair<std::string, std::string>> Config::*field,
|
Pass IPv6 vs IPv4 down to the shim
The runner currently tries to listen on IPv6 and then falls back to IPv4
on error. The shim does the same. If they pick different ones, this
breaks down.
Normally, fallback happens because the system doesn't have IPv6, and
both sides will make the same decision. But if binding to IPv6 fails for
other reasons, they may mismatch. We're observing them fail due to what
seems to port exhaustion. When this happens, shim and runner don't
agree on the same address family.
Instead, just tell the shim which address to connect to. This doesn't
fix the underlying port exhaustion problem, but it does seem to fix the
flakes. Although given we are still exhausting ports and falling back to
IPv4, it doesn't truly fix it. Later CLs will address port exhaustion by
using a single server port.
This changes the runner <-> shim protocol, but hopefully in a fairly
obvious way that others using BoGo can easily follow. It shouldn't break
our cross-version tests because we keep runner and shim at the same
versio there.
To avoid needing to make an incompatible change to the shim <->
handshaker protocol, which would impact our cross-version tests, this
introduces a mechanism for the shim omit flags when talking to the
handshaker. The handshaker doesn't need to know how to connect to the
runner.
Also print the error string on Windows. Sadly this is a bit tedious.
Change-Id: Ic8bda9a854a115c206c05a659a2e34f544b844a6
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60885
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
1 year ago
|
|
|
bool skip_handshaker = false) {
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
return Flag<Config>{
|
|
|
|
name, true, skip_handshaker,
|
|
|
|
[=](Config *config, const char *param) -> bool {
|
|
|
|
const char *comma = strchr(param, ',');
|
|
|
|
if (!comma) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
(config->*field)
|
|
|
|
.push_back(std::make_pair(std::string(param, comma - param),
|
|
|
|
std::string(comma + 1)));
|
|
|
|
return true;
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
Flag<TestConfig> NewCredentialFlag(const char *name,
|
|
|
|
CredentialConfigType type) {
|
|
|
|
return Flag<TestConfig>{name, /*has_param=*/false, /*skip_handshaker=*/false,
|
|
|
|
[=](TestConfig *config, const char *param) -> bool {
|
|
|
|
config->credentials.emplace_back();
|
|
|
|
config->credentials.back().type = type;
|
|
|
|
return true;
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
Flag<TestConfig> CredentialFlagWithDefault(Flag<TestConfig> default_flag,
|
|
|
|
Flag<CredentialConfig> flag) {
|
|
|
|
BSSL_CHECK(strcmp(default_flag.name, flag.name) == 0);
|
|
|
|
BSSL_CHECK(default_flag.has_param == flag.has_param);
|
|
|
|
return Flag<TestConfig>{flag.name, flag.has_param, /*skip_handshaker=*/false,
|
|
|
|
[=](TestConfig *config, const char *param) -> bool {
|
|
|
|
if (config->credentials.empty()) {
|
|
|
|
return default_flag.set_param(config, param);
|
|
|
|
}
|
|
|
|
return flag.set_param(&config->credentials.back(),
|
|
|
|
param);
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
Flag<TestConfig> CredentialFlag(Flag<CredentialConfig> flag) {
|
|
|
|
return Flag<TestConfig>{flag.name, flag.has_param, /*skip_handshaker=*/false,
|
|
|
|
[=](TestConfig *config, const char *param) -> bool {
|
|
|
|
if (config->credentials.empty()) {
|
|
|
|
fprintf(stderr, "No credentials configured.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return flag.set_param(&config->credentials.back(),
|
|
|
|
param);
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
struct FlagNameComparator {
|
|
|
|
template <typename Config>
|
|
|
|
bool operator()(const Flag<Config> &flag1, const Flag<Config> &flag2) const {
|
|
|
|
return strcmp(flag1.name, flag2.name) < 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Config>
|
|
|
|
bool operator()(const Flag<Config> &flag, const char *name) const {
|
|
|
|
return strcmp(flag.name, name) < 0;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const Flag<TestConfig> *FindFlag(const char *name) {
|
|
|
|
static const std::vector<Flag<TestConfig>> flags = [] {
|
|
|
|
std::vector<Flag<TestConfig>> ret = {
|
|
|
|
IntFlag("-port", &TestConfig::port, /*skip_handshaker=*/true),
|
|
|
|
BoolFlag("-ipv6", &TestConfig::ipv6, /*skip_handshaker=*/true),
|
|
|
|
IntFlag("-shim-id", &TestConfig::shim_id, /*skip_handshaker=*/true),
|
|
|
|
BoolFlag("-server", &TestConfig::is_server),
|
|
|
|
BoolFlag("-dtls", &TestConfig::is_dtls),
|
|
|
|
BoolFlag("-quic", &TestConfig::is_quic),
|
|
|
|
IntFlag("-resume-count", &TestConfig::resume_count),
|
|
|
|
StringFlag("-write-settings", &TestConfig::write_settings),
|
|
|
|
BoolFlag("-fallback-scsv", &TestConfig::fallback_scsv),
|
|
|
|
IntVectorFlag("-verify-prefs", &TestConfig::verify_prefs),
|
|
|
|
IntVectorFlag("-expect-peer-verify-pref",
|
|
|
|
&TestConfig::expect_peer_verify_prefs),
|
|
|
|
IntVectorFlag("-curves", &TestConfig::curves),
|
|
|
|
StringFlag("-trust-cert", &TestConfig::trust_cert),
|
|
|
|
StringFlag("-expect-server-name", &TestConfig::expect_server_name),
|
|
|
|
BoolFlag("-enable-ech-grease", &TestConfig::enable_ech_grease),
|
|
|
|
Base64VectorFlag("-ech-server-config", &TestConfig::ech_server_configs),
|
|
|
|
Base64VectorFlag("-ech-server-key", &TestConfig::ech_server_keys),
|
|
|
|
IntVectorFlag("-ech-is-retry-config", &TestConfig::ech_is_retry_config),
|
|
|
|
BoolFlag("-expect-ech-accept", &TestConfig::expect_ech_accept),
|
|
|
|
StringFlag("-expect-ech-name-override",
|
|
|
|
&TestConfig::expect_ech_name_override),
|
|
|
|
BoolFlag("-expect-no-ech-name-override",
|
|
|
|
&TestConfig::expect_no_ech_name_override),
|
|
|
|
Base64Flag("-expect-ech-retry-configs",
|
|
|
|
&TestConfig::expect_ech_retry_configs),
|
|
|
|
BoolFlag("-expect-no-ech-retry-configs",
|
|
|
|
&TestConfig::expect_no_ech_retry_configs),
|
|
|
|
Base64Flag("-ech-config-list", &TestConfig::ech_config_list),
|
|
|
|
Base64Flag("-expect-certificate-types",
|
|
|
|
&TestConfig::expect_certificate_types),
|
|
|
|
BoolFlag("-require-any-client-certificate",
|
|
|
|
&TestConfig::require_any_client_certificate),
|
|
|
|
StringFlag("-advertise-npn", &TestConfig::advertise_npn),
|
|
|
|
StringFlag("-expect-next-proto", &TestConfig::expect_next_proto),
|
|
|
|
BoolFlag("-false-start", &TestConfig::false_start),
|
|
|
|
StringFlag("-select-next-proto", &TestConfig::select_next_proto),
|
|
|
|
BoolFlag("-async", &TestConfig::async),
|
|
|
|
BoolFlag("-write-different-record-sizes",
|
|
|
|
&TestConfig::write_different_record_sizes),
|
|
|
|
BoolFlag("-cbc-record-splitting", &TestConfig::cbc_record_splitting),
|
|
|
|
BoolFlag("-partial-write", &TestConfig::partial_write),
|
|
|
|
BoolFlag("-no-tls13", &TestConfig::no_tls13),
|
|
|
|
BoolFlag("-no-tls12", &TestConfig::no_tls12),
|
|
|
|
BoolFlag("-no-tls11", &TestConfig::no_tls11),
|
|
|
|
BoolFlag("-no-tls1", &TestConfig::no_tls1),
|
|
|
|
BoolFlag("-no-ticket", &TestConfig::no_ticket),
|
|
|
|
Base64Flag("-expect-channel-id", &TestConfig::expect_channel_id),
|
|
|
|
BoolFlag("-enable-channel-id", &TestConfig::enable_channel_id),
|
|
|
|
StringFlag("-send-channel-id", &TestConfig::send_channel_id),
|
|
|
|
BoolFlag("-shim-writes-first", &TestConfig::shim_writes_first),
|
|
|
|
StringFlag("-host-name", &TestConfig::host_name),
|
|
|
|
StringFlag("-advertise-alpn", &TestConfig::advertise_alpn),
|
|
|
|
StringFlag("-expect-alpn", &TestConfig::expect_alpn),
|
|
|
|
StringFlag("-expect-advertised-alpn",
|
|
|
|
&TestConfig::expect_advertised_alpn),
|
|
|
|
StringFlag("-select-alpn", &TestConfig::select_alpn),
|
|
|
|
BoolFlag("-decline-alpn", &TestConfig::decline_alpn),
|
|
|
|
BoolFlag("-reject-alpn", &TestConfig::reject_alpn),
|
|
|
|
BoolFlag("-select-empty-alpn", &TestConfig::select_empty_alpn),
|
|
|
|
BoolFlag("-defer-alps", &TestConfig::defer_alps),
|
|
|
|
StringPairVectorFlag("-application-settings",
|
|
|
|
&TestConfig::application_settings),
|
|
|
|
OptionalStringFlag("-expect-peer-application-settings",
|
|
|
|
&TestConfig::expect_peer_application_settings),
|
|
|
|
BoolFlag("-alps-use-new-codepoint",
|
|
|
|
&TestConfig::alps_use_new_codepoint),
|
|
|
|
Base64Flag("-quic-transport-params",
|
|
|
|
&TestConfig::quic_transport_params),
|
|
|
|
Base64Flag("-expect-quic-transport-params",
|
|
|
|
&TestConfig::expect_quic_transport_params),
|
|
|
|
IntFlag("-quic-use-legacy-codepoint",
|
|
|
|
&TestConfig::quic_use_legacy_codepoint),
|
|
|
|
BoolFlag("-expect-session-miss", &TestConfig::expect_session_miss),
|
|
|
|
BoolFlag("-expect-extended-master-secret",
|
|
|
|
&TestConfig::expect_extended_master_secret),
|
|
|
|
StringFlag("-psk", &TestConfig::psk),
|
|
|
|
StringFlag("-psk-identity", &TestConfig::psk_identity),
|
|
|
|
StringFlag("-srtp-profiles", &TestConfig::srtp_profiles),
|
|
|
|
BoolFlag("-enable-ocsp-stapling", &TestConfig::enable_ocsp_stapling),
|
|
|
|
BoolFlag("-enable-signed-cert-timestamps",
|
|
|
|
&TestConfig::enable_signed_cert_timestamps),
|
|
|
|
Base64Flag("-expect-signed-cert-timestamps",
|
|
|
|
&TestConfig::expect_signed_cert_timestamps),
|
|
|
|
IntFlag("-min-version", &TestConfig::min_version),
|
|
|
|
IntFlag("-max-version", &TestConfig::max_version),
|
|
|
|
IntFlag("-expect-version", &TestConfig::expect_version),
|
|
|
|
IntFlag("-mtu", &TestConfig::mtu),
|
|
|
|
BoolFlag("-implicit-handshake", &TestConfig::implicit_handshake),
|
|
|
|
BoolFlag("-use-early-callback", &TestConfig::use_early_callback),
|
|
|
|
BoolFlag("-fail-early-callback", &TestConfig::fail_early_callback),
|
|
|
|
BoolFlag("-install-ddos-callback", &TestConfig::install_ddos_callback),
|
|
|
|
BoolFlag("-fail-ddos-callback", &TestConfig::fail_ddos_callback),
|
|
|
|
BoolFlag("-fail-cert-callback", &TestConfig::fail_cert_callback),
|
|
|
|
StringFlag("-cipher", &TestConfig::cipher),
|
|
|
|
BoolFlag("-handshake-never-done", &TestConfig::handshake_never_done),
|
|
|
|
IntFlag("-export-keying-material", &TestConfig::export_keying_material),
|
|
|
|
StringFlag("-export-label", &TestConfig::export_label),
|
|
|
|
StringFlag("-export-context", &TestConfig::export_context),
|
|
|
|
BoolFlag("-use-export-context", &TestConfig::use_export_context),
|
|
|
|
BoolFlag("-tls-unique", &TestConfig::tls_unique),
|
|
|
|
BoolFlag("-expect-ticket-renewal", &TestConfig::expect_ticket_renewal),
|
|
|
|
BoolFlag("-expect-no-session", &TestConfig::expect_no_session),
|
|
|
|
BoolFlag("-expect-ticket-supports-early-data",
|
|
|
|
&TestConfig::expect_ticket_supports_early_data),
|
|
|
|
BoolFlag("-expect-accept-early-data",
|
|
|
|
&TestConfig::expect_accept_early_data),
|
|
|
|
BoolFlag("-expect-reject-early-data",
|
|
|
|
&TestConfig::expect_reject_early_data),
|
|
|
|
BoolFlag("-expect-no-offer-early-data",
|
|
|
|
&TestConfig::expect_no_offer_early_data),
|
|
|
|
BoolFlag("-use-ticket-callback", &TestConfig::use_ticket_callback),
|
|
|
|
BoolFlag("-renew-ticket", &TestConfig::renew_ticket),
|
|
|
|
BoolFlag("-enable-early-data", &TestConfig::enable_early_data),
|
|
|
|
Base64Flag("-expect-ocsp-response", &TestConfig::expect_ocsp_response),
|
|
|
|
BoolFlag("-check-close-notify", &TestConfig::check_close_notify),
|
|
|
|
BoolFlag("-shim-shuts-down", &TestConfig::shim_shuts_down),
|
|
|
|
BoolFlag("-verify-fail", &TestConfig::verify_fail),
|
|
|
|
BoolFlag("-verify-peer", &TestConfig::verify_peer),
|
|
|
|
BoolFlag("-verify-peer-if-no-obc", &TestConfig::verify_peer_if_no_obc),
|
|
|
|
BoolFlag("-expect-verify-result", &TestConfig::expect_verify_result),
|
|
|
|
IntFlag("-expect-total-renegotiations",
|
|
|
|
&TestConfig::expect_total_renegotiations),
|
|
|
|
BoolFlag("-renegotiate-once", &TestConfig::renegotiate_once),
|
|
|
|
BoolFlag("-renegotiate-freely", &TestConfig::renegotiate_freely),
|
|
|
|
BoolFlag("-renegotiate-ignore", &TestConfig::renegotiate_ignore),
|
|
|
|
BoolFlag("-renegotiate-explicit", &TestConfig::renegotiate_explicit),
|
|
|
|
BoolFlag("-forbid-renegotiation-after-handshake",
|
|
|
|
&TestConfig::forbid_renegotiation_after_handshake),
|
|
|
|
IntFlag("-expect-peer-signature-algorithm",
|
|
|
|
&TestConfig::expect_peer_signature_algorithm),
|
|
|
|
IntFlag("-expect-curve-id", &TestConfig::expect_curve_id),
|
|
|
|
BoolFlag("-use-old-client-cert-callback",
|
|
|
|
&TestConfig::use_old_client_cert_callback),
|
|
|
|
IntFlag("-initial-timeout-duration-ms",
|
|
|
|
&TestConfig::initial_timeout_duration_ms),
|
|
|
|
StringFlag("-use-client-ca-list", &TestConfig::use_client_ca_list),
|
|
|
|
StringFlag("-expect-client-ca-list",
|
|
|
|
&TestConfig::expect_client_ca_list),
|
|
|
|
BoolFlag("-send-alert", &TestConfig::send_alert),
|
|
|
|
BoolFlag("-peek-then-read", &TestConfig::peek_then_read),
|
|
|
|
BoolFlag("-enable-grease", &TestConfig::enable_grease),
|
|
|
|
BoolFlag("-permute-extensions", &TestConfig::permute_extensions),
|
|
|
|
IntFlag("-max-cert-list", &TestConfig::max_cert_list),
|
|
|
|
Base64Flag("-ticket-key", &TestConfig::ticket_key),
|
|
|
|
BoolFlag("-use-exporter-between-reads",
|
|
|
|
&TestConfig::use_exporter_between_reads),
|
|
|
|
IntFlag("-expect-cipher-aes", &TestConfig::expect_cipher_aes),
|
|
|
|
IntFlag("-expect-cipher-no-aes", &TestConfig::expect_cipher_no_aes),
|
|
|
|
IntFlag("-expect-cipher", &TestConfig::expect_cipher),
|
|
|
|
StringFlag("-expect-peer-cert-file",
|
|
|
|
&TestConfig::expect_peer_cert_file),
|
|
|
|
IntFlag("-resumption-delay", &TestConfig::resumption_delay),
|
|
|
|
BoolFlag("-retain-only-sha256-client-cert",
|
|
|
|
&TestConfig::retain_only_sha256_client_cert),
|
|
|
|
BoolFlag("-expect-sha256-client-cert",
|
|
|
|
&TestConfig::expect_sha256_client_cert),
|
|
|
|
BoolFlag("-read-with-unfinished-write",
|
|
|
|
&TestConfig::read_with_unfinished_write),
|
|
|
|
BoolFlag("-expect-secure-renegotiation",
|
|
|
|
&TestConfig::expect_secure_renegotiation),
|
|
|
|
BoolFlag("-expect-no-secure-renegotiation",
|
|
|
|
&TestConfig::expect_no_secure_renegotiation),
|
|
|
|
IntFlag("-max-send-fragment", &TestConfig::max_send_fragment),
|
|
|
|
IntFlag("-read-size", &TestConfig::read_size),
|
|
|
|
BoolFlag("-expect-session-id", &TestConfig::expect_session_id),
|
|
|
|
BoolFlag("-expect-no-session-id", &TestConfig::expect_no_session_id),
|
|
|
|
IntFlag("-expect-ticket-age-skew", &TestConfig::expect_ticket_age_skew),
|
|
|
|
BoolFlag("-no-op-extra-handshake", &TestConfig::no_op_extra_handshake),
|
|
|
|
BoolFlag("-handshake-twice", &TestConfig::handshake_twice),
|
|
|
|
BoolFlag("-allow-unknown-alpn-protos",
|
|
|
|
&TestConfig::allow_unknown_alpn_protos),
|
|
|
|
BoolFlag("-use-custom-verify-callback",
|
|
|
|
&TestConfig::use_custom_verify_callback),
|
|
|
|
StringFlag("-expect-msg-callback", &TestConfig::expect_msg_callback),
|
|
|
|
BoolFlag("-allow-false-start-without-alpn",
|
|
|
|
&TestConfig::allow_false_start_without_alpn),
|
|
|
|
BoolFlag("-handoff", &TestConfig::handoff),
|
|
|
|
BoolFlag("-handshake-hints", &TestConfig::handshake_hints),
|
|
|
|
BoolFlag("-allow-hint-mismatch", &TestConfig::allow_hint_mismatch),
|
|
|
|
BoolFlag("-use-ocsp-callback", &TestConfig::use_ocsp_callback),
|
|
|
|
BoolFlag("-set-ocsp-in-callback", &TestConfig::set_ocsp_in_callback),
|
|
|
|
BoolFlag("-decline-ocsp-callback", &TestConfig::decline_ocsp_callback),
|
|
|
|
BoolFlag("-fail-ocsp-callback", &TestConfig::fail_ocsp_callback),
|
|
|
|
BoolFlag("-install-cert-compression-algs",
|
|
|
|
&TestConfig::install_cert_compression_algs),
|
|
|
|
IntFlag("-install-one-cert-compression-alg",
|
|
|
|
&TestConfig::install_one_cert_compression_alg),
|
|
|
|
BoolFlag("-reverify-on-resume", &TestConfig::reverify_on_resume),
|
|
|
|
BoolFlag("-ignore-rsa-key-usage", &TestConfig::ignore_rsa_key_usage),
|
|
|
|
BoolFlag("-expect-key-usage-invalid",
|
|
|
|
&TestConfig::expect_key_usage_invalid),
|
|
|
|
BoolFlag("-is-handshaker-supported",
|
|
|
|
&TestConfig::is_handshaker_supported),
|
|
|
|
BoolFlag("-handshaker-resume", &TestConfig::handshaker_resume),
|
|
|
|
StringFlag("-handshaker-path", &TestConfig::handshaker_path),
|
|
|
|
BoolFlag("-jdk11-workaround", &TestConfig::jdk11_workaround),
|
|
|
|
BoolFlag("-server-preference", &TestConfig::server_preference),
|
|
|
|
BoolFlag("-export-traffic-secrets",
|
|
|
|
&TestConfig::export_traffic_secrets),
|
|
|
|
BoolFlag("-key-update", &TestConfig::key_update),
|
|
|
|
StringFlag("-expect-early-data-reason",
|
|
|
|
&TestConfig::expect_early_data_reason),
|
|
|
|
BoolFlag("-expect-hrr", &TestConfig::expect_hrr),
|
|
|
|
BoolFlag("-expect-no-hrr", &TestConfig::expect_no_hrr),
|
|
|
|
BoolFlag("-wait-for-debugger", &TestConfig::wait_for_debugger),
|
|
|
|
StringFlag("-quic-early-data-context",
|
|
|
|
&TestConfig::quic_early_data_context),
|
|
|
|
IntFlag("-early-write-after-message",
|
|
|
|
&TestConfig::early_write_after_message),
|
|
|
|
BoolFlag("-fips-202205", &TestConfig::fips_202205),
|
|
|
|
BoolFlag("-wpa-202304", &TestConfig::wpa_202304),
|
|
|
|
BoolFlag("-no-check-client-certificate-type",
|
|
|
|
&TestConfig::no_check_client_certificate_type),
|
|
|
|
BoolFlag("-no-check-ecdsa-curve", &TestConfig::no_check_ecdsa_curve),
|
|
|
|
IntFlag("-expect-selected-credential",
|
|
|
|
&TestConfig::expect_selected_credential),
|
|
|
|
// Credential flags are stateful. First, use one of the
|
|
|
|
// -new-*-credential flags to introduce a new credential. Then the flags
|
|
|
|
// below switch from acting on the default credential to the newly-added
|
|
|
|
// one. Repeat this process to continue adding them.
|
|
|
|
NewCredentialFlag("-new-x509-credential", CredentialConfigType::kX509),
|
|
|
|
NewCredentialFlag("-new-delegated-credential",
|
|
|
|
CredentialConfigType::kDelegated),
|
|
|
|
CredentialFlagWithDefault(
|
|
|
|
StringFlag("-cert-file", &TestConfig::cert_file),
|
|
|
|
StringFlag("-cert-file", &CredentialConfig::cert_file)),
|
|
|
|
CredentialFlagWithDefault(
|
|
|
|
StringFlag("-key-file", &TestConfig::key_file),
|
|
|
|
StringFlag("-key-file", &CredentialConfig::key_file)),
|
|
|
|
CredentialFlagWithDefault(
|
|
|
|
IntVectorFlag("-signing-prefs", &TestConfig::signing_prefs),
|
|
|
|
IntVectorFlag("-signing-prefs", &CredentialConfig::signing_prefs)),
|
|
|
|
CredentialFlag(Base64Flag("-delegated-credential",
|
|
|
|
&CredentialConfig::delegated_credential)),
|
|
|
|
CredentialFlagWithDefault(
|
|
|
|
Base64Flag("-ocsp-response", &TestConfig::ocsp_response),
|
|
|
|
Base64Flag("-ocsp-response", &CredentialConfig::ocsp_response)),
|
|
|
|
CredentialFlagWithDefault(
|
|
|
|
Base64Flag("-signed-cert-timestamps",
|
|
|
|
&TestConfig::signed_cert_timestamps),
|
|
|
|
Base64Flag("-signed-cert-timestamps",
|
|
|
|
&CredentialConfig::signed_cert_timestamps)),
|
|
|
|
};
|
|
|
|
std::sort(ret.begin(), ret.end(), FlagNameComparator{});
|
|
|
|
return ret;
|
|
|
|
}();
|
|
|
|
auto iter =
|
|
|
|
std::lower_bound(flags.begin(), flags.end(), name, FlagNameComparator{});
|
|
|
|
if (iter == flags.end() || strcmp(iter->name, name) != 0) {
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
return nullptr;
|
|
|
|
}
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
return &*iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemovePrefix checks if |*str| begins with |prefix| + "-". If so, it advances
|
|
|
|
// |*str| past |prefix| (but not past the "-") and returns true. Otherwise, it
|
|
|
|
// returns false and leaves |*str| unmodified.
|
|
|
|
bool RemovePrefix(const char **str, const char *prefix) {
|
|
|
|
size_t prefix_len = strlen(prefix);
|
|
|
|
if (strncmp(*str, prefix, strlen(prefix)) == 0 && (*str)[prefix_len] == '-') {
|
|
|
|
*str += strlen(prefix);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
bool ParseConfig(int argc, char **argv, bool is_shim,
|
|
|
|
TestConfig *out_initial,
|
|
|
|
TestConfig *out_resume,
|
|
|
|
TestConfig *out_retry) {
|
|
|
|
for (int i = 0; i < argc; i++) {
|
|
|
|
bool skip = false;
|
Pass IPv6 vs IPv4 down to the shim
The runner currently tries to listen on IPv6 and then falls back to IPv4
on error. The shim does the same. If they pick different ones, this
breaks down.
Normally, fallback happens because the system doesn't have IPv6, and
both sides will make the same decision. But if binding to IPv6 fails for
other reasons, they may mismatch. We're observing them fail due to what
seems to port exhaustion. When this happens, shim and runner don't
agree on the same address family.
Instead, just tell the shim which address to connect to. This doesn't
fix the underlying port exhaustion problem, but it does seem to fix the
flakes. Although given we are still exhausting ports and falling back to
IPv4, it doesn't truly fix it. Later CLs will address port exhaustion by
using a single server port.
This changes the runner <-> shim protocol, but hopefully in a fairly
obvious way that others using BoGo can easily follow. It shouldn't break
our cross-version tests because we keep runner and shim at the same
versio there.
To avoid needing to make an incompatible change to the shim <->
handshaker protocol, which would impact our cross-version tests, this
introduces a mechanism for the shim omit flags when talking to the
handshaker. The handshaker doesn't need to know how to connect to the
runner.
Also print the error string on Windows. Sadly this is a bit tedious.
Change-Id: Ic8bda9a854a115c206c05a659a2e34f544b844a6
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60885
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
1 year ago
|
|
|
const char *arg = argv[i];
|
|
|
|
const char *name = arg;
|
|
|
|
|
|
|
|
// -on-shim and -on-handshaker prefixes enable flags only on the shim or
|
|
|
|
// handshaker.
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
if (RemovePrefix(&name, "-on-shim")) {
|
|
|
|
if (!is_shim) {
|
|
|
|
skip = true;
|
|
|
|
}
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
} else if (RemovePrefix(&name, "-on-handshaker")) {
|
|
|
|
if (is_shim) {
|
|
|
|
skip = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The following prefixes allow different configurations for each of the
|
|
|
|
// initial, resumption, and 0-RTT retry handshakes.
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
TestConfig *out = nullptr;
|
|
|
|
if (RemovePrefix(&name, "-on-initial")) {
|
|
|
|
out = out_initial;
|
|
|
|
} else if (RemovePrefix(&name, "-on-resume")) {
|
|
|
|
out = out_resume;
|
|
|
|
} else if (RemovePrefix(&name, "-on-retry")) {
|
|
|
|
out = out_retry;
|
|
|
|
}
|
|
|
|
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
const Flag<TestConfig> *flag = FindFlag(name);
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
if (flag == nullptr) {
|
|
|
|
fprintf(stderr, "Unrecognized flag: %s\n", name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *param = nullptr;
|
|
|
|
if (flag->has_param) {
|
|
|
|
if (i >= argc) {
|
|
|
|
fprintf(stderr, "Missing parameter for %s\n", name);
|
|
|
|
return false;
|
|
|
|
}
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
i++;
|
|
|
|
param = argv[i];
|
|
|
|
}
|
|
|
|
|
Pass IPv6 vs IPv4 down to the shim
The runner currently tries to listen on IPv6 and then falls back to IPv4
on error. The shim does the same. If they pick different ones, this
breaks down.
Normally, fallback happens because the system doesn't have IPv6, and
both sides will make the same decision. But if binding to IPv6 fails for
other reasons, they may mismatch. We're observing them fail due to what
seems to port exhaustion. When this happens, shim and runner don't
agree on the same address family.
Instead, just tell the shim which address to connect to. This doesn't
fix the underlying port exhaustion problem, but it does seem to fix the
flakes. Although given we are still exhausting ports and falling back to
IPv4, it doesn't truly fix it. Later CLs will address port exhaustion by
using a single server port.
This changes the runner <-> shim protocol, but hopefully in a fairly
obvious way that others using BoGo can easily follow. It shouldn't break
our cross-version tests because we keep runner and shim at the same
versio there.
To avoid needing to make an incompatible change to the shim <->
handshaker protocol, which would impact our cross-version tests, this
introduces a mechanism for the shim omit flags when talking to the
handshaker. The handshaker doesn't need to know how to connect to the
runner.
Also print the error string on Windows. Sadly this is a bit tedious.
Change-Id: Ic8bda9a854a115c206c05a659a2e34f544b844a6
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60885
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
1 year ago
|
|
|
if (!flag->skip_handshaker) {
|
|
|
|
out_initial->handshaker_args.push_back(arg);
|
|
|
|
if (flag->has_param) {
|
|
|
|
out_initial->handshaker_args.push_back(param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Rewrite bssl_shim command-line parser.
The command-line parser is slightly showing its age: first, it is hard
to add new integral types, such as uint16_t, which is getting in the way
of fixing some of the -Wformat-signedness errors. Second, the parameter
extraction logic and skipping logic is duplicated in every type.
While I'm here, use a binary search to look up the flag, since we have
rather a lot of them. With more C++ template tricks, we could avoid the
std::function, but that seemed more trouble than was worth it,
especially since, prior to C++17, it's a little hard to convince
template argument deduction to infer one of the parameters.
Change-Id: I208f89d46371b31fc8b44487725296bcd9d7c8e7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50769
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
3 years ago
|
|
|
if (!skip) {
|
|
|
|
if (out != nullptr) {
|
|
|
|
if (!flag->set_param(out, param)) {
|
|
|
|
fprintf(stderr, "Invalid parameter for %s: %s\n", name, param);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Unprefixed flags apply to all three.
|
|
|
|
if (!flag->set_param(out_initial, param) ||
|
|
|
|
!flag->set_param(out_resume, param) ||
|
|
|
|
!flag->set_param(out_retry, param)) {
|
|
|
|
fprintf(stderr, "Invalid parameter for %s: %s\n", name, param);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Pass IPv6 vs IPv4 down to the shim
The runner currently tries to listen on IPv6 and then falls back to IPv4
on error. The shim does the same. If they pick different ones, this
breaks down.
Normally, fallback happens because the system doesn't have IPv6, and
both sides will make the same decision. But if binding to IPv6 fails for
other reasons, they may mismatch. We're observing them fail due to what
seems to port exhaustion. When this happens, shim and runner don't
agree on the same address family.
Instead, just tell the shim which address to connect to. This doesn't
fix the underlying port exhaustion problem, but it does seem to fix the
flakes. Although given we are still exhausting ports and falling back to
IPv4, it doesn't truly fix it. Later CLs will address port exhaustion by
using a single server port.
This changes the runner <-> shim protocol, but hopefully in a fairly
obvious way that others using BoGo can easily follow. It shouldn't break
our cross-version tests because we keep runner and shim at the same
versio there.
To avoid needing to make an incompatible change to the shim <->
handshaker protocol, which would impact our cross-version tests, this
introduces a mechanism for the shim omit flags when talking to the
handshaker. The handshaker doesn't need to know how to connect to the
runner.
Also print the error string on Windows. Sadly this is a bit tedious.
Change-Id: Ic8bda9a854a115c206c05a659a2e34f544b844a6
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60885
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
1 year ago
|
|
|
out_resume->handshaker_args = out_initial->handshaker_args;
|
|
|
|
out_retry->handshaker_args = out_initial->handshaker_args;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static CRYPTO_BUFFER_POOL *BufferPool() {
|
|
|
|
static CRYPTO_BUFFER_POOL *pool = [&] {
|
|
|
|
OPENSSL_disable_malloc_failures_for_testing();
|
|
|
|
CRYPTO_BUFFER_POOL *ret = CRYPTO_BUFFER_POOL_new();
|
|
|
|
BSSL_CHECK(ret != nullptr);
|
|
|
|
OPENSSL_enable_malloc_failures_for_testing();
|
|
|
|
return ret;
|
|
|
|
}();
|
|
|
|
return pool;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int TestConfigExDataIndex() {
|
|
|
|
static int index = [&] {
|
|
|
|
OPENSSL_disable_malloc_failures_for_testing();
|
|
|
|
int ret = SSL_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr);
|
|
|
|
BSSL_CHECK(ret >= 0);
|
|
|
|
OPENSSL_enable_malloc_failures_for_testing();
|
|
|
|
return ret;
|
|
|
|
}();
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SetTestConfig(SSL *ssl, const TestConfig *config) {
|
|
|
|
return SSL_set_ex_data(ssl, TestConfigExDataIndex(), (void *)config) == 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TestConfig *GetTestConfig(const SSL *ssl) {
|
|
|
|
return static_cast<const TestConfig *>(
|
|
|
|
SSL_get_ex_data(ssl, TestConfigExDataIndex()));
|
|
|
|
}
|
|
|
|
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
struct CredentialInfo {
|
|
|
|
int number = -1;
|
|
|
|
bssl::UniquePtr<EVP_PKEY> private_key;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void CredentialInfoExDataFree(void *parent, void *ptr,
|
|
|
|
CRYPTO_EX_DATA *ad, int index, long argl,
|
|
|
|
void *argp) {
|
|
|
|
delete static_cast<CredentialInfo*>(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int CredentialInfoExDataIndex() {
|
|
|
|
static int index = [&] {
|
|
|
|
OPENSSL_disable_malloc_failures_for_testing();
|
|
|
|
int ret = SSL_CREDENTIAL_get_ex_new_index(0, nullptr, nullptr, nullptr,
|
|
|
|
CredentialInfoExDataFree);
|
|
|
|
BSSL_CHECK(ret >= 0);
|
|
|
|
OPENSSL_enable_malloc_failures_for_testing();
|
|
|
|
return ret;
|
|
|
|
}();
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const CredentialInfo *GetCredentialInfo(const SSL_CREDENTIAL *cred) {
|
|
|
|
return static_cast<const CredentialInfo *>(
|
|
|
|
SSL_CREDENTIAL_get_ex_data(cred, CredentialInfoExDataIndex()));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool SetCredentialInfo(SSL_CREDENTIAL *cred,
|
|
|
|
std::unique_ptr<CredentialInfo> info) {
|
|
|
|
if (!SSL_CREDENTIAL_set_ex_data(cred, CredentialInfoExDataIndex(),
|
|
|
|
info.get())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
info.release(); // |cred| takes ownership on success.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int LegacyOCSPCallback(SSL *ssl, void *arg) {
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
if (!SSL_is_server(ssl)) {
|
|
|
|
return !config->fail_ocsp_callback;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->ocsp_response.empty() && config->set_ocsp_in_callback &&
|
|
|
|
!SSL_set_ocsp_response(ssl, (const uint8_t *)config->ocsp_response.data(),
|
|
|
|
config->ocsp_response.size())) {
|
|
|
|
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
|
|
}
|
|
|
|
if (config->fail_ocsp_callback) {
|
|
|
|
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
|
|
}
|
|
|
|
if (config->decline_ocsp_callback) {
|
|
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
|
|
}
|
|
|
|
return SSL_TLSEXT_ERR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ServerNameCallback(SSL *ssl, int *out_alert, void *arg) {
|
|
|
|
// SNI must be accessible from the SNI callback.
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
|
|
|
if (server_name == nullptr ||
|
|
|
|
std::string(server_name) != config->expect_server_name) {
|
|
|
|
fprintf(stderr, "servername mismatch (got %s; want %s).\n", server_name,
|
|
|
|
config->expect_server_name.c_str());
|
|
|
|
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SSL_TLSEXT_ERR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int NextProtoSelectCallback(SSL *ssl, uint8_t **out, uint8_t *outlen,
|
|
|
|
const uint8_t *in, unsigned inlen,
|
|
|
|
void *arg) {
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
if (config->select_next_proto.empty()) {
|
|
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
|
|
}
|
|
|
|
|
|
|
|
*out = (uint8_t *)config->select_next_proto.data();
|
|
|
|
*outlen = config->select_next_proto.size();
|
|
|
|
return SSL_TLSEXT_ERR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out,
|
|
|
|
unsigned int *out_len, void *arg) {
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
if (config->advertise_npn.empty()) {
|
|
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config->advertise_npn.size() > UINT_MAX) {
|
|
|
|
fprintf(stderr, "NPN value too large.\n");
|
|
|
|
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*out = reinterpret_cast<const uint8_t *>(config->advertise_npn.data());
|
|
|
|
*out_len = static_cast<unsigned>(config->advertise_npn.size());
|
|
|
|
return SSL_TLSEXT_ERR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void MessageCallback(int is_write, int version, int content_type,
|
|
|
|
const void *buf, size_t len, SSL *ssl, void *arg) {
|
|
|
|
const uint8_t *buf_u8 = reinterpret_cast<const uint8_t *>(buf);
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
TestState *state = GetTestState(ssl);
|
|
|
|
if (!state->msg_callback_ok) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (content_type == SSL3_RT_HEADER) {
|
Simplify MSVC warning configuration
We've been setting /Wall on MSVC for a while, but /Wall in MSVC is
really "all". I.e., it's -Weverything in Clang and GCC, and includes
many warnings that are simply diagnostics. MSVC's own headers do not
promise to be clean under /Wall.
Rather, the equivalent of Clang and GCC's -Wall is /W4, which MSVC does
promise to pass. Under /Wall, every new MSVC release we've had to update
an ever-growing list of warning exceptions, to disable the
off-by-default warnings that were off for a reason. Switch to MSVC's
recommendations and just enable /W4.
From there, I've trimmed the exception list, now that we don't need to
re-disable disabled warnings. A few non-disabled warnings also no longer
need exceptions.
This should fix the build with VS2022, which was failing due to C5264.
C5264 flags a couple of instances in the library, but tons and tons in
MSVC headers and googletest. (The instances in the library are a little
goofy and reflect things that should have been 'inline constexpr', but
we can't rely on C++17 yet. Though I may go add a compat macro for
that.)
Fixed: 552
Change-Id: I9031c0d5bd4c7a4df1dc3040b38af9cfbcffc06e
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/56045
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
2 years ago
|
|
|
size_t header_len =
|
|
|
|
config->is_dtls ? DTLS1_RT_HEADER_LENGTH : SSL3_RT_HEADER_LENGTH;
|
|
|
|
if (len != header_len) {
|
|
|
|
fprintf(stderr, "Incorrect length for record header: %zu.\n", len);
|
|
|
|
state->msg_callback_ok = false;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
state->msg_callback_text += is_write ? "write " : "read ";
|
|
|
|
switch (content_type) {
|
|
|
|
case 0:
|
|
|
|
if (version != SSL2_VERSION) {
|
|
|
|
fprintf(stderr, "Incorrect version for V2ClientHello: %x.\n",
|
|
|
|
static_cast<unsigned>(version));
|
|
|
|
state->msg_callback_ok = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
state->msg_callback_text += "v2clienthello\n";
|
|
|
|
return;
|
|
|
|
|
|
|
|
case SSL3_RT_CLIENT_HELLO_INNER:
|
|
|
|
case SSL3_RT_HANDSHAKE: {
|
|
|
|
CBS cbs;
|
|
|
|
CBS_init(&cbs, buf_u8, len);
|
|
|
|
uint8_t type;
|
|
|
|
uint32_t msg_len;
|
|
|
|
if (!CBS_get_u8(&cbs, &type) ||
|
|
|
|
// TODO(davidben): Reporting on entire messages would be more
|
|
|
|
// consistent than fragments.
|
|
|
|
(config->is_dtls &&
|
|
|
|
!CBS_skip(&cbs, 3 /* total */ + 2 /* seq */ + 3 /* frag_off */)) ||
|
|
|
|
!CBS_get_u24(&cbs, &msg_len) || !CBS_skip(&cbs, msg_len) ||
|
|
|
|
CBS_len(&cbs) != 0) {
|
|
|
|
fprintf(stderr, "Could not parse handshake message.\n");
|
|
|
|
state->msg_callback_ok = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
char text[16];
|
|
|
|
if (content_type == SSL3_RT_CLIENT_HELLO_INNER) {
|
|
|
|
if (type != SSL3_MT_CLIENT_HELLO) {
|
|
|
|
fprintf(stderr, "Invalid header for ClientHelloInner.\n");
|
|
|
|
state->msg_callback_ok = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
state->msg_callback_text += "clienthelloinner\n";
|
|
|
|
} else {
|
|
|
|
snprintf(text, sizeof(text), "hs %d\n", type);
|
|
|
|
state->msg_callback_text += text;
|
|
|
|
if (!is_write) {
|
|
|
|
state->last_message_received = type;
|
|
|
|
}
|
Check hs->early_session, not ssl->session, for the early data limit.
ServerHello/EncryptedExtensions/Finished is logically one atomic flight
that exits the early data state, we have process each message
sequentially. Until we've processed Finished, we are still in the early
data state and must support writing data. Individual messages *are*
processed atomically, so the interesting points are before ServerHello
(already tested), after ServerHello, and after EncryptedExtensions.
The TLS 1.3 handshake internally clears ssl->session when processing
ServerHello, so getting the early data information from ssl->session
does not work. Instead, use hs->early_session, which is what other
codepaths use.
I've tested this with runner rather than ssl_test, so we can test both
post-SH and post-EE states. ssl_test would be more self-contained, since
we can directly control the API calls, but it cannot test the post-EE
state. To reduce record overhead, our production implementation packs EE
and Finished into the same record, which means the handshake will
process the two atomically. Instead, I've tested this in runner, with a
flag to partially drive the handshake before reading early data.
I've also tweaked the logic to hopefully be a little clearer.
Bug: chromium:1208784
Change-Id: Ia4901042419c5324054f97743bd1aac59ebf8f24
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47485
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
4 years ago
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case SSL3_RT_CHANGE_CIPHER_SPEC:
|
|
|
|
if (len != 1 || buf_u8[0] != 1) {
|
|
|
|
fprintf(stderr, "Invalid ChangeCipherSpec.\n");
|
|
|
|
state->msg_callback_ok = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
state->msg_callback_text += "ccs\n";
|
|
|
|
return;
|
|
|
|
|
|
|
|
case SSL3_RT_ALERT:
|
|
|
|
if (len != 2) {
|
|
|
|
fprintf(stderr, "Invalid alert.\n");
|
|
|
|
state->msg_callback_ok = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
char text[16];
|
|
|
|
snprintf(text, sizeof(text), "alert %d %d\n", buf_u8[0], buf_u8[1]);
|
|
|
|
state->msg_callback_text += text;
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "Invalid content_type: %d.\n", content_type);
|
|
|
|
state->msg_callback_ok = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int TicketKeyCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv,
|
|
|
|
EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx,
|
|
|
|
int encrypt) {
|
|
|
|
if (!encrypt) {
|
|
|
|
if (GetTestState(ssl)->ticket_decrypt_done) {
|
|
|
|
fprintf(stderr, "TicketKeyCallback called after completion.\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
GetTestState(ssl)->ticket_decrypt_done = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is just test code, so use the all-zeros key.
|
|
|
|
static const uint8_t kZeros[16] = {0};
|
|
|
|
|
|
|
|
if (encrypt) {
|
|
|
|
OPENSSL_memcpy(key_name, kZeros, sizeof(kZeros));
|
|
|
|
RAND_bytes(iv, 16);
|
|
|
|
} else if (OPENSSL_memcmp(key_name, kZeros, 16) != 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!HMAC_Init_ex(hmac_ctx, kZeros, sizeof(kZeros), EVP_sha256(), NULL) ||
|
|
|
|
!EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, kZeros, iv, encrypt)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!encrypt) {
|
|
|
|
return GetTestConfig(ssl)->renew_ticket ? 2 : 1;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int NewSessionCallback(SSL *ssl, SSL_SESSION *session) {
|
|
|
|
// This callback is called as the handshake completes. |SSL_get_session|
|
|
|
|
// must continue to work and, historically, |SSL_in_init| returned false at
|
|
|
|
// this point.
|
|
|
|
if (SSL_in_init(ssl) || SSL_get_session(ssl) == nullptr) {
|
|
|
|
fprintf(stderr, "Invalid state for NewSessionCallback.\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
GetTestState(ssl)->got_new_session = true;
|
|
|
|
GetTestState(ssl)->new_session.reset(session);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void InfoCallback(const SSL *ssl, int type, int val) {
|
|
|
|
if (type == SSL_CB_HANDSHAKE_DONE) {
|
|
|
|
if (GetTestConfig(ssl)->handshake_never_done) {
|
|
|
|
fprintf(stderr, "Handshake unexpectedly completed.\n");
|
|
|
|
// Abort before any expected error code is printed, to ensure the overall
|
|
|
|
// test fails.
|
|
|
|
abort();
|
|
|
|
}
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
|
|
|
|
// This callback is called when the handshake completes. |SSL_get_session|
|
|
|
|
// must continue to work and |SSL_in_init| must return false.
|
|
|
|
if (SSL_in_init(ssl) || SSL_get_session(ssl) == nullptr) {
|
|
|
|
fprintf(stderr, "Invalid state for SSL_CB_HANDSHAKE_DONE.\n");
|
|
|
|
abort();
|
|
|
|
}
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
|
|
|
|
TestState *test_state = GetTestState(ssl);
|
|
|
|
test_state->handshake_done = true;
|
|
|
|
|
|
|
|
// Save the selected credential for the tests to assert on.
|
|
|
|
const SSL_CREDENTIAL *cred = SSL_get0_selected_credential(ssl);
|
|
|
|
const CredentialInfo *cred_info =
|
|
|
|
cred != nullptr ? GetCredentialInfo(cred) : nullptr;
|
|
|
|
test_state->selected_credential =
|
|
|
|
cred_info != nullptr ? cred_info->number : -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static SSL_SESSION *GetSessionCallback(SSL *ssl, const uint8_t *data, int len,
|
|
|
|
int *copy) {
|
|
|
|
TestState *async_state = GetTestState(ssl);
|
|
|
|
if (async_state->session) {
|
|
|
|
*copy = 0;
|
|
|
|
return async_state->session.release();
|
|
|
|
} else if (async_state->pending_session) {
|
|
|
|
return SSL_magic_pending_session_ptr();
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) {
|
|
|
|
*out_clock = *GetClock();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int AlpnSelectCallback(SSL *ssl, const uint8_t **out, uint8_t *outlen,
|
|
|
|
const uint8_t *in, unsigned inlen, void *arg) {
|
|
|
|
if (GetTestState(ssl)->alpn_select_done) {
|
|
|
|
fprintf(stderr, "AlpnSelectCallback called after completion.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
GetTestState(ssl)->alpn_select_done = true;
|
|
|
|
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
if (config->decline_alpn) {
|
|
|
|
return SSL_TLSEXT_ERR_NOACK;
|
|
|
|
}
|
|
|
|
if (config->reject_alpn) {
|
|
|
|
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->expect_advertised_alpn.empty() &&
|
|
|
|
(config->expect_advertised_alpn.size() != inlen ||
|
|
|
|
OPENSSL_memcmp(config->expect_advertised_alpn.data(), in, inlen) !=
|
|
|
|
0)) {
|
|
|
|
fprintf(stderr, "bad ALPN select callback inputs.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config->defer_alps) {
|
|
|
|
for (const auto &pair : config->application_settings) {
|
|
|
|
if (!SSL_add_application_settings(
|
|
|
|
ssl, reinterpret_cast<const uint8_t *>(pair.first.data()),
|
|
|
|
pair.first.size(),
|
|
|
|
reinterpret_cast<const uint8_t *>(pair.second.data()),
|
|
|
|
pair.second.size())) {
|
|
|
|
fprintf(stderr, "error configuring ALPS.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(config->select_alpn.empty() || !config->select_empty_alpn);
|
|
|
|
*out = (const uint8_t *)config->select_alpn.data();
|
|
|
|
*outlen = config->select_alpn.size();
|
|
|
|
return SSL_TLSEXT_ERR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool CheckVerifyCallback(SSL *ssl) {
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
if (!config->expect_ocsp_response.empty()) {
|
|
|
|
const uint8_t *data;
|
|
|
|
size_t len;
|
|
|
|
SSL_get0_ocsp_response(ssl, &data, &len);
|
|
|
|
if (len == 0) {
|
|
|
|
fprintf(stderr, "OCSP response not available in verify callback.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Implement ClientHelloOuter handshakes.
If a client offers ECH, but the server rejects it, the client completes
the handshake with ClientHelloOuter in order to authenticate retry keys.
Implement this flow. This is largely allowing the existing handshake to
proceed, but with some changes:
- Certificate verification uses the other name. This CL routes this up to
the built-in verifier and adds SSL_get0_ech_name_override for the
callback.
- We need to disable False Start to pick up server Finished in TLS 1.2.
- Client certificates, notably in TLS 1.3 where they're encrypted,
should only be revealed to the true server. Fortunately, not sending
client certs is always an option, so do that.
Channel ID has a similar issue. I've just omitted the extension in
ClientHelloOuter because it's deprecated and is unlikely to be used
with ECH at this point. ALPS may be worth some pondering but, the way
it's currently used, is not sensitive.
(Possibly we should change the draft to terminate the handshake before
even sending that flight...)
- The session is never offered in ClientHelloOuter, but our internal
book-keeping doesn't quite notice.
I had to replace ech_accept with a tri-state ech_status to correctly
handle an edge case in SSL_get0_ech_name_override: when ECH + 0-RTT +
reverify_on_resume are all enabled, the first certificate verification
is for the 0-RTT session and should be against the true name, yet we
have selected_ech_config && !ech_accept. A tri-state tracks when ECH is
actually rejected. I've maintained this on the server as well, though
the server never actually cares.
Bug: 275
Change-Id: Ie55966ca3dc4ffcc8c381479f0fe9bcacd34d0f8
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48135
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
3 years ago
|
|
|
const char *name_override;
|
|
|
|
size_t name_override_len;
|
|
|
|
SSL_get0_ech_name_override(ssl, &name_override, &name_override_len);
|
|
|
|
if (config->expect_no_ech_name_override && name_override_len != 0) {
|
|
|
|
fprintf(stderr, "Unexpected ECH name override.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!config->expect_ech_name_override.empty() &&
|
|
|
|
config->expect_ech_name_override !=
|
|
|
|
std::string(name_override, name_override_len)) {
|
|
|
|
fprintf(stderr, "ECH name did not match expected value.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GetTestState(ssl)->cert_verified) {
|
|
|
|
fprintf(stderr, "Certificate verified twice.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int CertVerifyCallback(X509_STORE_CTX *store_ctx, void *arg) {
|
|
|
|
SSL *ssl = (SSL *)X509_STORE_CTX_get_ex_data(
|
|
|
|
store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
if (!CheckVerifyCallback(ssl)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
GetTestState(ssl)->cert_verified = true;
|
|
|
|
if (config->verify_fail) {
|
|
|
|
X509_STORE_CTX_set_error(store_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LoadCertificate(bssl::UniquePtr<X509> *out_x509,
|
|
|
|
bssl::UniquePtr<STACK_OF(X509)> *out_chain,
|
|
|
|
const std::string &file) {
|
|
|
|
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file()));
|
|
|
|
if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
out_x509->reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
|
|
|
|
if (!*out_x509) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
out_chain->reset(sk_X509_new_null());
|
|
|
|
if (!*out_chain) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep reading the certificate chain.
|
|
|
|
for (;;) {
|
|
|
|
bssl::UniquePtr<X509> cert(
|
|
|
|
PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
|
|
|
|
if (!cert) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bssl::PushToStack(out_chain->get(), std::move(cert))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t err = ERR_peek_last_error();
|
|
|
|
if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
|
|
|
|
ERR_GET_REASON(err) != PEM_R_NO_START_LINE) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ERR_clear_error();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file) {
|
|
|
|
bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file()));
|
|
|
|
if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return bssl::UniquePtr<EVP_PKEY>(
|
|
|
|
PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, NULL));
|
|
|
|
}
|
|
|
|
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
static bssl::UniquePtr<CRYPTO_BUFFER> X509ToBuffer(X509 *x509) {
|
|
|
|
uint8_t *der = nullptr;
|
|
|
|
int der_len = i2d_X509(x509, &der);
|
|
|
|
if (der_len < 0) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
bssl::UniquePtr<uint8_t> free_der(der);
|
|
|
|
return bssl::UniquePtr<CRYPTO_BUFFER>(
|
|
|
|
CRYPTO_BUFFER_new(der, der_len, nullptr));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ssl_private_key_result_t AsyncPrivateKeyComplete(SSL *ssl, uint8_t *out,
|
|
|
|
size_t *out_len,
|
|
|
|
size_t max_out);
|
|
|
|
|
|
|
|
static EVP_PKEY *GetPrivateKey(SSL *ssl) {
|
|
|
|
const CredentialInfo *cred_info =
|
|
|
|
GetCredentialInfo(SSL_get0_selected_credential(ssl));
|
|
|
|
if (cred_info != nullptr) {
|
|
|
|
return cred_info->private_key.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
return GetTestState(ssl)->private_key.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssl_private_key_result_t AsyncPrivateKeySign(
|
|
|
|
SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
|
|
|
|
uint16_t signature_algorithm, const uint8_t *in, size_t in_len) {
|
|
|
|
TestState *test_state = GetTestState(ssl);
|
|
|
|
test_state->used_private_key = true;
|
|
|
|
if (!test_state->private_key_result.empty()) {
|
|
|
|
fprintf(stderr, "AsyncPrivateKeySign called with operation pending.\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
EVP_PKEY *private_key = GetPrivateKey(ssl);
|
|
|
|
if (EVP_PKEY_id(private_key) !=
|
|
|
|
SSL_get_signature_algorithm_key_type(signature_algorithm)) {
|
|
|
|
fprintf(stderr, "Key type does not match signature algorithm.\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determine the hash.
|
|
|
|
const EVP_MD *md = SSL_get_signature_algorithm_digest(signature_algorithm);
|
|
|
|
bssl::ScopedEVP_MD_CTX ctx;
|
|
|
|
EVP_PKEY_CTX *pctx;
|
|
|
|
if (!EVP_DigestSignInit(ctx.get(), &pctx, md, nullptr, private_key)) {
|
|
|
|
return ssl_private_key_failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Configure additional signature parameters.
|
|
|
|
if (SSL_is_signature_algorithm_rsa_pss(signature_algorithm)) {
|
|
|
|
if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) ||
|
|
|
|
!EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1 /* salt len = hash len */)) {
|
|
|
|
return ssl_private_key_failure;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the signature into |test_state|.
|
|
|
|
size_t len = 0;
|
|
|
|
if (!EVP_DigestSign(ctx.get(), nullptr, &len, in, in_len)) {
|
|
|
|
return ssl_private_key_failure;
|
|
|
|
}
|
|
|
|
test_state->private_key_result.resize(len);
|
|
|
|
if (!EVP_DigestSign(ctx.get(), test_state->private_key_result.data(), &len,
|
|
|
|
in, in_len)) {
|
|
|
|
return ssl_private_key_failure;
|
|
|
|
}
|
|
|
|
test_state->private_key_result.resize(len);
|
|
|
|
|
|
|
|
return AsyncPrivateKeyComplete(ssl, out, out_len, max_out);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssl_private_key_result_t AsyncPrivateKeyDecrypt(SSL *ssl, uint8_t *out,
|
|
|
|
size_t *out_len,
|
|
|
|
size_t max_out,
|
|
|
|
const uint8_t *in,
|
|
|
|
size_t in_len) {
|
|
|
|
TestState *test_state = GetTestState(ssl);
|
|
|
|
test_state->used_private_key = true;
|
|
|
|
if (!test_state->private_key_result.empty()) {
|
|
|
|
fprintf(stderr, "AsyncPrivateKeyDecrypt called with operation pending.\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
EVP_PKEY *private_key = GetPrivateKey(ssl);
|
|
|
|
RSA *rsa = EVP_PKEY_get0_RSA(private_key);
|
|
|
|
if (rsa == NULL) {
|
|
|
|
fprintf(stderr, "AsyncPrivateKeyDecrypt called with incorrect key type.\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
test_state->private_key_result.resize(RSA_size(rsa));
|
|
|
|
if (!RSA_decrypt(rsa, out_len, test_state->private_key_result.data(),
|
|
|
|
RSA_size(rsa), in, in_len, RSA_NO_PADDING)) {
|
|
|
|
return ssl_private_key_failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
test_state->private_key_result.resize(*out_len);
|
|
|
|
|
|
|
|
return AsyncPrivateKeyComplete(ssl, out, out_len, max_out);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssl_private_key_result_t AsyncPrivateKeyComplete(SSL *ssl, uint8_t *out,
|
|
|
|
size_t *out_len,
|
|
|
|
size_t max_out) {
|
|
|
|
TestState *test_state = GetTestState(ssl);
|
|
|
|
if (test_state->private_key_result.empty()) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"AsyncPrivateKeyComplete called without operation pending.\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GetTestConfig(ssl)->async && test_state->private_key_retries < 2) {
|
|
|
|
// Only return the decryption on the second attempt, to test both incomplete
|
|
|
|
// |sign|/|decrypt| and |complete|.
|
|
|
|
return ssl_private_key_retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (max_out < test_state->private_key_result.size()) {
|
|
|
|
fprintf(stderr, "Output buffer too small.\n");
|
|
|
|
return ssl_private_key_failure;
|
|
|
|
}
|
|
|
|
OPENSSL_memcpy(out, test_state->private_key_result.data(),
|
|
|
|
test_state->private_key_result.size());
|
|
|
|
*out_len = test_state->private_key_result.size();
|
|
|
|
|
|
|
|
test_state->private_key_result.clear();
|
|
|
|
test_state->private_key_retries = 0;
|
|
|
|
return ssl_private_key_success;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const SSL_PRIVATE_KEY_METHOD g_async_private_key_method = {
|
|
|
|
AsyncPrivateKeySign,
|
|
|
|
AsyncPrivateKeyDecrypt,
|
|
|
|
AsyncPrivateKeyComplete,
|
|
|
|
};
|
|
|
|
|
|
|
|
static bssl::UniquePtr<SSL_CREDENTIAL> CredentialFromConfig(
|
|
|
|
const TestConfig &config, const CredentialConfig &cred_config, int number) {
|
|
|
|
bssl::UniquePtr<SSL_CREDENTIAL> cred;
|
|
|
|
switch (cred_config.type) {
|
|
|
|
case CredentialConfigType::kX509:
|
|
|
|
cred.reset(SSL_CREDENTIAL_new_x509());
|
|
|
|
break;
|
|
|
|
case CredentialConfigType::kDelegated:
|
|
|
|
cred.reset(SSL_CREDENTIAL_new_delegated());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (cred == nullptr) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto info = std::make_unique<CredentialInfo>();
|
|
|
|
info->number = number;
|
|
|
|
|
|
|
|
if (!cred_config.cert_file.empty()) {
|
|
|
|
bssl::UniquePtr<X509> x509;
|
|
|
|
bssl::UniquePtr<STACK_OF(X509)> chain;
|
|
|
|
if (!LoadCertificate(&x509, &chain, cred_config.cert_file.c_str())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> buffers;
|
|
|
|
buffers.push_back(X509ToBuffer(x509.get()));
|
|
|
|
if (buffers.back() == nullptr) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
for (X509 *cert : chain.get()) {
|
|
|
|
buffers.push_back(X509ToBuffer(cert));
|
|
|
|
if (buffers.back() == nullptr) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
std::vector<CRYPTO_BUFFER *> buffers_raw;
|
|
|
|
for (const auto &buffer : buffers) {
|
|
|
|
buffers_raw.push_back(buffer.get());
|
|
|
|
}
|
|
|
|
if (!SSL_CREDENTIAL_set1_cert_chain(cred.get(), buffers_raw.data(),
|
|
|
|
buffers_raw.size())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cred_config.key_file.empty()) {
|
|
|
|
bssl::UniquePtr<EVP_PKEY> pkey =
|
|
|
|
LoadPrivateKey(cred_config.key_file.c_str());
|
|
|
|
if (pkey == nullptr) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (config.async || config.handshake_hints) {
|
|
|
|
info->private_key = std::move(pkey);
|
|
|
|
if (!SSL_CREDENTIAL_set_private_key_method(cred.get(),
|
|
|
|
&g_async_private_key_method)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!SSL_CREDENTIAL_set1_private_key(cred.get(), pkey.get())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cred_config.signing_prefs.empty() &&
|
|
|
|
!SSL_CREDENTIAL_set1_signing_algorithm_prefs(
|
|
|
|
cred.get(), cred_config.signing_prefs.data(),
|
|
|
|
cred_config.signing_prefs.size())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cred_config.delegated_credential.empty()) {
|
|
|
|
bssl::UniquePtr<CRYPTO_BUFFER> buf(
|
|
|
|
CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t *>(
|
|
|
|
cred_config.delegated_credential.data()),
|
|
|
|
cred_config.delegated_credential.size(), nullptr));
|
|
|
|
if (buf == nullptr ||
|
|
|
|
!SSL_CREDENTIAL_set1_delegated_credential(cred.get(), buf.get())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cred_config.ocsp_response.empty()) {
|
|
|
|
bssl::UniquePtr<CRYPTO_BUFFER> buf(CRYPTO_BUFFER_new(
|
|
|
|
reinterpret_cast<const uint8_t *>(cred_config.ocsp_response.data()),
|
|
|
|
cred_config.ocsp_response.size(), nullptr));
|
|
|
|
if (buf == nullptr ||
|
|
|
|
!SSL_CREDENTIAL_set1_ocsp_response(cred.get(), buf.get())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cred_config.signed_cert_timestamps.empty()) {
|
|
|
|
bssl::UniquePtr<CRYPTO_BUFFER> buf(
|
|
|
|
CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t *>(
|
|
|
|
cred_config.signed_cert_timestamps.data()),
|
|
|
|
cred_config.signed_cert_timestamps.size(), nullptr));
|
|
|
|
if (buf == nullptr || !SSL_CREDENTIAL_set1_signed_cert_timestamp_list(
|
|
|
|
cred.get(), buf.get())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SetCredentialInfo(cred.get(), std::move(info))) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cred;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool GetCertificate(SSL *ssl, bssl::UniquePtr<X509> *out_x509,
|
|
|
|
bssl::UniquePtr<STACK_OF(X509)> *out_chain,
|
|
|
|
bssl::UniquePtr<EVP_PKEY> *out_pkey) {
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
|
|
|
|
if (!config->signing_prefs.empty()) {
|
|
|
|
if (!SSL_set_signing_algorithm_prefs(ssl, config->signing_prefs.data(),
|
|
|
|
config->signing_prefs.size())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->key_file.empty()) {
|
|
|
|
*out_pkey = LoadPrivateKey(config->key_file.c_str());
|
|
|
|
if (!*out_pkey) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!config->cert_file.empty() &&
|
|
|
|
!LoadCertificate(out_x509, out_chain, config->cert_file.c_str())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!config->ocsp_response.empty() && !config->set_ocsp_in_callback &&
|
|
|
|
!SSL_set_ocsp_response(ssl, (const uint8_t *)config->ocsp_response.data(),
|
|
|
|
config->ocsp_response.size())) {
|
|
|
|
return false;
|
|
|
|
}
|
Add an SSL_CREDENTIAL API for ECDSA/RSA and delegated credentials
This adds a notion of "credentials" to BoringSSL's API, to support
certificate selection by key type (typically ECDSA vs RSA), though the
aim is for it to be generalizable to other certificate types and other
kinds of selection criteria, such as Trust Expressions, or Merkle Tree
Certificates. Since we already had some nascent delegated credentials
I've reworked that feature with SSL_CREDENTIALs as well.
The model is that you create an SSL_CREDENTIAL object containing all the
configuration for what you are authenticating as. An X.509
SSL_CREDENTIAL has a certificate chain, private key, optionally an OCSP
response and SCT list. Delegated credentials are similar. In the future,
we might use this for raw public keys, other certificate types, etc.
Once you set those up, you configure those on the SSL or SSL_CTX in
preference order, and BoringSSL will internally pick the first one that
is usable.
The current implementation ends up redundantly selecting the signature
algorithm a couple of times. This works but is a little goofy. A
follow-up change will remove this redundancy. The protocol between the
runner and shim for tests is also a little weird, but it was the easiest
way I could think of for injecting that. Long-term, I think we should
just replace that protocol with a JSON structure. (See
https://crbug.com/boringssl/704.)
As split handshakes are in the process of being replaced with handshake
hints, this won't work with split handshakes. It works with handshake
hints without any extra work.
Update-Note: The delegated credentials API has been revamped.
Previously, it worked by configuring an optional delegated credential
and key with your normal certificate chain. This has the side effect of
forcing your DC issuer and your fallback certificate to be the same. The
SSL_CREDENTIAL API lifts this restriction.
A delegated credential is now just a different kind of credential. It
may use the same certificate chain as an X.509 credential or be
completely separate. All the SSL_CREDENTIAL APIs take CRYPTO_BUFFERs,
so, if common, the buffers may be shared to reduce memory.
The SSL_delegated_credential_used API is also removed, in favor of the
more general SSL_get0_selected_credential API. Callers can use ex_data
or pointer equality to identify the credential.
Bug: 249
Change-Id: Ibc290df3b7b95f148df12625e41cf55c50566602
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66690
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
9 months ago
|
|
|
|
|
|
|
for (size_t i = 0; i < config->credentials.size(); i++) {
|
|
|
|
bssl::UniquePtr<SSL_CREDENTIAL> cred = CredentialFromConfig(
|
|
|
|
*config, config->credentials[i], static_cast<int>(i));
|
|
|
|
if (cred == nullptr || !SSL_add1_credential(ssl, cred.get())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool HexDecode(std::string *out, const std::string &in) {
|
|
|
|
if ((in.size() & 1) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto buf = std::make_unique<uint8_t[]>(in.size() / 2);
|
|
|
|
for (size_t i = 0; i < in.size() / 2; i++) {
|
|
|
|
uint8_t high, low;
|
|
|
|
if (!OPENSSL_fromxdigit(&high, in[i * 2]) ||
|
|
|
|
!OPENSSL_fromxdigit(&low, in[i * 2 + 1])) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
buf[i] = (high << 4) | low;
|
|
|
|
}
|
|
|
|
|
|
|
|
out->assign(reinterpret_cast<const char *>(buf.get()), in.size() / 2);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::vector<std::string> SplitParts(const std::string &in,
|
|
|
|
const char delim) {
|
|
|
|
std::vector<std::string> ret;
|
|
|
|
size_t start = 0;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < in.size(); i++) {
|
|
|
|
if (in[i] == delim) {
|
|
|
|
ret.push_back(in.substr(start, i - start));
|
|
|
|
start = i + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret.push_back(in.substr(start, std::string::npos));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::vector<std::string> DecodeHexStrings(
|
|
|
|
const std::string &hex_strings) {
|
|
|
|
std::vector<std::string> ret;
|
|
|
|
const std::vector<std::string> parts = SplitParts(hex_strings, ',');
|
|
|
|
|
|
|
|
for (const auto &part : parts) {
|
|
|
|
std::string binary;
|
|
|
|
if (!HexDecode(&binary, part)) {
|
|
|
|
fprintf(stderr, "Bad hex string: %s.\n", part.c_str());
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret.push_back(binary);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bssl::UniquePtr<STACK_OF(X509_NAME)> DecodeHexX509Names(
|
|
|
|
const std::string &hex_names) {
|
|
|
|
const std::vector<std::string> der_names = DecodeHexStrings(hex_names);
|
|
|
|
bssl::UniquePtr<STACK_OF(X509_NAME)> ret(sk_X509_NAME_new_null());
|
|
|
|
if (!ret) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto &der_name : der_names) {
|
|
|
|
const uint8_t *const data =
|
|
|
|
reinterpret_cast<const uint8_t *>(der_name.data());
|
|
|
|
const uint8_t *derp = data;
|
|
|
|
bssl::UniquePtr<X509_NAME> name(
|
|
|
|
d2i_X509_NAME(nullptr, &derp, der_name.size()));
|
|
|
|
if (!name || derp != data + der_name.size()) {
|
|
|
|
fprintf(stderr, "Failed to parse X509_NAME.\n");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bssl::PushToStack(ret.get(), std::move(name))) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool CheckPeerVerifyPrefs(SSL *ssl) {
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
if (!config->expect_peer_verify_prefs.empty()) {
|
|
|
|
const uint16_t *peer_sigalgs;
|
|
|
|
size_t num_peer_sigalgs =
|
|
|
|
SSL_get0_peer_verify_algorithms(ssl, &peer_sigalgs);
|
|
|
|
if (config->expect_peer_verify_prefs.size() != num_peer_sigalgs) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"peer verify preferences length mismatch (got %zu, wanted %zu)\n",
|
|
|
|
num_peer_sigalgs, config->expect_peer_verify_prefs.size());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (size_t i = 0; i < num_peer_sigalgs; i++) {
|
|
|
|
if (peer_sigalgs[i] != config->expect_peer_verify_prefs[i]) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"peer verify preference %zu mismatch (got %04x, wanted %04x\n",
|
|
|
|
i, peer_sigalgs[i], config->expect_peer_verify_prefs[i]);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool CheckCertificateRequest(SSL *ssl) {
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
|
|
|
|
if (!CheckPeerVerifyPrefs(ssl)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->expect_certificate_types.empty()) {
|
|
|
|
const uint8_t *certificate_types;
|
|
|
|
size_t certificate_types_len =
|
|
|
|
SSL_get0_certificate_types(ssl, &certificate_types);
|
|
|
|
if (certificate_types_len != config->expect_certificate_types.size() ||
|
|
|
|
OPENSSL_memcmp(certificate_types,
|
|
|
|
config->expect_certificate_types.data(),
|
|
|
|
certificate_types_len) != 0) {
|
|
|
|
fprintf(stderr, "certificate types mismatch.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->expect_client_ca_list.empty()) {
|
|
|
|
bssl::UniquePtr<STACK_OF(X509_NAME)> expected =
|
|
|
|
DecodeHexX509Names(config->expect_client_ca_list);
|
|
|
|
const size_t num_expected = sk_X509_NAME_num(expected.get());
|
|
|
|
|
|
|
|
const STACK_OF(X509_NAME) *received = SSL_get_client_CA_list(ssl);
|
|
|
|
const size_t num_received = sk_X509_NAME_num(received);
|
|
|
|
|
|
|
|
if (num_received != num_expected) {
|
Fix the easy -Wformat-signedness errors.
GCC has a warning that complains about even more type mismatches in
printf. Some of these are a bit messy and will be fixed in separate CLs.
This covers the easy ones.
The .*s stuff is unfortunate, but printf has no size_t-clean string
printer. ALPN protocol lengths are bound by uint8_t, so it doesn't
really matter.
The IPv6 printing one is obnoxious and arguably a false positive. It's
really a C language flaw: all types smaller than int get converted to
int when you do arithmetic. So something like this first doesn't
overflow the shift because it computes over int, but then the result
overall is stored as an int.
uint8_t a, b;
(a << 8) | b
On the one hand, this fixes a "missing" cast to uint16_t before the
shift. At the same time, the incorrect final type means passing it to
%x, which expects unsigned int. The compiler has forgotten this value
actually fits in uint16_t and flags a warning. Mitigate this by storing
in a uint16_t first.
The story doesn't quite end here. Arguments passed to variadic functions
go through integer promotion[0], so the argument is still passed to
snprintf as an int! But then va_arg allows for a signedness mismatch[1],
provided the value is representable in both types. The combination means
that %x, though actually paired with unsigned, also accept uint8_t and
uint16_t, because those are guaranteed to promote to an int that meets
[1]. GCC recognizes [1] applies here.
(There's also PRI16x, but that's a bit tedious to use and, in glibc, is
defined as plain "x" anyway.)
[0] https://en.cppreference.com/w/c/language/conversion#Default_argument_promotions
[1] https://en.cppreference.com/w/c/variadic/va_arg
Bug: 450
Change-Id: Ic1d41356755a18ab922956dd2e07b560470341f4
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50765
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
3 years ago
|
|
|
fprintf(stderr, "expected %zu names in CertificateRequest but got %zu.\n",
|
|
|
|
num_expected, num_received);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < num_received; i++) {
|
|
|
|
if (X509_NAME_cmp(sk_X509_NAME_value(received, i),
|
|
|
|
sk_X509_NAME_value(expected.get(), i)) != 0) {
|
Fix the easy -Wformat-signedness errors.
GCC has a warning that complains about even more type mismatches in
printf. Some of these are a bit messy and will be fixed in separate CLs.
This covers the easy ones.
The .*s stuff is unfortunate, but printf has no size_t-clean string
printer. ALPN protocol lengths are bound by uint8_t, so it doesn't
really matter.
The IPv6 printing one is obnoxious and arguably a false positive. It's
really a C language flaw: all types smaller than int get converted to
int when you do arithmetic. So something like this first doesn't
overflow the shift because it computes over int, but then the result
overall is stored as an int.
uint8_t a, b;
(a << 8) | b
On the one hand, this fixes a "missing" cast to uint16_t before the
shift. At the same time, the incorrect final type means passing it to
%x, which expects unsigned int. The compiler has forgotten this value
actually fits in uint16_t and flags a warning. Mitigate this by storing
in a uint16_t first.
The story doesn't quite end here. Arguments passed to variadic functions
go through integer promotion[0], so the argument is still passed to
snprintf as an int! But then va_arg allows for a signedness mismatch[1],
provided the value is representable in both types. The combination means
that %x, though actually paired with unsigned, also accept uint8_t and
uint16_t, because those are guaranteed to promote to an int that meets
[1]. GCC recognizes [1] applies here.
(There's also PRI16x, but that's a bit tedious to use and, in glibc, is
defined as plain "x" anyway.)
[0] https://en.cppreference.com/w/c/language/conversion#Default_argument_promotions
[1] https://en.cppreference.com/w/c/variadic/va_arg
Bug: 450
Change-Id: Ic1d41356755a18ab922956dd2e07b560470341f4
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50765
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
3 years ago
|
|
|
fprintf(stderr, "names in CertificateRequest differ at index #%zu.\n",
|
|
|
|
i);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const STACK_OF(CRYPTO_BUFFER) *buffers = SSL_get0_server_requested_CAs(ssl);
|
|
|
|
if (sk_CRYPTO_BUFFER_num(buffers) != num_received) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Mismatch between SSL_get_server_requested_CAs and "
|
|
|
|
"SSL_get_client_CA_list.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ClientCertCallback(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey) {
|
|
|
|
if (!CheckCertificateRequest(ssl)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GetTestConfig(ssl)->async && !GetTestState(ssl)->cert_ready) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bssl::UniquePtr<X509> x509;
|
|
|
|
bssl::UniquePtr<STACK_OF(X509)> chain;
|
|
|
|
bssl::UniquePtr<EVP_PKEY> pkey;
|
|
|
|
if (!GetCertificate(ssl, &x509, &chain, &pkey)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return zero for no certificate.
|
|
|
|
if (!x509) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Chains and asynchronous private keys are not supported with client_cert_cb.
|
|
|
|
*out_x509 = x509.release();
|
|
|
|
*out_pkey = pkey.release();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool InstallCertificate(SSL *ssl) {
|
|
|
|
bssl::UniquePtr<X509> x509;
|
|
|
|
bssl::UniquePtr<STACK_OF(X509)> chain;
|
|
|
|
bssl::UniquePtr<EVP_PKEY> pkey;
|
|
|
|
if (!GetCertificate(ssl, &x509, &chain, &pkey)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pkey) {
|
|
|
|
TestState *test_state = GetTestState(ssl);
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
if (config->async || config->handshake_hints) {
|
|
|
|
// Install a custom private key if testing asynchronous callbacks, or if
|
|
|
|
// testing handshake hints. In the handshake hints case, we wish to check
|
|
|
|
// that hints only mismatch when allowed.
|
|
|
|
test_state->private_key = std::move(pkey);
|
|
|
|
SSL_set_private_key_method(ssl, &g_async_private_key_method);
|
|
|
|
} else if (!SSL_use_PrivateKey(ssl, pkey.get())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x509 && !SSL_use_certificate(ssl, x509.get())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sk_X509_num(chain.get()) > 0 && !SSL_set1_chain(ssl, chain.get())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static enum ssl_select_cert_result_t SelectCertificateCallback(
|
|
|
|
const SSL_CLIENT_HELLO *client_hello) {
|
|
|
|
SSL *ssl = client_hello->ssl;
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
TestState *test_state = GetTestState(ssl);
|
|
|
|
test_state->early_callback_called = true;
|
|
|
|
|
|
|
|
if (!config->expect_server_name.empty()) {
|
|
|
|
const char *server_name =
|
|
|
|
SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
|
|
|
if (server_name == nullptr ||
|
|
|
|
std::string(server_name) != config->expect_server_name) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Server name mismatch in early callback (got %s; want %s).\n",
|
|
|
|
server_name, config->expect_server_name.c_str());
|
|
|
|
return ssl_select_cert_error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config->fail_early_callback) {
|
|
|
|
return ssl_select_cert_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Simulate some asynchronous work in the early callback.
|
|
|
|
if ((config->use_early_callback || test_state->get_handshake_hints_cb) &&
|
|
|
|
config->async && !test_state->early_callback_ready) {
|
|
|
|
return ssl_select_cert_retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (test_state->get_handshake_hints_cb &&
|
|
|
|
!test_state->get_handshake_hints_cb(client_hello)) {
|
|
|
|
return ssl_select_cert_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config->use_early_callback && !InstallCertificate(ssl)) {
|
|
|
|
return ssl_select_cert_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ssl_select_cert_success;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int SetQuicReadSecret(SSL *ssl, enum ssl_encryption_level_t level,
|
|
|
|
const SSL_CIPHER *cipher, const uint8_t *secret,
|
|
|
|
size_t secret_len) {
|
|
|
|
MockQuicTransport *quic_transport = GetTestState(ssl)->quic_transport.get();
|
|
|
|
if (quic_transport == nullptr) {
|
|
|
|
fprintf(stderr, "No QUIC transport.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return quic_transport->SetReadSecret(level, cipher, secret, secret_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int SetQuicWriteSecret(SSL *ssl, enum ssl_encryption_level_t level,
|
|
|
|
const SSL_CIPHER *cipher, const uint8_t *secret,
|
|
|
|
size_t secret_len) {
|
|
|
|
MockQuicTransport *quic_transport = GetTestState(ssl)->quic_transport.get();
|
|
|
|
if (quic_transport == nullptr) {
|
|
|
|
fprintf(stderr, "No QUIC transport.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return quic_transport->SetWriteSecret(level, cipher, secret, secret_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int AddQuicHandshakeData(SSL *ssl, enum ssl_encryption_level_t level,
|
|
|
|
const uint8_t *data, size_t len) {
|
|
|
|
MockQuicTransport *quic_transport = GetTestState(ssl)->quic_transport.get();
|
|
|
|
if (quic_transport == nullptr) {
|
|
|
|
fprintf(stderr, "No QUIC transport.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return quic_transport->WriteHandshakeData(level, data, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int FlushQuicFlight(SSL *ssl) {
|
|
|
|
MockQuicTransport *quic_transport = GetTestState(ssl)->quic_transport.get();
|
|
|
|
if (quic_transport == nullptr) {
|
|
|
|
fprintf(stderr, "No QUIC transport.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return quic_transport->Flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int SendQuicAlert(SSL *ssl, enum ssl_encryption_level_t level,
|
|
|
|
uint8_t alert) {
|
|
|
|
MockQuicTransport *quic_transport = GetTestState(ssl)->quic_transport.get();
|
|
|
|
if (quic_transport == nullptr) {
|
|
|
|
fprintf(stderr, "No QUIC transport.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return quic_transport->SendAlert(level, alert);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const SSL_QUIC_METHOD g_quic_method = {
|
|
|
|
SetQuicReadSecret,
|
|
|
|
SetQuicWriteSecret,
|
|
|
|
AddQuicHandshakeData,
|
|
|
|
FlushQuicFlight,
|
|
|
|
SendQuicAlert,
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool MaybeInstallCertCompressionAlg(
|
|
|
|
const TestConfig *config, SSL_CTX *ssl_ctx, uint16_t alg,
|
|
|
|
ssl_cert_compression_func_t compress,
|
|
|
|
ssl_cert_decompression_func_t decompress) {
|
|
|
|
if (!config->install_cert_compression_algs &&
|
|
|
|
config->install_one_cert_compression_alg != alg) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return SSL_CTX_add_cert_compression_alg(ssl_ctx, alg, compress, decompress);
|
|
|
|
}
|
|
|
|
|
|
|
|
bssl::UniquePtr<SSL_CTX> TestConfig::SetupCtx(SSL_CTX *old_ctx) const {
|
|
|
|
bssl::UniquePtr<SSL_CTX> ssl_ctx(
|
|
|
|
SSL_CTX_new(is_dtls ? DTLS_method() : TLS_method()));
|
|
|
|
if (!ssl_ctx) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
SSL_CTX_set0_buffer_pool(ssl_ctx.get(), BufferPool());
|
|
|
|
|
|
|
|
std::string cipher_list = "ALL:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256";
|
|
|
|
if (!cipher.empty()) {
|
|
|
|
cipher_list = cipher;
|
|
|
|
SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE);
|
|
|
|
}
|
|
|
|
if (!SSL_CTX_set_strict_cipher_list(ssl_ctx.get(), cipher_list.c_str())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (async && is_server) {
|
|
|
|
// Disable the internal session cache. To test asynchronous session lookup,
|
|
|
|
// we use an external session cache.
|
|
|
|
SSL_CTX_set_session_cache_mode(
|
|
|
|
ssl_ctx.get(), SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_INTERNAL);
|
|
|
|
SSL_CTX_sess_set_get_cb(ssl_ctx.get(), GetSessionCallback);
|
|
|
|
} else {
|
|
|
|
SSL_CTX_set_session_cache_mode(ssl_ctx.get(), SSL_SESS_CACHE_BOTH);
|
|
|
|
}
|
|
|
|
|
|
|
|
SSL_CTX_set_select_certificate_cb(ssl_ctx.get(), SelectCertificateCallback);
|
|
|
|
|
|
|
|
if (use_old_client_cert_callback) {
|
|
|
|
SSL_CTX_set_client_cert_cb(ssl_ctx.get(), ClientCertCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
SSL_CTX_set_next_protos_advertised_cb(ssl_ctx.get(),
|
|
|
|
NextProtosAdvertisedCallback, NULL);
|
|
|
|
if (!select_next_proto.empty()) {
|
|
|
|
SSL_CTX_set_next_proto_select_cb(ssl_ctx.get(), NextProtoSelectCallback,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!select_alpn.empty() || decline_alpn || reject_alpn ||
|
|
|
|
select_empty_alpn) {
|
|
|
|
SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback);
|
|
|
|
|
|
|
|
SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback);
|
|
|
|
SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback);
|
|
|
|
|
|
|
|
if (use_ticket_callback || handshake_hints) {
|
|
|
|
// If using handshake hints, always enable the ticket callback, so we can
|
|
|
|
// check that hints only mismatch when allowed. The ticket callback also
|
|
|
|
// uses a constant key, which simplifies the test.
|
|
|
|
SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx.get(), TicketKeyCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!use_custom_verify_callback) {
|
|
|
|
SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), CertVerifyCallback, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!signed_cert_timestamps.empty() &&
|
|
|
|
!SSL_CTX_set_signed_cert_timestamp_list(
|
|
|
|
ssl_ctx.get(), (const uint8_t *)signed_cert_timestamps.data(),
|
|
|
|
signed_cert_timestamps.size())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!use_client_ca_list.empty()) {
|
|
|
|
if (use_client_ca_list == "<NULL>") {
|
|
|
|
SSL_CTX_set_client_CA_list(ssl_ctx.get(), nullptr);
|
|
|
|
} else if (use_client_ca_list == "<EMPTY>") {
|
|
|
|
bssl::UniquePtr<STACK_OF(X509_NAME)> names;
|
|
|
|
SSL_CTX_set_client_CA_list(ssl_ctx.get(), names.release());
|
|
|
|
} else {
|
|
|
|
bssl::UniquePtr<STACK_OF(X509_NAME)> names =
|
|
|
|
DecodeHexX509Names(use_client_ca_list);
|
|
|
|
SSL_CTX_set_client_CA_list(ssl_ctx.get(), names.release());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (enable_grease) {
|
|
|
|
SSL_CTX_set_grease_enabled(ssl_ctx.get(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (permute_extensions) {
|
|
|
|
SSL_CTX_set_permute_extensions(ssl_ctx.get(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!expect_server_name.empty()) {
|
|
|
|
SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(), ServerNameCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (enable_early_data) {
|
|
|
|
SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (allow_unknown_alpn_protos) {
|
|
|
|
SSL_CTX_set_allow_unknown_alpn_protos(ssl_ctx.get(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!verify_prefs.empty()) {
|
|
|
|
if (!SSL_CTX_set_verify_algorithm_prefs(ssl_ctx.get(), verify_prefs.data(),
|
|
|
|
verify_prefs.size())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SSL_CTX_set_msg_callback(ssl_ctx.get(), MessageCallback);
|
|
|
|
|
|
|
|
if (allow_false_start_without_alpn) {
|
|
|
|
SSL_CTX_set_false_start_allowed_without_alpn(ssl_ctx.get(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (use_ocsp_callback) {
|
|
|
|
SSL_CTX_set_tlsext_status_cb(ssl_ctx.get(), LegacyOCSPCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (old_ctx) {
|
|
|
|
uint8_t keys[48];
|
|
|
|
if (!SSL_CTX_get_tlsext_ticket_keys(old_ctx, &keys, sizeof(keys)) ||
|
|
|
|
!SSL_CTX_set_tlsext_ticket_keys(ssl_ctx.get(), keys, sizeof(keys))) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
CopySessions(ssl_ctx.get(), old_ctx);
|
|
|
|
} else if (!ticket_key.empty() &&
|
|
|
|
!SSL_CTX_set_tlsext_ticket_keys(ssl_ctx.get(), ticket_key.data(),
|
|
|
|
ticket_key.size())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// These mock compression algorithms match the corresponding ones in
|
|
|
|
// |addCertCompressionTests|.
|
|
|
|
if (!MaybeInstallCertCompressionAlg(
|
|
|
|
this, ssl_ctx.get(), 0xff02,
|
|
|
|
[](SSL *ssl, CBB *out, const uint8_t *in, size_t in_len) -> int {
|
|
|
|
if (!CBB_add_u8(out, 1) || !CBB_add_u8(out, 2) ||
|
|
|
|
!CBB_add_u8(out, 3) || !CBB_add_u8(out, 4) ||
|
|
|
|
!CBB_add_bytes(out, in, in_len)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
},
|
|
|
|
[](SSL *ssl, CRYPTO_BUFFER **out, size_t uncompressed_len,
|
|
|
|
const uint8_t *in, size_t in_len) -> int {
|
|
|
|
if (in_len < 4 || in[0] != 1 || in[1] != 2 || in[2] != 3 ||
|
|
|
|
in[3] != 4 || uncompressed_len != in_len - 4) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
const bssl::Span<const uint8_t> uncompressed(in + 4, in_len - 4);
|
|
|
|
*out = CRYPTO_BUFFER_new(uncompressed.data(), uncompressed.size(),
|
|
|
|
nullptr);
|
|
|
|
return *out != nullptr;
|
|
|
|
}) ||
|
|
|
|
!MaybeInstallCertCompressionAlg(
|
|
|
|
this, ssl_ctx.get(), 0xff01,
|
|
|
|
[](SSL *ssl, CBB *out, const uint8_t *in, size_t in_len) -> int {
|
|
|
|
if (in_len < 2 || in[0] != 0 || in[1] != 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return CBB_add_bytes(out, in + 2, in_len - 2);
|
|
|
|
},
|
|
|
|
[](SSL *ssl, CRYPTO_BUFFER **out, size_t uncompressed_len,
|
|
|
|
const uint8_t *in, size_t in_len) -> int {
|
|
|
|
if (uncompressed_len != 2 + in_len) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
auto buf = std::make_unique<uint8_t[]>(2 + in_len);
|
|
|
|
buf[0] = 0;
|
|
|
|
buf[1] = 0;
|
|
|
|
OPENSSL_memcpy(&buf[2], in, in_len);
|
|
|
|
*out = CRYPTO_BUFFER_new(buf.get(), 2 + in_len, nullptr);
|
|
|
|
return *out != nullptr;
|
|
|
|
}) ||
|
|
|
|
!MaybeInstallCertCompressionAlg(
|
|
|
|
this, ssl_ctx.get(), 0xff03,
|
|
|
|
[](SSL *ssl, CBB *out, const uint8_t *in, size_t in_len) -> int {
|
|
|
|
uint8_t byte;
|
|
|
|
return RAND_bytes(&byte, 1) && //
|
|
|
|
CBB_add_u8(out, byte) && //
|
|
|
|
CBB_add_bytes(out, in, in_len);
|
|
|
|
},
|
|
|
|
[](SSL *ssl, CRYPTO_BUFFER **out, size_t uncompressed_len,
|
|
|
|
const uint8_t *in, size_t in_len) -> int {
|
|
|
|
if (uncompressed_len + 1 != in_len) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
*out = CRYPTO_BUFFER_new(in + 1, in_len - 1, nullptr);
|
|
|
|
return *out != nullptr;
|
|
|
|
})) {
|
|
|
|
fprintf(stderr, "SSL_CTX_add_cert_compression_alg failed.\n");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (server_preference) {
|
|
|
|
SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_quic) {
|
|
|
|
SSL_CTX_set_quic_method(ssl_ctx.get(), &g_quic_method);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ssl_ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int DDoSCallback(const SSL_CLIENT_HELLO *client_hello) {
|
|
|
|
const TestConfig *config = GetTestConfig(client_hello->ssl);
|
|
|
|
return config->fail_ddos_callback ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned PskClientCallback(SSL *ssl, const char *hint,
|
|
|
|
char *out_identity, unsigned max_identity_len,
|
|
|
|
uint8_t *out_psk, unsigned max_psk_len) {
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
|
|
|
|
if (config->psk_identity.empty()) {
|
|
|
|
if (hint != nullptr) {
|
|
|
|
fprintf(stderr, "Server PSK hint was non-null.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else if (hint == nullptr ||
|
|
|
|
strcmp(hint, config->psk_identity.c_str()) != 0) {
|
|
|
|
fprintf(stderr, "Server PSK hint did not match.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Account for the trailing '\0' for the identity.
|
|
|
|
if (config->psk_identity.size() >= max_identity_len ||
|
|
|
|
config->psk.size() > max_psk_len) {
|
|
|
|
fprintf(stderr, "PSK buffers too small.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
OPENSSL_strlcpy(out_identity, config->psk_identity.c_str(), max_identity_len);
|
|
|
|
OPENSSL_memcpy(out_psk, config->psk.data(), config->psk.size());
|
|
|
|
return static_cast<unsigned>(config->psk.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned PskServerCallback(SSL *ssl, const char *identity,
|
|
|
|
uint8_t *out_psk, unsigned max_psk_len) {
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
|
|
|
|
if (strcmp(identity, config->psk_identity.c_str()) != 0) {
|
|
|
|
fprintf(stderr, "Client PSK identity did not match.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config->psk.size() > max_psk_len) {
|
|
|
|
fprintf(stderr, "PSK buffers too small.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
OPENSSL_memcpy(out_psk, config->psk.data(), config->psk.size());
|
|
|
|
return static_cast<unsigned>(config->psk.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssl_verify_result_t CustomVerifyCallback(SSL *ssl, uint8_t *out_alert) {
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
if (!CheckVerifyCallback(ssl)) {
|
|
|
|
return ssl_verify_invalid;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config->async && !GetTestState(ssl)->custom_verify_ready) {
|
|
|
|
return ssl_verify_retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
GetTestState(ssl)->cert_verified = true;
|
|
|
|
if (config->verify_fail) {
|
|
|
|
return ssl_verify_invalid;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ssl_verify_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int CertCallback(SSL *ssl, void *arg) {
|
|
|
|
const TestConfig *config = GetTestConfig(ssl);
|
|
|
|
|
|
|
|
// Check the peer certificate metadata is as expected.
|
|
|
|
if ((!SSL_is_server(ssl) && !CheckCertificateRequest(ssl)) ||
|
|
|
|
!CheckPeerVerifyPrefs(ssl)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config->fail_cert_callback) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The certificate will be installed via other means.
|
|
|
|
if (!config->async || config->use_early_callback) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!GetTestState(ssl)->cert_ready) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (!InstallCertificate(ssl)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bssl::UniquePtr<SSL> TestConfig::NewSSL(
|
|
|
|
SSL_CTX *ssl_ctx, SSL_SESSION *session,
|
|
|
|
std::unique_ptr<TestState> test_state) const {
|
|
|
|
bssl::UniquePtr<SSL> ssl(SSL_new(ssl_ctx));
|
|
|
|
if (!ssl) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!SetTestConfig(ssl.get(), this)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (test_state != nullptr) {
|
|
|
|
if (!SetTestState(ssl.get(), std::move(test_state))) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fallback_scsv && !SSL_set_mode(ssl.get(), SSL_MODE_SEND_FALLBACK_SCSV)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
// Install the certificate synchronously if nothing else will handle it.
|
|
|
|
if (!use_early_callback && !use_old_client_cert_callback && !async &&
|
|
|
|
!InstallCertificate(ssl.get())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (!use_old_client_cert_callback) {
|
|
|
|
SSL_set_cert_cb(ssl.get(), CertCallback, nullptr);
|
|
|
|
}
|
|
|
|
int mode = SSL_VERIFY_NONE;
|
|
|
|
if (require_any_client_certificate) {
|
|
|
|
mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
|
|
|
}
|
|
|
|
if (verify_peer) {
|
|
|
|
mode = SSL_VERIFY_PEER;
|
|
|
|
}
|
|
|
|
if (verify_peer_if_no_obc) {
|
|
|
|
// Set SSL_VERIFY_FAIL_IF_NO_PEER_CERT so testing whether client
|
|
|
|
// certificates were requested is easy.
|
|
|
|
mode = SSL_VERIFY_PEER | SSL_VERIFY_PEER_IF_NO_OBC |
|
|
|
|
SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
|
|
|
}
|
|
|
|
if (use_custom_verify_callback) {
|
|
|
|
SSL_set_custom_verify(ssl.get(), mode, CustomVerifyCallback);
|
|
|
|
} else if (mode != SSL_VERIFY_NONE) {
|
|
|
|
SSL_set_verify(ssl.get(), mode, NULL);
|
|
|
|
}
|
|
|
|
if (false_start) {
|
|
|
|
SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START);
|
|
|
|
}
|
|
|
|
if (cbc_record_splitting) {
|
|
|
|
SSL_set_mode(ssl.get(), SSL_MODE_CBC_RECORD_SPLITTING);
|
|
|
|
}
|
|
|
|
if (partial_write) {
|
|
|
|
SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_PARTIAL_WRITE);
|
|
|
|
}
|
|
|
|
if (reverify_on_resume) {
|
|
|
|
SSL_CTX_set_reverify_on_resume(ssl_ctx, 1);
|
|
|
|
}
|
Default SSL_set_enforce_rsa_key_usage to enabled.
This relands
https://boringssl-review.googlesource.com/c/boringssl/+/54606, which was
temporarily reverted.
Update-Note: By default, clients will now require RSA server
certificates used in TLS 1.2 and earlier to include the keyEncipherment
or digitalSignature bit. keyEncipherment is required if using RSA key
exchange. digitalSignature is required if using ECDHE_RSA key exchange.
If unsure, TLS RSA server signatures should include both, but some
deployments may wish to include only one if separating keys, or simply
disabling RSA key exchange. The latter is useful to mitigate either the
Bleichenbacher attack (from 1998, most recently resurfaced in 2017 as
ROBOT), or to strengthen TLS 1.3 downgrade protections, which is
particularly important for enterprise environments using client
certificates (aka "mTLS") because, prior to TLS 1.3, the TLS client
certificate flow was insufficiently encrypted or authenticated. Without
reflecting an RSA key exchange disable into key usage, and then the
client checking it, an attacker can spoof a CertificateRequest as coming
from some server.
This aligns with standard security requirements for using X.509
certificates, specified in RFC 5280, section 4.2.1.3, and reiterated in
TLS as early as TLS 1.0, RFC 2246, section 7.4.2, published 24 years ago
on January 1999. Constraints on usage of keys are important to mitigate
cross-protocol attacks, a class of cryptographic attacks that is
well-studied in the literature.
We already checked this for each of ECDSA, TLS 1.3, and servers
verifying client certificates, so this just fills in the remaining hole.
As a result, this change is also important for avoiding some weird
behaviors when configuration changes transition a server in or out of
this hole. (We've seen connection failures get misattributed to TLS 1.3
when it was really a certificate misconfiguration.)
Chrome has also enforced this for some time with publicly-trusted
certificates. As a temporary measure for callers that need more time,
the SSL_set_enforce_rsa_key_usage API, added to BoringSSL in January
2019, still exists where we need to turn this off.
Fixed: 519
Change-Id: I91bf2cfb04c92aec7875e640f90ba6f837146dc1
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/58805
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
2 years ago
|
|
|
if (ignore_rsa_key_usage) {
|
|
|
|
SSL_set_enforce_rsa_key_usage(ssl.get(), 0);
|
|
|
|
}
|
Check client certificate types in TLS <= 1.2
TLS <= 1.2 servers indicate supported client certificate key types with
a certificate_types field in the CertificateRequest. Historically, we've
just ignored this field, because we've always outsourced certificate
selection to the caller anyway. This meant that, if you configured an
RSA client certificate in response to a server that requested only ECDSA
certificates, we would happily send the certificate and leave it to the
server to decide if it was happy.
Strictly speaking, this was in violation of RFC 5246:
- The end-entity certificate provided by the client MUST contain a
key that is compatible with certificate_types. [...]
Although prior TLS versions didn't say anything useful about this either
way.
Once we move certificate selection into the library, we'll want to start
evaluating supported algorithms ourselves. A natural implementation of
it will, as a side effect, cause us to enforce this match, even when
only a single certificate is configured. Since this is unlikely to have
any real compatibility impact (every TLS server I've seen just hardcodes
this list), let's just try turning it on. On the off chance it does
break someone, I've left a flag, SSL_set_check_client_certificate_type,
for folks to turn this check off. The flag will most likely be
unnecessary, in which case we can retire it after a few months.
If this does cause a problem, we can opt to turn it off for the default
certificate, or only enable it when multiple certificates are
configured, or lean on the sigalgs list (doesn't work for 1.0/1.1), but
these all result in some slightly suboptimal behavior, so I think we
should treat them as contingency plans.
Update-Note: A TLS 1.2 (or below) client, using client certificates,
connecting to a TLS server which doesn't support its certificate type
will now fail the connection slightly earlier, rather than sending the
certificate and waiting for the server to reject it. The connection
should fail either way, but now it will fail earlier with
SSL_R_UNKNOWN_CERTIFICATE_TYPE. If the server was buggy and did not
correctly advertise its own capabilities (very very unlikely), this may
cause a connection to fail despite previously succeeding. We have
included a temporary API, SSL_set_check_client_certificate_type, to
disable this behavior in the unlikely event this has any impact, but
please contact the BoringSSL team if you need it, as it will interfere
with improvements down the line.
This change does not affect servers requesting client certificates, only
clients sending them.
Bug: 249
Change-Id: I159bc444c4ee79fbe5c476d4253b48d58d2538be
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66687
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
9 months ago
|
|
|
if (no_check_client_certificate_type) {
|
|
|
|
SSL_set_check_client_certificate_type(ssl.get(), 0);
|
|
|
|
}
|
Check ECDSA curves in TLS 1.2 servers
In TLS 1.2 and below, the supported_curves list simultaneously contrains
ECDH and ECDSA. Since BoringSSL, previously, did not handle ECDSA
certificate selection in the library, we ignored the latter and left it
to the callers. If configured with an ECDSA certificate that didn't
match the peer's curve list, we proceeded anyway, and left it to the
client to reject the connection.
This contradicts RFC 8422, which says:
The server constructs an appropriate certificate chain and conveys it
to the client in the Certificate message. If the client has used a
Supported Elliptic Curves Extension, the public key in the server's
certificate MUST respect the client's choice of elliptic curves. A
server that cannot satisfy this requirement MUST NOT choose an ECC
cipher suite in its ServerHello message.)
As with the previous client certificate change, once we move certificate
selection into the library, we'll need to evaluate this ourselves. A
natural implementation of it will, as a side effect, cause us to enforce
this match, even when only a single certificate is configured. This CL
lands that behavior change ahead of time and, in case there are
compatibility impats, leaves a flag, SSL_set_check_ecdsa_curve, to
restore the old behavior. If the change goes through fine, we can retire
the flag after a few months.
If this does cause a problem, we can opt to turn it off for the default
certificate, or only enable it when multiple certificates are
configured, but these all result in some slightly suboptimal behavior,
so I think we should treat them as contingency plans.
To help debugging, I gave this a dedicated error, though doing so is a
little tricky because of the PSK fallback. (See the
CheckECDSACurve-PSK-TLS12 test.)
Update-Note: A TLS 1.2 (or below) server, using an ECDSA certificate,
connecting to a client which doesn't advertise its ECDSA curve will now
fail the connection slightly earlier, rather than sending the
certificate and waiting for the client to reject it. The connection
should fail either way, but now it will fail earlier with
SSL_R_WRONG_CURVE. If the client was buggy and did not correctly
advertise its own capabilities, this may cause a connection to fail
despite previously succeeding. We have included a temporary API,
SSL_set_check_ecdsa_curve, to disable this behavior in the event this
has any impact, but please contact the BoringSSL team if you need it,
as it will interfere with improvements down the line.
TLS 1.3 is not impacted by this change, neither are clients, or RSA
certificiates. Additionally, if your server was already looking at the
curve list before configuring an ECDSA certificate in TLS 1.2, this
will also have no impact.
Bug: 249
Change-Id: I2f1d4e2627641319556847cbbbcdddf347bbc8a9
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66688
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
9 months ago
|
|
|
if (no_check_ecdsa_curve) {
|
|
|
|
SSL_set_check_ecdsa_curve(ssl.get(), 0);
|
|
|
|
}
|
|
|
|
if (no_tls13) {
|
|
|
|
SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_3);
|
|
|
|
}
|
|
|
|
if (no_tls12) {
|
|
|
|
SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_2);
|
|
|
|
}
|
|
|
|
if (no_tls11) {
|
|
|
|
SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_1);
|
|
|
|
}
|
|
|
|
if (no_tls1) {
|
|
|
|
SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1);
|
|
|
|
}
|
|
|
|
if (no_ticket) {
|
|
|
|
SSL_set_options(ssl.get(), SSL_OP_NO_TICKET);
|
|
|
|
}
|
|
|
|
if (!expect_channel_id.empty() || enable_channel_id) {
|
|
|
|
SSL_set_tls_channel_id_enabled(ssl.get(), 1);
|
|
|
|
}
|
|
|
|
if (enable_ech_grease) {
|
|
|
|
SSL_set_enable_ech_grease(ssl.get(), 1);
|
|
|
|
}
|
|
|
|
if (static_cast<int>(fips_202205) + static_cast<int>(wpa_202304) > 1) {
|
|
|
|
fprintf(stderr, "Multiple policy options given\n");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (fips_202205 && !SSL_set_compliance_policy(
|
|
|
|
ssl.get(), ssl_compliance_policy_fips_202205)) {
|
|
|
|
fprintf(stderr, "SSL_set_compliance_policy failed\n");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (wpa_202304 && !SSL_set_compliance_policy(
|
|
|
|
ssl.get(), ssl_compliance_policy_wpa3_192_202304)) {
|
|
|
|
fprintf(stderr, "SSL_set_compliance_policy failed\n");
|
|
|
|
return nullptr;
|
|
|
|
}
|
Add most of an ECH client implementation.
Based on an initial implementation by Dan McArdle at
https://boringssl-review.googlesource.com/c/boringssl/+/46784
This CL contains most of a client implementation for
draft-ietf-tls-esni-10. The pieces missing so far, which will be done in
follow-up CLs are:
1. While the ClientHelloInner is padded, the server Certificate message
is not. I'll add that once we resolve the spec discussions on how to
do that. (We were originally going to use TLS record-level padding,
but that doesn't work well with QUIC.)
2. The client should check the public name is a valid DNS name before
copying it into ClientHelloOuter.server_name.
3. The ClientHelloOuter handshake flow is not yet implemented. This CL
can detect when the server selects ClientHelloOuter, but for now the
handshake immediately fails. A follow-up CL will remove that logic
and instead add the APIs and extra checks needed.
Otherwise, this should be complete, including padding and compression.
The main interesting point design-wise is that we run through
ClientHello construction multiple times. We need to construct
ClientHelloInner and ClientHelloOuter. Then each of those has slight
variants: EncodedClientHelloInner is the compressed form, and
ClientHelloOuterAAD just has the ECH extension erased to avoid a
circular dependency.
I've computed ClientHelloInner and EncodedClientHelloInner concurrently
because the compression scheme requires shifting the extensions around
to be contiguous. However, I've computed ClientHelloOuterAAD and
ClientHelloOuter by running through the logic twice. This probably can
be done better, but the next draft revises the construction anyway, so
I'm thinking I'll rework it then. (In the next draft, we use a
placeholder payload of the same length, so we can construct the
ClientHello once and fill in the payload.)
Additionally, now that we have a client available in ssl_test, this adds
a threading test to confirm that SSL_CTX_set1_ech_keys is properly
synchronized. (Confirmed that, if I drop the lock in
SSL_CTX_set1_ech_keys, TSan notices.)
Change-Id: Icaff68b595035bdcc73c468ff638e67c84239ef4
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48004
Reviewed-by: Adam Langley <agl@google.com>
4 years ago
|
|
|
if (!ech_config_list.empty() &&
|
|
|
|
!SSL_set1_ech_config_list(
|
|
|
|
ssl.get(), reinterpret_cast<const uint8_t *>(ech_config_list.data()),
|
|
|
|
ech_config_list.size())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (ech_server_configs.size() != ech_server_keys.size() ||
|
|
|
|
ech_server_configs.size() != ech_is_retry_config.size()) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"-ech-server-config, -ech-server-key, and -ech-is-retry-config "
|
|
|
|
"flags must match.\n");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (!ech_server_configs.empty()) {
|
|
|
|
bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
|
|
|
|
if (!keys) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
for (size_t i = 0; i < ech_server_configs.size(); i++) {
|
|
|
|
const std::string &ech_config = ech_server_configs[i];
|
|
|
|
const std::string &ech_private_key = ech_server_keys[i];
|
|
|
|
const int is_retry_config = ech_is_retry_config[i];
|
|
|
|
bssl::ScopedEVP_HPKE_KEY key;
|
|
|
|
if (!EVP_HPKE_KEY_init(
|
|
|
|
key.get(), EVP_hpke_x25519_hkdf_sha256(),
|
|
|
|
reinterpret_cast<const uint8_t *>(ech_private_key.data()),
|
|
|
|
ech_private_key.size()) ||
|
|
|
|
!SSL_ECH_KEYS_add(
|
|
|
|
keys.get(), is_retry_config,
|
|
|
|
reinterpret_cast<const uint8_t *>(ech_config.data()),
|
|
|
|
ech_config.size(), key.get())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!SSL_CTX_set1_ech_keys(ssl_ctx, keys.get())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!send_channel_id.empty()) {
|
|
|
|
bssl::UniquePtr<EVP_PKEY> pkey = LoadPrivateKey(send_channel_id);
|
|
|
|
if (!pkey || !SSL_set1_tls_channel_id(ssl.get(), pkey.get())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!host_name.empty() &&
|
|
|
|
!SSL_set_tlsext_host_name(ssl.get(), host_name.c_str())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (!advertise_alpn.empty() &&
|
|
|
|
SSL_set_alpn_protos(
|
|
|
|
ssl.get(), reinterpret_cast<const uint8_t *>(advertise_alpn.data()),
|
|
|
|
advertise_alpn.size()) != 0) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (!defer_alps) {
|
|
|
|
for (const auto &pair : application_settings) {
|
|
|
|
if (!SSL_add_application_settings(
|
|
|
|
ssl.get(), reinterpret_cast<const uint8_t *>(pair.first.data()),
|
|
|
|
pair.first.size(),
|
|
|
|
reinterpret_cast<const uint8_t *>(pair.second.data()),
|
|
|
|
pair.second.size())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!psk.empty()) {
|
|
|
|
SSL_set_psk_client_callback(ssl.get(), PskClientCallback);
|
|
|
|
SSL_set_psk_server_callback(ssl.get(), PskServerCallback);
|
|
|
|
}
|
|
|
|
if (!psk_identity.empty() &&
|
|
|
|
!SSL_use_psk_identity_hint(ssl.get(), psk_identity.c_str())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (!srtp_profiles.empty() &&
|
|
|
|
!SSL_set_srtp_profiles(ssl.get(), srtp_profiles.c_str())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (enable_ocsp_stapling) {
|
|
|
|
SSL_enable_ocsp_stapling(ssl.get());
|
|
|
|
}
|
|
|
|
if (enable_signed_cert_timestamps) {
|
|
|
|
SSL_enable_signed_cert_timestamps(ssl.get());
|
|
|
|
}
|
|
|
|
if (min_version != 0 &&
|
|
|
|
!SSL_set_min_proto_version(ssl.get(), min_version)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (max_version != 0 &&
|
|
|
|
!SSL_set_max_proto_version(ssl.get(), max_version)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (mtu != 0) {
|
|
|
|
SSL_set_options(ssl.get(), SSL_OP_NO_QUERY_MTU);
|
|
|
|
SSL_set_mtu(ssl.get(), mtu);
|
|
|
|
}
|
|
|
|
if (install_ddos_callback) {
|
|
|
|
SSL_CTX_set_dos_protection_cb(ssl_ctx, DDoSCallback);
|
|
|
|
}
|
|
|
|
SSL_set_shed_handshake_config(ssl.get(), true);
|
|
|
|
if (renegotiate_once) {
|
|
|
|
SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_once);
|
|
|
|
}
|
|
|
|
if (renegotiate_freely || forbid_renegotiation_after_handshake) {
|
|
|
|
// |forbid_renegotiation_after_handshake| will disable renegotiation later.
|
|
|
|
SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_freely);
|
|
|
|
}
|
|
|
|
if (renegotiate_ignore) {
|
|
|
|
SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_ignore);
|
|
|
|
}
|
|
|
|
if (renegotiate_explicit) {
|
|
|
|
SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_explicit);
|
|
|
|
}
|
|
|
|
if (!check_close_notify) {
|
|
|
|
SSL_set_quiet_shutdown(ssl.get(), 1);
|
|
|
|
}
|
Align NIDs vs group IDs in TLS group APIs
Right now we use NIDs to configure the group list, but group IDs (the
TLS codepoints) to return the negotiated group. The NIDs come from
OpenSSL, while the group ID was original our API. OpenSSL has since
added SSL_get_negotiated_group, but we don't implement it.
To add Kyber to QUIC, we'll need to add an API for configuring groups to
QUICHE. Carrying over our inconsistency into QUICHE's public API would
be unfortunate, so let's use this as the time to align things.
We could either align with OpenSSL and say NIDs are now the group
representation at the public API, or we could add a parallel group ID
API. (Or we could make a whole new SSL_NAMED_GROUP object to pattern
after SSL_CIPHER, which isn't wrong, but is even more new APIs.)
Aligning with OpenSSL would be fewer APIs, but NIDs aren't a great
representation. The numbers are ad-hoc and even diverge a bit between
OpenSSL and BoringSSL. The TLS codepoints are better to export out to
callers. Also QUICHE has exported the negotiated group using the
codepoints, so the natural solution would be to use codepoints on input
too.
Thus, this CL adds SSL_CTX_set1_group_ids and SSL_set1_group_ids. It
also rearranges the API docs slightly to put the group ID ones first,
and leaves a little note about the NID representation before introducing
those.
While I'm here, I've added SSL_get_negotiated_group. NGINX seems to use
it when available, so we may as well fill in that unnecessary
compatibility hole.
Bug: chromium:1442377
Change-Id: I47ca8ae52c274133f28da9893aed7fc70f942bf8
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60208
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
1 year ago
|
|
|
if (!curves.empty() &&
|
|
|
|
!SSL_set1_group_ids(ssl.get(), curves.data(), curves.size())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (initial_timeout_duration_ms > 0) {
|
|
|
|
DTLSv1_set_initial_timeout_duration(ssl.get(), initial_timeout_duration_ms);
|
|
|
|
}
|
|
|
|
if (max_cert_list > 0) {
|
|
|
|
SSL_set_max_cert_list(ssl.get(), max_cert_list);
|
|
|
|
}
|
|
|
|
if (retain_only_sha256_client_cert) {
|
|
|
|
SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1);
|
|
|
|
}
|
|
|
|
if (max_send_fragment > 0) {
|
|
|
|
SSL_set_max_send_fragment(ssl.get(), max_send_fragment);
|
|
|
|
}
|
|
|
|
if (alps_use_new_codepoint) {
|
|
|
|
SSL_set_alps_use_new_codepoint(ssl.get(), 1);
|
|
|
|
}
|
|
|
|
if (quic_use_legacy_codepoint != -1) {
|
|
|
|
SSL_set_quic_use_legacy_codepoint(ssl.get(), quic_use_legacy_codepoint);
|
|
|
|
}
|
|
|
|
if (!quic_transport_params.empty()) {
|
|
|
|
if (!SSL_set_quic_transport_params(
|
|
|
|
ssl.get(),
|
|
|
|
reinterpret_cast<const uint8_t *>(quic_transport_params.data()),
|
|
|
|
quic_transport_params.size())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (jdk11_workaround) {
|
|
|
|
SSL_set_jdk11_workaround(ssl.get(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (session != NULL) {
|
|
|
|
if (!is_server) {
|
|
|
|
if (SSL_set_session(ssl.get(), session) != 1) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
} else if (async) {
|
|
|
|
// The internal session cache is disabled, so install the session
|
|
|
|
// manually.
|
|
|
|
SSL_SESSION_up_ref(session);
|
|
|
|
GetTestState(ssl.get())->pending_session.reset(session);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!quic_early_data_context.empty() &&
|
|
|
|
!SSL_set_quic_early_data_context(
|
|
|
|
ssl.get(),
|
|
|
|
reinterpret_cast<const uint8_t *>(quic_early_data_context.data()),
|
|
|
|
quic_early_data_context.size())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ssl;
|
|
|
|
}
|