@@ -15,6 +15,9 @@ env: | |||||
matrix: | matrix: | ||||
fast_finish: true | fast_finish: true | ||||
before_install: | |||||
- make -f _dev/Makefile fmtcheck | |||||
install: | install: | ||||
- sudo pip install docker | - sudo pip install docker | ||||
@@ -21,6 +21,8 @@ import ( | |||||
"sync/atomic" | "sync/atomic" | ||||
"time" | "time" | ||||
sidh "github_com/henrydcase/nobs/dh/sidh" | |||||
x448 "github_com/henrydcase/nobs/ec/x448" | |||||
"golang_org/x/crypto/curve25519" | "golang_org/x/crypto/curve25519" | ||||
) | ) | ||||
@@ -40,6 +42,21 @@ const ( | |||||
secretResumption | 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 { | type keySchedule13 struct { | ||||
suite *cipherSuite | suite *cipherSuite | ||||
transcriptHash hash.Hash // uses the cipher suite hash algo | 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. | 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 { | func newKeySchedule13(suite *cipherSuite, config *Config, clientRandom []byte) *keySchedule13 { | ||||
if config.KeyLogWriter == nil { | if config.KeyLogWriter == nil { | ||||
clientRandom = 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 { | if err != nil { | ||||
c.sendAlert(alertInternalError) | c.sendAlert(alertInternalError) | ||||
return err | return err | ||||
} | } | ||||
hs.hello.keyShare = serverKS | hs.hello.keyShare = serverKS | ||||
c.KeyShareCurve = serverKS.group | |||||
hash := hashForSuite(hs.suite) | hash := hashForSuite(hs.suite) | ||||
hashSize := hash.Size() | hashSize := hash.Size() | ||||
@@ -186,7 +221,7 @@ CurvePreferenceLoop: | |||||
earlyClientCipher, _ := hs.keySchedule.prepareCipher(secretEarlyClient) | earlyClientCipher, _ := hs.keySchedule.prepareCipher(secretEarlyClient) | ||||
ecdheSecret := deriveECDHESecret(ks, privateKey) | |||||
ecdheSecret := deriveECDHESecret(ks, privateKey, kRole_Server) | |||||
if ecdheSecret == nil { | if ecdheSecret == nil { | ||||
c.sendAlert(alertIllegalParameter) | c.sendAlert(alertIllegalParameter) | ||||
return errors.New("tls: bad ECDHE client share") | return errors.New("tls: bad ECDHE client share") | ||||
@@ -225,6 +260,7 @@ CurvePreferenceLoop: | |||||
certReq := new(certificateRequestMsg13) | certReq := new(certificateRequestMsg13) | ||||
// extension 'signature_algorithms' MUST be specified | // extension 'signature_algorithms' MUST be specified | ||||
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms13 | certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms13 | ||||
certReq.supportedSignatureAlgorithmsCert = supportedSigAlgorithmsCert(supportedSignatureAlgorithms13) | |||||
hs.keySchedule.write(certReq.marshal()) | hs.keySchedule.write(certReq.marshal()) | ||||
if _, err := hs.c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil { | if _, err := hs.c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil { | ||||
return err | return err | ||||
@@ -328,7 +364,7 @@ func (hs *serverHandshakeState) readClientFinished13(hasConfirmLock bool) error | |||||
return unexpectedMessageError(certVerify, msg) | return unexpectedMessageError(certVerify, msg) | ||||
} | } | ||||
err, alertCode := verifyPeerCertificate( | |||||
err, alertCode := verifyPeerHandshakeSignature( | |||||
certVerify, | certVerify, | ||||
pubKey, | pubKey, | ||||
supportedSignatureAlgorithms13, | supportedSignatureAlgorithms13, | ||||
@@ -540,9 +576,12 @@ func prepareDigitallySigned(hash crypto.Hash, context string, data []byte) []byt | |||||
return h.Sum(nil) | 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 { | if curveID == X25519 { | ||||
var scalar, public [32]byte | |||||
var scalar, public [x25519SharedSecretSize]byte | |||||
if _, err := io.ReadFull(c.rand(), scalar[:]); err != nil { | if _, err := io.ReadFull(c.rand(), scalar[:]); err != nil { | ||||
return nil, keyShare{}, err | 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 | 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) | curve, ok := curveForCurveID(curveID) | ||||
if !ok { | if !ok { | ||||
return nil, keyShare{}, errors.New("tls: preferredCurves includes unsupported curve") | 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 | 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 ks.group == X25519 { | ||||
if len(ks.data) != 32 { | |||||
if len(ks.data) != x25519SharedSecretSize { | |||||
return nil | return nil | ||||
} | } | ||||
var theirPublic, sharedKey, scalar [32]byte | |||||
var theirPublic, sharedKey, scalar [x25519SharedSecretSize]byte | |||||
copy(theirPublic[:], ks.data) | copy(theirPublic[:], ks.data) | ||||
copy(scalar[:], secretKey) | copy(scalar[:], secretKey) | ||||
curve25519.ScalarMult(&sharedKey, &scalar, &theirPublic) | curve25519.ScalarMult(&sharedKey, &scalar, &theirPublic) | ||||
return sharedKey[:] | 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) | curve, ok := curveForCurveID(ks.group) | ||||
if !ok { | if !ok { | ||||
return nil | return nil | ||||
@@ -841,7 +1017,7 @@ func (hs *clientHandshakeState) processEncryptedExtensions(ee *encryptedExtensio | |||||
return nil | return nil | ||||
} | } | ||||
func verifyPeerCertificate( | |||||
func verifyPeerHandshakeSignature( | |||||
certVerify *certificateVerifyMsg, | certVerify *certificateVerifyMsg, | ||||
pubKey crypto.PublicKey, | pubKey crypto.PublicKey, | ||||
signAlgosKnown []SignatureScheme, | signAlgosKnown []SignatureScheme, | ||||
@@ -953,6 +1129,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||||
hash := hashForSuite(hs.suite) | hash := hashForSuite(hs.suite) | ||||
hashSize := hash.Size() | hashSize := hash.Size() | ||||
serverHello := hs.serverHello | serverHello := hs.serverHello | ||||
c.scts = serverHello.scts | |||||
// middlebox compatibility mode, send CCS before second flight. | // middlebox compatibility mode, send CCS before second flight. | ||||
if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil { | 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. | // 0-RTT is not supported yet, so use an empty PSK. | ||||
hs.keySchedule.setSecret(nil) | hs.keySchedule.setSecret(nil) | ||||
ecdheSecret := deriveECDHESecret(serverHello.keyShare, hs.privateKey) | |||||
ecdheSecret := deriveECDHESecret(serverHello.keyShare, hs.privateKey, kRole_Client) | |||||
if ecdheSecret == nil { | if ecdheSecret == nil { | ||||
c.sendAlert(alertIllegalParameter) | c.sendAlert(alertIllegalParameter) | ||||
return errors.New("tls: bad ECDHE server share") | return errors.New("tls: bad ECDHE server share") | ||||
@@ -1048,7 +1225,7 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||||
c.sendAlert(alertUnexpectedMessage) | c.sendAlert(alertUnexpectedMessage) | ||||
return unexpectedMessageError(certVerifyMsg, msg) | return unexpectedMessageError(certVerifyMsg, msg) | ||||
} | } | ||||
err, alertCode := verifyPeerCertificate( | |||||
err, alertCode := verifyPeerHandshakeSignature( | |||||
certVerifyMsg, | certVerifyMsg, | ||||
hs.c.peerCertificates[0].PublicKey, | hs.c.peerCertificates[0].PublicKey, | ||||
hs.hello.supportedSignatureAlgorithms, | hs.hello.supportedSignatureAlgorithms, | ||||
@@ -1111,5 +1288,18 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { | |||||
return errors.New("tls: unexpected data after handshake") | return errors.New("tls: unexpected data after handshake") | ||||
} | } | ||||
c.in.setCipher(c.vers, appServerCipher) | c.in.setCipher(c.vers, appServerCipher) | ||||
c.KeyShareCurve = clientKS.group | |||||
return nil | 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 | 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 | ### Testing | ||||
@@ -70,7 +71,7 @@ We run 3 kinds of test:. | |||||
* Unit testing: <br/>``make -f _dev/Makefile test-unit`` | * Unit testing: <br/>``make -f _dev/Makefile test-unit`` | ||||
* Testing against BoringSSL test suite: <br/>``make -f _dev/Makefile test-bogo`` | * 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: | To run all the tests in one go use: | ||||
``` | ``` | ||||
@@ -8,6 +8,7 @@ BUILD_DIR ?= $(PRJ_DIR)/_dev/GOROOT | |||||
# Compiler | # Compiler | ||||
GO ?= go | GO ?= go | ||||
DOCKER ?= docker | DOCKER ?= docker | ||||
GIT ?= git | |||||
# Build environment | # Build environment | ||||
OS ?= $(shell $(GO) env GOHOSTOS) | OS ?= $(shell $(GO) env GOHOSTOS) | ||||
@@ -18,14 +19,19 @@ GOROOT_ENV := $(shell $(GO) env GOROOT) | |||||
GOROOT_LOCAL = $(BUILD_DIR)/$(OS_ARCH) | GOROOT_LOCAL = $(BUILD_DIR)/$(OS_ARCH) | ||||
# Flag indicates wheter invoke "go install -race std". Supported only on amd64 with CGO enabled | # 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)) | INSTALL_RACE:= $(words $(filter $(ARCH)_$(shell go env CGO_ENABLED), amd64_1)) | ||||
TMP_DIR := $(shell mktemp -d) | |||||
# Test targets used for compatibility testing | # Test targets used for compatibility testing | ||||
TARGET_TEST_COMPAT=boring picotls tstclnt | TARGET_TEST_COMPAT=boring picotls tstclnt | ||||
# Some target-specific constants | # Some target-specific constants | ||||
BORINGSSL_REVISION=1530ef3e | |||||
BORINGSSL_REVISION=03de6813d8992a649092b4874ef0ebc022e2f58a | |||||
BOGO_DOCKER_TRIS_LOCATION=/go/src/github.com/cloudflare/tls-tris | BOGO_DOCKER_TRIS_LOCATION=/go/src/github.com/cloudflare/tls-tris | ||||
# NOBS crypto | |||||
NOBS_REPO ?= https://github.com/henrydcase/nobscrypto.git | |||||
############### | ############### | ||||
# | # | ||||
# Build targets | # Build targets | ||||
@@ -48,13 +54,24 @@ $(BUILD_DIR)/$(OS_ARCH)/.ok_$(VER_OS_ARCH): clean | |||||
# Apply additional patches | # Apply additional patches | ||||
for p in $(wildcard $(DEV_DIR)/patches/*); do patch -d "$(GOROOT_LOCAL)" -p1 < "$$p"; done | 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 | # Create go package | ||||
GOARCH=$(ARCH) GOROOT="$(GOROOT_LOCAL)" $(GO) install -v std | GOARCH=$(ARCH) GOROOT="$(GOROOT_LOCAL)" $(GO) install -v std | ||||
ifeq ($(INSTALL_RACE),1) | ifeq ($(INSTALL_RACE),1) | ||||
GOARCH=$(ARCH) GOROOT="$(GOROOT_LOCAL)" $(GO) install -race -v std | GOARCH=$(ARCH) GOROOT="$(GOROOT_LOCAL)" $(GO) install -race -v std | ||||
endif | endif | ||||
rm -rf $(TMP_DIR) | |||||
@touch "$@" | @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) | build-test-%: $(BUILD_DIR)/$(OS_ARCH)/.ok_$(VER_OS_ARCH) | ||||
$(DOCKER) build $(BUILDARG) -t tls-tris:$* $(DEV_DIR)/$* | $(DOCKER) build $(BUILDARG) -t tls-tris:$* $(DEV_DIR)/$* | ||||
$(DOCKER) build $(BUILDARG) -t $(*)-localserver $(DEV_DIR)/$* | $(DOCKER) build $(BUILDARG) -t $(*)-localserver $(DEV_DIR)/$* | ||||
@@ -112,5 +129,7 @@ clean: | |||||
clean-all: clean | clean-all: clean | ||||
rm -rf $(BUILD_DIR) | 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 | 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 | # Draft 18 with client-tests branch | ||||
#ARG REVISION=3f5e87d6a1931b6f6930e4eadb7b2d0b2aa7c588 | #ARG REVISION=3f5e87d6a1931b6f6930e4eadb7b2d0b2aa7c588 | ||||
@@ -20,10 +20,19 @@ RUN git clone https://github.com/FiloSottile/crypto-tls-bogo-shim \ | |||||
#ARG REVISION=81cc32b846c9fe2ea32613287e57a6a0db7bbb9a | #ARG REVISION=81cc32b846c9fe2ea32613287e57a6a0db7bbb9a | ||||
# Draft 22 with draft22-client branch (client-tests + draft22) | # 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 | 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"] | CMD ["make", "run"] |
@@ -34,15 +34,24 @@ RUN mkdir boringssl/build | |||||
# ARG REVISION=89917a5 | # ARG REVISION=89917a5 | ||||
# Draft 18 | # Draft 18 | ||||
#ARG REVISION=9b885c5 | |||||
# ARG REVISION=9b885c5 | |||||
# Draft 18, but with "bssl server -loop -www" support and build fix | # Draft 18, but with "bssl server -loop -www" support and build fix | ||||
ARG REVISION=40b24c8154 | |||||
# ARG REVISION=40b24c8154 | |||||
# Draft 21 | # Draft 21 | ||||
#ARG REVISION=cd8470f | |||||
# ARG REVISION=cd8470f | |||||
# Draft 22 | # 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 fetch | ||||
RUN cd boringssl && git checkout $REVISION | RUN cd boringssl && git checkout $REVISION | ||||
@@ -2,7 +2,7 @@ | |||||
set -e | set -e | ||||
/boringssl/build/tool/bssl client -grease -min-version tls1.3 -max-version tls1.3 \ | /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 \ | 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 \ | bssl server \ | ||||
-key rsa.pem \ | -key rsa.pem \ | ||||
-min-version tls1.2 -max-version tls1.3 \ | -min-version tls1.2 -max-version tls1.3 \ | ||||
-tls13-draft22-variant \ | |||||
-tls13-variant draft28 \ | |||||
-accept 1443 -loop -www 2>&1 & | -accept 1443 -loop -www 2>&1 & | ||||
# ECDSA | # ECDSA | ||||
bssl server \ | bssl server \ | ||||
-key ecdsa.pem \ | -key ecdsa.pem \ | ||||
-min-version tls1.2 -max-version tls1.3 \ | -min-version tls1.2 -max-version tls1.3 \ | ||||
-tls13-draft22-variant \ | |||||
-tls13-variant draft28 \ | |||||
-accept 2443 -loop -www 2>&1 & | -accept 2443 -loop -www 2>&1 & | ||||
# Require client authentication (with ECDSA) | # Require client authentication (with ECDSA) | ||||
bssl server \ | bssl server \ | ||||
-key ecdsa.pem \ | -key ecdsa.pem \ | ||||
-min-version tls1.2 -max-version tls1.3 \ | -min-version tls1.2 -max-version tls1.3 \ | ||||
-tls13-draft22-variant \ | |||||
-tls13-variant draft28 \ | |||||
-accept 6443 -loop -www \ | -accept 6443 -loop -www \ | ||||
-require-any-client-cert -debug 2>&1 & | -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----- | |||||
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 | |||||
-----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 ''' | ''' Ensures that those regexe's actually work ''' | ||||
LINE_HELLO_TLS ="\nsomestuff\nHello TLS 1.3 _o/\nsomestuff" | 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): | def test_regexes(self): | ||||
self.assertIsNotNone( | self.assertIsNotNone( | ||||
@@ -210,14 +210,28 @@ class InteropServer_BoringSSL( | |||||
ServerNominalMixin, | ServerNominalMixin, | ||||
ServerClientAuthMixin, | ServerClientAuthMixin, | ||||
unittest.TestCase | 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( | class InteropServer_NSS( | ||||
InteropServer, | InteropServer, | ||||
@@ -241,7 +255,10 @@ class InteropClient_NSS( | |||||
): SERVER_NAME = "tstclnt-localserver" | ): SERVER_NAME = "tstclnt-localserver" | ||||
# TRIS as a client | # TRIS as a client | ||||
class InteropServer_TRIS(ClientNominalMixin, InteropServer, unittest.TestCase): | |||||
class InteropServer_TRIS( | |||||
ClientNominalMixin, | |||||
InteropServer, | |||||
unittest.TestCase): | |||||
CLIENT_NAME = 'tris-testclient' | 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") | res = self.d.run_client(self.CLIENT_NAME, '-rsa=false -ecdsa=false -cliauth '+self.server_ip+":6443") | ||||
self.assertEqual(res[0], 0) | 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__': | if __name__ == '__main__': | ||||
unittest.main() | unittest.main() |
@@ -8,6 +8,7 @@ EXPOSE 3443 | |||||
EXPOSE 4443 | EXPOSE 4443 | ||||
EXPOSE 5443 | EXPOSE 5443 | ||||
EXPOSE 6443 | EXPOSE 6443 | ||||
EXPOSE 7443 | |||||
ADD tris-localserver / | ADD tris-localserver / | ||||
ADD runner.sh / | 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: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: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: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 | wait |
@@ -2,15 +2,14 @@ package main | |||||
import ( | import ( | ||||
"crypto/tls" | "crypto/tls" | ||||
"crypto/x509" | |||||
"encoding/hex" | "encoding/hex" | ||||
"flag" | "flag" | ||||
"fmt" | "fmt" | ||||
"io" | |||||
"log" | "log" | ||||
"net/http" | "net/http" | ||||
"os" | "os" | ||||
"time" | "time" | ||||
"crypto/x509" | |||||
) | ) | ||||
type ZeroRTT_t int | type ZeroRTT_t int | ||||
@@ -18,21 +17,21 @@ type PubKeyAlgo_t int | |||||
// Bitset | // Bitset | ||||
const ( | 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 ( | const ( | ||||
PubKeyRSA PubKeyAlgo_t = iota | |||||
PubKeyRSA PubKeyAlgo_t = iota | |||||
PubKeyECDSA | PubKeyECDSA | ||||
) | ) | ||||
type server struct { | 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{ | var tlsVersionToName = map[uint16]string{ | ||||
@@ -43,14 +42,25 @@ var tlsVersionToName = map[uint16]string{ | |||||
tls.VersionTLS13Draft18: "1.3 (draft 18)", | tls.VersionTLS13Draft18: "1.3 (draft 18)", | ||||
tls.VersionTLS13Draft21: "1.3 (draft 21)", | tls.VersionTLS13Draft21: "1.3 (draft 21)", | ||||
tls.VersionTLS13Draft22: "1.3 (draft 22)", | tls.VersionTLS13Draft22: "1.3 (draft 22)", | ||||
tls.VersionTLS13Draft23: "1.3 (draft 23)", | |||||
tls.VersionTLS13Draft28: "1.3 (draft 28)", | |||||
} | } | ||||
func NewServer() *server { | 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() { | func (s *server) start() { | ||||
@@ -58,73 +68,72 @@ func (s *server) start() { | |||||
if s.PubKey == PubKeyRSA { | if s.PubKey == PubKeyRSA { | ||||
cert, err = tls.X509KeyPair([]byte(rsaCert), []byte(rsaKey)) | cert, err = tls.X509KeyPair([]byte(rsaCert), []byte(rsaKey)) | ||||
} | } | ||||
s.TLS.Certificates = []tls.Certificate{cert} | |||||
if err != nil { | if err != nil { | ||||
log.Fatal(err) | 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 != "" { | 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 { | if err != nil { | ||||
log.Fatalf("Cannot open keylog file: %v", err) | log.Fatalf("Cannot open keylog file: %v", err) | ||||
} | } | ||||
log.Println("Enabled keylog") | 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{ | 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("", "")) | 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() { | 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) { | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||||
tlsConn := r.Context().Value(http.TLSConnContextKey).(*tls.Conn) | tlsConn := r.Context().Value(http.TLSConnContextKey).(*tls.Conn) | ||||
@@ -212,7 +221,7 @@ ClMLEiNJQ0OMxAIaRtb2RehD4q3OWlpWf6joJ36PRBqL8T5+f2x6Tg3c64UR+QPX | |||||
98UcCQHHdEhm7y2z5Z2Wt0B48tZ+UAxDEoEwMghNyw7wUD79IRlXGYypBnXaMuLX | 98UcCQHHdEhm7y2z5Z2Wt0B48tZ+UAxDEoEwMghNyw7wUD79IRlXGYypBnXaMuLX | ||||
46aGxbsSQ7Rfg62Co3JG7vo+eJd0AoZHrtFUnfM8V70IFzMBZnSwRslHRJe56Q== | 46aGxbsSQ7Rfg62Co3JG7vo+eJd0AoZHrtFUnfM8V70IFzMBZnSwRslHRJe56Q== | ||||
-----END CERTIFICATE-----` | -----END CERTIFICATE-----` | ||||
rsaCa_client = `-----BEGIN CERTIFICATE----- | |||||
rsaCa_client = `-----BEGIN CERTIFICATE----- | |||||
MIIFYDCCA0igAwIBAgIJAPpBgIvtQb1EMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV | MIIFYDCCA0igAwIBAgIJAPpBgIvtQb1EMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV | ||||
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX | ||||
aWRnaXRzIFB0eSBMdGQwHhcNMTgwMjEzMjAxNjA3WhcNMTkwMjEzMjAxNjA3WjBF | aWRnaXRzIFB0eSBMdGQwHhcNMTgwMjEzMjAxNjA3WhcNMTkwMjEzMjAxNjA3WjBF | ||||
@@ -17,6 +17,8 @@ var tlsVersionToName = map[uint16]string{ | |||||
tls.VersionTLS12: "1.2", | tls.VersionTLS12: "1.2", | ||||
tls.VersionTLS13: "1.3", | tls.VersionTLS13: "1.3", | ||||
tls.VersionTLS13Draft18: "1.3 (draft 18)", | tls.VersionTLS13Draft18: "1.3 (draft 18)", | ||||
tls.VersionTLS13Draft23: "1.3 (draft 23)", | |||||
tls.VersionTLS13Draft28: "1.3 (draft 28)", | |||||
} | } | ||||
var cipherSuiteIdToName = map[uint16]string{ | var cipherSuiteIdToName = map[uint16]string{ | ||||
@@ -27,28 +29,42 @@ var cipherSuiteIdToName = map[uint16]string{ | |||||
tls.TLS_CHACHA20_POLY1305_SHA256: "TLS_CHACHA20_POLY1305_SHA256", | 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 { | 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 { | if err != nil { | ||||
fmt.Printf("handshake failed: %v\n\n", err) | fmt.Printf("handshake failed: %v\n\n", err) | ||||
c.failed++ | |||||
failed++ | |||||
return | return | ||||
} | } | ||||
defer con.Close() | 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")) | _, err = con.Write([]byte("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")) | ||||
if err != nil { | if err != nil { | ||||
fmt.Printf("Write failed: %v\n\n", err) | fmt.Printf("Write failed: %v\n\n", err) | ||||
c.failed++ | |||||
failed++ | |||||
return | return | ||||
} | } | ||||
@@ -66,7 +82,7 @@ func (c *Client) run(addr string, version, cipherSuite uint16) { | |||||
// is received right after reading data (observed with NSS selfserv). | // is received right after reading data (observed with NSS selfserv). | ||||
if !(n > 0 && err == io.EOF) && err != nil { | if !(n > 0 && err == io.EOF) && err != nil { | ||||
fmt.Printf("Read failed: %v\n\n", err) | fmt.Printf("Read failed: %v\n\n", err) | ||||
c.failed++ | |||||
failed++ | |||||
return | return | ||||
} | } | ||||
fmt.Printf("Read %d bytes\n", n) | fmt.Printf("Read %d bytes\n", n) | ||||
@@ -74,25 +90,47 @@ func (c *Client) run(addr string, version, cipherSuite uint16) { | |||||
fmt.Println("OK\n") | 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() { | func main() { | ||||
var keylog_file string | |||||
var keylog_file, qr string | |||||
var enable_rsa, enable_ecdsa, client_auth bool | var enable_rsa, enable_ecdsa, client_auth bool | ||||
flag.StringVar(&keylog_file, "keylogfile", "", "Secrets will be logged here") | 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_rsa, "rsa", true, "Whether to enable RSA cipher suites") | ||||
flag.BoolVar(&enable_ecdsa, "ecdsa", true, "Whether to enable ECDSA 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.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() | flag.Parse() | ||||
if flag.NArg() != 1 { | if flag.NArg() != 1 { | ||||
flag.Usage() | flag.Usage() | ||||
os.Exit(1) | 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 == "" { | if keylog_file == "" { | ||||
keylog_file = os.Getenv("SSLKEYLOGFILE") | keylog_file = os.Getenv("SSLKEYLOGFILE") | ||||
} | } | ||||
@@ -101,45 +139,64 @@ func main() { | |||||
if err != nil { | if err != nil { | ||||
log.Fatalf("Cannot open keylog file: %v", err) | log.Fatalf("Cannot open keylog file: %v", err) | ||||
} | } | ||||
client.KeyLogWriter = keylog_writer | |||||
client.TLS.KeyLogWriter = keylog_writer | |||||
log.Println("Enabled keylog") | log.Println("Enabled keylog") | ||||
} | } | ||||
if client_auth { | if client_auth { | ||||
var err error | 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 { | if err != nil { | ||||
panic("Can't load client certificate") | 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") | 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 { | if enable_rsa { | ||||
// Sanity check: TLS 1.2 with the mandatory cipher suite from RFC 5246 | // 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 { | if enable_ecdsa { | ||||
// Sane cipher suite for TLS 1.2 with an ECDSA cert (as used by boringssl) | // 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 test other kex methods besides X25519, like MTI secp256r1 | ||||
// TODO limit supported groups? | // TODO limit supported groups? | ||||
if client.failed > 0 { | |||||
log.Fatalf("Failed handshakes: %d\n", client.failed) | |||||
} else { | |||||
fmt.Println("All handshakes passed") | |||||
} | |||||
result() | |||||
} | } | ||||
const ( | const ( | ||||
@@ -226,4 +283,3 @@ LAoibwDU1NC8/3MfOBYMe6Qklu3kjexOJrfdo0Z7Khgd9F8A4tKwslUndSSlAfKF | |||||
2rjfqabVMZMLZ2XEbA4W5JTfaZS4YYGcrjY7+i7OsnSxoYG2sb+xlQ== | 2rjfqabVMZMLZ2XEbA4W5JTfaZS4YYGcrjY7+i7OsnSxoYG2sb+xlQ== | ||||
-----END RSA PRIVATE KEY-----` | -----END RSA PRIVATE KEY-----` | ||||
) | ) | ||||
@@ -21,7 +21,13 @@ ENV USE_64=1 NSS_ENABLE_TLS_1_3=1 | |||||
# ARG REVISION=e61c0f657100 | # ARG REVISION=e61c0f657100 | ||||
# Draft 22 | # 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 pull | ||||
RUN cd nss && hg checkout -C $REVISION | 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_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_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}, | {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{} { | func cipherRC4(key, iv []byte, isRead bool) interface{} { | ||||
@@ -174,7 +177,10 @@ type fixedNonceAEAD struct { | |||||
aead cipher.AEAD | 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) Overhead() int { return f.aead.Overhead() } | ||||
func (f *fixedNonceAEAD) explicitNonceLen() int { return 8 } | func (f *fixedNonceAEAD) explicitNonceLen() int { return 8 } | ||||
@@ -418,6 +424,11 @@ const ( | |||||
TLS_AES_256_GCM_SHA384 uint16 = 0x1302 | TLS_AES_256_GCM_SHA384 uint16 = 0x1302 | ||||
TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303 | 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 | // TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator | ||||
// that the client is doing version fallback. See | // that the client is doing version fallback. See | ||||
// https://tools.ietf.org/html/rfc7507. | // https://tools.ietf.org/html/rfc7507. | ||||
@@ -30,6 +30,8 @@ const ( | |||||
VersionTLS13Draft18 = 0x7f00 | 18 | VersionTLS13Draft18 = 0x7f00 | 18 | ||||
VersionTLS13Draft21 = 0x7f00 | 21 | VersionTLS13Draft21 = 0x7f00 | 21 | ||||
VersionTLS13Draft22 = 0x7f00 | 22 | VersionTLS13Draft22 = 0x7f00 | 22 | ||||
VersionTLS13Draft23 = 0x7f00 | 23 | |||||
VersionTLS13Draft28 = 0x7f00 | 28 | |||||
) | ) | ||||
const ( | const ( | ||||
@@ -40,7 +42,7 @@ const ( | |||||
maxWarnAlertCount = 5 // maximum number of consecutive warning alerts | maxWarnAlertCount = 5 // maximum number of consecutive warning alerts | ||||
minVersion = VersionTLS12 | minVersion = VersionTLS12 | ||||
maxVersion = VersionTLS13Draft22 | |||||
maxVersion = VersionTLS13Draft28 | |||||
) | ) | ||||
// TLS record types. | // TLS record types. | ||||
@@ -79,22 +81,23 @@ const ( | |||||
// TLS extension numbers | // TLS extension numbers | ||||
const ( | 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 | // TLS signaling cipher suite values | ||||
@@ -120,6 +123,12 @@ const ( | |||||
CurveP384 CurveID = 24 | CurveP384 CurveID = 24 | ||||
CurveP521 CurveID = 25 | CurveP521 CurveID = 25 | ||||
X25519 CurveID = 29 | X25519 CurveID = 29 | ||||
SidhP503Curve25519 CurveID = 0x0105 | |||||
// X448 + P751 | |||||
SidhP751Curve448 CurveID = 0x0106 | |||||
// X25519 + P751 | |||||
SidhP751Curve25519 CurveID = 0x0107 | |||||
) | ) | ||||
// TLS 1.3 Key Share | // TLS 1.3 Key Share | ||||
@@ -234,7 +243,8 @@ type ConnectionState struct { | |||||
// Unique0RTTToken is only present if HandshakeConfirmed is false. | // Unique0RTTToken is only present if HandshakeConfirmed is false. | ||||
Unique0RTTToken []byte | Unique0RTTToken []byte | ||||
ClientHello []byte // ClientHello packet | |||||
ClientHello []byte // ClientHello packet | |||||
KeyShareCurve CurveID | |||||
} | } | ||||
// ClientAuthType declares the policy the server will follow for | // 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. | // with TLS 1.3 draft versions included. | ||||
// | // | ||||
// TODO: remove once TLS 1.3 is finalised. | // 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 | // getSupportedVersions returns the protocol versions that are supported by the | ||||
// current configuration. | // current configuration. | ||||
@@ -11,6 +11,7 @@ import ( | |||||
"crypto/cipher" | "crypto/cipher" | ||||
"crypto/subtle" | "crypto/subtle" | ||||
"crypto/x509" | "crypto/x509" | ||||
"encoding/binary" | |||||
"errors" | "errors" | ||||
"fmt" | "fmt" | ||||
"io" | "io" | ||||
@@ -125,6 +126,9 @@ type Conn struct { | |||||
binder []byte | binder []byte | ||||
tmp [16]byte | tmp [16]byte | ||||
// OZAPTF: Stores curve ID established during TLS 1.3 handshake | |||||
KeyShareCurve CurveID | |||||
} | } | ||||
type handshakeStatus int | 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[11] = byte(n >> 8) | ||||
hc.additionalData[12] = byte(n) | hc.additionalData[12] = byte(n) | ||||
additionalData = hc.additionalData[:] | 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 | var err error | ||||
payload, err = c.Open(payload[:0], nonce, payload, additionalData) | 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) | c.XORKeyStream(payload, payload) | ||||
case aead: | case aead: | ||||
payloadLen := len(b.data) - recordHeaderLen - explicitIVLen | 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] | nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] | ||||
if len(nonce) == 0 { | if len(nonce) == 0 { | ||||
nonce = hc.seq[:] | nonce = hc.seq[:] | ||||
@@ -475,18 +482,25 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) { | |||||
if hc.version < VersionTLS13 { | if hc.version < VersionTLS13 { | ||||
copy(hc.additionalData[:], hc.seq[:]) | copy(hc.additionalData[:], hc.seq[:]) | ||||
copy(hc.additionalData[8:], b.data[:3]) | 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[:] | 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) | 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) | c.Seal(payload[:0], nonce, payload, additionalData) | ||||
case cbcMode: | case cbcMode: | ||||
blockSize := c.BlockSize() | blockSize := c.BlockSize() | ||||
@@ -155,8 +155,8 @@ func ExampleConfig_keyLogWriter_TLS13() { | |||||
// preferences. | // preferences. | ||||
// Output: | // 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.vers = VersionTLS12 | ||||
hello.supportedVersions = config.getSupportedVersions() | hello.supportedVersions = config.getSupportedVersions() | ||||
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms13 | hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms13 | ||||
hello.supportedSignatureAlgorithmsCert = supportedSigAlgorithmsCert(supportedSignatureAlgorithms13) | |||||
} | } | ||||
return hello, nil | return hello, nil | ||||
@@ -191,7 +192,7 @@ func (c *Conn) clientHandshake() error { | |||||
// Create one keyshare for the first default curve. If it is not | // Create one keyshare for the first default curve. If it is not | ||||
// appropriate, the server should raise a HRR. | // appropriate, the server should raise a HRR. | ||||
defaultGroup := c.config.curvePreferences()[0] | 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 { | if err != nil { | ||||
c.sendAlert(alertInternalError) | c.sendAlert(alertInternalError) | ||||
return err | return err | ||||
@@ -6,81 +6,97 @@ package tls | |||||
import ( | import ( | ||||
"bytes" | "bytes" | ||||
"encoding/binary" | |||||
"strings" | "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 { | 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 | // 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 | // 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:] | 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:] | data = data[2:] | ||||
} | } | ||||
return data | 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 | // In case of error function returns alertDecoderError otherwise filled SignatureScheme slice and alertSuccess | ||||
func unmarshalExtensionSignatureAlgorithms(data []byte, length int) ([]SignatureScheme, alert) { | func unmarshalExtensionSignatureAlgorithms(data []byte, length int) ([]SignatureScheme, alert) { | ||||
if length < 2 || length&1 != 0 { | if length < 2 || length&1 != 0 { | ||||
return nil, alertDecodeError | 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 | 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 { | func (m *clientHelloMsg) equal(i interface{}) bool { | ||||
@@ -104,6 +120,7 @@ func (m *clientHelloMsg) equal(i interface{}) bool { | |||||
m.ticketSupported == m1.ticketSupported && | m.ticketSupported == m1.ticketSupported && | ||||
bytes.Equal(m.sessionTicket, m1.sessionTicket) && | bytes.Equal(m.sessionTicket, m1.sessionTicket) && | ||||
eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) && | eqSignatureAlgorithms(m.supportedSignatureAlgorithms, m1.supportedSignatureAlgorithms) && | ||||
eqSignatureAlgorithms(m.supportedSignatureAlgorithmsCert, m1.supportedSignatureAlgorithmsCert) && | |||||
m.secureRenegotiationSupported == m1.secureRenegotiationSupported && | m.secureRenegotiationSupported == m1.secureRenegotiationSupported && | ||||
bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) && | bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) && | ||||
eqStrings(m.alpnProtocols, m1.alpnProtocols) && | 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) | length := 2 + 32 + 1 + len(m.sessionId) + 2 + len(m.cipherSuites)*2 + 1 + len(m.compressionMethods) | ||||
numExtensions := 0 | numExtensions := 0 | ||||
extensionsLength := 0 | extensionsLength := 0 | ||||
// Indicates wether to send signature_algorithms_cert extension | |||||
if m.nextProtoNeg { | if m.nextProtoNeg { | ||||
numExtensions++ | numExtensions++ | ||||
} | } | ||||
@@ -147,6 +166,10 @@ func (m *clientHelloMsg) marshal() []byte { | |||||
extensionsLength += 2 + 2*len(m.supportedSignatureAlgorithms) | extensionsLength += 2 + 2*len(m.supportedSignatureAlgorithms) | ||||
numExtensions++ | numExtensions++ | ||||
} | } | ||||
if m.getSignatureAlgorithmsCert() != nil { | |||||
extensionsLength += 2 + 2*len(m.getSignatureAlgorithmsCert()) | |||||
numExtensions++ | |||||
} | |||||
if m.secureRenegotiationSupported { | if m.secureRenegotiationSupported { | ||||
extensionsLength += 1 + len(m.secureRenegotiation) | extensionsLength += 1 + len(m.secureRenegotiation) | ||||
numExtensions++ | numExtensions++ | ||||
@@ -305,26 +328,15 @@ func (m *clientHelloMsg) marshal() []byte { | |||||
copy(z, m.sessionTicket) | copy(z, m.sessionTicket) | ||||
z = z[len(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 { | if m.secureRenegotiationSupported { | ||||
z[0] = byte(extensionRenegotiationInfo >> 8) | z[0] = byte(extensionRenegotiationInfo >> 8) | ||||
z[1] = byte(extensionRenegotiationInfo & 0xff) | z[1] = byte(extensionRenegotiationInfo & 0xff) | ||||
@@ -743,6 +755,10 @@ func (m *clientHelloMsg) unmarshal(data []byte) alert { | |||||
return alertSuccess | return alertSuccess | ||||
} | } | ||||
func (m *clientHelloMsg) getSignatureAlgorithmsCert() []SignatureScheme { | |||||
return signAlgosCertList(m.supportedSignatureAlgorithms, m.supportedSignatureAlgorithmsCert) | |||||
} | |||||
type serverHelloMsg struct { | type serverHelloMsg struct { | ||||
raw []byte | raw []byte | ||||
vers uint16 | vers uint16 | ||||
@@ -1069,7 +1085,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) alert { | |||||
if m.vers != VersionTLS12 { | if m.vers != VersionTLS12 { | ||||
return alertDecodeError | 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 { | for len(data) != 0 { | ||||
@@ -2074,15 +2094,17 @@ func (m *certificateRequestMsg) unmarshal(data []byte) alert { | |||||
if len(data) != 0 { | if len(data) != 0 { | ||||
return alertDecodeError | return alertDecodeError | ||||
} | } | ||||
return alertSuccess | return alertSuccess | ||||
} | } | ||||
type certificateRequestMsg13 struct { | type certificateRequestMsg13 struct { | ||||
raw []byte | raw []byte | ||||
requestContext []byte | |||||
supportedSignatureAlgorithms []SignatureScheme | |||||
certificateAuthorities [][]byte | |||||
requestContext []byte | |||||
supportedSignatureAlgorithms []SignatureScheme | |||||
supportedSignatureAlgorithmsCert []SignatureScheme | |||||
certificateAuthorities [][]byte | |||||
} | } | ||||
func (m *certificateRequestMsg13) equal(i interface{}) bool { | 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.raw, m1.raw) && | ||||
bytes.Equal(m.requestContext, m1.requestContext) && | bytes.Equal(m.requestContext, m1.requestContext) && | ||||
eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) && | 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) { | func (m *certificateRequestMsg13) marshal() (x []byte) { | ||||
@@ -2104,6 +2127,11 @@ func (m *certificateRequestMsg13) marshal() (x []byte) { | |||||
numExtensions := 1 | numExtensions := 1 | ||||
extensionsLength := 2 + 2*len(m.supportedSignatureAlgorithms) | extensionsLength := 2 + 2*len(m.supportedSignatureAlgorithms) | ||||
if m.getSignatureAlgorithmsCert() != nil { | |||||
numExtensions += 1 | |||||
extensionsLength += 2 + 2*len(m.getSignatureAlgorithmsCert()) | |||||
} | |||||
casLength := 0 | casLength := 0 | ||||
if len(m.certificateAuthorities) > 0 { | if len(m.certificateAuthorities) > 0 { | ||||
for _, ca := range m.certificateAuthorities { | for _, ca := range m.certificateAuthorities { | ||||
@@ -2131,7 +2159,11 @@ func (m *certificateRequestMsg13) marshal() (x []byte) { | |||||
z = z[2:] | z = z[2:] | ||||
// TODO: this function should be reused by CH | // 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 | // certificate_authorities | ||||
if casLength > 0 { | if casLength > 0 { | ||||
z[0] = byte(extensionCAs >> 8) | z[0] = byte(extensionCAs >> 8) | ||||
@@ -2204,6 +2236,12 @@ func (m *certificateRequestMsg13) unmarshal(data []byte) alert { | |||||
if err != alertSuccess { | if err != alertSuccess { | ||||
return err | return err | ||||
} | } | ||||
case extensionSignatureAlgorithmsCert: | |||||
var err alert | |||||
m.supportedSignatureAlgorithmsCert, err = unmarshalExtensionSignatureAlgorithms(data, length) | |||||
if err != alertSuccess { | |||||
return err | |||||
} | |||||
case extensionCAs: | case extensionCAs: | ||||
// TODO DRY: share code with CH | // TODO DRY: share code with CH | ||||
if length < 2 { | if length < 2 { | ||||
@@ -2240,6 +2278,10 @@ func (m *certificateRequestMsg13) unmarshal(data []byte) alert { | |||||
return alertSuccess | return alertSuccess | ||||
} | } | ||||
func (m *certificateRequestMsg13) getSignatureAlgorithmsCert() []SignatureScheme { | |||||
return signAlgosCertList(m.supportedSignatureAlgorithms, m.supportedSignatureAlgorithmsCert) | |||||
} | |||||
type certificateVerifyMsg struct { | type certificateVerifyMsg struct { | ||||
raw []byte | raw []byte | ||||
hasSignatureAndHash bool | hasSignatureAndHash bool | ||||
@@ -197,9 +197,9 @@ func TestDontSelectRSAWithECDSAKey(t *testing.T) { | |||||
func TestRenegotiationExtension(t *testing.T) { | func TestRenegotiationExtension(t *testing.T) { | ||||
clientHello := &clientHelloMsg{ | clientHello := &clientHelloMsg{ | ||||
vers: VersionTLS12, | |||||
compressionMethods: []uint8{compressionNone}, | |||||
random: make([]byte, 32), | |||||
vers: VersionTLS12, | |||||
compressionMethods: []uint8{compressionNone}, | |||||
random: make([]byte, 32), | |||||
secureRenegotiationSupported: true, | secureRenegotiationSupported: true, | ||||
cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, | cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, | ||||
} | } | ||||
@@ -394,8 +394,6 @@ func TestSCTHandshake(t *testing.T) { | |||||
PrivateKey: testRSAPrivateKey, | PrivateKey: testRSAPrivateKey, | ||||
SignedCertificateTimestamps: expected, | SignedCertificateTimestamps: expected, | ||||
}}, | }}, | ||||
// See GH#76 | |||||
MaxVersion: VersionTLS12, | |||||
} | } | ||||
clientConfig := &Config{ | clientConfig := &Config{ | ||||
InsecureSkipVerify: true, | InsecureSkipVerify: true, | ||||
@@ -1004,7 +1002,7 @@ func TestFallbackSCSV(t *testing.T) { | |||||
name: "FallbackSCSV", | name: "FallbackSCSV", | ||||
config: &serverConfig, | config: &serverConfig, | ||||
// OpenSSL 1.0.1j is needed for the -fallback_scsv option. | // 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", | expectHandshakeErrorIncluding: "inappropriate protocol fallback", | ||||
} | } | ||||
runServerTestTLS11(t, test) | runServerTestTLS11(t, test) | ||||