178 lines
5.9 KiB
C++
178 lines
5.9 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
// This is a standalone server that offers TLS 1.3 Encrypted
|
|
// Client Hello support.
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "nspr.h"
|
|
#include "ScopedNSSTypes.h"
|
|
#include "ssl.h"
|
|
#include "sslexp.h"
|
|
#include "TLSServer.h"
|
|
#include <pk11pub.h>
|
|
#include <vector>
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::test;
|
|
|
|
struct EchHost {
|
|
const char* mHostName;
|
|
const char* mCertName;
|
|
};
|
|
|
|
MOZ_RUNINIT const std::vector<uint32_t> kSuiteChaCha = {
|
|
(static_cast<uint32_t>(HpkeKdfHkdfSha256) << 16) |
|
|
HpkeAeadChaCha20Poly1305};
|
|
|
|
// Hostname, cert nickname pairs.
|
|
const EchHost sEchHosts[] = {{"ech-public.example.com", "default-ee"},
|
|
{"ech-private.example.com", "private-ee"},
|
|
{"selfsigned.example.com", "selfsigned"},
|
|
{nullptr, nullptr}};
|
|
|
|
int32_t DoSNISocketConfigBySubjectCN(PRFileDesc* aFd,
|
|
const SECItem* aSrvNameArr,
|
|
uint32_t aSrvNameArrSize) {
|
|
for (uint32_t i = 0; i < aSrvNameArrSize; i++) {
|
|
UniquePORTString name(
|
|
static_cast<char*>(PORT_ZAlloc(aSrvNameArr[i].len + 1)));
|
|
if (name) {
|
|
PORT_Memcpy(name.get(), aSrvNameArr[i].data, aSrvNameArr[i].len);
|
|
if (ConfigSecureServerWithNamedCert(aFd, name.get(), nullptr, nullptr,
|
|
nullptr) == SECSuccess) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return SSL_SNI_SEND_ALERT;
|
|
}
|
|
|
|
int32_t DoSNISocketConfig(PRFileDesc* aFd, const SECItem* aSrvNameArr,
|
|
uint32_t aSrvNameArrSize, void* aArg) {
|
|
const EchHost* host = GetHostForSNI(aSrvNameArr, aSrvNameArrSize, sEchHosts);
|
|
if (!host) {
|
|
PrintPRError("No cert found for hostname");
|
|
return SSL_SNI_SEND_ALERT;
|
|
}
|
|
|
|
if (gDebugLevel >= DEBUG_VERBOSE) {
|
|
fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName);
|
|
}
|
|
|
|
UniqueCERTCertificate cert;
|
|
SSLKEAType certKEA;
|
|
if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, host->mCertName, &cert,
|
|
&certKEA, nullptr)) {
|
|
return SSL_SNI_SEND_ALERT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t SetAlpnOptions(PRFileDesc* aFd, uint8_t flags) {
|
|
const std::vector<uint8_t> http1 = {0x08, 0x68, 0x74, 0x74, 0x70,
|
|
0x2f, 0x31, 0x2e, 0x31};
|
|
const std::vector<uint8_t> http2 = {0x02, 0x68, 0x32};
|
|
const std::vector<uint8_t> http3 = {0x02, 0x68, 0x33};
|
|
std::vector<uint8_t> alpnVec = {};
|
|
if (flags & 0b001) {
|
|
alpnVec.insert(alpnVec.end(), http1.begin(), http1.end());
|
|
}
|
|
if (flags & 0b010) {
|
|
alpnVec.insert(alpnVec.end(), http2.begin(), http2.end());
|
|
}
|
|
if (flags & 0b100) {
|
|
alpnVec.insert(alpnVec.end(), http3.begin(), http3.end());
|
|
}
|
|
fprintf(stderr, "ALPN Flags: %u\n", flags);
|
|
fprintf(stderr, "ALPN length: %zu\n", alpnVec.size());
|
|
if (SSL_SetNextProtoNego(aFd, alpnVec.data(), alpnVec.size()) != SECSuccess) {
|
|
fprintf(stderr, "Setting ALPN failed!\n");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SECStatus ConfigureServer(PRFileDesc* aFd) {
|
|
const char* alpnFlag = PR_GetEnv("MOZ_TLS_ECH_ALPN_FLAG");
|
|
if (alpnFlag) {
|
|
uint8_t flag = atoi(alpnFlag);
|
|
SetAlpnOptions(aFd, flag);
|
|
}
|
|
|
|
UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
|
|
if (!slot) {
|
|
PrintPRError("PK11_GetInternalKeySlot failed");
|
|
return SECFailure;
|
|
}
|
|
|
|
UniqueSECKEYPublicKey pubKey;
|
|
UniqueSECKEYPrivateKey privKey;
|
|
SECKEYPublicKey* tmpPubKey = nullptr;
|
|
SECKEYPrivateKey* tmpPrivKey = nullptr;
|
|
|
|
static const std::vector<uint8_t> pkcs8{
|
|
0x30, 0x67, 0x02, 0x01, 0x00, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48,
|
|
0xce, 0x3d, 0x02, 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda,
|
|
0x47, 0x0f, 0x01, 0x04, 0x4c, 0x30, 0x4a, 0x02, 0x01, 0x01, 0x04, 0x20,
|
|
0x8c, 0x49, 0x0e, 0x5b, 0x0c, 0x7d, 0xbe, 0x0c, 0x6d, 0x21, 0x92, 0x48,
|
|
0x4d, 0x2b, 0x7a, 0x04, 0x23, 0xb3, 0xb4, 0x54, 0x4f, 0x24, 0x81, 0x09,
|
|
0x5a, 0x99, 0xdb, 0xf2, 0x38, 0xfb, 0x35, 0x0f, 0xa1, 0x23, 0x03, 0x21,
|
|
0x00, 0x8a, 0x07, 0x56, 0x39, 0x49, 0xfa, 0xc6, 0x23, 0x29, 0x36, 0xed,
|
|
0x6f, 0x36, 0xc4, 0xfa, 0x73, 0x59, 0x30, 0xec, 0xde, 0xae, 0xf6, 0x73,
|
|
0x4e, 0x31, 0x4a, 0xea, 0xc3, 0x5a, 0x56, 0xfd, 0x0a};
|
|
|
|
SECItem pkcs8Item = {siBuffer, const_cast<uint8_t*>(pkcs8.data()),
|
|
static_cast<unsigned int>(pkcs8.size())};
|
|
SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
|
|
slot.get(), &pkcs8Item, nullptr, nullptr, false, false, KU_ALL,
|
|
&tmpPrivKey, nullptr);
|
|
|
|
if (rv != SECSuccess) {
|
|
PrintPRError("PK11_ImportDERPrivateKeyInfoAndReturnKey failed");
|
|
return SECFailure;
|
|
}
|
|
privKey.reset(tmpPrivKey);
|
|
tmpPubKey = SECKEY_ConvertToPublicKey(privKey.get());
|
|
pubKey.reset(tmpPubKey);
|
|
|
|
if (!privKey || !pubKey) {
|
|
PrintPRError("ECH/HPKE Public or Private key is null!");
|
|
return SECFailure;
|
|
}
|
|
|
|
std::vector<uint8_t> echConfig(1000, 0);
|
|
unsigned int len = 0;
|
|
const PRUint8 configId = 77;
|
|
const HpkeSymmetricSuite echCipherSuite = {HpkeKdfHkdfSha256,
|
|
HpkeAeadChaCha20Poly1305};
|
|
rv = SSL_EncodeEchConfigId(configId, "ech-public.example.com", 100,
|
|
HpkeDhKemX25519Sha256, pubKey.get(),
|
|
&echCipherSuite, 1, echConfig.data(), &len,
|
|
echConfig.size());
|
|
if (rv != SECSuccess) {
|
|
PrintPRError("SSL_EncodeEchConfig failed");
|
|
return rv;
|
|
}
|
|
|
|
rv = SSL_SetServerEchConfigs(aFd, pubKey.get(), privKey.get(),
|
|
echConfig.data(), len);
|
|
if (rv != SECSuccess) {
|
|
PrintPRError("SSL_SetServerEchConfigs failed");
|
|
return rv;
|
|
}
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
int rv = StartServer(argc, argv, DoSNISocketConfig, nullptr, ConfigureServer);
|
|
if (rv < 0) {
|
|
return rv;
|
|
}
|
|
}
|