Skip to content

Commit

Permalink
Implement TLS 1.3 early exporters.
Browse files Browse the repository at this point in the history
Bug: 222
Change-Id: I33ee56358a62afcd9c3921026d55efcc543a5c11
Reviewed-on: https://boringssl-review.googlesource.com/23945
Reviewed-by: Steven Valdez <svaldez@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
  • Loading branch information
davidben authored and CQ bot account: commit-bot@chromium.org committed Dec 11, 2017
1 parent 8f53fc0 commit 650d8c3
Show file tree
Hide file tree
Showing 14 changed files with 405 additions and 90 deletions.
1 change: 1 addition & 0 deletions crypto/err/ssl.errordata
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ SSL,254,DOWNGRADE_DETECTED
SSL,143,DTLS_MESSAGE_TOO_BIG
SSL,257,DUPLICATE_EXTENSION
SSL,264,DUPLICATE_KEY_SHARE
SSL,283,EARLY_DATA_NOT_IN_USE
SSL,144,ECC_CERT_NOT_FOR_SIGNING
SSL,282,EMPTY_HELLO_RETRY_REQUEST
SSL,145,EMS_STATE_INCONSISTENT
Expand Down
8 changes: 8 additions & 0 deletions include/openssl/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2988,6 +2988,13 @@ OPENSSL_EXPORT int SSL_early_data_accepted(const SSL *ssl);
// |SSL_ERROR_EARLY_DATA_REJECTED|.
OPENSSL_EXPORT void SSL_reset_early_data_reject(SSL *ssl);

// SSL_export_early_keying_material behaves like |SSL_export_keying_material|,
// but it uses the early exporter. The operation will fail if |ssl| did not
// negotiate TLS 1.3 or 0-RTT.
OPENSSL_EXPORT int SSL_export_early_keying_material(
SSL *ssl, uint8_t *out, size_t out_len, const char *label, size_t label_len,
const uint8_t *context, size_t context_len);


// Alerts.
//
Expand Down Expand Up @@ -4561,6 +4568,7 @@ OPENSSL_EXPORT bool SealRecord(SSL *ssl, Span<uint8_t> out_prefix,
#define SSL_R_NO_SUPPORTED_VERSIONS_ENABLED 280
#define SSL_R_APPLICATION_DATA_INSTEAD_OF_HANDSHAKE 281
#define SSL_R_EMPTY_HELLO_RETRY_REQUEST 282
#define SSL_R_EARLY_DATA_NOT_IN_USE 283
#define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
#define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
#define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
Expand Down
8 changes: 4 additions & 4 deletions ssl/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1244,10 +1244,10 @@ int tls13_derive_resumption_secret(SSL_HANDSHAKE *hs);

// tls13_export_keying_material provides an exporter interface to use the
// |exporter_secret|.
int tls13_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
const char *label, size_t label_len,
const uint8_t *context, size_t context_len,
int use_context);
int tls13_export_keying_material(SSL *ssl, Span<uint8_t> out,
Span<const uint8_t> secret,
Span<const char> label,
Span<const uint8_t> context);

// tls13_finished_mac calculates the MAC of the handshake transcript to verify
// the integrity of the Finished message, and stores the result in |out| and
Expand Down
34 changes: 32 additions & 2 deletions ssl/t1_enc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -467,8 +467,14 @@ int SSL_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
}

if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
return tls13_export_keying_material(ssl, out, out_len, label, label_len,
context, context_len, use_context);
if (!use_context) {
context = nullptr;
context_len = 0;
}
return tls13_export_keying_material(
ssl, MakeSpan(out, out_len),
MakeConstSpan(ssl->s3->exporter_secret, ssl->s3->exporter_secret_len),
MakeConstSpan(label, label_len), MakeConstSpan(context, context_len));
}

size_t seed_len = 2 * SSL3_RANDOM_SIZE;
Expand Down Expand Up @@ -501,3 +507,27 @@ int SSL_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
MakeConstSpan(session->master_key, session->master_key_length),
MakeConstSpan(label, label_len), seed, {});
}

int SSL_export_early_keying_material(
SSL *ssl, uint8_t *out, size_t out_len, const char *label, size_t label_len,
const uint8_t *context, size_t context_len) {
if (!SSL_in_early_data(ssl) &&
(!ssl->s3->have_version ||
ssl_protocol_version(ssl) < TLS1_3_VERSION)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION);
return 0;
}

// The early exporter only exists if we accepted early data or offered it as
// a client.
if (!SSL_in_early_data(ssl) && !SSL_early_data_accepted(ssl)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_EARLY_DATA_NOT_IN_USE);
return 0;
}

return tls13_export_keying_material(
ssl, MakeSpan(out, out_len),
MakeConstSpan(ssl->s3->early_exporter_secret,
ssl->s3->early_exporter_secret_len),
MakeConstSpan(label, label_len), MakeConstSpan(context, context_len));
}
16 changes: 16 additions & 0 deletions ssl/test/bssl_shim.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2205,6 +2205,22 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, SSL *ssl,
GetTestState(ssl)->got_new_session = false;
}

if (config->export_early_keying_material > 0) {
std::vector<uint8_t> result(
static_cast<size_t>(config->export_early_keying_material));
if (!SSL_export_early_keying_material(
ssl, result.data(), result.size(), config->export_label.data(),
config->export_label.size(),
reinterpret_cast<const uint8_t *>(config->export_context.data()),
config->export_context.size())) {
fprintf(stderr, "failed to export keying material\n");
return false;
}
if (WriteAll(ssl, result.data(), result.size()) < 0) {
return false;
}
}

if (config->export_keying_material > 0) {
std::vector<uint8_t> result(
static_cast<size_t>(config->export_keying_material));
Expand Down
15 changes: 15 additions & 0 deletions ssl/test/runner/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -1289,6 +1289,21 @@ type ProtocolBugs struct {
// it was accepted.
SendEarlyDataExtension bool

// ExpectEarlyKeyingMaterial, if non-zero, causes a TLS 1.3 server to
// read an application data record after the ClientHello before it sends
// a ServerHello. The record's contents have the specified length and
// match the corresponding early exporter value. This is used to test
// the client using the early exporter in the 0-RTT state.
ExpectEarlyKeyingMaterial int

// ExpectEarlyKeyingLabel is the label to use with
// ExpectEarlyKeyingMaterial.
ExpectEarlyKeyingLabel string

// ExpectEarlyKeyingContext is the context string to use with
// ExpectEarlyKeyingMaterial
ExpectEarlyKeyingContext string

// ExpectEarlyData causes a TLS 1.3 server to read application
// data after the ClientHello (assuming the server is able to
// derive the key under which the data is encrypted) before it
Expand Down
42 changes: 32 additions & 10 deletions ssl/test/runner/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type Conn struct {
didResume bool // whether this connection was a session resumption
extendedMasterSecret bool // whether this session used an extended master secret
cipherSuite *cipherSuite
earlyCipherSuite *cipherSuite
ocspResponse []byte // stapled OCSP response
sctList []byte // signed certificate timestamp list
peerCertificates []*x509.Certificate
Expand All @@ -63,6 +64,7 @@ type Conn struct {
curveID CurveID

clientRandom, serverRandom [32]byte
earlyExporterSecret []byte
exporterSecret []byte
resumptionSecret []byte

Expand Down Expand Up @@ -1847,6 +1849,23 @@ func (c *Conn) VerifyHostname(host string) error {
return c.peerCertificates[0].VerifyHostname(host)
}

func (c *Conn) exportKeyingMaterialTLS13(length int, secret, label, context []byte) []byte {
cipherSuite := c.cipherSuite
if cipherSuite == nil {
cipherSuite = c.earlyCipherSuite
}
if isDraft21(c.wireVersion) {
hash := cipherSuite.hash()
exporterKeyingLabel := []byte("exporter")
contextHash := hash.New()
contextHash.Write(context)
exporterContext := hash.New().Sum(nil)
derivedSecret := hkdfExpandLabel(cipherSuite.hash(), c.wireVersion, secret, label, exporterContext, hash.Size())
return hkdfExpandLabel(cipherSuite.hash(), c.wireVersion, derivedSecret, exporterKeyingLabel, contextHash.Sum(nil), length)
}
return hkdfExpandLabel(cipherSuite.hash(), c.wireVersion, secret, label, context, length)
}

// ExportKeyingMaterial exports keying material from the current connection
// state, as per RFC 5705.
func (c *Conn) ExportKeyingMaterial(length int, label, context []byte, useContext bool) ([]byte, error) {
Expand All @@ -1857,16 +1876,7 @@ func (c *Conn) ExportKeyingMaterial(length int, label, context []byte, useContex
}

if c.vers >= VersionTLS13 {
if isDraft21(c.wireVersion) {
hash := c.cipherSuite.hash()
exporterKeyingLabel := []byte("exporter")
contextHash := hash.New()
contextHash.Write(context)
exporterContext := hash.New().Sum(nil)
derivedSecret := hkdfExpandLabel(c.cipherSuite.hash(), c.wireVersion, c.exporterSecret, label, exporterContext, hash.Size())
return hkdfExpandLabel(c.cipherSuite.hash(), c.wireVersion, derivedSecret, exporterKeyingLabel, contextHash.Sum(nil), length), nil
}
return hkdfExpandLabel(c.cipherSuite.hash(), c.wireVersion, c.exporterSecret, label, context, length), nil
return c.exportKeyingMaterialTLS13(length, c.exporterSecret, label, context), nil
}

seedLen := len(c.clientRandom) + len(c.serverRandom)
Expand All @@ -1885,6 +1895,18 @@ func (c *Conn) ExportKeyingMaterial(length int, label, context []byte, useContex
return result, nil
}

func (c *Conn) ExportEarlyKeyingMaterial(length int, label, context []byte) ([]byte, error) {
if c.vers < VersionTLS13 {
return nil, errors.New("tls: early exporters not defined before TLS 1.3")
}

if c.earlyExporterSecret == nil {
return nil, errors.New("tls: no early exporter secret")
}

return c.exportKeyingMaterialTLS13(length, c.earlyExporterSecret, label, context), nil
}

// noRenegotiationInfo returns true if the renegotiation info extension
// should be supported in the current handshake.
func (c *Conn) noRenegotiationInfo() bool {
Expand Down
15 changes: 9 additions & 6 deletions ssl/test/runner/handshake_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,18 +414,21 @@ NextCipherSuite:
finishedHash.addEntropy(session.masterSecret)
finishedHash.Write(helloBytes)

earlyLabel := earlyTrafficLabel
if isDraft21(session.wireVersion) {
earlyLabel = earlyTrafficLabelDraft21
}

if !c.config.Bugs.SkipChangeCipherSpec && isDraft22(session.wireVersion) {
c.wireVersion = session.wireVersion
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
c.wireVersion = 0
}

earlyTrafficSecret := finishedHash.deriveSecret(earlyLabel)
var earlyTrafficSecret []byte
if isDraft21(session.wireVersion) {
earlyTrafficSecret = finishedHash.deriveSecret(earlyTrafficLabelDraft21)
c.earlyExporterSecret = finishedHash.deriveSecret(earlyExporterLabelDraft21)
} else {
earlyTrafficSecret = finishedHash.deriveSecret(earlyTrafficLabel)
c.earlyExporterSecret = finishedHash.deriveSecret(earlyExporterLabel)
}

c.useOutTrafficSecret(session.wireVersion, pskCipherSuite, earlyTrafficSecret)
for _, earlyData := range c.config.Bugs.SendEarlyData {
if _, err := c.writeRecord(recordTypeApplicationData, earlyData); err != nil {
Expand Down
26 changes: 20 additions & 6 deletions ssl/test/runner/handshake_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -694,22 +694,36 @@ ResendHelloRetryRequest:
}
}
if encryptedExtensions.extensions.hasEarlyData {
earlyLabel := earlyTrafficLabel
var earlyTrafficSecret []byte
if isDraft21(c.wireVersion) {
earlyLabel = earlyTrafficLabelDraft21
earlyTrafficSecret = hs.finishedHash.deriveSecret(earlyTrafficLabelDraft21)
c.earlyExporterSecret = hs.finishedHash.deriveSecret(earlyExporterLabelDraft21)
} else {
earlyTrafficSecret = hs.finishedHash.deriveSecret(earlyTrafficLabel)
c.earlyExporterSecret = hs.finishedHash.deriveSecret(earlyExporterLabel)
}

earlyTrafficSecret := hs.finishedHash.deriveSecret(earlyLabel)
if err := c.useInTrafficSecret(c.wireVersion, hs.suite, earlyTrafficSecret); err != nil {
return err
}

for _, expectedMsg := range config.Bugs.ExpectEarlyData {
c.earlyCipherSuite = hs.suite
expectEarlyData := config.Bugs.ExpectEarlyData
if n := config.Bugs.ExpectEarlyKeyingMaterial; n > 0 {
exporter, err := c.ExportEarlyKeyingMaterial(n, []byte(config.Bugs.ExpectEarlyKeyingLabel), []byte(config.Bugs.ExpectEarlyKeyingContext))
if err != nil {
return err
}
expectEarlyData = append([][]byte{exporter}, expectEarlyData...)
}

for _, expectedMsg := range expectEarlyData {
if err := c.readRecord(recordTypeApplicationData); err != nil {
return err
}
if !bytes.Equal(c.input.data[c.input.off:], expectedMsg) {
return errors.New("ExpectEarlyData: did not get expected message")
msg := c.input.data[c.input.off:]
if !bytes.Equal(msg, expectedMsg) {
return fmt.Errorf("tls: got early data record %x, wanted %x", msg, expectedMsg)
}
c.in.freeBlock(c.input)
c.input = nil
Expand Down
2 changes: 2 additions & 0 deletions ssl/test/runner/prf.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ var (
clientApplicationTrafficLabel = []byte("client application traffic secret")
serverApplicationTrafficLabel = []byte("server application traffic secret")
applicationTrafficLabel = []byte("application traffic secret")
earlyExporterLabel = []byte("early exporter master secret")
exporterLabel = []byte("exporter master secret")
resumptionLabel = []byte("resumption master secret")

Expand All @@ -457,6 +458,7 @@ var (
clientApplicationTrafficLabelDraft21 = []byte("c ap traffic")
serverApplicationTrafficLabelDraft21 = []byte("s ap traffic")
applicationTrafficLabelDraft21 = []byte("traffic upd")
earlyExporterLabelDraft21 = []byte("e exp master")
exporterLabelDraft21 = []byte("exp master")
resumptionLabelDraft21 = []byte("res master")

Expand Down
Loading

0 comments on commit 650d8c3

Please sign in to comment.