From 0e7b54069be9519e8b4834068dca3df7bb30650a Mon Sep 17 00:00:00 2001 From: Christopher Patton Date: Wed, 23 Sep 2020 17:10:26 -0700 Subject: [PATCH] crypto/tls: implement draft-ietf-tls-esni-13 Adds support for draft 13 of the Encrypted ClientHello (ECH) extension for TLS. This requires CIRCL to implement draft 08 or later of the HPKE specification (draft-irtf-cfrg-hpke-08). Adds a CFEvent for reporting when ECH is offered or greased by the client, when ECH is accepted or rejected by the server, and when the outer SNI doesn't match the public name of the ECH config. Missing ECH features: * Record-level padding. * Proper validation of the public name by the client. * Retry after rejection. * PSKs are disabled when ECH is accepted. --- src/crypto/tls/alert.go | 2 + src/crypto/tls/cfevent.go | 65 + src/crypto/tls/common.go | 83 +- src/crypto/tls/conn.go | 47 + src/crypto/tls/ech.go | 1094 +++++++++++++++++ src/crypto/tls/ech_config.go | 165 +++ src/crypto/tls/ech_provider.go | 303 +++++ src/crypto/tls/ech_test.go | 952 ++++++++++++++ src/crypto/tls/handshake_client.go | 37 +- src/crypto/tls/handshake_client_tls13.go | 236 +++- src/crypto/tls/handshake_messages.go | 42 + src/crypto/tls/handshake_server.go | 13 +- src/crypto/tls/handshake_server_tls13.go | 119 +- src/crypto/tls/hpke.go | 42 + src/crypto/tls/tls.go | 27 + src/crypto/tls/tls_cf_test.go | 54 + src/crypto/tls/tls_test.go | 6 +- .../cloudflare/circl/ecc/p384/LICENSE | 26 + .../cloudflare/circl/ecc/p384/arith.go | 149 +++ .../cloudflare/circl/ecc/p384/arith_amd64.go | 8 + .../cloudflare/circl/ecc/p384/arith_amd64.s | 730 +++++++++++ .../cloudflare/circl/ecc/p384/arith_arm64.s | 511 ++++++++ .../cloudflare/circl/ecc/p384/doc.go | 10 + .../cloudflare/circl/ecc/p384/p384.go | 28 + .../cloudflare/circl/ecc/p384/p384_generic.go | 21 + .../cloudflare/circl/ecc/p384/p384opt.go | 181 +++ .../cloudflare/circl/ecc/p384/point.go | 358 ++++++ .../cloudflare/circl/ecc/p384/tableBase.go | 331 +++++ .../github.com/cloudflare/circl/hpke/aead.go | 103 ++ .../github.com/cloudflare/circl/hpke/algs.go | 278 +++++ .../github.com/cloudflare/circl/hpke/hpke.go | 271 ++++ .../cloudflare/circl/hpke/hybridkem.go | 232 ++++ .../cloudflare/circl/hpke/kembase.go | 241 ++++ .../cloudflare/circl/hpke/marshal.go | 151 +++ .../cloudflare/circl/hpke/shortkem.go | 170 +++ .../github.com/cloudflare/circl/hpke/util.go | 121 ++ .../github.com/cloudflare/circl/hpke/xkem.go | 164 +++ src/vendor/modules.txt | 2 + 38 files changed, 7336 insertions(+), 37 deletions(-) create mode 100644 src/crypto/tls/ech.go create mode 100644 src/crypto/tls/ech_config.go create mode 100644 src/crypto/tls/ech_provider.go create mode 100644 src/crypto/tls/ech_test.go create mode 100644 src/crypto/tls/hpke.go create mode 100644 src/crypto/tls/tls_cf_test.go create mode 100644 src/vendor/github.com/cloudflare/circl/ecc/p384/LICENSE create mode 100644 src/vendor/github.com/cloudflare/circl/ecc/p384/arith.go create mode 100644 src/vendor/github.com/cloudflare/circl/ecc/p384/arith_amd64.go create mode 100644 src/vendor/github.com/cloudflare/circl/ecc/p384/arith_amd64.s create mode 100644 src/vendor/github.com/cloudflare/circl/ecc/p384/arith_arm64.s create mode 100644 src/vendor/github.com/cloudflare/circl/ecc/p384/doc.go create mode 100644 src/vendor/github.com/cloudflare/circl/ecc/p384/p384.go create mode 100644 src/vendor/github.com/cloudflare/circl/ecc/p384/p384_generic.go create mode 100644 src/vendor/github.com/cloudflare/circl/ecc/p384/p384opt.go create mode 100644 src/vendor/github.com/cloudflare/circl/ecc/p384/point.go create mode 100644 src/vendor/github.com/cloudflare/circl/ecc/p384/tableBase.go create mode 100644 src/vendor/github.com/cloudflare/circl/hpke/aead.go create mode 100644 src/vendor/github.com/cloudflare/circl/hpke/algs.go create mode 100644 src/vendor/github.com/cloudflare/circl/hpke/hpke.go create mode 100644 src/vendor/github.com/cloudflare/circl/hpke/hybridkem.go create mode 100644 src/vendor/github.com/cloudflare/circl/hpke/kembase.go create mode 100644 src/vendor/github.com/cloudflare/circl/hpke/marshal.go create mode 100644 src/vendor/github.com/cloudflare/circl/hpke/shortkem.go create mode 100644 src/vendor/github.com/cloudflare/circl/hpke/util.go create mode 100644 src/vendor/github.com/cloudflare/circl/hpke/xkem.go diff --git a/src/crypto/tls/alert.go b/src/crypto/tls/alert.go index 33022cd2b4b..aba46055368 100644 --- a/src/crypto/tls/alert.go +++ b/src/crypto/tls/alert.go @@ -58,6 +58,7 @@ const ( alertUnknownPSKIdentity alert = 115 alertCertificateRequired alert = 116 alertNoApplicationProtocol alert = 120 + alertECHRequired alert = 121 ) var alertText = map[alert]string{ @@ -94,6 +95,7 @@ var alertText = map[alert]string{ alertUnknownPSKIdentity: "unknown PSK identity", alertCertificateRequired: "certificate required", alertNoApplicationProtocol: "no application protocol", + alertECHRequired: "ECH required", } func (e alert) String() string { diff --git a/src/crypto/tls/cfevent.go b/src/crypto/tls/cfevent.go index a2be30f516c..bc092b811d6 100644 --- a/src/crypto/tls/cfevent.go +++ b/src/crypto/tls/cfevent.go @@ -97,6 +97,71 @@ func createTLS13ServerHandshakeTimingInfo(timerFunc func() time.Time) CFEventTLS } } +const ( + // Constants for ECH status events. + echStatusBypassed = 1 + iota + echStatusInner + echStatusOuter +) + +// CFEventECHClientStatus is emitted once it is known whether the client +// bypassed, offered, or greased ECH. +type CFEventECHClientStatus int + +// Bypassed returns true if the client bypassed ECH. +func (e CFEventECHClientStatus) Bypassed() bool { + return e == echStatusBypassed +} + +// Offered returns true if the client offered ECH. +func (e CFEventECHClientStatus) Offered() bool { + return e == echStatusInner +} + +// Greased returns true if the client greased ECH. +func (e CFEventECHClientStatus) Greased() bool { + return e == echStatusOuter +} + +// Name is required by the CFEvent interface. +func (e CFEventECHClientStatus) Name() string { + return "ech client status" +} + +// CFEventECHServerStatus is emitted once it is known whether the client +// bypassed, offered, or greased ECH. +type CFEventECHServerStatus int + +// Bypassed returns true if the client bypassed ECH. +func (e CFEventECHServerStatus) Bypassed() bool { + return e == echStatusBypassed +} + +// Accepted returns true if the client offered ECH. +func (e CFEventECHServerStatus) Accepted() bool { + return e == echStatusInner +} + +// Rejected returns true if the client greased ECH. +func (e CFEventECHServerStatus) Rejected() bool { + return e == echStatusOuter +} + +// Name is required by the CFEvent interface. +func (e CFEventECHServerStatus) Name() string { + return "ech server status" +} + +// CFEventECHPublicNameMismatch is emitted if the outer SNI does not match +// match the public name of the ECH configuration. Note that we do not record +// the outer SNI in order to avoid collecting this potentially sensitive data. +type CFEventECHPublicNameMismatch struct{} + +// Name is required by the CFEvent interface. +func (e CFEventECHPublicNameMismatch) Name() string { + return "ech public name does not match outer sni" +} + // For backwards compatibility. type CFEventTLS13NegotiatedKEX = CFEventTLSNegotiatedNamedKEX diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go index a22543b7c03..4620731b1be 100644 --- a/src/crypto/tls/common.go +++ b/src/crypto/tls/common.go @@ -124,6 +124,8 @@ const ( extensionKeyShare uint16 = 51 extensionQUICTransportParameters uint16 = 57 extensionRenegotiationInfo uint16 = 0xff01 + extensionECH uint16 = 0xfe0d // draft-ietf-tls-esni-13 + extensionECHOuterExtensions uint16 = 0xfd00 // draft-ietf-tls-esni-13 ) // TLS signaling cipher suite values @@ -247,6 +249,45 @@ const ( // include downgrade canaries even if it's using its highers supported version. var testingOnlyForceDowngradeCanary bool +// testingTriggerHRR causes the server to intentionally trigger a +// HelloRetryRequest (HRR). This is useful for testing new TLS features that +// change the HRR codepath. +var testingTriggerHRR bool + +// testingECHTriggerBypassAfterHRR causes the client to bypass ECH after HRR. +// If available, the client will offer ECH in the first CH only. +var testingECHTriggerBypassAfterHRR bool + +// testingECHTriggerBypassBeforeHRR causes the client to bypass ECH before HRR. +// The client will offer ECH in the second CH only. +var testingECHTriggerBypassBeforeHRR bool + +// testingECHIllegalHandleAfterHRR causes the client to illegally change the ECH +// extension after HRR. +var testingECHIllegalHandleAfterHRR bool + +// testingECHTriggerPayloadDecryptError causes the client to to send an +// inauthentic payload. +var testingECHTriggerPayloadDecryptError bool + +// testingECHOuterExtMany causes a client to incorporate a sequence of +// outer extensions into the ClientHelloInner when it offers the ECH extension. +// The "key_share" extension is the only incorporated extension by default. +var testingECHOuterExtMany bool + +// testingECHOuterExtNone causes a client to not use the "outer_extension" +// mechanism for ECH. The "key_shares" extension is incorporated by default. +var testingECHOuterExtNone bool + +// testingECHOuterExtIncorrectOrder causes the client to send the +// "outer_extension" extension in the wrong order when offering the ECH +// extension. +var testingECHOuterExtIncorrectOrder bool + +// testingECHOuterExtIllegal causes the client to send in its +// "outer_extension" extension the codepoint for the ECH extension. +var testingECHOuterExtIllegal bool + // ConnectionState records basic TLS details about the connection. type ConnectionState struct { // Version is the TLS version used by the connection (e.g. VersionTLS12). @@ -315,6 +356,14 @@ type ConnectionState struct { // resumed connections that don't support Extended Master Secret (RFC 7627). TLSUnique []byte + // ECHAccepted is set if the ECH extension was offered by the client and + // accepted by the server. + ECHAccepted bool + + // ECHOffered is set if the ECH extension is present in the ClientHello. + // This means the client has offered ECH or sent GREASE ECH. + ECHOffered bool + // ekm is a closure exposed via ExportKeyingMaterial. ekm func(label string, context []byte, length int) ([]byte, error) } @@ -729,7 +778,8 @@ type Config struct { // SessionTicketsDisabled may be set to true to disable session ticket and // PSK (resumption) support. Note that on clients, session ticket support is - // also disabled if ClientSessionCache is nil. + // also disabled if ClientSessionCache is nil. On clients or servers, + // support is disabled if the ECH extension is enabled. SessionTicketsDisabled bool // SessionTicketKey is used by TLS servers to provide session resumption. @@ -819,6 +869,23 @@ type Config struct { // used for debugging. KeyLogWriter io.Writer + // ECHEnabled determines whether the ECH extension is enabled for this + // connection. + ECHEnabled bool + + // ClientECHConfigs are the parameters used by the client when it offers the + // ECH extension. If ECH is enabled, a suitable configuration is found, and + // the client supports TLS 1.3, then it will offer ECH in this handshake. + // Otherwise, if ECH is enabled, it will send a dummy ECH extension. + ClientECHConfigs []ECHConfig + + // ServerECHProvider is the ECH provider used by the client-facing server + // for the ECH extension. If the client offers ECH and TLS 1.3 is + // negotiated, then the provider is used to compute the HPKE context + // (draft-irtf-cfrg-hpke-07), which in turn is used to decrypt the extension + // payload. + ServerECHProvider ECHProvider + // SupportDelegatedCredential is true if the client or server is willing // to negotiate the delegated credential extension. // This can only be used with TLS 1.3. @@ -915,6 +982,9 @@ func (c *Config) Clone() *Config { Renegotiation: c.Renegotiation, KeyLogWriter: c.KeyLogWriter, SupportDelegatedCredential: c.SupportDelegatedCredential, + ECHEnabled: c.ECHEnabled, + ClientECHConfigs: c.ClientECHConfigs, + ServerECHProvider: c.ServerECHProvider, sessionTicketKeys: c.sessionTicketKeys, autoSessionTicketKeys: c.autoSessionTicketKeys, } @@ -1113,6 +1183,17 @@ func (c *Config) supportedVersions(isClient bool) []uint16 { return versions } +func (c *Config) supportedVersionsFromMin(isClient bool, minVersion uint16) []uint16 { + versions := c.supportedVersions(isClient) + filteredVersions := versions[:0] + for _, v := range versions { + if v >= minVersion { + filteredVersions = append(filteredVersions, v) + } + } + return filteredVersions +} + func (c *Config) maxSupportedVersion(isClient bool) uint16 { supportedVersions := c.supportedVersions(isClient) if len(supportedVersions) == 0 { diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go index e14b3f8a3c2..5b04b0e2e2e 100644 --- a/src/crypto/tls/conn.go +++ b/src/crypto/tls/conn.go @@ -21,6 +21,8 @@ import ( "sync" "sync/atomic" "time" + + "github.com/cloudflare/circl/hpke" ) // A Conn represents a secured connection. @@ -127,6 +129,20 @@ type Conn struct { // cfEventHandler is called at several points during the handshake if // set. See also CFEventHandlerContextKey. cfEventHandler func(event CFEvent) + + // State used for the ECH extension. + ech struct { + sealer hpke.Sealer // The client's HPKE context + opener hpke.Opener // The server's HPKE context + + // The state shared by the client and server. + offered bool // Client offered ECH + greased bool // Client greased ECH + accepted bool // Server accepted ECH + retryConfigs []byte // The retry configurations + configId uint8 // The ECH config id + maxNameLen int // maximum_name_len indicated by the ECH config + } } // Access to net.Conn methods. @@ -728,6 +744,12 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error { return c.in.setErrorLocked(io.EOF) } if c.vers == VersionTLS13 { + if !c.isClient && c.ech.greased && alert(data[1]) == alertECHRequired { + // This condition indicates that the client intended to offer + // ECH, but did not use a known ECH config. + c.ech.offered = true + c.ech.greased = false + } return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])}) } switch data[0] { @@ -1436,6 +1458,29 @@ func (c *Conn) Close() error { if err := c.conn.Close(); err != nil { return err } + + // Resolve ECH status. + if !c.isClient && c.config.MaxVersion < VersionTLS13 { + c.handleCFEvent(CFEventECHServerStatus(echStatusBypassed)) + } else if !c.ech.offered { + if !c.ech.greased { + c.handleCFEvent(CFEventECHClientStatus(echStatusBypassed)) + } else { + c.handleCFEvent(CFEventECHClientStatus(echStatusOuter)) + } + } else { + c.handleCFEvent(CFEventECHClientStatus(echStatusInner)) + if !c.ech.accepted { + if len(c.ech.retryConfigs) > 0 { + c.handleCFEvent(CFEventECHServerStatus(echStatusOuter)) + } else { + c.handleCFEvent(CFEventECHServerStatus(echStatusBypassed)) + } + } else { + c.handleCFEvent(CFEventECHServerStatus(echStatusInner)) + } + } + return alertErr } @@ -1629,6 +1674,8 @@ func (c *Conn) connectionStateLocked() ConnectionState { } state.SignedCertificateTimestamps = c.scts state.OCSPResponse = c.ocspResponse + state.ECHAccepted = c.ech.accepted + state.ECHOffered = c.ech.offered || c.ech.greased if (!c.didResume || c.extMasterSecret) && c.vers != VersionTLS13 { if c.clientFinishedIsFirst { state.TLSUnique = c.clientFinished[:] diff --git a/src/crypto/tls/ech.go b/src/crypto/tls/ech.go new file mode 100644 index 00000000000..8e71a3a8b80 --- /dev/null +++ b/src/crypto/tls/ech.go @@ -0,0 +1,1094 @@ +// Copyright 2020 Cloudflare, Inc. All rights reserved. Use of this source code +// is governed by a BSD-style license that can be found in the LICENSE file. + +package tls + +import ( + "errors" + "fmt" + "io" + + "github.com/cloudflare/circl/hpke" + + "golang.org/x/crypto/cryptobyte" +) + +const ( + // Constants for TLS operations + echAcceptConfLabel = "ech accept confirmation" + echAcceptConfHRRLabel = "hrr ech accept confirmation" + + // Constants for HPKE operations + echHpkeInfoSetup = "tls ech" + + // When sent in the ClientHello, the first byte of the payload of the ECH + // extension indicates whether the message is the ClientHelloOuter or + // ClientHelloInner. + echClientHelloOuterVariant uint8 = 0 + echClientHelloInnerVariant uint8 = 1 +) + +var ( + zeros = [8]byte{} +) + +// echOfferOrGrease is called by the client after generating its ClientHello +// message to decide if it will offer or GREASE ECH. It does neither if ECH is +// disabled. Returns a pair of ClientHello messages, hello and helloInner. If +// offering ECH, these are the ClienthelloOuter and ClientHelloInner +// respectively. Otherwise, hello is the ClientHello and helloInner == nil. +// +// TODO(cjpatton): "[When offering ECH, the client] MUST NOT offer to resume any +// session for TLS 1.2 and below [in ClientHelloInner]." +func (c *Conn) echOfferOrGrease(helloBase *clientHelloMsg) (hello, helloInner *clientHelloMsg, err error) { + config := c.config + + if !config.ECHEnabled || testingECHTriggerBypassBeforeHRR { + // Bypass ECH. + return helloBase, nil, nil + } + + // Choose the ECHConfig to use for this connection. If none is available, or + // if we're not offering TLS 1.3 or above, then GREASE. + echConfig := config.echSelectConfig() + if echConfig == nil || config.maxSupportedVersion(roleClient) < VersionTLS13 { + var err error + + // Generate a dummy ClientECH. + helloBase.ech, err = echGenerateGreaseExt(config.rand()) + if err != nil { + return nil, nil, fmt.Errorf("tls: ech: failed to generate grease ECH: %s", err) + } + + // GREASE ECH. + c.ech.offered = false + c.ech.greased = true + helloBase.raw = nil + return helloBase, nil, nil + } + + // Store the ECH config parameters that are needed later. + c.ech.configId = echConfig.configId + c.ech.maxNameLen = int(echConfig.maxNameLen) + + // Generate the HPKE context. Store it in case of HRR. + var enc []byte + enc, c.ech.sealer, err = echConfig.setupSealer(config.rand()) + if err != nil { + return nil, nil, fmt.Errorf("tls: ech: %s", err) + } + + // ClientHelloInner is constructed from the base ClientHello. The payload of + // the "encrypted_client_hello" extension is a single 1 byte indicating that + // this is the ClientHelloInner. + helloInner = helloBase + helloInner.ech = []byte{echClientHelloInnerVariant} + + // Ensure that only TLS 1.3 and above are offered in the inner handshake. + if v := helloInner.supportedVersions; len(v) == 0 || v[len(v)-1] < VersionTLS13 { + return nil, nil, errors.New("tls: ech: only TLS 1.3 is allowed in ClientHelloInner") + } + + // ClientHelloOuter is constructed by generating a fresh ClientHello and + // copying "session_id" from ClientHelloInner, setting "server_name" to the + // client-facing server, and adding the "encrypted_client_hello" extension. + // + // In addition, we discard the "key_share" and instead use the one from + // ClientHelloInner. + hello, _, err = c.makeClientHello(config.MinVersion) + if err != nil { + return nil, nil, fmt.Errorf("tls: ech: %s", err) + } + hello.sessionId = helloBase.sessionId + hello.serverName = hostnameInSNI(string(echConfig.rawPublicName)) + if err := c.echUpdateClientHelloOuter(hello, helloInner, enc); err != nil { + return nil, nil, err + } + + // Offer ECH. + c.ech.offered = true + helloInner.raw = nil + hello.raw = nil + return hello, helloInner, nil +} + +// echUpdateClientHelloOuter is called by the client to construct the payload of +// the ECH extension in the outer handshake. +func (c *Conn) echUpdateClientHelloOuter(hello, helloInner *clientHelloMsg, enc []byte) error { + var ( + ech echClientOuter + err error + ) + + // Copy all compressed extensions from ClientHelloInner into + // ClientHelloOuter. + for _, ext := range echOuterExtensions() { + echCopyExtensionFromClientHelloInner(hello, helloInner, ext) + } + + // Always copy the "key_shares" extension from ClientHelloInner, regardless + // of whether it gets compressed. + hello.keyShares = helloInner.keyShares + + _, kdf, aead := c.ech.sealer.Suite().Params() + ech.handle.suite.kdfId = uint16(kdf) + ech.handle.suite.aeadId = uint16(aead) + ech.handle.configId = c.ech.configId + ech.handle.enc = enc + + // EncodedClientHelloInner + helloInner.raw = nil + helloInnerMarshalled, err := helloInner.marshal() + if err != nil { + return fmt.Errorf("tls: ech: failed to marshal helloInner: %w", err) + } + encodedHelloInner := echEncodeClientHelloInner( + helloInnerMarshalled, + len(helloInner.serverName), + c.ech.maxNameLen) + if encodedHelloInner == nil { + return errors.New("tls: ech: encoding of EncodedClientHelloInner failed") + } + + // ClientHelloOuterAAD + hello.raw = nil + hello.ech = ech.marshal() + helloMarshalled, err := hello.marshal() + if err != nil { + return fmt.Errorf("tls: ech: failed to marshal hello: %w", err) + } + helloOuterAad := echEncodeClientHelloOuterAAD(helloMarshalled, + aead.CipherLen(uint(len(encodedHelloInner)))) + if helloOuterAad == nil { + return errors.New("tls: ech: encoding of ClientHelloOuterAAD failed") + } + + ech.payload, err = c.ech.sealer.Seal(encodedHelloInner, helloOuterAad) + if err != nil { + return fmt.Errorf("tls: ech: seal failed: %s", err) + } + if testingECHTriggerPayloadDecryptError { + ech.payload[0] ^= 0xff // Inauthentic ciphertext + } + ech.raw = nil + hello.ech = ech.marshal() + + helloInner.raw = nil + hello.raw = nil + return nil +} + +// echAcceptOrReject is called by the client-facing server to determine whether +// ECH was offered by the client, and if so, whether to accept or reject. The +// return value is the ClientHello that will be used for the connection. +// +// This function is called prior to processing the ClientHello. In case of +// HelloRetryRequest, it is also called before processing the second +// ClientHello. This is indicated by the afterHRR flag. +func (c *Conn) echAcceptOrReject(hello *clientHelloMsg, afterHRR bool) (*clientHelloMsg, error) { + config := c.config + p := config.ServerECHProvider + + if !config.echCanAccept() { + // Bypass ECH. + return hello, nil + } + + if len(hello.ech) > 0 { // The ECH extension is present + switch hello.ech[0] { + case echClientHelloInnerVariant: // inner handshake + if len(hello.ech) > 1 { + c.sendAlert(alertIllegalParameter) + return nil, errors.New("ech: inner handshake has non-empty payload") + } + + // Continue as the backend server. + return hello, nil + case echClientHelloOuterVariant: // outer handshake + default: + c.sendAlert(alertIllegalParameter) + return nil, errors.New("ech: inner handshake has non-empty payload") + } + } else { + if c.ech.offered { + // This occurs if the server accepted prior to HRR, but the client + // failed to send the ECH extension in the second ClientHelloOuter. This + // would cause ClientHelloOuter to be used after ClientHelloInner, which + // is illegal. + c.sendAlert(alertMissingExtension) + return nil, errors.New("ech: hrr: bypass after offer") + } + + // Bypass ECH. + return hello, nil + } + + if afterHRR && !c.ech.offered && !c.ech.greased { + // The client bypassed ECH prior to HRR, but not after. This could + // cause ClientHelloInner to be used after ClientHelloOuter, which is + // illegal. + c.sendAlert(alertIllegalParameter) + return nil, errors.New("ech: hrr: offer or grease after bypass") + } + + // Parse ClientECH. + ech, err := echUnmarshalClientOuter(hello.ech) + if err != nil { + c.sendAlert(alertIllegalParameter) + return nil, fmt.Errorf("ech: failed to parse extension: %s", err) + } + + // Make sure that the HPKE suite and config id don't change across HRR and + // that the encapsulated key is not present after HRR. + if afterHRR && c.ech.offered { + _, kdf, aead := c.ech.opener.Suite().Params() + if ech.handle.suite.kdfId != uint16(kdf) || + ech.handle.suite.aeadId != uint16(aead) || + ech.handle.configId != c.ech.configId || + len(ech.handle.enc) > 0 { + c.sendAlert(alertIllegalParameter) + return nil, errors.New("ech: hrr: illegal handle in second hello") + } + } + + // Store the config id in case of HRR. + c.ech.configId = ech.handle.configId + + // Ask the ECH provider for the HPKE context. + if c.ech.opener == nil { + res := p.GetDecryptionContext(ech.handle.marshal(), extensionECH) + + // Compute retry configurations, skipping those indicating an + // unsupported version. + if len(res.RetryConfigs) > 0 { + configs, err := UnmarshalECHConfigs(res.RetryConfigs) // skips unrecognized versions + if err != nil { + c.sendAlert(alertInternalError) + return nil, fmt.Errorf("ech: %s", err) + } + + if len(configs) > 0 { + c.ech.retryConfigs, err = echMarshalConfigs(configs) + if err != nil { + c.sendAlert(alertInternalError) + return nil, fmt.Errorf("ech: %s", err) + } + } + + // Check if the outer SNI matches the public name of any ECH config + // advertised by the client-facing server. As of + // draft-ietf-tls-esni-10, the client is required to use the ECH + // config's public name as the outer SNI. Although there's no real + // reason for the server to enforce this, it's worth noting it when + // it happens. + pubNameMatches := false + for _, config := range configs { + if hello.serverName == string(config.rawPublicName) { + pubNameMatches = true + } + } + if !pubNameMatches { + c.handleCFEvent(CFEventECHPublicNameMismatch{}) + } + } + + switch res.Status { + case ECHProviderSuccess: + c.ech.opener, err = hpke.UnmarshalOpener(res.Context) + if err != nil { + c.sendAlert(alertInternalError) + return nil, fmt.Errorf("ech: %s", err) + } + case ECHProviderReject: + // Reject ECH. We do not know at this point whether the client + // intended to offer or grease ECH, so we presume grease until the + // client indicates rejection by sending an "ech_required" alert. + c.ech.greased = true + return hello, nil + case ECHProviderAbort: + c.sendAlert(alert(res.Alert)) + return nil, fmt.Errorf("ech: provider aborted: %s", res.Error) + default: + c.sendAlert(alertInternalError) + return nil, errors.New("ech: unexpected provider status") + } + } + + // ClientHelloOuterAAD + helloMarshalled, err := hello.marshal() + if err != nil { + return nil, fmt.Errorf("tls: ech: failed to marshal hello: %w", err) + } + rawHelloOuterAad := echEncodeClientHelloOuterAAD(helloMarshalled, uint(len(ech.payload))) + if rawHelloOuterAad == nil { + // This occurs if the ClientHelloOuter is malformed. This values was + // already parsed into `hello`, so this should not happen. + c.sendAlert(alertInternalError) + return nil, fmt.Errorf("ech: failed to encode ClientHelloOuterAAD") + } + + // EncodedClientHelloInner + rawEncodedHelloInner, err := c.ech.opener.Open(ech.payload, rawHelloOuterAad) + if err != nil { + if afterHRR && c.ech.accepted { + // Don't reject after accept, as this would result in processing the + // ClientHelloOuter after processing the ClientHelloInner. + c.sendAlert(alertDecryptError) + return nil, fmt.Errorf("ech: hrr: reject after accept: %s", err) + } + + // Reject ECH. We do not know at this point whether the client + // intended to offer or grease ECH, so we presume grease until the + // client indicates rejection by sending an "ech_required" alert. + c.ech.greased = true + return hello, nil + } + + // ClientHelloInner + rawHelloInner := echDecodeClientHelloInner(rawEncodedHelloInner, helloMarshalled, hello.sessionId) + if rawHelloInner == nil { + c.sendAlert(alertIllegalParameter) + return nil, fmt.Errorf("ech: failed to decode EncodedClientHelloInner") + } + helloInner := new(clientHelloMsg) + if !helloInner.unmarshal(rawHelloInner) { + c.sendAlert(alertIllegalParameter) + return nil, fmt.Errorf("ech: failed to parse ClientHelloInner") + } + + // Check for a well-formed ECH extension. + if len(helloInner.ech) != 1 || + helloInner.ech[0] != echClientHelloInnerVariant { + c.sendAlert(alertIllegalParameter) + return nil, fmt.Errorf("ech: ClientHelloInner does not have a well-formed ECH extension") + } + + // Check that the client did not offer TLS 1.2 or below in the inner + // handshake. + helloInnerSupportsTLS12OrBelow := len(helloInner.supportedVersions) == 0 + for _, v := range helloInner.supportedVersions { + if v < VersionTLS13 { + helloInnerSupportsTLS12OrBelow = true + } + } + if helloInnerSupportsTLS12OrBelow { + c.sendAlert(alertIllegalParameter) + return nil, errors.New("ech: ClientHelloInner offers TLS 1.2 or below") + } + + // Accept ECH. + c.ech.offered = true + c.ech.accepted = true + return helloInner, nil +} + +// echClientOuter represents a ClientECH structure, the payload of the client's +// "encrypted_client_hello" extension that appears in the outer handshake. +type echClientOuter struct { + raw []byte + + // Parsed from raw + handle echContextHandle + payload []byte +} + +// echUnmarshalClientOuter parses a ClientECH structure. The caller provides the +// ECH version indicated by the client. +func echUnmarshalClientOuter(raw []byte) (*echClientOuter, error) { + s := cryptobyte.String(raw) + ech := new(echClientOuter) + ech.raw = raw + + // Make sure this is the outer handshake. + var variant uint8 + if !s.ReadUint8(&variant) { + return nil, fmt.Errorf("error parsing ClientECH.type") + } + if variant != echClientHelloOuterVariant { + return nil, fmt.Errorf("unexpected ClientECH.type (want outer (0))") + } + + // Parse the context handle. + if !echReadContextHandle(&s, &ech.handle) { + return nil, fmt.Errorf("error parsing context handle") + } + endOfContextHandle := len(raw) - len(s) + ech.handle.raw = raw[1:endOfContextHandle] + + // Parse the payload. + var t cryptobyte.String + if !s.ReadUint16LengthPrefixed(&t) || + !t.ReadBytes(&ech.payload, len(t)) || !s.Empty() { + return nil, fmt.Errorf("error parsing payload") + } + + return ech, nil +} + +func (ech *echClientOuter) marshal() []byte { + if ech.raw != nil { + return ech.raw + } + var b cryptobyte.Builder + b.AddUint8(echClientHelloOuterVariant) + b.AddBytes(ech.handle.marshal()) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(ech.payload) + }) + return b.BytesOrPanic() +} + +// echContextHandle represents the prefix of a ClientECH structure used by +// the server to compute the HPKE context. +type echContextHandle struct { + raw []byte + + // Parsed from raw + suite hpkeSymmetricCipherSuite + configId uint8 + enc []byte +} + +func (handle *echContextHandle) marshal() []byte { + if handle.raw != nil { + return handle.raw + } + var b cryptobyte.Builder + b.AddUint16(handle.suite.kdfId) + b.AddUint16(handle.suite.aeadId) + b.AddUint8(handle.configId) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(handle.enc) + }) + return b.BytesOrPanic() +} + +func echReadContextHandle(s *cryptobyte.String, handle *echContextHandle) bool { + var t cryptobyte.String + if !s.ReadUint16(&handle.suite.kdfId) || // cipher_suite.kdf_id + !s.ReadUint16(&handle.suite.aeadId) || // cipher_suite.aead_id + !s.ReadUint8(&handle.configId) || // config_id + !s.ReadUint16LengthPrefixed(&t) || // enc + !t.ReadBytes(&handle.enc, len(t)) { + return false + } + return true +} + +// hpkeSymmetricCipherSuite represents an ECH ciphersuite, a KDF/AEAD algorithm pair. This +// is different from an HPKE ciphersuite, which represents a KEM/KDF/AEAD +// triple. +type hpkeSymmetricCipherSuite struct { + kdfId, aeadId uint16 +} + +// Generates a grease ECH extension using a hard-coded KEM public key. +func echGenerateGreaseExt(rand io.Reader) ([]byte, error) { + var err error + var dummyX25519PublicKey = []byte{ + 143, 38, 37, 36, 12, 6, 229, 30, 140, 27, 167, 73, 26, 100, 203, 107, 216, + 81, 163, 222, 52, 211, 54, 210, 46, 37, 78, 216, 157, 97, 241, 244, + } + dummyEncodedHelloInnerLen := 100 // TODO(cjpatton): Compute this correctly. + kem, kdf, aead := defaultHPKESuite.Params() + + pk, err := kem.Scheme().UnmarshalBinaryPublicKey(dummyX25519PublicKey) + if err != nil { + return nil, fmt.Errorf("tls: grease ech: failed to parse dummy public key: %s", err) + } + sender, err := defaultHPKESuite.NewSender(pk, nil) + if err != nil { + return nil, fmt.Errorf("tls: grease ech: failed to create sender: %s", err) + } + + var ech echClientOuter + ech.handle.suite.kdfId = uint16(kdf) + ech.handle.suite.aeadId = uint16(aead) + randomByte := make([]byte, 1) + _, err = io.ReadFull(rand, randomByte) + if err != nil { + return nil, fmt.Errorf("tls: grease ech: %s", err) + } + ech.handle.configId = randomByte[0] + ech.handle.enc, _, err = sender.Setup(rand) + if err != nil { + return nil, fmt.Errorf("tls: grease ech: %s", err) + } + ech.payload = make([]byte, + int(aead.CipherLen(uint(dummyEncodedHelloInnerLen)))) + if _, err = io.ReadFull(rand, ech.payload); err != nil { + return nil, fmt.Errorf("tls: grease ech: %s", err) + } + return ech.marshal(), nil +} + +// echEncodeClientHelloInner interprets innerData as a ClientHelloInner message +// and transforms it into an EncodedClientHelloInner. Returns nil if parsing +// innerData fails. +func echEncodeClientHelloInner(innerData []byte, serverNameLen, maxNameLen int) []byte { + var ( + errIllegalParameter = errors.New("illegal parameter") + outerExtensions = echOuterExtensions() + msgType uint8 + legacyVersion uint16 + random []byte + legacySessionId cryptobyte.String + cipherSuites cryptobyte.String + legacyCompressionMethods cryptobyte.String + extensions cryptobyte.String + s cryptobyte.String + b cryptobyte.Builder + ) + + u := cryptobyte.String(innerData) + if !u.ReadUint8(&msgType) || + !u.ReadUint24LengthPrefixed(&s) || !u.Empty() { + return nil + } + + if !s.ReadUint16(&legacyVersion) || + !s.ReadBytes(&random, 32) || + !s.ReadUint8LengthPrefixed(&legacySessionId) || + !s.ReadUint16LengthPrefixed(&cipherSuites) || + !s.ReadUint8LengthPrefixed(&legacyCompressionMethods) { + return nil + } + + if s.Empty() { + // Extensions field must be present in TLS 1.3. + return nil + } + + if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { + return nil + } + + b.AddUint16(legacyVersion) + b.AddBytes(random) + b.AddUint8(0) // 0-length legacy_session_id + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(cipherSuites) + }) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(legacyCompressionMethods) + }) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + if testingECHOuterExtIncorrectOrder { + // Replace outer extensions with "outer_extension" extension, but in + // the incorrect order. + echAddOuterExtensions(b, outerExtensions) + } + + for !extensions.Empty() { + var ext uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&ext) || + !extensions.ReadUint16LengthPrefixed(&extData) { + panic(cryptobyte.BuildError{Err: errIllegalParameter}) + } + + if len(outerExtensions) > 0 && ext == outerExtensions[0] { + if !testingECHOuterExtIncorrectOrder { + // Replace outer extensions with "outer_extension" extension. + echAddOuterExtensions(b, outerExtensions) + } + + // Consume the remaining outer extensions. + for _, outerExt := range outerExtensions[1:] { + if !extensions.ReadUint16(&ext) || + !extensions.ReadUint16LengthPrefixed(&extData) { + panic(cryptobyte.BuildError{Err: errIllegalParameter}) + } + if ext != outerExt { + panic("internal error: malformed ClientHelloInner") + } + } + + } else { + b.AddUint16(ext) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(extData) + }) + } + } + }) + + encodedData, err := b.Bytes() + if err == errIllegalParameter { + return nil // Input malformed + } else if err != nil { + panic(err) // Host encountered internal error + } + + // Add padding. + paddingLen := 0 + if serverNameLen > 0 { + // draft-ietf-tls-esni-13, Section 6.1.3: + // + // If the ClientHelloInner contained a "server_name" extension with a + // name of length D, add max(0, L - D) bytes of padding. + if n := maxNameLen - serverNameLen; n > 0 { + paddingLen += n + } + } else { + // draft-ietf-tls-esni-13, Section 6.1.3: + // + // If the ClientHelloInner did not contain a "server_name" extension + // (e.g., if the client is connecting to an IP address), add L + 9 bytes + // of padding. This is the length of a "server_name" extension with an + // L-byte name. + const sniPaddingLen = 9 + paddingLen += sniPaddingLen + maxNameLen + } + paddingLen = 31 - ((len(encodedData) + paddingLen - 1) % 32) + for i := 0; i < paddingLen; i++ { + encodedData = append(encodedData, 0) + } + + return encodedData +} + +func echAddOuterExtensions(b *cryptobyte.Builder, outerExtensions []uint16) { + b.AddUint16(extensionECHOuterExtensions) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + for _, outerExt := range outerExtensions { + b.AddUint16(outerExt) + } + if testingECHOuterExtIllegal { + // This is not allowed. + b.AddUint16(extensionECH) + } + }) + }) +} + +// echDecodeClientHelloInner interprets encodedData as an EncodedClientHelloInner +// message and substitutes the "outer_extension" extension with extensions from +// outerData, interpreted as the ClientHelloOuter message. Returns nil if +// parsing encodedData fails. +func echDecodeClientHelloInner(encodedData, outerData, outerSessionId []byte) []byte { + var ( + errIllegalParameter = errors.New("illegal parameter") + legacyVersion uint16 + random []byte + legacySessionId cryptobyte.String + cipherSuites cryptobyte.String + legacyCompressionMethods cryptobyte.String + extensions cryptobyte.String + b cryptobyte.Builder + ) + + s := cryptobyte.String(encodedData) + if !s.ReadUint16(&legacyVersion) || + !s.ReadBytes(&random, 32) || + !s.ReadUint8LengthPrefixed(&legacySessionId) || + !s.ReadUint16LengthPrefixed(&cipherSuites) || + !s.ReadUint8LengthPrefixed(&legacyCompressionMethods) { + return nil + } + + if len(legacySessionId) > 0 { + return nil + } + + if s.Empty() { + // Extensions field must be present in TLS 1.3. + return nil + } + + if !s.ReadUint16LengthPrefixed(&extensions) { + return nil + } + + b.AddUint8(typeClientHello) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16(legacyVersion) + b.AddBytes(random) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(outerSessionId) // ClientHelloOuter.legacy_session_id + }) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(cipherSuites) + }) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(legacyCompressionMethods) + }) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + var handledOuterExtensions bool + for !extensions.Empty() { + var ext uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&ext) || + !extensions.ReadUint16LengthPrefixed(&extData) { + panic(cryptobyte.BuildError{Err: errIllegalParameter}) + } + + if ext == extensionECHOuterExtensions { + if handledOuterExtensions { + // It is an error to send any extension more than once in a + // single message. + panic(cryptobyte.BuildError{Err: errIllegalParameter}) + } + handledOuterExtensions = true + + // Read the referenced outer extensions. + referencedExts := make([]uint16, 0, 10) + var outerExtData cryptobyte.String + if !extData.ReadUint8LengthPrefixed(&outerExtData) || + len(outerExtData)%2 != 0 || + !extData.Empty() { + panic(cryptobyte.BuildError{Err: errIllegalParameter}) + } + for !outerExtData.Empty() { + if !outerExtData.ReadUint16(&ext) || + ext == extensionECH { + panic(cryptobyte.BuildError{Err: errIllegalParameter}) + } + referencedExts = append(referencedExts, ext) + } + + // Add the outer extensions from the ClientHelloOuter into the + // ClientHelloInner. + outerCt := 0 + r := processClientHelloExtensions(outerData, func(ext uint16, extData cryptobyte.String) bool { + if outerCt < len(referencedExts) && ext == referencedExts[outerCt] { + outerCt++ + b.AddUint16(ext) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(extData) + }) + } + return true + }) + + // Ensure that all outer extensions have been incorporated + // exactly once, and in the correct order. + if !r || outerCt != len(referencedExts) { + panic(cryptobyte.BuildError{Err: errIllegalParameter}) + } + } else { + b.AddUint16(ext) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(extData) + }) + } + } + }) + }) + + innerData, err := b.Bytes() + if err == errIllegalParameter { + return nil // Input malformed + } else if err != nil { + panic(err) // Host encountered internal error + } + + // Read the padding. + for !s.Empty() { + var zero uint8 + if !s.ReadUint8(&zero) || zero != 0 { + return nil + } + } + + return innerData +} + +// echEncodeClientHelloOuterAAD interprets outerData as ClientHelloOuter and +// constructs a ClientHelloOuterAAD. The output doesn't have the 4-byte prefix +// that indicates the handshake message type and its length. +func echEncodeClientHelloOuterAAD(outerData []byte, payloadLen uint) []byte { + var ( + errIllegalParameter = errors.New("illegal parameter") + msgType uint8 + legacyVersion uint16 + random []byte + legacySessionId cryptobyte.String + cipherSuites cryptobyte.String + legacyCompressionMethods cryptobyte.String + extensions cryptobyte.String + s cryptobyte.String + b cryptobyte.Builder + ) + + u := cryptobyte.String(outerData) + if !u.ReadUint8(&msgType) || + !u.ReadUint24LengthPrefixed(&s) || !u.Empty() { + return nil + } + + if !s.ReadUint16(&legacyVersion) || + !s.ReadBytes(&random, 32) || + !s.ReadUint8LengthPrefixed(&legacySessionId) || + !s.ReadUint16LengthPrefixed(&cipherSuites) || + !s.ReadUint8LengthPrefixed(&legacyCompressionMethods) { + return nil + } + + if s.Empty() { + // Extensions field must be present in TLS 1.3. + return nil + } + + if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { + return nil + } + + b.AddUint16(legacyVersion) + b.AddBytes(random) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(legacySessionId) + }) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(cipherSuites) + }) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(legacyCompressionMethods) + }) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for !extensions.Empty() { + var ext uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&ext) || + !extensions.ReadUint16LengthPrefixed(&extData) { + panic(cryptobyte.BuildError{Err: errIllegalParameter}) + } + + // If this is the ECH extension and the payload is the outer variant + // of ClientECH, then replace the payloadLen 0 bytes. + if ext == extensionECH { + ech, err := echUnmarshalClientOuter(extData) + if err != nil { + panic(cryptobyte.BuildError{Err: errIllegalParameter}) + } + ech.payload = make([]byte, payloadLen) + ech.raw = nil + extData = ech.marshal() + } + + b.AddUint16(ext) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(extData) + }) + } + }) + + outerAadData, err := b.Bytes() + if err == errIllegalParameter { + return nil // Input malformed + } else if err != nil { + panic(err) // Host encountered internal error + } + + return outerAadData +} + +// echEncodeAcceptConfHelloRetryRequest interprets data as a ServerHello message +// and replaces the payload of the ECH extension with 8 zero bytes. The output +// includes the 4-byte prefix that indicates the message type and its length. +func echEncodeAcceptConfHelloRetryRequest(data []byte) []byte { + var ( + errIllegalParameter = errors.New("illegal parameter") + vers uint16 + random []byte + sessionId []byte + cipherSuite uint16 + compressionMethod uint8 + s cryptobyte.String + b cryptobyte.Builder + ) + + s = cryptobyte.String(data) + if !s.Skip(4) || // message type and uint24 length field + !s.ReadUint16(&vers) || !s.ReadBytes(&random, 32) || + !readUint8LengthPrefixed(&s, &sessionId) || + !s.ReadUint16(&cipherSuite) || + !s.ReadUint8(&compressionMethod) { + return nil + } + + if s.Empty() { + // ServerHello is optionally followed by extension data + return nil + } + + var extensions cryptobyte.String + if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { + return nil + } + + b.AddUint8(typeServerHello) + b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddUint16(vers) + b.AddBytes(random) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(sessionId) + }) + b.AddUint16(cipherSuite) + b.AddUint8(compressionMethod) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for !extensions.Empty() { + var extension uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&extension) || + !extensions.ReadUint16LengthPrefixed(&extData) { + panic(cryptobyte.BuildError{Err: errIllegalParameter}) + } + + b.AddUint16(extension) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + if extension == extensionECH { + b.AddBytes(zeros[:8]) + } else { + b.AddBytes(extData) + } + }) + } + }) + }) + + encodedData, err := b.Bytes() + if err == errIllegalParameter { + return nil // Input malformed + } else if err != nil { + panic(err) // Host encountered internal error + } + + return encodedData +} + +// processClientHelloExtensions interprets data as a ClientHello and applies a +// function proc to each extension. Returns a bool indicating whether parsing +// succeeded. +func processClientHelloExtensions(data []byte, proc func(ext uint16, extData cryptobyte.String) bool) bool { + _, extensionsData := splitClientHelloExtensions(data) + if extensionsData == nil { + return false + } + + s := cryptobyte.String(extensionsData) + if s.Empty() { + // Extensions field not present. + return true + } + + var extensions cryptobyte.String + if !s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() { + return false + } + + for !extensions.Empty() { + var ext uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&ext) || + !extensions.ReadUint16LengthPrefixed(&extData) { + return false + } + if ok := proc(ext, extData); !ok { + return false + } + } + return true +} + +// splitClientHelloExtensions interprets data as a ClientHello message and +// returns two strings: the first contains the start of the ClientHello up to +// the start of the extensions; and the second is the length-prefixed +// extensions. Returns (nil, nil) if parsing of data fails. +func splitClientHelloExtensions(data []byte) ([]byte, []byte) { + s := cryptobyte.String(data) + + var ignored uint16 + var t cryptobyte.String + if !s.Skip(4) || // message type and uint24 length field + !s.ReadUint16(&ignored) || !s.Skip(32) || // vers, random + !s.ReadUint8LengthPrefixed(&t) { // session_id + return nil, nil + } + + if !s.ReadUint16LengthPrefixed(&t) { // cipher_suites + return nil, nil + } + + if !s.ReadUint8LengthPrefixed(&t) { // compression_methods + return nil, nil + } + + return data[:len(data)-len(s)], s +} + +// TODO(cjpatton): Handle public name as described in draft-ietf-tls-esni-13, +// Section 4. +// +// TODO(cjpatton): Implement ECH config extensions as described in +// draft-ietf-tls-esni-13, Section 4.1. +func (c *Config) echSelectConfig() *ECHConfig { + for _, echConfig := range c.ClientECHConfigs { + if _, err := echConfig.selectSuite(); err == nil && + echConfig.version == extensionECH { + return &echConfig + } + } + return nil +} + +func (c *Config) echCanOffer() bool { + if c == nil { + return false + } + return c.ECHEnabled && + c.echSelectConfig() != nil && + c.maxSupportedVersion(roleClient) >= VersionTLS13 +} + +func (c *Config) echCanAccept() bool { + if c == nil { + return false + } + return c.ECHEnabled && + c.ServerECHProvider != nil && + c.maxSupportedVersion(roleServer) >= VersionTLS13 +} + +// echOuterExtensions returns the list of extensions of the ClientHelloOuter +// that will be incorporated into the CleintHelloInner. +func echOuterExtensions() []uint16 { + // NOTE(cjpatton): It would be nice to incorporate more extensions, but + // "key_share" is the last extension to appear in the ClientHello before + // "pre_shared_key". As a result, the only contiguous sequence of outer + // extensions that contains "key_share" is "key_share" itself. Note that + // we cannot change the order of extensions in the ClientHello, as the + // unit tests expect "key_share" to be the second to last extension. + outerExtensions := []uint16{extensionKeyShare} + if testingECHOuterExtMany { + // NOTE(cjpatton): Incorporating this particular sequence does not + // yield significant savings. However, it's useful to test that our + // server correctly handles a sequence of compressed extensions and + // not just one. + outerExtensions = []uint16{ + extensionStatusRequest, + extensionSupportedCurves, + extensionSupportedPoints, + } + } else if testingECHOuterExtNone { + outerExtensions = []uint16{} + } + + return outerExtensions +} + +func echCopyExtensionFromClientHelloInner(hello, helloInner *clientHelloMsg, ext uint16) { + switch ext { + case extensionStatusRequest: + hello.ocspStapling = helloInner.ocspStapling + case extensionSupportedCurves: + hello.supportedCurves = helloInner.supportedCurves + case extensionSupportedPoints: + hello.supportedPoints = helloInner.supportedPoints + case extensionKeyShare: + hello.keyShares = helloInner.keyShares + default: + panic(fmt.Errorf("tried to copy unrecognized extension: %04x", ext)) + } +} diff --git a/src/crypto/tls/ech_config.go b/src/crypto/tls/ech_config.go new file mode 100644 index 00000000000..62de3cec544 --- /dev/null +++ b/src/crypto/tls/ech_config.go @@ -0,0 +1,165 @@ +// Copyright 2020 Cloudflare, Inc. All rights reserved. Use of this source code +// is governed by a BSD-style license that can be found in the LICENSE file. + +package tls + +import ( + "errors" + "fmt" + "io" + + "github.com/cloudflare/circl/hpke" + "github.com/cloudflare/circl/kem" + + "golang.org/x/crypto/cryptobyte" +) + +// ECHConfig represents an ECH configuration. +type ECHConfig struct { + pk kem.PublicKey + raw []byte + + // Parsed from raw + version uint16 + configId uint8 + rawPublicName []byte + rawPublicKey []byte + kemId uint16 + suites []hpkeSymmetricCipherSuite + maxNameLen uint8 + ignoredExtensions []byte +} + +// UnmarshalECHConfigs parses a sequence of ECH configurations. +func UnmarshalECHConfigs(raw []byte) ([]ECHConfig, error) { + var ( + err error + config ECHConfig + t, contents cryptobyte.String + ) + configs := make([]ECHConfig, 0) + s := cryptobyte.String(raw) + if !s.ReadUint16LengthPrefixed(&t) || !s.Empty() { + return configs, errors.New("error parsing configs") + } + raw = raw[2:] +ConfigsLoop: + for !t.Empty() { + l := len(t) + if !t.ReadUint16(&config.version) || + !t.ReadUint16LengthPrefixed(&contents) { + return nil, errors.New("error parsing config") + } + n := l - len(t) + config.raw = raw[:n] + raw = raw[n:] + + if config.version != extensionECH { + continue ConfigsLoop + } + if !readConfigContents(&contents, &config) { + return nil, errors.New("error parsing config contents") + } + + kem := hpke.KEM(config.kemId) + if !kem.IsValid() { + continue ConfigsLoop + } + config.pk, err = kem.Scheme().UnmarshalBinaryPublicKey(config.rawPublicKey) + if err != nil { + return nil, fmt.Errorf("error parsing public key: %s", err) + } + configs = append(configs, config) + } + return configs, nil +} + +func echMarshalConfigs(configs []ECHConfig) ([]byte, error) { + var b cryptobyte.Builder + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, config := range configs { + if config.raw == nil { + panic("config.raw not set") + } + b.AddBytes(config.raw) + } + }) + return b.Bytes() +} + +func readConfigContents(contents *cryptobyte.String, config *ECHConfig) bool { + var t cryptobyte.String + if !contents.ReadUint8(&config.configId) || + !contents.ReadUint16(&config.kemId) || + !contents.ReadUint16LengthPrefixed(&t) || + !t.ReadBytes(&config.rawPublicKey, len(t)) || + !contents.ReadUint16LengthPrefixed(&t) || + len(t)%4 != 0 { + return false + } + + config.suites = nil + for !t.Empty() { + var kdfId, aeadId uint16 + if !t.ReadUint16(&kdfId) || !t.ReadUint16(&aeadId) { + // This indicates an internal bug. + panic("internal error while parsing contents.cipher_suites") + } + config.suites = append(config.suites, hpkeSymmetricCipherSuite{kdfId, aeadId}) + } + + if !contents.ReadUint8(&config.maxNameLen) || + !contents.ReadUint8LengthPrefixed(&t) || + !t.ReadBytes(&config.rawPublicName, len(t)) || + !contents.ReadUint16LengthPrefixed(&t) || + !t.ReadBytes(&config.ignoredExtensions, len(t)) || + !contents.Empty() { + return false + } + return true +} + +// setupSealer generates the client's HPKE context for use with the ECH +// extension. It returns the context and corresponding encapsulated key. +func (config *ECHConfig) setupSealer(rand io.Reader) (enc []byte, sealer hpke.Sealer, err error) { + if config.raw == nil { + panic("config.raw not set") + } + hpkeSuite, err := config.selectSuite() + if err != nil { + return nil, nil, err + } + info := append(append([]byte(echHpkeInfoSetup), 0), config.raw...) + sender, err := hpkeSuite.NewSender(config.pk, info) + if err != nil { + return nil, nil, err + } + return sender.Setup(rand) +} + +// isPeerCipherSuiteSupported returns true if this configuration indicates +// support for the given ciphersuite. +func (config *ECHConfig) isPeerCipherSuiteSupported(suite hpkeSymmetricCipherSuite) bool { + for _, configSuite := range config.suites { + if suite == configSuite { + return true + } + } + return false +} + +// selectSuite returns the first ciphersuite indicated by this +// configuration that is supported by the caller. +func (config *ECHConfig) selectSuite() (hpke.Suite, error) { + for _, suite := range config.suites { + hpkeSuite, err := hpkeAssembleSuite( + config.kemId, + suite.kdfId, + suite.aeadId, + ) + if err == nil { + return hpkeSuite, nil + } + } + return hpke.Suite{}, errors.New("could not negotiate a ciphersuite") +} diff --git a/src/crypto/tls/ech_provider.go b/src/crypto/tls/ech_provider.go new file mode 100644 index 00000000000..d114efb2766 --- /dev/null +++ b/src/crypto/tls/ech_provider.go @@ -0,0 +1,303 @@ +// Copyright 2020 Cloudflare, Inc. All rights reserved. Use of this source code +// is governed by a BSD-style license that can be found in the LICENSE file. + +package tls + +import ( + "errors" + "fmt" + + "github.com/cloudflare/circl/hpke" + "github.com/cloudflare/circl/kem" + + "golang.org/x/crypto/cryptobyte" +) + +// ECHProvider specifies the interface of an ECH service provider that decrypts +// the ECH payload on behalf of the client-facing server. It also defines the +// set of acceptable ECH configurations. +type ECHProvider interface { + // GetDecryptionContext attempts to construct the HPKE context used by the + // client-facing server for decryption. (See draft-irtf-cfrg-hpke-07, + // Section 5.2.) + // + // handle encodes the parameters of the client's "encrypted_client_hello" + // extension that are needed to construct the context. Since + // draft-ietf-tls-esni-10 these are the ECH cipher suite, the identity of + // the ECH configuration, and the encapsulated key. + // + // version is the version of ECH indicated by the client. + // + // res.Status == ECHProviderStatusSuccess indicates the call was successful + // and the caller may proceed. res.Context is set. + // + // res.Status == ECHProviderStatusReject indicates the caller must reject + // ECH. res.RetryConfigs may be set. + // + // res.Status == ECHProviderStatusAbort indicates the caller should abort + // the handshake. Note that, in some cases, it's appropriate to reject + // rather than abort. In particular, aborting with "illegal_parameter" might + // "stick out". res.Alert and res.Error are set. + GetDecryptionContext(handle []byte, version uint16) (res ECHProviderResult) +} + +// ECHProviderStatus is the status of the ECH provider's response. +type ECHProviderStatus uint + +const ( + ECHProviderSuccess ECHProviderStatus = 0 + ECHProviderReject = 1 + ECHProviderAbort = 2 + + errHPKEInvalidPublicKey = "hpke: invalid KEM public key" +) + +// ECHProviderResult represents the result of invoking the ECH provider. +type ECHProviderResult struct { + Status ECHProviderStatus + + // Alert is the TLS alert sent by the caller when aborting the handshake. + Alert uint8 + + // Error is the error propagated by the caller when aborting the handshake. + Error error + + // RetryConfigs is the sequence of ECH configs to offer to the client for + // retrying the handshake. This may be set in case of success or rejection. + RetryConfigs []byte + + // Context is the server's HPKE context. This is set if ECH is not rejected + // by the provider and no error was reported. The data has the following + // format (in TLS syntax): + // + // enum { sealer(0), opener(1) } HpkeRole; + // + // struct { + // HpkeRole role; + // HpkeKemId kem_id; // as defined in draft-irtf-cfrg-hpke-07 + // HpkeKdfId kdf_id; // as defined in draft-irtf-cfrg-hpke-07 + // HpkeAeadId aead_id; // as defined in draft-irtf-cfrg-hpke-07 + // opaque exporter_secret<0..255>; + // opaque key<0..255>; + // opaque base_nonce<0..255>; + // opaque seq<0..255>; + // } HpkeContext; + Context []byte +} + +// EXP_ECHKeySet implements the ECHProvider interface for a sequence of ECH keys. +// +// NOTE: This API is EXPERIMENTAL and subject to change. +type EXP_ECHKeySet struct { + // The serialized ECHConfigs, in order of the server's preference. + configs []byte + + // Maps a configuration identifier to its secret key. + sk map[uint8]EXP_ECHKey +} + +// EXP_NewECHKeySet constructs an EXP_ECHKeySet. +func EXP_NewECHKeySet(keys []EXP_ECHKey) (*EXP_ECHKeySet, error) { + if len(keys) > 255 { + return nil, fmt.Errorf("tls: ech provider: unable to support more than 255 ECH configurations at once") + } + + keySet := new(EXP_ECHKeySet) + keySet.sk = make(map[uint8]EXP_ECHKey) + configs := make([]byte, 0) + for _, key := range keys { + if _, ok := keySet.sk[key.config.configId]; ok { + return nil, fmt.Errorf("tls: ech provider: ECH config conflict for configId %d", key.config.configId) + } + + keySet.sk[key.config.configId] = key + configs = append(configs, key.config.raw...) + } + + var b cryptobyte.Builder + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(configs) + }) + keySet.configs = b.BytesOrPanic() + + return keySet, nil +} + +// GetDecryptionContext is required by the ECHProvider interface. +func (keySet *EXP_ECHKeySet) GetDecryptionContext(rawHandle []byte, version uint16) (res ECHProviderResult) { + // Propagate retry configurations regardless of the result. The caller sends + // these to the clients only if it rejects. + res.RetryConfigs = keySet.configs + + // Ensure we know how to proceed, i.e., the caller has indicated a supported + // version of ECH. Currently only draft-ietf-tls-esni-13 is supported. + if version != extensionECH { + res.Status = ECHProviderAbort + res.Alert = uint8(alertInternalError) + res.Error = errors.New("version not supported") + return // Abort + } + + // Parse the handle. + s := cryptobyte.String(rawHandle) + handle := new(echContextHandle) + if !echReadContextHandle(&s, handle) || !s.Empty() { + // This is the result of a client-side error. However, aborting with + // "illegal_parameter" would stick out, so we reject instead. + res.Status = ECHProviderReject + res.RetryConfigs = keySet.configs + return // Reject + } + handle.raw = rawHandle + + // Look up the secret key for the configuration indicated by the client. + key, ok := keySet.sk[handle.configId] + if !ok { + res.Status = ECHProviderReject + res.RetryConfigs = keySet.configs + return // Reject + } + + // Ensure that support for the selected ciphersuite is indicated by the + // configuration. + suite := handle.suite + if !key.config.isPeerCipherSuiteSupported(suite) { + // This is the result of a client-side error. However, aborting with + // "illegal_parameter" would stick out, so we reject instead. + res.Status = ECHProviderReject + res.RetryConfigs = keySet.configs + return // Reject + } + + // Ensure the version indicated by the client matches the version supported + // by the configuration. + if version != key.config.version { + // This is the result of a client-side error. However, aborting with + // "illegal_parameter" would stick out, so we reject instead. + res.Status = ECHProviderReject + res.RetryConfigs = keySet.configs + return // Reject + } + + // Compute the decryption context. + opener, err := key.setupOpener(handle.enc, suite) + if err != nil { + if err.Error() == errHPKEInvalidPublicKey { + // This occurs if the KEM algorithm used to generate handle.enc is + // not the same as the KEM algorithm of the key. One way this can + // happen is if the client sent a GREASE ECH extension with a + // config_id that happens to match a known config, but which uses a + // different KEM algorithm. + res.Status = ECHProviderReject + res.RetryConfigs = keySet.configs + return // Reject + } + + res.Status = ECHProviderAbort + res.Alert = uint8(alertInternalError) + res.Error = err + return // Abort + } + + // Serialize the decryption context. + res.Context, err = opener.MarshalBinary() + if err != nil { + res.Status = ECHProviderAbort + res.Alert = uint8(alertInternalError) + res.Error = err + return // Abort + } + + res.Status = ECHProviderSuccess + return // Success +} + +// EXP_ECHKey represents an ECH key and its corresponding configuration. The +// encoding of an ECH Key has the format defined below (in TLS syntax). Note +// that the ECH standard does not specify this format. +// +// struct { +// opaque sk<0..2^16-1>; +// ECHConfig config<0..2^16>; // draft-ietf-tls-esni-13 +// } ECHKey; +type EXP_ECHKey struct { + sk kem.PrivateKey + config ECHConfig +} + +// EXP_UnmarshalECHKeys parses a sequence of ECH keys. +func EXP_UnmarshalECHKeys(raw []byte) ([]EXP_ECHKey, error) { + var ( + err error + key EXP_ECHKey + sk, config, contents cryptobyte.String + ) + s := cryptobyte.String(raw) + keys := make([]EXP_ECHKey, 0) +KeysLoop: + for !s.Empty() { + if !s.ReadUint16LengthPrefixed(&sk) || + !s.ReadUint16LengthPrefixed(&config) { + return nil, errors.New("error parsing key") + } + + key.config.raw = config + if !config.ReadUint16(&key.config.version) || + !config.ReadUint16LengthPrefixed(&contents) || + !config.Empty() { + return nil, errors.New("error parsing config") + } + + if key.config.version != extensionECH { + continue KeysLoop + } + if !readConfigContents(&contents, &key.config) { + return nil, errors.New("error parsing config contents") + } + + for _, suite := range key.config.suites { + if !hpke.KDF(suite.kdfId).IsValid() || + !hpke.AEAD(suite.aeadId).IsValid() { + continue KeysLoop + } + } + + kem := hpke.KEM(key.config.kemId) + if !kem.IsValid() { + continue KeysLoop + } + key.config.pk, err = kem.Scheme().UnmarshalBinaryPublicKey(key.config.rawPublicKey) + if err != nil { + return nil, fmt.Errorf("error parsing public key: %s", err) + } + key.sk, err = kem.Scheme().UnmarshalBinaryPrivateKey(sk) + if err != nil { + return nil, fmt.Errorf("error parsing secret key: %s", err) + } + + keys = append(keys, key) + } + return keys, nil +} + +// setupOpener computes the HPKE context used by the server in the ECH +// extension.i +func (key *EXP_ECHKey) setupOpener(enc []byte, suite hpkeSymmetricCipherSuite) (hpke.Opener, error) { + if key.config.raw == nil { + panic("raw config not set") + } + hpkeSuite, err := hpkeAssembleSuite( + key.config.kemId, + suite.kdfId, + suite.aeadId, + ) + if err != nil { + return nil, err + } + info := append(append([]byte(echHpkeInfoSetup), 0), key.config.raw...) + receiver, err := hpkeSuite.NewReceiver(key.sk, info) + if err != nil { + return nil, err + } + return receiver.Setup(enc) +} diff --git a/src/crypto/tls/ech_test.go b/src/crypto/tls/ech_test.go new file mode 100644 index 00000000000..9c950234824 --- /dev/null +++ b/src/crypto/tls/ech_test.go @@ -0,0 +1,952 @@ +// Copyright 2020 Cloudflare, Inc. All rights reserved. Use of this source code +// is governed by a BSD-style license that can be found in the LICENSE file. + +package tls + +import ( + "bytes" + "context" + "crypto/rand" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "testing" + "time" +) + +const ( + echTestBackendServerName = "example.com" + echTestClientFacingServerName = "cloudflare-esni.com" +) + +// The client's root CA certificate. +const echTestCertRootPEM = ` +-----BEGIN CERTIFICATE----- +MIICQTCCAeigAwIBAgIUYGSqOFcpxSleCzSCaveKL8lV4N0wCgYIKoZIzj0EAwIw +fzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh +biBGcmFuY2lzY28xHzAdBgNVBAoTFkludGVybmV0IFdpZGdldHMsIEluYy4xDDAK +BgNVBAsTA1dXVzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjAwOTIyMTcwNjAw +WhcNMjUwOTIxMTcwNjAwWjB/MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZv +cm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEfMB0GA1UEChMWSW50ZXJuZXQg +V2lkZ2V0cywgSW5jLjEMMAoGA1UECxMDV1dXMRQwEgYDVQQDEwtleGFtcGxlLmNv +bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNcFaBtPRgekRBKTBvuKdTy3raqs +4IizMLFup434MfQ5oH71mYpKndfBzxcZDTMYeocKlt1pVYwvZ3ZdpRsW6yWjQjBA +MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQ2GJIW ++4m3/qpkage5tEvMg3NwPTAKBggqhkjOPQQDAgNHADBEAiB6J8UqRvdhLOiaDYqH +KG+TuveHOqlfQqQgXo4/hNKMiAIgV79TTPHu+Ymn/tcCy9LVWZcpgnCEjrZi0ou5 +et8BX9s= +-----END CERTIFICATE-----` + +// Certificate of the client-facing server. The server name is +// "cloudflare-esni.com". +const echTestCertClientFacingPEM = ` +-----BEGIN CERTIFICATE----- +MIICIjCCAcigAwIBAgIUCXySp2MadlDlcvFrSm4BtLUY70owCgYIKoZIzj0EAwIw +fzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh +biBGcmFuY2lzY28xHzAdBgNVBAoTFkludGVybmV0IFdpZGdldHMsIEluYy4xDDAK +BgNVBAsTA1dXVzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjAwOTIyMTcxMDAw +WhcNMjEwOTIyMTcxMDAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7nP/ +Txinb0JPE/xdjv5d3zrWJqXo7qwP67oVaMKJp5ausJ+0IZfiMWz8pa6T7pyyLrC5 +xvQNkfVkpP9/FxmNFaOBoDCBnTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI +KwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFNN7Afv+ +CgPAxRr4QdZn8JFvQ9nTMB8GA1UdIwQYMBaAFDYYkhb7ibf+qmRqB7m0S8yDc3A9 +MB4GA1UdEQQXMBWCE2Nsb3VkZmxhcmUtZXNuaS5jb20wCgYIKoZIzj0EAwIDSAAw +RQIgZ4VlBtjTRludP/JwfaNQyGKZFWFqRsECvGPbk+ZHLZwCIQCTjuMAFrnjf/j5 +3RNw67l7+QQPrmurSO86l1IlDWNtcA== +-----END CERTIFICATE-----` + +// Signing key of the client-facing server. +var echTestKeyClientFacingPEM = testingKey(` +-----BEGIN TESTING KEY----- +MHcCAQEEIPpCcU8mu+h4xHAm18NJvn73Ko9fjH9QxDCpRt7kCIq9oAoGCCqGSM49 +AwEHoUQDQgAE7nP/Txinb0JPE/xdjv5d3zrWJqXo7qwP67oVaMKJp5ausJ+0IZfi +MWz8pa6T7pyyLrC5xvQNkfVkpP9/FxmNFQ== +-----END TESTING KEY-----`) + +// Certificate of the backend server. The server name is "example.com". +const echTestCertBackendPEM = ` +-----BEGIN CERTIFICATE----- +MIICGTCCAcCgAwIBAgIUQJSSdOZs9wag1Toanlt9lol0uegwCgYIKoZIzj0EAwIw +fzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh +biBGcmFuY2lzY28xHzAdBgNVBAoTFkludGVybmV0IFdpZGdldHMsIEluYy4xDDAK +BgNVBAsTA1dXVzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjAwOTIyMTcwOTAw +WhcNMjEwOTIyMTcwOTAwWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAElq+q +E01Z87KIPHWdEAk0cWssHkRnS4aQCDfstoxDIWQ4rMwHvrWGFy/vytRwyjhHuX9n +tc5ArCpwbAmY+oW/46OBmDCBlTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI +KwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPz9Ct9U +EIjBEcUpv/yxHYccUDo1MB8GA1UdIwQYMBaAFDYYkhb7ibf+qmRqB7m0S8yDc3A9 +MBYGA1UdEQQPMA2CC2V4YW1wbGUuY29tMAoGCCqGSM49BAMCA0cAMEQCICDBEzzE +DF529x9Z4BkOKVxNDicfWSjxrcMohevjeCWDAiBaxXS5+6I2fcred0JGMsJgo7ts +S8GYhuKE99mQA0/mug== +-----END CERTIFICATE-----` + +// Signing key of the backend server. +var echTestKeyBackendPEM = testingKey(` +-----BEGIN TESTING KEY----- +MHcCAQEEIIJsLXmfzw6FDlqyRRLhY6lVB6ws5ewjUQjnS4DXsQ60oAoGCCqGSM49 +AwEHoUQDQgAElq+qE01Z87KIPHWdEAk0cWssHkRnS4aQCDfstoxDIWQ4rMwHvrWG +Fy/vytRwyjhHuX9ntc5ArCpwbAmY+oW/4w== +-----END TESTING KEY-----`) + +// The ECH keys used by the client-facing server. +const echTestKeys = `-----BEGIN ECH KEYS----- +ACBpvnEYyFK6Ey4Pajbm6VaEsQp4bgRxoPVOs2wOiMuD+QBG/g0AQsMAIAAgCfU+ +VOBXjOut9a9m7wLhrZhHfM0GqE5BQLQK03DJf10ABAABAAElE2Nsb3VkZmxhcmUt +ZXNuaS5jb20AAAAguffuF8tjWUORwFbQ3+cDDqkMQuuMV7py7p1EJfM9S3IAZ/4N +AGMDABAAQQRhm1JRi7hkaK1HhcJq4ByJpK4fbsaD65xSqUuW0L53OYK3zEtz78pk +NhWC9NlkItWc2SYOTrGGHc5WhmJxKCTbAAQAAQABKhNjbG91ZGZsYXJlLWVzbmku +Y29tAAA= +-----END ECH KEYS-----` + +// A sequence of ECH keys with unsupported versions. +const echTestInvalidVersionKeys = `-----BEGIN ECH KEYS----- +ACDhS0q2cTU1Qzi6hPM4BQ/HLnbEUZyWdY2GbmS0DVkumgBIAfUARAAAIAAgi1Tu +jWJ236k1VAMeRnysKbDigxLpDs/AGdEowK8KiBkABAABAAEAAAATY2xvdWRmbGFy +ZS1lc25pLmNvbQAAACBmNj/zQe6OT/MR/MM39G6kwMJCJEXpdvTAkbdHErlgXwBI +AfUARAEAIAAgZ1Ru1uyGX6N9HYs5/pAE3KwUXRDBHD0Bdna8oP4uVEwABAABAAEA +AAATY2xvdWRmbGFyZS1lc25pLmNvbQAA +-----END ECH KEYS-----` + +// The sequence of ECH configurations corresponding to echTestKeys. +const echTestConfigs = `-----BEGIN ECH CONFIGS----- +AK3+DQBCwwAgACAJ9T5U4FeM6631r2bvAuGtmEd8zQaoTkFAtArTcMl/XQAEAAEA +ASUTY2xvdWRmbGFyZS1lc25pLmNvbQAA/g0AYwMAEABBBGGbUlGLuGRorUeFwmrg +HImkrh9uxoPrnFKpS5bQvnc5grfMS3PvymQ2FYL02WQi1ZzZJg5OsYYdzlaGYnEo +JNsABAABAAEqE2Nsb3VkZmxhcmUtZXNuaS5jb20AAA== +-----END ECH CONFIGS-----` + +// An invalid sequence of ECH configurations. +const echTestStaleConfigs = `-----BEGIN ECH CONFIGS----- +AK3+DQBCfgAgACA02DWuCoykTn5CZ/t+h3dXN2JLS5r5RlJPaOzH1UdnRgAEAAEA +ASUTY2xvdWRmbGFyZS1lc25pLmNvbQAA/g0AY8YAEABBBIpQ8lWXbmjAgaFg6TDf +si7tgaTV7fUMbrOZzCyKyIfv/cO872MYb9dvEH1Izu6LtKdGAlmKmu2pxtdpbsSW +CX0ABAABAAEqE2Nsb3VkZmxhcmUtZXNuaS5jb20AAA== +-----END ECH CONFIGS-----` + +// echTestProviderAlwaysAbort mocks an ECHProvider that, in response to any +// request, sets an alert and returns an error. The client-facing server must +// abort the handshake. +type echTestProviderAlwaysAbort struct{} + +// Required by the ECHProvider interface. +func (p echTestProviderAlwaysAbort) GetDecryptionContext(_ []byte, _ uint16) (res ECHProviderResult) { + res.Status = ECHProviderAbort + res.Alert = uint8(alertInternalError) + res.Error = errors.New("provider failed") + return // Abort +} + +// echTestProviderAlwaysReject simulates fallover of the ECH provider. In +// response to any query, it rejects without sending retry configurations., in response to any +type echTestProviderAlwaysReject struct{} + +// Required by the ECHProvider interface. +func (p echTestProviderAlwaysReject) GetDecryptionContext(_ []byte, _ uint16) (res ECHProviderResult) { + res.Status = ECHProviderReject + return // Reject without retry configs +} + +func echTestLoadConfigs(pemData string) []ECHConfig { + block, rest := pem.Decode([]byte(pemData)) + if block == nil || block.Type != "ECH CONFIGS" || len(rest) > 0 { + panic("pem decoding fails") + } + + configs, err := UnmarshalECHConfigs(block.Bytes) + if err != nil { + panic(err) + } + + return configs +} + +func echTestLoadKeySet(pemData string) *EXP_ECHKeySet { + block, rest := pem.Decode([]byte(pemData)) + if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 { + panic("pem decoding fails") + } + + keys, err := EXP_UnmarshalECHKeys(block.Bytes) + if err != nil { + panic(err) + } + + keySet, err := EXP_NewECHKeySet(keys) + if err != nil { + panic(err) + } + + return keySet +} + +type echTestCase struct { + name string + + // expected outcomes + expectClientAbort bool // client aborts + expectServerAbort bool // server aborts + expectOffered bool // server indicates that ECH was offered + expectClientBypassed bool // server bypasses ECH + expectServerBypassed bool // server indicates that ECH was bypassed by client + expectAccepted bool // server indicates ECH acceptance + expectRejected bool // server indicates ECH rejection + expectGrease bool // server indicates dummy ECH was detected + expectBackendServerName bool // client verified backend server name + + // client config + clientEnabled bool // client enables ECH + clientStaleConfigs bool // client offers ECH with invalid config + clientNoConfigs bool // client sends dummy ECH if ECH enabled + clientInvalidTLSVersion bool // client does not offer 1.3 + + // server config + serverEnabled bool // server enables ECH + serverProviderAlwaysAbort bool // ECH provider always aborts + serverProviderAlwaysReject bool // ECH provider always rejects + serverProviderInvalidVersion bool // ECH provider uses configs with unsupported version + serverInvalidTLSVersion bool // server does not offer 1.3 + + // code path triggers + triggerHRR bool // server triggers HRR + triggerECHBypassAfterHRR bool // client bypasses after HRR + triggerECHBypassBeforeHRR bool // client bypasses before HRR + triggerIllegalHandleAfterHRR bool // client sends illegal ECH extension after HRR + triggerOuterExtMany bool // client sends many (not just one) outer extensions + triggerOuterExtIncorrectOrder bool // client sends malformed outer extensions + triggerOuterExtIllegal bool // client sends malformed outer extensions + triggerOuterExtNone bool // client does not incorporate outer extensions + triggerOuterIsInner bool // client sends "ech_is_inner" in ClientHelloOuter + triggerPayloadDecryptError bool // client sends inauthentic ciphertext +} + +// TODO(cjpatton): Add test cases for PSK interactions: +// - ECH bypassed, backend server consumes early data (baseline test config) +// - ECH accepted, backend server consumes early data +// - ECH rejected, client-facing server ignores early data intended for backend +var echTestCases = []echTestCase{ + { + // The client offers ECH and it is accepted by the server + name: "success / accepted", + expectOffered: true, + expectAccepted: true, + expectBackendServerName: true, + clientEnabled: true, + serverEnabled: true, + }, + { + // The client bypasses ECH, i.e., it neither offers ECH nor sends a + // dummy ECH extension. + name: "success / bypassed: not offered", + expectClientBypassed: true, + expectBackendServerName: true, + serverEnabled: true, + }, + { + // The client sends dummy (i.e., "GREASEd") ECH. The server sends retry + // configs in case the client meant to offer ECH. The client does not + // signal rejection, so the server concludes ECH was not offered. + name: "success / bypassed: grease", + expectGrease: true, + expectBackendServerName: true, + clientEnabled: true, + clientNoConfigs: true, + serverEnabled: true, + }, + { + // The client sends dummy ECH because it has enabled ECH but not TLS + // 1.3. The server sends retry configs in case the client meant to offer + // ECH. The client does not signal rejection, so the server concludes + // ECH was not offered. + name: "success / bypassed: client invalid version", + expectGrease: true, + expectBackendServerName: true, + clientInvalidTLSVersion: true, + clientEnabled: true, + serverEnabled: true, + }, + { + // The client offers ECH with an invalid (e.g., stale) config. The + // server sends retry configs. The client signals rejection by sending + // an "ech_required" alert. + name: "success / rejected: invalid config", + expectOffered: true, + expectRejected: true, + expectClientAbort: true, + expectServerAbort: true, + clientStaleConfigs: true, + clientEnabled: true, + serverEnabled: true, + }, + { + // The client offers ECH, but the payload is mangled in transit. The + // server sends retry configurations. The client signals rejection by + // sending an "ech_required" alert. + name: "success / rejected: inauthentic ciphertext", + expectOffered: true, + expectRejected: true, + expectClientAbort: true, + expectServerAbort: true, + clientEnabled: true, + serverEnabled: true, + triggerPayloadDecryptError: true, + }, + { + // The client offered ECH, but client-facing server terminates the + // connection without sending retry configurations. The client aborts + // with "ech_required" and regards ECH as securely disabled by the + // server. + name: "success / rejected: not supported by client-facing server", + expectServerBypassed: true, + expectClientAbort: true, + expectServerAbort: true, + clientEnabled: true, + }, + { + // The client offers ECH. The server ECH rejects without sending retry + // configurations, simulating fallover of the ECH provider. The client + // signals rejection. + name: "success / rejected: provider falls over", + expectServerAbort: true, + expectOffered: true, + expectServerBypassed: true, + expectClientAbort: true, + clientEnabled: true, + serverEnabled: true, + serverProviderAlwaysReject: true, + }, + { + // The client offers ECH. The server ECH rejects without sending retry + // configurations because the ECH provider returns configurations with + // unsupported versions only. + name: "success / rejected: provider invalid version", + expectServerAbort: true, + expectOffered: true, + expectServerBypassed: true, + expectClientAbort: true, + clientEnabled: true, + serverEnabled: true, + serverProviderInvalidVersion: true, + }, + { + // The client offers ECH. The server does not support TLS 1.3, so it + // ignores the extension and continues as usual. The client does not + // signal rejection because TLS 1.2 has been negotiated. + name: "success / bypassed: client-facing invalid version", + expectServerBypassed: true, + clientEnabled: true, + serverEnabled: true, + serverInvalidTLSVersion: true, + }, + { + // The client offers ECH. The ECH provider encounters an unrecoverable + // error, causing the server to abort. + name: "server abort: provider hard fails", + expectServerAbort: true, + expectClientAbort: true, + clientEnabled: true, + serverEnabled: true, + serverProviderAlwaysAbort: true, + }, + { + // The client offers ECH and it is accepted by the server. The HRR code + // path is triggered. + name: "hrr / accepted", + expectOffered: true, + expectAccepted: true, + expectBackendServerName: true, + triggerHRR: true, + clientEnabled: true, + serverEnabled: true, + }, + { + // The client sends a dummy ECH extension. The server sends retry + // configs in case the client meant to offer ECH. The client does not + // signal rejection, so the server concludes ECH was not offered. The + // HRR code path is triggered. + name: "hrr / bypassed: grease", + expectGrease: true, + expectBackendServerName: true, + clientEnabled: true, + clientNoConfigs: true, + serverEnabled: true, + triggerHRR: true, + }, + { + // The client offers ECH with an invalid (e.g., stale) config. The + // server sends retry configs. The client signals rejection. The HRR + // code path is triggered. + name: "hrr / rejected: invalid config", + expectOffered: true, + expectRejected: true, + expectClientAbort: true, + expectServerAbort: true, + clientEnabled: true, + clientStaleConfigs: true, + serverEnabled: true, + triggerHRR: true, + }, + { + // The HRR code path is triggered. The client offered ECH in the second + // CH but not the first. + name: "hrr / server abort: offer after bypass", + expectServerAbort: true, + expectClientAbort: true, + clientEnabled: true, + serverEnabled: true, + triggerHRR: true, + triggerECHBypassBeforeHRR: true, + }, + { + // The HRR code path is triggered. The client offered ECH in the first + // CH but not the second. + name: "hrr / server abort: bypass after offer", + expectOffered: true, + expectAccepted: true, + expectServerAbort: true, + expectClientAbort: true, + clientEnabled: true, + serverEnabled: true, + triggerHRR: true, + triggerECHBypassAfterHRR: true, + }, + { + // The HRR code path is triggered. In the second CH, the value of the + // context handle changes illegally. Specifically, the client sends a + // non-empty "config_id" and "enc". + name: "hrr / server abort: illegal handle", + expectOffered: true, + expectAccepted: true, + expectServerAbort: true, + expectClientAbort: true, + clientEnabled: true, + serverEnabled: true, + triggerHRR: true, + triggerIllegalHandleAfterHRR: true, + }, + { + // The client offers ECH and it is accepted by the server. The client + // incorporates many outer extensions instead of just one (the default + // behavior). + name: "outer extensions, many / accepted", + expectBackendServerName: true, + expectOffered: true, + expectAccepted: true, + clientEnabled: true, + serverEnabled: true, + triggerOuterExtMany: true, + }, + { + // The client offers ECH and it is accepted by the server. The client + // incorporates no outer extensions. + name: "outer extensions, none / accepted", + expectBackendServerName: true, + expectOffered: true, + expectAccepted: true, + clientEnabled: true, + serverEnabled: true, + triggerOuterExtNone: true, + }, + { + // The client offers ECH but does not implement the "outer_extension" + // mechanism correctly. Specifically, it sends them in the wrong order, + // causing the client and server to compute different transcripts. + name: "outer extensions, incorrect order / server abort: incorrect transcript", + expectOffered: true, + expectAccepted: true, + expectServerAbort: true, + expectClientAbort: true, + clientEnabled: true, + serverEnabled: true, + triggerOuterExtIncorrectOrder: true, + }, + { + // The client offers ECH but does not implement the "outer_extension" + // mechanism correctly. Specifically, the "outer extensions" contains + // the codepoint for the ECH extension itself. + name: "outer extensions, illegal: illegal parameter", + expectServerAbort: true, + expectClientAbort: true, + clientEnabled: true, + serverEnabled: true, + triggerOuterExtIllegal: true, + }, +} + +// Returns the base configurations for the client and client-facing server, +func echSetupConnTest() (clientConfig, serverConfig *Config) { + echTestNow := time.Date(2020, time.September, 23, 0, 0, 0, 0, time.UTC) + echTestConfig := &Config{ + Time: func() time.Time { + return echTestNow + }, + Rand: rand.Reader, + CipherSuites: allCipherSuites(), + InsecureSkipVerify: false, + } + + clientFacingCert, err := X509KeyPair([]byte(echTestCertClientFacingPEM), []byte(echTestKeyClientFacingPEM)) + if err != nil { + panic(err) + } + + backendCert, err := X509KeyPair([]byte(echTestCertBackendPEM), []byte(echTestKeyBackendPEM)) + if err != nil { + panic(err) + } + + block, rest := pem.Decode([]byte(echTestCertRootPEM)) + if block == nil || block.Type != "CERTIFICATE" || len(rest) > 0 { + panic("pem decoding fails") + } + + rootCert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + panic(err) + } + + clientConfig = echTestConfig.Clone() + clientConfig.ServerName = echTestBackendServerName + clientConfig.RootCAs = x509.NewCertPool() + clientConfig.RootCAs.AddCert(rootCert) + + serverConfig = echTestConfig.Clone() + serverConfig.GetCertificate = func(info *ClientHelloInfo) (*Certificate, error) { + if info.ServerName == echTestBackendServerName { + return &backendCert, nil + } else if info.ServerName == echTestClientFacingServerName { + return &clientFacingCert, nil + } + return nil, nil + } + return +} + +// echTestResult represents the ECH status and error status of a connection. +type echTestResult struct { + // Operational parameters + clientDone, serverDone bool + // Results + clientStatus CFEventECHClientStatus + serverStatus CFEventECHServerStatus + connState ConnectionState + err error +} + +func (r *echTestResult) eventHandler(event CFEvent) { + switch e := event.(type) { + case CFEventECHClientStatus: + if r.clientDone { + panic("expected at most one client ECH status event") + } + r.clientStatus = e + r.clientDone = true + case CFEventECHServerStatus: + if r.serverDone { + panic("expected at most one server ECH status event") + } + r.serverStatus = e + r.clientDone = true + } +} + +// echTestConn runs the handshake and returns the ECH and error status of the +// client and server. It also returns the server name verified by the client. +func echTestConn(t *testing.T, clientConfig, serverConfig *Config) (clientRes, serverRes echTestResult) { + testMessage := []byte("hey bud") + buf := make([]byte, len(testMessage)) + ln := newLocalListener(t) + defer ln.Close() + + serverCh := make(chan echTestResult, 1) + go func() { + var res echTestResult + serverConn, err := ln.Accept() + if err != nil { + res.err = err + serverCh <- res + return + } + + server := Server(serverConn, serverConfig) + defer func() { + server.Close() + serverCh <- res + }() + + sCtx := context.WithValue(context.Background(), CFEventHandlerContextKey{}, res.eventHandler) + if err := server.HandshakeContext(sCtx); err != nil { + res.err = err + return + } + + if _, err = server.Read(buf); err != nil { + res.err = err + } + + res.connState = server.ConnectionState() + }() + + cCtx := context.WithValue(context.Background(), CFEventHandlerContextKey{}, clientRes.eventHandler) + clientDialer := &Dialer{Config: clientConfig} + clientConn, err := clientDialer.DialContext(cCtx, "tcp", ln.Addr().String()) + if err != nil { + serverRes = <-serverCh + clientRes.err = err + return + } + client := clientConn.(*Conn) + defer client.Close() + + _, err = client.Write(testMessage) + if err != nil { + serverRes = <-serverCh + clientRes.err = err + return + } + + clientRes.connState = client.ConnectionState() + serverRes = <-serverCh + return +} + +func TestECHHandshake(t *testing.T) { + defer func() { + // Reset testing triggers after the test completes. + testingTriggerHRR = false + testingECHTriggerBypassAfterHRR = false + testingECHTriggerBypassBeforeHRR = false + testingECHIllegalHandleAfterHRR = false + testingECHOuterExtMany = false + testingECHOuterExtNone = false + testingECHOuterExtIncorrectOrder = false + testingECHOuterExtIllegal = false + testingECHTriggerPayloadDecryptError = false + }() + + staleConfigs := echTestLoadConfigs(echTestStaleConfigs) + configs := echTestLoadConfigs(echTestConfigs) + keySet := echTestLoadKeySet(echTestKeys) + invalidVersionKeySet := echTestLoadKeySet(echTestInvalidVersionKeys) + + clientConfig, serverConfig := echSetupConnTest() + for i, test := range echTestCases { + t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) { + // Configure the client. + n := 0 + if test.clientNoConfigs { + clientConfig.ClientECHConfigs = nil + n++ + } + if test.clientStaleConfigs { + clientConfig.ClientECHConfigs = staleConfigs + n++ + } + if n == 0 { + clientConfig.ClientECHConfigs = configs + } else if n > 1 { + panic("invalid test configuration") + } + + if test.clientEnabled { + clientConfig.ECHEnabled = true + } else { + clientConfig.ECHEnabled = false + } + + if test.clientInvalidTLSVersion { + clientConfig.MinVersion = VersionTLS10 + clientConfig.MaxVersion = VersionTLS12 + } else { + clientConfig.MinVersion = VersionTLS10 + clientConfig.MaxVersion = VersionTLS13 + } + + // Configure the client-facing server. + if test.serverEnabled { + serverConfig.ECHEnabled = true + } else { + serverConfig.ECHEnabled = false + } + + n = 0 + if test.serverProviderAlwaysAbort { + serverConfig.ServerECHProvider = &echTestProviderAlwaysAbort{} + n++ + } + if test.serverProviderAlwaysReject { + serverConfig.ServerECHProvider = &echTestProviderAlwaysReject{} + n++ + } + if test.serverProviderInvalidVersion { + serverConfig.ServerECHProvider = invalidVersionKeySet + n++ + } + if n == 0 { + serverConfig.ServerECHProvider = keySet + } else if n > 1 { + panic("invalid test configuration") + } + + if test.serverInvalidTLSVersion { + serverConfig.MinVersion = VersionTLS10 + serverConfig.MaxVersion = VersionTLS12 + } else { + serverConfig.MinVersion = VersionTLS10 + serverConfig.MaxVersion = VersionTLS13 + } + + testingTriggerHRR = false + if test.triggerHRR { + testingTriggerHRR = true + } + + testingECHTriggerBypassAfterHRR = false + if test.triggerECHBypassAfterHRR { + testingECHTriggerBypassAfterHRR = true + } + + testingECHTriggerBypassBeforeHRR = false + if test.triggerECHBypassBeforeHRR { + testingECHTriggerBypassBeforeHRR = true + } + + testingECHTriggerPayloadDecryptError = false + if test.triggerPayloadDecryptError { + testingECHTriggerPayloadDecryptError = true + } + + n = 0 + testingECHOuterExtMany = false + if test.triggerOuterExtMany { + testingECHOuterExtMany = true + n++ + } + testingECHOuterExtNone = false + if test.triggerOuterExtNone { + testingECHOuterExtNone = true + n++ + } + testingECHOuterExtIncorrectOrder = false + if test.triggerOuterExtIncorrectOrder { + testingECHOuterExtIncorrectOrder = true + n++ + } + testingECHOuterExtIllegal = false + if test.triggerOuterExtIllegal { + testingECHOuterExtIllegal = true + n++ + } + testingECHIllegalHandleAfterHRR = false + if test.triggerIllegalHandleAfterHRR { + testingECHIllegalHandleAfterHRR = true + n++ + } + if n > 1 { + panic("invalid test configuration") + } + + t.Logf("%s", test.name) + + // Run the handshake. + client, server := echTestConn(t, clientConfig, serverConfig) + if !test.expectClientAbort && client.err != nil { + t.Error("client aborts; want success") + } + + if !test.expectServerAbort && server.err != nil { + t.Error("server aborts; want success") + } + + if test.expectClientAbort && client.err == nil { + t.Error("client succeeds; want abort") + } else if client.err != nil { + t.Logf("client err: %s", client.err) + } + + if test.expectServerAbort && server.err == nil { + t.Errorf("server succeeds; want abort") + } else if server.err != nil { + t.Logf("server err: %s", server.err) + } + + if got := server.clientStatus.Offered(); got != test.expectOffered { + t.Errorf("got offered=%v; want %v", got, test.expectOffered) + } + + if got := server.clientStatus.Greased(); got != test.expectGrease { + t.Errorf("got grease=%v; want %v", got, test.expectGrease) + } + + if got := server.clientStatus.Bypassed(); got != test.expectClientBypassed && server.err == nil { + t.Errorf("got clientBypassed=%v; want %v", got, test.expectClientBypassed) + } + + if got := server.serverStatus.Bypassed(); got != test.expectServerBypassed && server.err == nil { + t.Errorf("got serverBypassed=%v; want %v", got, test.expectServerBypassed) + } + + if got := server.serverStatus.Accepted(); got != test.expectAccepted { + t.Errorf("got accepted=%v; want %v", got, test.expectAccepted) + } + + if got := server.serverStatus.Rejected(); got != test.expectRejected { + t.Errorf("got rejected=%v; want %v", got, test.expectRejected) + } + + if client.err != nil { + return + } + + if name := client.connState.ServerName; test.expectBackendServerName != (name == echTestBackendServerName) { + t.Errorf("got backend server name=%v; want %v", name == echTestBackendServerName, test.expectBackendServerName) + } + + if client.clientStatus.Greased() != server.clientStatus.Greased() || + client.clientStatus.Bypassed() != server.clientStatus.Bypassed() || + client.serverStatus.Bypassed() != server.serverStatus.Bypassed() || + client.serverStatus.Accepted() != server.serverStatus.Accepted() || + client.serverStatus.Rejected() != server.serverStatus.Rejected() { + t.Error("client and server disagree on ech usage") + t.Errorf("client=%+v", client) + t.Errorf("server=%+v", server) + } + + if accepted := client.connState.ECHAccepted; accepted != client.serverStatus.Accepted() { + t.Errorf("client got ECHAccepted=%v; want %v", accepted, client.serverStatus.Accepted()) + } + + if accepted := server.connState.ECHAccepted; accepted != server.serverStatus.Accepted() { + t.Errorf("server got ECHAccepted=%v; want %v", accepted, server.serverStatus.Accepted()) + } + }) + } +} + +func TestUnmarshalConfigs(t *testing.T) { + block, rest := pem.Decode([]byte(echTestConfigs)) + if block == nil || block.Type != "ECH CONFIGS" || len(rest) > 0 { + t.Fatal("pem decoding fails") + } + + configs, err := UnmarshalECHConfigs(block.Bytes) + if err != nil { + t.Fatal(err) + } + + if len(configs) != 2 { + t.Errorf("wrong number of configs: got %d; want %d", len(configs), 2) + } + + for i, config := range configs { + if len(config.suites) != 1 { + t.Errorf("wrong number of cipher suites in config #%d: got %d; want %d", i, len(config.suites), 1) + } + } + + for _, config := range configs { + if len(config.raw) == 0 { + t.Error("raw config not set") + } + } +} + +func TestUnmarshalKeys(t *testing.T) { + block, rest := pem.Decode([]byte(echTestKeys)) + if block == nil || block.Type != "ECH KEYS" || len(rest) > 0 { + t.Fatal("pem decoding fails") + } + + keys, err := EXP_UnmarshalECHKeys(block.Bytes) + if err != nil { + t.Fatal(err) + } + + if len(keys) != 2 { + t.Errorf("wrong number of configs: got %d; want %d", len(keys), 2) + } + + for i, key := range keys { + if len(key.config.raw) == 0 { + t.Error("raw config not set") + } + + if len(key.config.suites) != 1 { + t.Errorf("wrong number of cipher suites in config #%d: got %d; want %d", i, len(key.config.suites), 1) + } + } +} + +func testECHProvider(t *testing.T, p ECHProvider, handle []byte, version uint16, want ECHProviderResult) { + got := p.GetDecryptionContext(handle, version) + if got.Status != want.Status { + t.Errorf("incorrect status: got %+v; want %+v", got.Status, want.Status) + } + if got.Alert != want.Alert { + t.Errorf("incorrect alert: got %+v; want %+v", got.Alert, want.Alert) + } + if got.Error != want.Error { + t.Errorf("incorrect error: got %+v; want %+v", got.Error, want.Error) + } + if !bytes.Equal(got.RetryConfigs, want.RetryConfigs) { + t.Errorf("incorrect retry configs: got %+v; want %+v", got.RetryConfigs, want.RetryConfigs) + } + if !bytes.Equal(got.Context, want.Context) { + t.Errorf("incorrect context: got %+v; want %+v", got.Context, want.Context) + } +} + +func TestECHProvider(t *testing.T) { + p := echTestLoadKeySet(echTestKeys) + t.Run("ok", func(t *testing.T) { + handle := []byte{ + 0, 1, 0, 1, 195, 0, 32, 49, 215, 32, 55, 8, 132, 98, 118, 166, 113, + 184, 40, 196, 151, 103, 20, 221, 148, 22, 72, 112, 152, 18, 20, 107, + 15, 109, 178, 15, 98, 104, 66, + } + context := []byte{ + 1, 0, 32, 0, 1, 0, 1, 32, 111, 237, 227, 138, 43, 202, 113, 109, + 127, 174, 36, 48, 232, 103, 97, 52, 76, 112, 136, 36, 220, 91, 12, + 21, 63, 194, 77, 110, 112, 25, 241, 135, 16, 214, 55, 95, 236, 101, + 6, 49, 56, 18, 215, 166, 137, 136, 225, 58, 54, 12, 229, 100, 254, + 43, 179, 2, 188, 179, 6, 166, 138, 138, 12, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + } + testECHProvider(t, p, handle, extensionECH, ECHProviderResult{ + Status: ECHProviderSuccess, + RetryConfigs: p.configs, + Context: context, + }) + }) + t.Run("invalid config id", func(t *testing.T) { + handle := []byte{ + 0, 1, 0, 1, 255, 202, 62, 220, 1, 243, 58, 0, 32, 40, 52, 167, 167, + 21, 125, 151, 32, 250, 255, 1, 125, 206, 103, 62, 96, 189, 112, 126, + 48, 221, 41, 198, 146, 100, 149, 29, 133, 103, 87, 87, 78, + } + testECHProvider(t, p, handle, extensionECH, ECHProviderResult{ + Status: ECHProviderReject, + RetryConfigs: p.configs, + }) + }) + t.Run("invalid cipher suite", func(t *testing.T) { + handle := []byte{ + 99, 99, 0, 1, 8, 202, 62, 220, 1, 243, 58, 247, 102, 0, 32, 40, 52, + 167, 167, 21, 125, 151, 32, 250, 255, 1, 125, 206, 103, 62, 96, 189, + 112, 126, 48, 221, 41, 198, 146, 100, 149, 29, 133, 103, 87, 87, 78, + } + testECHProvider(t, p, handle, extensionECH, ECHProviderResult{ + Status: ECHProviderReject, + RetryConfigs: p.configs, + }) + }) + t.Run("malformed", func(t *testing.T) { + handle := []byte{ + 0, 1, 0, 1, 8, 202, 62, 220, 1, + } + testECHProvider(t, p, handle, extensionECH, ECHProviderResult{ + Status: ECHProviderReject, + RetryConfigs: p.configs, + }) + }) +} diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go index f1e816b3eb6..b7754c3aa72 100644 --- a/src/crypto/tls/handshake_client.go +++ b/src/crypto/tls/handshake_client.go @@ -40,7 +40,7 @@ type clientHandshakeState struct { var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme -func (c *Conn) makeClientHello() (*clientHelloMsg, clientKeySharePrivate, error) { +func (c *Conn) makeClientHello(minVersion uint16) (*clientHelloMsg, clientKeySharePrivate, error) { config := c.config if len(config.ServerName) == 0 && !config.InsecureSkipVerify { return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config") @@ -58,7 +58,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, clientKeySharePrivate, error) return nil, nil, errors.New("tls: NextProtos values too large") } - supportedVersions := config.supportedVersions(roleClient) + supportedVersions := config.supportedVersionsFromMin(roleClient, minVersion) if len(supportedVersions) == 0 { return nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion") } @@ -201,13 +201,30 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { // need to be reset. c.didResume = false - hello, ecdheKey, err := c.makeClientHello() + // Determine the minimum required version for this handshake. + minVersion := c.config.MinVersion + if c.config.echCanOffer() { + // If the ECH extension will be offered in this handshake, then the + // ClientHelloInner must not offer TLS 1.2 or below. + minVersion = VersionTLS13 + } + + helloBase, ecdheKey, err := c.makeClientHello(minVersion) if err != nil { return err } - c.serverName = hello.serverName - session, earlySecret, binderKey, err := c.loadSession(hello) + hello, helloInner, err := c.echOfferOrGrease(helloBase) + if err != nil { + return err + } + + helloResumed := hello + if c.ech.offered { + helloResumed = helloInner + } + + session, earlySecret, binderKey, err := c.loadSession(helloResumed) if err != nil { return err } @@ -277,6 +294,7 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { ctx: ctx, serverHello: serverHello, hello: hello, + helloInner: helloInner, keySharePrivate: ecdheKey, session: session, earlySecret: earlySecret, @@ -288,6 +306,7 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { return hs.handshake() } + c.serverName = hello.serverName hs := &clientHandshakeState{ c: c, ctx: ctx, @@ -305,7 +324,7 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { func (c *Conn) loadSession(hello *clientHelloMsg) ( session *SessionState, earlySecret, binderKey []byte, err error) { - if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil { + if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil || c.config.ECHEnabled { return nil, nil, nil, nil } @@ -1018,10 +1037,14 @@ func (c *Conn) verifyServerCertificate(certificates [][]byte) error { } if !c.config.InsecureSkipVerify { + dnsName := c.config.ServerName + if c.ech.offered && !c.ech.accepted { + dnsName = c.serverName + } opts := x509.VerifyOptions{ Roots: c.config.RootCAs, CurrentTime: c.config.time(), - DNSName: c.config.ServerName, + DNSName: dnsName, Intermediates: x509.NewCertPool(), } diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go index 6a7c526504a..eac73fcc685 100644 --- a/src/crypto/tls/handshake_client_tls13.go +++ b/src/crypto/tls/handshake_client_tls13.go @@ -11,6 +11,7 @@ import ( "crypto/ecdh" "crypto/hmac" "crypto/rsa" + "crypto/subtle" "errors" "fmt" "hash" @@ -22,6 +23,7 @@ type clientHandshakeStateTLS13 struct { ctx context.Context serverHello *serverHelloMsg hello *clientHelloMsg + helloInner *clientHelloMsg keySharePrivate clientKeySharePrivate session *SessionState @@ -29,13 +31,14 @@ type clientHandshakeStateTLS13 struct { binderKey []byte selectedGroup CurveID - certReq *certificateRequestMsgTLS13 - usingPSK bool - sentDummyCCS bool - suite *cipherSuiteTLS13 - transcript hash.Hash - masterSecret []byte - trafficSecret []byte // client_application_traffic_secret_0 + certReq *certificateRequestMsgTLS13 + usingPSK bool + sentDummyCCS bool + suite *cipherSuiteTLS13 + transcript hash.Hash + transcriptInner hash.Hash + masterSecret []byte + trafficSecret []byte // client_application_traffic_secret_0 hsTimings CFEventTLS13ClientHandshakeTimingInfo } @@ -114,6 +117,18 @@ func (hs *clientHandshakeStateTLS13) handshake() error { return err } + // When offering ECH, we don't know whether ECH was accepted or rejected + // until we get the server's response. Compute the transcript of both the + // inner and outer handshake until we know. + if c.ech.offered { + hs.transcriptInner = hs.suite.hash.New() + helloInnerMarshalled, err := hs.helloInner.marshal() + if err != nil { + return fmt.Errorf("tls: ech: helloInner.marshal(): %w", err) + } + hs.transcriptInner.Write(helloInnerMarshalled) + } + if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) { if err := hs.sendDummyChangeCipherSpec(); err != nil { return err @@ -123,10 +138,45 @@ func (hs *clientHandshakeStateTLS13) handshake() error { } } + // Check for ECH acceptance confirmation. + if c.ech.offered { + echAcceptConfTranscript := cloneHash(hs.transcriptInner, hs.suite.hash) + if echAcceptConfTranscript == nil { + c.sendAlert(alertInternalError) + return errors.New("tls: internal error: failed to clone hash") + } + + sh, err := hs.serverHello.marshal() + if err != nil { + return fmt.Errorf("tls: ech: hs.serverHello.marshal(): %w", err) + } + echAcceptConfTranscript.Write(sh[:30]) + echAcceptConfTranscript.Write(zeros[:8]) + echAcceptConfTranscript.Write(sh[38:]) + echAcceptConf := hs.suite.expandLabel( + hs.suite.extract(hs.helloInner.random, nil), + echAcceptConfLabel, + echAcceptConfTranscript.Sum(nil), + 8) + + if subtle.ConstantTimeCompare(hs.serverHello.random[24:], echAcceptConf) == 1 { + c.ech.accepted = true + hs.hello = hs.helloInner + hs.transcript = hs.transcriptInner + } + } + if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil { return err } + // Resolve the server name now that ECH acceptance has been determined. + // + // NOTE(cjpatton): Currently the client sends the same ALPN extension in the + // ClientHelloInner and ClientHelloOuter. If that changes, then we'll need + // to resolve ALPN here as well. + c.serverName = hs.hello.serverName + c.buffering = true if err := hs.processServerHello(); err != nil { return err @@ -152,6 +202,9 @@ func (hs *clientHandshakeStateTLS13) handshake() error { if err := hs.sendClientFinished(); err != nil { return err } + if err := hs.abortIfRequired(); err != nil { + return err + } if _, err := c.flush(); err != nil { return err } @@ -250,6 +303,50 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { return err } + // Determine which ClientHello message was consumed by the server. If ECH + // was offered, this may be the ClientHelloInner or ClientHelloOuter. + hello := hs.hello + isInner := false + if c.ech.offered { + chHash = hs.transcriptInner.Sum(nil) + hs.transcriptInner.Reset() + hs.transcriptInner.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) + hs.transcriptInner.Write(chHash) + + // Check for ECH acceptance confirmation. + serverHelloMarshalled, err := hs.serverHello.marshal() + if err != nil { + return fmt.Errorf("tls: serverHello.marshal(): %w", err) + } + if hs.serverHello.ech != nil { + if len(hs.serverHello.ech) != 8 { + c.sendAlert(alertDecodeError) + return errors.New("tls: ech: hrr: malformed acceptance signal") + } + + echAcceptConfHRRTranscript := cloneHash(hs.transcriptInner, hs.suite.hash) + if echAcceptConfHRRTranscript == nil { + c.sendAlert(alertInternalError) + return errors.New("tls: internal error: failed to clone hash") + } + + echAcceptConfHRR := echEncodeAcceptConfHelloRetryRequest(serverHelloMarshalled) + echAcceptConfHRRTranscript.Write(echAcceptConfHRR) + echAcceptConfHRRSignal := hs.suite.expandLabel( + hs.suite.extract(hs.helloInner.random, nil), + echAcceptConfHRRLabel, + echAcceptConfHRRTranscript.Sum(nil), + 8) + + if subtle.ConstantTimeCompare(hs.serverHello.ech, echAcceptConfHRRSignal) == 1 { + hello = hs.helloInner + isInner = true + } + } + + hs.transcriptInner.Write(serverHelloMarshalled) + } + // The only HelloRetryRequest extensions we support are key_share and // cookie, and clients must abort the handshake if the HRR would not result // in any change in the ClientHello. @@ -259,7 +356,7 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { } if hs.serverHello.cookie != nil { - hs.hello.cookie = hs.serverHello.cookie + hello.cookie = hs.serverHello.cookie } if hs.serverHello.serverShare.group != 0 { @@ -272,7 +369,7 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { // share for it this time. if curveID := hs.serverHello.selectedGroup; curveID != 0 { curveOK := false - for _, id := range hs.hello.supportedCurves { + for _, id := range hello.supportedCurves { if id == curveID { curveOK = true break @@ -300,7 +397,7 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { scheme.Name(), err) } hs.keySharePrivate = sk - hs.hello.keyShares = []keyShare{{group: curveID, data: packedPk}} + hello.keyShares = []keyShare{{group: curveID, data: packedPk}} } else { if _, ok := curveForCurveID(curveID); !ok { c.sendAlert(alertInternalError) @@ -312,12 +409,12 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { return err } hs.keySharePrivate = key - hs.hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}} + hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}} } } - hs.hello.raw = nil - if len(hs.hello.pskIdentities) > 0 { + hello.raw = nil + if len(hello.pskIdentities) > 0 { pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite) if pskSuite == nil { return c.sendAlert(alertInternalError) @@ -325,7 +422,7 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { if pskSuite.hash == hs.suite.hash { // Update binders and obfuscated_ticket_age. ticketAge := c.config.time().Sub(time.Unix(int64(hs.session.createdAt), 0)) - hs.hello.pskIdentities[0].obfuscatedTicketAge = uint32(ticketAge/time.Millisecond) + hs.session.ageAdd + hello.pskIdentities[0].obfuscatedTicketAge = uint32(ticketAge/time.Millisecond) + hs.session.ageAdd transcript := hs.suite.hash.New() transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))}) @@ -333,28 +430,80 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { if err := transcriptMsg(hs.serverHello, transcript); err != nil { return err } - helloBytes, err := hs.hello.marshalWithoutBinders() + helloBytes, err := hello.marshalWithoutBinders() if err != nil { return err } transcript.Write(helloBytes) pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)} - if err := hs.hello.updateBinders(pskBinders); err != nil { + if err := hello.updateBinders(pskBinders); err != nil { return err } } else { // Server selected a cipher suite incompatible with the PSK. - hs.hello.pskIdentities = nil - hs.hello.pskBinders = nil + hello.pskIdentities = nil + hello.pskBinders = nil } } - if hs.hello.earlyData { - hs.hello.earlyData = false + if hello.earlyData { + hello.earlyData = false c.quicRejectedEarlyData() } - if _, err := hs.c.writeHandshakeRecord(hs.hello, hs.transcript); err != nil { + if isInner { + hs.helloInner = hello + helloInnerMarshalled, err := hs.helloInner.marshal() + if err != nil { + return fmt.Errorf("tls: ech: helloInner.marshal(): %w", err) + } + hs.transcriptInner.Write(helloInnerMarshalled) + if err := c.echUpdateClientHelloOuter(hs.hello, hs.helloInner, nil); err != nil { + return err + } + } else { + hs.hello = hello + } + + if c.ech.offered && testingECHIllegalHandleAfterHRR { + hs.hello.raw = nil + + // Change the cipher suite and config id and set an encapsulated key in + // the updated ClientHello. This will trigger a server abort because the + // cipher suite and config id are supposed to match the previous + // ClientHello and the encapsulated key is supposed to be empty. + var ech echClientOuter + _, kdf, aead := c.ech.sealer.Suite().Params() + ech.handle.suite.kdfId = uint16(kdf) ^ 0xff + ech.handle.suite.aeadId = uint16(aead) ^ 0xff + ech.handle.configId = c.ech.configId ^ 0xff + ech.handle.enc = []byte{1, 2, 3, 4, 5} + ech.payload = []byte{1, 2, 3, 4, 5} + hs.hello.ech = ech.marshal() + } + + if testingECHTriggerBypassAfterHRR { + hs.hello.raw = nil + + // Don't send the ECH extension in the updated ClientHello. This will + // trigger a server abort, since this is illegal. + hs.hello.ech = nil + } + + if testingECHTriggerBypassBeforeHRR { + hs.hello.raw = nil + + // Send a dummy ECH extension in the updated ClientHello. This will + // trigger a server abort, since no ECH extension was sent in the + // previous ClientHello. + var err error + hs.hello.ech, err = echGenerateGreaseExt(c.config.rand()) + if err != nil { + return fmt.Errorf("tls: ech: failed to generate grease ECH: %s", err) + } + } + + if _, err := c.writeHandshakeRecord(hs.hello, nil); err != nil { return err } @@ -375,6 +524,11 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { return err } + helloMarshalled, err := hs.hello.marshal() + if err != nil { + return fmt.Errorf("tls: hello.marshal(): %w", err) + } + hs.transcript.Write(helloMarshalled) return nil } @@ -417,6 +571,16 @@ func (hs *clientHandshakeStateTLS13) processServerHello() error { return nil } + // Per the rules of draft-ietf-tls-esni-13, Section 6.1, the server is not + // permitted to resume a connection connection in the outer handshake. If + // ECH is rejected and the client-facing server replies with a + // "pre_shared_key" extension in its ServerHello, then the client MUST abort + // the handshake with an "illegal_parameter" alert. + if c.ech.offered && !c.ech.accepted { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: ech: client-facing server offered PSK after ECH rejection") + } + if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) { c.sendAlert(alertIllegalParameter) return errors.New("tls: server selected an invalid PSK") @@ -563,6 +727,22 @@ func (hs *clientHandshakeStateTLS13) readServerParameters() error { return errors.New("tls: server accepted 0-RTT with the wrong ALPN") } } + if c.ech.offered && len(encryptedExtensions.ech) > 0 { + if !c.ech.accepted { + // If the server rejects ECH, then it may send retry configurations. + // If present, we must check them for syntactic correctness and + // abort if they are not correct. + c.ech.retryConfigs = encryptedExtensions.ech + if _, err = UnmarshalECHConfigs(c.ech.retryConfigs); err != nil { + c.sendAlert(alertDecodeError) + return fmt.Errorf("tls: ech: failed to parse retry configs: %s", err) + } + } else { + // Retry configs must not be sent in the inner handshake. + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: ech: got retry configs after ECH acceptance") + } + } hs.hsTimings.ReadEncryptedExtensions = hs.hsTimings.elapsedTime() @@ -880,7 +1060,7 @@ func (hs *clientHandshakeStateTLS13) sendClientFinished() error { c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret) - if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil { + if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil && !c.config.ECHEnabled { c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret, resumptionLabel, hs.transcript) } @@ -901,7 +1081,7 @@ func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error { return errors.New("tls: received new session ticket from a client") } - if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil { + if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil || c.config.ECHEnabled { return nil } @@ -946,3 +1126,13 @@ func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error { return nil } + +func (hs *clientHandshakeStateTLS13) abortIfRequired() error { + c := hs.c + if c.ech.offered && !c.ech.accepted { + // If ECH was rejected, then abort the handshake. + c.sendAlert(alertECHRequired) + return errors.New("tls: ech: rejected") + } + return nil +} diff --git a/src/crypto/tls/handshake_messages.go b/src/crypto/tls/handshake_messages.go index 23b854bf173..40c54cca58b 100644 --- a/src/crypto/tls/handshake_messages.go +++ b/src/crypto/tls/handshake_messages.go @@ -97,6 +97,7 @@ type clientHelloMsg struct { pskIdentities []pskIdentity pskBinders [][]byte quicTransportParameters []byte + ech []byte } func (m *clientHelloMsg) marshal() ([]byte, error) { @@ -105,6 +106,13 @@ func (m *clientHelloMsg) marshal() ([]byte, error) { } var exts cryptobyte.Builder + if len(m.ech) > 0 { + // draft-ietf-tls-esni-13, "encrypted_client_hello" + exts.AddUint16(extensionECH) + exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { + exts.AddBytes(m.ech) + }) + } if len(m.serverName) > 0 { // RFC 6066, Section 3 exts.AddUint16(extensionServerName) @@ -437,6 +445,12 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { seenExts[extension] = true switch extension { + case extensionECH: + // draft-ietf-tls-esni-13, "encrypted_client_hello" + if len(extData) == 0 || + !extData.ReadBytes(&m.ech, len(extData)) { + return false + } case extensionServerName: // RFC 6066, Section 3 var nameList cryptobyte.String @@ -677,6 +691,7 @@ type serverHelloMsg struct { // HelloRetryRequest extensions cookie []byte selectedGroup CurveID + ech []byte } func (m *serverHelloMsg) marshal() ([]byte, error) { @@ -771,6 +786,13 @@ func (m *serverHelloMsg) marshal() ([]byte, error) { }) }) } + if len(m.ech) > 0 { + // draft-ietf-tls-esni-13, "encrypted_client_hello" + exts.AddUint16(extensionECH) + exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) { + exts.AddBytes(m.ech) + }) + } extBytes, err := exts.Bytes() if err != nil { @@ -904,6 +926,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool { len(m.supportedPoints) == 0 { return false } + case extensionECH: + // draft-ietf-tls-esni-13, "encrypted_client_hello" + if !extData.ReadBytes(&m.ech, len(extData)) { + return false + } default: // Ignore unknown extensions. continue @@ -922,6 +949,7 @@ type encryptedExtensionsMsg struct { alpnProtocol string quicTransportParameters []byte earlyData bool + ech []byte } func (m *encryptedExtensionsMsg) marshal() ([]byte, error) { @@ -955,6 +983,15 @@ func (m *encryptedExtensionsMsg) marshal() ([]byte, error) { b.AddUint16(extensionEarlyData) b.AddUint16(0) // empty extension_data } + if len(m.ech) > 0 { + // draft-ietf-tls-esni-13, "encrypted_client_hello" + b.AddUint16(extensionECH) + b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + // If the client-facing server rejects ECH, then it may + // sends retry configurations here. + b.AddBytes(m.ech) + }) + } }) }) @@ -1001,6 +1038,11 @@ func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool { case extensionEarlyData: // RFC 8446, Section 4.2.10 m.earlyData = true + case extensionECH: + // draft-ietf-tls-esni-13 + if !extData.ReadBytes(&m.ech, len(extData)) { + return false + } default: // Ignore unknown extensions. continue diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index f01fdf81c8c..2182a438c97 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -145,6 +145,15 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) { return nil, unexpectedMessageError(clientHello, msg) } + // NOTE(cjpatton): ECH usage is resolved before calling GetConfigForClient() + // or GetCertifciate(). Hence, it is not currently possible to reject ECH if + // we don't recognize the inner SNI. This may or may not be desirable in the + // future. + clientHello, err = c.echAcceptOrReject(clientHello, false) // afterHRR == false + if err != nil { + return nil, fmt.Errorf("tls: %s", err) // Alert sent. + } + var configForClient *Config originalConfig := c.config if c.config.GetConfigForClient != nil { @@ -416,7 +425,7 @@ func (hs *serverHandshakeState) cipherSuiteOk(c *cipherSuite) bool { func (hs *serverHandshakeState) checkForResumption() error { c := hs.c - if c.config.SessionTicketsDisabled { + if c.config.SessionTicketsDisabled || c.config.ECHEnabled { return nil } @@ -551,7 +560,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { hs.hello.ocspStapling = true } - hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled + hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled && !c.config.ECHEnabled hs.hello.cipherSuite = hs.suite.id hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite) diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go index 6ad7aa09c08..f65a4edf34a 100644 --- a/src/crypto/tls/handshake_server_tls13.go +++ b/src/crypto/tls/handshake_server_tls13.go @@ -47,6 +47,10 @@ type serverHandshakeStateTLS13 struct { hsTimings CFEventTLS13ServerHandshakeTimingInfo } +func (hs *serverHandshakeStateTLS13) echIsInner() bool { + return len(hs.clientHello.ech) == 1 && hs.clientHello.ech[0] == echClientHelloInnerVariant +} + // processDelegatedCredentialFromClient unmarshals the DelegatedCredential // offered by the client (if present) and validates it using the peer's // certificate. @@ -222,12 +226,42 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error { hs.hello.cipherSuite = hs.suite.id hs.transcript = hs.suite.hash.New() + // Resolve the server's preference for the ECDHE group. + supportedCurves := c.config.curvePreferences() + if testingTriggerHRR { + // A HelloRetryRequest (HRR) is sent if the client does not offer a key + // share for a curve supported by the server. To trigger this condition + // intentionally, we compute the set of ECDHE groups supported by both + // the client and server but for which the client did not offer a key + // share. + m := make(map[CurveID]bool) + for _, serverGroup := range c.config.curvePreferences() { + for _, clientGroup := range hs.clientHello.supportedCurves { + if clientGroup == serverGroup { + m[clientGroup] = true + } + } + } + for _, ks := range hs.clientHello.keyShares { + delete(m, ks.group) + } + supportedCurves = nil + for group := range m { + supportedCurves = append(supportedCurves, group) + } + if len(supportedCurves) == 0 { + // This occurs if the client offered a key share for each mutually + // supported group. + panic("failed to trigger HelloRetryRequest") + } + } + // Pick the ECDHE group in server preference order, but give priority to // groups with a key share, to avoid a HelloRetryRequest round-trip. var selectedGroup CurveID var clientKeyShare *keyShare GroupSelection: - for _, preferredGroup := range c.config.curvePreferences() { + for _, preferredGroup := range supportedCurves { for _, ks := range hs.clientHello.keyShares { if ks.group == preferredGroup { selectedGroup = ks.group @@ -326,7 +360,7 @@ GroupSelection: func (hs *serverHandshakeStateTLS13) checkForResumption() error { c := hs.c - if c.config.SessionTicketsDisabled { + if c.config.SessionTicketsDisabled || c.config.ECHEnabled { return nil } @@ -605,6 +639,42 @@ func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) selectedGroup: selectedGroup, } + // Decide whether to send "encrypted_client_hello" extension. + if hs.echIsInner() { + // Confirm ECH acceptance if this is the inner handshake. + echAcceptConfHRRTranscript := cloneHash(hs.transcript, hs.suite.hash) + if echAcceptConfHRRTranscript == nil { + c.sendAlert(alertInternalError) + return errors.New("tls: internal error: failed to clone hash") + } + + helloRetryRequest.ech = zeros[:8] + echAcceptConfHRR, err := helloRetryRequest.marshal() + if err != nil { + return fmt.Errorf("tls: ech: HRR.marshal(): %w", err) + } + echAcceptConfHRRTranscript.Write(echAcceptConfHRR) + echAcceptConfHRRSignal := hs.suite.expandLabel( + hs.suite.extract(hs.clientHello.random, nil), + echAcceptConfHRRLabel, + echAcceptConfHRRTranscript.Sum(nil), + 8) + + helloRetryRequest.ech = echAcceptConfHRRSignal + helloRetryRequest.raw = nil + } else if c.ech.greased { + // draft-ietf-tls-esni-13, Section 7.1: + // + // If sending a HelloRetryRequest, the server MAY include an + // "encrypted_client_hello" extension with a payload of 8 random bytes; + // see Section 10.9.4 for details. + helloRetryRequest.ech = make([]byte, 8) + if _, err := io.ReadFull(c.config.rand(), helloRetryRequest.ech); err != nil { + c.sendAlert(alertInternalError) + return fmt.Errorf("tls: internal error: rng failure: %s", err) + } + } + if _, err := hs.c.writeHandshakeRecord(helloRetryRequest, hs.transcript); err != nil { return err } @@ -625,6 +695,11 @@ func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) return unexpectedMessageError(clientHello, msg) } + clientHello, err = c.echAcceptOrReject(clientHello, true) // afterHRR == true + if err != nil { + return fmt.Errorf("tls: %s", err) // Alert sent + } + if len(clientHello.keyShares) != 1 || clientHello.keyShares[0].group != selectedGroup { c.sendAlert(alertIllegalParameter) return errors.New("tls: client sent invalid key share in second ClientHello") @@ -712,6 +787,40 @@ func illegalClientHelloChange(ch, ch1 *clientHelloMsg) bool { func (hs *serverHandshakeStateTLS13) sendServerParameters() error { c := hs.c + // Confirm ECH acceptance. + if hs.echIsInner() { + // Clear the last 8 bytes of the ServerHello.random in preparation for + // computing the confirmation hint. + copy(hs.hello.random[24:], zeros[:8]) + + // Set the last 8 bytes of ServerHello.random to a string derived from + // the inner handshake. + echAcceptConfTranscript := cloneHash(hs.transcript, hs.suite.hash) + if echAcceptConfTranscript == nil { + c.sendAlert(alertInternalError) + return errors.New("tls: internal error: failed to clone hash") + } + chMarshalled, err := hs.clientHello.marshal() + if err != nil { + return fmt.Errorf("tls: ech: clientHello.marshal(): %w", err) + } + echAcceptConfTranscript.Write(chMarshalled) + hMarshalled, err := hs.hello.marshal() + if err != nil { + return fmt.Errorf("tls: ech: hello.marshal(): %w", err) + } + echAcceptConfTranscript.Write(hMarshalled) + + echAcceptConf := hs.suite.expandLabel( + hs.suite.extract(hs.clientHello.random, nil), + echAcceptConfLabel, + echAcceptConfTranscript.Sum(nil), + 8) + + copy(hs.hello.random[24:], echAcceptConf) + hs.hello.raw = nil + } + if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil { return err } @@ -770,6 +879,10 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error { encryptedExtensions.earlyData = hs.earlyData } + if !c.ech.accepted && len(c.ech.retryConfigs) > 0 { + encryptedExtensions.ech = c.ech.retryConfigs + } + if _, err := hs.c.writeHandshakeRecord(encryptedExtensions, hs.transcript); err != nil { return err } @@ -915,7 +1028,7 @@ func (hs *serverHandshakeStateTLS13) sendServerFinished() error { } func (hs *serverHandshakeStateTLS13) shouldSendSessionTickets() bool { - if hs.c.config.SessionTicketsDisabled { + if hs.c.config.SessionTicketsDisabled || hs.c.config.ECHEnabled { return false } diff --git a/src/crypto/tls/hpke.go b/src/crypto/tls/hpke.go new file mode 100644 index 00000000000..b3861d0951b --- /dev/null +++ b/src/crypto/tls/hpke.go @@ -0,0 +1,42 @@ +// Copyright 2020 Cloudflare, Inc. All rights reserved. Use of this source code +// is governed by a BSD-style license that can be found in the LICENSE file. + +package tls + +import ( + "errors" + "fmt" + + "github.com/cloudflare/circl/hpke" +) + +// The mandatory-to-implement HPKE cipher suite for use with the ECH extension. +var defaultHPKESuite hpke.Suite + +func init() { + var err error + defaultHPKESuite, err = hpkeAssembleSuite( + uint16(hpke.KEM_X25519_HKDF_SHA256), + uint16(hpke.KDF_HKDF_SHA256), + uint16(hpke.AEAD_AES128GCM), + ) + if err != nil { + panic(fmt.Sprintf("hpke: mandatory-to-implement cipher suite not supported: %s", err)) + } +} + +func hpkeAssembleSuite(kemId, kdfId, aeadId uint16) (hpke.Suite, error) { + kem := hpke.KEM(kemId) + if !kem.IsValid() { + return hpke.Suite{}, errors.New("KEM is not supported") + } + kdf := hpke.KDF(kdfId) + if !kdf.IsValid() { + return hpke.Suite{}, errors.New("KDF is not supported") + } + aead := hpke.AEAD(aeadId) + if !aead.IsValid() { + return hpke.Suite{}, errors.New("AEAD is not supported") + } + return hpke.NewSuite(kem, kdf, aead), nil +} diff --git a/src/crypto/tls/tls.go b/src/crypto/tls/tls.go index 668bb807a76..a4826be91a6 100644 --- a/src/crypto/tls/tls.go +++ b/src/crypto/tls/tls.go @@ -5,6 +5,14 @@ // Package tls partially implements TLS 1.2, as specified in RFC 5246, // and TLS 1.3, as specified in RFC 8446. // +// This package implements the "Encrypted ClientHello (ECH)" extension, as +// specified by draft-ietf-tls-esni-13. This extension allows the client to +// encrypt its ClientHello to the public key of an ECH-service provider, known +// as the client-facing server. If successful, then the client-facing server +// forwards the decrypted ClientHello to the intended recipient, known as the +// backend server. The goal of this mechanism is to ensure that connections made +// to backend servers are indistinguishable from one another. +// // This package implements the "Delegated Credentials" extension, as // specified by draft-ietf-tls-subcerts-10. This extension allows the usage // of a limited delegation mechanism that allows a TLS peer to issue its own @@ -20,6 +28,25 @@ // valid for a short time (days, hours, or even minutes). package tls +// BUG(cjpatton): In order to achieve its security goal, the ECH extension +// requires padding in order to ensure that the length of handshake messages +// doesn't depend on who terminates the connection. This package does not yet +// implement server-side padding: see +// https://github.com/tlswg/draft-ietf-tls-esni/issues/264. + +// BUG(cjpatton): The interaction of the ECH extension with PSK has not yet been +// fully vetted. For now, the server disables session tickets if ECH is enabled. + +// BUG(cjpatton): Upon ECH rejection, if retry configurations are provided, then +// the client is expected to retry the connection. Otherwise, it may regard ECH +// as being securely disabled by the client-facing server. The client in this +// package does not attempt to retry the handshake. + +// BUG(cjpatton): If the client offers the ECH extension and the client-facing +// server rejects it, then only the client-facing server is authenticated. In +// particular, the client is expected to respond to a CertificateRequest with an +// empty certificate. This package does not yet implement this behavior. + // BUG(agl): The crypto/tls package only implements some countermeasures // against Lucky13 attacks on CBC-mode encryption, and only on SHA1 // variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and diff --git a/src/crypto/tls/tls_cf_test.go b/src/crypto/tls/tls_cf_test.go new file mode 100644 index 00000000000..3106795d5a3 --- /dev/null +++ b/src/crypto/tls/tls_cf_test.go @@ -0,0 +1,54 @@ +package tls + +import ( + "crypto/rand" + "testing" + + "github.com/cloudflare/circl/hpke" +) + +// If the client uses the wrong KEM algorithm to offer ECH, the ECH provider +// should reject rather than abort. We check for this condition by looking at +// the error returned by hpke.Receiver.Setup(). This test asserts that the +// CIRCL's HPKE implementation returns the error we expect. +func TestCirclHpkeKemAlgorithmMismatchError(t *testing.T) { + kem := hpke.KEM_P256_HKDF_SHA256 + kdf := hpke.KDF_HKDF_SHA256 + aead := hpke.AEAD_AES128GCM + suite := hpke.NewSuite(kem, kdf, aead) + _, sk, err := kem.Scheme().GenerateKeyPair() + if err != nil { + t.Fatal(err) + } + + incorrectKEM := hpke.KEM_X25519_HKDF_SHA256 + incorrectSuite := hpke.NewSuite(incorrectKEM, kdf, aead) + incorrectPK, _, err := incorrectKEM.Scheme().GenerateKeyPair() + if err != nil { + t.Fatal(err) + } + + // Generate an encapsulated key share with the incorrect KEM algorithm. + incorrectSender, err := incorrectSuite.NewSender(incorrectPK, []byte("some info string")) + if err != nil { + t.Fatal(err) + } + incorrectEnc, _, err := incorrectSender.Setup(rand.Reader) + if err != nil { + t.Fatal(err) + } + + // Attempt to parse an encapsulated key generated using the incorrect KEM + // algorithm. + receiver, err := suite.NewReceiver(sk, []byte("some info string")) + if err != nil { + t.Fatal(err) + } + + expectedErrorString := "hpke: invalid KEM public key" + if _, err := receiver.Setup(incorrectEnc); err == nil { + t.Errorf("expected error; got success") + } else if err.Error() != expectedErrorString { + t.Errorf("incorrect error string: got '%s'; want '%s'", err, expectedErrorString) + } +} diff --git a/src/crypto/tls/tls_test.go b/src/crypto/tls/tls_test.go index 5d1b42f21fc..64d2a86084b 100644 --- a/src/crypto/tls/tls_test.go +++ b/src/crypto/tls/tls_test.go @@ -830,7 +830,7 @@ func TestCloneNonFuncFields(t *testing.T) { switch fn := typ.Field(i).Name; fn { case "Rand": f.Set(reflect.ValueOf(io.Reader(os.Stdin))) - case "Time", "GetCertificate", "GetConfigForClient", "VerifyPeerCertificate", "VerifyConnection", "GetClientCertificate", "WrapSession", "UnwrapSession": + case "Time", "GetCertificate", "GetConfigForClient", "VerifyPeerCertificate", "VerifyConnection", "GetClientCertificate", "WrapSession", "UnwrapSession", "ServerECHProvider": // DeepEqual can't compare functions. If you add a // function field to this list, you must also change // TestCloneFuncFields to ensure that the func field is @@ -871,6 +871,10 @@ func TestCloneNonFuncFields(t *testing.T) { f.Set(reflect.ValueOf(RenegotiateOnceAsClient)) case "mutex", "autoSessionTicketKeys", "sessionTicketKeys": continue // these are unexported fields that are handled separately + case "ClientECHConfigs": + f.Set(reflect.ValueOf([]ECHConfig{ECHConfig{}})) + case "ECHEnabled": + f.Set(reflect.ValueOf(true)) default: t.Errorf("all fields must be accounted for, but saw unknown field %q", fn) } diff --git a/src/vendor/github.com/cloudflare/circl/ecc/p384/LICENSE b/src/vendor/github.com/cloudflare/circl/ecc/p384/LICENSE new file mode 100644 index 00000000000..4e5596db89d --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/ecc/p384/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2018, Brendan McMillion. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/vendor/github.com/cloudflare/circl/ecc/p384/arith.go b/src/vendor/github.com/cloudflare/circl/ecc/p384/arith.go new file mode 100644 index 00000000000..1b742de871a --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/ecc/p384/arith.go @@ -0,0 +1,149 @@ +//go:build (!noasm && arm64) || (!noasm && amd64) +// +build !noasm,arm64 !noasm,amd64 + +package p384 + +import ( + "math/big" + + "github.com/cloudflare/circl/internal/conv" +) + +const sizeFp = 48 + +type fp384 [sizeFp]byte + +func (e fp384) BigInt() *big.Int { return conv.BytesLe2BigInt(e[:]) } +func (e fp384) String() string { return conv.BytesLe2Hex(e[:]) } + +func (e *fp384) SetBigInt(b *big.Int) { + if b.BitLen() > 384 || b.Sign() < 0 { + b = new(big.Int).Mod(b, p.BigInt()) + } + conv.BigInt2BytesLe(e[:], b) +} + +func montEncode(c, a *fp384) { fp384Mul(c, a, &r2) } +func montDecode(c, a *fp384) { fp384Mul(c, a, &fp384{1}) } +func fp384Sqr(c, a *fp384) { fp384Mul(c, a, a) } + +func fp384Inv(z, x *fp384) { + t0, t1, t2, t3, t4 := &fp384{}, &fp384{}, &fp384{}, &fp384{}, &fp384{} + /* alpha_1 */ + fp384Sqr(t4, x) + /* alpha_2 */ + fp384Mul(t4, t4, x) + /* alpha_3 */ + fp384Sqr(t0, t4) + fp384Mul(t0, t0, x) + /* alpha_6 */ + fp384Sqr(t1, t0) + fp384Sqr(t1, t1) + fp384Sqr(t1, t1) + fp384Mul(t1, t1, t0) + /* alpha_12 */ + fp384Sqr(t2, t1) + for i := 0; i < 5; i++ { + fp384Sqr(t2, t2) + } + fp384Mul(t2, t2, t1) + /* alpha_15 */ + for i := 0; i < 3; i++ { + fp384Sqr(t2, t2) + } + fp384Mul(t2, t2, t0) + /* alpha_30 */ + fp384Sqr(t1, t2) + for i := 0; i < 14; i++ { + fp384Sqr(t1, t1) + } + fp384Mul(t1, t1, t2) + /* alpha_60 */ + fp384Sqr(t3, t1) + for i := 0; i < 29; i++ { + fp384Sqr(t3, t3) + } + fp384Mul(t3, t3, t1) + /* T_3 = alpha_30^(2^2) */ + fp384Sqr(t1, t1) + fp384Sqr(t1, t1) + /* alpha_32 */ + *t0 = *t1 + fp384Mul(t0, t0, t4) + /* T_3 = a^(2^32-3) = (alpha_30)^(2^2)*alpha_1 */ + fp384Mul(t1, t1, x) + /* alpha_120 */ + fp384Sqr(t4, t3) + for i := 0; i < 59; i++ { + fp384Sqr(t4, t4) + } + fp384Mul(t4, t4, t3) + /* alpha_240 */ + fp384Sqr(t3, t4) + for i := 0; i < 119; i++ { + fp384Sqr(t3, t3) + } + fp384Mul(t3, t3, t4) + /* alpha_255 */ + for i := 0; i < 15; i++ { + fp384Sqr(t3, t3) + } + fp384Mul(t3, t3, t2) + /* T_5 = a^(2^288-2^32-1) = (alpha_255)^(2^33)*alpha_32 */ + for i := 0; i < 33; i++ { + fp384Sqr(t3, t3) + } + fp384Mul(t3, t3, t0) + /* T_1 = a^(2^384-2^128-2^96+2^32-3) = (T_1)^(2^96)*T_3 */ + fp384Sqr(t4, t3) + for i := 0; i < 95; i++ { + fp384Sqr(t4, t4) + } + fp384Mul(z, t4, t1) +} + +//go:noescape +func fp384Cmov(x, y *fp384, b int) + +//go:noescape +func fp384Neg(c, a *fp384) + +//go:noescape +func fp384Add(c, a, b *fp384) + +//go:noescape +func fp384Sub(c, a, b *fp384) + +//go:noescape +func fp384Mul(c, a, b *fp384) + +var ( + // p is the order of the base field, represented as little-endian 64-bit words. + p = fp384{ + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + } + // pp satisfies r*rp - p*pp = 1 where rp and pp are both integers. + pp = fp384{ //nolint + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, + 0xfa, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + } + // r2 is R^2 where R = 2^384 mod p. + r2 = fp384{ + 0x01, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + } + // bb is the Montgomery encoding of the curve parameter B. + bb = fp384{ + 0xcc, 0x2d, 0x41, 0x9d, 0x71, 0x88, 0x11, 0x08, 0xec, 0x32, 0x4c, 0x7a, + 0xd8, 0xad, 0x29, 0xf7, 0x2e, 0x02, 0x20, 0x19, 0x9b, 0x20, 0xf2, 0x77, + 0xe2, 0x8a, 0x93, 0x94, 0xee, 0x4b, 0x37, 0xe3, 0x94, 0x20, 0x02, 0x1f, + 0xf4, 0x21, 0x2b, 0xb6, 0xf9, 0xbf, 0x4f, 0x60, 0x4b, 0x11, 0x08, 0xcd, + } +) diff --git a/src/vendor/github.com/cloudflare/circl/ecc/p384/arith_amd64.go b/src/vendor/github.com/cloudflare/circl/ecc/p384/arith_amd64.go new file mode 100644 index 00000000000..1ea343b5daa --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/ecc/p384/arith_amd64.go @@ -0,0 +1,8 @@ +//go:build amd64 && !noasm +// +build amd64,!noasm + +package p384 + +import "golang.org/x/sys/cpu" + +var hasBMI2 = cpu.X86.HasBMI2 //nolint diff --git a/src/vendor/github.com/cloudflare/circl/ecc/p384/arith_amd64.s b/src/vendor/github.com/cloudflare/circl/ecc/p384/arith_amd64.s new file mode 100644 index 00000000000..5f53c637d06 --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/ecc/p384/arith_amd64.s @@ -0,0 +1,730 @@ +// +build amd64,!noasm + +#include "textflag.h" + +#define storeBlock(a0,a1,a2,a3,a4,a5, r) \ + MOVQ a0, 0+r \ + MOVQ a1, 8+r \ + MOVQ a2, 16+r \ + MOVQ a3, 24+r \ + MOVQ a4, 32+r \ + MOVQ a5, 40+r + +#define loadBlock(r, a0,a1,a2,a3,a4,a5) \ + MOVQ 0+r, a0 \ + MOVQ 8+r, a1 \ + MOVQ 16+r, a2 \ + MOVQ 24+r, a3 \ + MOVQ 32+r, a4 \ + MOVQ 40+r, a5 + +#define fp384Carry(a0,a1,a2,a3,a4,a5,a6, b0,b1,b2,b3,b4,b5,b6) \ + \ // b = a-p + MOVQ a0, b0 \ + MOVQ a1, b1 \ + MOVQ a2, b2 \ + MOVQ a3, b3 \ + MOVQ a4, b4 \ + MOVQ a5, b5 \ + MOVQ a6, b6 \ + \ + SUBQ ·p+0(SB), b0 \ + SBBQ ·p+8(SB), b1 \ + SBBQ ·p+16(SB), b2 \ + SBBQ ·p+24(SB), b3 \ + SBBQ ·p+32(SB), b4 \ + SBBQ ·p+40(SB), b5 \ + SBBQ $0, b6 \ + \ + \ // if b is negative then return a + \ // else return b + CMOVQCC b0, a0 \ + CMOVQCC b1, a1 \ + CMOVQCC b2, a2 \ + CMOVQCC b3, a3 \ + CMOVQCC b4, a4 \ + CMOVQCC b5, a5 + +#define mul(a0,a1,a2,a3,a4,a5, rb, stack) \ + \ // a0 + MOVQ a0, AX \ + MULQ 0+rb \ + MOVQ AX, R8 \ + MOVQ DX, R9 \ + MOVQ a0, AX \ + MULQ 8+rb \ + ADDQ AX, R9 \ + ADCQ $0, DX \ + MOVQ DX, R10 \ + MOVQ a0, AX \ + MULQ 16+rb \ + ADDQ AX, R10 \ + ADCQ $0, DX \ + MOVQ DX, R11 \ + MOVQ a0, AX \ + MULQ 24+rb \ + ADDQ AX, R11 \ + ADCQ $0, DX \ + MOVQ DX, R12 \ + MOVQ a0, AX \ + MULQ 32+rb \ + ADDQ AX, R12 \ + ADCQ $0, DX \ + MOVQ DX, R13 \ + MOVQ a0, AX \ + MULQ 40+rb \ + ADDQ AX, R13 \ + ADCQ $0, DX \ + MOVQ DX, R14 \ + \ + storeBlock(R8,R9,R10,R11,R12,R13, 0+stack) \ + MOVQ R14, 48+stack \ + \ + \ // a1 + MOVQ a1, AX \ + MULQ 0+rb \ + MOVQ AX, R8 \ + MOVQ DX, R9 \ + MOVQ a1, AX \ + MULQ 8+rb \ + ADDQ AX, R9 \ + ADCQ $0, DX \ + MOVQ DX, R10 \ + MOVQ a1, AX \ + MULQ 16+rb \ + ADDQ AX, R10 \ + ADCQ $0, DX \ + MOVQ DX, R11 \ + MOVQ a1, AX \ + MULQ 24+rb \ + ADDQ AX, R11 \ + ADCQ $0, DX \ + MOVQ DX, R12 \ + MOVQ a1, AX \ + MULQ 32+rb \ + ADDQ AX, R12 \ + ADCQ $0, DX \ + MOVQ DX, R13 \ + MOVQ a1, AX \ + MULQ 40+rb \ + ADDQ AX, R13 \ + ADCQ $0, DX \ + MOVQ DX, R14 \ + \ + ADDQ 8+stack, R8 \ + ADCQ 16+stack, R9 \ + ADCQ 24+stack, R10 \ + ADCQ 32+stack, R11 \ + ADCQ 40+stack, R12 \ + ADCQ 48+stack, R13 \ + ADCQ $0, R14 \ + storeBlock(R8,R9,R10,R11,R12,R13, 8+stack) \ + MOVQ R14, 56+stack \ + \ + \ // a2 + MOVQ a2, AX \ + MULQ 0+rb \ + MOVQ AX, R8 \ + MOVQ DX, R9 \ + MOVQ a2, AX \ + MULQ 8+rb \ + ADDQ AX, R9 \ + ADCQ $0, DX \ + MOVQ DX, R10 \ + MOVQ a2, AX \ + MULQ 16+rb \ + ADDQ AX, R10 \ + ADCQ $0, DX \ + MOVQ DX, R11 \ + MOVQ a2, AX \ + MULQ 24+rb \ + ADDQ AX, R11 \ + ADCQ $0, DX \ + MOVQ DX, R12 \ + MOVQ a2, AX \ + MULQ 32+rb \ + ADDQ AX, R12 \ + ADCQ $0, DX \ + MOVQ DX, R13 \ + MOVQ a2, AX \ + MULQ 40+rb \ + ADDQ AX, R13 \ + ADCQ $0, DX \ + MOVQ DX, R14 \ + \ + ADDQ 16+stack, R8 \ + ADCQ 24+stack, R9 \ + ADCQ 32+stack, R10 \ + ADCQ 40+stack, R11 \ + ADCQ 48+stack, R12 \ + ADCQ 56+stack, R13 \ + ADCQ $0, R14 \ + storeBlock(R8,R9,R10,R11,R12,R13, 16+stack) \ + MOVQ R14, 64+stack \ + \ + \ // a3 + MOVQ a3, AX \ + MULQ 0+rb \ + MOVQ AX, R8 \ + MOVQ DX, R9 \ + MOVQ a3, AX \ + MULQ 8+rb \ + ADDQ AX, R9 \ + ADCQ $0, DX \ + MOVQ DX, R10 \ + MOVQ a3, AX \ + MULQ 16+rb \ + ADDQ AX, R10 \ + ADCQ $0, DX \ + MOVQ DX, R11 \ + MOVQ a3, AX \ + MULQ 24+rb \ + ADDQ AX, R11 \ + ADCQ $0, DX \ + MOVQ DX, R12 \ + MOVQ a3, AX \ + MULQ 32+rb \ + ADDQ AX, R12 \ + ADCQ $0, DX \ + MOVQ DX, R13 \ + MOVQ a3, AX \ + MULQ 40+rb \ + ADDQ AX, R13 \ + ADCQ $0, DX \ + MOVQ DX, R14 \ + \ + ADDQ 24+stack, R8 \ + ADCQ 32+stack, R9 \ + ADCQ 40+stack, R10 \ + ADCQ 48+stack, R11 \ + ADCQ 56+stack, R12 \ + ADCQ 64+stack, R13 \ + ADCQ $0, R14 \ + storeBlock(R8,R9,R10,R11,R12,R13, 24+stack) \ + MOVQ R14, 72+stack \ + \ + \ // a4 + MOVQ a4, AX \ + MULQ 0+rb \ + MOVQ AX, R8 \ + MOVQ DX, R9 \ + MOVQ a4, AX \ + MULQ 8+rb \ + ADDQ AX, R9 \ + ADCQ $0, DX \ + MOVQ DX, R10 \ + MOVQ a4, AX \ + MULQ 16+rb \ + ADDQ AX, R10 \ + ADCQ $0, DX \ + MOVQ DX, R11 \ + MOVQ a4, AX \ + MULQ 24+rb \ + ADDQ AX, R11 \ + ADCQ $0, DX \ + MOVQ DX, R12 \ + MOVQ a4, AX \ + MULQ 32+rb \ + ADDQ AX, R12 \ + ADCQ $0, DX \ + MOVQ DX, R13 \ + MOVQ a4, AX \ + MULQ 40+rb \ + ADDQ AX, R13 \ + ADCQ $0, DX \ + MOVQ DX, R14 \ + \ + ADDQ 32+stack, R8 \ + ADCQ 40+stack, R9 \ + ADCQ 48+stack, R10 \ + ADCQ 56+stack, R11 \ + ADCQ 64+stack, R12 \ + ADCQ 72+stack, R13 \ + ADCQ $0, R14 \ + storeBlock(R8,R9,R10,R11,R12,R13, 32+stack) \ + MOVQ R14, 80+stack \ + \ + \ // a5 + MOVQ a5, AX \ + MULQ 0+rb \ + MOVQ AX, R8 \ + MOVQ DX, R9 \ + MOVQ a5, AX \ + MULQ 8+rb \ + ADDQ AX, R9 \ + ADCQ $0, DX \ + MOVQ DX, R10 \ + MOVQ a5, AX \ + MULQ 16+rb \ + ADDQ AX, R10 \ + ADCQ $0, DX \ + MOVQ DX, R11 \ + MOVQ a5, AX \ + MULQ 24+rb \ + ADDQ AX, R11 \ + ADCQ $0, DX \ + MOVQ DX, R12 \ + MOVQ a5, AX \ + MULQ 32+rb \ + ADDQ AX, R12 \ + ADCQ $0, DX \ + MOVQ DX, R13 \ + MOVQ a5, AX \ + MULQ 40+rb \ + ADDQ AX, R13 \ + ADCQ $0, DX \ + MOVQ DX, R14 \ + \ + ADDQ 40+stack, R8 \ + ADCQ 48+stack, R9 \ + ADCQ 56+stack, R10 \ + ADCQ 64+stack, R11 \ + ADCQ 72+stack, R12 \ + ADCQ 80+stack, R13 \ + ADCQ $0, R14 \ + storeBlock(R8,R9,R10,R11,R12,R13, 40+stack) \ + MOVQ R14, 88+stack + +#define fp384Reduce(stack) \ + \ // m = (T * P') mod R, store m in R8:R9:R10:R11:R12:R13 + MOVQ ·pp+0(SB), AX \ + MULQ 0+stack \ + MOVQ AX, R8 ; MOVQ R8, 96+stack\ + MOVQ DX, R9 \ + MOVQ ·pp+0(SB), AX \ + MULQ 8+stack \ + ADDQ AX, R9 \ + ADCQ $0, DX \ + MOVQ DX, R10 \ + MOVQ ·pp+0(SB), AX \ + MULQ 16+stack \ + ADDQ AX, R10 \ + ADCQ $0, DX \ + MOVQ DX, R11 \ + MOVQ ·pp+0(SB), AX \ + MULQ 24+stack \ + ADDQ AX, R11 \ + ADCQ $0, DX \ + MOVQ DX, R12 \ + MOVQ ·pp+0(SB), AX \ + MULQ 32+stack \ + ADDQ AX, R12 \ + ADCQ $0, DX \ + MOVQ DX, R13 \ + MOVQ ·pp+0(SB), AX \ + MULQ 40+stack \ + ADDQ AX, R13 \ + \ + ADDQ 0+stack, R9 \ + ADCQ 8+stack, R10 \ + ADCQ 16+stack, R11 \ + ADCQ 24+stack, R12 \ + ADCQ 32+stack, R13 \ + \ + MOVQ ·pp+16(SB), AX \ + MULQ 0+stack \ + MOVQ AX, R14 \ + MOVQ DX, R8 \ + MOVQ ·pp+16(SB), AX \ + MULQ 8+stack \ + ADDQ AX, R8 \ + ADCQ $0, DX \ + MOVQ DX, BX \ + MOVQ ·pp+16(SB), AX \ + MULQ 16+stack \ + ADDQ AX, BX \ + ADCQ $0, DX \ + MOVQ DX, CX \ + MOVQ ·pp+16(SB), AX \ + MULQ 24+stack \ + ADDQ AX, CX \ + \ + ADDQ R14, R10 \ + ADCQ R8, R11 \ + ADCQ BX, R12 \ + ADCQ CX, R13 \ + \ + MOVQ ·pp+24(SB), AX \ + MULQ 0+stack \ + MOVQ AX, R14 \ + MOVQ DX, R8 \ + MOVQ ·pp+24(SB), AX \ + MULQ 8+stack \ + ADDQ AX, R8 \ + ADCQ $0, DX \ + MOVQ DX, BX \ + MOVQ ·pp+24(SB), AX \ + MULQ 16+stack \ + ADDQ AX, BX \ + \ + ADDQ R14, R11 \ + ADCQ R8, R12 \ + ADCQ BX, R13 \ + \ + MOVQ ·pp+32(SB), AX \ + MULQ 0+stack \ + MOVQ AX, R14 \ + MOVQ DX, R8 \ + MOVQ ·pp+32(SB), AX \ + MULQ 8+stack \ + ADDQ AX, R8 \ + \ + ADDQ R14, R12 \ + ADCQ R8, R13 \ + \ + MOVQ ·pp+40(SB), AX \ + MULQ 0+stack \ + ADDQ AX, R13 \ + \ + MOVQ 96+stack, R8 \ + \ + storeBlock(R8,R9,R10,R11,R12,R13, 96+stack) \ + \ + \ // m * P + mul(·p+0(SB),·p+8(SB),·p+16(SB),·p+24(SB),·p+32(SB),·p+40(SB), 96+stack, 144+stack) \ + \ + \ // Add the 768-bit intermediate to m*N + MOVQ $0, R15 \ + loadBlock(144+stack, R8,R9,R10,R11,R12,R13) \ + loadBlock(192+stack, R14,SI,AX,BX,CX,DX) \ + \ + ADDQ 0+stack, R8 \ + ADCQ 8+stack, R9 \ + ADCQ 16+stack, R10 \ + ADCQ 24+stack, R11 \ + ADCQ 32+stack, R12 \ + ADCQ 40+stack, R13 \ + ADCQ 48+stack, R14 \ + ADCQ 56+stack, SI \ + ADCQ 64+stack, AX \ + ADCQ 72+stack, BX \ + ADCQ 80+stack, CX \ + ADCQ 88+stack, DX \ + ADCQ $0, R15 \ + \ + fp384Carry(R14,SI,AX,BX,CX,DX,R15, R8,R9,R10,R11,R12,R13,DI) + +#define mulBMI2(a0,a1,a2,a3,a4,a5, rb, stack) \ + MOVQ a0, DX \ + MULXQ 0+rb, R8, R9; MOVQ R8, 0+stack; MOVQ $0, R8 \ + MULXQ 8+rb, AX, R10 \ + ADDQ AX, R9 \ + MULXQ 16+rb, AX, R11 \ + ADCQ AX, R10 \ + MULXQ 24+rb, AX, R12 \ + ADCQ AX, R11 \ + MULXQ 32+rb, AX, R13 \ + ADCQ AX, R12 \ + MULXQ 40+rb, AX, R14 \ + ADCQ AX, R13 \ + ADCQ $0, R14 \ + \ + MOVQ a1, DX \ + MULXQ 0+rb, AX, BX \ + ADDQ AX, R9; MOVQ R9, 8+stack; MOVL $0, R9 \ + ADCQ BX, R10 \ + MULXQ 16+rb, AX, BX \ + ADCQ AX, R11 \ + ADCQ BX, R12 \ + MULXQ 32+rb, AX, BX \ + ADCQ AX, R13 \ + ADCQ BX, R14 \ + ADCQ $0, R8 \ + MULXQ 8+rb, AX, BX \ + ADDQ AX, R10 \ + ADCQ BX, R11 \ + MULXQ 24+rb, AX, BX \ + ADCQ AX, R12 \ + ADCQ BX, R13 \ + MULXQ 40+rb, AX, BX \ + ADCQ AX, R14 \ + ADCQ BX, R8 \ + ADCQ $0, R9 \ + \ + MOVQ a2, DX \ + MULXQ 0+rb, AX, BX \ + ADDQ AX, R10; MOVQ R10, 16+stack; MOVL $0, R10 \ + ADCQ BX, R11 \ + MULXQ 16+rb, AX, BX \ + ADCQ AX, R12 \ + ADCQ BX, R13 \ + MULXQ 32+rb, AX, BX \ + ADCQ AX, R14 \ + ADCQ BX, R8 \ + ADCQ $0, R9 \ + MULXQ 8+rb, AX, BX \ + ADDQ AX, R11 \ + ADCQ BX, R12 \ + MULXQ 24+rb, AX, BX \ + ADCQ AX, R13 \ + ADCQ BX, R14 \ + MULXQ 40+rb, AX, BX \ + ADCQ AX, R8 \ + ADCQ BX, R9 \ + ADCQ $0, R10 \ + \ + MOVQ a3, DX \ + MULXQ 0+rb, AX, BX \ + ADDQ AX, R11; MOVQ R11, 24+stack; MOVL $0, R11 \ + ADCQ BX, R12 \ + MULXQ 16+rb, AX, BX \ + ADCQ AX, R13 \ + ADCQ BX, R14 \ + MULXQ 32+rb, AX, BX \ + ADCQ AX, R8 \ + ADCQ BX, R9 \ + ADCQ $0, R10 \ + MULXQ 8+rb, AX, BX \ + ADDQ AX, R12 \ + ADCQ BX, R13 \ + MULXQ 24+rb, AX, BX \ + ADCQ AX, R14 \ + ADCQ BX, R8 \ + MULXQ 40+rb, AX, BX \ + ADCQ AX, R9 \ + ADCQ BX, R10 \ + ADCQ $0, R11 \ + \ + MOVQ a4, DX \ + MULXQ 0+rb, AX, BX \ + ADDQ AX, R12; MOVQ R12, 32+stack; MOVL $0, R12 \ + ADCQ BX, R13 \ + MULXQ 16+rb, AX, BX \ + ADCQ AX, R14 \ + ADCQ BX, R8 \ + MULXQ 32+rb, AX, BX \ + ADCQ AX, R9 \ + ADCQ BX, R10 \ + ADCQ $0, R11 \ + MULXQ 8+rb, AX, BX \ + ADDQ AX, R13 \ + ADCQ BX, R14 \ + MULXQ 24+rb, AX, BX \ + ADCQ AX, R8 \ + ADCQ BX, R9 \ + MULXQ 40+rb, AX, BX \ + ADCQ AX, R10 \ + ADCQ BX, R11 \ + ADCQ $0, R12 \ + \ + MOVQ a5, DX \ + MULXQ 0+rb, AX, BX \ + ADDQ AX, R13; MOVQ R13, 40+stack \ + ADCQ BX, R14 \ + MULXQ 16+rb, AX, BX \ + ADCQ AX, R8 \ + ADCQ BX, R9 \ + MULXQ 32+rb, AX, BX \ + ADCQ AX, R10 \ + ADCQ BX, R11 \ + ADCQ $0, R12 \ + MULXQ 8+rb, AX, BX \ + ADDQ AX, R14 \ + ADCQ BX, R8 \ + MULXQ 24+rb, AX, BX \ + ADCQ AX, R9 \ + ADCQ BX, R10 \ + MULXQ 40+rb, AX, BX \ + ADCQ AX, R11 \ + ADCQ BX, R12 + +#define fp384ReduceBMI2(stack) \ + \ // m = (T * P') mod R, store m in R8:R9:R10:R11:R12:R13 + MOVQ ·pp+0(SB), DX \ + MULXQ 0+stack, R8, R9 \ + MULXQ 8+stack, AX, R10 \ + ADDQ AX, R9 \ + MULXQ 16+stack, AX, R11 \ + ADCQ AX, R10 \ + MULXQ 24+stack, AX, R12 \ + ADCQ AX, R11 \ + MULXQ 32+stack, AX, R13 \ + ADCQ AX, R12 \ + MULXQ 40+stack, AX, BX \ + ADCQ AX, R13 \ + \ + ADDQ 0+stack, R9 \ + ADCQ 8+stack, R10 \ + ADCQ 16+stack, R11 \ + ADCQ 24+stack, R12 \ + ADCQ 32+stack, R13 \ + \ + MOVQ ·pp+16(SB), DX \ + MULXQ 0+stack, AX, BX \ + ADDQ AX, R10 \ + ADCQ BX, R11 \ + MULXQ 16+stack, AX, BX \ + ADCQ AX, R12 \ + ADCQ BX, R13 \ + MULXQ 8+stack, AX, BX \ + ADDQ AX, R11 \ + ADCQ BX, R12 \ + MULXQ 24+stack, AX, BX \ + ADCQ AX, R13 \ + \ + MOVQ ·pp+24(SB), DX \ + MULXQ 0+stack, AX, BX \ + ADDQ AX, R11 \ + ADCQ BX, R12 \ + MULXQ 16+stack, AX, BX \ + ADCQ AX, R13 \ + MULXQ 8+stack, AX, BX \ + ADDQ AX, R12 \ + ADCQ BX, R13 \ + \ + MOVQ ·pp+32(SB), DX \ + MULXQ 0+stack, AX, BX \ + ADDQ AX, R12 \ + ADCQ BX, R13 \ + MULXQ 8+stack, AX, BX \ + ADDQ AX, R13 \ + \ + MOVQ ·pp+40(SB), DX \ + MULXQ 0+stack, AX, BX \ + ADDQ AX, R13 \ + \ + storeBlock(R8,R9,R10,R11,R12,R13, 96+stack) \ + \ + \ // m * P + mulBMI2(·p+0(SB),·p+8(SB),·p+16(SB),·p+24(SB),·p+32(SB),·p+40(SB), 96+stack, 144+stack) \ + \ + \ // Add the 768-bit intermediate to m*N + loadBlock(144+stack, AX,R13,BX,CX,DX,DI) \ + \ + ADDQ 0+stack, AX \ + ADCQ 8+stack, R13 \ + ADCQ 16+stack, BX \ + ADCQ 24+stack, CX \ + ADCQ 32+stack, DX \ + ADCQ 40+stack, DI \ + ADCQ 48+stack, R14 \ + ADCQ 56+stack, R8 \ + ADCQ 64+stack, R9 \ + ADCQ 72+stack, R10 \ + ADCQ 80+stack, R11 \ + ADCQ 88+stack, R12 \ + MOVQ $0, 0+stack \ + ADCQ $0, 0+stack \ + \ + fp384Carry(R14,R8,R9,R10,R11,R12, 0+stack, AX,R13,BX,CX,DX,DI,SI) + +TEXT ·fp384Neg(SB), NOSPLIT, $0-16 + MOVQ ·p+0(SB), R8 + MOVQ ·p+8(SB), R9 + MOVQ ·p+16(SB), R10 + MOVQ ·p+24(SB), R11 + MOVQ ·p+32(SB), R12 + MOVQ ·p+40(SB), R13 + + MOVQ a+8(FP), DI + SUBQ 0(DI), R8 + SBBQ 8(DI), R9 + SBBQ 16(DI), R10 + SBBQ 24(DI), R11 + SBBQ 32(DI), R12 + SBBQ 40(DI), R13 + + MOVQ $0, R15 + fp384Carry(R8,R9,R10,R11,R12,R13,R15, R14,AX,BX,CX,DX,DI,SI) + + MOVQ c+0(FP), DI + storeBlock(R8,R9,R10,R11,R12,R13, 0(DI)) + RET + +TEXT ·fp384Add(SB), NOSPLIT, $0-24 + MOVQ a+8(FP), DI + MOVQ b+16(FP), SI + + loadBlock(0(DI), R8,R9,R10,R11,R12,R13) + MOVQ $0, R15 + + ADDQ 0(SI), R8 + ADCQ 8(SI), R9 + ADCQ 16(SI), R10 + ADCQ 24(SI), R11 + ADCQ 32(SI), R12 + ADCQ 40(SI), R13 + ADCQ $0, R15 + + fp384Carry(R8,R9,R10,R11,R12,R13,R15, R14,AX,BX,CX,DX,DI,SI) + + MOVQ c+0(FP), DI + storeBlock(R8,R9,R10,R11,R12,R13, 0(DI)) + RET + +TEXT ·fp384Sub(SB), NOSPLIT, $0-24 + MOVQ ·p+0(SB), R8 + MOVQ ·p+8(SB), R9 + MOVQ ·p+16(SB), R10 + MOVQ ·p+24(SB), R11 + MOVQ ·p+32(SB), R12 + MOVQ ·p+40(SB), R13 + + MOVQ b+16(FP), DI + SUBQ 0(DI), R8 + SBBQ 8(DI), R9 + SBBQ 16(DI), R10 + SBBQ 24(DI), R11 + SBBQ 32(DI), R12 + SBBQ 40(DI), R13 + + MOVQ $0, R15 + MOVQ a+8(FP), DI + ADDQ 0(DI), R8 + ADCQ 8(DI), R9 + ADCQ 16(DI), R10 + ADCQ 24(DI), R11 + ADCQ 32(DI), R12 + ADCQ 40(DI), R13 + ADCQ $0, R15 + + fp384Carry(R8,R9,R10,R11,R12,R13,R15, R14,AX,BX,CX,DX,DI,SI) + + MOVQ c+0(FP), DI + storeBlock(R8,R9,R10,R11,R12,R13, 0(DI)) + RET + +TEXT ·fp384Mul(SB), NOSPLIT, $240-24 + MOVQ a+8(FP), DI + MOVQ b+16(FP), SI + + // Jump to a slightly different implementation if MULX isn't supported. + CMPB ·hasBMI2(SB), $0 + JE nobmi2Mul + + // T = a * b + mulBMI2(0(DI),8(DI),16(DI),24(DI),32(DI),40(DI), 0(SI), 0(SP)) + storeBlock(R14,R8,R9,R10,R11,R12, 48(SP)) + + // Reduce T. + fp384ReduceBMI2(0(SP)) + + MOVQ c+0(FP), DI + storeBlock(R14,R8,R9,R10,R11,R12, 0(DI)) + JMP end + +nobmi2Mul: + // T = a * b + mul(0(DI),8(DI),16(DI),24(DI),32(DI),40(DI), 0(SI), 0(SP)) + + // Reduce T. + fp384Reduce(0(SP)) + + MOVQ c+0(FP), DI + storeBlock(R14,SI,AX,BX,CX,DX, 0(DI)) + +end: + RET + +TEXT ·fp384Cmov(SB), NOSPLIT, $0 + MOVQ x+0(FP), DI + MOVQ y+8(FP), SI + MOVQ b+16(FP), BX + TESTQ BX, BX + MOVQ 0(DI), AX; MOVQ 0(SI), DX; CMOVQNE DX, AX; MOVQ AX, 0(DI); + MOVQ 8(DI), AX; MOVQ 8(SI), DX; CMOVQNE DX, AX; MOVQ AX, 8(DI); + MOVQ 16(DI), AX; MOVQ 16(SI), DX; CMOVQNE DX, AX; MOVQ AX, 16(DI); + MOVQ 24(DI), AX; MOVQ 24(SI), DX; CMOVQNE DX, AX; MOVQ AX, 24(DI); + MOVQ 32(DI), AX; MOVQ 32(SI), DX; CMOVQNE DX, AX; MOVQ AX, 32(DI); + MOVQ 40(DI), AX; MOVQ 40(SI), DX; CMOVQNE DX, AX; MOVQ AX, 40(DI); + RET diff --git a/src/vendor/github.com/cloudflare/circl/ecc/p384/arith_arm64.s b/src/vendor/github.com/cloudflare/circl/ecc/p384/arith_arm64.s new file mode 100644 index 00000000000..ec93991d426 --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/ecc/p384/arith_arm64.s @@ -0,0 +1,511 @@ +// +build arm64,!noasm + +#include "textflag.h" + +TEXT ·fp384Cmov(SB), NOSPLIT, $0 + MOVD x+0(FP), R0 + MOVD y+8(FP), R1 + MOVW b+16(FP), R2 + CMP $0, R2 + LDP 0(R0), (R3, R5) + LDP 0(R1), (R4, R6) + CSEL NE,R4,R3,R7 + CSEL NE,R6,R5,R8 + STP (R7, R8), 0(R0) + LDP 16(R0), (R3, R5) + LDP 16(R1), (R4, R6) + CSEL NE,R4,R3,R7 + CSEL NE,R6,R5,R8 + STP (R7, R8), 16(R0) + LDP 32(R0), (R3, R5) + LDP 32(R1), (R4, R6) + CSEL NE,R4,R3,R7 + CSEL NE,R6,R5,R8 + STP (R7, R8), 32(R0) + RET + +// Compute c = -a mod p +TEXT ·fp384Neg(SB), NOSPLIT, $0-16 + MOVD c+0(FP), R0 + MOVD a+8(FP), R1 + + // Load p in R2-R7, a in R8-R13 + // Compute p-a in R8-R13 + LDP ·p+0(SB), (R2, R3) + LDP 0(R1), (R8, R9) + SUBS R8, R2, R8 + SBCS R9, R3, R9 + LDP ·p+16(SB), (R4, R5) + LDP 16(R1), (R10, R11) + SBCS R10, R4, R10 + SBCS R11, R5, R11 + LDP ·p+32(SB), (R6, R7) + LDP 32(R1), (R12, R13) + SBCS R12, R6, R12 + SBC R13, R7, R13 + + // Compute (p-a)-p in R2-R7 + SUBS R2, R8, R2 + SBCS R3, R9, R3 + SBCS R4, R10, R4 + SBCS R5, R11, R5 + SBCS R6, R12, R6 + SBCS R7, R13, R7 + + // If (p-a)-p < 0 (nearly always), return p-a + // Only return (p-a)-p for a = 0 + // Store result in c + CSEL CC, R8, R2, R2 + CSEL CC, R9, R3, R3 + STP (R2, R3), 0(R0) + CSEL CC, R10, R4, R4 + CSEL CC, R11, R5, R5 + STP (R4, R5), 16(R0) + CSEL CC, R12, R6, R6 + CSEL CC, R13, R7, R7 + STP (R6, R7), 32(R0) + + RET + +// Compute c = a+b mod p +TEXT ·fp384Add(SB), NOSPLIT, $0-24 + MOVD c+0(FP), R0 + MOVD a+8(FP), R1 + MOVD b+16(FP), R2 + + // Load a in R3-R8, b in R9-R14 + // Compute a+b in R3-R9 + LDP 0(R1), (R3, R4) + LDP 0(R2), (R9, R10) + ADDS R9, R3 + ADCS R10, R4 + LDP 16(R1), (R5, R6) + LDP 16(R2), (R11, R12) + ADCS R11, R5 + ADCS R12, R6 + LDP 32(R1), (R7, R8) + LDP 32(R2), (R13, R14) + ADCS R13, R7 + ADCS R14, R8 + ADC ZR, ZR, R9 + + // Load p in R10-R15 + LDP ·p+ 0(SB), (R10, R11) + LDP ·p+16(SB), (R12, R13) + LDP ·p+32(SB), (R14, R15) + + // Compute a+b-p in R10-R16 + SUBS R10, R3, R10 + SBCS R11, R4, R11 + SBCS R12, R5, R12 + SBCS R13, R6, R13 + SBCS R14, R7, R14 + SBCS R15, R8, R15 + SBCS ZR, R9, R16 + + // If a+b-p is negative, return a+b + // Store result in c + CSEL CC, R3, R10, R3 + CSEL CC, R4, R11, R4 + STP (R3, R4), 0(R0) + CSEL CC, R5, R12, R5 + CSEL CC, R6, R13, R6 + STP (R5, R6), 16(R0) + CSEL CC, R7, R14, R7 + CSEL CC, R8, R15, R8 + STP (R7, R8), 32(R0) + + RET + +// Compute c = a-b mod p +TEXT ·fp384Sub(SB), NOSPLIT, $0-24 + MOVD c+0(FP), R0 + MOVD a+8(FP), R1 + MOVD b+16(FP), R2 + + // Load a in R3-R8, b in R9-R14 + // Compute a-b in R3-R9 + LDP 0(R1), (R3, R4) + LDP 0(R2), (R9, R10) + SUBS R9, R3 + SBCS R10, R4 + LDP 16(R1), (R5, R6) + LDP 16(R2), (R11, R12) + SBCS R11, R5 + SBCS R12, R6 + LDP 32(R1), (R7, R8) + LDP 32(R2), (R13, R14) + SBCS R13, R7 + SBCS R14, R8 + SBC ZR, ZR, R9 + + // Load p in R10-R15 + // If a-b < 0, (a-b)+p to R3-R8 + // Store result in c + LDP ·p+ 0(SB), (R10, R11) + AND R9, R10 + LDP ·p+16(SB), (R12, R13) + AND R9, R11 + AND R9, R12 + LDP ·p+32(SB), (R14, R15) + AND R9, R13 + AND R9, R14 + AND R9, R15 + + ADDS R10, R3 + ADCS R11, R4 + STP (R3, R4), 0(R0) + ADCS R12, R5 + ADCS R13, R6 + STP (R5, R6), 16(R0) + ADCS R14, R7 + ADC R15, R8 + STP (R7, R8), 32(R0) + + RET + +// Expects that A0*B0 is already in C0(low),C3(high) and A0*B1 in C1(low),C2(high) +// C0 is not actually touched +// Result of (A0-A2) * (B0-B2) will be in C0-C5 +// Inputs remain intact +#define mul192x192comba(A0,A1,A2, B0,B1,B2, C0,C1,C2,C3,C4,C5, S0,S1,S2,S3) \ + MUL A1, B0, S2 \ + UMULH A1, B0, S3 \ + \ + ADDS C3, C1 \ + ADCS ZR, C2 \ + ADC ZR, ZR, C3 \ + \ + MUL A0, B2, S0 \ + UMULH A0, B2, S1 \ + \ + ADDS S2, C1 \ + ADCS S3, C2 \ + ADC ZR, C3 \ + \ + MUL A1, B1, S2 \ + UMULH A1, B1, S3 \ + \ + ADDS S0, C2 \ + ADCS S1, C3 \ + ADC ZR, ZR, C4 \ + \ + MUL A2, B0, S0 \ + UMULH A2, B0, S1 \ + \ + ADDS S2, C2 \ + ADCS S3, C3 \ + ADC ZR, C4 \ + \ + MUL A1, B2, S2 \ + UMULH A1, B2, S3 \ + \ + ADDS S0, C2 \ + ADCS S1, C3 \ + ADC ZR, C4 \ + \ + MUL A2, B1, S0 \ + UMULH A2, B1, S1 \ + \ + ADDS S2, C3 \ + ADCS S3, C4 \ + ADC ZR, ZR, C5 \ + \ + MUL A2, B2, S2 \ + UMULH A2, B2, S3 \ + \ + ADDS S0, C3 \ + ADCS S1, C4 \ + ADC ZR, C5 \ + \ + ADDS S2, C4 \ + ADC S3, C5 + + +// Assumes that there are at least 96 bytes left on the stack +// Expects that X and Y point to input +// X and Y get overwritten, Z0 will be in Y +#define mul384x384karatsuba(X,Y, Z1,Z2,Z3,Z4,Z5,Z6,Z7,Z8,Z9,Z10,Z11, T0,T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12) \ + /* Load a in Z1-Z6, b in T12,Z7-Z11 */ \ + LDP 0(X), ( Z1, Z2) \ + LDP 0(Y), (T12, Z7) \ + MUL Z1, Z7, T1 \ + UMULH Z1, T12, T3 \ + LDP 16(X), ( Z3, Z4) \ + LDP 16(Y), ( Z8, Z9) \ + MUL Z1, T12, T0 \ + UMULH Z1, Z7, T2 \ + LDP 32(X), ( Z5, Z6) \ + LDP 32(Y), (Z10, Z11) \ + \ + /* Compute aL*bL in T0-T5 */ \ + mul192x192comba(Z1,Z2,Z3, T12,Z7,Z8, T0,T1,T2,T3,T4,T5, T6,T7,T8,T9) \ + \ + /* Compute aH*bH in T6-T11, destroys aL and bL */ \ + MUL Z4, Z10, T7 \ + MUL Z4, Z9, T6 \ + UMULH Z4, Z9, T9 \ + UMULH Z4, Z10, T8 \ + mul192x192comba(Z4,Z5,Z6, Z9,Z10,Z11, T6,T7,T8,T9,T10,T11, Z1,Z2,T12,Z7) \ + \ + /* Compute aL*bL + aH*bH in Z1-Z6,T12, destroys aH */ \ + ADDS T0, T6, Z1 \ + ADCS T1, T7, Z2 \ + ADCS T2, T8, Z3 \ + ADCS T3, T9, Z4 \ + ADCS T4, T10, Z5 \ + ADCS T5, T11, Z6 \ + ADC ZR, ZR, T12 \ + \ + /* Add to T0-T11 and store on stack */ \ + STP ( T0, T1), -16(RSP) \ + ADDS Z1, T3 \ + STP ( T2, T3), -32(RSP) \ + ADCS Z2, T4 \ + ADCS Z3, T5 \ + STP ( T4, T5), -48(RSP) \ + ADCS Z4, T6 \ + ADCS Z5, T7 \ + STP ( T6, T7), -64(RSP) \ + ADCS Z6, T8 \ + ADC ZR, T12 \ + STP ( T8, T9), -80(RSP) \ + STP (T10, T11), -96(RSP) \ + \ + /* Load a to Z1-Z6 */ \ + LDP 0(X), (Z1, Z2) \ + LDP 16(X), (Z3, Z4) \ + LDP 32(X), (Z5, Z6) \ + \ + /* Compute |aL-aH| to Z1-Z3, keep borrow in X */ \ + SUBS Z4, Z1 \ + SBCS Z5, Z2 \ + SBCS Z6, Z3 \ + SBC ZR, ZR, X \ + NEGS Z1, Z4 \ + NGCS Z2, Z5 \ + NGC Z3, Z6 \ + ADDS $1, X \ + \ + /* Load b to Z7-Z11,T0 */ \ + LDP 0(Y), ( Z7, Z8) \ + LDP 16(Y), ( Z9, Z10) \ + LDP 32(Y), (Z11, T0) \ + \ + CSEL EQ, Z4, Z1, Z1 \ + CSEL EQ, Z5, Z2 ,Z2 \ + CSEL EQ, Z6, Z3, Z3 \ + \ + /* Compute |bH-bL| to Z7-Z9, keep borrow in Y */ \ + SUBS Z7, Z10 \ + SBCS Z8, Z11 \ + SBCS Z9, T0 \ + SBC ZR, ZR, Y \ + NEGS Z10, Z7 \ + NGCS Z11, Z8 \ + NGC T0, Z9 \ + ADDS $1, Y \ + CSEL EQ, Z7, Z10, Z7 \ + CSEL EQ, Z8, Z11, Z8 \ + CSEL EQ, Z9, T0, Z9 \ + \ + /* Combine borrows */ \ + EOR Y, X \ + \ + /* Compute |aL-aH|*|bH-bL| to Z10,Z11,T0-T3 */ \ + MUL Z1, Z8, Z11 \ + MUL Z1, Z7, Z10 \ + UMULH Z1, Z8, T0 \ + UMULH Z1, Z7, T1 \ + mul192x192comba(Z1,Z2,Z3, Z7,Z8,Z9, Z10,Z11,T0,T1,T2,T3, T4,T5,T6,T7) \ + \ + /* The result has to be negated if exactly one of the operands was negative */ \ + NEGS Z10, Y \ + NGCS Z11, Z1 \ + NGCS T0, Z2 \ + NGCS T1, Z3 \ + NGCS T2, Z4 \ + NGCS T3, Z5 \ + NGC ZR, T4 \ + \ + AND T4, X \ + CMP $1, X \ + CSEL EQ, Y, Z10, Z10 \ + CSEL EQ, Z1, Z11, Z11 \ + CSEL EQ, Z2, T0, T0 \ + CSEL EQ, Z3, T1, T1 \ + CSEL EQ, Z4, T2, T2 \ + CSEL EQ, Z5, T3, T3 \ + \ + /* Add that to the middle part */ \ + LDP -16(RSP), ( Y, Z1) \ + LDP -32(RSP), ( Z2, Z3) \ + LDP -48(RSP), ( Z4, Z5) \ + ADDS Z10, Z3 \ + ADCS Z11, Z4 \ + LDP -64(RSP), ( Z6, Z7) \ + ADCS T0, Z5 \ + ADCS T1, Z6 \ + LDP -80(RSP), ( Z8, Z9) \ + ADCS T2, Z7 \ + ADCS T3, Z8 \ + LDP -96(RSP), (Z10, Z11) \ + ADCS T12, Z9 \ + ADCS ZR, Z10 \ + ADC ZR, Z11 \ + SUBS X, Z9 \ + SBCS ZR, Z10 \ + SBC ZR, Z11 + +// Compute c = a*b*R^-1 mod p +TEXT ·fp384Mul(SB), NOSPLIT, $200-24 + MOVD c+0(FP), R0 + MOVD a+8(FP), R1 + MOVD b+16(FP), R2 + + // Compute a*b in R2-R13 + mul384x384karatsuba(R1, R2, R3,R4,R5,R6,R7,R8,R9,R10,R11,R12,R13, R14,R15,R16,R17,R19,R20,R21,R22,R23,R24,R25,R26,R27) + + // Store a*b on the stack + STP ( R2, R3), -112(RSP) + STP ( R4, R5), -128(RSP) + STP ( R6, R7), -144(RSP) + STP ( R8, R9), -160(RSP) + STP (R10, R11), -176(RSP) + STP (R12, R13), -192(RSP) + + // Compute m = a*b*pp mod 2^384 in R19-R24 + // Store it temporarily in c + MOVD ·pp+0(SB), R14 + MUL R14, R2, R19 + UMULH R14, R2, R20 + + MUL R14, R3, R16 + UMULH R14, R3, R21 + ADDS R16, R20 + ADC ZR, R21 + + MUL R14, R4, R16 + UMULH R14, R4, R22 + ADDS R16, R21 + ADC ZR, R22 + + MUL R14, R5, R16 + UMULH R14, R5, R23 + ADDS R16, R22 + ADC ZR, R23 + + MUL R14, R6, R16 + UMULH R14, R6, R24 + ADDS R16, R23 + ADC ZR, R24 + + MADD R14, R24, R7, R24 + + // ·pp+8(SB) = 1, so we can just add + ADDS R2, R20 + STP (R19, R20), 0(R0) + ADCS R3, R21 + ADCS R4, R22 + ADCS R5, R23 + ADC R6, R24 + + LDP ·pp+16(SB), (R14, R15) + MUL R14, R2, R8 + UMULH R14, R2, R9 + + MUL R14, R3, R16 + UMULH R14, R3, R10 + ADDS R16, R9 + ADC ZR, R10 + + MUL R14, R4, R16 + UMULH R14, R4, R11 + ADDS R16, R10 + ADC ZR, R11 + + MUL R14, R5, R16 + ADD R16, R11 + + ADDS R8, R21 + ADCS R9, R22 + ADCS R10, R23 + ADC R11, R24 + + MUL R15, R2, R8 + UMULH R15, R2, R9 + + MUL R15, R3, R16 + UMULH R15, R3, R10 + ADDS R16, R9 + ADC ZR, R10 + + MADD R15, R10, R4, R10 + + ADDS R8, R22 + STP (R21, R22), 16(R0) + ADCS R9, R23 + ADC R10, R24 + + LDP ·pp+32(SB), (R14, R15) + MUL R14, R2, R8 + UMULH R14, R2, R9 + + MADD R14, R9, R3, R9 + + ADDS R8, R23 + ADC R9, R24 + + MADD R15, R24, R2, R24 + STP (R23, R24), 32(R0) + + // Compute m*p in R1-R12 + MOVD $·p(SB), R1 + mul384x384karatsuba(R0, R1, R2,R3,R4,R5,R6,R7,R8,R9,R10,R11,R12, R13,R14,R15,R16,R17,R19,R20,R21,R22,R23,R24,R25,R26) + + // Add a*b to m*p in R1-R12,R26 + LDP -112(RSP), (R13, R14) + ADDS R13, R1 + LDP -128(RSP), (R15, R16) + ADCS R14, R2 + ADCS R15, R3 + LDP -144(RSP), (R17, R19) + ADCS R16, R4 + ADCS R17, R5 + LDP -160(RSP), (R20, R21) + ADCS R19, R6 + ADCS R20, R7 + LDP -176(RSP), (R22, R23) + ADCS R21, R8 + ADCS R22, R9 + LDP -192(RSP), (R24, R25) + ADCS R23, R10 + ADCS R24, R11 + ADCS R25, R12 + ADC ZR, ZR, R26 + + // Reduce the top half mod p + LDP ·p+ 0(SB), (R13, R14) + SUBS R13, R7, R13 + LDP ·p+16(SB), (R15, R16) + SBCS R14, R8, R14 + SBCS R15, R9, R15 + LDP ·p+32(SB), (R17, R19) + SBCS R16, R10, R16 + SBCS R17, R11, R17 + SBCS R19, R12, R19 + SBCS ZR, R26 + + // Store result in c + MOVD c+0(FP), R0 + CSEL CC, R7, R13, R7 + CSEL CC, R8, R14, R8 + STP ( R7, R8), 0(R0) + CSEL CC, R9, R15, R9 + CSEL CC, R10, R16, R10 + STP ( R9, R10), 16(R0) + CSEL CC, R11, R17, R11 + CSEL CC, R12, R19, R12 + STP (R11, R12), 32(R0) + + RET diff --git a/src/vendor/github.com/cloudflare/circl/ecc/p384/doc.go b/src/vendor/github.com/cloudflare/circl/ecc/p384/doc.go new file mode 100644 index 00000000000..807676fb7fd --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/ecc/p384/doc.go @@ -0,0 +1,10 @@ +// Package p384 provides optimized elliptic curve operations on the P-384 curve. +// +// These are some improvements over crypto/elliptic package: +// - Around 10x faster in amd64 architecture. +// - Reduced number of memory allocations. +// - Native support for arm64 architecture. +// - ScalarMult is performed using a constant-time algorithm. +// - ScalarBaseMult fallbacks into ScalarMult. +// - A new method included for double-point multiplication. +package p384 diff --git a/src/vendor/github.com/cloudflare/circl/ecc/p384/p384.go b/src/vendor/github.com/cloudflare/circl/ecc/p384/p384.go new file mode 100644 index 00000000000..75005d08df9 --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/ecc/p384/p384.go @@ -0,0 +1,28 @@ +package p384 + +import ( + "crypto/elliptic" + "math/big" +) + +// Curve is used to provide the extended functionality and performance of +// elliptic.Curve interface. +type Curve interface { + elliptic.Curve + // IsAtInfinity returns True is the point is the identity point. + IsAtInfinity(X, Y *big.Int) bool + // CombinedMult calculates P=mG+nQ, where G is the generator and + // Q=(Qx,Qy). The scalars m and n are positive integers in big-endian form. + // Runs in non-constant time to be used in signature verification. + CombinedMult(Qx, Qy *big.Int, m, n []byte) (Px, Py *big.Int) +} + +// Params returns the parameters for the curve. Note: The value returned by +// this function fallbacks to the stdlib implementation of elliptic curve +// operations. Use this method to only recover elliptic curve parameters. +func (c curve) Params() *elliptic.CurveParams { return elliptic.P384().Params() } + +// IsAtInfinity returns True is the point is the identity point. +func (c curve) IsAtInfinity(x, y *big.Int) bool { + return x.Sign() == 0 && y.Sign() == 0 +} diff --git a/src/vendor/github.com/cloudflare/circl/ecc/p384/p384_generic.go b/src/vendor/github.com/cloudflare/circl/ecc/p384/p384_generic.go new file mode 100644 index 00000000000..8444a59e204 --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/ecc/p384/p384_generic.go @@ -0,0 +1,21 @@ +//go:build noasm || (!amd64 && !arm64) +// +build noasm !amd64,!arm64 + +package p384 + +import ( + "crypto/elliptic" + "math/big" +) + +type curve struct{ elliptic.Curve } + +func P384() Curve { return curve{elliptic.P384()} } + +// CombinedMult calculates P=mG+nQ, where G is the generator and Q=(x,y,z). +// The scalars m and n are integers in big-endian form. Non-constant time. +func (c curve) CombinedMult(xQ, yQ *big.Int, m, n []byte) (xP, yP *big.Int) { + x1, y1 := c.ScalarBaseMult(m) + x2, y2 := c.ScalarMult(xQ, yQ, n) + return c.Add(x1, y1, x2, y2) +} diff --git a/src/vendor/github.com/cloudflare/circl/ecc/p384/p384opt.go b/src/vendor/github.com/cloudflare/circl/ecc/p384/p384opt.go new file mode 100644 index 00000000000..91d17705175 --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/ecc/p384/p384opt.go @@ -0,0 +1,181 @@ +//go:build (!noasm && arm64) || (!noasm && amd64) +// +build !noasm,arm64 !noasm,amd64 + +package p384 + +import ( + "crypto/subtle" + "math/big" + + "github.com/cloudflare/circl/math" +) + +type curve struct{} + +// P384 returns a Curve which implements P-384 (see FIPS 186-3, section D.2.4). +func P384() Curve { return curve{} } + +// IsOnCurve reports whether the given (x,y) lies on the curve. +func (c curve) IsOnCurve(x, y *big.Int) bool { + x1, y1 := &fp384{}, &fp384{} + x1.SetBigInt(x) + y1.SetBigInt(y) + montEncode(x1, x1) + montEncode(y1, y1) + + y2, x3 := &fp384{}, &fp384{} + fp384Sqr(y2, y1) + fp384Sqr(x3, x1) + fp384Mul(x3, x3, x1) + + threeX := &fp384{} + fp384Add(threeX, x1, x1) + fp384Add(threeX, threeX, x1) + + fp384Sub(x3, x3, threeX) + fp384Add(x3, x3, &bb) + + return *y2 == *x3 +} + +// Add returns the sum of (x1,y1) and (x2,y2). +func (c curve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) { + P := newAffinePoint(x1, y1).toJacobian() + P.mixadd(P, newAffinePoint(x2, y2)) + return P.toAffine().toInt() +} + +// Double returns 2*(x,y). +func (c curve) Double(x1, y1 *big.Int) (x, y *big.Int) { + P := newAffinePoint(x1, y1).toJacobian() + P.double() + return P.toAffine().toInt() +} + +// reduceScalar shorten a scalar modulo the order of the curve. +func (c curve) reduceScalar(k []byte) []byte { + bigK := new(big.Int).SetBytes(k) + bigK.Mod(bigK, c.Params().N) + return bigK.FillBytes(make([]byte, sizeFp)) +} + +// toOdd performs k = (-k mod N) if k is even. +func (c curve) toOdd(k []byte) ([]byte, int) { + var X, Y big.Int + X.SetBytes(k) + Y.Neg(&X).Mod(&Y, c.Params().N) + isEven := 1 - int(X.Bit(0)) + x := X.Bytes() + y := Y.Bytes() + + if len(x) < len(y) { + x = append(make([]byte, len(y)-len(x)), x...) + } else if len(x) > len(y) { + y = append(make([]byte, len(x)-len(y)), y...) + } + subtle.ConstantTimeCopy(isEven, x, y) + return x, isEven +} + +// ScalarMult returns (Qx,Qy)=k*(Px,Py) where k is a number in big-endian form. +func (c curve) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int) { + return c.scalarMultOmega(x1, y1, k, 5) +} + +func (c curve) scalarMultOmega(x1, y1 *big.Int, k []byte, omega uint) (x, y *big.Int) { + k = c.reduceScalar(k) + oddK, isEvenK := c.toOdd(k) + + var scalar big.Int + scalar.SetBytes(oddK) + if scalar.Sign() == 0 { + return new(big.Int), new(big.Int) + } + const bitsN = uint(384) + L := math.SignedDigit(&scalar, omega, bitsN) + + var R jacobianPoint + Q := zeroPoint().toJacobian() + TabP := newAffinePoint(x1, y1).oddMultiples(omega) + for i := len(L) - 1; i > 0; i-- { + for j := uint(0); j < omega-1; j++ { + Q.double() + } + idx := absolute(L[i]) >> 1 + for j := range TabP { + R.cmov(&TabP[j], subtle.ConstantTimeEq(int32(j), idx)) + } + R.cneg(int(L[i]>>31) & 1) + Q.add(Q, &R) + } + // Calculate the last iteration using complete addition formula. + for j := uint(0); j < omega-1; j++ { + Q.double() + } + idx := absolute(L[0]) >> 1 + for j := range TabP { + R.cmov(&TabP[j], subtle.ConstantTimeEq(int32(j), idx)) + } + R.cneg(int(L[0]>>31) & 1) + QQ := Q.toProjective() + QQ.completeAdd(QQ, R.toProjective()) + QQ.cneg(isEvenK) + return QQ.toAffine().toInt() +} + +// ScalarBaseMult returns k*G, where G is the base point of the group +// and k is an integer in big-endian form. +func (c curve) ScalarBaseMult(k []byte) (x, y *big.Int) { + params := c.Params() + return c.ScalarMult(params.Gx, params.Gy, k) +} + +// CombinedMult calculates P=mG+nQ, where G is the generator and Q=(x,y,z). +// The scalars m and n are integers in big-endian form. Non-constant time. +func (c curve) CombinedMult(xQ, yQ *big.Int, m, n []byte) (xP, yP *big.Int) { + const nOmega = uint(5) + var k big.Int + k.SetBytes(m) + nafM := math.OmegaNAF(&k, baseOmega) + k.SetBytes(n) + nafN := math.OmegaNAF(&k, nOmega) + + if len(nafM) > len(nafN) { + nafN = append(nafN, make([]int32, len(nafM)-len(nafN))...) + } else if len(nafM) < len(nafN) { + nafM = append(nafM, make([]int32, len(nafN)-len(nafM))...) + } + + TabQ := newAffinePoint(xQ, yQ).oddMultiples(nOmega) + var jR jacobianPoint + var aR affinePoint + P := zeroPoint().toJacobian() + for i := len(nafN) - 1; i >= 0; i-- { + P.double() + // Generator point + if nafM[i] != 0 { + idxM := absolute(nafM[i]) >> 1 + aR = baseOddMultiples[idxM] + if nafM[i] < 0 { + aR.neg() + } + P.mixadd(P, &aR) + } + // Input point + if nafN[i] != 0 { + idxN := absolute(nafN[i]) >> 1 + jR = TabQ[idxN] + if nafN[i] < 0 { + jR.neg() + } + P.add(P, &jR) + } + } + return P.toAffine().toInt() +} + +// absolute returns always a positive value. +func absolute(x int32) int32 { + mask := x >> 31 + return (x + mask) ^ mask +} diff --git a/src/vendor/github.com/cloudflare/circl/ecc/p384/point.go b/src/vendor/github.com/cloudflare/circl/ecc/p384/point.go new file mode 100644 index 00000000000..0ba1da8bdb3 --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/ecc/p384/point.go @@ -0,0 +1,358 @@ +//go:build (!noasm && arm64) || (!noasm && amd64) +// +build !noasm,arm64 !noasm,amd64 + +package p384 + +import ( + "fmt" + "math/big" +) + +// affinePoint represents an affine point of the curve. The point at +// infinity is (0,0) leveraging that it is not an affine point. +type affinePoint struct{ x, y fp384 } + +func newAffinePoint(x, y *big.Int) *affinePoint { + var P affinePoint + P.x.SetBigInt(x) + P.y.SetBigInt(y) + montEncode(&P.x, &P.x) + montEncode(&P.y, &P.y) + return &P +} + +func zeroPoint() *affinePoint { return &affinePoint{} } + +func (ap affinePoint) String() string { + if ap.isZero() { + return "inf" + } + return fmt.Sprintf("x: %v\ny: %v", ap.x, ap.y) +} + +func (ap *affinePoint) isZero() bool { + zero := fp384{} + return ap.x == zero && ap.y == zero +} + +func (ap *affinePoint) neg() { fp384Neg(&ap.y, &ap.y) } + +func (ap *affinePoint) toInt() (x, y *big.Int) { + var x1, y1 fp384 + montDecode(&x1, &ap.x) + montDecode(&y1, &ap.y) + return x1.BigInt(), y1.BigInt() +} + +func (ap *affinePoint) toJacobian() *jacobianPoint { + var P jacobianPoint + if ap.isZero() { + montEncode(&P.x, &fp384{1}) + montEncode(&P.y, &fp384{1}) + } else { + P.x = ap.x + P.y = ap.y + montEncode(&P.z, &fp384{1}) + } + return &P +} + +func (ap *affinePoint) toProjective() *projectivePoint { + var P projectivePoint + if ap.isZero() { + montEncode(&P.y, &fp384{1}) + } else { + P.x = ap.x + P.y = ap.y + montEncode(&P.z, &fp384{1}) + } + return &P +} + +// OddMultiples calculates the points iP for i={1,3,5,7,..., 2^(n-1)-1} +// Ensure that 1 < n < 31, otherwise it returns an empty slice. +func (ap affinePoint) oddMultiples(n uint) []jacobianPoint { + var t []jacobianPoint + if n > 1 && n < 31 { + P := ap.toJacobian() + s := int32(1) << (n - 1) + t = make([]jacobianPoint, s) + t[0] = *P + _2P := *P + _2P.double() + for i := int32(1); i < s; i++ { + t[i].add(&t[i-1], &_2P) + } + } + return t +} + +// p2Point is a point in P^2 +type p2Point struct{ x, y, z fp384 } + +func (P *p2Point) String() string { + return fmt.Sprintf("x: %v\ny: %v\nz: %v", P.x, P.y, P.z) +} + +func (P *p2Point) neg() { fp384Neg(&P.y, &P.y) } + +// condNeg if P is negated if b=1. +func (P *p2Point) cneg(b int) { + var mY fp384 + fp384Neg(&mY, &P.y) + fp384Cmov(&P.y, &mY, b) +} + +// cmov sets P to Q if b=1. +func (P *p2Point) cmov(Q *p2Point, b int) { + fp384Cmov(&P.x, &Q.x, b) + fp384Cmov(&P.y, &Q.y, b) + fp384Cmov(&P.z, &Q.z, b) +} + +func (P *p2Point) toInt() (x, y, z *big.Int) { + var x1, y1, z1 fp384 + montDecode(&x1, &P.x) + montDecode(&y1, &P.y) + montDecode(&z1, &P.z) + return x1.BigInt(), y1.BigInt(), z1.BigInt() +} + +// jacobianPoint represents a point in Jacobian coordinates. The point at +// infinity is any point (x,y,0) such that x and y are different from 0. +type jacobianPoint struct{ p2Point } + +func (P *jacobianPoint) isZero() bool { + zero := fp384{} + return P.x != zero && P.y != zero && P.z == zero +} + +func (P *jacobianPoint) toAffine() *affinePoint { + var aP affinePoint + z, z2 := &fp384{}, &fp384{} + fp384Inv(z, &P.z) + fp384Sqr(z2, z) + fp384Mul(&aP.x, &P.x, z2) + fp384Mul(&aP.y, &P.y, z) + fp384Mul(&aP.y, &aP.y, z2) + return &aP +} + +func (P *jacobianPoint) cmov(Q *jacobianPoint, b int) { P.p2Point.cmov(&Q.p2Point, b) } + +// add calculates P=Q+R such that Q and R are different than the identity point, +// and Q!==R. This function cannot be used for doublings. +func (P *jacobianPoint) add(Q, R *jacobianPoint) { + if Q.isZero() { + *P = *R + return + } else if R.isZero() { + *P = *Q + return + } + + // Cohen-Miyagi-Ono (1998) + // https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-1998-cmo-2 + X1, Y1, Z1 := &Q.x, &Q.y, &Q.z + X2, Y2, Z2 := &R.x, &R.y, &R.z + Z1Z1, Z2Z2, U1, U2 := &fp384{}, &fp384{}, &fp384{}, &fp384{} + H, HH, HHH, RR := &fp384{}, &fp384{}, &fp384{}, &fp384{} + V, t4, t5, t6, t7, t8 := &fp384{}, &fp384{}, &fp384{}, &fp384{}, &fp384{}, &fp384{} + t0, t1, t2, t3, S1, S2 := &fp384{}, &fp384{}, &fp384{}, &fp384{}, &fp384{}, &fp384{} + fp384Sqr(Z1Z1, Z1) // Z1Z1 = Z1 ^ 2 + fp384Sqr(Z2Z2, Z2) // Z2Z2 = Z2 ^ 2 + fp384Mul(U1, X1, Z2Z2) // U1 = X1 * Z2Z2 + fp384Mul(U2, X2, Z1Z1) // U2 = X2 * Z1Z1 + fp384Mul(t0, Z2, Z2Z2) // t0 = Z2 * Z2Z2 + fp384Mul(S1, Y1, t0) // S1 = Y1 * t0 + fp384Mul(t1, Z1, Z1Z1) // t1 = Z1 * Z1Z1 + fp384Mul(S2, Y2, t1) // S2 = Y2 * t1 + fp384Sub(H, U2, U1) // H = U2 - U1 + fp384Sqr(HH, H) // HH = H ^ 2 + fp384Mul(HHH, H, HH) // HHH = H * HH + fp384Sub(RR, S2, S1) // r = S2 - S1 + fp384Mul(V, U1, HH) // V = U1 * HH + fp384Sqr(t2, RR) // t2 = r ^ 2 + fp384Add(t3, V, V) // t3 = V + V + fp384Sub(t4, t2, HHH) // t4 = t2 - HHH + fp384Sub(&P.x, t4, t3) // X3 = t4 - t3 + fp384Sub(t5, V, &P.x) // t5 = V - X3 + fp384Mul(t6, S1, HHH) // t6 = S1 * HHH + fp384Mul(t7, RR, t5) // t7 = r * t5 + fp384Sub(&P.y, t7, t6) // Y3 = t7 - t6 + fp384Mul(t8, Z2, H) // t8 = Z2 * H + fp384Mul(&P.z, Z1, t8) // Z3 = Z1 * t8 +} + +// mixadd calculates P=Q+R such that P and Q different than the identity point, +// and Q not in {P,-P, O}. +func (P *jacobianPoint) mixadd(Q *jacobianPoint, R *affinePoint) { + if Q.isZero() { + *P = *R.toJacobian() + return + } else if R.isZero() { + *P = *Q + return + } + + z1z1, u2 := &fp384{}, &fp384{} + fp384Sqr(z1z1, &Q.z) + fp384Mul(u2, &R.x, z1z1) + + s2 := &fp384{} + fp384Mul(s2, &R.y, &Q.z) + fp384Mul(s2, s2, z1z1) + if Q.x == *u2 { + if Q.y != *s2 { + *P = *(zeroPoint().toJacobian()) + return + } + *P = *Q + P.double() + return + } + + h, r := &fp384{}, &fp384{} + fp384Sub(h, u2, &Q.x) + fp384Mul(&P.z, h, &Q.z) + fp384Sub(r, s2, &Q.y) + + h2, h3 := &fp384{}, &fp384{} + fp384Sqr(h2, h) + fp384Mul(h3, h2, h) + h3y1 := &fp384{} + fp384Mul(h3y1, h3, &Q.y) + + h2x1 := &fp384{} + fp384Mul(h2x1, h2, &Q.x) + + fp384Sqr(&P.x, r) + fp384Sub(&P.x, &P.x, h3) + fp384Sub(&P.x, &P.x, h2x1) + fp384Sub(&P.x, &P.x, h2x1) + + fp384Sub(&P.y, h2x1, &P.x) + fp384Mul(&P.y, &P.y, r) + fp384Sub(&P.y, &P.y, h3y1) +} + +func (P *jacobianPoint) double() { + delta, gamma, alpha, alpha2 := &fp384{}, &fp384{}, &fp384{}, &fp384{} + fp384Sqr(delta, &P.z) + fp384Sqr(gamma, &P.y) + fp384Sub(alpha, &P.x, delta) + fp384Add(alpha2, &P.x, delta) + fp384Mul(alpha, alpha, alpha2) + *alpha2 = *alpha + fp384Add(alpha, alpha, alpha) + fp384Add(alpha, alpha, alpha2) + + beta := &fp384{} + fp384Mul(beta, &P.x, gamma) + + beta8 := &fp384{} + fp384Sqr(&P.x, alpha) + fp384Add(beta8, beta, beta) + fp384Add(beta8, beta8, beta8) + fp384Add(beta8, beta8, beta8) + fp384Sub(&P.x, &P.x, beta8) + + fp384Add(&P.z, &P.y, &P.z) + fp384Sqr(&P.z, &P.z) + fp384Sub(&P.z, &P.z, gamma) + fp384Sub(&P.z, &P.z, delta) + + fp384Add(beta, beta, beta) + fp384Add(beta, beta, beta) + fp384Sub(beta, beta, &P.x) + + fp384Mul(&P.y, alpha, beta) + + fp384Sqr(gamma, gamma) + fp384Add(gamma, gamma, gamma) + fp384Add(gamma, gamma, gamma) + fp384Add(gamma, gamma, gamma) + fp384Sub(&P.y, &P.y, gamma) +} + +func (P *jacobianPoint) toProjective() *projectivePoint { + var hP projectivePoint + hP.y = P.y + fp384Mul(&hP.x, &P.x, &P.z) + fp384Sqr(&hP.z, &P.z) + fp384Mul(&hP.z, &hP.z, &P.z) + return &hP +} + +// projectivePoint represents a point in projective homogeneous coordinates. +// The point at infinity is (0,y,0) such that y is different from 0. +type projectivePoint struct{ p2Point } + +func (P *projectivePoint) isZero() bool { + zero := fp384{} + return P.x == zero && P.y != zero && P.z == zero +} + +func (P *projectivePoint) toAffine() *affinePoint { + var aP affinePoint + z := &fp384{} + fp384Inv(z, &P.z) + fp384Mul(&aP.x, &P.x, z) + fp384Mul(&aP.y, &P.y, z) + return &aP +} + +// add calculates P=Q+R using complete addition formula for prime groups. +func (P *projectivePoint) completeAdd(Q, R *projectivePoint) { + // Reference: + // "Complete addition formulas for prime order elliptic curves" by + // Costello-Renes-Batina. [Alg.4] (eprint.iacr.org/2015/1060). + X1, Y1, Z1 := &Q.x, &Q.y, &Q.z + X2, Y2, Z2 := &R.x, &R.y, &R.z + X3, Y3, Z3 := &fp384{}, &fp384{}, &fp384{} + t0, t1, t2, t3, t4 := &fp384{}, &fp384{}, &fp384{}, &fp384{}, &fp384{} + fp384Mul(t0, X1, X2) // 1. t0 ← X1 · X2 + fp384Mul(t1, Y1, Y2) // 2. t1 ← Y1 · Y2 + fp384Mul(t2, Z1, Z2) // 3. t2 ← Z1 · Z2 + fp384Add(t3, X1, Y1) // 4. t3 ← X1 + Y1 + fp384Add(t4, X2, Y2) // 5. t4 ← X2 + Y2 + fp384Mul(t3, t3, t4) // 6. t3 ← t3 · t4 + fp384Add(t4, t0, t1) // 7. t4 ← t0 + t1 + fp384Sub(t3, t3, t4) // 8. t3 ← t3 − t4 + fp384Add(t4, Y1, Z1) // 9. t4 ← Y1 + Z1 + fp384Add(X3, Y2, Z2) // 10. X3 ← Y2 + Z2 + fp384Mul(t4, t4, X3) // 11. t4 ← t4 · X3 + fp384Add(X3, t1, t2) // 12. X3 ← t1 + t2 + fp384Sub(t4, t4, X3) // 13. t4 ← t4 − X3 + fp384Add(X3, X1, Z1) // 14. X3 ← X1 + Z1 + fp384Add(Y3, X2, Z2) // 15. Y3 ← X2 + Z2 + fp384Mul(X3, X3, Y3) // 16. X3 ← X3 · Y3 + fp384Add(Y3, t0, t2) // 17. Y3 ← t0 + t2 + fp384Sub(Y3, X3, Y3) // 18. Y3 ← X3 − Y3 + fp384Mul(Z3, &bb, t2) // 19. Z3 ← b · t2 + fp384Sub(X3, Y3, Z3) // 20. X3 ← Y3 − Z3 + fp384Add(Z3, X3, X3) // 21. Z3 ← X3 + X3 + fp384Add(X3, X3, Z3) // 22. X3 ← X3 + Z3 + fp384Sub(Z3, t1, X3) // 23. Z3 ← t1 − X3 + fp384Add(X3, t1, X3) // 24. X3 ← t1 + X3 + fp384Mul(Y3, &bb, Y3) // 25. Y3 ← b · Y3 + fp384Add(t1, t2, t2) // 26. t1 ← t2 + t2 + fp384Add(t2, t1, t2) // 27. t2 ← t1 + t2 + fp384Sub(Y3, Y3, t2) // 28. Y3 ← Y3 − t2 + fp384Sub(Y3, Y3, t0) // 29. Y3 ← Y3 − t0 + fp384Add(t1, Y3, Y3) // 30. t1 ← Y3 + Y3 + fp384Add(Y3, t1, Y3) // 31. Y3 ← t1 + Y3 + fp384Add(t1, t0, t0) // 32. t1 ← t0 + t0 + fp384Add(t0, t1, t0) // 33. t0 ← t1 + t0 + fp384Sub(t0, t0, t2) // 34. t0 ← t0 − t2 + fp384Mul(t1, t4, Y3) // 35. t1 ← t4 · Y3 + fp384Mul(t2, t0, Y3) // 36. t2 ← t0 · Y3 + fp384Mul(Y3, X3, Z3) // 37. Y3 ← X3 · Z3 + fp384Add(Y3, Y3, t2) // 38. Y3 ← Y3 + t2 + fp384Mul(X3, t3, X3) // 39. X3 ← t3 · X3 + fp384Sub(X3, X3, t1) // 40. X3 ← X3 − t1 + fp384Mul(Z3, t4, Z3) // 41. Z3 ← t4 · Z3 + fp384Mul(t1, t3, t0) // 42. t1 ← t3 · t0 + fp384Add(Z3, Z3, t1) // 43. Z3 ← Z3 + t1 + P.x, P.y, P.z = *X3, *Y3, *Z3 +} diff --git a/src/vendor/github.com/cloudflare/circl/ecc/p384/tableBase.go b/src/vendor/github.com/cloudflare/circl/ecc/p384/tableBase.go new file mode 100644 index 00000000000..59cd319f321 --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/ecc/p384/tableBase.go @@ -0,0 +1,331 @@ +//go:build (!noasm && arm64) || (!noasm && amd64) +// +build !noasm,arm64 !noasm,amd64 + +package p384 + +const baseOmega = uint(7) + +// baseOddMultiples has [2*i+1] * G at position i. +// Each coordinate has been multiplied by R=2^384 +var baseOddMultiples = [1 << (baseOmega - 1)]affinePoint{ + // 1P + { + x: fp384{0x28, 0xb5, 0xc0, 0x49, 0x66, 0x75, 0xd0, 0x3d, 0x38, 0xce, 0xd6, 0xa0, 0xe2, 0x78, 0xe3, 0x20, 0x6e, 0x4d, 0x1b, 0x54, 0xfc, 0x3a, 0x9c, 0x87, 0xff, 0xe, 0xa3, 0x59, 0x84, 0x86, 0x54, 0x64, 0x2b, 0xde, 0x4e, 0x61, 0x23, 0xf7, 0x2f, 0x81, 0x13, 0x15, 0x9e, 0x29, 0xc2, 0xad, 0x3a, 0x4d}, + y: fp384{0xfe, 0xa4, 0x3, 0x4b, 0xad, 0x3d, 0x4, 0x23, 0xac, 0xa9, 0xb4, 0x7b, 0xbf, 0xa8, 0xbf, 0xa1, 0x50, 0xb0, 0x83, 0x2e, 0x56, 0xe7, 0xad, 0x8b, 0xd9, 0xff, 0xf4, 0x68, 0x19, 0x52, 0xc3, 0xc6, 0x40, 0xa8, 0x69, 0x39, 0x26, 0x2, 0x80, 0xdd, 0xe9, 0xc5, 0x15, 0x5a, 0xc2, 0xab, 0x78, 0x2b}, + }, + // 3P + { + x: fp384{0x73, 0x40, 0xdc, 0xc1, 0xe6, 0xdb, 0xe4, 0x5, 0x9c, 0x77, 0x4f, 0xf0, 0xff, 0xa9, 0x4e, 0xc5, 0xf0, 0xcc, 0x70, 0xa1, 0xe9, 0x34, 0x20, 0x6b, 0x3e, 0x6c, 0x1c, 0xd5, 0x32, 0xd7, 0x48, 0x3a, 0x70, 0xa4, 0x3a, 0x26, 0x2d, 0x7e, 0x6f, 0xe3, 0xac, 0xc3, 0xc1, 0xe7, 0x68, 0xfe, 0x83, 0xd2}, + y: fp384{0x57, 0xe1, 0x4e, 0xc0, 0x21, 0x48, 0x28, 0x7e, 0x6d, 0xe3, 0xe0, 0x7a, 0xa7, 0x89, 0xd7, 0x92, 0x46, 0x74, 0xf6, 0x4e, 0xc0, 0x63, 0x26, 0x13, 0xb4, 0xd0, 0xe1, 0xd2, 0x5a, 0x2d, 0x1, 0x68, 0x39, 0xb3, 0x2, 0x51, 0xb1, 0x68, 0xdb, 0xf6, 0xaf, 0x92, 0x32, 0x98, 0xfc, 0x65, 0x54, 0x46}, + }, + // 5P + { + x: fp384{0xdf, 0xf0, 0xf1, 0x68, 0xba, 0x5e, 0x59, 0xbb, 0x66, 0x34, 0x87, 0xcc, 0xcb, 0xc0, 0x85, 0xc1, 0x3b, 0x70, 0x3c, 0x29, 0xb5, 0xb1, 0x1e, 0x7f, 0xe6, 0x5, 0xcc, 0xaa, 0xf5, 0x2c, 0xdb, 0x60, 0xc6, 0xe4, 0xe8, 0xe2, 0x87, 0xb9, 0x76, 0xc6, 0xfb, 0x8f, 0x17, 0x1d, 0xb1, 0x26, 0xbb, 0xe1}, + y: fp384{0x21, 0xfa, 0x73, 0x70, 0xa0, 0x4b, 0x69, 0x2b, 0x66, 0x45, 0xf3, 0x72, 0x2e, 0x6e, 0xc1, 0x22, 0x99, 0x5b, 0xc3, 0x1, 0x31, 0x1b, 0xb6, 0x80, 0x11, 0x4, 0x2c, 0x98, 0xaf, 0x7f, 0x23, 0x4b, 0x6d, 0x23, 0xde, 0x24, 0x40, 0x94, 0xc5, 0xe6, 0xa3, 0xe4, 0x9, 0xe2, 0xd6, 0xc9, 0xb1, 0x4d}, + }, + // 7P + { + x: fp384{0x2b, 0x22, 0x69, 0x7d, 0xd1, 0xb9, 0x13, 0xdf, 0xb1, 0x74, 0x47, 0x87, 0x5f, 0x41, 0xe6, 0x4c, 0x95, 0xaa, 0x1f, 0x21, 0xf8, 0xdc, 0x1e, 0x73, 0xed, 0x53, 0x97, 0x65, 0xd1, 0x15, 0x42, 0x5f, 0x55, 0xdf, 0xb2, 0x9d, 0x58, 0xdb, 0x93, 0xf8, 0x5b, 0x2, 0x89, 0x1c, 0x81, 0x9f, 0x2c, 0x93}, + y: fp384{0x1e, 0xa6, 0x6, 0x77, 0x20, 0xb2, 0x96, 0x9, 0x79, 0x1c, 0x64, 0xa8, 0xd5, 0x49, 0x53, 0x13, 0x44, 0x8, 0x13, 0x50, 0x6f, 0xd7, 0xaa, 0x65, 0x80, 0xf7, 0xff, 0x1, 0x4, 0x7c, 0xf3, 0xf, 0x6, 0x7, 0x3b, 0x69, 0x8e, 0x23, 0x7f, 0xf5, 0x3e, 0x9b, 0x6c, 0xaf, 0xb6, 0x16, 0xa, 0xd9}, + }, + // 9P + { + x: fp384{0x2f, 0xb9, 0x53, 0x23, 0xe, 0x20, 0x5d, 0x2f, 0xf9, 0xe4, 0xd7, 0x3f, 0x29, 0x87, 0x5d, 0xe3, 0x5d, 0x74, 0x6d, 0xa9, 0x33, 0x48, 0x9, 0x26, 0x3f, 0xff, 0xbf, 0x3c, 0xc1, 0x1d, 0x35, 0xdc, 0x6a, 0x4d, 0xd5, 0xda, 0xc6, 0x64, 0xd4, 0x26, 0x6a, 0x6c, 0x63, 0x53, 0x1d, 0x1d, 0xab, 0x5c}, + y: fp384{0xb0, 0xc0, 0x8e, 0xb1, 0x72, 0x30, 0x81, 0xf2, 0x2f, 0xaa, 0x42, 0xd7, 0x70, 0xe2, 0x77, 0x37, 0xc2, 0xa7, 0x3c, 0x3, 0xc7, 0x61, 0xf0, 0x27, 0xd8, 0xd0, 0xea, 0x68, 0xcc, 0xac, 0xec, 0xa6, 0x54, 0xa7, 0x69, 0xee, 0xf4, 0x29, 0x94, 0x7d, 0xc6, 0xf5, 0xe8, 0x31, 0x34, 0x63, 0x70, 0xe7}, + }, + // 11P + { + x: fp384{0x7d, 0x8c, 0x8b, 0xb6, 0x19, 0x8b, 0x70, 0xc7, 0xba, 0x7a, 0x37, 0x44, 0x7c, 0x7, 0x32, 0x45, 0x4f, 0xd6, 0xda, 0x6c, 0x70, 0x67, 0xcc, 0xd, 0x2, 0x66, 0x7b, 0x14, 0x56, 0xbf, 0xb8, 0x1, 0x79, 0x1d, 0x56, 0xf0, 0x85, 0x98, 0xd8, 0xf8, 0x37, 0xc4, 0xa9, 0x7b, 0xfc, 0xe9, 0x19, 0x9c}, + y: fp384{0x25, 0xba, 0xc4, 0xbd, 0x46, 0xb1, 0x4e, 0x76, 0x83, 0x4b, 0x14, 0xac, 0x6b, 0xe4, 0x4f, 0x60, 0x80, 0xe7, 0x77, 0x8a, 0x29, 0x13, 0xe8, 0x3c, 0x2e, 0x68, 0x9e, 0xfe, 0x36, 0xf, 0x7, 0x2e, 0x7a, 0x28, 0x53, 0x3a, 0xc, 0x1d, 0x82, 0x41, 0x18, 0xf9, 0x33, 0x35, 0x9f, 0x2f, 0xa6, 0x9a}, + }, + // 13P + { + x: fp384{0xfb, 0xbd, 0xcc, 0x75, 0x7e, 0xeb, 0x7a, 0x9b, 0x95, 0x9a, 0x74, 0xf6, 0xc5, 0x28, 0x5e, 0xb2, 0xae, 0xd4, 0xb7, 0x33, 0x46, 0x8e, 0x7a, 0x8a, 0x56, 0xbd, 0xc1, 0xd9, 0xa8, 0x3, 0x52, 0xdb, 0x97, 0xdf, 0x22, 0xed, 0x65, 0x72, 0x65, 0xd2, 0x94, 0x3c, 0xf2, 0x8c, 0xe1, 0x56, 0x1c, 0xb5}, + y: fp384{0x2d, 0x81, 0x3d, 0x6c, 0x59, 0x94, 0xd3, 0xf4, 0xc2, 0xe0, 0xca, 0x87, 0x1a, 0x8f, 0xe8, 0xd8, 0xe3, 0xf, 0x4d, 0xcf, 0x48, 0x2a, 0x9a, 0x78, 0x60, 0x8d, 0xc3, 0xfe, 0x2d, 0xac, 0xfe, 0xb7, 0xc3, 0xe, 0x49, 0x3b, 0x1c, 0xbd, 0xfd, 0x81, 0xe1, 0x79, 0x69, 0xcc, 0xb7, 0xad, 0x17, 0x46}, + }, + // 15P + { + x: fp384{0xa9, 0xf4, 0x9, 0x47, 0x88, 0xd8, 0x6a, 0x44, 0xd8, 0xab, 0x3d, 0xec, 0xe2, 0x10, 0x72, 0x2b, 0x34, 0x7b, 0xe0, 0x50, 0x95, 0xf1, 0xcc, 0x83, 0x75, 0x30, 0x9b, 0x78, 0x17, 0x9, 0x50, 0x59, 0x93, 0x59, 0x8, 0xeb, 0xd4, 0x1f, 0xc0, 0xf, 0x6b, 0x2, 0x3, 0x49, 0x6f, 0xd2, 0x62, 0xfb}, + y: fp384{0xbb, 0x89, 0xe9, 0x6f, 0x9d, 0xcc, 0x9, 0x23, 0x86, 0xd5, 0x4b, 0x14, 0xbd, 0x9c, 0x60, 0x61, 0xc, 0x61, 0x6, 0xde, 0xa0, 0xd3, 0x23, 0x4b, 0x70, 0xf4, 0x98, 0xd8, 0x66, 0x28, 0xdc, 0xdd, 0x97, 0x57, 0xc, 0x40, 0x41, 0xfc, 0x33, 0x87, 0x16, 0x27, 0xbc, 0xd0, 0xfe, 0xc6, 0x68, 0x5a}, + }, + // 17P + { + x: fp384{0xd0, 0x3c, 0x4a, 0x4b, 0x30, 0xe1, 0x3, 0x89, 0x3e, 0xf4, 0xf1, 0x8f, 0x4c, 0xea, 0xa4, 0x3e, 0xd, 0xa1, 0x55, 0xf6, 0x2a, 0x3f, 0xfc, 0xe6, 0xfc, 0xfe, 0x4f, 0x52, 0x7d, 0x73, 0xe3, 0x7b, 0x5e, 0x45, 0x30, 0x53, 0x55, 0x28, 0x69, 0x9f, 0x70, 0xce, 0x75, 0xe4, 0x6e, 0x16, 0x4f, 0x52}, + y: fp384{0x55, 0xf0, 0x12, 0x6c, 0xcd, 0x69, 0xcc, 0x3f, 0xda, 0xc0, 0xb9, 0xd5, 0xff, 0xb6, 0x23, 0x4e, 0x83, 0xf1, 0x6b, 0x33, 0x93, 0x69, 0xce, 0x49, 0x4a, 0x50, 0x54, 0x4a, 0x85, 0x6d, 0x7d, 0xf8, 0x7a, 0x67, 0xc2, 0xb3, 0xf1, 0x5d, 0xeb, 0x25, 0xc9, 0x64, 0xb1, 0x55, 0x6f, 0x98, 0x37, 0xac}, + }, + // 19P + { + x: fp384{0x8, 0x4c, 0xa8, 0xba, 0x4a, 0xed, 0xa2, 0x82, 0x12, 0xc9, 0xa8, 0x41, 0x5f, 0xcc, 0xc4, 0x22, 0x5e, 0xad, 0x4a, 0x15, 0x3b, 0x9c, 0x10, 0xca, 0x8e, 0x53, 0x38, 0xfc, 0x98, 0x12, 0x89, 0x23, 0xae, 0x2, 0x98, 0x53, 0x9c, 0x63, 0xb6, 0xb3, 0x6, 0xd7, 0x90, 0x3, 0x45, 0x1f, 0xf, 0xfa}, + y: fp384{0xd0, 0x21, 0xdc, 0xb0, 0x5d, 0x8e, 0xb7, 0x46, 0xac, 0x2e, 0xda, 0xc3, 0x3c, 0x2d, 0xc7, 0xa8, 0x43, 0xf6, 0xf2, 0x6f, 0x78, 0xb3, 0x70, 0x91, 0xc3, 0x30, 0x7f, 0xb6, 0x9b, 0x79, 0x5a, 0x3f, 0x72, 0xb6, 0x64, 0x82, 0x77, 0xdc, 0xd1, 0x15, 0x64, 0x77, 0x57, 0xe9, 0x23, 0x7b, 0xd4, 0xa1}, + }, + // 21P + { + x: fp384{0x2f, 0xce, 0x22, 0x4, 0x51, 0x5e, 0x26, 0x8, 0x21, 0x9e, 0x2f, 0xdd, 0x96, 0xd4, 0xe0, 0x88, 0x5d, 0xf7, 0x77, 0x61, 0xa0, 0x8a, 0x12, 0x30, 0x69, 0xbe, 0x9e, 0xbd, 0x62, 0xab, 0x59, 0x2e, 0x37, 0xe5, 0xf0, 0x5d, 0x6c, 0xf, 0x1a, 0x1b, 0xb5, 0x12, 0xc0, 0xda, 0x26, 0xc6, 0x16, 0xab}, + y: fp384{0xe7, 0x5d, 0x8c, 0x0, 0x4b, 0x21, 0x14, 0x80, 0xea, 0x7b, 0xf1, 0x38, 0x9e, 0xa, 0x74, 0xaa, 0x98, 0x90, 0x14, 0x8a, 0x49, 0xbb, 0x2e, 0x26, 0x59, 0xcd, 0x27, 0x85, 0x1e, 0x11, 0x54, 0xb4, 0x17, 0x58, 0xea, 0xac, 0x5a, 0xd1, 0x6a, 0x26, 0xba, 0xcc, 0x53, 0x13, 0x41, 0x4f, 0x82, 0x21}, + }, + // 23P + { + x: fp384{0x3b, 0x68, 0xe3, 0x12, 0x4d, 0xe7, 0xb4, 0xd1, 0xf6, 0x8e, 0x9b, 0x56, 0xb, 0xd2, 0xe, 0x99, 0x18, 0xa, 0x9c, 0x42, 0x25, 0xdd, 0xd3, 0xb9, 0x83, 0x17, 0x35, 0x2a, 0xab, 0xb8, 0x75, 0x1c, 0xf0, 0x32, 0x54, 0x90, 0x2b, 0xca, 0xe4, 0x61, 0x24, 0xf2, 0xa8, 0xee, 0x69, 0x6a, 0x82, 0x80}, + y: fp384{0xad, 0xab, 0x52, 0xec, 0x6b, 0x3a, 0xc3, 0x7f, 0x13, 0x48, 0x5e, 0xa6, 0xf0, 0xa3, 0xcc, 0xb, 0xbe, 0xce, 0x27, 0xa5, 0x32, 0xa1, 0xd8, 0x7a, 0x7e, 0x2c, 0xf2, 0xea, 0x50, 0x89, 0x13, 0xf0, 0xc1, 0x18, 0x67, 0x56, 0x37, 0x24, 0x2d, 0x28, 0x59, 0x25, 0x21, 0xe2, 0xd, 0xcb, 0xfc, 0x9d}, + }, + // 25P + { + x: fp384{0x83, 0x3b, 0xce, 0x58, 0x27, 0x72, 0x93, 0x1e, 0x36, 0xfb, 0xb3, 0x3c, 0xfa, 0xd, 0x28, 0xbb, 0x4a, 0x17, 0xbe, 0xe2, 0xd2, 0xf3, 0xd0, 0x57, 0x1e, 0xbe, 0x8a, 0x20, 0x99, 0x1b, 0xd5, 0x9b, 0x24, 0x80, 0x24, 0xde, 0x50, 0xab, 0x9, 0x38, 0x31, 0x73, 0xbb, 0xa5, 0x2c, 0x6e, 0x9c, 0xc2}, + y: fp384{0x5, 0x4f, 0x12, 0x61, 0x2e, 0xfd, 0x44, 0x99, 0x91, 0xe3, 0x9, 0x90, 0x4e, 0xbc, 0xcc, 0x83, 0xcc, 0xa3, 0x24, 0x94, 0x5, 0x8f, 0x62, 0x1, 0x44, 0x43, 0x8e, 0xea, 0x1d, 0xf5, 0xa2, 0xd6, 0x6e, 0xc9, 0xeb, 0x4c, 0x3d, 0x1a, 0x3e, 0xda, 0xdc, 0x9, 0x78, 0xe9, 0x42, 0xfb, 0xe6, 0x1f}, + }, + // 27P + { + x: fp384{0xe4, 0x66, 0x7d, 0x46, 0xd2, 0x82, 0x44, 0xa0, 0x1d, 0x29, 0x78, 0x4d, 0x93, 0x12, 0x19, 0xcf, 0xf9, 0x96, 0x23, 0x48, 0x68, 0x41, 0xd, 0x8e, 0xd0, 0x14, 0x8f, 0xd1, 0xd5, 0xe2, 0x28, 0x72, 0xfe, 0x58, 0x6a, 0x9c, 0x50, 0x8d, 0x7e, 0x2f, 0xec, 0x5a, 0x3e, 0x37, 0xe, 0x78, 0xca, 0xe8}, + y: fp384{0xf8, 0xe9, 0x68, 0x1b, 0xd6, 0xd1, 0xaa, 0x42, 0xf4, 0xf8, 0xe2, 0x69, 0xf5, 0xd7, 0xa6, 0x58, 0xea, 0x1b, 0xda, 0x31, 0xfe, 0xad, 0x79, 0xd7, 0x85, 0x5a, 0xc8, 0x38, 0x6, 0x54, 0x26, 0x7d, 0xdf, 0x3c, 0x4d, 0xd4, 0x95, 0x71, 0xe6, 0x67, 0xd7, 0x4e, 0x13, 0xc5, 0xb, 0xa, 0x82, 0x17}, + }, + // 29P + { + x: fp384{0x70, 0x14, 0x2, 0xd3, 0xc5, 0x6a, 0x9d, 0x1, 0xd6, 0x43, 0x4, 0x78, 0x66, 0x6b, 0x84, 0x25, 0x47, 0x76, 0xc9, 0x55, 0xed, 0x15, 0x3c, 0xce, 0xf, 0xeb, 0x3f, 0xe, 0x49, 0x2d, 0xc2, 0x3d, 0xe4, 0x26, 0xdf, 0xa7, 0xcb, 0xb7, 0x65, 0x20, 0x1f, 0xea, 0x7c, 0x18, 0xe8, 0xa, 0xb0, 0xc8}, + y: fp384{0xd3, 0xde, 0x5d, 0x86, 0xa0, 0x84, 0x52, 0x1a, 0xe2, 0x3d, 0xc8, 0x20, 0x49, 0x16, 0x3c, 0x29, 0xb3, 0x51, 0xe8, 0xcc, 0x26, 0x8d, 0x17, 0xab, 0xfb, 0x5, 0x45, 0x40, 0xb, 0xb1, 0x6d, 0x8e, 0x33, 0x20, 0xc8, 0x90, 0x71, 0x7e, 0xf5, 0xf6, 0x6c, 0xf1, 0x77, 0x59, 0x1, 0x1c, 0x2a, 0x1d}, + }, + // 31P + { + x: fp384{0xa4, 0x6, 0x89, 0x7c, 0x31, 0x89, 0x9c, 0xa3, 0xe6, 0x1e, 0x82, 0x9e, 0xdd, 0xec, 0xe7, 0xb6, 0xe6, 0x4f, 0xdf, 0xf0, 0x40, 0x83, 0xcf, 0x2e, 0x65, 0x49, 0xc1, 0x53, 0xc9, 0x7d, 0x2f, 0xd4, 0x85, 0x82, 0xba, 0xe3, 0xa3, 0x51, 0xfb, 0x1a, 0xd1, 0x5, 0x33, 0xa, 0x4, 0xc4, 0x7, 0x6c}, + y: fp384{0xda, 0xc1, 0x7f, 0x12, 0x88, 0x32, 0xb8, 0xda, 0x8, 0x4b, 0x4c, 0x37, 0x9b, 0x69, 0xa, 0xbc, 0xdd, 0x20, 0xeb, 0x42, 0xab, 0x9b, 0x2a, 0x40, 0x1c, 0x7a, 0x5a, 0x4, 0x4f, 0x46, 0xdd, 0xd7, 0xc4, 0xec, 0xbe, 0x36, 0x6d, 0xd, 0x3d, 0x5b, 0x9d, 0xa1, 0x98, 0x63, 0x75, 0x3e, 0x5a, 0x47}, + }, + // 33P + { + x: fp384{0x63, 0xba, 0xb3, 0x2f, 0x38, 0x3a, 0x33, 0x61, 0x86, 0x3c, 0x94, 0x5b, 0x9d, 0xd, 0x33, 0xdf, 0xaf, 0xf3, 0x5e, 0x95, 0xee, 0xc7, 0xc7, 0xbb, 0xfb, 0x9e, 0xf0, 0x60, 0xc1, 0x1f, 0x63, 0xda, 0x0, 0xc4, 0xd5, 0x41, 0x26, 0x62, 0xaf, 0x68, 0x9d, 0x3e, 0x83, 0x6c, 0xa4, 0x97, 0x9e, 0xcc}, + y: fp384{0x76, 0x5e, 0x62, 0x3a, 0x8e, 0x3e, 0xd7, 0x7f, 0x5e, 0xe5, 0x9, 0xc2, 0x24, 0x61, 0xbf, 0x13, 0x91, 0xb, 0xb9, 0x48, 0xea, 0x7c, 0x46, 0x8, 0xba, 0xa, 0x6f, 0xbb, 0xb9, 0x6e, 0x41, 0x8a, 0x72, 0x10, 0xc3, 0xb8, 0xa1, 0x93, 0xcc, 0x6f, 0xd7, 0xda, 0x57, 0x90, 0x61, 0x2b, 0xfd, 0xa7}, + }, + // 35P + { + x: fp384{0x9b, 0xec, 0x20, 0x37, 0x43, 0xb5, 0xa5, 0x58, 0xb4, 0x2f, 0x7c, 0x2d, 0xd5, 0x0, 0x38, 0xbb, 0xa, 0xbd, 0xe6, 0xdd, 0x20, 0x86, 0x50, 0x4a, 0xfd, 0x83, 0x25, 0xa0, 0x73, 0x62, 0xf1, 0x65, 0x23, 0x85, 0xc7, 0x4f, 0xe3, 0xd8, 0x2b, 0x83, 0xc6, 0x7b, 0x41, 0xe9, 0x75, 0x9f, 0x14, 0xd6}, + y: fp384{0x2a, 0xb5, 0xee, 0x3d, 0xe9, 0x26, 0xb0, 0xfe, 0x56, 0x9, 0x5e, 0xa5, 0x88, 0x80, 0xe1, 0xc, 0xa2, 0x92, 0x80, 0x98, 0x98, 0x89, 0x1, 0x50, 0xee, 0x5e, 0xf3, 0x28, 0xab, 0x9f, 0xf1, 0x22, 0x5c, 0xd3, 0xcc, 0x52, 0x7f, 0x87, 0x8a, 0xac, 0x26, 0x3f, 0xe2, 0x30, 0xd8, 0x8a, 0x3a, 0xb1}, + }, + // 37P + { + x: fp384{0xa3, 0x61, 0x4f, 0xe4, 0x7d, 0xd5, 0x2, 0x2, 0xf2, 0xe, 0x63, 0xb5, 0x4b, 0x70, 0x27, 0x40, 0x5d, 0x4a, 0xb5, 0xf5, 0xdf, 0xe2, 0x29, 0xa1, 0x86, 0x2b, 0x48, 0x97, 0x75, 0xa, 0xb6, 0xac, 0x14, 0x71, 0xf2, 0x7e, 0xe8, 0xed, 0x61, 0x92, 0xb5, 0x58, 0xfc, 0xde, 0xf3, 0x28, 0xba, 0x1e}, + y: fp384{0x9e, 0x58, 0xe5, 0x8b, 0xc9, 0xc0, 0x91, 0x6c, 0xee, 0x4b, 0x59, 0x14, 0xd5, 0x43, 0x16, 0x2f, 0x34, 0xa0, 0x2c, 0x5d, 0x43, 0x12, 0xa9, 0x2e, 0x1f, 0x7d, 0x4, 0x94, 0xa8, 0x49, 0x6, 0xb5, 0x37, 0xa3, 0x8c, 0x63, 0xb5, 0xcb, 0x4f, 0x28, 0x85, 0xbf, 0x85, 0xfe, 0xb7, 0x7, 0xe, 0xfa}, + }, + // 39P + { + x: fp384{0x42, 0xe, 0x6e, 0x50, 0x80, 0x4f, 0x89, 0x7d, 0x46, 0x2c, 0x3d, 0x8e, 0x4a, 0x24, 0x84, 0xd9, 0x6f, 0x0, 0x7f, 0x2b, 0x64, 0xdf, 0x7e, 0x6d, 0x30, 0x62, 0x9b, 0xde, 0x6d, 0xcd, 0xa1, 0x36, 0x65, 0x6, 0x6c, 0xb7, 0x40, 0x50, 0x98, 0xc9, 0xc2, 0x1f, 0x9b, 0xb8, 0xd6, 0xf4, 0x7d, 0x58}, + y: fp384{0x7a, 0xae, 0x71, 0x6a, 0x47, 0x38, 0x6, 0x4c, 0x47, 0x47, 0x29, 0xe8, 0xb3, 0xa, 0x2b, 0x7b, 0xb8, 0x53, 0x31, 0xb5, 0x3a, 0x55, 0x5c, 0x34, 0xe2, 0x9f, 0x6d, 0x43, 0x53, 0xe4, 0x46, 0xb6, 0x40, 0x3, 0xd6, 0x1c, 0x5f, 0x35, 0x95, 0x1a, 0xfb, 0x68, 0x49, 0x7, 0x28, 0xc1, 0x7b, 0x2d}, + }, + // 41P + { + x: fp384{0x4c, 0xd1, 0xa6, 0xbc, 0x87, 0x8e, 0x14, 0xad, 0x1e, 0x20, 0x6a, 0x45, 0x4d, 0xd2, 0xdf, 0x41, 0xf3, 0x68, 0xd, 0xa8, 0x33, 0x29, 0xa8, 0x73, 0x35, 0xa0, 0x2c, 0x85, 0x8d, 0x6c, 0x74, 0x89, 0xae, 0x71, 0xfd, 0x95, 0x88, 0x77, 0xbc, 0xe3, 0x5d, 0x24, 0x92, 0xda, 0x2c, 0xcd, 0x64, 0x87}, + y: fp384{0xe2, 0x23, 0xeb, 0x82, 0x47, 0x2c, 0xfe, 0xa2, 0x6e, 0x9d, 0x3c, 0xf, 0xe0, 0x62, 0xc7, 0x5a, 0x31, 0x6f, 0x64, 0x21, 0xe1, 0xc, 0x86, 0x57, 0x9a, 0x58, 0x9f, 0x4f, 0xc3, 0xd6, 0xc9, 0xbd, 0x2e, 0x27, 0x93, 0xd1, 0xc7, 0x52, 0x99, 0x67, 0xc5, 0xf1, 0x18, 0xeb, 0x2e, 0x70, 0xea, 0x82}, + }, + // 43P + { + x: fp384{0x44, 0x6d, 0x84, 0x0, 0x55, 0x93, 0xfa, 0x37, 0x8c, 0xbc, 0x78, 0x5, 0xc5, 0x2f, 0x11, 0x9, 0x3d, 0x94, 0xc4, 0x39, 0xb2, 0xf5, 0xd9, 0xda, 0x86, 0xbd, 0x6d, 0x41, 0xf0, 0xf5, 0x14, 0x73, 0x56, 0xfb, 0xfe, 0x1, 0xa9, 0x95, 0xf0, 0x5c, 0x93, 0xb3, 0xda, 0x22, 0xad, 0x8b, 0x17, 0x35}, + y: fp384{0xa7, 0xf1, 0xba, 0x36, 0x1b, 0xfc, 0x79, 0xcf, 0x98, 0x54, 0x9e, 0x74, 0x2d, 0xe4, 0x7e, 0x1b, 0xbb, 0x14, 0xe3, 0xed, 0xa9, 0x8a, 0xe7, 0xbc, 0xdf, 0x28, 0x6, 0xbd, 0xf6, 0xe0, 0xf8, 0xaa, 0x48, 0xf9, 0xcb, 0x15, 0x94, 0xb0, 0x74, 0xa9, 0x78, 0x2b, 0x63, 0xc9, 0x63, 0x1f, 0x3f, 0x8f}, + }, + // 45P + { + x: fp384{0x5b, 0xda, 0xdd, 0x4f, 0x56, 0x11, 0xc4, 0xd4, 0x12, 0x91, 0xad, 0x73, 0xc6, 0x65, 0xaf, 0xd4, 0x59, 0x8f, 0xeb, 0x39, 0xbb, 0xe0, 0xe8, 0xff, 0x13, 0xcf, 0x6f, 0x8d, 0xe, 0xc, 0x4, 0xb0, 0x99, 0xb5, 0x2b, 0x1f, 0xc6, 0xc0, 0xe1, 0x99, 0x5, 0x34, 0xac, 0xb2, 0x58, 0xc8, 0x94, 0x9c}, + y: fp384{0x5d, 0xd8, 0xee, 0x6e, 0xd7, 0x78, 0x88, 0x8f, 0x3f, 0xca, 0xfc, 0x51, 0x43, 0xf5, 0xb2, 0x62, 0x18, 0x69, 0xb5, 0xe5, 0xa9, 0x44, 0x3b, 0xeb, 0x93, 0x4e, 0x23, 0xb7, 0x76, 0x66, 0xf9, 0x16, 0x9e, 0xf1, 0x2a, 0xbd, 0x22, 0x77, 0x47, 0x17, 0x85, 0xa4, 0x83, 0xdb, 0x79, 0x29, 0xeb, 0x42}, + }, + // 47P + { + x: fp384{0xca, 0x68, 0xc6, 0xf0, 0x7d, 0x8f, 0x88, 0x6f, 0x6c, 0xc6, 0xd, 0x5f, 0x78, 0x88, 0xc7, 0x65, 0xa0, 0x7, 0x5b, 0x5f, 0x12, 0x85, 0xb1, 0xbf, 0xd0, 0xac, 0x78, 0xd8, 0xf7, 0xbf, 0xa, 0x78, 0x50, 0xf9, 0xc, 0x57, 0xb1, 0x21, 0x4f, 0x50, 0x71, 0x33, 0x23, 0xda, 0xc5, 0x37, 0x5b, 0xea}, + y: fp384{0xd1, 0x7e, 0x43, 0x22, 0xbd, 0xe8, 0x7a, 0x48, 0xb7, 0xf9, 0x9c, 0x24, 0x58, 0x17, 0x70, 0x9c, 0xff, 0x34, 0xfb, 0x98, 0xa8, 0x62, 0x65, 0xf8, 0x91, 0xfc, 0xe0, 0x65, 0xa2, 0xa1, 0xee, 0xdf, 0x23, 0xfc, 0x20, 0x2e, 0x91, 0x6, 0xf0, 0xee, 0x8b, 0x2a, 0xa7, 0xdf, 0xc7, 0xfe, 0x9d, 0xac}, + }, + // 49P + { + x: fp384{0xc6, 0x36, 0x71, 0x69, 0xef, 0x3a, 0x5c, 0xfa, 0xb8, 0x6f, 0xea, 0xa5, 0x63, 0xaf, 0xa5, 0x8e, 0xa4, 0x65, 0xe3, 0x42, 0x65, 0x15, 0x69, 0xa6, 0x86, 0x33, 0x6e, 0x5b, 0x11, 0x6c, 0xc5, 0x47, 0x56, 0x3f, 0xa0, 0xce, 0x2b, 0x83, 0x97, 0x11, 0x9e, 0xea, 0xe4, 0x50, 0xb2, 0xb, 0x47, 0xb}, + y: fp384{0x12, 0x57, 0xb2, 0x13, 0x43, 0xc7, 0x13, 0x31, 0x48, 0x7d, 0x49, 0xd2, 0x4e, 0x17, 0x6c, 0x8d, 0xe8, 0xeb, 0xc9, 0x49, 0xee, 0x86, 0x44, 0xfc, 0xd3, 0xbd, 0x82, 0x7f, 0xd5, 0xed, 0x87, 0x24, 0x2f, 0xbe, 0x57, 0x5b, 0x41, 0x64, 0x1e, 0x77, 0xdb, 0x2b, 0x8b, 0xe2, 0x18, 0xc5, 0x1c, 0x2d}, + }, + // 51P + { + x: fp384{0x8d, 0xac, 0x70, 0x20, 0xc7, 0xca, 0x4c, 0x2c, 0xb8, 0x22, 0x4a, 0xec, 0xca, 0xc0, 0x47, 0x19, 0xd9, 0x78, 0x5a, 0x8c, 0x59, 0xfb, 0xe0, 0xa5, 0xe7, 0x4d, 0xa8, 0x41, 0xd2, 0xe8, 0x4a, 0x46, 0x27, 0xbc, 0xaa, 0xda, 0xe9, 0x16, 0xba, 0x3d, 0x3c, 0xcb, 0x35, 0x4f, 0x50, 0x4a, 0x63, 0x16}, + y: fp384{0x4f, 0xc8, 0x6e, 0xb1, 0xf9, 0x8b, 0xc1, 0xad, 0x35, 0xdd, 0x59, 0x73, 0x7e, 0x6, 0x4d, 0x32, 0xf0, 0x43, 0x5, 0x57, 0xc3, 0xc0, 0xea, 0xda, 0x36, 0x7d, 0x88, 0x3c, 0x0, 0x40, 0x22, 0xb, 0xd, 0x1a, 0x3f, 0x37, 0xe2, 0x89, 0x94, 0xc6, 0x97, 0xd, 0xaa, 0xcb, 0x7d, 0x4, 0x8b, 0x51}, + }, + // 53P + { + x: fp384{0xef, 0x49, 0xde, 0xfb, 0xc6, 0xdd, 0x1b, 0x3b, 0xcc, 0x15, 0x9, 0x8a, 0x26, 0x7c, 0xed, 0xda, 0xa2, 0x22, 0x4, 0xf, 0x61, 0x10, 0x1, 0xb, 0x16, 0x4b, 0xc5, 0xa7, 0x74, 0x5c, 0x48, 0xcf, 0xe2, 0xaa, 0xc3, 0x15, 0xe6, 0xc4, 0x2e, 0x64, 0xea, 0x83, 0xf3, 0xe0, 0x10, 0x8f, 0xba, 0xa8}, + y: fp384{0x1, 0x85, 0x61, 0x95, 0xb4, 0x54, 0x20, 0x2a, 0x8b, 0xfa, 0x9e, 0x8, 0x42, 0x64, 0xec, 0xeb, 0x3e, 0xa8, 0x2f, 0x4e, 0x9a, 0xa1, 0x86, 0x57, 0x63, 0x99, 0x6, 0x39, 0xd1, 0x1a, 0xc7, 0xd2, 0xe2, 0x65, 0x17, 0x48, 0x9a, 0x3d, 0xc9, 0xad, 0x85, 0x94, 0xcc, 0x7e, 0xeb, 0xe3, 0xf2, 0xed}, + }, + // 55P + { + x: fp384{0x67, 0x33, 0x9f, 0x6, 0x60, 0x5f, 0xab, 0xbc, 0x3c, 0xec, 0x18, 0x17, 0xbc, 0x22, 0x66, 0xfd, 0xd6, 0x42, 0xa1, 0xe3, 0x67, 0x78, 0xfb, 0xa4, 0xb3, 0xae, 0x5f, 0x8, 0xbf, 0xd8, 0x78, 0x60, 0x4f, 0x55, 0xf4, 0x60, 0xda, 0xbf, 0x5c, 0xfa, 0x8, 0xd4, 0xc, 0x69, 0xd1, 0xd5, 0xfc, 0xb3}, + y: fp384{0x84, 0x78, 0x1f, 0x28, 0x7d, 0xee, 0xbd, 0x4e, 0xa7, 0x63, 0xa, 0x18, 0xaa, 0x23, 0xaf, 0x82, 0x61, 0x9f, 0x7, 0x3d, 0x7c, 0x10, 0xe3, 0x8d, 0xf8, 0x34, 0x23, 0xbe, 0xcb, 0xb5, 0xc6, 0x17, 0x6, 0xfa, 0xd0, 0x97, 0x39, 0xe7, 0x91, 0x6a, 0xd4, 0xee, 0xce, 0x14, 0x73, 0x25, 0x60, 0x74}, + }, + // 57P + { + x: fp384{0x5c, 0x86, 0x7f, 0xf9, 0x1c, 0xa6, 0x4b, 0xb1, 0xd, 0x8b, 0x4b, 0x69, 0xc1, 0xe4, 0xba, 0x73, 0x62, 0xbf, 0x4b, 0xac, 0xdf, 0x67, 0x49, 0xa1, 0xe0, 0x46, 0xf4, 0x9b, 0x50, 0xd1, 0x9d, 0x1e, 0xef, 0xce, 0x99, 0x1c, 0xeb, 0xf3, 0x52, 0xc0, 0x89, 0xc1, 0x78, 0x7a, 0xa0, 0x7f, 0x4d, 0x81}, + y: fp384{0x5d, 0xb0, 0x74, 0xab, 0x83, 0xa4, 0x1, 0xa1, 0x65, 0x7b, 0x73, 0xa1, 0x58, 0xc2, 0x88, 0x77, 0x3c, 0xa1, 0x9, 0xe8, 0xb7, 0xba, 0x60, 0xd, 0x5b, 0x1d, 0xc8, 0x73, 0xc4, 0x7b, 0x42, 0x8f, 0xfc, 0xc1, 0x52, 0x29, 0x55, 0x30, 0xe1, 0xd2, 0x63, 0xdf, 0x26, 0x4b, 0x9a, 0x3b, 0x82, 0xa}, + }, + // 59P + { + x: fp384{0xc9, 0x64, 0xbf, 0x27, 0xe2, 0x7c, 0x46, 0xaf, 0x4c, 0x97, 0x29, 0xf9, 0x97, 0x68, 0xca, 0xdf, 0x38, 0x27, 0x32, 0x5c, 0x59, 0x3b, 0x47, 0x64, 0x15, 0xe3, 0xd0, 0x1e, 0xcf, 0x17, 0xa9, 0x96, 0xb9, 0x4d, 0xe6, 0xd, 0x5b, 0x43, 0x3, 0x37, 0x46, 0xb6, 0x67, 0x92, 0x67, 0x39, 0xa0, 0x9b}, + y: fp384{0xbe, 0x2f, 0x52, 0x3a, 0xae, 0x2a, 0xc, 0xdf, 0xf0, 0xef, 0x35, 0xb3, 0x41, 0xb7, 0xbd, 0x41, 0x3, 0x97, 0x5, 0x7b, 0xdd, 0x2e, 0xcf, 0xac, 0xce, 0x3c, 0x46, 0x28, 0x30, 0x4b, 0xb3, 0x6f, 0x19, 0xca, 0xe3, 0xd9, 0xb, 0xba, 0xd9, 0x96, 0xc1, 0x55, 0x46, 0x50, 0x12, 0x6f, 0x33, 0xff}, + }, + // 61P + { + x: fp384{0xe0, 0xa6, 0x60, 0xfc, 0xd3, 0x1f, 0xda, 0x48, 0xe8, 0x41, 0x22, 0x22, 0x34, 0x5a, 0xfb, 0x54, 0x80, 0xe0, 0x2a, 0x77, 0x4f, 0xe3, 0x35, 0x60, 0xd0, 0x82, 0x29, 0x33, 0xf2, 0x7f, 0xf7, 0x5f, 0xfd, 0x51, 0xfe, 0x0, 0x73, 0x46, 0x66, 0x23, 0x6, 0xa0, 0x6b, 0xef, 0x49, 0xa0, 0x3e, 0xc9}, + y: fp384{0x66, 0x12, 0x38, 0x7d, 0x17, 0xf1, 0x40, 0x66, 0xac, 0xf4, 0xe9, 0x6a, 0xcd, 0x32, 0x4d, 0x39, 0xeb, 0x3, 0xd3, 0x70, 0x53, 0x88, 0xa7, 0xe6, 0x67, 0x57, 0x27, 0xe5, 0xff, 0x19, 0xda, 0xd, 0x23, 0x6d, 0x46, 0x1, 0x72, 0xc7, 0xa6, 0xb0, 0x29, 0x98, 0xc6, 0x1f, 0x45, 0x11, 0xcc, 0xc4}, + }, + // 63P + { + x: fp384{0xc0, 0x89, 0xed, 0xaa, 0xd7, 0xe6, 0xc0, 0xc5, 0x96, 0x18, 0x9a, 0x14, 0xd6, 0xea, 0xe8, 0x6c, 0x8f, 0x9f, 0x94, 0x8c, 0x45, 0xf7, 0x50, 0x7a, 0xaa, 0x71, 0x2b, 0x6e, 0xf7, 0x35, 0x7e, 0xcd, 0x7a, 0x9f, 0x4, 0x9a, 0x51, 0x9e, 0x15, 0xf6, 0x1e, 0x2d, 0xe5, 0xf1, 0xb0, 0xf0, 0x9b, 0x1c}, + y: fp384{0x80, 0x2c, 0x20, 0x18, 0xf5, 0xc1, 0xb6, 0x3b, 0x1a, 0x7b, 0xcd, 0x1e, 0x62, 0x5f, 0x3a, 0x8d, 0x19, 0x7f, 0xd1, 0x88, 0xe8, 0x34, 0xb0, 0x3b, 0x8d, 0x4, 0xd4, 0x97, 0x49, 0xbd, 0x89, 0xdc, 0x22, 0xdf, 0x35, 0x37, 0x8e, 0x7b, 0xaf, 0xf5, 0xe8, 0x89, 0xa6, 0xa0, 0x12, 0x37, 0xbb, 0x52}, + }, + // 65P + { + x: fp384{0x79, 0x96, 0x9b, 0x83, 0x54, 0x94, 0x46, 0x8e, 0x9f, 0x27, 0x1d, 0xd, 0x3b, 0xa4, 0xcd, 0xbe, 0x80, 0x3c, 0xb6, 0xed, 0x15, 0xdc, 0x9e, 0xcf, 0x2, 0xf0, 0xd5, 0xbd, 0x8a, 0xec, 0x97, 0x45, 0x53, 0x22, 0xb, 0x65, 0xb2, 0x50, 0x3, 0x8b, 0x6f, 0x26, 0xd4, 0x5f, 0x6a, 0x3a, 0x4c, 0xb8}, + y: fp384{0xf9, 0x79, 0xfc, 0x30, 0x3d, 0xd8, 0x16, 0xe, 0xd3, 0x95, 0xd9, 0x3a, 0x83, 0x87, 0xa4, 0xb6, 0x66, 0xc2, 0x2b, 0xde, 0x2c, 0x4b, 0x78, 0xab, 0xdd, 0x66, 0x9d, 0x88, 0x6d, 0x3d, 0x76, 0x19, 0x87, 0xf0, 0xf9, 0xc8, 0x24, 0x6c, 0x86, 0xa2, 0xc9, 0xb1, 0x5b, 0xa5, 0x28, 0x25, 0x6f, 0xea}, + }, + // 67P + { + x: fp384{0xca, 0x72, 0x17, 0x8b, 0xbc, 0x5c, 0xfc, 0x6e, 0x68, 0x4f, 0x63, 0xb, 0x3b, 0xdd, 0xc7, 0xea, 0x85, 0x61, 0x5d, 0xa5, 0xda, 0x5e, 0xd7, 0x65, 0xf7, 0xd8, 0xf3, 0x6e, 0x7e, 0x63, 0x6e, 0x55, 0x25, 0x9d, 0x90, 0x90, 0x4d, 0x3e, 0xaf, 0xf9, 0x5a, 0x3, 0x53, 0xe1, 0x48, 0xca, 0x62, 0xe4}, + y: fp384{0xd1, 0x9e, 0x10, 0xa, 0x7b, 0x3b, 0x76, 0x52, 0x6, 0x5d, 0x78, 0x42, 0xa6, 0xb1, 0x4a, 0x10, 0xbc, 0xe4, 0x59, 0x3f, 0xed, 0x35, 0x7e, 0x1a, 0x3, 0xc0, 0xeb, 0xc6, 0x46, 0xb7, 0x90, 0x59, 0xf9, 0xc5, 0x62, 0x13, 0xf0, 0x6b, 0x6a, 0x9, 0x94, 0x49, 0x79, 0xac, 0x8a, 0xcb, 0x22, 0x83}, + }, + // 69P + { + x: fp384{0x52, 0x27, 0xc, 0x67, 0x74, 0xdf, 0xd3, 0xf8, 0x28, 0x5e, 0x11, 0xa1, 0x59, 0x28, 0xcb, 0x5c, 0x1b, 0x98, 0x19, 0xf, 0xaa, 0xc8, 0x66, 0xcc, 0x65, 0xfd, 0xab, 0x25, 0xb1, 0xc8, 0xbc, 0xd4, 0xca, 0x3c, 0xdd, 0x4f, 0x16, 0xee, 0x86, 0x2c, 0xf4, 0x35, 0xf7, 0xa3, 0x78, 0x5d, 0xd6, 0x59}, + y: fp384{0xce, 0x55, 0xc, 0x7c, 0x28, 0xde, 0x5a, 0x3, 0x4d, 0x99, 0xd3, 0x45, 0x62, 0x9d, 0x76, 0x57, 0xa4, 0x23, 0xd4, 0x2b, 0x89, 0xe4, 0x3a, 0xd1, 0xbc, 0x66, 0xed, 0x82, 0x48, 0xee, 0xce, 0xa, 0x32, 0x7a, 0x16, 0x73, 0x8b, 0x9e, 0x2c, 0x6f, 0x7a, 0xbd, 0x1d, 0x27, 0x0, 0x1, 0xcd, 0xfb}, + }, + // 71P + { + x: fp384{0xb1, 0xfc, 0xfe, 0xef, 0x45, 0xce, 0x2e, 0xc4, 0x41, 0x14, 0xb1, 0xab, 0xb3, 0x31, 0x57, 0xbf, 0x92, 0xa, 0x8d, 0xe5, 0xcf, 0x15, 0xba, 0x93, 0x54, 0xac, 0x8, 0x35, 0x14, 0xde, 0xbb, 0x27, 0xda, 0x5d, 0x25, 0xfe, 0x38, 0x37, 0x12, 0x2b, 0x70, 0xcd, 0x7e, 0x53, 0x5a, 0xaa, 0x4f, 0xb}, + y: fp384{0xc6, 0x83, 0xec, 0xdd, 0x84, 0xd0, 0x44, 0x6a, 0x3d, 0x52, 0x1, 0x8, 0x4d, 0x3c, 0x79, 0x86, 0x19, 0xe3, 0xb3, 0x25, 0x50, 0x28, 0x4f, 0x15, 0x3a, 0x37, 0x64, 0xce, 0xf1, 0xda, 0x44, 0x21, 0x66, 0x95, 0x13, 0x7, 0xee, 0x24, 0xb9, 0xbd, 0x1c, 0xd0, 0x2a, 0x73, 0x28, 0xea, 0xa7, 0xf9}, + }, + // 73P + { + x: fp384{0x2, 0xa7, 0x51, 0xd8, 0x32, 0x5c, 0x38, 0x43, 0x3b, 0x96, 0xe8, 0x1d, 0xb1, 0xa9, 0xc2, 0xa9, 0x24, 0x9b, 0x8b, 0xdf, 0xb, 0x23, 0x8d, 0x5b, 0xc2, 0x31, 0x37, 0x2d, 0x25, 0xa9, 0x29, 0x90, 0xd2, 0xa7, 0xca, 0xe8, 0x4d, 0xb0, 0x8d, 0xb0, 0x3c, 0x67, 0xf4, 0x2b, 0xe7, 0x83, 0x9d, 0xb8}, + y: fp384{0x24, 0x44, 0xf2, 0xe1, 0xbf, 0x39, 0xfd, 0xa8, 0x16, 0xd6, 0xe8, 0xe1, 0xd6, 0xb6, 0x89, 0x9f, 0x6e, 0x21, 0x1d, 0x4f, 0x7b, 0xc8, 0x9f, 0xaf, 0x59, 0x5a, 0x26, 0x92, 0x3, 0x1e, 0xe2, 0x4, 0x73, 0x63, 0xb8, 0x87, 0x3, 0x74, 0x15, 0xb, 0x62, 0xa1, 0x98, 0x7c, 0x3b, 0xaf, 0x65, 0x6f}, + }, + // 75P + { + x: fp384{0xde, 0x3, 0x73, 0x86, 0x14, 0x40, 0x5d, 0x6b, 0xef, 0xb7, 0xa, 0x42, 0xa, 0xc4, 0xcb, 0xc8, 0x96, 0x9a, 0x54, 0x91, 0x89, 0xcf, 0x28, 0x5b, 0x66, 0x8f, 0x2f, 0x39, 0xb4, 0x31, 0x46, 0x5d, 0xc8, 0xb5, 0x67, 0xf3, 0x3d, 0xe2, 0x7e, 0x4a, 0x15, 0x7a, 0xca, 0xe6, 0xf, 0xae, 0xe0, 0xa1}, + y: fp384{0x7c, 0xa2, 0x9f, 0x13, 0xe1, 0x5e, 0x65, 0x2a, 0x1, 0xb9, 0xfa, 0x50, 0xc4, 0xc2, 0x31, 0xa5, 0x71, 0xed, 0xa, 0x43, 0x69, 0xab, 0x62, 0x67, 0x1d, 0x2e, 0x40, 0xf0, 0xe3, 0x39, 0xe2, 0x64, 0x45, 0xe4, 0x7f, 0x95, 0x42, 0x0, 0x87, 0xd7, 0x62, 0x65, 0x7d, 0x41, 0x62, 0xd8, 0x42, 0xfd}, + }, + // 77P + { + x: fp384{0x3f, 0x1a, 0x50, 0x2a, 0xc1, 0x34, 0x21, 0x9f, 0xa7, 0x93, 0x47, 0x33, 0x40, 0x9b, 0x4a, 0x11, 0x25, 0x61, 0x2a, 0x21, 0xc0, 0xb6, 0xcc, 0x27, 0xa3, 0x78, 0xce, 0x98, 0xb9, 0x30, 0xa9, 0xc, 0x27, 0x26, 0xa1, 0x28, 0xb, 0x6, 0x2e, 0x74, 0x16, 0x98, 0x5c, 0xfa, 0x7, 0x36, 0xd2, 0x63}, + y: fp384{0xdc, 0x91, 0x1f, 0xec, 0xa4, 0xcb, 0x4d, 0x77, 0x23, 0x1d, 0x4d, 0xf3, 0x2e, 0x7e, 0xcd, 0x34, 0x3d, 0x3c, 0xfd, 0x49, 0xe, 0x85, 0x0, 0x15, 0xb2, 0xcf, 0xc9, 0x30, 0x0, 0x95, 0x3, 0xdd, 0xa6, 0x41, 0x29, 0x57, 0xe5, 0xc3, 0x61, 0xce, 0x2e, 0xfd, 0x2c, 0x30, 0x75, 0x10, 0x4b, 0xd4}, + }, + // 79P + { + x: fp384{0x24, 0xc2, 0xa6, 0xe9, 0xd4, 0xd9, 0x11, 0xca, 0x85, 0x45, 0xb7, 0xc3, 0x48, 0xc2, 0xda, 0x2c, 0x71, 0x55, 0xca, 0x4, 0xa2, 0x1, 0x89, 0xe5, 0xf0, 0x1, 0x5d, 0xd1, 0x2d, 0x31, 0x1c, 0x49, 0x7c, 0xcb, 0x69, 0x70, 0x8a, 0x81, 0xcc, 0xb3, 0xda, 0x12, 0x39, 0xd5, 0xa5, 0x45, 0xb2, 0x23}, + y: fp384{0x63, 0xb1, 0xb0, 0xa6, 0x8b, 0x83, 0xf7, 0x30, 0x52, 0x55, 0x77, 0x32, 0x3e, 0xeb, 0x52, 0xa0, 0xc1, 0x5e, 0xb9, 0xee, 0xe9, 0x43, 0x32, 0x40, 0xb3, 0x2e, 0x63, 0x8, 0x1b, 0x30, 0x7b, 0x61, 0xf9, 0x81, 0x2, 0xf3, 0xc9, 0x66, 0x1c, 0x94, 0x55, 0xb2, 0x7a, 0x1d, 0xf8, 0x28, 0x97, 0x4b}, + }, + // 81P + { + x: fp384{0x82, 0xf6, 0x6, 0xdb, 0xcb, 0xd3, 0xbd, 0x3c, 0x85, 0x87, 0xc0, 0x86, 0x3e, 0x47, 0x45, 0xde, 0xfb, 0x1b, 0x7b, 0xe0, 0x60, 0x35, 0xcb, 0xd8, 0xaf, 0x38, 0xf1, 0xea, 0x8f, 0x11, 0xc3, 0xf2, 0xaf, 0x6f, 0x43, 0x9f, 0x85, 0x8a, 0x23, 0x11, 0x4f, 0x34, 0xdf, 0x7c, 0xe3, 0xc2, 0x84, 0x9}, + y: fp384{0x4f, 0x58, 0x82, 0xc3, 0x17, 0x3c, 0x8, 0xe1, 0x40, 0xa2, 0x6f, 0x73, 0xd4, 0x14, 0x9a, 0x95, 0x7d, 0x63, 0xf2, 0x18, 0xf0, 0xd2, 0xad, 0x56, 0x8d, 0x27, 0xdd, 0xe5, 0x5e, 0x7a, 0x4, 0x1, 0xa7, 0x6, 0x27, 0xae, 0x63, 0x8d, 0xe3, 0x5d, 0x7e, 0xb8, 0x45, 0x97, 0x60, 0xb7, 0xd7, 0x9d}, + }, + // 83P + { + x: fp384{0x71, 0x40, 0xe9, 0x44, 0xbf, 0xf0, 0x61, 0x8e, 0xb4, 0x5e, 0xf0, 0xd2, 0x7e, 0x84, 0xcf, 0x44, 0xeb, 0x94, 0x66, 0x4a, 0xd, 0xaa, 0x87, 0x84, 0xb5, 0xaa, 0xa4, 0xc7, 0xa1, 0x8c, 0xb8, 0xee, 0x26, 0x89, 0xa, 0x52, 0x3, 0xaa, 0x5b, 0xc2, 0x5, 0x78, 0x3d, 0xc0, 0x6a, 0xb1, 0x75, 0xa0}, + y: fp384{0xc3, 0x70, 0xd9, 0x13, 0x8a, 0xa4, 0x57, 0xc3, 0x3e, 0x64, 0x9, 0x60, 0x70, 0x13, 0x3c, 0x4e, 0x29, 0x4, 0xe6, 0x49, 0x57, 0xa9, 0x21, 0xcf, 0xda, 0xbc, 0x51, 0x22, 0xba, 0x62, 0xa2, 0xa, 0xe4, 0x78, 0x3c, 0x56, 0xd0, 0x7c, 0x2a, 0xb7, 0xf9, 0x1c, 0xce, 0x69, 0xda, 0xfe, 0x3d, 0x77}, + }, + // 85P + { + x: fp384{0x2d, 0x76, 0x6, 0x9b, 0xdf, 0x2c, 0x5d, 0x93, 0x5a, 0x25, 0x7, 0x82, 0xb8, 0xbb, 0x48, 0x21, 0xd8, 0x53, 0x31, 0x5, 0xdd, 0xca, 0xbd, 0x7b, 0x57, 0x6a, 0x25, 0x73, 0xff, 0xaa, 0x53, 0xd9, 0x1, 0x6f, 0x4e, 0x11, 0xe1, 0x8f, 0xa0, 0xb0, 0x4d, 0x4d, 0xe6, 0x46, 0x54, 0x2, 0x35, 0xc8}, + y: fp384{0x57, 0xc1, 0xdb, 0x0, 0xd8, 0xba, 0xcf, 0xe7, 0x69, 0xe9, 0xfb, 0x71, 0x2, 0xae, 0x2a, 0x39, 0x14, 0xe3, 0xeb, 0xb, 0x2a, 0xa7, 0x22, 0x74, 0xf2, 0x52, 0xaf, 0x2c, 0xd2, 0x55, 0xd4, 0xc3, 0x95, 0x51, 0x89, 0xe2, 0x9f, 0x83, 0x31, 0xa2, 0x13, 0xf2, 0x93, 0xc9, 0x92, 0x8d, 0x21, 0x6c}, + }, + // 87P + { + x: fp384{0x2e, 0x37, 0xa0, 0x4a, 0x2e, 0xa0, 0x11, 0x4f, 0x75, 0x77, 0x96, 0x10, 0x30, 0x47, 0x42, 0x59, 0x95, 0x91, 0x80, 0x8b, 0xba, 0xc1, 0xe, 0xdf, 0xf1, 0x3a, 0x3c, 0x9a, 0x8d, 0x92, 0xa, 0xf3, 0x2e, 0x6b, 0x7b, 0x38, 0x20, 0x3, 0x84, 0xc4, 0x22, 0xbb, 0x0, 0xa5, 0x17, 0x34, 0x1b, 0x1c}, + y: fp384{0xf2, 0x66, 0x32, 0xbf, 0xc7, 0xe6, 0xe, 0x69, 0x81, 0x96, 0x90, 0xa7, 0x74, 0x6d, 0x24, 0x18, 0x8f, 0xa8, 0x84, 0xf9, 0x54, 0x24, 0xdf, 0xde, 0x9, 0x9f, 0x55, 0xe7, 0x75, 0x41, 0x94, 0x31, 0xb7, 0x4d, 0x7b, 0xe, 0x88, 0x81, 0xac, 0xbd, 0x68, 0xfe, 0x96, 0xe9, 0xae, 0x6f, 0x4, 0x9f}, + }, + // 89P + { + x: fp384{0xac, 0xd4, 0x71, 0x2d, 0x4b, 0x9, 0x62, 0xc, 0xe0, 0x63, 0xf1, 0x4b, 0xdc, 0x9f, 0xa4, 0x18, 0x1f, 0x72, 0x96, 0x0, 0xb, 0xf3, 0x3d, 0x46, 0x7b, 0x5b, 0xe5, 0xc8, 0x4a, 0x64, 0xd3, 0x67, 0xb6, 0xe2, 0xe0, 0x19, 0x9d, 0xd2, 0x3d, 0xd6, 0x61, 0x73, 0x4b, 0x16, 0xde, 0x5, 0xd1, 0xd0}, + y: fp384{0x8e, 0x18, 0x17, 0x2, 0x4e, 0x5c, 0x86, 0xe5, 0x7e, 0xcf, 0x93, 0x20, 0x1b, 0xb7, 0x61, 0x78, 0x3c, 0x25, 0xaa, 0x2d, 0x51, 0xf0, 0xcc, 0x65, 0xc0, 0xe4, 0x4d, 0xb, 0x9, 0x1, 0x77, 0x14, 0xd9, 0x62, 0xb9, 0x40, 0x35, 0x24, 0x28, 0x1d, 0xf3, 0x37, 0xdf, 0xb8, 0x39, 0xe9, 0xc7, 0x1}, + }, + // 91P + { + x: fp384{0x58, 0x8, 0xba, 0x9c, 0x9c, 0x40, 0xea, 0x5d, 0x63, 0x3f, 0xae, 0x14, 0x1b, 0x42, 0xc2, 0x35, 0x8a, 0x61, 0xc2, 0xbb, 0x34, 0xe5, 0xf6, 0xa4, 0x5f, 0x4f, 0xd7, 0x42, 0x7d, 0x97, 0x4b, 0xb0, 0xb7, 0x3e, 0xdc, 0x59, 0x27, 0x38, 0xe7, 0xe7, 0xb0, 0x23, 0xf2, 0x8b, 0x34, 0x52, 0xf, 0xeb}, + y: fp384{0x58, 0x51, 0xc7, 0xf, 0x67, 0x51, 0x1c, 0x99, 0x6d, 0x2c, 0xde, 0x3f, 0x1a, 0x81, 0x12, 0xd0, 0x3d, 0x34, 0x36, 0x92, 0x31, 0xb3, 0xd0, 0x9, 0xa0, 0xcb, 0xa2, 0x63, 0xc8, 0xe0, 0x78, 0xcb, 0x78, 0x84, 0xb8, 0x9a, 0xbd, 0xb3, 0x9, 0xb, 0xe2, 0xc6, 0x16, 0xf1, 0x3, 0xc7, 0xca, 0x47}, + }, + // 93P + { + x: fp384{0xaf, 0x80, 0x2d, 0xe3, 0x71, 0x4d, 0xd1, 0xeb, 0x5e, 0x67, 0x80, 0x53, 0x4f, 0xc2, 0x1c, 0x1b, 0xcd, 0x42, 0x61, 0xb1, 0x64, 0x47, 0x63, 0x1c, 0xe, 0xb0, 0xfd, 0x7c, 0xde, 0x45, 0x35, 0x7a, 0x54, 0x51, 0xd1, 0xcf, 0x89, 0x16, 0xf9, 0x2d, 0xd7, 0xa2, 0xa, 0x43, 0xb3, 0x55, 0x33, 0x81}, + y: fp384{0x7e, 0xca, 0xf2, 0x85, 0xc9, 0xe2, 0xdc, 0x16, 0x79, 0xbb, 0x3a, 0xe0, 0xf7, 0x3, 0xe3, 0xd9, 0x98, 0x66, 0xb0, 0x3c, 0x10, 0xa8, 0x7c, 0xfe, 0xcb, 0xd, 0x67, 0x71, 0x5b, 0x42, 0x2a, 0x57, 0xe5, 0x68, 0xa2, 0x90, 0x54, 0x39, 0x1b, 0xc7, 0x9, 0x9d, 0xbf, 0x66, 0x8f, 0xf3, 0x71, 0xc0}, + }, + // 95P + { + x: fp384{0x91, 0x3c, 0xbc, 0xd, 0x63, 0xf2, 0xc0, 0xf, 0xb1, 0xcb, 0xc0, 0x9b, 0x16, 0xca, 0x74, 0x9f, 0x77, 0x89, 0xf9, 0xe, 0x9d, 0x7b, 0x60, 0x4a, 0x79, 0x2b, 0x7d, 0xa2, 0xef, 0xb, 0xe9, 0xb, 0xe1, 0x23, 0x7f, 0xad, 0xb5, 0xb, 0x7b, 0x25, 0xe4, 0xd1, 0xea, 0x2f, 0xe5, 0x2f, 0xf4, 0xb8}, + y: fp384{0x92, 0xb5, 0x93, 0x5f, 0x31, 0xe4, 0x78, 0xde, 0x7d, 0x61, 0x79, 0xfd, 0xeb, 0xe7, 0x5f, 0x50, 0xfb, 0xf9, 0x99, 0xdd, 0x14, 0x3f, 0xcc, 0x45, 0x91, 0xb5, 0xc7, 0x3b, 0xe8, 0x64, 0xf, 0x8c, 0xf9, 0x93, 0xab, 0x55, 0xed, 0x4d, 0x36, 0x65, 0x6e, 0x28, 0xdf, 0x42, 0xa5, 0x4b, 0x6d, 0xbb}, + }, + // 97P + { + x: fp384{0xe2, 0x54, 0x5c, 0x7f, 0x5d, 0x3, 0xd2, 0x7c, 0x2c, 0x49, 0x49, 0x4a, 0x0, 0xdb, 0xf0, 0xc1, 0x98, 0x8f, 0x27, 0x65, 0x30, 0x3e, 0x27, 0x8f, 0x9d, 0x9d, 0xef, 0x6f, 0xd1, 0x1e, 0x24, 0x10, 0x16, 0x96, 0x6, 0xac, 0x1b, 0xe5, 0xb1, 0x87, 0x7f, 0x80, 0xd, 0x17, 0xcd, 0x1a, 0x8, 0x8c}, + y: fp384{0x98, 0xb5, 0x73, 0x1c, 0xea, 0x51, 0x50, 0x0, 0xf4, 0x5b, 0x86, 0xd6, 0x12, 0x55, 0xeb, 0xe4, 0xd3, 0x5d, 0xe0, 0x30, 0xf4, 0x4e, 0xd2, 0xd7, 0x9e, 0xa3, 0x89, 0x78, 0xed, 0xe4, 0x47, 0xcb, 0x64, 0x69, 0xf5, 0xc4, 0x91, 0x24, 0x3e, 0x4c, 0x6, 0x30, 0xf0, 0xc4, 0x8e, 0xb7, 0xc6, 0x83}, + }, + // 99P + { + x: fp384{0xa1, 0x84, 0x5f, 0xb0, 0xa9, 0x35, 0x38, 0x6e, 0x4e, 0x17, 0xaa, 0x41, 0xf, 0x9e, 0x4f, 0xbb, 0x69, 0xa4, 0x8b, 0x70, 0x7c, 0x17, 0x41, 0x1b, 0xbd, 0xf8, 0xc8, 0x79, 0xb4, 0xe6, 0xb2, 0xb9, 0xbd, 0xd6, 0xd9, 0x82, 0x9, 0xae, 0x9c, 0xd4, 0x3a, 0x22, 0x28, 0xbb, 0x0, 0x5f, 0x8c, 0x64}, + y: fp384{0xa5, 0xc4, 0x8c, 0x5d, 0x76, 0x85, 0xf4, 0x4, 0x8d, 0xa3, 0xa0, 0x81, 0xdd, 0x96, 0x30, 0xfe, 0xe1, 0x47, 0x82, 0x70, 0x8f, 0x13, 0x75, 0x66, 0x8, 0xea, 0xa0, 0xd1, 0xd8, 0x59, 0xf7, 0xd6, 0x98, 0xd, 0x19, 0x76, 0xb1, 0x93, 0x74, 0x1f, 0xbd, 0x63, 0x2d, 0x32, 0x8b, 0x69, 0xe1, 0x3}, + }, + // 101P + { + x: fp384{0xac, 0xde, 0x8a, 0x9b, 0xf6, 0xc6, 0x66, 0x8e, 0x70, 0x6d, 0xc6, 0x50, 0xfe, 0xf5, 0x39, 0xb5, 0x1a, 0xfb, 0xcc, 0x2e, 0x2c, 0xcd, 0xdd, 0x73, 0xfc, 0x5c, 0x8b, 0x8b, 0xad, 0x1f, 0x3b, 0xd7, 0x10, 0x87, 0xa1, 0x47, 0x35, 0x94, 0xdd, 0x9c, 0xdb, 0x14, 0xc8, 0x7b, 0xbf, 0x92, 0x23, 0x7e}, + y: fp384{0xe8, 0x74, 0x8f, 0x6c, 0xc0, 0x93, 0x41, 0x91, 0x4d, 0xc0, 0x67, 0x1d, 0x21, 0xdc, 0xed, 0xff, 0x93, 0x86, 0xd6, 0xa, 0x85, 0x41, 0xef, 0x1f, 0x54, 0x80, 0x3e, 0xf5, 0x9a, 0x1d, 0x3e, 0xa7, 0x4, 0xc1, 0xe, 0x42, 0x6f, 0x65, 0x9b, 0x90, 0xde, 0xe1, 0x2, 0x17, 0x36, 0x2d, 0x87, 0x73}, + }, + // 103P + { + x: fp384{0x5f, 0x5d, 0x35, 0x21, 0xaa, 0xab, 0x4c, 0xd2, 0xbd, 0x2, 0xd4, 0xaf, 0xe, 0xb1, 0xcb, 0xdd, 0xe1, 0x1d, 0xc2, 0x29, 0x43, 0xd1, 0xf0, 0xd3, 0xfc, 0x84, 0x3d, 0xf2, 0x94, 0x5a, 0xd9, 0x1a, 0xd, 0x53, 0xda, 0xe3, 0x2, 0x79, 0x3a, 0x11, 0x48, 0x10, 0x67, 0x64, 0x8e, 0xa8, 0xd, 0x59}, + y: fp384{0xda, 0xf0, 0x5b, 0x92, 0x82, 0x82, 0xa5, 0xf4, 0xb, 0x6e, 0x2e, 0xe6, 0xe7, 0xe4, 0x52, 0x1a, 0xfd, 0x4d, 0x6f, 0xd8, 0x4e, 0xbb, 0xeb, 0x97, 0xc3, 0xd1, 0xfa, 0x58, 0xc3, 0xd1, 0x1b, 0x60, 0x52, 0x5, 0xe4, 0x2d, 0xa6, 0xd5, 0xe2, 0xd6, 0xed, 0xdb, 0x73, 0x72, 0x37, 0x71, 0x9e, 0xc}, + }, + // 105P + { + x: fp384{0x45, 0x28, 0x80, 0xe5, 0x85, 0x19, 0xe9, 0xe6, 0xbc, 0x1, 0xe2, 0xa6, 0xae, 0xd6, 0x67, 0xc3, 0xbe, 0x23, 0x2c, 0xdd, 0xe5, 0x30, 0x7f, 0x7, 0xe3, 0x75, 0x50, 0x27, 0x9c, 0xf3, 0x2e, 0x86, 0x42, 0x65, 0x80, 0x1a, 0x44, 0x75, 0x2c, 0x2, 0x9c, 0xd0, 0xeb, 0x99, 0x3a, 0x3e, 0xab, 0xd0}, + y: fp384{0xb8, 0x28, 0x90, 0xd9, 0x60, 0xef, 0x67, 0x7d, 0x6b, 0x13, 0xad, 0xa9, 0x4f, 0x36, 0xcf, 0xa5, 0x8c, 0xff, 0x1f, 0xa9, 0xa9, 0x23, 0x2c, 0x37, 0x60, 0x25, 0x20, 0x47, 0x95, 0x7a, 0xd5, 0xd3, 0x23, 0x19, 0x2f, 0x15, 0x45, 0xf1, 0xba, 0xce, 0xa5, 0x73, 0x89, 0x6d, 0x2c, 0xe7, 0x3a, 0xcc}, + }, + // 107P + { + x: fp384{0x23, 0x3f, 0x36, 0xf4, 0xe1, 0xe8, 0x18, 0xd7, 0x90, 0x14, 0x76, 0x4f, 0x20, 0x1e, 0xc2, 0x73, 0x46, 0x5d, 0x7, 0xa0, 0x25, 0xec, 0xc6, 0x27, 0xd0, 0x40, 0x3f, 0x75, 0x56, 0xf4, 0xd4, 0xcc, 0x22, 0x16, 0xa9, 0x60, 0x49, 0x4c, 0x99, 0x50, 0x48, 0xb, 0x74, 0x3f, 0x79, 0x4d, 0x96, 0x77}, + y: fp384{0x2, 0xed, 0x47, 0xf6, 0xe2, 0x55, 0xc9, 0xf4, 0x79, 0x3d, 0x19, 0xe4, 0x9a, 0xc7, 0x4f, 0x84, 0x9f, 0x3f, 0x7f, 0xa2, 0xd1, 0x74, 0x83, 0x48, 0x63, 0x94, 0x65, 0xb8, 0x45, 0xad, 0xd6, 0x95, 0x39, 0x8e, 0x43, 0xc7, 0x78, 0x9f, 0xd9, 0xe1, 0xf2, 0xed, 0xfc, 0x2a, 0x17, 0xa7, 0x82, 0x51}, + }, + // 109P + { + x: fp384{0xfa, 0xb1, 0xde, 0xb2, 0x27, 0x2e, 0xfc, 0xe, 0x4b, 0x18, 0xc1, 0x3c, 0x83, 0x35, 0x7d, 0x9c, 0xc8, 0xbd, 0xdc, 0xa8, 0xf8, 0xc5, 0x41, 0x58, 0xbb, 0x3a, 0xd9, 0xf0, 0x8a, 0xa5, 0x11, 0xa9, 0x87, 0xf8, 0xcf, 0xb9, 0x33, 0x8c, 0xd1, 0x3c, 0x3, 0x24, 0x3f, 0xf1, 0xbb, 0x27, 0x3c, 0x6e}, + y: fp384{0xe6, 0x3a, 0x53, 0xea, 0x12, 0x17, 0x39, 0x5b, 0x5, 0x4c, 0x29, 0x54, 0xb, 0xd9, 0xcd, 0x35, 0xf4, 0xda, 0x60, 0xf5, 0xb4, 0x7f, 0xb5, 0x2d, 0x1d, 0x41, 0xa1, 0xf5, 0xca, 0x33, 0xad, 0x75, 0x68, 0x75, 0x7c, 0x7f, 0x4e, 0x9a, 0xe1, 0x87, 0x5, 0x77, 0x8c, 0x19, 0x56, 0x6c, 0xc4, 0xf4}, + }, + // 111P + { + x: fp384{0xb8, 0x74, 0xbe, 0x5, 0x48, 0xb7, 0xb3, 0x8d, 0xab, 0x5f, 0x76, 0x0, 0x50, 0x80, 0xe0, 0x8, 0xd9, 0x7f, 0x44, 0x50, 0xb, 0x72, 0x44, 0x67, 0x3, 0xbe, 0x27, 0xc7, 0x12, 0x20, 0x55, 0x27, 0x7f, 0x66, 0x2, 0x8b, 0x51, 0x7b, 0x90, 0xc3, 0x14, 0xef, 0xbd, 0xb9, 0xbc, 0xaa, 0x58, 0xa1}, + y: fp384{0x6f, 0xc5, 0x9c, 0xf4, 0x3, 0xfa, 0xdd, 0xaa, 0x96, 0xfb, 0x5, 0xe3, 0xe4, 0xbe, 0x95, 0x10, 0x19, 0x26, 0xc3, 0x93, 0xbc, 0x54, 0xff, 0xa2, 0x85, 0x26, 0xeb, 0x4a, 0xf9, 0xe1, 0xf8, 0xcc, 0xfe, 0xd9, 0x0, 0x23, 0xb8, 0x47, 0x4b, 0xc6, 0xa9, 0x9d, 0x27, 0x8, 0x68, 0x77, 0xca, 0x9}, + }, + // 113P + { + x: fp384{0x12, 0xda, 0xb, 0x9b, 0xb7, 0x86, 0x5b, 0xf8, 0x72, 0x35, 0xe3, 0xbb, 0xd1, 0x16, 0xc6, 0xd2, 0x7f, 0xd2, 0x2b, 0x85, 0xef, 0x15, 0x22, 0xe7, 0xc5, 0x7f, 0x9b, 0x13, 0x9, 0x5b, 0x81, 0xe9, 0xc2, 0xa0, 0x7f, 0x8d, 0x4, 0xdb, 0x5b, 0x4a, 0x86, 0xa9, 0x86, 0xff, 0x17, 0xcd, 0x9f, 0x86}, + y: fp384{0xbe, 0xf6, 0xda, 0x91, 0x17, 0x52, 0x78, 0x32, 0x4, 0x4d, 0x66, 0xd6, 0xf1, 0x7, 0xc0, 0xb9, 0x29, 0x20, 0xb1, 0xe7, 0x2a, 0x72, 0x98, 0x69, 0x67, 0xbb, 0x31, 0xfc, 0xd9, 0xde, 0x7f, 0xbf, 0x57, 0x77, 0xb8, 0x31, 0x7c, 0xd1, 0xe, 0x3d, 0x4b, 0x94, 0xf7, 0x55, 0xf2, 0x3d, 0xb, 0xa1}, + }, + // 115P + { + x: fp384{0x56, 0x8f, 0x49, 0x34, 0x3b, 0x67, 0x4e, 0xaa, 0x6d, 0x45, 0x43, 0xa0, 0xb3, 0xf7, 0x20, 0x91, 0x26, 0x88, 0xa4, 0x26, 0xf3, 0x68, 0xe8, 0x33, 0xfd, 0x6b, 0x40, 0x32, 0xf4, 0xcc, 0x7a, 0xa9, 0x46, 0x7, 0xc5, 0x23, 0x41, 0x2a, 0x44, 0x84, 0x2d, 0x98, 0x50, 0x73, 0x65, 0x69, 0x4c, 0x27}, + y: fp384{0xaa, 0x9e, 0xdc, 0x95, 0xfb, 0xb7, 0x6, 0x31, 0x41, 0xe2, 0x7a, 0xec, 0xb1, 0x1d, 0xbf, 0x3e, 0x86, 0x5b, 0x8a, 0x84, 0xb8, 0x39, 0xdc, 0x0, 0xc6, 0x2e, 0x31, 0xf6, 0x3a, 0x34, 0x9a, 0xdf, 0x3, 0x1f, 0x12, 0xcf, 0x32, 0x9e, 0x2, 0x75, 0xdb, 0x11, 0x5a, 0xcf, 0xf6, 0xbd, 0xc5, 0xc0}, + }, + // 117P + { + x: fp384{0x9a, 0x55, 0x23, 0x7, 0xa8, 0xe7, 0xcc, 0xfd, 0x70, 0xd0, 0x29, 0xa2, 0xe, 0xb3, 0x32, 0x95, 0x61, 0x79, 0x85, 0xfe, 0x94, 0x52, 0x3e, 0x61, 0x8d, 0x73, 0xea, 0x9a, 0x9a, 0x74, 0x9e, 0xad, 0x53, 0xba, 0xac, 0x23, 0xae, 0x6b, 0x7a, 0x51, 0x8b, 0x56, 0x23, 0x78, 0xcd, 0x74, 0x67, 0xf2}, + y: fp384{0x4d, 0x11, 0x53, 0xae, 0xa1, 0x67, 0x7, 0x94, 0x7b, 0x5, 0x8c, 0xeb, 0x4b, 0x3a, 0xcb, 0xc6, 0x0, 0xf6, 0xae, 0x6, 0x5f, 0xc0, 0x99, 0x6d, 0x81, 0x84, 0x79, 0xb2, 0x8d, 0x9b, 0x72, 0xa6, 0x5b, 0x58, 0xf1, 0xbf, 0x82, 0x65, 0x54, 0x7a, 0x73, 0x97, 0xb0, 0x82, 0xb0, 0x9a, 0x50, 0x70}, + }, + // 119P + { + x: fp384{0xd1, 0xd8, 0x29, 0x73, 0xac, 0x95, 0x25, 0x73, 0xc4, 0xbc, 0x73, 0xab, 0x8d, 0x7, 0xf9, 0x59, 0xde, 0x9d, 0xcf, 0x21, 0xb4, 0xe7, 0x55, 0xcd, 0x27, 0xab, 0x5f, 0x87, 0xa1, 0xb, 0x63, 0xa4, 0xcd, 0xf4, 0x9c, 0xec, 0xb8, 0x7f, 0x16, 0x13, 0x77, 0x5, 0x8b, 0x99, 0x57, 0x5c, 0xfc, 0xd2}, + y: fp384{0x6f, 0x1b, 0xf5, 0x5f, 0xc1, 0x60, 0x13, 0x76, 0xeb, 0x9c, 0xcf, 0x6, 0xf2, 0x96, 0xdb, 0xb2, 0x46, 0x8f, 0xdc, 0x99, 0x48, 0xa6, 0x77, 0xd6, 0xe5, 0xb7, 0x9, 0x91, 0xbc, 0xca, 0x9e, 0xeb, 0xa3, 0xb7, 0x21, 0x92, 0xac, 0xb1, 0x1b, 0x9a, 0xcd, 0xa5, 0x40, 0x8a, 0x28, 0x36, 0x65, 0xb1}, + }, + // 121P + { + x: fp384{0x8d, 0xfa, 0x9d, 0x18, 0x40, 0xba, 0x98, 0x51, 0x1e, 0xab, 0x96, 0xa8, 0xe3, 0x16, 0x9a, 0x8c, 0xe4, 0x44, 0x67, 0xba, 0xd6, 0xd5, 0x33, 0x6c, 0x8a, 0x77, 0x72, 0x77, 0xd4, 0xfb, 0x9a, 0xc2, 0xe0, 0xf7, 0x29, 0x93, 0x95, 0x3c, 0xdf, 0x65, 0x81, 0xdb, 0x91, 0x38, 0x1e, 0x3f, 0xcd, 0x79}, + y: fp384{0x9b, 0x1, 0x84, 0xd7, 0xb, 0x8f, 0xaa, 0xca, 0x62, 0x8e, 0x2, 0xe9, 0x6b, 0x4f, 0x40, 0xf5, 0x85, 0x89, 0x4d, 0xae, 0x54, 0x7a, 0x50, 0x95, 0x1b, 0xf2, 0x16, 0xb7, 0xa8, 0x39, 0x1d, 0x9c, 0x7e, 0x5b, 0x26, 0xf8, 0xf9, 0xd, 0x3d, 0x47, 0x16, 0x9, 0x4d, 0xc6, 0xa1, 0xed, 0xae, 0x11}, + }, + // 123P + { + x: fp384{0x80, 0xb, 0x4c, 0xc, 0x48, 0x98, 0x11, 0x15, 0xfe, 0x18, 0x10, 0x2a, 0x4a, 0x7d, 0xb0, 0x46, 0x7b, 0x90, 0x80, 0xea, 0xeb, 0xc3, 0xa9, 0x14, 0x5, 0x44, 0x95, 0x42, 0xee, 0x5, 0x3d, 0x4b, 0xd2, 0x18, 0x4, 0x55, 0x78, 0xfd, 0xd6, 0xd4, 0xa6, 0x82, 0xdc, 0x21, 0x63, 0x4f, 0x4a, 0x8e}, + y: fp384{0xd9, 0xb9, 0x78, 0xa1, 0xf7, 0xd7, 0xe4, 0x1b, 0x9f, 0x8c, 0x78, 0x70, 0x4, 0xba, 0xc6, 0x83, 0xe8, 0xf7, 0x30, 0x9f, 0x3e, 0xaf, 0x82, 0x5a, 0x7b, 0x44, 0x8f, 0x49, 0xf, 0xab, 0x86, 0x8a, 0x33, 0xcf, 0x5c, 0xc2, 0xec, 0xbc, 0xba, 0x3, 0xdd, 0x8a, 0x79, 0x1a, 0xed, 0x62, 0xc4, 0x74}, + }, + // 125P + { + x: fp384{0xbb, 0x44, 0xfb, 0x3c, 0xbc, 0xb8, 0x12, 0x96, 0xda, 0x6a, 0x9d, 0x71, 0x77, 0x56, 0xf2, 0x70, 0xe2, 0x14, 0xf6, 0x46, 0xaa, 0x65, 0xea, 0x50, 0x6d, 0x35, 0x50, 0x34, 0xcf, 0xa8, 0x36, 0x94, 0xac, 0x3, 0x67, 0x3, 0xad, 0xeb, 0xd6, 0x7a, 0xae, 0x48, 0xcf, 0x45, 0xb2, 0xda, 0xb2, 0x3}, + y: fp384{0x1e, 0x6, 0x17, 0x41, 0x2a, 0x3d, 0x95, 0x21, 0xa4, 0x55, 0x4f, 0xed, 0x30, 0xb7, 0x3f, 0xdf, 0x8e, 0xab, 0x19, 0xe1, 0x41, 0x36, 0xf5, 0xec, 0xaa, 0x87, 0x91, 0x21, 0xbc, 0x41, 0x1f, 0x55, 0x2, 0x58, 0x9, 0xb0, 0x4b, 0xa7, 0xd5, 0x7c, 0xcb, 0x5c, 0xf3, 0x72, 0xf5, 0xbf, 0x33, 0x89}, + }, + // 127P + { + x: fp384{0xec, 0x1, 0xa1, 0xec, 0x46, 0x32, 0x75, 0xf7, 0xaf, 0x4, 0x96, 0x56, 0x4c, 0xfa, 0xac, 0x2a, 0x79, 0x62, 0x2c, 0x52, 0x34, 0x8f, 0xf2, 0xee, 0xf, 0x1e, 0x23, 0x74, 0x38, 0xe6, 0xfd, 0x96, 0x9d, 0xf0, 0xe0, 0xd6, 0x1b, 0xb1, 0x2b, 0xa9, 0xb4, 0x5d, 0x39, 0xf, 0x74, 0x4e, 0xe3, 0xbb}, + y: fp384{0xf9, 0x3c, 0x94, 0xbf, 0xdd, 0x59, 0x6e, 0xaa, 0xaa, 0xd5, 0x8a, 0x1, 0xbe, 0xbd, 0x98, 0x56, 0x19, 0xc5, 0x67, 0xa4, 0x44, 0x2a, 0xd2, 0x88, 0xe, 0xb, 0x18, 0xad, 0x39, 0xe3, 0x29, 0x9e, 0x94, 0x2f, 0x7b, 0x36, 0x2e, 0x83, 0xd6, 0xf3, 0x69, 0x80, 0x94, 0xe3, 0x61, 0x2a, 0xe9, 0xc7}, + }, +} diff --git a/src/vendor/github.com/cloudflare/circl/hpke/aead.go b/src/vendor/github.com/cloudflare/circl/hpke/aead.go new file mode 100644 index 00000000000..baf147bb4fa --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/hpke/aead.go @@ -0,0 +1,103 @@ +package hpke + +import ( + "crypto/cipher" + "fmt" +) + +type encdecContext struct { + // Serialized parameters + suite Suite + sharedSecret []byte + secret []byte + keyScheduleContext []byte + exporterSecret []byte + key []byte + baseNonce []byte + sequenceNumber []byte + + // Operational parameters + cipher.AEAD + nonce []byte +} + +type ( + sealContext struct{ *encdecContext } + openContext struct{ *encdecContext } +) + +// Export takes a context string exporterContext and a desired length (in +// bytes), and produces a secret derived from the internal exporter secret +// using the corresponding KDF Expand function. It panics if length is +// greater than 255*N bytes, where N is the size (in bytes) of the KDF's +// output. +func (c *encdecContext) Export(exporterContext []byte, length uint) []byte { + maxLength := uint(255 * c.suite.kdfID.ExtractSize()) + if length > maxLength { + panic(fmt.Errorf("output length must be lesser than %v bytes", maxLength)) + } + return c.suite.labeledExpand(c.exporterSecret, []byte("sec"), + exporterContext, uint16(length)) +} + +func (c *encdecContext) Suite() Suite { + return c.suite +} + +func (c *encdecContext) calcNonce() []byte { + for i := range c.baseNonce { + c.nonce[i] = c.baseNonce[i] ^ c.sequenceNumber[i] + } + return c.nonce +} + +func (c *encdecContext) increment() error { + // tests whether the sequence number is all-ones, which prevents an + // overflow after the increment. + allOnes := byte(0xFF) + for i := range c.sequenceNumber { + allOnes &= c.sequenceNumber[i] + } + if allOnes == byte(0xFF) { + return ErrAEADSeqOverflows + } + + // performs an increment by 1 and verifies whether the sequence overflows. + carry := uint(1) + for i := len(c.sequenceNumber) - 1; i >= 0; i-- { + sum := uint(c.sequenceNumber[i]) + carry + carry = sum >> 8 + c.sequenceNumber[i] = byte(sum & 0xFF) + } + if carry != 0 { + return ErrAEADSeqOverflows + } + return nil +} + +func (c *sealContext) Seal(pt, aad []byte) ([]byte, error) { + ct := c.AEAD.Seal(nil, c.calcNonce(), pt, aad) + err := c.increment() + if err != nil { + for i := range ct { + ct[i] = 0 + } + return nil, err + } + return ct, nil +} + +func (c *openContext) Open(ct, aad []byte) ([]byte, error) { + pt, err := c.AEAD.Open(nil, c.calcNonce(), ct, aad) + if err != nil { + return nil, err + } + err = c.increment() + if err != nil { + for i := range pt { + pt[i] = 0 + } + return nil, err + } + return pt, nil +} diff --git a/src/vendor/github.com/cloudflare/circl/hpke/algs.go b/src/vendor/github.com/cloudflare/circl/hpke/algs.go new file mode 100644 index 00000000000..a9fbc661232 --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/hpke/algs.go @@ -0,0 +1,278 @@ +package hpke + +import ( + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/elliptic" + _ "crypto/sha256" // Linking sha256. + _ "crypto/sha512" // Linking sha512. + "fmt" + "hash" + "io" + + "github.com/cloudflare/circl/dh/x25519" + "github.com/cloudflare/circl/dh/x448" + "github.com/cloudflare/circl/ecc/p384" + "github.com/cloudflare/circl/kem" + "github.com/cloudflare/circl/kem/kyber/kyber768" + "golang.org/x/crypto/chacha20poly1305" + "golang.org/x/crypto/hkdf" +) + +type KEM uint16 + +//nolint:golint,stylecheck +const ( + // KEM_P256_HKDF_SHA256 is a KEM using P256 curve and HKDF with SHA-256. + KEM_P256_HKDF_SHA256 KEM = 0x10 + // KEM_P384_HKDF_SHA384 is a KEM using P384 curve and HKDF with SHA-384. + KEM_P384_HKDF_SHA384 KEM = 0x11 + // KEM_P521_HKDF_SHA512 is a KEM using P521 curve and HKDF with SHA-512. + KEM_P521_HKDF_SHA512 KEM = 0x12 + // KEM_X25519_HKDF_SHA256 is a KEM using X25519 Diffie-Hellman function + // and HKDF with SHA-256. + KEM_X25519_HKDF_SHA256 KEM = 0x20 + // KEM_X448_HKDF_SHA512 is a KEM using X448 Diffie-Hellman function and + // HKDF with SHA-512. + KEM_X448_HKDF_SHA512 KEM = 0x21 + // KEM_X25519_KYBER768_DRAFT00 is a hybrid KEM built on DHKEM(X25519, HKDF-SHA256) + // and Kyber768Draft00 + KEM_X25519_KYBER768_DRAFT00 KEM = 0x30 +) + +// IsValid returns true if the KEM identifier is supported by the HPKE package. +func (k KEM) IsValid() bool { + switch k { + case KEM_P256_HKDF_SHA256, + KEM_P384_HKDF_SHA384, + KEM_P521_HKDF_SHA512, + KEM_X25519_HKDF_SHA256, + KEM_X448_HKDF_SHA512, + KEM_X25519_KYBER768_DRAFT00: + return true + default: + return false + } +} + +// Scheme returns an instance of a KEM that supports authentication. Panics if +// the KEM identifier is invalid. +func (k KEM) Scheme() kem.AuthScheme { + switch k { + case KEM_P256_HKDF_SHA256: + return dhkemp256hkdfsha256 + case KEM_P384_HKDF_SHA384: + return dhkemp384hkdfsha384 + case KEM_P521_HKDF_SHA512: + return dhkemp521hkdfsha512 + case KEM_X25519_HKDF_SHA256: + return dhkemx25519hkdfsha256 + case KEM_X448_HKDF_SHA512: + return dhkemx448hkdfsha512 + case KEM_X25519_KYBER768_DRAFT00: + return hybridkemX25519Kyber768 + default: + panic(ErrInvalidKEM) + } +} + +type KDF uint16 + +//nolint:golint,stylecheck +const ( + // KDF_HKDF_SHA256 is a KDF using HKDF with SHA-256. + KDF_HKDF_SHA256 KDF = 0x01 + // KDF_HKDF_SHA384 is a KDF using HKDF with SHA-384. + KDF_HKDF_SHA384 KDF = 0x02 + // KDF_HKDF_SHA512 is a KDF using HKDF with SHA-512. + KDF_HKDF_SHA512 KDF = 0x03 +) + +func (k KDF) IsValid() bool { + switch k { + case KDF_HKDF_SHA256, + KDF_HKDF_SHA384, + KDF_HKDF_SHA512: + return true + default: + return false + } +} + +// ExtractSize returns the size (in bytes) of the pseudorandom key produced +// by KDF.Extract. +func (k KDF) ExtractSize() int { + switch k { + case KDF_HKDF_SHA256: + return crypto.SHA256.Size() + case KDF_HKDF_SHA384: + return crypto.SHA384.Size() + case KDF_HKDF_SHA512: + return crypto.SHA512.Size() + default: + panic(ErrInvalidKDF) + } +} + +// Extract derives a pseudorandom key from a high-entropy, secret input and a +// salt. The size of the output is determined by KDF.ExtractSize. +func (k KDF) Extract(secret, salt []byte) (pseudorandomKey []byte) { + return hkdf.Extract(k.hash(), secret, salt) +} + +// Expand derives a variable length pseudorandom string from a pseudorandom key +// and an information string. Panics if the pseudorandom key is less +// than N bytes, or if the output length is greater than 255*N bytes, +// where N is the size returned by KDF.Extract function. +func (k KDF) Expand(pseudorandomKey, info []byte, outputLen uint) []byte { + extractSize := k.ExtractSize() + if len(pseudorandomKey) < extractSize { + panic(fmt.Errorf("pseudorandom key must be %v bytes", extractSize)) + } + maxLength := uint(255 * extractSize) + if outputLen > maxLength { + panic(fmt.Errorf("output length must be less than %v bytes", maxLength)) + } + output := make([]byte, outputLen) + rd := hkdf.Expand(k.hash(), pseudorandomKey[:extractSize], info) + _, err := io.ReadFull(rd, output) + if err != nil { + panic(err) + } + return output +} + +func (k KDF) hash() func() hash.Hash { + switch k { + case KDF_HKDF_SHA256: + return crypto.SHA256.New + case KDF_HKDF_SHA384: + return crypto.SHA384.New + case KDF_HKDF_SHA512: + return crypto.SHA512.New + default: + panic(ErrInvalidKDF) + } +} + +type AEAD uint16 + +//nolint:golint,stylecheck +const ( + // AEAD_AES128GCM is AES-128 block cipher in Galois Counter Mode (GCM). + AEAD_AES128GCM AEAD = 0x01 + // AEAD_AES256GCM is AES-256 block cipher in Galois Counter Mode (GCM). + AEAD_AES256GCM AEAD = 0x02 + // AEAD_ChaCha20Poly1305 is ChaCha20 stream cipher and Poly1305 MAC. + AEAD_ChaCha20Poly1305 AEAD = 0x03 +) + +// New instantiates an AEAD cipher from the identifier, returns an error if the +// identifier is not known. +func (a AEAD) New(key []byte) (cipher.AEAD, error) { + switch a { + case AEAD_AES128GCM, AEAD_AES256GCM: + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return cipher.NewGCM(block) + case AEAD_ChaCha20Poly1305: + return chacha20poly1305.New(key) + default: + panic(ErrInvalidAEAD) + } +} + +func (a AEAD) IsValid() bool { + switch a { + case AEAD_AES128GCM, + AEAD_AES256GCM, + AEAD_ChaCha20Poly1305: + return true + default: + return false + } +} + +// KeySize returns the size in bytes of the keys used by the AEAD cipher. +func (a AEAD) KeySize() uint { + switch a { + case AEAD_AES128GCM: + return 16 + case AEAD_AES256GCM: + return 32 + case AEAD_ChaCha20Poly1305: + return chacha20poly1305.KeySize + default: + panic(ErrInvalidAEAD) + } +} + +// NonceSize returns the size in bytes of the nonce used by the AEAD cipher. +func (a AEAD) NonceSize() uint { + switch a { + case AEAD_AES128GCM, + AEAD_AES256GCM, + AEAD_ChaCha20Poly1305: + return 12 + default: + panic(ErrInvalidAEAD) + } +} + +// CipherLen returns the length of a ciphertext corresponding to a message of +// length mLen. +func (a AEAD) CipherLen(mLen uint) uint { + switch a { + case AEAD_AES128GCM, AEAD_AES256GCM, AEAD_ChaCha20Poly1305: + return mLen + 16 + default: + panic(ErrInvalidAEAD) + } +} + +var ( + dhkemp256hkdfsha256, dhkemp384hkdfsha384, dhkemp521hkdfsha512 shortKEM + dhkemx25519hkdfsha256, dhkemx448hkdfsha512 xKEM + hybridkemX25519Kyber768 hybridKEM +) + +func init() { + dhkemp256hkdfsha256.Curve = elliptic.P256() + dhkemp256hkdfsha256.dhKemBase.id = KEM_P256_HKDF_SHA256 + dhkemp256hkdfsha256.dhKemBase.name = "HPKE_KEM_P256_HKDF_SHA256" + dhkemp256hkdfsha256.dhKemBase.Hash = crypto.SHA256 + dhkemp256hkdfsha256.dhKemBase.dhKEM = dhkemp256hkdfsha256 + + dhkemp384hkdfsha384.Curve = p384.P384() + dhkemp384hkdfsha384.dhKemBase.id = KEM_P384_HKDF_SHA384 + dhkemp384hkdfsha384.dhKemBase.name = "HPKE_KEM_P384_HKDF_SHA384" + dhkemp384hkdfsha384.dhKemBase.Hash = crypto.SHA384 + dhkemp384hkdfsha384.dhKemBase.dhKEM = dhkemp384hkdfsha384 + + dhkemp521hkdfsha512.Curve = elliptic.P521() + dhkemp521hkdfsha512.dhKemBase.id = KEM_P521_HKDF_SHA512 + dhkemp521hkdfsha512.dhKemBase.name = "HPKE_KEM_P521_HKDF_SHA512" + dhkemp521hkdfsha512.dhKemBase.Hash = crypto.SHA512 + dhkemp521hkdfsha512.dhKemBase.dhKEM = dhkemp521hkdfsha512 + + dhkemx25519hkdfsha256.size = x25519.Size + dhkemx25519hkdfsha256.dhKemBase.id = KEM_X25519_HKDF_SHA256 + dhkemx25519hkdfsha256.dhKemBase.name = "HPKE_KEM_X25519_HKDF_SHA256" + dhkemx25519hkdfsha256.dhKemBase.Hash = crypto.SHA256 + dhkemx25519hkdfsha256.dhKemBase.dhKEM = dhkemx25519hkdfsha256 + + dhkemx448hkdfsha512.size = x448.Size + dhkemx448hkdfsha512.dhKemBase.id = KEM_X448_HKDF_SHA512 + dhkemx448hkdfsha512.dhKemBase.name = "HPKE_KEM_X448_HKDF_SHA512" + dhkemx448hkdfsha512.dhKemBase.Hash = crypto.SHA512 + dhkemx448hkdfsha512.dhKemBase.dhKEM = dhkemx448hkdfsha512 + + hybridkemX25519Kyber768.kemBase.id = KEM_X25519_KYBER768_DRAFT00 + hybridkemX25519Kyber768.kemBase.name = "HPKE_KEM_X25519_KYBER768_HKDF_SHA256" + hybridkemX25519Kyber768.kemBase.Hash = crypto.SHA256 + hybridkemX25519Kyber768.kemA = dhkemx25519hkdfsha256 + hybridkemX25519Kyber768.kemB = kyber768.Scheme() +} diff --git a/src/vendor/github.com/cloudflare/circl/hpke/hpke.go b/src/vendor/github.com/cloudflare/circl/hpke/hpke.go new file mode 100644 index 00000000000..4075b285e17 --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/hpke/hpke.go @@ -0,0 +1,271 @@ +// Package hpke implements the Hybrid Public Key Encryption (HPKE) standard +// specified by draft-irtf-cfrg-hpke-07. +// +// HPKE works for any combination of a public-key encapsulation mechanism +// (KEM), a key derivation function (KDF), and an authenticated encryption +// scheme with additional data (AEAD). +// +// Specification in +// https://datatracker.ietf.org/doc/draft-irtf-cfrg-hpke +// +// BUG(cjpatton): This package does not implement the "Export-Only" mode of the +// HPKE context. In particular, it does not recognize the AEAD codepoint +// reserved for this purpose (0xFFFF). +package hpke + +import ( + "crypto/rand" + "encoding" + "errors" + "io" + + "github.com/cloudflare/circl/kem" +) + +const versionLabel = "HPKE-v1" + +// Context defines the capabilities of an HPKE context. +type Context interface { + encoding.BinaryMarshaler + // Export takes a context string exporterContext and a desired length (in + // bytes), and produces a secret derived from the internal exporter secret + // using the corresponding KDF Expand function. It panics if length is + // greater than 255*N bytes, where N is the size (in bytes) of the KDF's + // output. + Export(exporterContext []byte, length uint) []byte + // Suite returns the cipher suite corresponding to this context. + Suite() Suite +} + +// Sealer encrypts a plaintext using an AEAD encryption. +type Sealer interface { + Context + // Seal takes a plaintext and associated data to produce a ciphertext. + // The nonce is handled by the Sealer and incremented after each call. + Seal(pt, aad []byte) (ct []byte, err error) +} + +// Opener decrypts a ciphertext using an AEAD encryption. +type Opener interface { + Context + // Open takes a ciphertext and associated data to recover, if successful, + // the plaintext. The nonce is handled by the Opener and incremented after + // each call. + Open(ct, aad []byte) (pt []byte, err error) +} + +// modeID represents an HPKE variant. +type modeID = uint8 + +const ( + // modeBase to enable encryption to the holder of a given KEM private key. + modeBase modeID = 0x00 + // modePSK extends the base mode by allowing the Receiver to authenticate + // that the sender possessed a given pre-shared key (PSK). + modePSK modeID = 0x01 + // modeAuth extends the base mode by allowing the Receiver to authenticate + // that the sender possessed a given KEM private key. + modeAuth modeID = 0x02 + // modeAuthPSK provides a combination of the PSK and Auth modes. + modeAuthPSK modeID = 0x03 +) + +// Suite is an HPKE cipher suite consisting of a KEM, KDF, and AEAD algorithm. +type Suite struct { + kemID KEM + kdfID KDF + aeadID AEAD +} + +// NewSuite builds a Suite from a specified set of algorithms. Panics +// if an algorithm identifier is not valid. +func NewSuite(kemID KEM, kdfID KDF, aeadID AEAD) Suite { + s := Suite{kemID, kdfID, aeadID} + if !s.isValid() { + panic(ErrInvalidHPKESuite) + } + return s +} + +type state struct { + Suite + modeID modeID + skS kem.PrivateKey + pkS kem.PublicKey + psk []byte + pskID []byte + info []byte +} + +// Sender performs hybrid public-key encryption. +type Sender struct { + state + pkR kem.PublicKey +} + +// NewSender creates a Sender with knowledge of the receiver's public-key. +func (suite Suite) NewSender(pkR kem.PublicKey, info []byte) (*Sender, error) { + return &Sender{ + state: state{Suite: suite, info: info}, + pkR: pkR, + }, nil +} + +// Setup generates a new HPKE context used for Base Mode encryption. +// Returns the Sealer and corresponding encapsulated key. +func (s *Sender) Setup(rnd io.Reader) (enc []byte, seal Sealer, err error) { + s.modeID = modeBase + return s.allSetup(rnd) +} + +// SetupAuth generates a new HPKE context used for Auth Mode encryption. +// Returns the Sealer and corresponding encapsulated key. +func (s *Sender) SetupAuth(rnd io.Reader, skS kem.PrivateKey) ( + enc []byte, seal Sealer, err error, +) { + s.modeID = modeAuth + s.state.skS = skS + return s.allSetup(rnd) +} + +// SetupPSK generates a new HPKE context used for PSK Mode encryption. +// Returns the Sealer and corresponding encapsulated key. +func (s *Sender) SetupPSK(rnd io.Reader, psk, pskID []byte) ( + enc []byte, seal Sealer, err error, +) { + s.modeID = modePSK + s.state.psk = psk + s.state.pskID = pskID + return s.allSetup(rnd) +} + +// SetupAuthPSK generates a new HPKE context used for Auth-PSK Mode encryption. +// Returns the Sealer and corresponding encapsulated key. +func (s *Sender) SetupAuthPSK(rnd io.Reader, skS kem.PrivateKey, psk, pskID []byte) ( + enc []byte, seal Sealer, err error, +) { + s.modeID = modeAuthPSK + s.state.skS = skS + s.state.psk = psk + s.state.pskID = pskID + return s.allSetup(rnd) +} + +// Receiver performs hybrid public-key decryption. +type Receiver struct { + state + skR kem.PrivateKey + enc []byte +} + +// NewReceiver creates a Receiver with knowledge of a private key. +func (suite Suite) NewReceiver(skR kem.PrivateKey, info []byte) ( + *Receiver, error, +) { + return &Receiver{state: state{Suite: suite, info: info}, skR: skR}, nil +} + +// Setup generates a new HPKE context used for Base Mode encryption. +// Setup takes an encapsulated key and returns an Opener. +func (r *Receiver) Setup(enc []byte) (Opener, error) { + r.modeID = modeBase + r.enc = enc + return r.allSetup() +} + +// SetupAuth generates a new HPKE context used for Auth Mode encryption. +// SetupAuth takes an encapsulated key and a public key, and returns an Opener. +func (r *Receiver) SetupAuth(enc []byte, pkS kem.PublicKey) (Opener, error) { + r.modeID = modeAuth + r.enc = enc + r.state.pkS = pkS + return r.allSetup() +} + +// SetupPSK generates a new HPKE context used for PSK Mode encryption. +// SetupPSK takes an encapsulated key, and a pre-shared key; and returns an +// Opener. +func (r *Receiver) SetupPSK(enc, psk, pskID []byte) (Opener, error) { + r.modeID = modePSK + r.enc = enc + r.state.psk = psk + r.state.pskID = pskID + return r.allSetup() +} + +// SetupAuthPSK generates a new HPKE context used for Auth-PSK Mode encryption. +// SetupAuthPSK takes an encapsulated key, a public key, and a pre-shared key; +// and returns an Opener. +func (r *Receiver) SetupAuthPSK( + enc, psk, pskID []byte, pkS kem.PublicKey, +) (Opener, error) { + r.modeID = modeAuthPSK + r.enc = enc + r.state.psk = psk + r.state.pskID = pskID + r.state.pkS = pkS + return r.allSetup() +} + +func (s *Sender) allSetup(rnd io.Reader) ([]byte, Sealer, error) { + scheme := s.kemID.Scheme() + + if rnd == nil { + rnd = rand.Reader + } + seed := make([]byte, scheme.EncapsulationSeedSize()) + _, err := io.ReadFull(rnd, seed) + if err != nil { + return nil, nil, err + } + + var enc, ss []byte + switch s.modeID { + case modeBase, modePSK: + enc, ss, err = scheme.EncapsulateDeterministically(s.pkR, seed) + case modeAuth, modeAuthPSK: + enc, ss, err = scheme.AuthEncapsulateDeterministically(s.pkR, s.skS, seed) + } + if err != nil { + return nil, nil, err + } + + ctx, err := s.keySchedule(ss, s.info, s.psk, s.pskID) + if err != nil { + return nil, nil, err + } + + return enc, &sealContext{ctx}, nil +} + +func (r *Receiver) allSetup() (Opener, error) { + var err error + var ss []byte + scheme := r.kemID.Scheme() + switch r.modeID { + case modeBase, modePSK: + ss, err = scheme.Decapsulate(r.skR, r.enc) + case modeAuth, modeAuthPSK: + ss, err = scheme.AuthDecapsulate(r.skR, r.enc, r.pkS) + } + if err != nil { + return nil, err + } + + ctx, err := r.keySchedule(ss, r.info, r.psk, r.pskID) + if err != nil { + return nil, err + } + return &openContext{ctx}, nil +} + +var ( + ErrInvalidHPKESuite = errors.New("hpke: invalid HPKE suite") + ErrInvalidKDF = errors.New("hpke: invalid KDF identifier") + ErrInvalidKEM = errors.New("hpke: invalid KEM identifier") + ErrInvalidAEAD = errors.New("hpke: invalid AEAD identifier") + ErrInvalidKEMPublicKey = errors.New("hpke: invalid KEM public key") + ErrInvalidKEMPrivateKey = errors.New("hpke: invalid KEM private key") + ErrInvalidKEMSharedSecret = errors.New("hpke: invalid KEM shared secret") + ErrAEADSeqOverflows = errors.New("hpke: AEAD sequence number overflows") +) diff --git a/src/vendor/github.com/cloudflare/circl/hpke/hybridkem.go b/src/vendor/github.com/cloudflare/circl/hpke/hybridkem.go new file mode 100644 index 00000000000..74e1ea6f1f3 --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/hpke/hybridkem.go @@ -0,0 +1,232 @@ +package hpke + +// This file implements a hybrid KEM for HPKE using a simple concatenation +// combiner. +// +// WARNING It is not safe to combine arbitrary KEMs using this combiner. +// See the draft specification for more details: +// https://bwesterb.github.io/draft-westerbaan-cfrg-hpke-xyber768d00/draft-westerbaan-cfrg-hpke-xyber768d00.html#name-security-considerations + +import ( + "crypto/rand" + + "github.com/cloudflare/circl/kem" +) + +type hybridKEM struct { + kemBase + kemA kem.Scheme + kemB kem.Scheme +} + +func (h hybridKEM) PrivateKeySize() int { return h.kemA.PrivateKeySize() + h.kemB.PrivateKeySize() } +func (h hybridKEM) SeedSize() int { return 32 } +func (h hybridKEM) CiphertextSize() int { return h.kemA.CiphertextSize() + h.kemB.CiphertextSize() } +func (h hybridKEM) PublicKeySize() int { return h.kemA.PublicKeySize() + h.kemB.PublicKeySize() } +func (h hybridKEM) EncapsulationSeedSize() int { + return h.kemA.EncapsulationSeedSize() + h.kemB.EncapsulationSeedSize() +} +func (h hybridKEM) SharedKeySize() int { return h.kemA.SharedKeySize() + h.kemB.SharedKeySize() } +func (h hybridKEM) Name() string { return h.name } + +func (h hybridKEM) AuthDecapsulate(skR kem.PrivateKey, + ct []byte, + pkS kem.PublicKey, +) ([]byte, error) { + panic("AuthDecapsulate is not supported for this KEM") +} + +func (h hybridKEM) AuthEncapsulate(pkr kem.PublicKey, sks kem.PrivateKey) ( + ct []byte, ss []byte, err error, +) { + panic("AuthEncapsulate is not supported for this KEM") +} + +func (h hybridKEM) AuthEncapsulateDeterministically(pkr kem.PublicKey, sks kem.PrivateKey, seed []byte) (ct, ss []byte, err error) { + panic("AuthEncapsulateDeterministically is not supported for this KEM") +} + +func (h hybridKEM) Encapsulate(pkr kem.PublicKey) ( + ct []byte, ss []byte, err error, +) { + panic("Encapsulate is not implemented") +} + +func (h hybridKEM) Decapsulate(skr kem.PrivateKey, ct []byte) ([]byte, error) { + hybridSk := skr.(*hybridKEMPrivKey) + ssA, err := h.kemA.Decapsulate(hybridSk.privA, ct[0:h.kemA.CiphertextSize()]) + if err != nil { + return nil, err + } + ssB, err := h.kemB.Decapsulate(hybridSk.privB, ct[h.kemA.CiphertextSize():]) + if err != nil { + return nil, err + } + + ss := append(ssA, ssB...) + + return ss, nil +} + +func (h hybridKEM) EncapsulateDeterministically( + pkr kem.PublicKey, seed []byte, +) (ct, ss []byte, err error) { + hybridPk := pkr.(*hybridKEMPubKey) + encA, ssA, err := h.kemA.EncapsulateDeterministically(hybridPk.pubA, seed[0:h.kemA.EncapsulationSeedSize()]) + if err != nil { + return nil, nil, err + } + encB, ssB, err := h.kemB.EncapsulateDeterministically(hybridPk.pubB, seed[h.kemA.EncapsulationSeedSize():]) + if err != nil { + return nil, nil, err + } + + ct = append(encA, encB...) + ss = append(ssA, ssB...) + + return ct, ss, nil +} + +type hybridKEMPrivKey struct { + scheme kem.Scheme + privA kem.PrivateKey + privB kem.PrivateKey +} + +func (k *hybridKEMPrivKey) Scheme() kem.Scheme { + return k.scheme +} + +func (k *hybridKEMPrivKey) MarshalBinary() ([]byte, error) { + skA, err := k.privA.MarshalBinary() + if err != nil { + return nil, err + } + skB, err := k.privB.MarshalBinary() + if err != nil { + return nil, err + } + return append(skA, skB...), nil +} + +func (k *hybridKEMPrivKey) Equal(sk kem.PrivateKey) bool { + k1, ok := sk.(*hybridKEMPrivKey) + return ok && + k.privA.Equal(k1.privA) && + k.privB.Equal(k1.privB) +} + +func (k *hybridKEMPrivKey) Public() kem.PublicKey { + return &hybridKEMPubKey{ + scheme: k.scheme, + pubA: k.privA.Public(), + pubB: k.privB.Public(), + } +} + +type hybridKEMPubKey struct { + scheme kem.Scheme + pubA kem.PublicKey + pubB kem.PublicKey +} + +func (k *hybridKEMPubKey) Scheme() kem.Scheme { + return k.scheme +} + +func (k hybridKEMPubKey) MarshalBinary() ([]byte, error) { + pkA, err := k.pubA.MarshalBinary() + if err != nil { + return nil, err + } + pkB, err := k.pubB.MarshalBinary() + if err != nil { + return nil, err + } + return append(pkA, pkB...), nil +} + +func (k *hybridKEMPubKey) Equal(pk kem.PublicKey) bool { + k1, ok := pk.(*hybridKEMPubKey) + return ok && + k.pubA.Equal(k1.pubA) && + k.pubB.Equal(k1.pubB) +} + +// Deterministically derives a keypair from a seed. If you're unsure, +// you're better off using GenerateKey(). +// +// Panics if seed is not of length SeedSize(). +func (h hybridKEM) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + // Implementation based on + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hpke-07.html#name-derivekeypair + if len(seed) != h.SeedSize() { + panic(kem.ErrSeedSize) + } + + outputSeedSize := h.kemA.SeedSize() + h.kemB.SeedSize() + dkpPrk := h.labeledExtract([]byte(""), []byte("dkp_prk"), seed) + bytes := h.labeledExpand( + dkpPrk, + []byte("sk"), + nil, + uint16(outputSeedSize), + ) + seedA := bytes[0:h.kemA.SeedSize()] + seedB := bytes[h.kemA.SeedSize():] + pubA, privA := h.kemA.DeriveKeyPair(seedA) + pubB, privB := h.kemB.DeriveKeyPair(seedB) + + privKey := &hybridKEMPrivKey{ + privA: privA, + privB: privB, + } + pubKey := &hybridKEMPubKey{ + pubA: pubA, + pubB: pubB, + } + + return pubKey, privKey +} + +func (h hybridKEM) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + seed := make([]byte, h.SeedSize()) + _, err := rand.Read(seed) + if err != nil { + return nil, nil, err + } + pk, sk := h.DeriveKeyPair(seed) + return pk, sk, nil +} + +func (h hybridKEM) UnmarshalBinaryPrivateKey(data []byte) (kem.PrivateKey, error) { + skA, err := h.kemA.UnmarshalBinaryPrivateKey(data[0:h.kemA.PrivateKeySize()]) + if err != nil { + return nil, err + } + skB, err := h.kemB.UnmarshalBinaryPrivateKey(data[h.kemA.PrivateKeySize():]) + if err != nil { + return nil, err + } + + return &hybridKEMPrivKey{ + privA: skA, + privB: skB, + }, nil +} + +func (h hybridKEM) UnmarshalBinaryPublicKey(data []byte) (kem.PublicKey, error) { + pkA, err := h.kemA.UnmarshalBinaryPublicKey(data[0:h.kemA.PublicKeySize()]) + if err != nil { + return nil, err + } + pkB, err := h.kemB.UnmarshalBinaryPublicKey(data[h.kemA.PublicKeySize():]) + if err != nil { + return nil, err + } + + return &hybridKEMPubKey{ + pubA: pkA, + pubB: pkB, + }, nil +} diff --git a/src/vendor/github.com/cloudflare/circl/hpke/kembase.go b/src/vendor/github.com/cloudflare/circl/hpke/kembase.go new file mode 100644 index 00000000000..a15765f6df2 --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/hpke/kembase.go @@ -0,0 +1,241 @@ +package hpke + +import ( + "crypto" + "crypto/rand" + "encoding/binary" + "io" + + "github.com/cloudflare/circl/kem" + "golang.org/x/crypto/hkdf" +) + +type dhKEM interface { + sizeDH() int + calcDH(dh []byte, sk kem.PrivateKey, pk kem.PublicKey) error + SeedSize() int + DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) + UnmarshalBinaryPrivateKey(data []byte) (kem.PrivateKey, error) + UnmarshalBinaryPublicKey(data []byte) (kem.PublicKey, error) +} + +type kemBase struct { + id KEM + name string + crypto.Hash +} + +type dhKemBase struct { + kemBase + dhKEM +} + +func (k kemBase) Name() string { return k.name } +func (k kemBase) SharedKeySize() int { return k.Hash.Size() } + +func (k kemBase) getSuiteID() (sid [5]byte) { + sid[0], sid[1], sid[2] = 'K', 'E', 'M' + binary.BigEndian.PutUint16(sid[3:5], uint16(k.id)) + return +} + +func (k kemBase) extractExpand(dh, kemCtx []byte) []byte { + eaePkr := k.labeledExtract([]byte(""), []byte("eae_prk"), dh) + return k.labeledExpand( + eaePkr, + []byte("shared_secret"), + kemCtx, + uint16(k.Size()), + ) +} + +func (k kemBase) labeledExtract(salt, label, info []byte) []byte { + suiteID := k.getSuiteID() + labeledIKM := append(append(append(append( + make([]byte, 0, len(versionLabel)+len(suiteID)+len(label)+len(info)), + versionLabel...), + suiteID[:]...), + label...), + info...) + return hkdf.Extract(k.New, labeledIKM, salt) +} + +func (k kemBase) labeledExpand(prk, label, info []byte, l uint16) []byte { + suiteID := k.getSuiteID() + labeledInfo := make( + []byte, + 2, + 2+len(versionLabel)+len(suiteID)+len(label)+len(info), + ) + binary.BigEndian.PutUint16(labeledInfo[0:2], l) + labeledInfo = append(append(append(append(labeledInfo, + versionLabel...), + suiteID[:]...), + label...), + info...) + b := make([]byte, l) + rd := hkdf.Expand(k.New, prk, labeledInfo) + if _, err := io.ReadFull(rd, b); err != nil { + panic(err) + } + return b +} + +func (k dhKemBase) AuthEncapsulate(pkr kem.PublicKey, sks kem.PrivateKey) ( + ct []byte, ss []byte, err error, +) { + seed := make([]byte, k.SeedSize()) + _, err = io.ReadFull(rand.Reader, seed) + if err != nil { + return nil, nil, err + } + + return k.authEncap(pkr, sks, seed) +} + +func (k dhKemBase) Encapsulate(pkr kem.PublicKey) ( + ct []byte, ss []byte, err error, +) { + seed := make([]byte, k.SeedSize()) + _, err = io.ReadFull(rand.Reader, seed) + if err != nil { + return nil, nil, err + } + + return k.encap(pkr, seed) +} + +func (k dhKemBase) AuthEncapsulateDeterministically( + pkr kem.PublicKey, sks kem.PrivateKey, seed []byte, +) (ct, ss []byte, err error) { + return k.authEncap(pkr, sks, seed) +} + +func (k dhKemBase) EncapsulateDeterministically( + pkr kem.PublicKey, seed []byte, +) (ct, ss []byte, err error) { + return k.encap(pkr, seed) +} + +func (k dhKemBase) encap( + pkR kem.PublicKey, + seed []byte, +) (ct []byte, ss []byte, err error) { + dh := make([]byte, k.sizeDH()) + enc, kemCtx, err := k.coreEncap(dh, pkR, seed) + if err != nil { + return nil, nil, err + } + ss = k.extractExpand(dh, kemCtx) + return enc, ss, nil +} + +func (k dhKemBase) authEncap( + pkR kem.PublicKey, + skS kem.PrivateKey, + seed []byte, +) (ct []byte, ss []byte, err error) { + dhLen := k.sizeDH() + dh := make([]byte, 2*dhLen) + enc, kemCtx, err := k.coreEncap(dh[:dhLen], pkR, seed) + if err != nil { + return nil, nil, err + } + + err = k.calcDH(dh[dhLen:], skS, pkR) + if err != nil { + return nil, nil, err + } + + pkS := skS.Public() + pkSm, err := pkS.MarshalBinary() + if err != nil { + return nil, nil, err + } + kemCtx = append(kemCtx, pkSm...) + + ss = k.extractExpand(dh, kemCtx) + return enc, ss, nil +} + +func (k dhKemBase) coreEncap( + dh []byte, + pkR kem.PublicKey, + seed []byte, +) (enc []byte, kemCtx []byte, err error) { + pkE, skE := k.DeriveKeyPair(seed) + err = k.calcDH(dh, skE, pkR) + if err != nil { + return nil, nil, err + } + + enc, err = pkE.MarshalBinary() + if err != nil { + return nil, nil, err + } + pkRm, err := pkR.MarshalBinary() + if err != nil { + return nil, nil, err + } + kemCtx = append(append([]byte{}, enc...), pkRm...) + + return enc, kemCtx, nil +} + +func (k dhKemBase) Decapsulate(skr kem.PrivateKey, ct []byte) ([]byte, error) { + dh := make([]byte, k.sizeDH()) + kemCtx, err := k.coreDecap(dh, skr, ct) + if err != nil { + return nil, err + } + return k.extractExpand(dh, kemCtx), nil +} + +func (k dhKemBase) AuthDecapsulate( + skR kem.PrivateKey, + ct []byte, + pkS kem.PublicKey, +) ([]byte, error) { + dhLen := k.sizeDH() + dh := make([]byte, 2*dhLen) + kemCtx, err := k.coreDecap(dh[:dhLen], skR, ct) + if err != nil { + return nil, err + } + + err = k.calcDH(dh[dhLen:], skR, pkS) + if err != nil { + return nil, err + } + + pkSm, err := pkS.MarshalBinary() + if err != nil { + return nil, err + } + kemCtx = append(kemCtx, pkSm...) + return k.extractExpand(dh, kemCtx), nil +} + +func (k dhKemBase) coreDecap( + dh []byte, + skR kem.PrivateKey, + ct []byte, +) ([]byte, error) { + pkE, err := k.UnmarshalBinaryPublicKey(ct) + if err != nil { + return nil, err + } + + err = k.calcDH(dh, skR, pkE) + if err != nil { + return nil, err + } + + pkR := skR.Public() + pkRm, err := pkR.MarshalBinary() + if err != nil { + return nil, err + } + + return append(append([]byte{}, ct...), pkRm...), nil +} diff --git a/src/vendor/github.com/cloudflare/circl/hpke/marshal.go b/src/vendor/github.com/cloudflare/circl/hpke/marshal.go new file mode 100644 index 00000000000..4ce02eedaac --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/hpke/marshal.go @@ -0,0 +1,151 @@ +package hpke + +import ( + "errors" + + "golang.org/x/crypto/cryptobyte" +) + +// marshal serializes an HPKE context. +func (c *encdecContext) marshal() ([]byte, error) { + var b cryptobyte.Builder + b.AddUint16(uint16(c.suite.kemID)) + b.AddUint16(uint16(c.suite.kdfID)) + b.AddUint16(uint16(c.suite.aeadID)) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(c.exporterSecret) + }) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(c.key) + }) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(c.baseNonce) + }) + b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { + b.AddBytes(c.sequenceNumber) + }) + return b.Bytes() +} + +// unmarshalContext parses an HPKE context. +func unmarshalContext(raw []byte) (*encdecContext, error) { + var ( + err error + t cryptobyte.String + ) + + c := new(encdecContext) + s := cryptobyte.String(raw) + if !s.ReadUint16((*uint16)(&c.suite.kemID)) || + !s.ReadUint16((*uint16)(&c.suite.kdfID)) || + !s.ReadUint16((*uint16)(&c.suite.aeadID)) || + !s.ReadUint8LengthPrefixed(&t) || + !t.ReadBytes(&c.exporterSecret, len(t)) || + !s.ReadUint8LengthPrefixed(&t) || + !t.ReadBytes(&c.key, len(t)) || + !s.ReadUint8LengthPrefixed(&t) || + !t.ReadBytes(&c.baseNonce, len(t)) || + !s.ReadUint8LengthPrefixed(&t) || + !t.ReadBytes(&c.sequenceNumber, len(t)) { + return nil, errors.New("failed to parse context") + } + + if !c.suite.isValid() { + return nil, ErrInvalidHPKESuite + } + + Nh := c.suite.kdfID.ExtractSize() + if len(c.exporterSecret) != Nh { + return nil, errors.New("invalid exporter secret length") + } + + Nk := int(c.suite.aeadID.KeySize()) + if len(c.key) != Nk { + return nil, errors.New("invalid key length") + } + + c.AEAD, err = c.suite.aeadID.New(c.key) + if err != nil { + return nil, err + } + + Nn := int(c.suite.aeadID.NonceSize()) + if len(c.baseNonce) != Nn { + return nil, errors.New("invalid base nonce length") + } + if len(c.sequenceNumber) != Nn { + return nil, errors.New("invalid sequence number length") + } + c.nonce = make([]byte, Nn) + + return c, nil +} + +// MarshalBinary serializes an HPKE sealer according to the format specified +// below. (Expressed in TLS syntax.) Note that this format is not defined by +// the HPKE standard. +// +// enum { sealer(0), opener(1) } HpkeRole; +// +// struct { +// HpkeKemId kem_id; // draft-irtf-cfrg-hpke-07 +// HpkeKdfId kdf_id; // draft-irtf-cfrg-hpke-07 +// HpkeAeadId aead_id; // draft-irtf-cfrg-hpke-07 +// opaque exporter_secret<0..255>; +// opaque key<0..255>; +// opaque base_nonce<0..255>; +// opaque seq<0..255>; +// } HpkeContext; +// +// struct { +// HpkeRole role = 0; // sealer +// HpkeContext context; +// } HpkeSealer; +func (c *sealContext) MarshalBinary() ([]byte, error) { + rawContext, err := c.encdecContext.marshal() + if err != nil { + return nil, err + } + return append([]byte{0}, rawContext...), nil +} + +// UnmarshalSealer parses an HPKE sealer. +func UnmarshalSealer(raw []byte) (Sealer, error) { + if raw[0] != 0 { + return nil, errors.New("incorrect role") + } + context, err := unmarshalContext(raw[1:]) + if err != nil { + return nil, err + } + return &sealContext{context}, nil +} + +// MarshalBinary serializes an HPKE opener according to the format specified +// below. (Expressed in TLS syntax.) Note that this format is not defined by the +// HPKE standard. +// +// struct { +// HpkeRole role = 1; // opener +// HpkeContext context; +// } HpkeOpener; +func (c *openContext) MarshalBinary() ([]byte, error) { + rawContext, err := c.encdecContext.marshal() + if err != nil { + return nil, err + } + return append([]byte{1}, rawContext...), nil +} + +// UnmarshalOpener parses a serialized HPKE opener and returns the corresponding +// Opener. +func UnmarshalOpener(raw []byte) (Opener, error) { + if raw[0] != 1 { + return nil, errors.New("incorrect role") + } + context, err := unmarshalContext(raw[1:]) + if err != nil { + return nil, err + } + return &openContext{context}, nil +} diff --git a/src/vendor/github.com/cloudflare/circl/hpke/shortkem.go b/src/vendor/github.com/cloudflare/circl/hpke/shortkem.go new file mode 100644 index 00000000000..e5c55e991be --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/hpke/shortkem.go @@ -0,0 +1,170 @@ +package hpke + +import ( + "crypto/elliptic" + "crypto/rand" + "crypto/subtle" + "fmt" + "math/big" + + "github.com/cloudflare/circl/kem" +) + +type shortKEM struct { + dhKemBase + elliptic.Curve +} + +func (s shortKEM) PrivateKeySize() int { return s.byteSize() } +func (s shortKEM) SeedSize() int { return s.byteSize() } +func (s shortKEM) CiphertextSize() int { return 1 + 2*s.byteSize() } +func (s shortKEM) PublicKeySize() int { return 1 + 2*s.byteSize() } +func (s shortKEM) EncapsulationSeedSize() int { return s.byteSize() } + +func (s shortKEM) byteSize() int { return (s.Params().BitSize + 7) / 8 } + +func (s shortKEM) sizeDH() int { return s.byteSize() } +func (s shortKEM) calcDH(dh []byte, sk kem.PrivateKey, pk kem.PublicKey) error { + PK := pk.(*shortKEMPubKey) + SK := sk.(*shortKEMPrivKey) + l := len(dh) + x, _ := s.ScalarMult(PK.x, PK.y, SK.priv) // only x-coordinate is used. + if x.Sign() == 0 { + return ErrInvalidKEMSharedSecret + } + b := x.Bytes() + copy(dh[l-len(b):l], b) + return nil +} + +// Deterministically derives a keypair from a seed. If you're unsure, +// you're better off using GenerateKey(). +// +// Panics if seed is not of length SeedSize(). +func (s shortKEM) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + // Implementation based on + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hpke-07.html#name-derivekeypair + if len(seed) != s.SeedSize() { + panic(kem.ErrSeedSize) + } + + bitmask := byte(0xFF) + if s.Params().BitSize == 521 { + bitmask = 0x01 + } + + dkpPrk := s.labeledExtract([]byte(""), []byte("dkp_prk"), seed) + var bytes []byte + ctr := 0 + for skBig := new(big.Int); skBig.Sign() == 0 || skBig.Cmp(s.Params().N) >= 0; ctr++ { + if ctr > 255 { + panic("derive key error") + } + bytes = s.labeledExpand( + dkpPrk, + []byte("candidate"), + []byte{byte(ctr)}, + uint16(s.byteSize()), + ) + bytes[0] &= bitmask + skBig.SetBytes(bytes) + } + l := s.PrivateKeySize() + sk := &shortKEMPrivKey{s, make([]byte, l), nil} + copy(sk.priv[l-len(bytes):], bytes) + return sk.Public(), sk +} + +func (s shortKEM) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + sk, x, y, err := elliptic.GenerateKey(s, rand.Reader) + pub := &shortKEMPubKey{s, x, y} + return pub, &shortKEMPrivKey{s, sk, pub}, err +} + +func (s shortKEM) UnmarshalBinaryPrivateKey(data []byte) (kem.PrivateKey, error) { + l := s.PrivateKeySize() + if len(data) < l { + return nil, ErrInvalidKEMPrivateKey + } + sk := &shortKEMPrivKey{s, make([]byte, l), nil} + copy(sk.priv[l-len(data):l], data[:l]) + if !sk.validate() { + return nil, ErrInvalidKEMPrivateKey + } + + return sk, nil +} + +func (s shortKEM) UnmarshalBinaryPublicKey(data []byte) (kem.PublicKey, error) { + x, y := elliptic.Unmarshal(s, data) + if x == nil { + return nil, ErrInvalidKEMPublicKey + } + key := &shortKEMPubKey{s, x, y} + if !key.validate() { + return nil, ErrInvalidKEMPublicKey + } + return key, nil +} + +type shortKEMPubKey struct { + scheme shortKEM + x, y *big.Int +} + +func (k *shortKEMPubKey) String() string { + return fmt.Sprintf("x: %v\ny: %v", k.x.Text(16), k.y.Text(16)) +} +func (k *shortKEMPubKey) Scheme() kem.Scheme { return k.scheme } +func (k *shortKEMPubKey) MarshalBinary() ([]byte, error) { + return elliptic.Marshal(k.scheme, k.x, k.y), nil +} + +func (k *shortKEMPubKey) Equal(pk kem.PublicKey) bool { + k1, ok := pk.(*shortKEMPubKey) + return ok && + k.scheme.Params().Name == k1.scheme.Params().Name && + k.x.Cmp(k1.x) == 0 && + k.y.Cmp(k1.y) == 0 +} + +func (k *shortKEMPubKey) validate() bool { + p := k.scheme.Params().P + notAtInfinity := k.x.Sign() > 0 && k.y.Sign() > 0 + lessThanP := k.x.Cmp(p) < 0 && k.y.Cmp(p) < 0 + onCurve := k.scheme.IsOnCurve(k.x, k.y) + return notAtInfinity && lessThanP && onCurve +} + +type shortKEMPrivKey struct { + scheme shortKEM + priv []byte + pub *shortKEMPubKey +} + +func (k *shortKEMPrivKey) String() string { return fmt.Sprintf("%x", k.priv) } +func (k *shortKEMPrivKey) Scheme() kem.Scheme { return k.scheme } +func (k *shortKEMPrivKey) MarshalBinary() ([]byte, error) { + return append(make([]byte, 0, k.scheme.PrivateKeySize()), k.priv...), nil +} + +func (k *shortKEMPrivKey) Equal(pk kem.PrivateKey) bool { + k1, ok := pk.(*shortKEMPrivKey) + return ok && + k.scheme.Params().Name == k1.scheme.Params().Name && + subtle.ConstantTimeCompare(k.priv, k1.priv) == 1 +} + +func (k *shortKEMPrivKey) Public() kem.PublicKey { + if k.pub == nil { + x, y := k.scheme.ScalarBaseMult(k.priv) + k.pub = &shortKEMPubKey{k.scheme, x, y} + } + return k.pub +} + +func (k *shortKEMPrivKey) validate() bool { + n := new(big.Int).SetBytes(k.priv) + order := k.scheme.Curve.Params().N + return len(k.priv) == k.scheme.PrivateKeySize() && n.Cmp(order) < 0 +} diff --git a/src/vendor/github.com/cloudflare/circl/hpke/util.go b/src/vendor/github.com/cloudflare/circl/hpke/util.go new file mode 100644 index 00000000000..c9fed4fcf74 --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/hpke/util.go @@ -0,0 +1,121 @@ +package hpke + +import ( + "encoding/binary" + "errors" + "fmt" +) + +func (st state) keySchedule(ss, info, psk, pskID []byte) (*encdecContext, error) { + if err := st.verifyPSKInputs(psk, pskID); err != nil { + return nil, err + } + + pskIDHash := st.labeledExtract(nil, []byte("psk_id_hash"), pskID) + infoHash := st.labeledExtract(nil, []byte("info_hash"), info) + keySchCtx := append(append( + []byte{st.modeID}, + pskIDHash...), + infoHash...) + + secret := st.labeledExtract(ss, []byte("secret"), psk) + + Nk := uint16(st.aeadID.KeySize()) + key := st.labeledExpand(secret, []byte("key"), keySchCtx, Nk) + + aead, err := st.aeadID.New(key) + if err != nil { + return nil, err + } + + Nn := uint16(aead.NonceSize()) + baseNonce := st.labeledExpand(secret, []byte("base_nonce"), keySchCtx, Nn) + exporterSecret := st.labeledExpand( + secret, + []byte("exp"), + keySchCtx, + uint16(st.kdfID.ExtractSize()), + ) + + return &encdecContext{ + st.Suite, + ss, + secret, + keySchCtx, + exporterSecret, + key, + baseNonce, + make([]byte, Nn), + aead, + make([]byte, Nn), + }, nil +} + +func (st state) verifyPSKInputs(psk, pskID []byte) error { + gotPSK := psk != nil + gotPSKID := pskID != nil + if gotPSK != gotPSKID { + return errors.New("inconsistent PSK inputs") + } + switch st.modeID { + case modeBase | modeAuth: + if gotPSK { + return errors.New("PSK input provided when not needed") + } + case modePSK | modeAuthPSK: + if !gotPSK { + return errors.New("missing required PSK input") + } + } + return nil +} + +// Params returns the codepoints for the algorithms comprising the suite. +func (suite Suite) Params() (KEM, KDF, AEAD) { + return suite.kemID, suite.kdfID, suite.aeadID +} + +func (suite Suite) String() string { + return fmt.Sprintf( + "kem_id: %v kdf_id: %v aead_id: %v", + suite.kemID, suite.kdfID, suite.aeadID, + ) +} + +func (suite Suite) getSuiteID() (id [10]byte) { + id[0], id[1], id[2], id[3] = 'H', 'P', 'K', 'E' + binary.BigEndian.PutUint16(id[4:6], uint16(suite.kemID)) + binary.BigEndian.PutUint16(id[6:8], uint16(suite.kdfID)) + binary.BigEndian.PutUint16(id[8:10], uint16(suite.aeadID)) + return +} + +func (suite Suite) isValid() bool { + return suite.kemID.IsValid() && + suite.kdfID.IsValid() && + suite.aeadID.IsValid() +} + +func (suite Suite) labeledExtract(salt, label, ikm []byte) []byte { + suiteID := suite.getSuiteID() + labeledIKM := append(append(append(append( + make([]byte, 0, len(versionLabel)+len(suiteID)+len(label)+len(ikm)), + versionLabel...), + suiteID[:]...), + label...), + ikm...) + return suite.kdfID.Extract(labeledIKM, salt) +} + +func (suite Suite) labeledExpand(prk, label, info []byte, l uint16) []byte { + suiteID := suite.getSuiteID() + labeledInfo := make([]byte, + 2, 2+len(versionLabel)+len(suiteID)+len(label)+len(info)) + binary.BigEndian.PutUint16(labeledInfo[0:2], l) + labeledInfo = append(append(append(append(labeledInfo, + versionLabel...), + suiteID[:]...), + label...), + info...) + return suite.kdfID.Expand(prk, labeledInfo, uint(l)) +} diff --git a/src/vendor/github.com/cloudflare/circl/hpke/xkem.go b/src/vendor/github.com/cloudflare/circl/hpke/xkem.go new file mode 100644 index 00000000000..f11ab6b374a --- /dev/null +++ b/src/vendor/github.com/cloudflare/circl/hpke/xkem.go @@ -0,0 +1,164 @@ +package hpke + +import ( + "bytes" + "crypto/rand" + "crypto/subtle" + "fmt" + "io" + + "github.com/cloudflare/circl/dh/x25519" + "github.com/cloudflare/circl/dh/x448" + "github.com/cloudflare/circl/kem" +) + +type xKEM struct { + dhKemBase + size int +} + +func (x xKEM) PrivateKeySize() int { return x.size } +func (x xKEM) SeedSize() int { return x.size } +func (x xKEM) CiphertextSize() int { return x.size } +func (x xKEM) PublicKeySize() int { return x.size } +func (x xKEM) EncapsulationSeedSize() int { return x.size } + +func (x xKEM) sizeDH() int { return x.size } +func (x xKEM) calcDH(dh []byte, sk kem.PrivateKey, pk kem.PublicKey) error { + PK := pk.(*xKEMPubKey) + SK := sk.(*xKEMPrivKey) + switch x.size { + case x25519.Size: + var ss, sKey, pKey x25519.Key + copy(sKey[:], SK.priv) + copy(pKey[:], PK.pub) + if !x25519.Shared(&ss, &sKey, &pKey) { + return ErrInvalidKEMSharedSecret + } + copy(dh, ss[:]) + case x448.Size: + var ss, sKey, pKey x448.Key + copy(sKey[:], SK.priv) + copy(pKey[:], PK.pub) + if !x448.Shared(&ss, &sKey, &pKey) { + return ErrInvalidKEMSharedSecret + } + copy(dh, ss[:]) + } + return nil +} + +// Deterministically derives a keypair from a seed. If you're unsure, +// you're better off using GenerateKey(). +// +// Panics if seed is not of length SeedSize(). +func (x xKEM) DeriveKeyPair(seed []byte) (kem.PublicKey, kem.PrivateKey) { + // Implementation based on + // https://www.ietf.org/archive/id/draft-irtf-cfrg-hpke-07.html#name-derivekeypair + if len(seed) != x.SeedSize() { + panic(kem.ErrSeedSize) + } + sk := &xKEMPrivKey{scheme: x, priv: make([]byte, x.size)} + dkpPrk := x.labeledExtract([]byte(""), []byte("dkp_prk"), seed) + bytes := x.labeledExpand( + dkpPrk, + []byte("sk"), + nil, + uint16(x.PrivateKeySize()), + ) + copy(sk.priv, bytes) + return sk.Public(), sk +} + +func (x xKEM) GenerateKeyPair() (kem.PublicKey, kem.PrivateKey, error) { + sk := &xKEMPrivKey{scheme: x, priv: make([]byte, x.PrivateKeySize())} + _, err := io.ReadFull(rand.Reader, sk.priv) + if err != nil { + return nil, nil, err + } + return sk.Public(), sk, nil +} + +func (x xKEM) UnmarshalBinaryPrivateKey(data []byte) (kem.PrivateKey, error) { + l := x.PrivateKeySize() + if len(data) < l { + return nil, ErrInvalidKEMPrivateKey + } + sk := &xKEMPrivKey{x, make([]byte, l), nil} + copy(sk.priv, data[:l]) + if !sk.validate() { + return nil, ErrInvalidKEMPrivateKey + } + return sk, nil +} + +func (x xKEM) UnmarshalBinaryPublicKey(data []byte) (kem.PublicKey, error) { + l := x.PublicKeySize() + if len(data) < l { + return nil, ErrInvalidKEMPublicKey + } + pk := &xKEMPubKey{x, make([]byte, l)} + copy(pk.pub, data[:l]) + if !pk.validate() { + return nil, ErrInvalidKEMPublicKey + } + return pk, nil +} + +type xKEMPubKey struct { + scheme xKEM + pub []byte +} + +func (k *xKEMPubKey) String() string { return fmt.Sprintf("%x", k.pub) } +func (k *xKEMPubKey) Scheme() kem.Scheme { return k.scheme } +func (k *xKEMPubKey) MarshalBinary() ([]byte, error) { + return append(make([]byte, 0, k.scheme.PublicKeySize()), k.pub...), nil +} + +func (k *xKEMPubKey) Equal(pk kem.PublicKey) bool { + k1, ok := pk.(*xKEMPubKey) + return ok && + k.scheme.id == k1.scheme.id && + bytes.Equal(k.pub, k1.pub) +} +func (k *xKEMPubKey) validate() bool { return len(k.pub) == k.scheme.PublicKeySize() } + +type xKEMPrivKey struct { + scheme xKEM + priv []byte + pub *xKEMPubKey +} + +func (k *xKEMPrivKey) String() string { return fmt.Sprintf("%x", k.priv) } +func (k *xKEMPrivKey) Scheme() kem.Scheme { return k.scheme } +func (k *xKEMPrivKey) MarshalBinary() ([]byte, error) { + return append(make([]byte, 0, k.scheme.PrivateKeySize()), k.priv...), nil +} + +func (k *xKEMPrivKey) Equal(pk kem.PrivateKey) bool { + k1, ok := pk.(*xKEMPrivKey) + return ok && + k.scheme.id == k1.scheme.id && + subtle.ConstantTimeCompare(k.priv, k1.priv) == 1 +} + +func (k *xKEMPrivKey) Public() kem.PublicKey { + if k.pub == nil { + k.pub = &xKEMPubKey{scheme: k.scheme, pub: make([]byte, k.scheme.size)} + switch k.scheme.size { + case x25519.Size: + var sk, pk x25519.Key + copy(sk[:], k.priv) + x25519.KeyGen(&pk, &sk) + copy(k.pub.pub, pk[:]) + case x448.Size: + var sk, pk x448.Key + copy(sk[:], k.priv) + x448.KeyGen(&pk, &sk) + copy(k.pub.pub, pk[:]) + } + } + return k.pub +} +func (k *xKEMPrivKey) validate() bool { return len(k.priv) == k.scheme.PrivateKeySize() } diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt index 529b8a6a973..dd4540d7e4e 100644 --- a/src/vendor/modules.txt +++ b/src/vendor/modules.txt @@ -3,6 +3,8 @@ github.com/cloudflare/circl/dh/x25519 github.com/cloudflare/circl/dh/x448 github.com/cloudflare/circl/ecc/goldilocks +github.com/cloudflare/circl/ecc/p384 +github.com/cloudflare/circl/hpke github.com/cloudflare/circl/internal/conv github.com/cloudflare/circl/internal/sha3 github.com/cloudflare/circl/kem