@@ -15,6 +15,9 @@ env: | |||
matrix: | |||
fast_finish: true | |||
before_install: | |||
- make -f _dev/Makefile fmtcheck | |||
install: | |||
- sudo pip install docker | |||
@@ -21,6 +21,8 @@ import ( | |||
"sync/atomic" | |||
"time" | |||
sidh "github_com/henrydcase/nobs/dh/sidh" | |||
x448 "github_com/henrydcase/nobs/ec/x448" | |||
"golang_org/x/crypto/curve25519" | |||
) | |||
@@ -40,6 +42,21 @@ const ( | |||
secretResumption | |||
) | |||
// OZAPTF: better index by AlgID | |||
const ( | |||
x25519SharedSecretSize = 32 | |||
P751PubKeySize = 564 | |||
P751PrvKeySize = 48 | |||
P751SharedSecretSize = 188 | |||
SidhP751Curve25519PubKeySize = x25519SharedSecretSize + P751PubKeySize | |||
SidhP751Curve25519PrvKeySize = x25519SharedSecretSize + P751PrvKeySize | |||
SidhP751Curve25519SharedKeySize = x25519SharedSecretSize + P751SharedSecretSize | |||
SidhP751Curve448PubKeySize = x448.SharedSecretSize + P751PubKeySize | |||
SidhP751Curve448PrvKeySize = x448.SharedSecretSize + P751PrvKeySize | |||
SidhP751Curve448SharedKeySize = x448.SharedSecretSize + P751SharedSecretSize | |||
) | |||
type keySchedule13 struct { | |||
suite *cipherSuite | |||
transcriptHash hash.Hash // uses the cipher suite hash algo | |||
@@ -49,6 +66,23 @@ type keySchedule13 struct { | |||
config *Config // Used for KeyLogWriter callback, nil if keylogging is disabled. | |||
} | |||
type Role uint8 | |||
const ( | |||
kRole_Server = iota | |||
kRole_Client | |||
) | |||
// OZAPTF: comment | |||
// Depending on role returns pair of key variant to be used by | |||
// local and remote process. | |||
func getSidhKeyVariant(r Role) (sidh.KeyVariant, sidh.KeyVariant) { | |||
if r == kRole_Client { | |||
return sidh.KeyVariant_SIDH_A, sidh.KeyVariant_SIDH_B | |||
} | |||
return sidh.KeyVariant_SIDH_B, sidh.KeyVariant_SIDH_A | |||
} | |||
func newKeySchedule13(suite *cipherSuite, config *Config, clientRandom []byte) *keySchedule13 { | |||
if config.KeyLogWriter == nil { | |||
clientRandom = nil | |||
@@ -158,12 +192,13 @@ CurvePreferenceLoop: | |||
} | |||
} | |||
privateKey, serverKS, err := config.generateKeyShare(ks.group) | |||
privateKey, serverKS, err := config.generateKeyShare(ks.group, kRole_Server) | |||
if err != nil { | |||
c.sendAlert(alertInternalError) | |||
return err | |||
} | |||
hs.hello.keyShare = serverKS | |||
c.KeyShareCurve = serverKS.group | |||
hash := hashForSuite(hs.suite) | |||
hashSize := hash.Size() | |||
@@ -186,7 +221,7 @@ CurvePreferenceLoop: | |||
earlyClientCipher, _ := hs.keySchedule.prepareCipher(secretEarlyClient) | |||
ecdheSecret := deriveECDHESecret(ks, privateKey) | |||
ecdheSecret := deriveECDHESecret(ks, privateKey, kRole_Server) | |||
if ecdheSecret == nil { | |||
c.sendAlert(alertIllegalParameter) | |||
return errors.New("tls: bad ECDHE client share") | |||
@@ -225,6 +260,7 @@ CurvePreferenceLoop: | |||
certReq := new(certificateRequestMsg13) | |||
// extension 'signature_algorithms' MUST be specified | |||
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms13 | |||
certReq.supportedSignatureAlgorithmsCert = supportedSigAlgorithmsCert(supportedSignatureAlgorithms13) | |||
hs.keySchedule.write(certReq.marshal()) | |||
if _, err := hs.c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil { | |||
return err | |||
@@ -328,7 +364,7 @@ func (hs *serverHandshakeState) readClientFinished13(hasConfirmLock bool) error | |||
return unexpectedMessageError(certVerify, msg) | |||
} | |||
err, alertCode := verifyPeerCertificate( | |||
err, alertCode := verifyPeerHandshakeSignature( | |||
certVerify, | |||
pubKey, | |||
supportedSignatureAlgorithms13, | |||
@@ -540,9 +576,12 @@ func prepareDigitallySigned(hash crypto.Hash, context string, data []byte) []byt | |||
return h.Sum(nil) | |||
} | |||
func (c *Config) generateKeyShare(curveID CurveID) ([]byte, keyShare, error) { | |||
// generateKeyShare generates keypair. Private key is returned as first argument, public key | |||
// is returned in keyShare.data. keyshare.curveID stores ID of the scheme used. | |||
func (c *Config) generateKeyShare(curveID CurveID, role Role) ([]byte, keyShare, error) { | |||
// OZAPTF: refactor | |||
if curveID == X25519 { | |||
var scalar, public [32]byte | |||
var scalar, public [x25519SharedSecretSize]byte | |||
if _, err := io.ReadFull(c.rand(), scalar[:]); err != nil { | |||
return nil, keyShare{}, err | |||
} | |||
@@ -551,6 +590,69 @@ func (c *Config) generateKeyShare(curveID CurveID) ([]byte, keyShare, error) { | |||
return scalar[:], keyShare{group: curveID, data: public[:]}, nil | |||
} | |||
if curveID == SidhP751Curve25519 { | |||
var public25519 [x25519SharedSecretSize]byte | |||
var secret25519 [x25519SharedSecretSize]byte | |||
var public [SidhP751Curve25519PubKeySize]byte | |||
var secret [SidhP751Curve25519PrvKeySize]byte | |||
var variant, _ = getSidhKeyVariant(role) | |||
var prvKey = sidh.NewPrivateKey(sidh.FP_751, variant) | |||
if _, err := io.ReadFull(c.rand(), secret25519[:]); err != nil { | |||
return nil, keyShare{}, err | |||
} | |||
curve25519.ScalarBaseMult(&public25519, &secret25519) | |||
if prvKey.Generate(c.rand()) != nil { | |||
return nil, keyShare{}, errors.New("tls: private SIDH key generation failed") | |||
} | |||
pubKey, err := sidh.GeneratePublicKey(prvKey) | |||
if err != nil { | |||
return nil, keyShare{}, errors.New("tls: public SIDH key generation failed") | |||
} | |||
// Hybrid "shared key" = X25519 || P751 | |||
// OZAPTF SIZE-len | |||
copy(public[:], pubKey.Export()) | |||
copy(public[P751PubKeySize:], public25519[:]) | |||
copy(secret[:], prvKey.Export()) | |||
copy(secret[P751PrvKeySize:], secret25519[:]) | |||
return secret[:], keyShare{group: curveID, data: public[:]}, nil | |||
} | |||
if curveID == SidhP751Curve448 { | |||
var public448 [x448.SharedSecretSize]byte | |||
var secret448 [x448.SharedSecretSize]byte | |||
var public [SidhP751Curve448PubKeySize]byte | |||
var secret [SidhP751Curve448PrvKeySize]byte | |||
var variant, _ = getSidhKeyVariant(role) | |||
var prvKey = sidh.NewPrivateKey(sidh.FP_751, variant) | |||
if _, err := io.ReadFull(c.rand(), secret448[:]); err != nil { | |||
return nil, keyShare{}, err | |||
} | |||
x448.ScalarBaseMult(&public448, &secret448) | |||
if prvKey.Generate(c.rand()) != nil { | |||
return nil, keyShare{}, errors.New("tls: private SIDH key generation failed") | |||
} | |||
pubKey, err := sidh.GeneratePublicKey(prvKey) | |||
if err != nil { | |||
return nil, keyShare{}, errors.New("tls: public SIDH key generation failed") | |||
} | |||
// Hybrid "shared key" = X448 || P751 | |||
// OZAPTF SIZE-len | |||
copy(public[:], pubKey.Export()) | |||
copy(public[P751PubKeySize:], public448[:]) | |||
copy(secret[:], prvKey.Export()) | |||
copy(secret[P751PrvKeySize:], secret448[:]) | |||
return secret[:], keyShare{group: curveID, data: public[:]}, nil | |||
} | |||
// else... | |||
curve, ok := curveForCurveID(curveID) | |||
if !ok { | |||
return nil, keyShare{}, errors.New("tls: preferredCurves includes unsupported curve") | |||
@@ -565,19 +667,93 @@ func (c *Config) generateKeyShare(curveID CurveID) ([]byte, keyShare, error) { | |||
return privateKey, keyShare{group: curveID, data: ecdhePublic}, nil | |||
} | |||
func deriveECDHESecret(ks keyShare, secretKey []byte) []byte { | |||
func deriveECDHESecret(ks keyShare, secretKey []byte, role Role) []byte { | |||
if ks.group == X25519 { | |||
if len(ks.data) != 32 { | |||
if len(ks.data) != x25519SharedSecretSize { | |||
return nil | |||
} | |||
var theirPublic, sharedKey, scalar [32]byte | |||
var theirPublic, sharedKey, scalar [x25519SharedSecretSize]byte | |||
copy(theirPublic[:], ks.data) | |||
copy(scalar[:], secretKey) | |||
curve25519.ScalarMult(&sharedKey, &scalar, &theirPublic) | |||
return sharedKey[:] | |||
} | |||
if ks.group == SidhP751Curve25519 { | |||
var prvVariant, pubVariant = getSidhKeyVariant(role) | |||
var sharedKey [SidhP751Curve25519SharedKeySize]byte | |||
var pubX25519, prvX25519, sharedKeyX25519 [x25519SharedSecretSize]byte | |||
if len(ks.data) != SidhP751Curve25519PubKeySize { | |||
return nil | |||
} | |||
if len(secretKey) != SidhP751Curve25519PrvKeySize { | |||
return nil | |||
} | |||
prvKey := sidh.NewPrivateKey(sidh.FP_751, prvVariant) | |||
pubKey := sidh.NewPublicKey(sidh.FP_751, pubVariant) | |||
if err := prvKey.Import(secretKey[:P751PrvKeySize]); err != nil { | |||
return nil | |||
} | |||
if err := pubKey.Import(ks.data[:P751PubKeySize]); err != nil { | |||
return nil | |||
} | |||
sharedSidhKey, err := sidh.DeriveSecret(prvKey, pubKey) | |||
if err != nil { | |||
return nil | |||
} | |||
// OZAPTF: same thing for x25519 | |||
copy(prvX25519[:], secretKey[P751PrvKeySize:]) | |||
copy(pubX25519[:], ks.data[P751PubKeySize:]) | |||
curve25519.ScalarMult(&sharedKeyX25519, &prvX25519, &pubX25519) | |||
// format shared key | |||
copy(sharedKey[:], sharedSidhKey) | |||
copy(sharedKey[P751SharedSecretSize:], sharedKeyX25519[:]) | |||
return sharedKey[:] | |||
} | |||
if ks.group == SidhP751Curve448 { | |||
var prvVariant, pubVariant = getSidhKeyVariant(role) | |||
var sharedKey [SidhP751Curve448SharedKeySize]byte | |||
var pubX448, prvX448, sharedKeyX448 [x448.SharedSecretSize]byte | |||
if len(ks.data) != SidhP751Curve448PubKeySize { | |||
return nil | |||
} | |||
if len(secretKey) != SidhP751Curve448PrvKeySize { | |||
return nil | |||
} | |||
prvKey := sidh.NewPrivateKey(sidh.FP_751, prvVariant) | |||
pubKey := sidh.NewPublicKey(sidh.FP_751, pubVariant) | |||
if err := prvKey.Import(secretKey[:P751PrvKeySize]); err != nil { | |||
return nil | |||
} | |||
if err := pubKey.Import(ks.data[:P751PubKeySize]); err != nil { | |||
return nil | |||
} | |||
sharedSidhKey, err := sidh.DeriveSecret(prvKey, pubKey) | |||
if err != nil { | |||
return nil | |||
} | |||
// OZAPTF: same thing for x25519 | |||
copy(prvX448[:], secretKey[P751PrvKeySize:]) | |||
copy(pubX448[:], ks.data[P751PubKeySize:]) | |||
x448.ScalarMult(&sharedKeyX448, &prvX448, &pubX448) | |||
// format shared key | |||
copy(sharedKey[:], sharedSidhKey) | |||
copy(sharedKey[P751SharedSecretSize:], sharedKeyX448[:]) | |||
return sharedKey[:] | |||
} | |||
curve, ok := curveForCurveID(ks.group) | |||
if !ok { | |||
return nil | |||
@@ -841,7 +1017,7 @@ func (hs *clientHandshakeState) processEncryptedExtensions(ee *encryptedExtensio | |||
return nil | |||
} | |||
func verifyPeerCertificate( | |||
func verifyPeerHandshakeSignature( | |||
certVerify *certificateVerifyMsg, | |||
pubKey crypto.PublicKey, | |||
signAlgosKnown []SignatureScheme, | |||
@@ -953,6 +1129,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||
hash := hashForSuite(hs.suite) | |||
hashSize := hash.Size() | |||
serverHello := hs.serverHello | |||
c.scts = serverHello.scts | |||
// middlebox compatibility mode, send CCS before second flight. | |||
if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil { | |||
@@ -969,7 +1146,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||
// 0-RTT is not supported yet, so use an empty PSK. | |||
hs.keySchedule.setSecret(nil) | |||
ecdheSecret := deriveECDHESecret(serverHello.keyShare, hs.privateKey) | |||
ecdheSecret := deriveECDHESecret(serverHello.keyShare, hs.privateKey, kRole_Client) | |||
if ecdheSecret == nil { | |||
c.sendAlert(alertIllegalParameter) | |||
return errors.New("tls: bad ECDHE server share") | |||
@@ -1048,7 +1225,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||
c.sendAlert(alertUnexpectedMessage) | |||
return unexpectedMessageError(certVerifyMsg, msg) | |||
} | |||
err, alertCode := verifyPeerCertificate( | |||
err, alertCode := verifyPeerHandshakeSignature( | |||
certVerifyMsg, | |||
hs.c.peerCertificates[0].PublicKey, | |||
hs.hello.supportedSignatureAlgorithms, | |||
@@ -1111,5 +1288,18 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||
return errors.New("tls: unexpected data after handshake") | |||
} | |||
c.in.setCipher(c.vers, appServerCipher) | |||
c.KeyShareCurve = clientKS.group | |||
return nil | |||
} | |||
// supportedSigAlgorithmsCert iterates over schemes and filters out those algorithms | |||
// which are not supported for certificate verification. | |||
func supportedSigAlgorithmsCert(schemes []SignatureScheme) (ret []SignatureScheme) { | |||
for _, sig := range schemes { | |||
// X509 doesn't support PSS signatures | |||
if !signatureSchemeIsPSS(sig) { | |||
ret = append(ret, sig) | |||
} | |||
} | |||
return | |||
} |
@@ -61,7 +61,8 @@ There are number of things that need to be setup before running tests. Most impo | |||
``` | |||
git clone https://github.com/cloudflare/tls-tris.git | |||
cd tls-tris; make -f _dev/Makefile build-all | |||
cd tls-tris; cp _dev/utils/pre-commit .git/hooks/ | |||
make -f _dev/Makefile build-all | |||
``` | |||
### Testing | |||
@@ -70,7 +71,7 @@ We run 3 kinds of test:. | |||
* Unit testing: <br/>``make -f _dev/Makefile test-unit`` | |||
* Testing against BoringSSL test suite: <br/>``make -f _dev/Makefile test-bogo`` | |||
* Compatibility testing (see below):<br/>``make -f _dev/Makefile test-compat`` | |||
* Compatibility testing (see below):<br/>``make -f _dev/Makefile test-interop`` | |||
To run all the tests in one go use: | |||
``` | |||
@@ -8,6 +8,7 @@ BUILD_DIR ?= $(PRJ_DIR)/_dev/GOROOT | |||
# Compiler | |||
GO ?= go | |||
DOCKER ?= docker | |||
GIT ?= git | |||
# Build environment | |||
OS ?= $(shell $(GO) env GOHOSTOS) | |||
@@ -18,14 +19,19 @@ GOROOT_ENV := $(shell $(GO) env GOROOT) | |||
GOROOT_LOCAL = $(BUILD_DIR)/$(OS_ARCH) | |||
# Flag indicates wheter invoke "go install -race std". Supported only on amd64 with CGO enabled | |||
INSTALL_RACE:= $(words $(filter $(ARCH)_$(shell go env CGO_ENABLED), amd64_1)) | |||
TMP_DIR := $(shell mktemp -d) | |||
# Test targets used for compatibility testing | |||
TARGET_TEST_COMPAT=boring picotls tstclnt | |||
# Some target-specific constants | |||
BORINGSSL_REVISION=1530ef3e | |||
BORINGSSL_REVISION=03de6813d8992a649092b4874ef0ebc022e2f58a | |||
BOGO_DOCKER_TRIS_LOCATION=/go/src/github.com/cloudflare/tls-tris | |||
# NOBS crypto | |||
NOBS_REPO ?= https://github.com/henrydcase/nobscrypto.git | |||
############### | |||
# | |||
# Build targets | |||
@@ -48,13 +54,24 @@ $(BUILD_DIR)/$(OS_ARCH)/.ok_$(VER_OS_ARCH): clean | |||
# Apply additional patches | |||
for p in $(wildcard $(DEV_DIR)/patches/*); do patch -d "$(GOROOT_LOCAL)" -p1 < "$$p"; done | |||
# Vendor nobs | |||
$(GIT) clone $(NOBS_REPO) $(TMP_DIR)/nobs | |||
cd $(TMP_DIR)/nobs; make vendor-sidh-for-tls | |||
cp -rf $(TMP_DIR)/nobs/tls_vendor/* $(GOROOT_LOCAL)/src/vendor/ | |||
# Create go package | |||
GOARCH=$(ARCH) GOROOT="$(GOROOT_LOCAL)" $(GO) install -v std | |||
ifeq ($(INSTALL_RACE),1) | |||
GOARCH=$(ARCH) GOROOT="$(GOROOT_LOCAL)" $(GO) install -race -v std | |||
endif | |||
rm -rf $(TMP_DIR) | |||
@touch "$@" | |||
# Swap TLS implementation | |||
swap_tls: | |||
rm -r $(GOROOT_LOCAL)/src/crypto/tls/* | |||
rsync -rltgoD $(PRJ_DIR)/ $(GOROOT_LOCAL)/src/crypto/tls/ --exclude=$(lastword $(subst /, ,$(DEV_DIR))) | |||
build-test-%: $(BUILD_DIR)/$(OS_ARCH)/.ok_$(VER_OS_ARCH) | |||
$(DOCKER) build $(BUILDARG) -t tls-tris:$* $(DEV_DIR)/$* | |||
$(DOCKER) build $(BUILDARG) -t $(*)-localserver $(DEV_DIR)/$* | |||
@@ -112,5 +129,7 @@ clean: | |||
clean-all: clean | |||
rm -rf $(BUILD_DIR) | |||
fmtcheck: | |||
$(DEV_DIR)/utils/fmtcheck.sh | |||
.PHONY: $(BUILD_DIR) clean build build-test test test-unit test-bogo test-compat | |||
.PHONY: $(BUILD_DIR) clean build build-test test test-unit test-bogo test-interop |
@@ -10,8 +10,8 @@ RUN apk add --update \ | |||
ENV CGO_ENABLED=0 | |||
RUN git clone https://github.com/FiloSottile/crypto-tls-bogo-shim \ | |||
/go/src/github.com/FiloSottile/crypto-tls-bogo-shim | |||
RUN git clone https://github.com/henrydcase/crypto-tls-bogo-shim \ | |||
/go/src/github.com/henrydcase/crypto-tls-bogo-shim | |||
# Draft 18 with client-tests branch | |||
#ARG REVISION=3f5e87d6a1931b6f6930e4eadb7b2d0b2aa7c588 | |||
@@ -20,10 +20,19 @@ RUN git clone https://github.com/FiloSottile/crypto-tls-bogo-shim \ | |||
#ARG REVISION=81cc32b846c9fe2ea32613287e57a6a0db7bbb9a | |||
# Draft 22 with draft22-client branch (client-tests + draft22) | |||
ARG REVISION=f9729b5e4eafb1f1d313949388c3c2b167e84734 | |||
# ARG REVISION=f9729b5e4eafb1f1d313949388c3c2b167e84734 | |||
RUN cd /go/src/github.com/FiloSottile/crypto-tls-bogo-shim && \ | |||
# Draft 23 | |||
#ARG REVISION=d07b9e80a87c871c2569ce4aabd06695336c5dc5 | |||
# Draft 23 (+ client authentication) | |||
# ARG REVISION=cd33ad248ae9490854f0077ca046b47cac3735bf | |||
# Draft 28 | |||
ARG REVISION=33204d1eaa497819c6325998d7ba6b66316790f3 | |||
RUN cd /go/src/github.com/henrydcase/crypto-tls-bogo-shim && \ | |||
git checkout $REVISION | |||
WORKDIR /go/src/github.com/FiloSottile/crypto-tls-bogo-shim | |||
WORKDIR /go/src/github.com/henrydcase/crypto-tls-bogo-shim | |||
CMD ["make", "run"] |
@@ -34,15 +34,24 @@ RUN mkdir boringssl/build | |||
# ARG REVISION=89917a5 | |||
# Draft 18 | |||
#ARG REVISION=9b885c5 | |||
# ARG REVISION=9b885c5 | |||
# Draft 18, but with "bssl server -loop -www" support and build fix | |||
ARG REVISION=40b24c8154 | |||
# ARG REVISION=40b24c8154 | |||
# Draft 21 | |||
#ARG REVISION=cd8470f | |||
# ARG REVISION=cd8470f | |||
# Draft 22 | |||
ARG REVISION=1530ef3e | |||
# ARG REVISION=1530ef3e | |||
# Draft 23 | |||
# ARG REVISION=cb15cfda29c0c60d8d74145b17c93b43a7667837 | |||
# Draft 28 | |||
# ARG REVISION=861f384d7bc59241a9df1634ae938d8e75be2d30 | |||
# Latest | |||
ARG REVISION=03de6813d8992a649092b4874ef0ebc022e2f58a | |||
RUN cd boringssl && git fetch | |||
RUN cd boringssl && git checkout $REVISION | |||
@@ -2,7 +2,7 @@ | |||
set -e | |||
/boringssl/build/tool/bssl client -grease -min-version tls1.3 -max-version tls1.3 \ | |||
-tls13-variant draft22 -session-out /session -connect "$@" < /httpreq.txt | |||
-tls13-variant draft28 -session-out /session -connect "$@" < /httpreq.txt | |||
exec /boringssl/build/tool/bssl client -grease -min-version tls1.3 -max-version tls1.3 \ | |||
-tls13-variant draft22 -session-in /session -connect "$@" < /httpreq.txt | |||
-tls13-variant draft28 -session-in /session -connect "$@" < /httpreq.txt | |||
@@ -6,21 +6,21 @@ set -x | |||
bssl server \ | |||
-key rsa.pem \ | |||
-min-version tls1.2 -max-version tls1.3 \ | |||
-tls13-draft22-variant \ | |||
-tls13-variant draft28 \ | |||
-accept 1443 -loop -www 2>&1 & | |||
# ECDSA | |||
bssl server \ | |||
-key ecdsa.pem \ | |||
-min-version tls1.2 -max-version tls1.3 \ | |||
-tls13-draft22-variant \ | |||
-tls13-variant draft28 \ | |||
-accept 2443 -loop -www 2>&1 & | |||
# Require client authentication (with ECDSA) | |||
bssl server \ | |||
-key ecdsa.pem \ | |||
-min-version tls1.2 -max-version tls1.3 \ | |||
-tls13-draft22-variant \ | |||
-tls13-variant draft28 \ | |||
-accept 6443 -loop -www \ | |||
-require-any-client-cert -debug 2>&1 & | |||
@@ -0,0 +1,4 @@ | |||
all: | |||
make -f ~/repos/tls-tris/_dev/Makefile swap_tls | |||
GOROOT=../GOROOT/linux_amd64 go build client.go | |||
GOROOT=../GOROOT/linux_amd64 go build server.go |
@@ -0,0 +1,239 @@ | |||
package main | |||
import ( | |||
"crypto/tls" | |||
"crypto/x509" | |||
"flag" | |||
"fmt" | |||
"io" | |||
"log" | |||
"os" | |||
"strings" | |||
) | |||
var tlsVersionToName = map[uint16]string{ | |||
tls.VersionTLS10: "1.0", | |||
tls.VersionTLS11: "1.1", | |||
tls.VersionTLS12: "1.2", | |||
tls.VersionTLS13: "1.3", | |||
tls.VersionTLS13Draft18: "1.3 (draft 18)", | |||
tls.VersionTLS13Draft23: "1.3 (draft 23)", | |||
} | |||
var cipherSuiteIdToName = map[uint16]string{ | |||
tls.TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA", | |||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", | |||
tls.TLS_AES_128_GCM_SHA256: "TLS_AES_128_GCM_SHA256", | |||
tls.TLS_AES_256_GCM_SHA384: "TLS_AES_256_GCM_SHA384", | |||
tls.TLS_CHACHA20_POLY1305_SHA256: "TLS_CHACHA20_POLY1305_SHA256", | |||
tls.TLS_ECDHE_SIDH_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_SIDH_ECDSA_WITH_AES_128_GCM_SHA256", | |||
} | |||
type Client struct { | |||
KeyLogWriter io.Writer | |||
failed uint | |||
client_cert tls.Certificate | |||
client_certpool *x509.CertPool | |||
preferedCurves []tls.CurveID | |||
} | |||
func (c *Client) run(addr string, version, cipherSuite uint16) { | |||
tls_config := &tls.Config{ | |||
InsecureSkipVerify: true, | |||
MinVersion: version, | |||
MaxVersion: version, | |||
CipherSuites: []uint16{cipherSuite}, | |||
KeyLogWriter: c.KeyLogWriter, | |||
Certificates: []tls.Certificate{c.client_cert}, | |||
RootCAs: c.client_certpool, | |||
CurvePreferences: c.preferedCurves, | |||
} | |||
con, err := tls.Dial("tcp", addr, tls_config) | |||
if err != nil { | |||
fmt.Printf("handshake failed: %v\n\n", err) | |||
c.failed++ | |||
return | |||
} | |||
defer con.Close() | |||
fmt.Printf("Established: TLS ver %X CipherSuite: %X CurveID %X\n", | |||
con.ConnectionState().Version, | |||
con.ConnectionState().CipherSuite, | |||
con.KeyShareCurve) | |||
_, err = con.Write([]byte("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")) | |||
if err != nil { | |||
fmt.Printf("Write failed: %v\n\n", err) | |||
c.failed++ | |||
return | |||
} | |||
buf := make([]byte, 1024) | |||
n, err := con.Read(buf) | |||
// A non-zero read with EOF is acceptable and occurs when a close_notify | |||
// is received right after reading data (observed with NSS selfserv). | |||
if !(n > 0 && err == io.EOF) && err != nil { | |||
fmt.Printf("Read failed: %v\n\n", err) | |||
c.failed++ | |||
return | |||
} | |||
fmt.Printf("Read %d bytes\n", n) | |||
fmt.Println("OK\n") | |||
} | |||
func main() { | |||
var keylog_file, enable_sidh string | |||
var enable_rsa, enable_ecdsa, client_auth bool | |||
flag.StringVar(&keylog_file, "keylogfile", "", "Secrets will be logged here") | |||
flag.BoolVar(&enable_rsa, "rsa", false, "Whether to enable RSA cipher suites") | |||
flag.BoolVar(&enable_ecdsa, "ecdsa", false, "Whether to enable ECDSA cipher suites") | |||
flag.BoolVar(&client_auth, "cliauth", false, "Whether to enable client authentication") | |||
flag.StringVar(&enable_sidh, "sidh", "", "Whether to use SIDH. Argumens: X25519 or X448") | |||
flag.Parse() | |||
if flag.NArg() != 1 { | |||
flag.Usage() | |||
os.Exit(1) | |||
} | |||
addr := flag.Arg(0) | |||
if !strings.Contains(addr, ":") { | |||
addr += ":443" | |||
} | |||
client := Client{} | |||
if keylog_file == "" { | |||
keylog_file = os.Getenv("SSLKEYLOGFILE") | |||
} | |||
if keylog_file != "" { | |||
keylog_writer, err := os.OpenFile(keylog_file, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) | |||
if err != nil { | |||
log.Fatalf("Cannot open keylog file: %v", err) | |||
} | |||
client.KeyLogWriter = keylog_writer | |||
log.Println("Enabled keylog") | |||
} | |||
if client_auth { | |||
var err error | |||
client.client_cert, err = tls.X509KeyPair([]byte(client_crt), []byte(client_key)) | |||
if err != nil { | |||
panic("Can't load client certificate") | |||
} | |||
client.client_certpool = x509.NewCertPool() | |||
if !client.client_certpool.AppendCertsFromPEM([]byte(client_ca)) { | |||
panic("Can't load client CA cert") | |||
} | |||
} | |||
if enable_rsa { | |||
// Sanity check: TLS 1.2 with the mandatory cipher suite from RFC 5246 | |||
client.run(addr, tls.VersionTLS12, tls.TLS_RSA_WITH_AES_128_CBC_SHA) | |||
} | |||
if enable_ecdsa { | |||
// Sane cipher suite for TLS 1.2 with an ECDSA cert (as used by boringssl) | |||
client.run(addr, tls.VersionTLS12, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) | |||
} | |||
if enable_sidh != "" { | |||
switch enable_sidh { | |||
case "X25519": | |||
client.preferedCurves = []tls.CurveID{tls.SidhP751Curve25519} | |||
case "X448": | |||
client.preferedCurves = []tls.CurveID{tls.SidhP751Curve448} | |||
} | |||
client.run(addr, tls.VersionTLS13, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) | |||
} | |||
if client.failed > 0 { | |||
log.Fatalf("Failed handshakes: %d\n", client.failed) | |||
} else { | |||
fmt.Println("All handshakes passed") | |||
} | |||
} | |||
const ( | |||
client_ca = `-----BEGIN CERTIFICATE----- | |||
MIIFYDCCA0igAwIBAgIJAPpBgIvtQb1EMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV | |||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX | |||
aWRnaXRzIFB0eSBMdGQwHhcNMTgwMjEzMjAxNjA3WhcNMTkwMjEzMjAxNjA3WjBF | |||
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 | |||
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC | |||
CgKCAgEAr4xgdmB4DaEh8zRFmg/1ZxYhQZMUP0iQX/Y8nDWxNlcd42p3TgpY1biz | |||
jrq58ln9Om4U/GAn2RmtBAynSBXIlR5oVa44JeMM8Ka8R/dMKyHpF0Nj2EJB9unb | |||
TC33PfzOlnKQxATwevnnhI6tGluWmwvxXUi7WnX0di+nQg9HrIVom3KrmRr2/41y | |||
g497ccYUuNnKE6sewGdGzw045oWZpMDA2Us+MFo1IywOurjaM9bueRhPTcIiQ8RE | |||
h7qb+FRwfxaj9ynZA2PCM7WMSSWCiZJV0uj/pshYF2lvtJcJef4dhwnsYBpc+mgx | |||
2q9qcUBeo3ZHbi1/PRqjwSmcW3yY5cQRbpYp6xFmgmX3oHQkVXS0UlpNVZ+morcS | |||
HEpaK8b76fCFcL5yFsAJkPPfny1IKU+CfaVq60dM/mxbEW6J4mZT/uAiqrCilMC+ | |||
FyiATCZur8Ks7p47eZy700DllLod7gWTiuZTgHeQFVoX+jxbCZKlFn5Xspu8ALoK | |||
Mla/q83mICRVy3+eMUsD7DNvoWYpCAYy/oMk0VWfrQ48JkCGbBW2PW/dU2nmqVhY | |||
/11rurkr+1TUvYodnajANtXvUjW1DPOLb4dES4Qc4b7Fw8eFXrARhl5mXiL5HFKR | |||
/VnRshiJ+QwTVkxl+KkZHEm/WS8QD+Zd8leAxh9MCoaU/XrBUBkCAwEAAaNTMFEw | |||
HQYDVR0OBBYEFKUinuD1xRvcNd2Wti/PnBJp7On1MB8GA1UdIwQYMBaAFKUinuD1 | |||
xRvcNd2Wti/PnBJp7On1MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD | |||
ggIBAJdJrNBftqkTs2HyuJ3x5RIsTxYh85hJYwNOdFLyzVG6HER9jRCnvmNTjG0O | |||
I5wz5hQvDpwXs4BCCXHQZrTLAi3BEjq3AjrmR/XeGHulbWh3eh8LVu7MiLRgt+Ys | |||
GnL2IaERrbkje24nCCMNPbI3fGDQEhTIYmmX8RJp+5BOJgCycKk6pFgfrjJv2C+d | |||
78pcjlYII6M4vPnr/a08M49Bq6b5ADvIfe5G2KrUvD/+vwoAwv6d/daymHCQ2rY5 | |||
kmdVk9VUp3Q4uKoeej4ENJSAUNTV7oTu346oc7q9sJffB5OltqbrE7ichak7lL+v | |||
EjArZHElAhKNFXRZViCMvGDs+7JztqbsfT8Xb6Z27e+WyudB2bOUGm3hKuTIl06D | |||
bA7yUskwEhmkd1CJqO5RLEJjKitOqe6Ye0/GsmPQNDK8GvyXTyGQK5OqBuzEexF0 | |||
mlPoIhpSVH3K9SkRTTHvvcbdYlaQLi6gKq2uhbk4PnS2nfBtXqYIy9mxcgBJzLiB | |||
/ydfLcf3GClwgvO1JHp6qAl4CO7oe8jqHpoGuznwi1aqkTyNkQWh0OXq3MS+dyqB | |||
2yXFCFIeKCx18TE1OtuTD3ppBDjpyd0o/a6kYR3FDmdks/J33bGwLsLH3lbN6VjF | |||
PNfNkaE1tfkpSGYsuT1DPxX8aAT4JLUfZ1Si6iO+E0Sj9LXA | |||
-----END CERTIFICATE-----` | |||
client_crt = `-----BEGIN CERTIFICATE----- | |||
MIID/jCCAeYCAQEwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV | |||
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0 | |||
ZDAeFw0xODAyMTMyMDU0MjZaFw0xOTAyMTMyMDU0MjZaMEUxCzAJBgNVBAYTAkFV | |||
MRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRz | |||
IFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDD1li7/35Q | |||
C/T6FACSbsd0WlImu42i6w96wfngAfEgbz5Ip+IA2rJ8G5LNHTYYCpr9LlmhY6zm | |||
soHgAkff6XwUnZaetX01UmGP4CD4D3UumkR1uKY4bCSNImm53SZgelOznpsqAWKE | |||
zosMrDcOAJKJSN411KwVzWysfRCPyxvmLETzU9KHFCJ1oY3t1HzYIAqpHv9sMSst | |||
dNHW3X7bWEAVKCQMKO+rWe/wAhE4iTVdlRi02oRoRWSVj41+nk6jI8KJNq70stHc | |||
QSST0A7SUacPYKJWqJRhP1pZ6k4G3ZVE8332az7jvcN1uGGjERZoUbZxGB+mbMCC | |||
GJwnwnNiI6/hAgMBAAEwDQYJKoZIhvcNAQELBQADggIBADNr6QMl57CdHEzNwc2M | |||
CSZsuOLakp8YiovVDOXJ/p/lykUIIcR1rI1iNfb8oOFTZmrndGZVAh76EExdMHYG | |||
m+4Vr2+z/73AZwvhhnLhftOKFFwkdjCfXouPlkc/zmhOORakIFGlLZFkuZRY6k2D | |||
Q8uIt7E5uXSVl11A1LxN5X8lhK2G4lxJZuj1AqEFj9QD44Qy+MdgX38lzGCEXd8c | |||
Y5K8zLJGbgXgYaFxqd0bImfjgjj82+Mui0OTV5PcRlczJX08ygKjcoAMVyvPHu72 | |||
3zzxvoNcqUrvbptVvg9c7FSOpK95YZOe1LiyqZCwNJQl4fPRE++XQ4zDNdyiAp76 | |||
a6BQg/M8gOpV/VBMTsNDr/yP/7eBqkfvU7jLfz7wKMDdcjeZnKom42f+/XOLEo6E | |||
hyDuHGdQh10bZD/Ukcs69+pA3ioic1A8pQzAElH3IuDBsMJg30x8tACLKNcUY8BE | |||
2eJgrCxWcvq88DeAT03W9AVpFZA8ZQUR3SHCquMBFogsmUDDMN+CoC0u5dBwHP+O | |||
9rmWOXn8gp/zBCKGwemgVV5vSNzJs7z3aoqIiAABl56LBaXxjKzRmXoB/SyUW5zl | |||
1zy4SQTE6SJYqqU6h2yRdT8n0oWN3AMy0VxbJTRq32kdYJVQK9cLKVqpxtCCtPnN | |||
3lV+HDsj7k+AJjHiu1F4O+sp | |||
-----END CERTIFICATE-----` | |||
client_key = `-----BEGIN RSA PRIVATE KEY----- | |||
MIIEpAIBAAKCAQEAw9ZYu/9+UAv0+hQAkm7HdFpSJruNousPesH54AHxIG8+SKfi | |||
ANqyfBuSzR02GAqa/S5ZoWOs5rKB4AJH3+l8FJ2WnrV9NVJhj+Ag+A91LppEdbim | |||
OGwkjSJpud0mYHpTs56bKgFihM6LDKw3DgCSiUjeNdSsFc1srH0Qj8sb5ixE81PS | |||
hxQidaGN7dR82CAKqR7/bDErLXTR1t1+21hAFSgkDCjvq1nv8AIROIk1XZUYtNqE | |||
aEVklY+Nfp5OoyPCiTau9LLR3EEkk9AO0lGnD2CiVqiUYT9aWepOBt2VRPN99ms+ | |||
473DdbhhoxEWaFG2cRgfpmzAghicJ8JzYiOv4QIDAQABAoIBADKcbZhAYjt7q5cJ | |||
nlA5svA9+2cpJ2SITRrTkKk0t0VDmpwaTw0bd+8dDSZXO0ihTQbLeLx9zwxb67ah | |||
wEN8yuVlCK0BiFdEcBRHvx18mTMvCSxHSSXhxNx4nUw8fBOI6aLNBZqoevaJjmP7 | |||
CctjmHtESrEswkBsM36sX6BZxF8Kc4Q5Znuxqksnl6HNoxnjhmygJmYCFTToiTHa | |||
f2HWKBiZfgfxX7WEuHer3h6nmBbBCOX1/hcipBMBBVIqFl1ZSIF/B3lR8UV4/X+a | |||
SNMqggOqkEIuHKkSCKo1lNxEPP2p54EHrKkjepoqMzIFuYnn4qWesMznpmy+zBGB | |||
6PCjfzUCgYEA92etvRVQjBx8dHTSiyCNbS1ELgiCkzar8PGH+ytTIaj/TTF1LfAi | |||
UYRp5MtOKmQXxE3IRLDF8P8rEKC06aV12hVwhd2xfHjje+KZkwWZ2PIj+GbK7f1r | |||
MvKN5eE0NhGiSvu5SiFuks/SV8Qc4StFPmiWf33XKvJuAWNkCu+bUZsCgYEAyqQL | |||
nVNKTlgHNKDJKMHi/buZt8wtwGGXCxcv+w88PmEC0OCbH/V2niCPLvFmK1xDXpru | |||
k7z9FTc+QeasEMtxY/Gcs3IgUzxOHxAL7cn6KBM44uDhpIcv3BFWtR053acVU6S4 | |||
IKuijWIJNJEk2qksgQTX7Mv/xq2uXvfZqajdKjMCgYEA3x+5F9s+Pm5+a4TkUSc1 | |||
hS4a3C0+ncfjv7QEwCftnGDOhu7A0IJOYRg7bGVShHaq3JaNtC19BwEJ9MALCOD5 | |||
bYqCZahvpmNcPeE6Qdb+TiLq/96sy4AOiu8nvBejv9Ode2SUUd/e2jbla9Ppe8VL | |||
eKJYgHicchYb0dKyag54FFsCgYEAuToEB9W3aS9bvsZtuZyooSfXFcND2sMZrqCO | |||
Uh2WAqroSQfVo/vaZiX623z62A2o4xQZmd+5MqhhdxmkFGHyDtouU3SxiYPpIMmp | |||
Lb1etT0E1ZWbi6mqnK0YpcrGNw5gFynMyMg6eKOxKGS33EuhC3ni6Wd7MB9X8ST6 | |||
x/M73jMCgYBBge3/ugnZPE78TDL3DdefrjeYFaKhVc622eimS/MEPbkbdxh8azTM | |||
LAoibwDU1NC8/3MfOBYMe6Qklu3kjexOJrfdo0Z7Khgd9F8A4tKwslUndSSlAfKF | |||
2rjfqabVMZMLZ2XEbA4W5JTfaZS4YYGcrjY7+i7OsnSxoYG2sb+xlQ== | |||
-----END RSA PRIVATE KEY-----` | |||
) |
@@ -0,0 +1,274 @@ | |||
package main | |||
import ( | |||
"crypto/tls" | |||
"crypto/x509" | |||
"encoding/hex" | |||
"flag" | |||
"fmt" | |||
"io" | |||
"log" | |||
"net/http" | |||
"os" | |||
"time" | |||
) | |||
type ZeroRTT_t int | |||
type Algo_t int | |||
// Bitset | |||
const ( | |||
ZeroRTT_None ZeroRTT_t = 0 | |||
ZeroRTT_Offer = 1 << 0 | |||
ZeroRTT_Accept = 1 << 1 | |||
) | |||
const ( | |||
PubKeyRSA Algo_t = iota | |||
PubKeyECDSA | |||
KeyShareSIDH | |||
) | |||
type server struct { | |||
Address string | |||
ZeroRTT ZeroRTT_t | |||
PubKey Algo_t | |||
ClientAuthMethod tls.ClientAuthType | |||
use_sidh bool | |||
curves []tls.CurveID | |||
} | |||
var tlsVersionToName = map[uint16]string{ | |||
tls.VersionTLS10: "1.0", | |||
tls.VersionTLS11: "1.1", | |||
tls.VersionTLS12: "1.2", | |||
tls.VersionTLS13: "1.3", | |||
tls.VersionTLS13Draft23: "1.3 (draft 23)", | |||
} | |||
func NewServer() *server { | |||
s := new(server) | |||
s.ClientAuthMethod = tls.NoClientCert | |||
s.ZeroRTT = ZeroRTT_None | |||
s.Address = "0.0.0.1:443" | |||
s.curves = []tls.CurveID{tls.X25519, tls.CurveP256, tls.CurveP384, tls.CurveP521} | |||
return s | |||
} | |||
func (s *server) start() { | |||
cert, err := tls.X509KeyPair([]byte(ecdsaCert), []byte(ecdsaKey)) | |||
if s.PubKey == PubKeyRSA { | |||
cert, err = tls.X509KeyPair([]byte(rsaCert), []byte(rsaKey)) | |||
} | |||
if err != nil { | |||
log.Fatal(err) | |||
} | |||
var Max0RTTDataSize uint32 | |||
if (s.ZeroRTT & ZeroRTT_Offer) == ZeroRTT_Offer { | |||
Max0RTTDataSize = 100 * 1024 | |||
} | |||
var keyLogWriter io.Writer | |||
if keyLogFile := os.Getenv("SSLKEYLOGFILE"); keyLogFile != "" { | |||
keyLogWriter, err = os.OpenFile(keyLogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) | |||
if err != nil { | |||
log.Fatalf("Cannot open keylog file: %v", err) | |||
} | |||
log.Println("Enabled keylog") | |||
} | |||
clientCAs := x509.NewCertPool() | |||
clientCAs.AppendCertsFromPEM([]byte(rsaCa_client)) | |||
httpServer := &http.Server{ | |||
Addr: s.Address, | |||
TLSConfig: &tls.Config{ | |||
Certificates: []tls.Certificate{cert}, | |||
Max0RTTDataSize: Max0RTTDataSize, | |||
Accept0RTTData: (s.ZeroRTT & ZeroRTT_Accept) == ZeroRTT_Accept, | |||
KeyLogWriter: keyLogWriter, | |||
GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) { | |||
// If we send the first flight too fast, NSS sends empty early data. | |||
time.Sleep(500 * time.Millisecond) | |||
return nil, nil | |||
}, | |||
MaxVersion: tls.VersionTLS13, | |||
ClientAuth: s.ClientAuthMethod, | |||
ClientCAs: clientCAs, | |||
CurvePreferences: s.curves, | |||
}, | |||
} | |||
log.Fatal(httpServer.ListenAndServeTLS("", "")) | |||
} | |||
func main() { | |||
s := NewServer() | |||
arg_addr := flag.String("b", "0.0.0.0:443", "Address:port used for binding") | |||
arg_palg := flag.String("palg", "rsa", "Public algorithm to use: rsa or ecdsa") | |||
arg_zerortt := flag.String("rtt0", "n", `0-RTT, accepts following values [n: None, a: Accept, o: Offer, oa: Offer and Accept]`) | |||
arg_confirm := flag.Bool("rtt0ack", false, "0-RTT confirm") | |||
arg_clientauth := flag.Bool("cliauth", false, "Performs client authentication (RequireAndVerifyClientCert used)") | |||
arg_sidhon := flag.Bool("enable_sidh", false, "Use SIDH for key exchange") | |||
flag.Parse() | |||
s.Address = *arg_addr | |||
if *arg_palg == "ecdsa" { | |||
s.PubKey = PubKeyECDSA | |||
} | |||
if *arg_zerortt == "a" { | |||
s.ZeroRTT = ZeroRTT_Accept | |||
} else if *arg_zerortt == "o" { | |||
s.ZeroRTT = ZeroRTT_Offer | |||
} else if *arg_zerortt == "oa" { | |||
s.ZeroRTT = ZeroRTT_Offer | ZeroRTT_Accept | |||
} | |||
if *arg_clientauth { | |||
s.ClientAuthMethod = tls.RequireAndVerifyClientCert | |||
} | |||
if *arg_sidhon { | |||
s.curves = []tls.CurveID{tls.SidhP751Curve25519, tls.SidhP751Curve448} | |||
} | |||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |||
tlsConn := r.Context().Value(http.TLSConnContextKey).(*tls.Conn) | |||
with0RTT := "" | |||
if !tlsConn.ConnectionState().HandshakeConfirmed { | |||
with0RTT = " [0-RTT]" | |||
} | |||
if *arg_confirm || r.URL.Path == "/confirm" { | |||
if err := tlsConn.ConfirmHandshake(); err != nil { | |||
log.Fatal(err) | |||
} | |||
if with0RTT != "" { | |||
with0RTT = " [0-RTT confirmed]" | |||
} | |||
if !tlsConn.ConnectionState().HandshakeConfirmed { | |||
panic("HandshakeConfirmed false after ConfirmHandshake") | |||
} | |||
} | |||
resumed := "" | |||
if r.TLS.DidResume { | |||
resumed = " [resumed]" | |||
} | |||
http2 := "" | |||
if r.ProtoMajor == 2 { | |||
http2 = " [HTTP/2]" | |||
} | |||
fmt.Printf("Established: TLS ver %X CipherSuite: %X CurveID: %X\n", | |||
r.TLS.Version, | |||
r.TLS.CipherSuite, | |||
tlsConn.KeyShareCurve) | |||
fmt.Fprintf(w, "<!DOCTYPE html><p>Hello TLS %s%s%s%s _o/\n", tlsVersionToName[r.TLS.Version], resumed, with0RTT, http2) | |||
}) | |||
http.HandleFunc("/ch", func(w http.ResponseWriter, r *http.Request) { | |||
w.Header().Set("Content-Type", "text/plain") | |||
fmt.Fprintf(w, "Client Hello packet (%d bytes):\n%s", len(r.TLS.ClientHello), hex.Dump(r.TLS.ClientHello)) | |||
}) | |||
s.start() | |||
} | |||
const ( | |||
rsaKey = `-----BEGIN RSA PRIVATE KEY----- | |||
MIIEpAIBAAKCAQEA1DHcIM3SThFqy8nAkPQFX0E7ph8jqh8EATXryjKHGuVjR3Xh | |||
OQ0BSPoJxyfdg/VEwevFrtmZAfz0WCbxvP2SVCmf7oobg4V2KPSo3nNt9vlBFUne | |||
RtIyHRQ8YRnGSWaRHzJbX6ffltnG2aD+8qUfk161rdZgxBA9G0Ga47IkwQhT2Hqu | |||
H3dW2Uu4W2WMyt6gX/tdyEAV57MOPcoceknr7Nb2kfiuDPR7h6wFrW3I6eoj8oX2 | |||
SkIOuVNt1Z31BAUcPJDUjqopI0o9tolM/7X13M8dEY0OJQVr7FQYDF9JeSYeEMyb | |||
wizjBaHDm48mSghP1o5UssQBbNNC83btXCjiLQIDAQABAoIBACzvGgRAUYaCnbDl | |||
2kdXxUN0luMIuQ6vXrO67WF17bI+XRWm2riwDlObzzJDON9Wsua1vLjYD1SickOw | |||
i4RP1grIfbuPt1/UhT8LAC+LFgA0rBmL+OvaWw5ZWKffQ2QLujN3AG5zKB/Tog43 | |||
z4UmfldAuQxE11zta2M4M0qAUNQnQj1oiuI8RUdG0VvvLw8Htdi1ogH0CI5R669z | |||
NjHt+JV+2gzKx6EX0s8mQL3yXGkC2xXItRbFclyCMJEhPS7QbBu+tru35N6WpzAq | |||
BCl2Q7LQogvSA6MXuMOx6CyuExVfgmhbfeoheLE8gmXwl0Y37n/g6ZBZFAtpCjcs | |||
UckPv0ECgYEA1orl7RwgIsZljMap6vWtMGoRIHKmT91DGpMmkh4suZe+yAk85maU | |||
49Vd+8ZfIN41AH37yrsGOcPHgz5o5QufELpoub6DCsQ7u9F1vQp55cp+qyBWzAgz | |||
b/xUuVnIyv3kLan3fpk7ZGCBXFBpLG0QXMFOHtda3Mlk5SmuoEYaYRkCgYEA/TLR | |||
u4neKqyqwsqMuRJGC1iKFVmfCjZeNMtPNbTWpdqez/vvT8APnEpIumUGt8YROLGZ | |||
8biUr5/ViOkmaP3wmQbO9m2/cE01lMTYv75w1cw2KVQe6kAHJkOx+JEx9xg53RJ/ | |||
QlFtG5MQUy2599Gxp8BMGaXLH5yo4qwvNvY6CDUCgYEArxr7AwX7rKZlZ/sV4HHY | |||
gzVu+R7aY0DibiRATO5X7rrNuhLgI+UCDNqvNLn6FqeGdvpcsmDneeozQwmDL77G | |||
ey7KHyBBcF4tquQQxtRwHX+i1yUz8p+W7AX1WLrRSezjeenJ2QhUE1849hGjZeE2 | |||
g546lq2Kub2enfPhVWsiSLECgYEA72T5QCPeVuLioUH5Q5Kvf1K7W+xcnr9A2xHP | |||
Vqwgtre5qFQ/tFuXZuIlWXbjnyY6aiwhrZYjntm0f7pRgrt2nHj/fafOdVPK8Voc | |||
xU4+SSbHntPWVw0qtVcUEjzVzRauvwMaJ43tZ0DpEnwNdO5i1oTObwF+x+jLFWZP | |||
TdwIinECgYBzjZeCxxOMk5SlPpTsLUtgC+q3m1AavXhUVNEPP2gKMOIPTETPbhbG | |||
LBxB2vVbJiS3J7itQy8gceT89O0vSEZnaTPXiM/Ws1QbkBJ8yW7KI7X4WuzN4Imq | |||
/cLBRXLb8R328U27YyQFNGMjr2tX/+vx5FulJjSloWMRNuFWUngv7w== | |||
-----END RSA PRIVATE KEY-----` | |||
rsaCert = `-----BEGIN CERTIFICATE----- | |||
MIIC+jCCAeKgAwIBAgIRANBDimJ/ww2tz77qcYIhuZowDQYJKoZIhvcNAQELBQAw | |||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjA5MjQxNzI5MTlaFw0yNjA5MjIxNzI5 | |||
MTlaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw | |||
ggEKAoIBAQDUMdwgzdJOEWrLycCQ9AVfQTumHyOqHwQBNevKMoca5WNHdeE5DQFI | |||
+gnHJ92D9UTB68Wu2ZkB/PRYJvG8/ZJUKZ/uihuDhXYo9Kjec232+UEVSd5G0jId | |||
FDxhGcZJZpEfMltfp9+W2cbZoP7ypR+TXrWt1mDEED0bQZrjsiTBCFPYeq4fd1bZ | |||
S7hbZYzK3qBf+13IQBXnsw49yhx6Sevs1vaR+K4M9HuHrAWtbcjp6iPyhfZKQg65 | |||
U23VnfUEBRw8kNSOqikjSj22iUz/tfXczx0RjQ4lBWvsVBgMX0l5Jh4QzJvCLOMF | |||
ocObjyZKCE/WjlSyxAFs00Lzdu1cKOItAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF | |||
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC | |||
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAygPV4enmvwSuMd1JarxOXpOK | |||
Z4Nsk7EKlfCPgzxQUOkFdLIr5ZG1kUkQt/omzTmoIWjLAsoYzT0ZCPOrioczKsWj | |||
MceFUIkT0w+eIl+8DzauPy34o8rjcApglF165UG3iphlpI+jdPzv5TBarUAbwsFb | |||
ClMLEiNJQ0OMxAIaRtb2RehD4q3OWlpWf6joJ36PRBqL8T5+f2x6Tg3c64UR+QPX | |||
98UcCQHHdEhm7y2z5Z2Wt0B48tZ+UAxDEoEwMghNyw7wUD79IRlXGYypBnXaMuLX | |||
46aGxbsSQ7Rfg62Co3JG7vo+eJd0AoZHrtFUnfM8V70IFzMBZnSwRslHRJe56Q== | |||
-----END CERTIFICATE-----` | |||
rsaCa_client = `-----BEGIN CERTIFICATE----- | |||
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 | |||
-----END CERTIFICATE-----` | |||
ecdsaCert = `-----BEGIN CERTIFICATE----- | |||
MIIBbTCCAROgAwIBAgIQZCsHZcs5ZkzV+zC2E6j5RzAKBggqhkjOPQQDAjASMRAw | |||
DgYDVQQKEwdBY21lIENvMB4XDTE2MDkyNDE3NTE1OFoXDTI2MDkyMjE3NTE1OFow | |||
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDTO | |||
B3IyzjYfKCp2HWy+P3QHxhdBT4AUGYgwTiSEj5phumPIahFNcOSWptN0UzlZvJdN | |||
MMjVmrFYK/FjF4abkNKjSzBJMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr | |||
BgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDAKBggq | |||
hkjOPQQDAgNIADBFAiEAp9W157PM1IadPBc33Cbj7vaFvp+rXs/hSuMCzP8pgV8C | |||
IHCswo1qiC0ZjQmWsBlmz5Zbp9rOorIzBYmGRhRdNs3j | |||
-----END CERTIFICATE-----` | |||
ecdsaKey = `-----BEGIN EC PRIVATE KEY----- | |||
MHcCAQEEIFdhO7IW5UIwpB1e2Vunm9QyKvUHWcVwGfLjhpOajuR7oAoGCCqGSM49 | |||
AwEHoUQDQgAENM4HcjLONh8oKnYdbL4/dAfGF0FPgBQZiDBOJISPmmG6Y8hqEU1w | |||
5Jam03RTOVm8l00wyNWasVgr8WMXhpuQ0g== | |||
-----END EC PRIVATE KEY-----` | |||
) |
@@ -46,13 +46,13 @@ class RegexSelfTest(unittest.TestCase): | |||
''' Ensures that those regexe's actually work ''' | |||
LINE_HELLO_TLS ="\nsomestuff\nHello TLS 1.3 _o/\nsomestuff" | |||
LINE_HELLO_DRAFT_TLS="\nsomestuff\nHello TLS 1.3 (draft 22) _o/\nsomestuff" | |||
LINE_HELLO_DRAFT_TLS="\nsomestuff\nHello TLS 1.3 (draft 23) _o/\nsomestuff" | |||
LINE_HELLO_RESUMED ="\nsomestuff\nHello TLS 1.3 (draft 22) [resumed] _o/\nsomestuff" | |||
LINE_HELLO_MIXED ="\nsomestuff\nHello TLS 1.3 (draft 22) _o/\nHello TLS 1.3 (draft 22) [resumed] _o/\nsomestuff" | |||
LINE_HELLO_TLS_12 ="\nsomestuff\nHello TLS 1.2 (draft 22) [resumed] _o/\nsomestuff" | |||
LINE_HELLO_TLS_13_0RTT="\nsomestuff\nHello TLS 1.3 (draft 22) [resumed] [0-RTT] _o/\nsomestuff" | |||
LINE_HELLO_TLS_13_0RTT_CONFIRMED="\nsomestuff\nHello TLS 1.3 (draft 22) [resumed] [0-RTT confirmed] _o/\nsomestuff" | |||
LINE_HELLO_RESUMED ="\nsomestuff\nHello TLS 1.3 (draft 23) [resumed] _o/\nsomestuff" | |||
LINE_HELLO_MIXED ="\nsomestuff\nHello TLS 1.3 (draft 23) _o/\nHello TLS 1.3 (draft 23) [resumed] _o/\nsomestuff" | |||
LINE_HELLO_TLS_12 ="\nsomestuff\nHello TLS 1.2 (draft 23) [resumed] _o/\nsomestuff" | |||
LINE_HELLO_TLS_13_0RTT="\nsomestuff\nHello TLS 1.3 (draft 23) [resumed] [0-RTT] _o/\nsomestuff" | |||
LINE_HELLO_TLS_13_0RTT_CONFIRMED="\nsomestuff\nHello TLS 1.3 (draft 23) [resumed] [0-RTT confirmed] _o/\nsomestuff" | |||
def test_regexes(self): | |||
self.assertIsNotNone( | |||
@@ -210,14 +210,28 @@ class InteropServer_BoringSSL( | |||
ServerNominalMixin, | |||
ServerClientAuthMixin, | |||
unittest.TestCase | |||
): CLIENT_NAME = "tls-tris:boring" | |||
): | |||
class InteropServer_PicoTLS( | |||
InteropServer, | |||
ServerNominalMixin, | |||
ServerZeroRttMixin, | |||
unittest.TestCase | |||
): CLIENT_NAME = "tls-tris:picotls" | |||
CLIENT_NAME = "tls-tris:boring" | |||
def test_qrServerAcceptsNonQR(self): | |||
res = self.d.run_client(self.CLIENT_NAME, self.server_ip+":"+'7443') | |||
self.assertTrue(res[0] == 0) | |||
# Check there was TLS hello without resume | |||
self.assertIsNotNone( | |||
re.search(RE_PATTERN_HELLO_TLS_13_NORESUME, res[1], re.MULTILINE)) | |||
# Check there was TLS hello with resume | |||
self.assertIsNotNone( | |||
re.search(RE_PATTERN_HELLO_TLS_13_RESUME, res[1], re.MULTILINE)) | |||
# PicoTLS doesn't seem to implement draft-23 correctly. It will | |||
# be enabled when draft-28 is implemented. | |||
# class InteropServer_PicoTLS( | |||
# InteropServer, | |||
# ServerNominalMixin, | |||
# ServerZeroRttMixin, | |||
# unittest.TestCase | |||
# ): CLIENT_NAME = "tls-tris:picotls" | |||
class InteropServer_NSS( | |||
InteropServer, | |||
@@ -241,7 +255,10 @@ class InteropClient_NSS( | |||
): SERVER_NAME = "tstclnt-localserver" | |||
# TRIS as a client | |||
class InteropServer_TRIS(ClientNominalMixin, InteropServer, unittest.TestCase): | |||
class InteropServer_TRIS( | |||
ClientNominalMixin, | |||
InteropServer, | |||
unittest.TestCase): | |||
CLIENT_NAME = 'tris-testclient' | |||
@@ -250,5 +267,15 @@ class InteropServer_TRIS(ClientNominalMixin, InteropServer, unittest.TestCase): | |||
res = self.d.run_client(self.CLIENT_NAME, '-rsa=false -ecdsa=false -cliauth '+self.server_ip+":6443") | |||
self.assertEqual(res[0], 0) | |||
def test_qr(self): | |||
res = self.d.run_client(self.CLIENT_NAME, '-rsa=false -ecdsa=true -qr SIDH-P751-X25519 '+self.server_ip+":7443") | |||
self.assertEqual(res[0], 0) | |||
res = self.d.run_client(self.CLIENT_NAME, '-rsa=false -ecdsa=true -qr SIDH-P751-X448 '+self.server_ip+":7443") | |||
self.assertEqual(res[0], 0) | |||
def test_qrServerAcceptsNonQR(self): | |||
res = self.d.run_client(self.CLIENT_NAME, '-rsa=false -ecdsa=true '+self.server_ip+":7443") | |||
self.assertEqual(res[0], 0) | |||
if __name__ == '__main__': | |||
unittest.main() |
@@ -8,6 +8,7 @@ EXPOSE 3443 | |||
EXPOSE 4443 | |||
EXPOSE 5443 | |||
EXPOSE 6443 | |||
EXPOSE 7443 | |||
ADD tris-localserver / | |||
ADD runner.sh / | |||
@@ -6,5 +6,6 @@ | |||
./tris-localserver -b 0.0.0.0:4443 -palg=ecdsa -rtt0=oa 2>&1 & # fourth port: offer and accept 0-RTT | |||
./tris-localserver -b 0.0.0.0:5443 -palg=ecdsa -rtt0=oa -rtt0ack 2>&1 & # fifth port: offer and accept 0-RTT but confirm | |||
./tris-localserver -b 0.0.0.0:6443 -palg=rsa -cliauth 2>&1 & # sixth port: RSA with required client authentication | |||
./tris-localserver -b 0.0.0.0:7443 -palg=ecdsa -qr & # Enables post-quantum algorithms | |||
wait |
@@ -2,15 +2,14 @@ package main | |||
import ( | |||
"crypto/tls" | |||
"crypto/x509" | |||
"encoding/hex" | |||
"flag" | |||
"fmt" | |||
"io" | |||
"log" | |||
"net/http" | |||
"os" | |||
"time" | |||
"crypto/x509" | |||
) | |||
type ZeroRTT_t int | |||
@@ -18,21 +17,21 @@ type PubKeyAlgo_t int | |||
// Bitset | |||
const ( | |||
ZeroRTT_None ZeroRTT_t = 0 | |||
ZeroRTT_Offer = 1 << 0 | |||
ZeroRTT_Accept = 1 << 1 | |||
ZeroRTT_None ZeroRTT_t = 0 | |||
ZeroRTT_Offer = 1 << 0 | |||
ZeroRTT_Accept = 1 << 1 | |||
) | |||
const ( | |||
PubKeyRSA PubKeyAlgo_t = iota | |||
PubKeyRSA PubKeyAlgo_t = iota | |||
PubKeyECDSA | |||
) | |||
type server struct { | |||
Address string | |||
ZeroRTT ZeroRTT_t | |||
PubKey PubKeyAlgo_t | |||
ClientAuthMethod tls.ClientAuthType | |||
Address string | |||
ZeroRTT ZeroRTT_t | |||
PubKey PubKeyAlgo_t | |||
TLS tls.Config | |||
} | |||
var tlsVersionToName = map[uint16]string{ | |||
@@ -43,14 +42,25 @@ var tlsVersionToName = map[uint16]string{ | |||
tls.VersionTLS13Draft18: "1.3 (draft 18)", | |||
tls.VersionTLS13Draft21: "1.3 (draft 21)", | |||
tls.VersionTLS13Draft22: "1.3 (draft 22)", | |||
tls.VersionTLS13Draft23: "1.3 (draft 23)", | |||
tls.VersionTLS13Draft28: "1.3 (draft 28)", | |||
} | |||
func NewServer() *server { | |||
s := new(server) | |||
s.ClientAuthMethod = tls.NoClientCert | |||
s.ZeroRTT = ZeroRTT_None | |||
s.Address = "0.0.0.1:443" | |||
return s | |||
s := new(server) | |||
s.ZeroRTT = ZeroRTT_None | |||
s.Address = "0.0.0.0:443" | |||
s.TLS = tls.Config{ | |||
GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) { | |||
// If we send the first flight too fast, NSS sends empty early data. | |||
time.Sleep(500 * time.Millisecond) | |||
return nil, nil | |||
}, | |||
MaxVersion: tls.VersionTLS13, | |||
ClientAuth: tls.NoClientCert, | |||
} | |||
return s | |||
} | |||
func (s *server) start() { | |||
@@ -58,73 +68,72 @@ func (s *server) start() { | |||
if s.PubKey == PubKeyRSA { | |||
cert, err = tls.X509KeyPair([]byte(rsaCert), []byte(rsaKey)) | |||
} | |||
s.TLS.Certificates = []tls.Certificate{cert} | |||
if err != nil { | |||
log.Fatal(err) | |||
} | |||
var Max0RTTDataSize uint32 | |||
if ((s.ZeroRTT&ZeroRTT_Offer) == ZeroRTT_Offer) { | |||
Max0RTTDataSize = 100 * 1024 | |||
if (s.ZeroRTT & ZeroRTT_Offer) == ZeroRTT_Offer { | |||
s.TLS.Max0RTTDataSize = 100 * 1024 | |||
} | |||
var keyLogWriter io.Writer | |||
if keyLogFile := os.Getenv("SSLKEYLOGFILE"); keyLogFile != "" { | |||
keyLogWriter, err = os.OpenFile(keyLogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) | |||
s.TLS.KeyLogWriter, err = os.OpenFile(keyLogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600) | |||
if err != nil { | |||
log.Fatalf("Cannot open keylog file: %v", err) | |||
} | |||
log.Println("Enabled keylog") | |||
} | |||
clientCAs := x509.NewCertPool() | |||
clientCAs.AppendCertsFromPEM([]byte(rsaCa_client)) | |||
s.TLS.ClientCAs = x509.NewCertPool() | |||
s.TLS.ClientCAs.AppendCertsFromPEM([]byte(rsaCa_client)) | |||
s.TLS.Accept0RTTData = ((s.ZeroRTT & ZeroRTT_Accept) == ZeroRTT_Accept) | |||
httpServer := &http.Server{ | |||
Addr: s.Address, | |||
TLSConfig: &tls.Config{ | |||
Certificates: []tls.Certificate{cert}, | |||
Max0RTTDataSize: Max0RTTDataSize, | |||
Accept0RTTData: (s.ZeroRTT&ZeroRTT_Accept) == ZeroRTT_Accept, | |||
KeyLogWriter: keyLogWriter, | |||
GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) { | |||
// If we send the first flight too fast, NSS sends empty early data. | |||
time.Sleep(500 * time.Millisecond) | |||
return nil, nil | |||
}, | |||
MaxVersion: tls.VersionTLS13, | |||
ClientAuth: s.ClientAuthMethod, | |||
ClientCAs: clientCAs, | |||
}, | |||
Addr: s.Address, | |||
TLSConfig: &s.TLS, | |||
} | |||
log.Fatal(httpServer.ListenAndServeTLS("", "")) | |||
} | |||
func enableQR(s *server) { | |||
var defaultCurvePreferences = []tls.CurveID{tls.X25519, tls.CurveP256, tls.CurveP384, tls.CurveP521} | |||
var sidhCurves = []tls.CurveID{tls.SidhP751Curve25519, tls.SidhP751Curve448} | |||
s.TLS.CurvePreferences = append(defaultCurvePreferences, sidhCurves...) | |||
} | |||
func main() { | |||
s := NewServer() | |||
s := NewServer() | |||
arg_addr := flag.String("b" , "0.0.0.0:443", "Address:port used for binding") | |||
arg_palg := flag.String("palg", "rsa", "Public algorithm to use: rsa or ecdsa") | |||
arg_zerortt := flag.String("rtt0", "n", `0-RTT, accepts following values [n: None, a: Accept, o: Offer, oa: Offer and Accept]`) | |||
arg_confirm := flag.Bool("rtt0ack", false, "0-RTT confirm") | |||
arg_clientauth := flag.Bool("cliauth", false, "Performs client authentication (RequireAndVerifyClientCert used)") | |||
flag.Parse() | |||
arg_addr := flag.String("b", "0.0.0.0:443", "Address:port used for binding") | |||
arg_palg := flag.String("palg", "rsa", "Public algorithm to use: rsa or ecdsa") | |||
arg_zerortt := flag.String("rtt0", "n", `0-RTT, accepts following values [n: None, a: Accept, o: Offer, oa: Offer and Accept]`) | |||
arg_confirm := flag.Bool("rtt0ack", false, "0-RTT confirm") | |||
arg_clientauth := flag.Bool("cliauth", false, "Performs client authentication (RequireAndVerifyClientCert used)") | |||
arg_qr := flag.Bool("qr", false, "Enable quantum-resistant algorithms") | |||
flag.Parse() | |||
s.Address=*arg_addr | |||
s.Address = *arg_addr | |||
if *arg_palg == "ecdsa" { | |||
s.PubKey = PubKeyECDSA | |||
} | |||
if *arg_palg == "ecdsa" { | |||
s.PubKey = PubKeyECDSA | |||
} | |||
if *arg_zerortt == "a" { | |||
s.ZeroRTT = ZeroRTT_Accept | |||
} else if *arg_zerortt == "o" { | |||
s.ZeroRTT = ZeroRTT_Offer | |||
} else if *arg_zerortt == "oa" { | |||
s.ZeroRTT = ZeroRTT_Offer | ZeroRTT_Accept | |||
} | |||
if *arg_zerortt == "a" { | |||
s.ZeroRTT = ZeroRTT_Accept | |||
} else if *arg_zerortt == "o" { | |||
s.ZeroRTT = ZeroRTT_Offer | |||
} else if *arg_zerortt == "oa" { | |||
s.ZeroRTT = ZeroRTT_Offer | ZeroRTT_Accept | |||
} | |||
if *arg_clientauth { | |||
s.ClientAuthMethod = tls.RequireAndVerifyClientCert | |||
} | |||
if *arg_clientauth { | |||
s.TLS.ClientAuth = tls.RequireAndVerifyClientCert | |||
} | |||
if *arg_qr { | |||
enableQR(s) | |||
} | |||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | |||
tlsConn := r.Context().Value(http.TLSConnContextKey).(*tls.Conn) | |||
@@ -212,7 +221,7 @@ ClMLEiNJQ0OMxAIaRtb2RehD4q3OWlpWf6joJ36PRBqL8T5+f2x6Tg3c64UR+QPX | |||
98UcCQHHdEhm7y2z5Z2Wt0B48tZ+UAxDEoEwMghNyw7wUD79IRlXGYypBnXaMuLX | |||
46aGxbsSQ7Rfg62Co3JG7vo+eJd0AoZHrtFUnfM8V70IFzMBZnSwRslHRJe56Q== | |||
-----END CERTIFICATE-----` | |||
rsaCa_client = `-----BEGIN CERTIFICATE----- | |||
rsaCa_client = `-----BEGIN CERTIFICATE----- | |||
MIIFYDCCA0igAwIBAgIJAPpBgIvtQb1EMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV | |||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX | |||
aWRnaXRzIFB0eSBMdGQwHhcNMTgwMjEzMjAxNjA3WhcNMTkwMjEzMjAxNjA3WjBF | |||
@@ -17,6 +17,8 @@ var tlsVersionToName = map[uint16]string{ | |||
tls.VersionTLS12: "1.2", | |||
tls.VersionTLS13: "1.3", | |||
tls.VersionTLS13Draft18: "1.3 (draft 18)", | |||
tls.VersionTLS13Draft23: "1.3 (draft 23)", | |||
tls.VersionTLS13Draft28: "1.3 (draft 28)", | |||
} | |||
var cipherSuiteIdToName = map[uint16]string{ | |||
@@ -27,28 +29,42 @@ var cipherSuiteIdToName = map[uint16]string{ | |||
tls.TLS_CHACHA20_POLY1305_SHA256: "TLS_CHACHA20_POLY1305_SHA256", | |||
} | |||
var failed uint | |||
const ( | |||
qr_algos = "[SIDH-P751-X448, SIDH-P503-X25519, SIDH-P751-X25519]" | |||
) | |||
type Client struct { | |||
KeyLogWriter io.Writer | |||
failed uint | |||
client_cert tls.Certificate | |||
client_certpool *x509.CertPool | |||
TLS tls.Config | |||
addr string | |||
} | |||
func (c *Client) run(addr string, version, cipherSuite uint16) { | |||
fmt.Printf("TLS %s with %s\n", tlsVersionToName[version], cipherSuiteIdToName[cipherSuite]) | |||
tls_config := &tls.Config{ | |||
InsecureSkipVerify: true, | |||
MinVersion: version, | |||
MaxVersion: version, | |||
CipherSuites: []uint16{cipherSuite}, | |||
KeyLogWriter: c.KeyLogWriter, | |||
Certificates: []tls.Certificate{c.client_cert}, | |||
RootCAs: c.client_certpool, | |||
} | |||
con, err := tls.Dial("tcp", addr, tls_config) | |||
func NewClient() *Client { | |||
var c Client | |||
c.TLS.InsecureSkipVerify = true | |||
return &c | |||
} | |||
func (c *Client) clone() *Client { | |||
var clone Client | |||
clone.TLS = *c.TLS.Clone() | |||
clone.addr = c.addr | |||
return &clone | |||
} | |||
func (c *Client) setMinMaxTLS(ver uint16) { | |||
c.TLS.MinVersion = ver | |||
c.TLS.MaxVersion = ver | |||
} | |||
func (c *Client) run() { | |||
fmt.Printf("TLS %s with %s\n", tlsVersionToName[c.TLS.MinVersion], cipherSuiteIdToName[c.TLS.CipherSuites[0]]) | |||
con, err := tls.Dial("tcp", c.addr, &c.TLS) | |||
if err != nil { | |||
fmt.Printf("handshake failed: %v\n\n", err) | |||
c.failed++ | |||
failed++ | |||
return | |||
} | |||
defer con.Close() | |||
@@ -56,7 +72,7 @@ func (c *Client) run(addr string, version, cipherSuite uint16) { | |||
_, err = con.Write([]byte("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")) | |||
if err != nil { | |||
fmt.Printf("Write failed: %v\n\n", err) | |||
c.failed++ | |||
failed++ | |||
return | |||
} | |||
@@ -66,7 +82,7 @@ func (c *Client) run(addr string, version, cipherSuite uint16) { | |||
// is received right after reading data (observed with NSS selfserv). | |||
if !(n > 0 && err == io.EOF) && err != nil { | |||
fmt.Printf("Read failed: %v\n\n", err) | |||
c.failed++ | |||
failed++ | |||
return | |||
} | |||
fmt.Printf("Read %d bytes\n", n) | |||
@@ -74,25 +90,47 @@ func (c *Client) run(addr string, version, cipherSuite uint16) { | |||
fmt.Println("OK\n") | |||
} | |||
func result() { | |||
if failed > 0 { | |||
log.Fatalf("Failed handshakes: %d\n", failed) | |||
} else { | |||
fmt.Println("All handshakes passed") | |||
} | |||
} | |||
func setQrAlgo(qr string, client *Client) { | |||
switch qr { | |||
case "SIDH-P751-X448": | |||
client.TLS.CurvePreferences = []tls.CurveID{tls.SidhP751Curve448} | |||
case "SIDH-P503-X25519": | |||
//client.TLS.CurvePreferences = []tls.CurveID{tls.SidhP503Curve25519} | |||
panic("UNSUPPORTED") | |||
case "SIDH-P751-X25519": | |||
client.TLS.CurvePreferences = []tls.CurveID{tls.SidhP751Curve25519} | |||
} | |||
} | |||
func main() { | |||
var keylog_file string | |||
var keylog_file, qr string | |||
var enable_rsa, enable_ecdsa, client_auth bool | |||
flag.StringVar(&keylog_file, "keylogfile", "", "Secrets will be logged here") | |||
flag.BoolVar(&enable_rsa, "rsa", true, "Whether to enable RSA cipher suites") | |||
flag.BoolVar(&enable_ecdsa, "ecdsa", true, "Whether to enable ECDSA cipher suites") | |||
flag.BoolVar(&client_auth, "cliauth", false, "Whether to enable client authentication") | |||
flag.StringVar(&qr, "qr", "", "Specifies qr algorithm from following list:\n"+qr_algos) | |||
flag.Parse() | |||
if flag.NArg() != 1 { | |||
flag.Usage() | |||
os.Exit(1) | |||
} | |||
addr := flag.Arg(0) | |||
if !strings.Contains(addr, ":") { | |||
addr += ":443" | |||
client := NewClient() | |||
client.addr = flag.Arg(0) | |||
if !strings.Contains(client.addr, ":") { | |||
client.addr += ":443" | |||
} | |||
client := Client{} | |||
if keylog_file == "" { | |||
keylog_file = os.Getenv("SSLKEYLOGFILE") | |||
} | |||
@@ -101,45 +139,64 @@ func main() { | |||
if err != nil { | |||
log.Fatalf("Cannot open keylog file: %v", err) | |||
} | |||
client.KeyLogWriter = keylog_writer | |||
client.TLS.KeyLogWriter = keylog_writer | |||
log.Println("Enabled keylog") | |||
} | |||
if client_auth { | |||
var err error | |||
client.client_cert, err = tls.X509KeyPair([]byte(client_crt), []byte(client_key)) | |||
client_cert, err := tls.X509KeyPair([]byte(client_crt), []byte(client_key)) | |||
if err != nil { | |||
panic("Can't load client certificate") | |||
} | |||
client.client_certpool = x509.NewCertPool() | |||
if !client.client_certpool.AppendCertsFromPEM([]byte(client_ca)) { | |||
client.TLS.Certificates = []tls.Certificate{client_cert} | |||
client.TLS.RootCAs = x509.NewCertPool() | |||
if !client.TLS.RootCAs.AppendCertsFromPEM([]byte(client_ca)) { | |||
panic("Can't load client CA cert") | |||
} | |||
} | |||
if len(qr) > 0 { | |||
setQrAlgo(qr, client) | |||
c := client.clone() | |||
c.TLS.CipherSuites = []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256} | |||
c.setMinMaxTLS(tls.VersionTLS13) | |||
c.run() | |||
return | |||
} | |||
if enable_rsa { | |||
// Sanity check: TLS 1.2 with the mandatory cipher suite from RFC 5246 | |||
client.run(addr, tls.VersionTLS12, tls.TLS_RSA_WITH_AES_128_CBC_SHA) | |||
c := client.clone() | |||
c.TLS.CipherSuites = []uint16{tls.TLS_RSA_WITH_AES_128_CBC_SHA} | |||
c.setMinMaxTLS(tls.VersionTLS12) | |||
c.run() | |||
} | |||
if enable_ecdsa { | |||
// Sane cipher suite for TLS 1.2 with an ECDSA cert (as used by boringssl) | |||
client.run(addr, tls.VersionTLS12, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) | |||
c := client.clone() | |||
c.TLS.CipherSuites = []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256} | |||
c.setMinMaxTLS(tls.VersionTLS12) | |||
c.run() | |||
} | |||
client.setMinMaxTLS(tls.VersionTLS13) | |||
client.TLS.CipherSuites = []uint16{tls.TLS_CHACHA20_POLY1305_SHA256} | |||
client.run() | |||
client.run(addr, tls.VersionTLS13, tls.TLS_CHACHA20_POLY1305_SHA256) | |||
client.run(addr, tls.VersionTLS13, tls.TLS_AES_128_GCM_SHA256) | |||
client.run(addr, tls.VersionTLS13, tls.TLS_AES_256_GCM_SHA384) | |||
client.setMinMaxTLS(tls.VersionTLS13) | |||
client.TLS.CipherSuites = []uint16{tls.TLS_AES_128_GCM_SHA256} | |||
client.run() | |||
client.setMinMaxTLS(tls.VersionTLS13) | |||
client.TLS.CipherSuites = []uint16{tls.TLS_AES_256_GCM_SHA384} | |||
client.run() | |||
// TODO test other kex methods besides X25519, like MTI secp256r1 | |||
// TODO limit supported groups? | |||
if client.failed > 0 { | |||
log.Fatalf("Failed handshakes: %d\n", client.failed) | |||
} else { | |||
fmt.Println("All handshakes passed") | |||
} | |||
result() | |||
} | |||
const ( | |||
@@ -226,4 +283,3 @@ LAoibwDU1NC8/3MfOBYMe6Qklu3kjexOJrfdo0Z7Khgd9F8A4tKwslUndSSlAfKF | |||
2rjfqabVMZMLZ2XEbA4W5JTfaZS4YYGcrjY7+i7OsnSxoYG2sb+xlQ== | |||
-----END RSA PRIVATE KEY-----` | |||
) | |||
@@ -21,7 +21,13 @@ ENV USE_64=1 NSS_ENABLE_TLS_1_3=1 | |||
# ARG REVISION=e61c0f657100 | |||
# Draft 22 | |||
ARG REVISION=88c3f3fa581b | |||
#ARG REVISION=88c3f3fa581b | |||
# Draft 23 | |||
# ARG REVISION=16c622c9e1cc | |||
# Latest | |||
ARG REVISION=09ab3310e710 | |||
RUN cd nss && hg pull | |||
RUN cd nss && hg checkout -C $REVISION | |||
@@ -0,0 +1,20 @@ | |||
#!/bin/sh | |||
# Copyright 2012 The Go Authors. All rights reserved. | |||
# Use of this source code is governed by a BSD-style | |||
# license that can be found in the LICENSE file. | |||
gofiles=$(find . -iname "*.go" ! -path "*/_dev/*") | |||
#[ -z "$gofiles" ] && exit 0 | |||
unformatted=$(gofmt -l $gofiles) | |||
[ -z "$unformatted" ] && exit 0 | |||
# Some files are not gofmt'd. Print message and fail. | |||
echo >&2 "Go files must be formatted with gofmt. Please run:" | |||
for fn in $unformatted; do | |||
echo >&2 " gofmt -w $PWD/$fn" | |||
done | |||
exit 1 | |||
@@ -0,0 +1,27 @@ | |||
#!/bin/sh | |||
# Copyright 2012 The Go Authors. All rights reserved. | |||
# Use of this source code is governed by a BSD-style | |||
# license that can be found in the LICENSE file. | |||
# git gofmt pre-commit hook | |||
# | |||
# To use, store as .git/hooks/pre-commit inside your repository and make sure | |||
# it has execute permissions. | |||
# | |||
# This script does not handle file names that contain spaces. | |||
gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$') | |||
[ -z "$gofiles" ] && exit 0 | |||
unformatted=$(gofmt -l $gofiles) | |||
[ -z "$unformatted" ] && exit 0 | |||
# Some files are not gofmt'd. Print message and fail. | |||
echo >&2 "Go files must be formatted with gofmt. Please run:" | |||
for fn in $unformatted; do | |||
echo >&2 " gofmt -w $PWD/$fn" | |||
done | |||
exit 1 | |||
@@ -110,6 +110,9 @@ var cipherSuites = []*cipherSuite{ | |||
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, suiteDefaultOff, cipherRC4, macSHA1, nil}, | |||
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE | suiteDefaultOff, cipherRC4, macSHA1, nil}, | |||
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteDefaultOff, cipherRC4, macSHA1, nil}, | |||
// Experimental TLS 1.3 cipher suites | |||
{TLS_ECDHE_SIDH_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, nil, suiteDefaultOff | suiteTLS13, nil, nil, aeadAESGCM13}, | |||
} | |||
func cipherRC4(key, iv []byte, isRead bool) interface{} { | |||
@@ -174,7 +177,10 @@ type fixedNonceAEAD struct { | |||
aead cipher.AEAD | |||
} | |||
func (f *fixedNonceAEAD) NonceSize() int { return 8 } | |||
func (f *fixedNonceAEAD) NonceSize() int { return 8 } | |||
// Overhead returns the maximum difference between the lengths of a | |||
// plaintext and its ciphertext. | |||
func (f *fixedNonceAEAD) Overhead() int { return f.aead.Overhead() } | |||
func (f *fixedNonceAEAD) explicitNonceLen() int { return 8 } | |||
@@ -418,6 +424,11 @@ const ( | |||
TLS_AES_256_GCM_SHA384 uint16 = 0x1302 | |||
TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303 | |||
// SIDH (OZAPTF: I'm not sure about IDs - it's unasigned, but not reserved/private) | |||
TLS_ECDHE_SIDH_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0x1304 | |||
// OZAPTF, AES 256? | |||
TLS_ECDHE_SIDH_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0x1305 | |||
// TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator | |||
// that the client is doing version fallback. See | |||
// https://tools.ietf.org/html/rfc7507. | |||
@@ -30,6 +30,8 @@ const ( | |||
VersionTLS13Draft18 = 0x7f00 | 18 | |||
VersionTLS13Draft21 = 0x7f00 | 21 | |||
VersionTLS13Draft22 = 0x7f00 | 22 | |||
VersionTLS13Draft23 = 0x7f00 | 23 | |||
VersionTLS13Draft28 = 0x7f00 | 28 | |||
) | |||
const ( | |||
@@ -40,7 +42,7 @@ const ( | |||
maxWarnAlertCount = 5 // maximum number of consecutive warning alerts | |||
minVersion = VersionTLS12 | |||
maxVersion = VersionTLS13Draft22 | |||
maxVersion = VersionTLS13Draft28 | |||
) | |||
// TLS record types. | |||
@@ -79,22 +81,23 @@ const ( | |||
// TLS extension numbers | |||
const ( | |||
extensionServerName uint16 = 0 | |||
extensionStatusRequest uint16 = 5 | |||
extensionSupportedCurves uint16 = 10 // Supported Groups in 1.3 nomenclature | |||
extensionSupportedPoints uint16 = 11 | |||
extensionSignatureAlgorithms uint16 = 13 | |||
extensionALPN uint16 = 16 | |||
extensionSCT uint16 = 18 // https://tools.ietf.org/html/rfc6962#section-6 | |||
extensionSessionTicket uint16 = 35 | |||
extensionKeyShare uint16 = 40 | |||
extensionPreSharedKey uint16 = 41 | |||
extensionEarlyData uint16 = 42 | |||
extensionSupportedVersions uint16 = 43 | |||
extensionPSKKeyExchangeModes uint16 = 45 | |||
extensionCAs uint16 = 47 | |||
extensionNextProtoNeg uint16 = 13172 // not IANA assigned | |||
extensionRenegotiationInfo uint16 = 0xff01 | |||
extensionServerName uint16 = 0 | |||
extensionStatusRequest uint16 = 5 | |||
extensionSupportedCurves uint16 = 10 // Supported Groups in 1.3 nomenclature | |||
extensionSupportedPoints uint16 = 11 | |||
extensionSignatureAlgorithms uint16 = 13 | |||
extensionALPN uint16 = 16 | |||
extensionSCT uint16 = 18 // https://tools.ietf.org/html/rfc6962#section-6 | |||
extensionSessionTicket uint16 = 35 | |||
extensionPreSharedKey uint16 = 41 | |||
extensionEarlyData uint16 = 42 | |||
extensionSupportedVersions uint16 = 43 | |||
extensionPSKKeyExchangeModes uint16 = 45 | |||
extensionCAs uint16 = 47 | |||
extensionSignatureAlgorithmsCert uint16 = 50 | |||
extensionKeyShare uint16 = 51 | |||
extensionNextProtoNeg uint16 = 13172 // not IANA assigned | |||
extensionRenegotiationInfo uint16 = 0xff01 | |||
) | |||
// TLS signaling cipher suite values | |||
@@ -120,6 +123,12 @@ const ( | |||
CurveP384 CurveID = 24 | |||
CurveP521 CurveID = 25 | |||
X25519 CurveID = 29 | |||
SidhP503Curve25519 CurveID = 0x0105 | |||
// X448 + P751 | |||
SidhP751Curve448 CurveID = 0x0106 | |||
// X25519 + P751 | |||
SidhP751Curve25519 CurveID = 0x0107 | |||
) | |||
// TLS 1.3 Key Share | |||
@@ -234,7 +243,8 @@ type ConnectionState struct { | |||
// Unique0RTTToken is only present if HandshakeConfirmed is false. | |||
Unique0RTTToken []byte | |||
ClientHello []byte // ClientHello packet | |||
ClientHello []byte // ClientHello packet | |||
KeyShareCurve CurveID | |||
} | |||
// ClientAuthType declares the policy the server will follow for | |||
@@ -858,7 +868,7 @@ var configSuppVersArray = [...]uint16{VersionTLS13, VersionTLS12, VersionTLS11, | |||
// with TLS 1.3 draft versions included. | |||
// | |||
// TODO: remove once TLS 1.3 is finalised. | |||
var tls13DraftSuppVersArray = [...]uint16{VersionTLS13Draft22, VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30} | |||
var tls13DraftSuppVersArray = [...]uint16{VersionTLS13Draft28, VersionTLS12, VersionTLS11, VersionTLS10, VersionSSL30} | |||
// getSupportedVersions returns the protocol versions that are supported by the | |||
// current configuration. | |||
@@ -11,6 +11,7 @@ import ( | |||
"crypto/cipher" | |||
"crypto/subtle" | |||
"crypto/x509" | |||
"encoding/binary" | |||
"errors" | |||
"fmt" | |||
"io" | |||
@@ -125,6 +126,9 @@ type Conn struct { | |||
binder []byte | |||
tmp [16]byte | |||
// OZAPTF: Stores curve ID established during TLS 1.3 handshake | |||
KeyShareCurve CurveID | |||
} | |||
type handshakeStatus int | |||
@@ -355,6 +359,15 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) | |||
hc.additionalData[11] = byte(n >> 8) | |||
hc.additionalData[12] = byte(n) | |||
additionalData = hc.additionalData[:] | |||
} else { | |||
if len(payload) > int((1<<14)+256) { | |||
return false, 0, alertRecordOverflow | |||
} | |||
// Check AD header, see 5.2 of RFC8446 | |||
additionalData = make([]byte, 5) | |||
additionalData[0] = byte(recordTypeApplicationData) | |||
binary.BigEndian.PutUint16(additionalData[1:], VersionTLS12) | |||
binary.BigEndian.PutUint16(additionalData[3:], uint16(len(payload))) | |||
} | |||
var err error | |||
payload, err = c.Open(payload[:0], nonce, payload, additionalData) | |||
@@ -458,12 +471,6 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) { | |||
c.XORKeyStream(payload, payload) | |||
case aead: | |||
payloadLen := len(b.data) - recordHeaderLen - explicitIVLen | |||
overhead := c.Overhead() | |||
if hc.version >= VersionTLS13 { | |||
overhead++ | |||
} | |||
b.resize(len(b.data) + overhead) | |||
nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] | |||
if len(nonce) == 0 { | |||
nonce = hc.seq[:] | |||
@@ -475,18 +482,25 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) { | |||
if hc.version < VersionTLS13 { | |||
copy(hc.additionalData[:], hc.seq[:]) | |||
copy(hc.additionalData[8:], b.data[:3]) | |||
hc.additionalData[11] = byte(payloadLen >> 8) | |||
hc.additionalData[12] = byte(payloadLen) | |||
binary.BigEndian.PutUint16(hc.additionalData[11:], uint16(payloadLen)) | |||
additionalData = hc.additionalData[:] | |||
} | |||
b.resize(len(b.data) + c.Overhead()) | |||
} else { | |||
// 1 byte of content type is appended to payload and encrypted | |||
payload = append(payload, b.data[0]) | |||
if hc.version >= VersionTLS13 { | |||
// opaque type | |||
payload = payload[:len(payload)+1] | |||
payload[len(payload)-1] = b.data[0] | |||
// opaque_type | |||
b.data[0] = byte(recordTypeApplicationData) | |||
} | |||
// Add AD header, see 5.2 of RFC8446 | |||
additionalData = make([]byte, 5) | |||
additionalData[0] = b.data[0] | |||
binary.BigEndian.PutUint16(additionalData[1:], VersionTLS12) | |||
binary.BigEndian.PutUint16(additionalData[3:], uint16(len(payload)+c.Overhead())) | |||
// make room for TLSCiphertext.encrypted_record | |||
b.resize(len(payload) + recordHeaderLen + c.Overhead()) | |||
} | |||
c.Seal(payload[:0], nonce, payload, additionalData) | |||
case cbcMode: | |||
blockSize := c.BlockSize() | |||
@@ -155,8 +155,8 @@ func ExampleConfig_keyLogWriter_TLS13() { | |||
// preferences. | |||
// Output: | |||
// CLIENT_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 ab02b68658d18ef1a4056b3094fe511b43084d40e9a6518753a7f832da724292 | |||
// SERVER_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 d2e96648d170e2524bee07b651f4cca932a52247493ca33cc0714260a7424b2d | |||
// SERVER_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 371fab23269e3cd73496e0e78f3dbc487f7cd5a563cc9f8c1a71be242268c375 | |||
// CLIENT_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 ca30484e48ec9a6f3b05b41c7492dbed8dea8e92d2abece2824a96052ac8ed8d | |||
// CLIENT_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 16ca97d21087a14d406b2601b4713dd82b156cc01d54665baaa4bdb62b72b9a4 | |||
// SERVER_HANDSHAKE_TRAFFIC_SECRET 0000000000000000000000000000000000000000000000000000000000000000 102c68d960da4f5e2b76a99636ac07bb5774e43b8ce8c14aa4dfd9bf54d11754 | |||
// SERVER_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 f3208d533bb885f32f52142acb484eed104739970c2f426e72a1ee31f6d28650 | |||
// CLIENT_TRAFFIC_SECRET_0 0000000000000000000000000000000000000000000000000000000000000000 70de6b1936df7db171c02f9cfdb04dfa9405a891c959beb15b86f26b2057ba23 | |||
} |
@@ -106,6 +106,7 @@ NextCipherSuite: | |||
hello.vers = VersionTLS12 | |||
hello.supportedVersions = config.getSupportedVersions() | |||
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms13 | |||
hello.supportedSignatureAlgorithmsCert = supportedSigAlgorithmsCert(supportedSignatureAlgorithms13) | |||
} | |||
return hello, nil | |||
@@ -191,7 +192,7 @@ func (c *Conn) clientHandshake() error { | |||
// Create one keyshare for the first default curve. If it is not | |||
// appropriate, the server should raise a HRR. | |||
defaultGroup := c.config.curvePreferences()[0] | |||
hs.privateKey, clientKS, err = c.config.generateKeyShare(defaultGroup) | |||
hs.privateKey, clientKS, err = c.config.generateKeyShare(defaultGroup, kRole_Client) | |||
if err != nil { | |||
c.sendAlert(alertInternalError) | |||
return err | |||
@@ -6,81 +6,97 @@ package tls | |||
import ( | |||
"bytes" | |||
"encoding/binary" | |||
"strings" | |||
) | |||
// signAlgosCertList helper function returns either list of signature algorithms in case | |||
// signature_algorithms_cert extension should be marshalled or nil in the other case. | |||
// signAlgos is a list of algorithms from signature_algorithms extension. signAlgosCert is a list | |||
// of algorithms from signature_algorithms_cert extension. | |||
func signAlgosCertList(signAlgos, signAlgosCert []SignatureScheme) []SignatureScheme { | |||
if eqSignatureAlgorithms(signAlgos, signAlgosCert) { | |||
// ensure that only supported_algorithms extension is send if supported_algorithms_cert | |||
// has identical content | |||
return nil | |||
} | |||
return signAlgosCert | |||
} | |||
type clientHelloMsg struct { | |||
raw []byte | |||
rawTruncated []byte // for PSK binding | |||
vers uint16 | |||
random []byte | |||
sessionId []byte | |||
cipherSuites []uint16 | |||
compressionMethods []uint8 | |||
nextProtoNeg bool | |||
serverName string | |||
ocspStapling bool | |||
scts bool | |||
supportedCurves []CurveID | |||
supportedPoints []uint8 | |||
ticketSupported bool | |||
sessionTicket []uint8 | |||
supportedSignatureAlgorithms []SignatureScheme | |||
secureRenegotiation []byte | |||
secureRenegotiationSupported bool | |||
alpnProtocols []string | |||
keyShares []keyShare | |||
supportedVersions []uint16 | |||
psks []psk | |||
pskKeyExchangeModes []uint8 | |||
earlyData bool | |||
raw []byte | |||
rawTruncated []byte // for PSK binding | |||
vers uint16 | |||
random []byte | |||
sessionId []byte | |||
cipherSuites []uint16 | |||
compressionMethods []uint8 | |||
nextProtoNeg bool | |||
serverName string | |||
ocspStapling bool | |||
scts bool | |||
supportedCurves []CurveID | |||
supportedPoints []uint8 | |||
ticketSupported bool | |||
sessionTicket []uint8 | |||
supportedSignatureAlgorithms []SignatureScheme | |||
supportedSignatureAlgorithmsCert []SignatureScheme | |||
secureRenegotiation []byte | |||
secureRenegotiationSupported bool | |||
alpnProtocols []string | |||
keyShares []keyShare | |||
supportedVersions []uint16 | |||
psks []psk | |||
pskKeyExchangeModes []uint8 | |||
earlyData bool | |||
} | |||
// Helpers | |||
// Marshalling of signature_algorithms extension see https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 | |||
// for more details. Extension is serialized in data buffer | |||
// Function used for signature_algorithms and signature_algorithrms_cert extensions only | |||
// (for more details, see TLS 1.3 draft 28, 4.2.3) | |||
// Function advances data slice and returns it, so that it can be used for further processing | |||
func marshalExtensionSignatureAlgorithms(data []byte, sigSchemes []SignatureScheme) []byte { | |||
data[0] = byte(extensionSignatureAlgorithms >> 8) | |||
data[1] = byte(extensionSignatureAlgorithms) | |||
l := 2 + 2*len(sigSchemes) | |||
data[2] = byte(l >> 8) | |||
data[3] = byte(l) | |||
data = data[4:] | |||
func marshalExtensionSignatureAlgorithms(extension uint16, data []byte, schemes []SignatureScheme) []byte { | |||
algNum := uint16(len(schemes)) | |||
if algNum == 0 { | |||
return data | |||
} | |||
l -= 2 | |||
data[0] = byte(l >> 8) | |||
data[1] = byte(l) | |||
binary.BigEndian.PutUint16(data, extension) | |||
data = data[2:] | |||
binary.BigEndian.PutUint16(data, (2*algNum)+2) // +1 for length | |||
data = data[2:] | |||
binary.BigEndian.PutUint16(data, (2 * algNum)) | |||
data = data[2:] | |||
for _, sigAlgo := range sigSchemes { | |||
data[0] = byte(sigAlgo >> 8) | |||
data[1] = byte(sigAlgo) | |||
for _, algo := range schemes { | |||
binary.BigEndian.PutUint16(data, uint16(algo)) | |||
data = data[2:] | |||
} | |||
return data | |||
} | |||
// Unmrshalling of signature_algorithms extension see https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 | |||
// for more details. | |||
// Function used for unmarshalling signature_algorithms or signature_algorithms_cert extensions only | |||
// (for more details, see TLS 1.3 draft 28, 4.2.3) | |||
// In case of error function returns alertDecoderError otherwise filled SignatureScheme slice and alertSuccess | |||
func unmarshalExtensionSignatureAlgorithms(data []byte, length int) ([]SignatureScheme, alert) { | |||
if length < 2 || length&1 != 0 { | |||
return nil, alertDecodeError | |||
} | |||
l := int(data[0])<<8 | int(data[1]) | |||
if l != length-2 { | |||
algLen := binary.BigEndian.Uint16(data) | |||
idx := 2 | |||
if int(algLen) != length-2 { | |||
return nil, alertDecodeError | |||
} | |||
n := l / 2 | |||
d := data[2:] | |||
sigSchemes := make([]SignatureScheme, n) | |||
for i := range sigSchemes { | |||
sigSchemes[i] = SignatureScheme(d[0])<<8 | SignatureScheme(d[1]) | |||
d = d[2:] | |||
schemes := make([]SignatureScheme, algLen/2) | |||
for i := range schemes { | |||
schemes[i] = SignatureScheme(binary.BigEndian.Uint16(data[idx:])) | |||
idx += 2 | |||
} | |||
return sigSchemes, alertSuccess | |||
return schemes, alertSuccess | |||
} | |||
func (m *clientHelloMsg) equal(i interface{}) bool { | |||
@@ -104,6 +120,7 @@ func (m *clientHelloMsg) equal(i interface{}) bool { | |||
m.ticketSupported == m1.ticketSupported && | |||
bytes.Equal(m.sessionTicket, m1.sessionTicket) && | |||
eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) && | |||
eqSignatureAlgorithms(m.supportedSignatureAlgorithmsCert, m1.supportedSignatureAlgorithmsCert) && | |||
m.secureRenegotiationSupported == m1.secureRenegotiationSupported && | |||
bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) && | |||
eqStrings(m.alpnProtocols, m1.alpnProtocols) && | |||
@@ -120,6 +137,8 @@ func (m *clientHelloMsg) marshal() []byte { | |||
length := 2 + 32 + 1 + len(m.sessionId) + 2 + len(m.cipherSuites)*2 + 1 + len(m.compressionMethods) | |||
numExtensions := 0 | |||
extensionsLength := 0 | |||
// Indicates wether to send signature_algorithms_cert extension | |||
if m.nextProtoNeg { | |||
numExtensions++ | |||
} | |||
@@ -147,6 +166,10 @@ func (m *clientHelloMsg) marshal() []byte { | |||
extensionsLength += 2 + 2*len(m.supportedSignatureAlgorithms) | |||
numExtensions++ | |||
} | |||
if m.getSignatureAlgorithmsCert() != nil { | |||
extensionsLength += 2 + 2*len(m.getSignatureAlgorithmsCert()) | |||
numExtensions++ | |||
} | |||
if m.secureRenegotiationSupported { | |||
extensionsLength += 1 + len(m.secureRenegotiation) | |||
numExtensions++ | |||
@@ -305,26 +328,15 @@ func (m *clientHelloMsg) marshal() []byte { | |||
copy(z, m.sessionTicket) | |||
z = z[len(m.sessionTicket):] | |||
} | |||
if len(m.supportedSignatureAlgorithms) > 0 { | |||
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 | |||
// https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.3 | |||
z[0] = byte(extensionSignatureAlgorithms >> 8) | |||
z[1] = byte(extensionSignatureAlgorithms) | |||
l := 2 + 2*len(m.supportedSignatureAlgorithms) | |||
z[2] = byte(l >> 8) | |||
z[3] = byte(l) | |||
z = z[4:] | |||
l -= 2 | |||
z[0] = byte(l >> 8) | |||
z[1] = byte(l) | |||
z = z[2:] | |||
for _, sigAlgo := range m.supportedSignatureAlgorithms { | |||
z[0] = byte(sigAlgo >> 8) | |||
z[1] = byte(sigAlgo) | |||
z = z[2:] | |||
} | |||
if len(m.supportedSignatureAlgorithms) > 0 { | |||
z = marshalExtensionSignatureAlgorithms(extensionSignatureAlgorithms, z, m.supportedSignatureAlgorithms) | |||
} | |||
if m.getSignatureAlgorithmsCert() != nil { | |||
// Ensure only one list of algorithms is sent if supported_algorithms and supported_algorithms_cert are the same | |||
z = marshalExtensionSignatureAlgorithms(extensionSignatureAlgorithmsCert, z, m.getSignatureAlgorithmsCert()) | |||
} | |||
if m.secureRenegotiationSupported { | |||
z[0] = byte(extensionRenegotiationInfo >> 8) | |||
z[1] = byte(extensionRenegotiationInfo & 0xff) | |||
@@ -743,6 +755,10 @@ func (m *clientHelloMsg) unmarshal(data []byte) alert { | |||
return alertSuccess | |||
} | |||
func (m *clientHelloMsg) getSignatureAlgorithmsCert() []SignatureScheme { | |||
return signAlgosCertList(m.supportedSignatureAlgorithms, m.supportedSignatureAlgorithmsCert) | |||
} | |||
type serverHelloMsg struct { | |||
raw []byte | |||
vers uint16 | |||
@@ -1069,7 +1085,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) alert { | |||
if m.vers != VersionTLS12 { | |||
return alertDecodeError | |||
} | |||
m.vers = uint16(svData[0])<<8 | uint16(svData[1]) | |||
rcvVer := binary.BigEndian.Uint16(svData[0:]) | |||
if rcvVer < VersionTLS13 { | |||
return alertIllegalParameter | |||
} | |||
m.vers = rcvVer | |||
} | |||
for len(data) != 0 { | |||
@@ -2074,15 +2094,17 @@ func (m *certificateRequestMsg) unmarshal(data []byte) alert { | |||
if len(data) != 0 { | |||
return alertDecodeError | |||
} | |||
return alertSuccess | |||
} | |||
type certificateRequestMsg13 struct { | |||
raw []byte | |||
requestContext []byte | |||
supportedSignatureAlgorithms []SignatureScheme | |||
certificateAuthorities [][]byte | |||
requestContext []byte | |||
supportedSignatureAlgorithms []SignatureScheme | |||
supportedSignatureAlgorithmsCert []SignatureScheme | |||
certificateAuthorities [][]byte | |||
} | |||
func (m *certificateRequestMsg13) equal(i interface{}) bool { | |||
@@ -2091,7 +2113,8 @@ func (m *certificateRequestMsg13) equal(i interface{}) bool { | |||
bytes.Equal(m.raw, m1.raw) && | |||
bytes.Equal(m.requestContext, m1.requestContext) && | |||
eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) && | |||
eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) | |||
eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) && | |||
eqSignatureAlgorithms(m.supportedSignatureAlgorithmsCert, m1.supportedSignatureAlgorithmsCert) | |||
} | |||
func (m *certificateRequestMsg13) marshal() (x []byte) { | |||
@@ -2104,6 +2127,11 @@ func (m *certificateRequestMsg13) marshal() (x []byte) { | |||
numExtensions := 1 | |||
extensionsLength := 2 + 2*len(m.supportedSignatureAlgorithms) | |||
if m.getSignatureAlgorithmsCert() != nil { | |||
numExtensions += 1 | |||
extensionsLength += 2 + 2*len(m.getSignatureAlgorithmsCert()) | |||
} | |||
casLength := 0 | |||
if len(m.certificateAuthorities) > 0 { | |||
for _, ca := range m.certificateAuthorities { | |||
@@ -2131,7 +2159,11 @@ func (m *certificateRequestMsg13) marshal() (x []byte) { | |||
z = z[2:] | |||
// TODO: this function should be reused by CH | |||
z = marshalExtensionSignatureAlgorithms(z, m.supportedSignatureAlgorithms) | |||
z = marshalExtensionSignatureAlgorithms(extensionSignatureAlgorithms, z, m.supportedSignatureAlgorithms) | |||
if m.getSignatureAlgorithmsCert() != nil { | |||
z = marshalExtensionSignatureAlgorithms(extensionSignatureAlgorithmsCert, z, m.getSignatureAlgorithmsCert()) | |||
} | |||
// certificate_authorities | |||
if casLength > 0 { | |||
z[0] = byte(extensionCAs >> 8) | |||
@@ -2204,6 +2236,12 @@ func (m *certificateRequestMsg13) unmarshal(data []byte) alert { | |||
if err != alertSuccess { | |||
return err | |||
} | |||
case extensionSignatureAlgorithmsCert: | |||
var err alert | |||
m.supportedSignatureAlgorithmsCert, err = unmarshalExtensionSignatureAlgorithms(data, length) | |||
if err != alertSuccess { | |||
return err | |||
} | |||
case extensionCAs: | |||
// TODO DRY: share code with CH | |||
if length < 2 { | |||
@@ -2240,6 +2278,10 @@ func (m *certificateRequestMsg13) unmarshal(data []byte) alert { | |||
return alertSuccess | |||
} | |||
func (m *certificateRequestMsg13) getSignatureAlgorithmsCert() []SignatureScheme { | |||
return signAlgosCertList(m.supportedSignatureAlgorithms, m.supportedSignatureAlgorithmsCert) | |||
} | |||
type certificateVerifyMsg struct { | |||
raw []byte | |||
hasSignatureAndHash bool | |||
@@ -197,9 +197,9 @@ func TestDontSelectRSAWithECDSAKey(t *testing.T) { | |||
func TestRenegotiationExtension(t *testing.T) { | |||
clientHello := &clientHelloMsg{ | |||
vers: VersionTLS12, | |||
compressionMethods: []uint8{compressionNone}, | |||
random: make([]byte, 32), | |||
vers: VersionTLS12, | |||
compressionMethods: []uint8{compressionNone}, | |||
random: make([]byte, 32), | |||
secureRenegotiationSupported: true, | |||
cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, | |||
} | |||
@@ -394,8 +394,6 @@ func TestSCTHandshake(t *testing.T) { | |||
PrivateKey: testRSAPrivateKey, | |||
SignedCertificateTimestamps: expected, | |||
}}, | |||
// See GH#76 | |||
MaxVersion: VersionTLS12, | |||
} | |||
clientConfig := &Config{ | |||
InsecureSkipVerify: true, | |||
@@ -1004,7 +1002,7 @@ func TestFallbackSCSV(t *testing.T) { | |||
name: "FallbackSCSV", | |||
config: &serverConfig, | |||
// OpenSSL 1.0.1j is needed for the -fallback_scsv option. | |||
command: []string{"openssl", "s_client", "-fallback_scsv"}, | |||
command: []string{"openssl", "s_client", "-fallback_scsv"}, | |||
expectHandshakeErrorIncluding: "inappropriate protocol fallback", | |||
} | |||
runServerTestTLS11(t, test) | |||