diff --git a/Makefile b/Makefile index a1bc6a8..a04f2f8 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ testdata: util mkdir -p ${TESTDATA_DIR} ${UTIL} -make-root -out ${TESTDATA_DIR}/root.crt -key-out ${TESTDATA_DIR}/root.key -host root.com ${UTIL} -make-intermediate -cert-in ${TESTDATA_DIR}/root.crt -key-in ${TESTDATA_DIR}/root.key -out ${TESTDATA_DIR}/example.crt -key-out ${TESTDATA_DIR}/example.key -host example.com - ${UTIL} -make-intermediate -cert-in ${TESTDATA_DIR}/root.crt -key-in ${TESTDATA_DIR}/root.key -out ${TESTDATA_DIR}/client_facing.crt -key-out ${TESTDATA_DIR}/client_facing.key -host client-facing.com + ${UTIL} -make-intermediate -cert-in ${TESTDATA_DIR}/root.crt -key-in ${TESTDATA_DIR}/root.key -out ${TESTDATA_DIR}/client-facing.crt -key-out ${TESTDATA_DIR}/client-facing.key -host client-facing.com ${UTIL} -make-dc -cert-in ${TESTDATA_DIR}/example.crt -key-in ${TESTDATA_DIR}/example.key -out ${TESTDATA_DIR}/dc.txt ${UTIL} -make-ech -out ${TESTDATA_DIR}/ech_configs -key-out ${TESTDATA_DIR}/ech_key -host client-facing.com diff --git a/cmd/util/make.go b/cmd/util/make.go index 33241b5..98f1002 100644 --- a/cmd/util/make.go +++ b/cmd/util/make.go @@ -106,7 +106,7 @@ func makeIntermediateCertificate(config *Config, inCertPath string, inKeyPath st SerialNumber: serialNumber, Subject: pkix.Name{ Organization: []string{"tls-interop-runner development certificate"}, - OrganizationalUnit: []string{"tls-interop-runner"}, + OrganizationalUnit: []string{outPath}, }, NotBefore: config.ValidFrom, diff --git a/cmd/util/util.go b/cmd/util/util.go index a74c766..6a57427 100644 --- a/cmd/util/util.go +++ b/cmd/util/util.go @@ -24,7 +24,7 @@ const usage = `Usage: $ util -make-root -out root.crt -key-out root.key -host root.com $ util -make-intermediate -cert-in parent.crt -key-in parent.key -out child.crt -key-out child.key -host example.com $ util -make-dc -cert-in leaf.crt -key-in leaf.key -out dc.txt - $ util -make-ech-key -cert-in client_facing.crt -out ech_configs -key-out ech_key + $ util -make-ech-key -cert-in client-facing.crt -out ech_configs -key-out ech_key Note: This is a barebones CLI intended for basic usage/debugging. ` diff --git a/impl-endpoints/cloudflare-go/runner.go b/impl-endpoints/cloudflare-go/runner.go index d16d6e9..b222655 100644 --- a/impl-endpoints/cloudflare-go/runner.go +++ b/impl-endpoints/cloudflare-go/runner.go @@ -44,8 +44,8 @@ func init() { } clientFacingCert, err := tls.LoadX509KeyPair( - "/testdata/client_facing.crt", - "/testdata/client_facing.key", + "/testdata/client-facing.crt", + "/testdata/client-facing.key", ) if err != nil { log.Fatal(err) diff --git a/impl-endpoints/nss/Dockerfile b/impl-endpoints/nss/Dockerfile new file mode 100644 index 0000000..382484e --- /dev/null +++ b/impl-endpoints/nss/Dockerfile @@ -0,0 +1,50 @@ +FROM golang:latest AS builder + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + gyp \ + mercurial \ + ninja-build \ + zlib1g-dev \ + && rm -rf /var/lib/apt/lists/* \ + && apt-get autoremove -y && apt-get clean -y + +RUN mkdir /build +WORKDIR /build + +# Clone and build. Output in /nss/dist/Debug/. +RUN cd /build \ + && hg clone https://hg.mozilla.org/projects/nspr \ + && hg clone https://hg.mozilla.org/projects/nss \ + && cd nss \ + && ./build.sh -Denable_draft_hpke=1 \ + && cd / + +FROM ubuntu:20.04 +COPY --from=builder /build/dist/Debug/bin/* /usr/bin/ +COPY --from=builder /build/dist/Debug/lib/* /usr/lib/ + +# Setup the nss database +RUN mkdir /db && cd /db \ + && certutil -N -d . --empty-password + +RUN apt-get update && \ + apt-get install -y \ + net-tools \ + tcpdump \ + ethtool \ + iproute2 \ + python3 \ + openssl + +COPY ech_key_converter.py / + +COPY setup.sh /setup.sh +RUN chmod +x /setup.sh + +COPY run_endpoint.sh /run_endpoint.sh +RUN chmod +x /run_endpoint.sh + +ENTRYPOINT [ "/run_endpoint.sh" ] diff --git a/impl-endpoints/nss/ech_key_converter.py b/impl-endpoints/nss/ech_key_converter.py new file mode 100644 index 0000000..c64fec7 --- /dev/null +++ b/impl-endpoints/nss/ech_key_converter.py @@ -0,0 +1,87 @@ +#!/usr/bin/python3 + +""" +This script rewrites an ECHKey to contain a PKCS8-formatted keypair, which is the format +required by the selfserv utility in NSS. Specifically, the output is formed as follows: + + * struct { + * opaque pkcs8_ech_keypair<0..2^16-1>; + * ECHConfigs configs<0..2^16>; // draft-ietf-tls-esni-09 + * } ECHKey; +""" + +import sys +import struct +import base64 + +ECH_VERSION = 0xFE09 +DHKEM_X25519_SHA256 = 0x0020 + +# Hardcoded ASN.1 for ECPrivateKey, curve25519. See section 2 of rfc5958. +pkcs8_start = b"\x30\x67\x02\x01\x00\x30\x14\x06\x07\x2A\x86\x48\xCE\x3D\x02\x01\x06\x09\x2B\x06\x01\x04\x01\xDA\x47\x0F\x01\x04\x4C\x30\x4A\x02\x01\x01\x04\x20" +pkcs8_pub_header = b"\xa1\x23\x03\x21\x00" + + +def convert_ech_key(in_file, out_file): + with open(in_file, "rb") as f: + ech_keypair = base64.b64decode(f.read(), None, True) + + offset = 0 + length = struct.unpack("!H", ech_keypair[:2])[0] + offset += 2 + private_key = ech_keypair[offset : offset + length] + offset += length + + ech_configs = ech_keypair[offset:] + + # Parse the public key out of the ECHConfig. + length = struct.unpack("!H", ech_keypair[offset : offset + 2])[0] + offset += 2 + version = struct.unpack("!H", ech_keypair[offset : offset + 2])[0] + offset += 2 + + if version != ECH_VERSION: + print("ECHConfig.version is not 0xFE09: %x", hex(version)) + exit(1) + + length = struct.unpack("!H", ech_keypair[offset : offset + 2])[0] + offset += 2 + + # Public name + length = struct.unpack("!H", ech_keypair[offset : offset + 2])[0] + offset += 2 + length + + # Public key + length = struct.unpack("!H", ech_keypair[offset : offset + 2])[0] + offset += 2 + public_key = ech_keypair[offset : offset + length] + offset += length + + # Verify that the KEM is X25519. We don't support anything else. + kem_id = struct.unpack("!H", ech_keypair[offset : offset + 2])[0] + if kem_id != DHKEM_X25519_SHA256: + print("Unsupported KEM ID: %x", hex(kem_id)) + exit(1) + + pkcs8 = bytearray() + pkcs8 += ( + bytearray(pkcs8_start) + + bytearray(private_key) + + bytearray(pkcs8_pub_header) + + bytearray(public_key) + ) + + out_bytes = bytearray() + out_bytes += struct.pack("!H", len(pkcs8)) + pkcs8 + ech_configs + + out = open(out_file, "wb") + out.write(base64.b64encode(out_bytes)) + out.close() + + +if __name__ == "__main__": + if len(sys.argv) < 3: + print("Usage: ech_key_converter.py ") + exit(1) + + convert_ech_key(sys.argv[1], sys.argv[2]) diff --git a/impl-endpoints/nss/run_endpoint.sh b/impl-endpoints/nss/run_endpoint.sh new file mode 100644 index 0000000..e75af2a --- /dev/null +++ b/impl-endpoints/nss/run_endpoint.sh @@ -0,0 +1,51 @@ +#!/bin/bash +set -e + +/setup.sh + +DB_DIR="db" +P12_PASS="runner" +PORT=4433 + +rm -rf nss_testdata +mkdir nss_testdata + +if [ "$TESTCASE" = "ech-accept" ]; then + # Create a PKCS8 file for the ECH keypair + python3 /ech_key_converter.py testdata/ech_key nss_testdata/ech_key_converted + + # Create pfx files for pk12util + openssl pkcs12 -export -out nss_testdata/root.pfx -name root.com -inkey testdata/root.key -in testdata/root.crt -passout pass:"$P12_PASS" + openssl pkcs12 -export -out nss_testdata/example.pfx -name example.com -inkey testdata/example.key -in testdata/example.crt -passout pass:"$P12_PASS" + openssl pkcs12 -export -out nss_testdata/client-facing.pfx -name client-facing.com -inkey testdata/client-facing.key -in testdata/client-facing.crt -passout pass:"$P12_PASS" +else + echo "Test case not supported." + return true +fi + +# Import certs and keys +certs=("example" "client-facing" "root") +for i in "${certs[@]}" +do + pk12util -i nss_testdata/"$i".pfx -d "$DB_DIR" -W "$P12_PASS" + + # Trust the root + if [ "$i" = "root" ]; then + certutil -A -n "$i".com -t "C,C,C" -i testdata/"$i".crt -d "$DB_DIR" + fi +done + +if [ "$ROLE" = "client" ]; then + echo "Running NSS client." + echo "Client params: $SERVER_PARAMS" + echo "Test case: $TESTCASE" + ECH_CONFIGS=$( req.txt + tstclnt -d "$DB_DIR" -h example.com -p "$PORT" -N "$ECH_CONFIGS" -A req.txt +else + echo "Running NSS server." + echo "Server params: $SERVER_PARAMS" + echo "Test case: $TESTCASE" + ECH_KEY=$(