working-config-otel

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
This commit is contained in:
Davanum Srinivas
2023-10-18 18:47:37 -04:00
parent af52a7052b
commit 702d911e22
322 changed files with 51855 additions and 7098 deletions

6
vendor/github.com/google/s2a-go/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,6 @@
# Ignore binaries without extension
//example/client/client
//example/server/server
//internal/v2/fakes2av2_server/fakes2av2_server
.idea/

93
vendor/github.com/google/s2a-go/CODE_OF_CONDUCT.md generated vendored Normal file
View File

@@ -0,0 +1,93 @@
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of
experience, education, socio-economic status, nationality, personal appearance,
race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, or to ban temporarily or permanently any
contributor for other behaviors that they deem inappropriate, threatening,
offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
This Code of Conduct also applies outside the project spaces when the Project
Steward has a reasonable belief that an individual's behavior may have a
negative impact on the project or its community.
## Conflict Resolution
We do not believe that all conflict is bad; healthy debate and disagreement
often yield positive results. However, it is never okay to be disrespectful or
to engage in behavior that violates the projects code of conduct.
If you see someone violating the code of conduct, you are encouraged to address
the behavior directly with those involved. Many issues can be resolved quickly
and easily, and this gives people more control over the outcome of their
dispute. If you are unable to resolve the matter for any reason, or if the
behavior is threatening or harassing, report it. We are dedicated to providing
an environment where participants feel welcome and safe.
Reports should be directed to *[PROJECT STEWARD NAME(s) AND EMAIL(s)]*, the
Project Steward(s) for *[PROJECT NAME]*. It is the Project Stewards duty to
receive and address reported violations of the code of conduct. They will then
work with a committee consisting of representatives from the Open Source
Programs Office and the Google Open Source Strategy team. If for any reason you
are uncomfortable reaching out to the Project Steward, please email
opensource@google.com.
We will investigate every complaint, but you may not receive a direct response.
We will use our discretion in determining when and how to follow up on reported
incidents, which may range from not taking action to permanent expulsion from
the project and project-sponsored spaces. We will notify the accused of the
report and provide them an opportunity to discuss it before any action is taken.
The identity of the reporter will be omitted from the details of the report
supplied to the accused. In potentially harmful situations, such as ongoing
harassment or threats to anyone's safety, we may take action without notice.
## Attribution
This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
available at
https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

29
vendor/github.com/google/s2a-go/CONTRIBUTING.md generated vendored Normal file
View File

@@ -0,0 +1,29 @@
# How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement (CLA). You (or your employer) retain the copyright to your
contribution; this simply gives us permission to use and redistribute your
contributions as part of the project. Head over to
<https://cla.developers.google.com/> to see your current agreements on file or
to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
## Community Guidelines
This project follows
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).

202
vendor/github.com/google/s2a-go/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

14
vendor/github.com/google/s2a-go/README.md generated vendored Normal file
View File

@@ -0,0 +1,14 @@
# Secure Session Agent Client Libraries
The Secure Session Agent is a service that enables a workload to offload select
operations from the mTLS handshake and protects a workload's private key
material from exfiltration. Specifically, the workload asks the Secure Session
Agent for the TLS configuration to use during the handshake, to perform private
key operations, and to validate the peer certificate chain. The Secure Session
Agent's client libraries enable applications to communicate with the Secure
Session Agent during the TLS handshake, and to encrypt traffic to the peer
after the TLS handshake is complete.
This repository contains the source code for the Secure Session Agent's Go
client libraries, which allow gRPC and HTTP Go applications to use the Secure Session
Agent.

View File

@@ -0,0 +1,167 @@
/*
*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package fallback provides default implementations of fallback options when S2A fails.
package fallback
import (
"context"
"crypto/tls"
"fmt"
"net"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
)
const (
alpnProtoStrH2 = "h2"
alpnProtoStrHTTP = "http/1.1"
defaultHTTPSPort = "443"
)
// FallbackTLSConfigGRPC is a tls.Config used by the DefaultFallbackClientHandshakeFunc function.
// It supports GRPC use case, thus the alpn is set to 'h2'.
var FallbackTLSConfigGRPC = tls.Config{
MinVersion: tls.VersionTLS13,
ClientSessionCache: nil,
NextProtos: []string{alpnProtoStrH2},
}
// FallbackTLSConfigHTTP is a tls.Config used by the DefaultFallbackDialerAndAddress func.
// It supports the HTTP use case and the alpn is set to both 'http/1.1' and 'h2'.
var FallbackTLSConfigHTTP = tls.Config{
MinVersion: tls.VersionTLS13,
ClientSessionCache: nil,
NextProtos: []string{alpnProtoStrH2, alpnProtoStrHTTP},
}
// ClientHandshake establishes a TLS connection and returns it, plus its auth info.
// Inputs:
//
// targetServer: the server attempted with S2A.
// conn: the tcp connection to the server at address targetServer that was passed into S2A's ClientHandshake func.
// If fallback is successful, the `conn` should be closed.
// err: the error encountered when performing the client-side TLS handshake with S2A.
type ClientHandshake func(ctx context.Context, targetServer string, conn net.Conn, err error) (net.Conn, credentials.AuthInfo, error)
// DefaultFallbackClientHandshakeFunc returns a ClientHandshake function,
// which establishes a TLS connection to the provided fallbackAddr, returns the new connection and its auth info.
// Example use:
//
// transportCreds, _ = s2a.NewClientCreds(&s2a.ClientOptions{
// S2AAddress: s2aAddress,
// FallbackOpts: &s2a.FallbackOptions{ // optional
// FallbackClientHandshakeFunc: fallback.DefaultFallbackClientHandshakeFunc(fallbackAddr),
// },
// })
//
// The fallback server's certificate must be verifiable using OS root store.
// The fallbackAddr is expected to be a network address, e.g. example.com:port. If port is not specified,
// it uses default port 443.
// In the returned function's TLS config, ClientSessionCache is explicitly set to nil to disable TLS resumption,
// and min TLS version is set to 1.3.
func DefaultFallbackClientHandshakeFunc(fallbackAddr string) (ClientHandshake, error) {
var fallbackDialer = tls.Dialer{Config: &FallbackTLSConfigGRPC}
return defaultFallbackClientHandshakeFuncInternal(fallbackAddr, fallbackDialer.DialContext)
}
func defaultFallbackClientHandshakeFuncInternal(fallbackAddr string, dialContextFunc func(context.Context, string, string) (net.Conn, error)) (ClientHandshake, error) {
fallbackServerAddr, err := processFallbackAddr(fallbackAddr)
if err != nil {
if grpclog.V(1) {
grpclog.Infof("error processing fallback address [%s]: %v", fallbackAddr, err)
}
return nil, err
}
return func(ctx context.Context, targetServer string, conn net.Conn, s2aErr error) (net.Conn, credentials.AuthInfo, error) {
fbConn, fbErr := dialContextFunc(ctx, "tcp", fallbackServerAddr)
if fbErr != nil {
grpclog.Infof("dialing to fallback server %s failed: %v", fallbackServerAddr, fbErr)
return nil, nil, fmt.Errorf("dialing to fallback server %s failed: %v; S2A client handshake with %s error: %w", fallbackServerAddr, fbErr, targetServer, s2aErr)
}
tc, success := fbConn.(*tls.Conn)
if !success {
grpclog.Infof("the connection with fallback server is expected to be tls but isn't")
return nil, nil, fmt.Errorf("the connection with fallback server is expected to be tls but isn't; S2A client handshake with %s error: %w", targetServer, s2aErr)
}
tlsInfo := credentials.TLSInfo{
State: tc.ConnectionState(),
CommonAuthInfo: credentials.CommonAuthInfo{
SecurityLevel: credentials.PrivacyAndIntegrity,
},
}
if grpclog.V(1) {
grpclog.Infof("ConnectionState.NegotiatedProtocol: %v", tc.ConnectionState().NegotiatedProtocol)
grpclog.Infof("ConnectionState.HandshakeComplete: %v", tc.ConnectionState().HandshakeComplete)
grpclog.Infof("ConnectionState.ServerName: %v", tc.ConnectionState().ServerName)
}
conn.Close()
return fbConn, tlsInfo, nil
}, nil
}
// DefaultFallbackDialerAndAddress returns a TLS dialer and the network address to dial.
// Example use:
//
// fallbackDialer, fallbackServerAddr := fallback.DefaultFallbackDialerAndAddress(fallbackAddr)
// dialTLSContext := s2a.NewS2aDialTLSContextFunc(&s2a.ClientOptions{
// S2AAddress: s2aAddress, // required
// FallbackOpts: &s2a.FallbackOptions{
// FallbackDialer: &s2a.FallbackDialer{
// Dialer: fallbackDialer,
// ServerAddr: fallbackServerAddr,
// },
// },
// })
//
// The fallback server's certificate should be verifiable using OS root store.
// The fallbackAddr is expected to be a network address, e.g. example.com:port. If port is not specified,
// it uses default port 443.
// In the returned function's TLS config, ClientSessionCache is explicitly set to nil to disable TLS resumption,
// and min TLS version is set to 1.3.
func DefaultFallbackDialerAndAddress(fallbackAddr string) (*tls.Dialer, string, error) {
fallbackServerAddr, err := processFallbackAddr(fallbackAddr)
if err != nil {
if grpclog.V(1) {
grpclog.Infof("error processing fallback address [%s]: %v", fallbackAddr, err)
}
return nil, "", err
}
return &tls.Dialer{Config: &FallbackTLSConfigHTTP}, fallbackServerAddr, nil
}
func processFallbackAddr(fallbackAddr string) (string, error) {
var fallbackServerAddr string
var err error
if fallbackAddr == "" {
return "", fmt.Errorf("empty fallback address")
}
_, _, err = net.SplitHostPort(fallbackAddr)
if err != nil {
// fallbackAddr does not have port suffix
fallbackServerAddr = net.JoinHostPort(fallbackAddr, defaultHTTPSPort)
} else {
// FallbackServerAddr already has port suffix
fallbackServerAddr = fallbackAddr
}
return fallbackServerAddr, nil
}

View File

@@ -0,0 +1,119 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package authinfo provides authentication and authorization information that
// results from the TLS handshake.
package authinfo
import (
"errors"
commonpb "github.com/google/s2a-go/internal/proto/common_go_proto"
contextpb "github.com/google/s2a-go/internal/proto/s2a_context_go_proto"
grpcpb "github.com/google/s2a-go/internal/proto/s2a_go_proto"
"google.golang.org/grpc/credentials"
)
var _ credentials.AuthInfo = (*S2AAuthInfo)(nil)
const s2aAuthType = "s2a"
// S2AAuthInfo exposes authentication and authorization information from the
// S2A session result to the gRPC stack.
type S2AAuthInfo struct {
s2aContext *contextpb.S2AContext
commonAuthInfo credentials.CommonAuthInfo
}
// NewS2AAuthInfo returns a new S2AAuthInfo object from the S2A session result.
func NewS2AAuthInfo(result *grpcpb.SessionResult) (credentials.AuthInfo, error) {
return newS2AAuthInfo(result)
}
func newS2AAuthInfo(result *grpcpb.SessionResult) (*S2AAuthInfo, error) {
if result == nil {
return nil, errors.New("NewS2aAuthInfo given nil session result")
}
return &S2AAuthInfo{
s2aContext: &contextpb.S2AContext{
ApplicationProtocol: result.GetApplicationProtocol(),
TlsVersion: result.GetState().GetTlsVersion(),
Ciphersuite: result.GetState().GetTlsCiphersuite(),
PeerIdentity: result.GetPeerIdentity(),
LocalIdentity: result.GetLocalIdentity(),
PeerCertFingerprint: result.GetPeerCertFingerprint(),
LocalCertFingerprint: result.GetLocalCertFingerprint(),
IsHandshakeResumed: result.GetState().GetIsHandshakeResumed(),
},
commonAuthInfo: credentials.CommonAuthInfo{SecurityLevel: credentials.PrivacyAndIntegrity},
}, nil
}
// AuthType returns the authentication type.
func (s *S2AAuthInfo) AuthType() string {
return s2aAuthType
}
// ApplicationProtocol returns the application protocol, e.g. "grpc".
func (s *S2AAuthInfo) ApplicationProtocol() string {
return s.s2aContext.GetApplicationProtocol()
}
// TLSVersion returns the TLS version negotiated during the handshake.
func (s *S2AAuthInfo) TLSVersion() commonpb.TLSVersion {
return s.s2aContext.GetTlsVersion()
}
// Ciphersuite returns the ciphersuite negotiated during the handshake.
func (s *S2AAuthInfo) Ciphersuite() commonpb.Ciphersuite {
return s.s2aContext.GetCiphersuite()
}
// PeerIdentity returns the authenticated identity of the peer.
func (s *S2AAuthInfo) PeerIdentity() *commonpb.Identity {
return s.s2aContext.GetPeerIdentity()
}
// LocalIdentity returns the local identity of the application used during
// session setup.
func (s *S2AAuthInfo) LocalIdentity() *commonpb.Identity {
return s.s2aContext.GetLocalIdentity()
}
// PeerCertFingerprint returns the SHA256 hash of the peer certificate used in
// the S2A handshake.
func (s *S2AAuthInfo) PeerCertFingerprint() []byte {
return s.s2aContext.GetPeerCertFingerprint()
}
// LocalCertFingerprint returns the SHA256 hash of the local certificate used
// in the S2A handshake.
func (s *S2AAuthInfo) LocalCertFingerprint() []byte {
return s.s2aContext.GetLocalCertFingerprint()
}
// IsHandshakeResumed returns true if a cached session was used to resume
// the handshake.
func (s *S2AAuthInfo) IsHandshakeResumed() bool {
return s.s2aContext.GetIsHandshakeResumed()
}
// SecurityLevel returns the security level of the connection.
func (s *S2AAuthInfo) SecurityLevel() credentials.SecurityLevel {
return s.commonAuthInfo.SecurityLevel
}

View File

@@ -0,0 +1,438 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package handshaker communicates with the S2A handshaker service.
package handshaker
import (
"context"
"errors"
"fmt"
"io"
"net"
"sync"
"github.com/google/s2a-go/internal/authinfo"
commonpb "github.com/google/s2a-go/internal/proto/common_go_proto"
s2apb "github.com/google/s2a-go/internal/proto/s2a_go_proto"
"github.com/google/s2a-go/internal/record"
"github.com/google/s2a-go/internal/tokenmanager"
grpc "google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
)
var (
// appProtocol contains the application protocol accepted by the handshaker.
appProtocol = "grpc"
// frameLimit is the maximum size of a frame in bytes.
frameLimit = 1024 * 64
// peerNotRespondingError is the error thrown when the peer doesn't respond.
errPeerNotResponding = errors.New("peer is not responding and re-connection should be attempted")
)
// Handshaker defines a handshaker interface.
type Handshaker interface {
// ClientHandshake starts and completes a TLS handshake from the client side,
// and returns a secure connection along with additional auth information.
ClientHandshake(ctx context.Context) (net.Conn, credentials.AuthInfo, error)
// ServerHandshake starts and completes a TLS handshake from the server side,
// and returns a secure connection along with additional auth information.
ServerHandshake(ctx context.Context) (net.Conn, credentials.AuthInfo, error)
// Close terminates the Handshaker. It should be called when the handshake
// is complete.
Close() error
}
// ClientHandshakerOptions contains the options needed to configure the S2A
// handshaker service on the client-side.
type ClientHandshakerOptions struct {
// MinTLSVersion specifies the min TLS version supported by the client.
MinTLSVersion commonpb.TLSVersion
// MaxTLSVersion specifies the max TLS version supported by the client.
MaxTLSVersion commonpb.TLSVersion
// TLSCiphersuites is the ordered list of ciphersuites supported by the
// client.
TLSCiphersuites []commonpb.Ciphersuite
// TargetIdentities contains a list of allowed server identities. One of the
// target identities should match the peer identity in the handshake
// result; otherwise, the handshake fails.
TargetIdentities []*commonpb.Identity
// LocalIdentity is the local identity of the client application. If none is
// provided, then the S2A will choose the default identity.
LocalIdentity *commonpb.Identity
// TargetName is the allowed server name, which may be used for server
// authorization check by the S2A if it is provided.
TargetName string
// EnsureProcessSessionTickets allows users to wait and ensure that all
// available session tickets are sent to S2A before a process completes.
EnsureProcessSessionTickets *sync.WaitGroup
}
// ServerHandshakerOptions contains the options needed to configure the S2A
// handshaker service on the server-side.
type ServerHandshakerOptions struct {
// MinTLSVersion specifies the min TLS version supported by the server.
MinTLSVersion commonpb.TLSVersion
// MaxTLSVersion specifies the max TLS version supported by the server.
MaxTLSVersion commonpb.TLSVersion
// TLSCiphersuites is the ordered list of ciphersuites supported by the
// server.
TLSCiphersuites []commonpb.Ciphersuite
// LocalIdentities is the list of local identities that may be assumed by
// the server. If no local identity is specified, then the S2A chooses a
// default local identity.
LocalIdentities []*commonpb.Identity
}
// s2aHandshaker performs a TLS handshake using the S2A handshaker service.
type s2aHandshaker struct {
// stream is used to communicate with the S2A handshaker service.
stream s2apb.S2AService_SetUpSessionClient
// conn is the connection to the peer.
conn net.Conn
// clientOpts should be non-nil iff the handshaker is client-side.
clientOpts *ClientHandshakerOptions
// serverOpts should be non-nil iff the handshaker is server-side.
serverOpts *ServerHandshakerOptions
// isClient determines if the handshaker is client or server side.
isClient bool
// hsAddr stores the address of the S2A handshaker service.
hsAddr string
// tokenManager manages access tokens for authenticating to S2A.
tokenManager tokenmanager.AccessTokenManager
// localIdentities is the set of local identities for whom the
// tokenManager should fetch a token when preparing a request to be
// sent to S2A.
localIdentities []*commonpb.Identity
}
// NewClientHandshaker creates an s2aHandshaker instance that performs a
// client-side TLS handshake using the S2A handshaker service.
func NewClientHandshaker(ctx context.Context, conn *grpc.ClientConn, c net.Conn, hsAddr string, opts *ClientHandshakerOptions) (Handshaker, error) {
stream, err := s2apb.NewS2AServiceClient(conn).SetUpSession(ctx, grpc.WaitForReady(true))
if err != nil {
return nil, err
}
tokenManager, err := tokenmanager.NewSingleTokenAccessTokenManager()
if err != nil {
grpclog.Infof("failed to create single token access token manager: %v", err)
}
return newClientHandshaker(stream, c, hsAddr, opts, tokenManager), nil
}
func newClientHandshaker(stream s2apb.S2AService_SetUpSessionClient, c net.Conn, hsAddr string, opts *ClientHandshakerOptions, tokenManager tokenmanager.AccessTokenManager) *s2aHandshaker {
var localIdentities []*commonpb.Identity
if opts != nil {
localIdentities = []*commonpb.Identity{opts.LocalIdentity}
}
return &s2aHandshaker{
stream: stream,
conn: c,
clientOpts: opts,
isClient: true,
hsAddr: hsAddr,
tokenManager: tokenManager,
localIdentities: localIdentities,
}
}
// NewServerHandshaker creates an s2aHandshaker instance that performs a
// server-side TLS handshake using the S2A handshaker service.
func NewServerHandshaker(ctx context.Context, conn *grpc.ClientConn, c net.Conn, hsAddr string, opts *ServerHandshakerOptions) (Handshaker, error) {
stream, err := s2apb.NewS2AServiceClient(conn).SetUpSession(ctx, grpc.WaitForReady(true))
if err != nil {
return nil, err
}
tokenManager, err := tokenmanager.NewSingleTokenAccessTokenManager()
if err != nil {
grpclog.Infof("failed to create single token access token manager: %v", err)
}
return newServerHandshaker(stream, c, hsAddr, opts, tokenManager), nil
}
func newServerHandshaker(stream s2apb.S2AService_SetUpSessionClient, c net.Conn, hsAddr string, opts *ServerHandshakerOptions, tokenManager tokenmanager.AccessTokenManager) *s2aHandshaker {
var localIdentities []*commonpb.Identity
if opts != nil {
localIdentities = opts.LocalIdentities
}
return &s2aHandshaker{
stream: stream,
conn: c,
serverOpts: opts,
isClient: false,
hsAddr: hsAddr,
tokenManager: tokenManager,
localIdentities: localIdentities,
}
}
// ClientHandshake performs a client-side TLS handshake using the S2A handshaker
// service. When complete, returns a TLS connection.
func (h *s2aHandshaker) ClientHandshake(_ context.Context) (net.Conn, credentials.AuthInfo, error) {
if !h.isClient {
return nil, nil, errors.New("only handshakers created using NewClientHandshaker can perform a client-side handshake")
}
// Extract the hostname from the target name. The target name is assumed to be an authority.
hostname, _, err := net.SplitHostPort(h.clientOpts.TargetName)
if err != nil {
// If the target name had no host port or could not be parsed, use it as is.
hostname = h.clientOpts.TargetName
}
// Prepare a client start message to send to the S2A handshaker service.
req := &s2apb.SessionReq{
ReqOneof: &s2apb.SessionReq_ClientStart{
ClientStart: &s2apb.ClientSessionStartReq{
ApplicationProtocols: []string{appProtocol},
MinTlsVersion: h.clientOpts.MinTLSVersion,
MaxTlsVersion: h.clientOpts.MaxTLSVersion,
TlsCiphersuites: h.clientOpts.TLSCiphersuites,
TargetIdentities: h.clientOpts.TargetIdentities,
LocalIdentity: h.clientOpts.LocalIdentity,
TargetName: hostname,
},
},
AuthMechanisms: h.getAuthMechanisms(),
}
conn, result, err := h.setUpSession(req)
if err != nil {
return nil, nil, err
}
authInfo, err := authinfo.NewS2AAuthInfo(result)
if err != nil {
return nil, nil, err
}
return conn, authInfo, nil
}
// ServerHandshake performs a server-side TLS handshake using the S2A handshaker
// service. When complete, returns a TLS connection.
func (h *s2aHandshaker) ServerHandshake(_ context.Context) (net.Conn, credentials.AuthInfo, error) {
if h.isClient {
return nil, nil, errors.New("only handshakers created using NewServerHandshaker can perform a server-side handshake")
}
p := make([]byte, frameLimit)
n, err := h.conn.Read(p)
if err != nil {
return nil, nil, err
}
// Prepare a server start message to send to the S2A handshaker service.
req := &s2apb.SessionReq{
ReqOneof: &s2apb.SessionReq_ServerStart{
ServerStart: &s2apb.ServerSessionStartReq{
ApplicationProtocols: []string{appProtocol},
MinTlsVersion: h.serverOpts.MinTLSVersion,
MaxTlsVersion: h.serverOpts.MaxTLSVersion,
TlsCiphersuites: h.serverOpts.TLSCiphersuites,
LocalIdentities: h.serverOpts.LocalIdentities,
InBytes: p[:n],
},
},
AuthMechanisms: h.getAuthMechanisms(),
}
conn, result, err := h.setUpSession(req)
if err != nil {
return nil, nil, err
}
authInfo, err := authinfo.NewS2AAuthInfo(result)
if err != nil {
return nil, nil, err
}
return conn, authInfo, nil
}
// setUpSession proxies messages between the peer and the S2A handshaker
// service.
func (h *s2aHandshaker) setUpSession(req *s2apb.SessionReq) (net.Conn, *s2apb.SessionResult, error) {
resp, err := h.accessHandshakerService(req)
if err != nil {
return nil, nil, err
}
// Check if the returned status is an error.
if resp.GetStatus() != nil {
if got, want := resp.GetStatus().Code, uint32(codes.OK); got != want {
return nil, nil, fmt.Errorf("%v", resp.GetStatus().Details)
}
}
// Calculate the extra unread bytes from the Session. Attempting to consume
// more than the bytes sent will throw an error.
var extra []byte
if req.GetServerStart() != nil {
if resp.GetBytesConsumed() > uint32(len(req.GetServerStart().GetInBytes())) {
return nil, nil, errors.New("handshaker service consumed bytes value is out-of-bounds")
}
extra = req.GetServerStart().GetInBytes()[resp.GetBytesConsumed():]
}
result, extra, err := h.processUntilDone(resp, extra)
if err != nil {
return nil, nil, err
}
if result.GetLocalIdentity() == nil {
return nil, nil, errors.New("local identity must be populated in session result")
}
// Create a new TLS record protocol using the Session Result.
newConn, err := record.NewConn(&record.ConnParameters{
NetConn: h.conn,
Ciphersuite: result.GetState().GetTlsCiphersuite(),
TLSVersion: result.GetState().GetTlsVersion(),
InTrafficSecret: result.GetState().GetInKey(),
OutTrafficSecret: result.GetState().GetOutKey(),
UnusedBuf: extra,
InSequence: result.GetState().GetInSequence(),
OutSequence: result.GetState().GetOutSequence(),
HSAddr: h.hsAddr,
ConnectionID: result.GetState().GetConnectionId(),
LocalIdentity: result.GetLocalIdentity(),
EnsureProcessSessionTickets: h.ensureProcessSessionTickets(),
})
if err != nil {
return nil, nil, err
}
return newConn, result, nil
}
func (h *s2aHandshaker) ensureProcessSessionTickets() *sync.WaitGroup {
if h.clientOpts == nil {
return nil
}
return h.clientOpts.EnsureProcessSessionTickets
}
// accessHandshakerService sends the session request to the S2A handshaker
// service and returns the session response.
func (h *s2aHandshaker) accessHandshakerService(req *s2apb.SessionReq) (*s2apb.SessionResp, error) {
if err := h.stream.Send(req); err != nil {
return nil, err
}
resp, err := h.stream.Recv()
if err != nil {
return nil, err
}
return resp, nil
}
// processUntilDone continues proxying messages between the peer and the S2A
// handshaker service until the handshaker service returns the SessionResult at
// the end of the handshake or an error occurs.
func (h *s2aHandshaker) processUntilDone(resp *s2apb.SessionResp, unusedBytes []byte) (*s2apb.SessionResult, []byte, error) {
for {
if len(resp.OutFrames) > 0 {
if _, err := h.conn.Write(resp.OutFrames); err != nil {
return nil, nil, err
}
}
if resp.Result != nil {
return resp.Result, unusedBytes, nil
}
buf := make([]byte, frameLimit)
n, err := h.conn.Read(buf)
if err != nil && err != io.EOF {
return nil, nil, err
}
// If there is nothing to send to the handshaker service and nothing is
// received from the peer, then we are stuck. This covers the case when
// the peer is not responding. Note that handshaker service connection
// issues are caught in accessHandshakerService before we even get
// here.
if len(resp.OutFrames) == 0 && n == 0 {
return nil, nil, errPeerNotResponding
}
// Append extra bytes from the previous interaction with the handshaker
// service with the current buffer read from conn.
p := append(unusedBytes, buf[:n]...)
// From here on, p and unusedBytes point to the same slice.
resp, err = h.accessHandshakerService(&s2apb.SessionReq{
ReqOneof: &s2apb.SessionReq_Next{
Next: &s2apb.SessionNextReq{
InBytes: p,
},
},
AuthMechanisms: h.getAuthMechanisms(),
})
if err != nil {
return nil, nil, err
}
// Cache the local identity returned by S2A, if it is populated. This
// overwrites any existing local identities. This is done because, once the
// S2A has selected a local identity, then only that local identity should
// be asserted in future requests until the end of the current handshake.
if resp.GetLocalIdentity() != nil {
h.localIdentities = []*commonpb.Identity{resp.GetLocalIdentity()}
}
// Set unusedBytes based on the handshaker service response.
if resp.GetBytesConsumed() > uint32(len(p)) {
return nil, nil, errors.New("handshaker service consumed bytes value is out-of-bounds")
}
unusedBytes = p[resp.GetBytesConsumed():]
}
}
// Close shuts down the handshaker and the stream to the S2A handshaker service
// when the handshake is complete. It should be called when the caller obtains
// the secure connection at the end of the handshake.
func (h *s2aHandshaker) Close() error {
return h.stream.CloseSend()
}
func (h *s2aHandshaker) getAuthMechanisms() []*s2apb.AuthenticationMechanism {
if h.tokenManager == nil {
return nil
}
// First handle the special case when no local identities have been provided
// by the application. In this case, an AuthenticationMechanism with no local
// identity will be sent.
if len(h.localIdentities) == 0 {
token, err := h.tokenManager.DefaultToken()
if err != nil {
grpclog.Infof("unable to get token for empty local identity: %v", err)
return nil
}
return []*s2apb.AuthenticationMechanism{
{
MechanismOneof: &s2apb.AuthenticationMechanism_Token{
Token: token,
},
},
}
}
// Next, handle the case where the application (or the S2A) has provided
// one or more local identities.
var authMechanisms []*s2apb.AuthenticationMechanism
for _, localIdentity := range h.localIdentities {
token, err := h.tokenManager.Token(localIdentity)
if err != nil {
grpclog.Infof("unable to get token for local identity %v: %v", localIdentity, err)
continue
}
authMechanism := &s2apb.AuthenticationMechanism{
Identity: localIdentity,
MechanismOneof: &s2apb.AuthenticationMechanism_Token{
Token: token,
},
}
authMechanisms = append(authMechanisms, authMechanism)
}
return authMechanisms
}

View File

@@ -0,0 +1,66 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package service is a utility for calling the S2A handshaker service.
package service
import (
"context"
"sync"
grpc "google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
)
var (
// mu guards hsConnMap and hsDialer.
mu sync.Mutex
// hsConnMap represents a mapping from an S2A handshaker service address
// to a corresponding connection to an S2A handshaker service instance.
hsConnMap = make(map[string]*grpc.ClientConn)
// hsDialer will be reassigned in tests.
hsDialer = grpc.DialContext
)
// Dial dials the S2A handshaker service. If a connection has already been
// established, this function returns it. Otherwise, a new connection is
// created.
func Dial(ctx context.Context, handshakerServiceAddress string, transportCreds credentials.TransportCredentials) (*grpc.ClientConn, error) {
mu.Lock()
defer mu.Unlock()
hsConn, ok := hsConnMap[handshakerServiceAddress]
if !ok {
// Create a new connection to the S2A handshaker service. Note that
// this connection stays open until the application is closed.
var grpcOpts []grpc.DialOption
if transportCreds != nil {
grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(transportCreds))
} else {
grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
}
var err error
hsConn, err = hsDialer(ctx, handshakerServiceAddress, grpcOpts...)
if err != nil {
return nil, err
}
hsConnMap[handshakerServiceAddress] = hsConn
}
return hsConn, nil
}

View File

@@ -0,0 +1,389 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// source: internal/proto/common/common.proto
package common_go_proto
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// The ciphersuites supported by S2A. The name determines the confidentiality,
// and authentication ciphers as well as the hash algorithm used for PRF in
// TLS 1.2 or HKDF in TLS 1.3. Thus, the components of the name are:
// - AEAD -- for encryption and authentication, e.g., AES_128_GCM.
// - Hash algorithm -- used in PRF or HKDF, e.g., SHA256.
type Ciphersuite int32
const (
Ciphersuite_AES_128_GCM_SHA256 Ciphersuite = 0
Ciphersuite_AES_256_GCM_SHA384 Ciphersuite = 1
Ciphersuite_CHACHA20_POLY1305_SHA256 Ciphersuite = 2
)
// Enum value maps for Ciphersuite.
var (
Ciphersuite_name = map[int32]string{
0: "AES_128_GCM_SHA256",
1: "AES_256_GCM_SHA384",
2: "CHACHA20_POLY1305_SHA256",
}
Ciphersuite_value = map[string]int32{
"AES_128_GCM_SHA256": 0,
"AES_256_GCM_SHA384": 1,
"CHACHA20_POLY1305_SHA256": 2,
}
)
func (x Ciphersuite) Enum() *Ciphersuite {
p := new(Ciphersuite)
*p = x
return p
}
func (x Ciphersuite) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Ciphersuite) Descriptor() protoreflect.EnumDescriptor {
return file_internal_proto_common_common_proto_enumTypes[0].Descriptor()
}
func (Ciphersuite) Type() protoreflect.EnumType {
return &file_internal_proto_common_common_proto_enumTypes[0]
}
func (x Ciphersuite) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Ciphersuite.Descriptor instead.
func (Ciphersuite) EnumDescriptor() ([]byte, []int) {
return file_internal_proto_common_common_proto_rawDescGZIP(), []int{0}
}
// The TLS versions supported by S2A's handshaker module.
type TLSVersion int32
const (
TLSVersion_TLS1_2 TLSVersion = 0
TLSVersion_TLS1_3 TLSVersion = 1
)
// Enum value maps for TLSVersion.
var (
TLSVersion_name = map[int32]string{
0: "TLS1_2",
1: "TLS1_3",
}
TLSVersion_value = map[string]int32{
"TLS1_2": 0,
"TLS1_3": 1,
}
)
func (x TLSVersion) Enum() *TLSVersion {
p := new(TLSVersion)
*p = x
return p
}
func (x TLSVersion) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (TLSVersion) Descriptor() protoreflect.EnumDescriptor {
return file_internal_proto_common_common_proto_enumTypes[1].Descriptor()
}
func (TLSVersion) Type() protoreflect.EnumType {
return &file_internal_proto_common_common_proto_enumTypes[1]
}
func (x TLSVersion) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use TLSVersion.Descriptor instead.
func (TLSVersion) EnumDescriptor() ([]byte, []int) {
return file_internal_proto_common_common_proto_rawDescGZIP(), []int{1}
}
type Identity struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Types that are assignable to IdentityOneof:
//
// *Identity_SpiffeId
// *Identity_Hostname
// *Identity_Uid
// *Identity_MdbUsername
// *Identity_GaiaId
IdentityOneof isIdentity_IdentityOneof `protobuf_oneof:"identity_oneof"`
// Additional identity-specific attributes.
Attributes map[string]string `protobuf:"bytes,3,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
func (x *Identity) Reset() {
*x = Identity{}
if protoimpl.UnsafeEnabled {
mi := &file_internal_proto_common_common_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Identity) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Identity) ProtoMessage() {}
func (x *Identity) ProtoReflect() protoreflect.Message {
mi := &file_internal_proto_common_common_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Identity.ProtoReflect.Descriptor instead.
func (*Identity) Descriptor() ([]byte, []int) {
return file_internal_proto_common_common_proto_rawDescGZIP(), []int{0}
}
func (m *Identity) GetIdentityOneof() isIdentity_IdentityOneof {
if m != nil {
return m.IdentityOneof
}
return nil
}
func (x *Identity) GetSpiffeId() string {
if x, ok := x.GetIdentityOneof().(*Identity_SpiffeId); ok {
return x.SpiffeId
}
return ""
}
func (x *Identity) GetHostname() string {
if x, ok := x.GetIdentityOneof().(*Identity_Hostname); ok {
return x.Hostname
}
return ""
}
func (x *Identity) GetUid() string {
if x, ok := x.GetIdentityOneof().(*Identity_Uid); ok {
return x.Uid
}
return ""
}
func (x *Identity) GetMdbUsername() string {
if x, ok := x.GetIdentityOneof().(*Identity_MdbUsername); ok {
return x.MdbUsername
}
return ""
}
func (x *Identity) GetGaiaId() string {
if x, ok := x.GetIdentityOneof().(*Identity_GaiaId); ok {
return x.GaiaId
}
return ""
}
func (x *Identity) GetAttributes() map[string]string {
if x != nil {
return x.Attributes
}
return nil
}
type isIdentity_IdentityOneof interface {
isIdentity_IdentityOneof()
}
type Identity_SpiffeId struct {
// The SPIFFE ID of a connection endpoint.
SpiffeId string `protobuf:"bytes,1,opt,name=spiffe_id,json=spiffeId,proto3,oneof"`
}
type Identity_Hostname struct {
// The hostname of a connection endpoint.
Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3,oneof"`
}
type Identity_Uid struct {
// The UID of a connection endpoint.
Uid string `protobuf:"bytes,4,opt,name=uid,proto3,oneof"`
}
type Identity_MdbUsername struct {
// The MDB username of a connection endpoint.
MdbUsername string `protobuf:"bytes,5,opt,name=mdb_username,json=mdbUsername,proto3,oneof"`
}
type Identity_GaiaId struct {
// The Gaia ID of a connection endpoint.
GaiaId string `protobuf:"bytes,6,opt,name=gaia_id,json=gaiaId,proto3,oneof"`
}
func (*Identity_SpiffeId) isIdentity_IdentityOneof() {}
func (*Identity_Hostname) isIdentity_IdentityOneof() {}
func (*Identity_Uid) isIdentity_IdentityOneof() {}
func (*Identity_MdbUsername) isIdentity_IdentityOneof() {}
func (*Identity_GaiaId) isIdentity_IdentityOneof() {}
var File_internal_proto_common_common_proto protoreflect.FileDescriptor
var file_internal_proto_common_common_proto_rawDesc = []byte{
0x0a, 0x22, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x73, 0x32, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
0xb1, 0x02, 0x0a, 0x08, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x1d, 0x0a, 0x09,
0x73, 0x70, 0x69, 0x66, 0x66, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48,
0x00, 0x52, 0x08, 0x73, 0x70, 0x69, 0x66, 0x66, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x08, 0x68,
0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52,
0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x75, 0x69, 0x64,
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x75, 0x69, 0x64, 0x12, 0x23, 0x0a,
0x0c, 0x6d, 0x64, 0x62, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x6d, 0x64, 0x62, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61,
0x6d, 0x65, 0x12, 0x19, 0x0a, 0x07, 0x67, 0x61, 0x69, 0x61, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20,
0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, 0x67, 0x61, 0x69, 0x61, 0x49, 0x64, 0x12, 0x43, 0x0a,
0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x23, 0x2e, 0x73, 0x32, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74,
0x65, 0x73, 0x1a, 0x3d, 0x0a, 0x0f, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73,
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
0x01, 0x42, 0x10, 0x0a, 0x0e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6f, 0x6e,
0x65, 0x6f, 0x66, 0x2a, 0x5b, 0x0a, 0x0b, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x73, 0x75, 0x69,
0x74, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x45, 0x53, 0x5f, 0x31, 0x32, 0x38, 0x5f, 0x47, 0x43,
0x4d, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x45,
0x53, 0x5f, 0x32, 0x35, 0x36, 0x5f, 0x47, 0x43, 0x4d, 0x5f, 0x53, 0x48, 0x41, 0x33, 0x38, 0x34,
0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x48, 0x41, 0x43, 0x48, 0x41, 0x32, 0x30, 0x5f, 0x50,
0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x02,
0x2a, 0x24, 0x0a, 0x0a, 0x54, 0x4c, 0x53, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0a,
0x0a, 0x06, 0x54, 0x4c, 0x53, 0x31, 0x5f, 0x32, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x4c,
0x53, 0x31, 0x5f, 0x33, 0x10, 0x01, 0x42, 0x36, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x73, 0x32, 0x61, 0x2f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x5f, 0x67, 0x6f, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_internal_proto_common_common_proto_rawDescOnce sync.Once
file_internal_proto_common_common_proto_rawDescData = file_internal_proto_common_common_proto_rawDesc
)
func file_internal_proto_common_common_proto_rawDescGZIP() []byte {
file_internal_proto_common_common_proto_rawDescOnce.Do(func() {
file_internal_proto_common_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_proto_common_common_proto_rawDescData)
})
return file_internal_proto_common_common_proto_rawDescData
}
var file_internal_proto_common_common_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_internal_proto_common_common_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_internal_proto_common_common_proto_goTypes = []interface{}{
(Ciphersuite)(0), // 0: s2a.proto.Ciphersuite
(TLSVersion)(0), // 1: s2a.proto.TLSVersion
(*Identity)(nil), // 2: s2a.proto.Identity
nil, // 3: s2a.proto.Identity.AttributesEntry
}
var file_internal_proto_common_common_proto_depIdxs = []int32{
3, // 0: s2a.proto.Identity.attributes:type_name -> s2a.proto.Identity.AttributesEntry
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_internal_proto_common_common_proto_init() }
func file_internal_proto_common_common_proto_init() {
if File_internal_proto_common_common_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_internal_proto_common_common_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Identity); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_internal_proto_common_common_proto_msgTypes[0].OneofWrappers = []interface{}{
(*Identity_SpiffeId)(nil),
(*Identity_Hostname)(nil),
(*Identity_Uid)(nil),
(*Identity_MdbUsername)(nil),
(*Identity_GaiaId)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_internal_proto_common_common_proto_rawDesc,
NumEnums: 2,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_internal_proto_common_common_proto_goTypes,
DependencyIndexes: file_internal_proto_common_common_proto_depIdxs,
EnumInfos: file_internal_proto_common_common_proto_enumTypes,
MessageInfos: file_internal_proto_common_common_proto_msgTypes,
}.Build()
File_internal_proto_common_common_proto = out.File
file_internal_proto_common_common_proto_rawDesc = nil
file_internal_proto_common_common_proto_goTypes = nil
file_internal_proto_common_common_proto_depIdxs = nil
}

View File

@@ -0,0 +1,267 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// source: internal/proto/s2a_context/s2a_context.proto
package s2a_context_go_proto
import (
common_go_proto "github.com/google/s2a-go/internal/proto/common_go_proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type S2AContext struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// The application protocol negotiated for this connection, e.g., 'grpc'.
ApplicationProtocol string `protobuf:"bytes,1,opt,name=application_protocol,json=applicationProtocol,proto3" json:"application_protocol,omitempty"`
// The TLS version number that the S2A's handshaker module used to set up the
// session.
TlsVersion common_go_proto.TLSVersion `protobuf:"varint,2,opt,name=tls_version,json=tlsVersion,proto3,enum=s2a.proto.TLSVersion" json:"tls_version,omitempty"`
// The TLS ciphersuite negotiated by the S2A's handshaker module.
Ciphersuite common_go_proto.Ciphersuite `protobuf:"varint,3,opt,name=ciphersuite,proto3,enum=s2a.proto.Ciphersuite" json:"ciphersuite,omitempty"`
// The authenticated identity of the peer.
PeerIdentity *common_go_proto.Identity `protobuf:"bytes,4,opt,name=peer_identity,json=peerIdentity,proto3" json:"peer_identity,omitempty"`
// The local identity used during session setup. This could be:
// - The local identity that the client specifies in ClientSessionStartReq.
// - One of the local identities that the server specifies in
// ServerSessionStartReq.
// - If neither client or server specifies local identities, the S2A picks the
// default one. In this case, this field will contain that identity.
LocalIdentity *common_go_proto.Identity `protobuf:"bytes,5,opt,name=local_identity,json=localIdentity,proto3" json:"local_identity,omitempty"`
// The SHA256 hash of the peer certificate used in the handshake.
PeerCertFingerprint []byte `protobuf:"bytes,6,opt,name=peer_cert_fingerprint,json=peerCertFingerprint,proto3" json:"peer_cert_fingerprint,omitempty"`
// The SHA256 hash of the local certificate used in the handshake.
LocalCertFingerprint []byte `protobuf:"bytes,7,opt,name=local_cert_fingerprint,json=localCertFingerprint,proto3" json:"local_cert_fingerprint,omitempty"`
// Set to true if a cached session was reused to resume the handshake.
IsHandshakeResumed bool `protobuf:"varint,8,opt,name=is_handshake_resumed,json=isHandshakeResumed,proto3" json:"is_handshake_resumed,omitempty"`
}
func (x *S2AContext) Reset() {
*x = S2AContext{}
if protoimpl.UnsafeEnabled {
mi := &file_internal_proto_s2a_context_s2a_context_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *S2AContext) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*S2AContext) ProtoMessage() {}
func (x *S2AContext) ProtoReflect() protoreflect.Message {
mi := &file_internal_proto_s2a_context_s2a_context_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use S2AContext.ProtoReflect.Descriptor instead.
func (*S2AContext) Descriptor() ([]byte, []int) {
return file_internal_proto_s2a_context_s2a_context_proto_rawDescGZIP(), []int{0}
}
func (x *S2AContext) GetApplicationProtocol() string {
if x != nil {
return x.ApplicationProtocol
}
return ""
}
func (x *S2AContext) GetTlsVersion() common_go_proto.TLSVersion {
if x != nil {
return x.TlsVersion
}
return common_go_proto.TLSVersion(0)
}
func (x *S2AContext) GetCiphersuite() common_go_proto.Ciphersuite {
if x != nil {
return x.Ciphersuite
}
return common_go_proto.Ciphersuite(0)
}
func (x *S2AContext) GetPeerIdentity() *common_go_proto.Identity {
if x != nil {
return x.PeerIdentity
}
return nil
}
func (x *S2AContext) GetLocalIdentity() *common_go_proto.Identity {
if x != nil {
return x.LocalIdentity
}
return nil
}
func (x *S2AContext) GetPeerCertFingerprint() []byte {
if x != nil {
return x.PeerCertFingerprint
}
return nil
}
func (x *S2AContext) GetLocalCertFingerprint() []byte {
if x != nil {
return x.LocalCertFingerprint
}
return nil
}
func (x *S2AContext) GetIsHandshakeResumed() bool {
if x != nil {
return x.IsHandshakeResumed
}
return false
}
var File_internal_proto_s2a_context_s2a_context_proto protoreflect.FileDescriptor
var file_internal_proto_s2a_context_s2a_context_proto_rawDesc = []byte{
0x0a, 0x2c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2f, 0x73, 0x32, 0x61, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x73, 0x32, 0x61,
0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09,
0x73, 0x32, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x69, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc3, 0x03,
0x0a, 0x0a, 0x53, 0x32, 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x31, 0x0a, 0x14,
0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x61, 0x70, 0x70, 0x6c,
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12,
0x36, 0x0a, 0x0b, 0x74, 0x6c, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e, 0x73, 0x32, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2e, 0x54, 0x4c, 0x53, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x74, 0x6c, 0x73,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x0b, 0x63, 0x69, 0x70, 0x68, 0x65,
0x72, 0x73, 0x75, 0x69, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x73,
0x32, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x73,
0x75, 0x69, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x73, 0x75, 0x69, 0x74,
0x65, 0x12, 0x38, 0x0a, 0x0d, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69,
0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x32, 0x61, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0c, 0x70,
0x65, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x3a, 0x0a, 0x0e, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x32, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e,
0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49,
0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x65, 0x65, 0x72, 0x5f,
0x63, 0x65, 0x72, 0x74, 0x5f, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74,
0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x70, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74,
0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x34, 0x0a, 0x16, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72,
0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x6c, 0x6f, 0x63,
0x61, 0x6c, 0x43, 0x65, 0x72, 0x74, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e,
0x74, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b,
0x65, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52,
0x12, 0x69, 0x73, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x52, 0x65, 0x73, 0x75,
0x6d, 0x65, 0x64, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x73, 0x32, 0x61, 0x2f, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x32, 0x61, 0x5f,
0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x67, 0x6f, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_internal_proto_s2a_context_s2a_context_proto_rawDescOnce sync.Once
file_internal_proto_s2a_context_s2a_context_proto_rawDescData = file_internal_proto_s2a_context_s2a_context_proto_rawDesc
)
func file_internal_proto_s2a_context_s2a_context_proto_rawDescGZIP() []byte {
file_internal_proto_s2a_context_s2a_context_proto_rawDescOnce.Do(func() {
file_internal_proto_s2a_context_s2a_context_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_proto_s2a_context_s2a_context_proto_rawDescData)
})
return file_internal_proto_s2a_context_s2a_context_proto_rawDescData
}
var file_internal_proto_s2a_context_s2a_context_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_internal_proto_s2a_context_s2a_context_proto_goTypes = []interface{}{
(*S2AContext)(nil), // 0: s2a.proto.S2AContext
(common_go_proto.TLSVersion)(0), // 1: s2a.proto.TLSVersion
(common_go_proto.Ciphersuite)(0), // 2: s2a.proto.Ciphersuite
(*common_go_proto.Identity)(nil), // 3: s2a.proto.Identity
}
var file_internal_proto_s2a_context_s2a_context_proto_depIdxs = []int32{
1, // 0: s2a.proto.S2AContext.tls_version:type_name -> s2a.proto.TLSVersion
2, // 1: s2a.proto.S2AContext.ciphersuite:type_name -> s2a.proto.Ciphersuite
3, // 2: s2a.proto.S2AContext.peer_identity:type_name -> s2a.proto.Identity
3, // 3: s2a.proto.S2AContext.local_identity:type_name -> s2a.proto.Identity
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_internal_proto_s2a_context_s2a_context_proto_init() }
func file_internal_proto_s2a_context_s2a_context_proto_init() {
if File_internal_proto_s2a_context_s2a_context_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_internal_proto_s2a_context_s2a_context_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*S2AContext); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_internal_proto_s2a_context_s2a_context_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_internal_proto_s2a_context_s2a_context_proto_goTypes,
DependencyIndexes: file_internal_proto_s2a_context_s2a_context_proto_depIdxs,
MessageInfos: file_internal_proto_s2a_context_s2a_context_proto_msgTypes,
}.Build()
File_internal_proto_s2a_context_s2a_context_proto = out.File
file_internal_proto_s2a_context_s2a_context_proto_rawDesc = nil
file_internal_proto_s2a_context_s2a_context_proto_goTypes = nil
file_internal_proto_s2a_context_s2a_context_proto_depIdxs = nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,173 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v3.21.12
// source: internal/proto/s2a/s2a.proto
package s2a_go_proto
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
const (
S2AService_SetUpSession_FullMethodName = "/s2a.proto.S2AService/SetUpSession"
)
// S2AServiceClient is the client API for S2AService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type S2AServiceClient interface {
// S2A service accepts a stream of session setup requests and returns a stream
// of session setup responses. The client of this service is expected to send
// exactly one client_start or server_start message followed by at least one
// next message. Applications running TLS clients can send requests with
// resumption_ticket messages only after the session is successfully set up.
//
// Every time S2A client sends a request, this service sends a response.
// However, clients do not have to wait for service response before sending
// the next request.
SetUpSession(ctx context.Context, opts ...grpc.CallOption) (S2AService_SetUpSessionClient, error)
}
type s2AServiceClient struct {
cc grpc.ClientConnInterface
}
func NewS2AServiceClient(cc grpc.ClientConnInterface) S2AServiceClient {
return &s2AServiceClient{cc}
}
func (c *s2AServiceClient) SetUpSession(ctx context.Context, opts ...grpc.CallOption) (S2AService_SetUpSessionClient, error) {
stream, err := c.cc.NewStream(ctx, &S2AService_ServiceDesc.Streams[0], S2AService_SetUpSession_FullMethodName, opts...)
if err != nil {
return nil, err
}
x := &s2AServiceSetUpSessionClient{stream}
return x, nil
}
type S2AService_SetUpSessionClient interface {
Send(*SessionReq) error
Recv() (*SessionResp, error)
grpc.ClientStream
}
type s2AServiceSetUpSessionClient struct {
grpc.ClientStream
}
func (x *s2AServiceSetUpSessionClient) Send(m *SessionReq) error {
return x.ClientStream.SendMsg(m)
}
func (x *s2AServiceSetUpSessionClient) Recv() (*SessionResp, error) {
m := new(SessionResp)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// S2AServiceServer is the server API for S2AService service.
// All implementations must embed UnimplementedS2AServiceServer
// for forward compatibility
type S2AServiceServer interface {
// S2A service accepts a stream of session setup requests and returns a stream
// of session setup responses. The client of this service is expected to send
// exactly one client_start or server_start message followed by at least one
// next message. Applications running TLS clients can send requests with
// resumption_ticket messages only after the session is successfully set up.
//
// Every time S2A client sends a request, this service sends a response.
// However, clients do not have to wait for service response before sending
// the next request.
SetUpSession(S2AService_SetUpSessionServer) error
mustEmbedUnimplementedS2AServiceServer()
}
// UnimplementedS2AServiceServer must be embedded to have forward compatible implementations.
type UnimplementedS2AServiceServer struct {
}
func (UnimplementedS2AServiceServer) SetUpSession(S2AService_SetUpSessionServer) error {
return status.Errorf(codes.Unimplemented, "method SetUpSession not implemented")
}
func (UnimplementedS2AServiceServer) mustEmbedUnimplementedS2AServiceServer() {}
// UnsafeS2AServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to S2AServiceServer will
// result in compilation errors.
type UnsafeS2AServiceServer interface {
mustEmbedUnimplementedS2AServiceServer()
}
func RegisterS2AServiceServer(s grpc.ServiceRegistrar, srv S2AServiceServer) {
s.RegisterService(&S2AService_ServiceDesc, srv)
}
func _S2AService_SetUpSession_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(S2AServiceServer).SetUpSession(&s2AServiceSetUpSessionServer{stream})
}
type S2AService_SetUpSessionServer interface {
Send(*SessionResp) error
Recv() (*SessionReq, error)
grpc.ServerStream
}
type s2AServiceSetUpSessionServer struct {
grpc.ServerStream
}
func (x *s2AServiceSetUpSessionServer) Send(m *SessionResp) error {
return x.ServerStream.SendMsg(m)
}
func (x *s2AServiceSetUpSessionServer) Recv() (*SessionReq, error) {
m := new(SessionReq)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// S2AService_ServiceDesc is the grpc.ServiceDesc for S2AService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var S2AService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "s2a.proto.S2AService",
HandlerType: (*S2AServiceServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "SetUpSession",
Handler: _S2AService_SetUpSession_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "internal/proto/s2a/s2a.proto",
}

View File

@@ -0,0 +1,367 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// source: internal/proto/v2/common/common.proto
package common_go_proto
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// The TLS 1.0-1.2 ciphersuites that the application can negotiate when using
// S2A.
type Ciphersuite int32
const (
Ciphersuite_CIPHERSUITE_UNSPECIFIED Ciphersuite = 0
Ciphersuite_CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 Ciphersuite = 1
Ciphersuite_CIPHERSUITE_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 Ciphersuite = 2
Ciphersuite_CIPHERSUITE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 Ciphersuite = 3
Ciphersuite_CIPHERSUITE_ECDHE_RSA_WITH_AES_128_GCM_SHA256 Ciphersuite = 4
Ciphersuite_CIPHERSUITE_ECDHE_RSA_WITH_AES_256_GCM_SHA384 Ciphersuite = 5
Ciphersuite_CIPHERSUITE_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 Ciphersuite = 6
)
// Enum value maps for Ciphersuite.
var (
Ciphersuite_name = map[int32]string{
0: "CIPHERSUITE_UNSPECIFIED",
1: "CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
2: "CIPHERSUITE_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
3: "CIPHERSUITE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
4: "CIPHERSUITE_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
5: "CIPHERSUITE_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
6: "CIPHERSUITE_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
}
Ciphersuite_value = map[string]int32{
"CIPHERSUITE_UNSPECIFIED": 0,
"CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": 1,
"CIPHERSUITE_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": 2,
"CIPHERSUITE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": 3,
"CIPHERSUITE_ECDHE_RSA_WITH_AES_128_GCM_SHA256": 4,
"CIPHERSUITE_ECDHE_RSA_WITH_AES_256_GCM_SHA384": 5,
"CIPHERSUITE_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": 6,
}
)
func (x Ciphersuite) Enum() *Ciphersuite {
p := new(Ciphersuite)
*p = x
return p
}
func (x Ciphersuite) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Ciphersuite) Descriptor() protoreflect.EnumDescriptor {
return file_internal_proto_v2_common_common_proto_enumTypes[0].Descriptor()
}
func (Ciphersuite) Type() protoreflect.EnumType {
return &file_internal_proto_v2_common_common_proto_enumTypes[0]
}
func (x Ciphersuite) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Ciphersuite.Descriptor instead.
func (Ciphersuite) EnumDescriptor() ([]byte, []int) {
return file_internal_proto_v2_common_common_proto_rawDescGZIP(), []int{0}
}
// The TLS versions supported by S2A's handshaker module.
type TLSVersion int32
const (
TLSVersion_TLS_VERSION_UNSPECIFIED TLSVersion = 0
TLSVersion_TLS_VERSION_1_0 TLSVersion = 1
TLSVersion_TLS_VERSION_1_1 TLSVersion = 2
TLSVersion_TLS_VERSION_1_2 TLSVersion = 3
TLSVersion_TLS_VERSION_1_3 TLSVersion = 4
)
// Enum value maps for TLSVersion.
var (
TLSVersion_name = map[int32]string{
0: "TLS_VERSION_UNSPECIFIED",
1: "TLS_VERSION_1_0",
2: "TLS_VERSION_1_1",
3: "TLS_VERSION_1_2",
4: "TLS_VERSION_1_3",
}
TLSVersion_value = map[string]int32{
"TLS_VERSION_UNSPECIFIED": 0,
"TLS_VERSION_1_0": 1,
"TLS_VERSION_1_1": 2,
"TLS_VERSION_1_2": 3,
"TLS_VERSION_1_3": 4,
}
)
func (x TLSVersion) Enum() *TLSVersion {
p := new(TLSVersion)
*p = x
return p
}
func (x TLSVersion) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (TLSVersion) Descriptor() protoreflect.EnumDescriptor {
return file_internal_proto_v2_common_common_proto_enumTypes[1].Descriptor()
}
func (TLSVersion) Type() protoreflect.EnumType {
return &file_internal_proto_v2_common_common_proto_enumTypes[1]
}
func (x TLSVersion) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use TLSVersion.Descriptor instead.
func (TLSVersion) EnumDescriptor() ([]byte, []int) {
return file_internal_proto_v2_common_common_proto_rawDescGZIP(), []int{1}
}
// The side in the TLS connection.
type ConnectionSide int32
const (
ConnectionSide_CONNECTION_SIDE_UNSPECIFIED ConnectionSide = 0
ConnectionSide_CONNECTION_SIDE_CLIENT ConnectionSide = 1
ConnectionSide_CONNECTION_SIDE_SERVER ConnectionSide = 2
)
// Enum value maps for ConnectionSide.
var (
ConnectionSide_name = map[int32]string{
0: "CONNECTION_SIDE_UNSPECIFIED",
1: "CONNECTION_SIDE_CLIENT",
2: "CONNECTION_SIDE_SERVER",
}
ConnectionSide_value = map[string]int32{
"CONNECTION_SIDE_UNSPECIFIED": 0,
"CONNECTION_SIDE_CLIENT": 1,
"CONNECTION_SIDE_SERVER": 2,
}
)
func (x ConnectionSide) Enum() *ConnectionSide {
p := new(ConnectionSide)
*p = x
return p
}
func (x ConnectionSide) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (ConnectionSide) Descriptor() protoreflect.EnumDescriptor {
return file_internal_proto_v2_common_common_proto_enumTypes[2].Descriptor()
}
func (ConnectionSide) Type() protoreflect.EnumType {
return &file_internal_proto_v2_common_common_proto_enumTypes[2]
}
func (x ConnectionSide) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use ConnectionSide.Descriptor instead.
func (ConnectionSide) EnumDescriptor() ([]byte, []int) {
return file_internal_proto_v2_common_common_proto_rawDescGZIP(), []int{2}
}
// The ALPN protocols that the application can negotiate during a TLS handshake.
type AlpnProtocol int32
const (
AlpnProtocol_ALPN_PROTOCOL_UNSPECIFIED AlpnProtocol = 0
AlpnProtocol_ALPN_PROTOCOL_GRPC AlpnProtocol = 1
AlpnProtocol_ALPN_PROTOCOL_HTTP2 AlpnProtocol = 2
AlpnProtocol_ALPN_PROTOCOL_HTTP1_1 AlpnProtocol = 3
)
// Enum value maps for AlpnProtocol.
var (
AlpnProtocol_name = map[int32]string{
0: "ALPN_PROTOCOL_UNSPECIFIED",
1: "ALPN_PROTOCOL_GRPC",
2: "ALPN_PROTOCOL_HTTP2",
3: "ALPN_PROTOCOL_HTTP1_1",
}
AlpnProtocol_value = map[string]int32{
"ALPN_PROTOCOL_UNSPECIFIED": 0,
"ALPN_PROTOCOL_GRPC": 1,
"ALPN_PROTOCOL_HTTP2": 2,
"ALPN_PROTOCOL_HTTP1_1": 3,
}
)
func (x AlpnProtocol) Enum() *AlpnProtocol {
p := new(AlpnProtocol)
*p = x
return p
}
func (x AlpnProtocol) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (AlpnProtocol) Descriptor() protoreflect.EnumDescriptor {
return file_internal_proto_v2_common_common_proto_enumTypes[3].Descriptor()
}
func (AlpnProtocol) Type() protoreflect.EnumType {
return &file_internal_proto_v2_common_common_proto_enumTypes[3]
}
func (x AlpnProtocol) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use AlpnProtocol.Descriptor instead.
func (AlpnProtocol) EnumDescriptor() ([]byte, []int) {
return file_internal_proto_v2_common_common_proto_rawDescGZIP(), []int{3}
}
var File_internal_proto_v2_common_common_proto protoreflect.FileDescriptor
var file_internal_proto_v2_common_common_proto_rawDesc = []byte{
0x0a, 0x25, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x73, 0x32, 0x61, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x2e, 0x76, 0x32, 0x2a, 0xee, 0x02, 0x0a, 0x0b, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72,
0x73, 0x75, 0x69, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x53,
0x55, 0x49, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44,
0x10, 0x00, 0x12, 0x33, 0x0a, 0x2f, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x53, 0x55, 0x49, 0x54,
0x45, 0x5f, 0x45, 0x43, 0x44, 0x48, 0x45, 0x5f, 0x45, 0x43, 0x44, 0x53, 0x41, 0x5f, 0x57, 0x49,
0x54, 0x48, 0x5f, 0x41, 0x45, 0x53, 0x5f, 0x31, 0x32, 0x38, 0x5f, 0x47, 0x43, 0x4d, 0x5f, 0x53,
0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x01, 0x12, 0x33, 0x0a, 0x2f, 0x43, 0x49, 0x50, 0x48, 0x45,
0x52, 0x53, 0x55, 0x49, 0x54, 0x45, 0x5f, 0x45, 0x43, 0x44, 0x48, 0x45, 0x5f, 0x45, 0x43, 0x44,
0x53, 0x41, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x5f, 0x41, 0x45, 0x53, 0x5f, 0x32, 0x35, 0x36, 0x5f,
0x47, 0x43, 0x4d, 0x5f, 0x53, 0x48, 0x41, 0x33, 0x38, 0x34, 0x10, 0x02, 0x12, 0x39, 0x0a, 0x35,
0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x53, 0x55, 0x49, 0x54, 0x45, 0x5f, 0x45, 0x43, 0x44, 0x48,
0x45, 0x5f, 0x45, 0x43, 0x44, 0x53, 0x41, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x5f, 0x43, 0x48, 0x41,
0x43, 0x48, 0x41, 0x32, 0x30, 0x5f, 0x50, 0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x5f, 0x53,
0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x03, 0x12, 0x31, 0x0a, 0x2d, 0x43, 0x49, 0x50, 0x48, 0x45,
0x52, 0x53, 0x55, 0x49, 0x54, 0x45, 0x5f, 0x45, 0x43, 0x44, 0x48, 0x45, 0x5f, 0x52, 0x53, 0x41,
0x5f, 0x57, 0x49, 0x54, 0x48, 0x5f, 0x41, 0x45, 0x53, 0x5f, 0x31, 0x32, 0x38, 0x5f, 0x47, 0x43,
0x4d, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x04, 0x12, 0x31, 0x0a, 0x2d, 0x43, 0x49,
0x50, 0x48, 0x45, 0x52, 0x53, 0x55, 0x49, 0x54, 0x45, 0x5f, 0x45, 0x43, 0x44, 0x48, 0x45, 0x5f,
0x52, 0x53, 0x41, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x5f, 0x41, 0x45, 0x53, 0x5f, 0x32, 0x35, 0x36,
0x5f, 0x47, 0x43, 0x4d, 0x5f, 0x53, 0x48, 0x41, 0x33, 0x38, 0x34, 0x10, 0x05, 0x12, 0x37, 0x0a,
0x33, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x53, 0x55, 0x49, 0x54, 0x45, 0x5f, 0x45, 0x43, 0x44,
0x48, 0x45, 0x5f, 0x52, 0x53, 0x41, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x5f, 0x43, 0x48, 0x41, 0x43,
0x48, 0x41, 0x32, 0x30, 0x5f, 0x50, 0x4f, 0x4c, 0x59, 0x31, 0x33, 0x30, 0x35, 0x5f, 0x53, 0x48,
0x41, 0x32, 0x35, 0x36, 0x10, 0x06, 0x2a, 0x7d, 0x0a, 0x0a, 0x54, 0x4c, 0x53, 0x56, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x17, 0x54, 0x4c, 0x53, 0x5f, 0x56, 0x45, 0x52, 0x53,
0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
0x00, 0x12, 0x13, 0x0a, 0x0f, 0x54, 0x4c, 0x53, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e,
0x5f, 0x31, 0x5f, 0x30, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x54, 0x4c, 0x53, 0x5f, 0x56, 0x45,
0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x31, 0x5f, 0x31, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x54,
0x4c, 0x53, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x31, 0x5f, 0x32, 0x10, 0x03,
0x12, 0x13, 0x0a, 0x0f, 0x54, 0x4c, 0x53, 0x5f, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x5f,
0x31, 0x5f, 0x33, 0x10, 0x04, 0x2a, 0x69, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x53, 0x69, 0x64, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x43, 0x4f, 0x4e, 0x4e, 0x45,
0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x49, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4e, 0x4e,
0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x49, 0x44, 0x45, 0x5f, 0x43, 0x4c, 0x49, 0x45,
0x4e, 0x54, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49,
0x4f, 0x4e, 0x5f, 0x53, 0x49, 0x44, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x10, 0x02,
0x2a, 0x79, 0x0a, 0x0c, 0x41, 0x6c, 0x70, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x12, 0x1d, 0x0a, 0x19, 0x41, 0x4c, 0x50, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f,
0x4c, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12,
0x16, 0x0a, 0x12, 0x41, 0x4c, 0x50, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c,
0x5f, 0x47, 0x52, 0x50, 0x43, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x4c, 0x50, 0x4e, 0x5f,
0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f, 0x4c, 0x5f, 0x48, 0x54, 0x54, 0x50, 0x32, 0x10, 0x02,
0x12, 0x19, 0x0a, 0x15, 0x41, 0x4c, 0x50, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x43, 0x4f,
0x4c, 0x5f, 0x48, 0x54, 0x54, 0x50, 0x31, 0x5f, 0x31, 0x10, 0x03, 0x42, 0x39, 0x5a, 0x37, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2f, 0x73, 0x32, 0x61, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x32, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x5f, 0x67, 0x6f,
0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_internal_proto_v2_common_common_proto_rawDescOnce sync.Once
file_internal_proto_v2_common_common_proto_rawDescData = file_internal_proto_v2_common_common_proto_rawDesc
)
func file_internal_proto_v2_common_common_proto_rawDescGZIP() []byte {
file_internal_proto_v2_common_common_proto_rawDescOnce.Do(func() {
file_internal_proto_v2_common_common_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_proto_v2_common_common_proto_rawDescData)
})
return file_internal_proto_v2_common_common_proto_rawDescData
}
var file_internal_proto_v2_common_common_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
var file_internal_proto_v2_common_common_proto_goTypes = []interface{}{
(Ciphersuite)(0), // 0: s2a.proto.v2.Ciphersuite
(TLSVersion)(0), // 1: s2a.proto.v2.TLSVersion
(ConnectionSide)(0), // 2: s2a.proto.v2.ConnectionSide
(AlpnProtocol)(0), // 3: s2a.proto.v2.AlpnProtocol
}
var file_internal_proto_v2_common_common_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_internal_proto_v2_common_common_proto_init() }
func file_internal_proto_v2_common_common_proto_init() {
if File_internal_proto_v2_common_common_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_internal_proto_v2_common_common_proto_rawDesc,
NumEnums: 4,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_internal_proto_v2_common_common_proto_goTypes,
DependencyIndexes: file_internal_proto_v2_common_common_proto_depIdxs,
EnumInfos: file_internal_proto_v2_common_common_proto_enumTypes,
}.Build()
File_internal_proto_v2_common_common_proto = out.File
file_internal_proto_v2_common_common_proto_rawDesc = nil
file_internal_proto_v2_common_common_proto_goTypes = nil
file_internal_proto_v2_common_common_proto_depIdxs = nil
}

View File

@@ -0,0 +1,248 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v3.21.12
// source: internal/proto/v2/s2a_context/s2a_context.proto
package s2a_context_go_proto
import (
common_go_proto "github.com/google/s2a-go/internal/proto/common_go_proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type S2AContext struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// The SPIFFE ID from the peer leaf certificate, if present.
//
// This field is only populated if the leaf certificate is a valid SPIFFE
// SVID; in particular, there is a unique URI SAN and this URI SAN is a valid
// SPIFFE ID.
LeafCertSpiffeId string `protobuf:"bytes,1,opt,name=leaf_cert_spiffe_id,json=leafCertSpiffeId,proto3" json:"leaf_cert_spiffe_id,omitempty"`
// The URIs that are present in the SubjectAltName extension of the peer leaf
// certificate.
//
// Note that the extracted URIs are not validated and may not be properly
// formatted.
LeafCertUris []string `protobuf:"bytes,2,rep,name=leaf_cert_uris,json=leafCertUris,proto3" json:"leaf_cert_uris,omitempty"`
// The DNSNames that are present in the SubjectAltName extension of the peer
// leaf certificate.
LeafCertDnsnames []string `protobuf:"bytes,3,rep,name=leaf_cert_dnsnames,json=leafCertDnsnames,proto3" json:"leaf_cert_dnsnames,omitempty"`
// The (ordered) list of fingerprints in the certificate chain used to verify
// the given leaf certificate. The order MUST be from leaf certificate
// fingerprint to root certificate fingerprint.
//
// A fingerprint is the base-64 encoding of the SHA256 hash of the
// DER-encoding of a certificate. The list MAY be populated even if the peer
// certificate chain was NOT validated successfully.
PeerCertificateChainFingerprints []string `protobuf:"bytes,4,rep,name=peer_certificate_chain_fingerprints,json=peerCertificateChainFingerprints,proto3" json:"peer_certificate_chain_fingerprints,omitempty"`
// The local identity used during session setup.
LocalIdentity *common_go_proto.Identity `protobuf:"bytes,5,opt,name=local_identity,json=localIdentity,proto3" json:"local_identity,omitempty"`
// The SHA256 hash of the DER-encoding of the local leaf certificate used in
// the handshake.
LocalLeafCertFingerprint []byte `protobuf:"bytes,6,opt,name=local_leaf_cert_fingerprint,json=localLeafCertFingerprint,proto3" json:"local_leaf_cert_fingerprint,omitempty"`
}
func (x *S2AContext) Reset() {
*x = S2AContext{}
if protoimpl.UnsafeEnabled {
mi := &file_internal_proto_v2_s2a_context_s2a_context_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *S2AContext) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*S2AContext) ProtoMessage() {}
func (x *S2AContext) ProtoReflect() protoreflect.Message {
mi := &file_internal_proto_v2_s2a_context_s2a_context_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use S2AContext.ProtoReflect.Descriptor instead.
func (*S2AContext) Descriptor() ([]byte, []int) {
return file_internal_proto_v2_s2a_context_s2a_context_proto_rawDescGZIP(), []int{0}
}
func (x *S2AContext) GetLeafCertSpiffeId() string {
if x != nil {
return x.LeafCertSpiffeId
}
return ""
}
func (x *S2AContext) GetLeafCertUris() []string {
if x != nil {
return x.LeafCertUris
}
return nil
}
func (x *S2AContext) GetLeafCertDnsnames() []string {
if x != nil {
return x.LeafCertDnsnames
}
return nil
}
func (x *S2AContext) GetPeerCertificateChainFingerprints() []string {
if x != nil {
return x.PeerCertificateChainFingerprints
}
return nil
}
func (x *S2AContext) GetLocalIdentity() *common_go_proto.Identity {
if x != nil {
return x.LocalIdentity
}
return nil
}
func (x *S2AContext) GetLocalLeafCertFingerprint() []byte {
if x != nil {
return x.LocalLeafCertFingerprint
}
return nil
}
var File_internal_proto_v2_s2a_context_s2a_context_proto protoreflect.FileDescriptor
var file_internal_proto_v2_s2a_context_s2a_context_proto_rawDesc = []byte{
0x0a, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2f, 0x76, 0x32, 0x2f, 0x73, 0x32, 0x61, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2f,
0x73, 0x32, 0x61, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x0c, 0x73, 0x32, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x76, 0x32, 0x1a,
0x22, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x22, 0xd9, 0x02, 0x0a, 0x0a, 0x53, 0x32, 0x41, 0x43, 0x6f, 0x6e, 0x74, 0x65,
0x78, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x6c, 0x65, 0x61, 0x66, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f,
0x73, 0x70, 0x69, 0x66, 0x66, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x10, 0x6c, 0x65, 0x61, 0x66, 0x43, 0x65, 0x72, 0x74, 0x53, 0x70, 0x69, 0x66, 0x66, 0x65, 0x49,
0x64, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x65, 0x61, 0x66, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x75,
0x72, 0x69, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x65, 0x61, 0x66, 0x43,
0x65, 0x72, 0x74, 0x55, 0x72, 0x69, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x6c, 0x65, 0x61, 0x66, 0x5f,
0x63, 0x65, 0x72, 0x74, 0x5f, 0x64, 0x6e, 0x73, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20,
0x03, 0x28, 0x09, 0x52, 0x10, 0x6c, 0x65, 0x61, 0x66, 0x43, 0x65, 0x72, 0x74, 0x44, 0x6e, 0x73,
0x6e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x4d, 0x0a, 0x23, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65,
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f,
0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03,
0x28, 0x09, 0x52, 0x20, 0x70, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72,
0x69, 0x6e, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x69, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73,
0x32, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
0x79, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79,
0x12, 0x3d, 0x0a, 0x1b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x6c, 0x65, 0x61, 0x66, 0x5f, 0x63,
0x65, 0x72, 0x74, 0x5f, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18,
0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x18, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x4c, 0x65, 0x61, 0x66,
0x43, 0x65, 0x72, 0x74, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x42,
0x3e, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x73, 0x32, 0x61, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x32, 0x2f, 0x73, 0x32, 0x61, 0x5f, 0x63,
0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x67, 0x6f, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_internal_proto_v2_s2a_context_s2a_context_proto_rawDescOnce sync.Once
file_internal_proto_v2_s2a_context_s2a_context_proto_rawDescData = file_internal_proto_v2_s2a_context_s2a_context_proto_rawDesc
)
func file_internal_proto_v2_s2a_context_s2a_context_proto_rawDescGZIP() []byte {
file_internal_proto_v2_s2a_context_s2a_context_proto_rawDescOnce.Do(func() {
file_internal_proto_v2_s2a_context_s2a_context_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_proto_v2_s2a_context_s2a_context_proto_rawDescData)
})
return file_internal_proto_v2_s2a_context_s2a_context_proto_rawDescData
}
var file_internal_proto_v2_s2a_context_s2a_context_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_internal_proto_v2_s2a_context_s2a_context_proto_goTypes = []interface{}{
(*S2AContext)(nil), // 0: s2a.proto.v2.S2AContext
(*common_go_proto.Identity)(nil), // 1: s2a.proto.Identity
}
var file_internal_proto_v2_s2a_context_s2a_context_proto_depIdxs = []int32{
1, // 0: s2a.proto.v2.S2AContext.local_identity:type_name -> s2a.proto.Identity
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_internal_proto_v2_s2a_context_s2a_context_proto_init() }
func file_internal_proto_v2_s2a_context_s2a_context_proto_init() {
if File_internal_proto_v2_s2a_context_s2a_context_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_internal_proto_v2_s2a_context_s2a_context_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*S2AContext); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_internal_proto_v2_s2a_context_s2a_context_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_internal_proto_v2_s2a_context_s2a_context_proto_goTypes,
DependencyIndexes: file_internal_proto_v2_s2a_context_s2a_context_proto_depIdxs,
MessageInfos: file_internal_proto_v2_s2a_context_s2a_context_proto_msgTypes,
}.Build()
File_internal_proto_v2_s2a_context_s2a_context_proto = out.File
file_internal_proto_v2_s2a_context_s2a_context_proto_rawDesc = nil
file_internal_proto_v2_s2a_context_s2a_context_proto_goTypes = nil
file_internal_proto_v2_s2a_context_s2a_context_proto_depIdxs = nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,159 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v3.21.12
// source: internal/proto/v2/s2a/s2a.proto
package s2a_go_proto
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
const (
S2AService_SetUpSession_FullMethodName = "/s2a.proto.v2.S2AService/SetUpSession"
)
// S2AServiceClient is the client API for S2AService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type S2AServiceClient interface {
// SetUpSession is a bidirectional stream used by applications to offload
// operations from the TLS handshake.
SetUpSession(ctx context.Context, opts ...grpc.CallOption) (S2AService_SetUpSessionClient, error)
}
type s2AServiceClient struct {
cc grpc.ClientConnInterface
}
func NewS2AServiceClient(cc grpc.ClientConnInterface) S2AServiceClient {
return &s2AServiceClient{cc}
}
func (c *s2AServiceClient) SetUpSession(ctx context.Context, opts ...grpc.CallOption) (S2AService_SetUpSessionClient, error) {
stream, err := c.cc.NewStream(ctx, &S2AService_ServiceDesc.Streams[0], S2AService_SetUpSession_FullMethodName, opts...)
if err != nil {
return nil, err
}
x := &s2AServiceSetUpSessionClient{stream}
return x, nil
}
type S2AService_SetUpSessionClient interface {
Send(*SessionReq) error
Recv() (*SessionResp, error)
grpc.ClientStream
}
type s2AServiceSetUpSessionClient struct {
grpc.ClientStream
}
func (x *s2AServiceSetUpSessionClient) Send(m *SessionReq) error {
return x.ClientStream.SendMsg(m)
}
func (x *s2AServiceSetUpSessionClient) Recv() (*SessionResp, error) {
m := new(SessionResp)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// S2AServiceServer is the server API for S2AService service.
// All implementations must embed UnimplementedS2AServiceServer
// for forward compatibility
type S2AServiceServer interface {
// SetUpSession is a bidirectional stream used by applications to offload
// operations from the TLS handshake.
SetUpSession(S2AService_SetUpSessionServer) error
mustEmbedUnimplementedS2AServiceServer()
}
// UnimplementedS2AServiceServer must be embedded to have forward compatible implementations.
type UnimplementedS2AServiceServer struct {
}
func (UnimplementedS2AServiceServer) SetUpSession(S2AService_SetUpSessionServer) error {
return status.Errorf(codes.Unimplemented, "method SetUpSession not implemented")
}
func (UnimplementedS2AServiceServer) mustEmbedUnimplementedS2AServiceServer() {}
// UnsafeS2AServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to S2AServiceServer will
// result in compilation errors.
type UnsafeS2AServiceServer interface {
mustEmbedUnimplementedS2AServiceServer()
}
func RegisterS2AServiceServer(s grpc.ServiceRegistrar, srv S2AServiceServer) {
s.RegisterService(&S2AService_ServiceDesc, srv)
}
func _S2AService_SetUpSession_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(S2AServiceServer).SetUpSession(&s2AServiceSetUpSessionServer{stream})
}
type S2AService_SetUpSessionServer interface {
Send(*SessionResp) error
Recv() (*SessionReq, error)
grpc.ServerStream
}
type s2AServiceSetUpSessionServer struct {
grpc.ServerStream
}
func (x *s2AServiceSetUpSessionServer) Send(m *SessionResp) error {
return x.ServerStream.SendMsg(m)
}
func (x *s2AServiceSetUpSessionServer) Recv() (*SessionReq, error) {
m := new(SessionReq)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// S2AService_ServiceDesc is the grpc.ServiceDesc for S2AService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var S2AService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "s2a.proto.v2.S2AService",
HandlerType: (*S2AServiceServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "SetUpSession",
Handler: _S2AService_SetUpSession_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "internal/proto/v2/s2a/s2a.proto",
}

View File

@@ -0,0 +1,34 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package aeadcrypter provides the interface for AEAD cipher implementations
// used by S2A's record protocol.
package aeadcrypter
// S2AAEADCrypter is the interface for an AEAD cipher used by the S2A record
// protocol.
type S2AAEADCrypter interface {
// Encrypt encrypts the plaintext and computes the tag of dst and plaintext.
// dst and plaintext may fully overlap or not at all.
Encrypt(dst, plaintext, nonce, aad []byte) ([]byte, error)
// Decrypt decrypts ciphertext and verifies the tag. dst and ciphertext may
// fully overlap or not at all.
Decrypt(dst, ciphertext, nonce, aad []byte) ([]byte, error)
// TagSize returns the tag size in bytes.
TagSize() int
}

View File

@@ -0,0 +1,70 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package aeadcrypter
import (
"crypto/aes"
"crypto/cipher"
"fmt"
)
// Supported key sizes in bytes.
const (
AES128GCMKeySize = 16
AES256GCMKeySize = 32
)
// aesgcm is the struct that holds an AES-GCM cipher for the S2A AEAD crypter.
type aesgcm struct {
aead cipher.AEAD
}
// NewAESGCM creates an AES-GCM crypter instance. Note that the key must be
// either 128 bits or 256 bits.
func NewAESGCM(key []byte) (S2AAEADCrypter, error) {
if len(key) != AES128GCMKeySize && len(key) != AES256GCMKeySize {
return nil, fmt.Errorf("%d or %d bytes, given: %d", AES128GCMKeySize, AES256GCMKeySize, len(key))
}
c, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
a, err := cipher.NewGCM(c)
if err != nil {
return nil, err
}
return &aesgcm{aead: a}, nil
}
// Encrypt is the encryption function. dst can contain bytes at the beginning of
// the ciphertext that will not be encrypted but will be authenticated. If dst
// has enough capacity to hold these bytes, the ciphertext and the tag, no
// allocation and copy operations will be performed. dst and plaintext may
// fully overlap or not at all.
func (s *aesgcm) Encrypt(dst, plaintext, nonce, aad []byte) ([]byte, error) {
return encrypt(s.aead, dst, plaintext, nonce, aad)
}
func (s *aesgcm) Decrypt(dst, ciphertext, nonce, aad []byte) ([]byte, error) {
return decrypt(s.aead, dst, ciphertext, nonce, aad)
}
func (s *aesgcm) TagSize() int {
return TagSize
}

View File

@@ -0,0 +1,67 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package aeadcrypter
import (
"crypto/cipher"
"fmt"
"golang.org/x/crypto/chacha20poly1305"
)
// Supported key size in bytes.
const (
Chacha20Poly1305KeySize = 32
)
// chachapoly is the struct that holds a CHACHA-POLY cipher for the S2A AEAD
// crypter.
type chachapoly struct {
aead cipher.AEAD
}
// NewChachaPoly creates a Chacha-Poly crypter instance. Note that the key must
// be Chacha20Poly1305KeySize bytes in length.
func NewChachaPoly(key []byte) (S2AAEADCrypter, error) {
if len(key) != Chacha20Poly1305KeySize {
return nil, fmt.Errorf("%d bytes, given: %d", Chacha20Poly1305KeySize, len(key))
}
c, err := chacha20poly1305.New(key)
if err != nil {
return nil, err
}
return &chachapoly{aead: c}, nil
}
// Encrypt is the encryption function. dst can contain bytes at the beginning of
// the ciphertext that will not be encrypted but will be authenticated. If dst
// has enough capacity to hold these bytes, the ciphertext and the tag, no
// allocation and copy operations will be performed. dst and plaintext may
// fully overlap or not at all.
func (s *chachapoly) Encrypt(dst, plaintext, nonce, aad []byte) ([]byte, error) {
return encrypt(s.aead, dst, plaintext, nonce, aad)
}
func (s *chachapoly) Decrypt(dst, ciphertext, nonce, aad []byte) ([]byte, error) {
return decrypt(s.aead, dst, ciphertext, nonce, aad)
}
func (s *chachapoly) TagSize() int {
return TagSize
}

View File

@@ -0,0 +1,92 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package aeadcrypter
import (
"crypto/cipher"
"fmt"
)
const (
// TagSize is the tag size in bytes for AES-128-GCM-SHA256,
// AES-256-GCM-SHA384, and CHACHA20-POLY1305-SHA256.
TagSize = 16
// NonceSize is the size of the nonce in number of bytes for
// AES-128-GCM-SHA256, AES-256-GCM-SHA384, and CHACHA20-POLY1305-SHA256.
NonceSize = 12
// SHA256DigestSize is the digest size of sha256 in bytes.
SHA256DigestSize = 32
// SHA384DigestSize is the digest size of sha384 in bytes.
SHA384DigestSize = 48
)
// sliceForAppend takes a slice and a requested number of bytes. It returns a
// slice with the contents of the given slice followed by that many bytes and a
// second slice that aliases into it and contains only the extra bytes. If the
// original slice has sufficient capacity then no allocation is performed.
func sliceForAppend(in []byte, n int) (head, tail []byte) {
if total := len(in) + n; cap(in) >= total {
head = in[:total]
} else {
head = make([]byte, total)
copy(head, in)
}
tail = head[len(in):]
return head, tail
}
// encrypt is the encryption function for an AEAD crypter. aead determines
// the type of AEAD crypter. dst can contain bytes at the beginning of the
// ciphertext that will not be encrypted but will be authenticated. If dst has
// enough capacity to hold these bytes, the ciphertext and the tag, no
// allocation and copy operations will be performed. dst and plaintext may
// fully overlap or not at all.
func encrypt(aead cipher.AEAD, dst, plaintext, nonce, aad []byte) ([]byte, error) {
if len(nonce) != NonceSize {
return nil, fmt.Errorf("nonce size must be %d bytes. received: %d", NonceSize, len(nonce))
}
// If we need to allocate an output buffer, we want to include space for
// the tag to avoid forcing the caller to reallocate as well.
dlen := len(dst)
dst, out := sliceForAppend(dst, len(plaintext)+TagSize)
data := out[:len(plaintext)]
copy(data, plaintext) // data may fully overlap plaintext
// Seal appends the ciphertext and the tag to its first argument and
// returns the updated slice. However, sliceForAppend above ensures that
// dst has enough capacity to avoid a reallocation and copy due to the
// append.
dst = aead.Seal(dst[:dlen], nonce, data, aad)
return dst, nil
}
// decrypt is the decryption function for an AEAD crypter, where aead determines
// the type of AEAD crypter, and dst the destination bytes for the decrypted
// ciphertext. The dst buffer may fully overlap with plaintext or not at all.
func decrypt(aead cipher.AEAD, dst, ciphertext, nonce, aad []byte) ([]byte, error) {
if len(nonce) != NonceSize {
return nil, fmt.Errorf("nonce size must be %d bytes. received: %d", NonceSize, len(nonce))
}
// If dst is equal to ciphertext[:0], ciphertext storage is reused.
plaintext, err := aead.Open(dst, nonce, ciphertext, aad)
if err != nil {
return nil, fmt.Errorf("message auth failed: %v", err)
}
return plaintext, nil
}

View File

@@ -0,0 +1,98 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package halfconn
import (
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
s2apb "github.com/google/s2a-go/internal/proto/common_go_proto"
"github.com/google/s2a-go/internal/record/internal/aeadcrypter"
)
// ciphersuite is the interface for retrieving ciphersuite-specific information
// and utilities.
type ciphersuite interface {
// keySize returns the key size in bytes. This refers to the key used by
// the AEAD crypter. This is derived by calling HKDF expand on the traffic
// secret.
keySize() int
// nonceSize returns the nonce size in bytes.
nonceSize() int
// trafficSecretSize returns the traffic secret size in bytes. This refers
// to the secret used to derive the traffic key and nonce, as specified in
// https://tools.ietf.org/html/rfc8446#section-7.
trafficSecretSize() int
// hashFunction returns the hash function for the ciphersuite.
hashFunction() func() hash.Hash
// aeadCrypter takes a key and creates an AEAD crypter for the ciphersuite
// using that key.
aeadCrypter(key []byte) (aeadcrypter.S2AAEADCrypter, error)
}
func newCiphersuite(ciphersuite s2apb.Ciphersuite) (ciphersuite, error) {
switch ciphersuite {
case s2apb.Ciphersuite_AES_128_GCM_SHA256:
return &aesgcm128sha256{}, nil
case s2apb.Ciphersuite_AES_256_GCM_SHA384:
return &aesgcm256sha384{}, nil
case s2apb.Ciphersuite_CHACHA20_POLY1305_SHA256:
return &chachapolysha256{}, nil
default:
return nil, fmt.Errorf("unrecognized ciphersuite: %v", ciphersuite)
}
}
// aesgcm128sha256 is the AES-128-GCM-SHA256 implementation of the ciphersuite
// interface.
type aesgcm128sha256 struct{}
func (aesgcm128sha256) keySize() int { return aeadcrypter.AES128GCMKeySize }
func (aesgcm128sha256) nonceSize() int { return aeadcrypter.NonceSize }
func (aesgcm128sha256) trafficSecretSize() int { return aeadcrypter.SHA256DigestSize }
func (aesgcm128sha256) hashFunction() func() hash.Hash { return sha256.New }
func (aesgcm128sha256) aeadCrypter(key []byte) (aeadcrypter.S2AAEADCrypter, error) {
return aeadcrypter.NewAESGCM(key)
}
// aesgcm256sha384 is the AES-256-GCM-SHA384 implementation of the ciphersuite
// interface.
type aesgcm256sha384 struct{}
func (aesgcm256sha384) keySize() int { return aeadcrypter.AES256GCMKeySize }
func (aesgcm256sha384) nonceSize() int { return aeadcrypter.NonceSize }
func (aesgcm256sha384) trafficSecretSize() int { return aeadcrypter.SHA384DigestSize }
func (aesgcm256sha384) hashFunction() func() hash.Hash { return sha512.New384 }
func (aesgcm256sha384) aeadCrypter(key []byte) (aeadcrypter.S2AAEADCrypter, error) {
return aeadcrypter.NewAESGCM(key)
}
// chachapolysha256 is the ChaChaPoly-SHA256 implementation of the ciphersuite
// interface.
type chachapolysha256 struct{}
func (chachapolysha256) keySize() int { return aeadcrypter.Chacha20Poly1305KeySize }
func (chachapolysha256) nonceSize() int { return aeadcrypter.NonceSize }
func (chachapolysha256) trafficSecretSize() int { return aeadcrypter.SHA256DigestSize }
func (chachapolysha256) hashFunction() func() hash.Hash { return sha256.New }
func (chachapolysha256) aeadCrypter(key []byte) (aeadcrypter.S2AAEADCrypter, error) {
return aeadcrypter.NewChachaPoly(key)
}

View File

@@ -0,0 +1,60 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package halfconn
import "errors"
// counter is a 64-bit counter.
type counter struct {
val uint64
hasOverflowed bool
}
// newCounter creates a new counter with the initial value set to val.
func newCounter(val uint64) counter {
return counter{val: val}
}
// value returns the current value of the counter.
func (c *counter) value() (uint64, error) {
if c.hasOverflowed {
return 0, errors.New("counter has overflowed")
}
return c.val, nil
}
// increment increments the counter and checks for overflow.
func (c *counter) increment() {
// If the counter is already invalid due to overflow, there is no need to
// increase it. We check for the hasOverflowed flag in the call to value().
if c.hasOverflowed {
return
}
c.val++
if c.val == 0 {
c.hasOverflowed = true
}
}
// reset sets the counter value to zero and sets the hasOverflowed flag to
// false.
func (c *counter) reset() {
c.val = 0
c.hasOverflowed = false
}

View File

@@ -0,0 +1,59 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package halfconn
import (
"fmt"
"hash"
"golang.org/x/crypto/hkdf"
)
// hkdfExpander is the interface for the HKDF expansion function; see
// https://tools.ietf.org/html/rfc5869 for details. its use in TLS 1.3 is
// specified in https://tools.ietf.org/html/rfc8446#section-7.2
type hkdfExpander interface {
// expand takes a secret, a label, and the output length in bytes, and
// returns the resulting expanded key.
expand(secret, label []byte, length int) ([]byte, error)
}
// defaultHKDFExpander is the default HKDF expander which uses Go's crypto/hkdf
// for HKDF expansion.
type defaultHKDFExpander struct {
h func() hash.Hash
}
// newDefaultHKDFExpander creates an instance of the default HKDF expander
// using the given hash function.
func newDefaultHKDFExpander(h func() hash.Hash) hkdfExpander {
return &defaultHKDFExpander{h: h}
}
func (d *defaultHKDFExpander) expand(secret, label []byte, length int) ([]byte, error) {
outBuf := make([]byte, length)
n, err := hkdf.Expand(d.h, secret, label).Read(outBuf)
if err != nil {
return nil, fmt.Errorf("hkdf.Expand.Read failed with error: %v", err)
}
if n < length {
return nil, fmt.Errorf("hkdf.Expand.Read returned unexpected length, got %d, want %d", n, length)
}
return outBuf, nil
}

View File

@@ -0,0 +1,193 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package halfconn manages the inbound or outbound traffic of a TLS 1.3
// connection.
package halfconn
import (
"fmt"
"sync"
s2apb "github.com/google/s2a-go/internal/proto/common_go_proto"
"github.com/google/s2a-go/internal/record/internal/aeadcrypter"
"golang.org/x/crypto/cryptobyte"
)
// The constants below were taken from Section 7.2 and 7.3 in
// https://tools.ietf.org/html/rfc8446#section-7. They are used as the label
// in HKDF-Expand-Label.
const (
tls13Key = "tls13 key"
tls13Nonce = "tls13 iv"
tls13Update = "tls13 traffic upd"
)
// S2AHalfConnection stores the state of the TLS 1.3 connection in the
// inbound or outbound direction.
type S2AHalfConnection struct {
cs ciphersuite
expander hkdfExpander
// mutex guards sequence, aeadCrypter, trafficSecret, and nonce.
mutex sync.Mutex
aeadCrypter aeadcrypter.S2AAEADCrypter
sequence counter
trafficSecret []byte
nonce []byte
}
// New creates a new instance of S2AHalfConnection given a ciphersuite and a
// traffic secret.
func New(ciphersuite s2apb.Ciphersuite, trafficSecret []byte, sequence uint64) (*S2AHalfConnection, error) {
cs, err := newCiphersuite(ciphersuite)
if err != nil {
return nil, fmt.Errorf("failed to create new ciphersuite: %v", ciphersuite)
}
if cs.trafficSecretSize() != len(trafficSecret) {
return nil, fmt.Errorf("supplied traffic secret must be %v bytes, given: %v bytes", cs.trafficSecretSize(), len(trafficSecret))
}
hc := &S2AHalfConnection{cs: cs, expander: newDefaultHKDFExpander(cs.hashFunction()), sequence: newCounter(sequence), trafficSecret: trafficSecret}
if err = hc.updateCrypterAndNonce(hc.trafficSecret); err != nil {
return nil, fmt.Errorf("failed to create half connection using traffic secret: %v", err)
}
return hc, nil
}
// Encrypt encrypts the plaintext and computes the tag of dst and plaintext.
// dst and plaintext may fully overlap or not at all. Note that the sequence
// number will still be incremented on failure, unless the sequence has
// overflowed.
func (hc *S2AHalfConnection) Encrypt(dst, plaintext, aad []byte) ([]byte, error) {
hc.mutex.Lock()
sequence, err := hc.getAndIncrementSequence()
if err != nil {
hc.mutex.Unlock()
return nil, err
}
nonce := hc.maskedNonce(sequence)
crypter := hc.aeadCrypter
hc.mutex.Unlock()
return crypter.Encrypt(dst, plaintext, nonce, aad)
}
// Decrypt decrypts ciphertext and verifies the tag. dst and ciphertext may
// fully overlap or not at all. Note that the sequence number will still be
// incremented on failure, unless the sequence has overflowed.
func (hc *S2AHalfConnection) Decrypt(dst, ciphertext, aad []byte) ([]byte, error) {
hc.mutex.Lock()
sequence, err := hc.getAndIncrementSequence()
if err != nil {
hc.mutex.Unlock()
return nil, err
}
nonce := hc.maskedNonce(sequence)
crypter := hc.aeadCrypter
hc.mutex.Unlock()
return crypter.Decrypt(dst, ciphertext, nonce, aad)
}
// UpdateKey advances the traffic secret key, as specified in
// https://tools.ietf.org/html/rfc8446#section-7.2. In addition, it derives
// a new key and nonce, and resets the sequence number.
func (hc *S2AHalfConnection) UpdateKey() error {
hc.mutex.Lock()
defer hc.mutex.Unlock()
var err error
hc.trafficSecret, err = hc.deriveSecret(hc.trafficSecret, []byte(tls13Update), hc.cs.trafficSecretSize())
if err != nil {
return fmt.Errorf("failed to derive traffic secret: %v", err)
}
if err = hc.updateCrypterAndNonce(hc.trafficSecret); err != nil {
return fmt.Errorf("failed to update half connection: %v", err)
}
hc.sequence.reset()
return nil
}
// TagSize returns the tag size in bytes of the underlying AEAD crypter.
func (hc *S2AHalfConnection) TagSize() int {
return hc.aeadCrypter.TagSize()
}
// updateCrypterAndNonce takes a new traffic secret and updates the crypter
// and nonce. Note that the mutex must be held while calling this function.
func (hc *S2AHalfConnection) updateCrypterAndNonce(newTrafficSecret []byte) error {
key, err := hc.deriveSecret(newTrafficSecret, []byte(tls13Key), hc.cs.keySize())
if err != nil {
return fmt.Errorf("failed to update key: %v", err)
}
hc.nonce, err = hc.deriveSecret(newTrafficSecret, []byte(tls13Nonce), hc.cs.nonceSize())
if err != nil {
return fmt.Errorf("failed to update nonce: %v", err)
}
hc.aeadCrypter, err = hc.cs.aeadCrypter(key)
if err != nil {
return fmt.Errorf("failed to update AEAD crypter: %v", err)
}
return nil
}
// getAndIncrement returns the current sequence number and increments it. Note
// that the mutex must be held while calling this function.
func (hc *S2AHalfConnection) getAndIncrementSequence() (uint64, error) {
sequence, err := hc.sequence.value()
if err != nil {
return 0, err
}
hc.sequence.increment()
return sequence, nil
}
// maskedNonce creates a copy of the nonce that is masked with the sequence
// number. Note that the mutex must be held while calling this function.
func (hc *S2AHalfConnection) maskedNonce(sequence uint64) []byte {
const uint64Size = 8
nonce := make([]byte, len(hc.nonce))
copy(nonce, hc.nonce)
for i := 0; i < uint64Size; i++ {
nonce[aeadcrypter.NonceSize-uint64Size+i] ^= byte(sequence >> uint64(56-uint64Size*i))
}
return nonce
}
// deriveSecret implements the Derive-Secret function, as specified in
// https://tools.ietf.org/html/rfc8446#section-7.1.
func (hc *S2AHalfConnection) deriveSecret(secret, label []byte, length int) ([]byte, error) {
var hkdfLabel cryptobyte.Builder
hkdfLabel.AddUint16(uint16(length))
hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes(label)
})
// Append an empty `Context` field to the label, as specified in the RFC.
// The half connection does not use the `Context` field.
hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes([]byte(""))
})
hkdfLabelBytes, err := hkdfLabel.Bytes()
if err != nil {
return nil, fmt.Errorf("deriveSecret failed: %v", err)
}
return hc.expander.expand(secret, hkdfLabelBytes, length)
}

View File

@@ -0,0 +1,757 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package record implements the TLS 1.3 record protocol used by the S2A
// transport credentials.
package record
import (
"encoding/binary"
"errors"
"fmt"
"math"
"net"
"sync"
commonpb "github.com/google/s2a-go/internal/proto/common_go_proto"
"github.com/google/s2a-go/internal/record/internal/halfconn"
"github.com/google/s2a-go/internal/tokenmanager"
"google.golang.org/grpc/grpclog"
)
// recordType is the `ContentType` as described in
// https://tools.ietf.org/html/rfc8446#section-5.1.
type recordType byte
const (
alert recordType = 21
handshake recordType = 22
applicationData recordType = 23
)
// keyUpdateRequest is the `KeyUpdateRequest` as described in
// https://tools.ietf.org/html/rfc8446#section-4.6.3.
type keyUpdateRequest byte
const (
updateNotRequested keyUpdateRequest = 0
updateRequested keyUpdateRequest = 1
)
// alertDescription is the `AlertDescription` as described in
// https://tools.ietf.org/html/rfc8446#section-6.
type alertDescription byte
const (
closeNotify alertDescription = 0
)
// sessionTicketState is used to determine whether session tickets have not yet
// been received, are in the process of being received, or have finished
// receiving.
type sessionTicketState byte
const (
ticketsNotYetReceived sessionTicketState = 0
receivingTickets sessionTicketState = 1
notReceivingTickets sessionTicketState = 2
)
const (
// The TLS 1.3-specific constants below (tlsRecordMaxPlaintextSize,
// tlsRecordHeaderSize, tlsRecordTypeSize) were taken from
// https://tools.ietf.org/html/rfc8446#section-5.1.
// tlsRecordMaxPlaintextSize is the maximum size in bytes of the plaintext
// in a single TLS 1.3 record.
tlsRecordMaxPlaintextSize = 16384 // 2^14
// tlsRecordTypeSize is the size in bytes of the TLS 1.3 record type.
tlsRecordTypeSize = 1
// tlsTagSize is the size in bytes of the tag of the following three
// ciphersuites: AES-128-GCM-SHA256, AES-256-GCM-SHA384,
// CHACHA20-POLY1305-SHA256.
tlsTagSize = 16
// tlsRecordMaxPayloadSize is the maximum size in bytes of the payload in a
// single TLS 1.3 record. This is the maximum size of the plaintext plus the
// record type byte and 16 bytes of the tag.
tlsRecordMaxPayloadSize = tlsRecordMaxPlaintextSize + tlsRecordTypeSize + tlsTagSize
// tlsRecordHeaderTypeSize is the size in bytes of the TLS 1.3 record
// header type.
tlsRecordHeaderTypeSize = 1
// tlsRecordHeaderLegacyRecordVersionSize is the size in bytes of the TLS
// 1.3 record header legacy record version.
tlsRecordHeaderLegacyRecordVersionSize = 2
// tlsRecordHeaderPayloadLengthSize is the size in bytes of the TLS 1.3
// record header payload length.
tlsRecordHeaderPayloadLengthSize = 2
// tlsRecordHeaderSize is the size in bytes of the TLS 1.3 record header.
tlsRecordHeaderSize = tlsRecordHeaderTypeSize + tlsRecordHeaderLegacyRecordVersionSize + tlsRecordHeaderPayloadLengthSize
// tlsRecordMaxSize
tlsRecordMaxSize = tlsRecordMaxPayloadSize + tlsRecordHeaderSize
// tlsApplicationData is the application data type of the TLS 1.3 record
// header.
tlsApplicationData = 23
// tlsLegacyRecordVersion is the legacy record version of the TLS record.
tlsLegacyRecordVersion = 3
// tlsAlertSize is the size in bytes of an alert of TLS 1.3.
tlsAlertSize = 2
)
const (
// These are TLS 1.3 handshake-specific constants.
// tlsHandshakeNewSessionTicketType is the prefix of a handshake new session
// ticket message of TLS 1.3.
tlsHandshakeNewSessionTicketType = 4
// tlsHandshakeKeyUpdateType is the prefix of a handshake key update message
// of TLS 1.3.
tlsHandshakeKeyUpdateType = 24
// tlsHandshakeMsgTypeSize is the size in bytes of the TLS 1.3 handshake
// message type field.
tlsHandshakeMsgTypeSize = 1
// tlsHandshakeLengthSize is the size in bytes of the TLS 1.3 handshake
// message length field.
tlsHandshakeLengthSize = 3
// tlsHandshakeKeyUpdateMsgSize is the size in bytes of the TLS 1.3
// handshake key update message.
tlsHandshakeKeyUpdateMsgSize = 1
// tlsHandshakePrefixSize is the size in bytes of the prefix of the TLS 1.3
// handshake message.
tlsHandshakePrefixSize = 4
// tlsMaxSessionTicketSize is the maximum size of a NewSessionTicket message
// in TLS 1.3. This is the sum of the max sizes of all the fields in the
// NewSessionTicket struct specified in
// https://tools.ietf.org/html/rfc8446#section-4.6.1.
tlsMaxSessionTicketSize = 131338
)
const (
// outBufMaxRecords is the maximum number of records that can fit in the
// ourRecordsBuf buffer.
outBufMaxRecords = 16
// outBufMaxSize is the maximum size (in bytes) of the outRecordsBuf buffer.
outBufMaxSize = outBufMaxRecords * tlsRecordMaxSize
// maxAllowedTickets is the maximum number of session tickets that are
// allowed. The number of tickets are limited to ensure that the size of the
// ticket queue does not grow indefinitely. S2A also keeps a limit on the
// number of tickets that it caches.
maxAllowedTickets = 5
)
// preConstructedKeyUpdateMsg holds the key update message. This is needed as an
// optimization so that the same message does not need to be constructed every
// time a key update message is sent.
var preConstructedKeyUpdateMsg = buildKeyUpdateRequest()
// conn represents a secured TLS connection. It implements the net.Conn
// interface.
type conn struct {
net.Conn
// inConn is the half connection responsible for decrypting incoming bytes.
inConn *halfconn.S2AHalfConnection
// outConn is the half connection responsible for encrypting outgoing bytes.
outConn *halfconn.S2AHalfConnection
// pendingApplicationData holds data that has been read from the connection
// and decrypted, but has not yet been returned by Read.
pendingApplicationData []byte
// unusedBuf holds data read from the network that has not yet been
// decrypted. This data might not consist of a complete record. It may
// consist of several records, the last of which could be incomplete.
unusedBuf []byte
// outRecordsBuf is a buffer used to store outgoing TLS records before
// they are written to the network.
outRecordsBuf []byte
// nextRecord stores the next record info in the unusedBuf buffer.
nextRecord []byte
// overheadSize is the overhead size in bytes of each TLS 1.3 record, which
// is computed as overheadSize = header size + record type byte + tag size.
// Note that there is no padding by zeros in the overhead calculation.
overheadSize int
// readMutex guards against concurrent calls to Read. This is required since
// Close may be called during a Read.
readMutex sync.Mutex
// writeMutex guards against concurrent calls to Write. This is required
// since Close may be called during a Write, and also because a key update
// message may be written during a Read.
writeMutex sync.Mutex
// handshakeBuf holds handshake messages while they are being processed.
handshakeBuf []byte
// ticketState is the current processing state of the session tickets.
ticketState sessionTicketState
// sessionTickets holds the completed session tickets until they are sent to
// the handshaker service for processing.
sessionTickets [][]byte
// ticketSender sends session tickets to the S2A handshaker service.
ticketSender s2aTicketSender
// callComplete is a channel that blocks closing the record protocol until a
// pending call to the S2A completes.
callComplete chan bool
}
// ConnParameters holds the parameters used for creating a new conn object.
type ConnParameters struct {
// NetConn is the TCP connection to the peer. This parameter is required.
NetConn net.Conn
// Ciphersuite is the TLS ciphersuite negotiated by the S2A handshaker
// service. This parameter is required.
Ciphersuite commonpb.Ciphersuite
// TLSVersion is the TLS version number negotiated by the S2A handshaker
// service. This parameter is required.
TLSVersion commonpb.TLSVersion
// InTrafficSecret is the traffic secret used to derive the session key for
// the inbound direction. This parameter is required.
InTrafficSecret []byte
// OutTrafficSecret is the traffic secret used to derive the session key
// for the outbound direction. This parameter is required.
OutTrafficSecret []byte
// UnusedBuf is the data read from the network that has not yet been
// decrypted. This parameter is optional. If not provided, then no
// application data was sent in the same flight of messages as the final
// handshake message.
UnusedBuf []byte
// InSequence is the sequence number of the next, incoming, TLS record.
// This parameter is required.
InSequence uint64
// OutSequence is the sequence number of the next, outgoing, TLS record.
// This parameter is required.
OutSequence uint64
// HSAddr stores the address of the S2A handshaker service. This parameter
// is optional. If not provided, then TLS resumption is disabled.
HSAddr string
// ConnectionId is the connection identifier that was created and sent by
// S2A at the end of a handshake.
ConnectionID uint64
// LocalIdentity is the local identity that was used by S2A during session
// setup and included in the session result.
LocalIdentity *commonpb.Identity
// EnsureProcessSessionTickets allows users to wait and ensure that all
// available session tickets are sent to S2A before a process completes.
EnsureProcessSessionTickets *sync.WaitGroup
}
// NewConn creates a TLS record protocol that wraps the TCP connection.
func NewConn(o *ConnParameters) (net.Conn, error) {
if o == nil {
return nil, errors.New("conn options must not be nil")
}
if o.TLSVersion != commonpb.TLSVersion_TLS1_3 {
return nil, errors.New("TLS version must be TLS 1.3")
}
inConn, err := halfconn.New(o.Ciphersuite, o.InTrafficSecret, o.InSequence)
if err != nil {
return nil, fmt.Errorf("failed to create inbound half connection: %v", err)
}
outConn, err := halfconn.New(o.Ciphersuite, o.OutTrafficSecret, o.OutSequence)
if err != nil {
return nil, fmt.Errorf("failed to create outbound half connection: %v", err)
}
// The tag size for the in/out connections should be the same.
overheadSize := tlsRecordHeaderSize + tlsRecordTypeSize + inConn.TagSize()
var unusedBuf []byte
if o.UnusedBuf == nil {
// We pre-allocate unusedBuf to be of size
// 2*tlsRecordMaxSize-1 during initialization. We only read from the
// network into unusedBuf when unusedBuf does not contain a complete
// record and the incomplete record is at most tlsRecordMaxSize-1
// (bytes). And we read at most tlsRecordMaxSize bytes of data from the
// network into unusedBuf at one time. Therefore, 2*tlsRecordMaxSize-1
// is large enough to buffer data read from the network.
unusedBuf = make([]byte, 0, 2*tlsRecordMaxSize-1)
} else {
unusedBuf = make([]byte, len(o.UnusedBuf))
copy(unusedBuf, o.UnusedBuf)
}
tokenManager, err := tokenmanager.NewSingleTokenAccessTokenManager()
if err != nil {
grpclog.Infof("failed to create single token access token manager: %v", err)
}
s2aConn := &conn{
Conn: o.NetConn,
inConn: inConn,
outConn: outConn,
unusedBuf: unusedBuf,
outRecordsBuf: make([]byte, tlsRecordMaxSize),
nextRecord: unusedBuf,
overheadSize: overheadSize,
ticketState: ticketsNotYetReceived,
// Pre-allocate the buffer for one session ticket message and the max
// plaintext size. This is the largest size that handshakeBuf will need
// to hold. The largest incomplete handshake message is the
// [handshake header size] + [max session ticket size] - 1.
// Then, tlsRecordMaxPlaintextSize is the maximum size that will be
// appended to the handshakeBuf before the handshake message is
// completed. Therefore, the buffer size below should be large enough to
// buffer any handshake messages.
handshakeBuf: make([]byte, 0, tlsHandshakePrefixSize+tlsMaxSessionTicketSize+tlsRecordMaxPlaintextSize-1),
ticketSender: &ticketSender{
hsAddr: o.HSAddr,
connectionID: o.ConnectionID,
localIdentity: o.LocalIdentity,
tokenManager: tokenManager,
ensureProcessSessionTickets: o.EnsureProcessSessionTickets,
},
callComplete: make(chan bool),
}
return s2aConn, nil
}
// Read reads and decrypts a TLS 1.3 record from the underlying connection, and
// copies any application data received from the peer into b. If the size of the
// payload is greater than len(b), Read retains the remaining bytes in an
// internal buffer, and subsequent calls to Read will read from this buffer
// until it is exhausted. At most 1 TLS record worth of application data is
// written to b for each call to Read.
//
// Note that for the user to efficiently call this method, the user should
// ensure that the buffer b is allocated such that the buffer does not have any
// unused segments. This can be done by calling Read via io.ReadFull, which
// continually calls Read until the specified buffer has been filled. Also note
// that the user should close the connection via Close() if an error is thrown
// by a call to Read.
func (p *conn) Read(b []byte) (n int, err error) {
p.readMutex.Lock()
defer p.readMutex.Unlock()
// Check if p.pendingApplication data has leftover application data from
// the previous call to Read.
if len(p.pendingApplicationData) == 0 {
// Read a full record from the wire.
record, err := p.readFullRecord()
if err != nil {
return 0, err
}
// Now we have a complete record, so split the header and validate it
// The TLS record is split into 2 pieces: the record header and the
// payload. The payload has the following form:
// [payload] = [ciphertext of application data]
// + [ciphertext of record type byte]
// + [(optionally) ciphertext of padding by zeros]
// + [tag]
header, payload, err := splitAndValidateHeader(record)
if err != nil {
return 0, err
}
// Decrypt the ciphertext.
p.pendingApplicationData, err = p.inConn.Decrypt(payload[:0], payload, header)
if err != nil {
return 0, err
}
// Remove the padding by zeros and the record type byte from the
// p.pendingApplicationData buffer.
msgType, err := p.stripPaddingAndType()
if err != nil {
return 0, err
}
// Check that the length of the plaintext after stripping the padding
// and record type byte is under the maximum plaintext size.
if len(p.pendingApplicationData) > tlsRecordMaxPlaintextSize {
return 0, errors.New("plaintext size larger than maximum")
}
// The expected message types are application data, alert, and
// handshake. For application data, the bytes are directly copied into
// b. For an alert, the type of the alert is checked and the connection
// is closed on a close notify alert. For a handshake message, the
// handshake message type is checked. The handshake message type can be
// a key update type, for which we advance the traffic secret, and a
// new session ticket type, for which we send the received ticket to S2A
// for processing.
switch msgType {
case applicationData:
if len(p.handshakeBuf) > 0 {
return 0, errors.New("application data received while processing fragmented handshake messages")
}
if p.ticketState == receivingTickets {
p.ticketState = notReceivingTickets
grpclog.Infof("Sending session tickets to S2A.")
p.ticketSender.sendTicketsToS2A(p.sessionTickets, p.callComplete)
}
case alert:
return 0, p.handleAlertMessage()
case handshake:
if err = p.handleHandshakeMessage(); err != nil {
return 0, err
}
return 0, nil
default:
return 0, errors.New("unknown record type")
}
}
// Write as much application data as possible to b, the output buffer.
n = copy(b, p.pendingApplicationData)
p.pendingApplicationData = p.pendingApplicationData[n:]
return n, nil
}
// Write divides b into segments of size tlsRecordMaxPlaintextSize, builds a
// TLS 1.3 record (of type "application data") from each segment, and sends
// the record to the peer. It returns the number of plaintext bytes that were
// successfully sent to the peer.
func (p *conn) Write(b []byte) (n int, err error) {
p.writeMutex.Lock()
defer p.writeMutex.Unlock()
return p.writeTLSRecord(b, tlsApplicationData)
}
// writeTLSRecord divides b into segments of size maxPlaintextBytesPerRecord,
// builds a TLS 1.3 record (of type recordType) from each segment, and sends
// the record to the peer. It returns the number of plaintext bytes that were
// successfully sent to the peer.
func (p *conn) writeTLSRecord(b []byte, recordType byte) (n int, err error) {
// Create a record of only header, record type, and tag if given empty
// byte array.
if len(b) == 0 {
recordEndIndex, _, err := p.buildRecord(b, recordType, 0)
if err != nil {
return 0, err
}
// Write the bytes stored in outRecordsBuf to p.Conn. Since we return
// the number of plaintext bytes written without overhead, we will
// always return 0 while p.Conn.Write returns the entire record length.
_, err = p.Conn.Write(p.outRecordsBuf[:recordEndIndex])
return 0, err
}
numRecords := int(math.Ceil(float64(len(b)) / float64(tlsRecordMaxPlaintextSize)))
totalRecordsSize := len(b) + numRecords*p.overheadSize
partialBSize := len(b)
if totalRecordsSize > outBufMaxSize {
totalRecordsSize = outBufMaxSize
partialBSize = outBufMaxRecords * tlsRecordMaxPlaintextSize
}
if len(p.outRecordsBuf) < totalRecordsSize {
p.outRecordsBuf = make([]byte, totalRecordsSize)
}
for bStart := 0; bStart < len(b); bStart += partialBSize {
bEnd := bStart + partialBSize
if bEnd > len(b) {
bEnd = len(b)
}
partialB := b[bStart:bEnd]
recordEndIndex := 0
for len(partialB) > 0 {
recordEndIndex, partialB, err = p.buildRecord(partialB, recordType, recordEndIndex)
if err != nil {
// Return the amount of bytes written prior to the error.
return bStart, err
}
}
// Write the bytes stored in outRecordsBuf to p.Conn. If there is an
// error, calculate the total number of plaintext bytes of complete
// records successfully written to the peer and return it.
nn, err := p.Conn.Write(p.outRecordsBuf[:recordEndIndex])
if err != nil {
numberOfCompletedRecords := int(math.Floor(float64(nn) / float64(tlsRecordMaxSize)))
return bStart + numberOfCompletedRecords*tlsRecordMaxPlaintextSize, err
}
}
return len(b), nil
}
// buildRecord builds a TLS 1.3 record of type recordType from plaintext,
// and writes the record to outRecordsBuf at recordStartIndex. The record will
// have at most tlsRecordMaxPlaintextSize bytes of payload. It returns the
// index of outRecordsBuf where the current record ends, as well as any
// remaining plaintext bytes.
func (p *conn) buildRecord(plaintext []byte, recordType byte, recordStartIndex int) (n int, remainingPlaintext []byte, err error) {
// Construct the payload, which consists of application data and record type.
dataLen := len(plaintext)
if dataLen > tlsRecordMaxPlaintextSize {
dataLen = tlsRecordMaxPlaintextSize
}
remainingPlaintext = plaintext[dataLen:]
newRecordBuf := p.outRecordsBuf[recordStartIndex:]
copy(newRecordBuf[tlsRecordHeaderSize:], plaintext[:dataLen])
newRecordBuf[tlsRecordHeaderSize+dataLen] = recordType
payload := newRecordBuf[tlsRecordHeaderSize : tlsRecordHeaderSize+dataLen+1] // 1 is for the recordType.
// Construct the header.
newRecordBuf[0] = tlsApplicationData
newRecordBuf[1] = tlsLegacyRecordVersion
newRecordBuf[2] = tlsLegacyRecordVersion
binary.BigEndian.PutUint16(newRecordBuf[3:], uint16(len(payload)+tlsTagSize))
header := newRecordBuf[:tlsRecordHeaderSize]
// Encrypt the payload using header as aad.
encryptedPayload, err := p.outConn.Encrypt(newRecordBuf[tlsRecordHeaderSize:][:0], payload, header)
if err != nil {
return 0, plaintext, err
}
recordStartIndex += len(header) + len(encryptedPayload)
return recordStartIndex, remainingPlaintext, nil
}
func (p *conn) Close() error {
p.readMutex.Lock()
defer p.readMutex.Unlock()
p.writeMutex.Lock()
defer p.writeMutex.Unlock()
// If p.ticketState is equal to notReceivingTickets, then S2A has
// been sent a flight of session tickets, and we must wait for the
// call to S2A to complete before closing the record protocol.
if p.ticketState == notReceivingTickets {
<-p.callComplete
grpclog.Infof("Safe to close the connection because sending tickets to S2A is (already) complete.")
}
return p.Conn.Close()
}
// stripPaddingAndType strips the padding by zeros and record type from
// p.pendingApplicationData and returns the record type. Note that
// p.pendingApplicationData should be of the form:
// [application data] + [record type byte] + [trailing zeros]
func (p *conn) stripPaddingAndType() (recordType, error) {
if len(p.pendingApplicationData) == 0 {
return 0, errors.New("application data had length 0")
}
i := len(p.pendingApplicationData) - 1
// Search for the index of the record type byte.
for i > 0 {
if p.pendingApplicationData[i] != 0 {
break
}
i--
}
rt := recordType(p.pendingApplicationData[i])
p.pendingApplicationData = p.pendingApplicationData[:i]
return rt, nil
}
// readFullRecord reads from the wire until a record is completed and returns
// the full record.
func (p *conn) readFullRecord() (fullRecord []byte, err error) {
fullRecord, p.nextRecord, err = parseReadBuffer(p.nextRecord, tlsRecordMaxPayloadSize)
if err != nil {
return nil, err
}
// Check whether the next record to be decrypted has been completely
// received.
if len(fullRecord) == 0 {
copy(p.unusedBuf, p.nextRecord)
p.unusedBuf = p.unusedBuf[:len(p.nextRecord)]
// Always copy next incomplete record to the beginning of the
// unusedBuf buffer and reset nextRecord to it.
p.nextRecord = p.unusedBuf
}
// Keep reading from the wire until we have a complete record.
for len(fullRecord) == 0 {
if len(p.unusedBuf) == cap(p.unusedBuf) {
tmp := make([]byte, len(p.unusedBuf), cap(p.unusedBuf)+tlsRecordMaxSize)
copy(tmp, p.unusedBuf)
p.unusedBuf = tmp
}
n, err := p.Conn.Read(p.unusedBuf[len(p.unusedBuf):min(cap(p.unusedBuf), len(p.unusedBuf)+tlsRecordMaxSize)])
if err != nil {
return nil, err
}
p.unusedBuf = p.unusedBuf[:len(p.unusedBuf)+n]
fullRecord, p.nextRecord, err = parseReadBuffer(p.unusedBuf, tlsRecordMaxPayloadSize)
if err != nil {
return nil, err
}
}
return fullRecord, nil
}
// parseReadBuffer parses the provided buffer and returns a full record and any
// remaining bytes in that buffer. If the record is incomplete, nil is returned
// for the first return value and the given byte buffer is returned for the
// second return value. The length of the payload specified by the header should
// not be greater than maxLen, otherwise an error is returned. Note that this
// function does not allocate or copy any buffers.
func parseReadBuffer(b []byte, maxLen uint16) (fullRecord, remaining []byte, err error) {
// If the header is not complete, return the provided buffer as remaining
// buffer.
if len(b) < tlsRecordHeaderSize {
return nil, b, nil
}
msgLenField := b[tlsRecordHeaderTypeSize+tlsRecordHeaderLegacyRecordVersionSize : tlsRecordHeaderSize]
length := binary.BigEndian.Uint16(msgLenField)
if length > maxLen {
return nil, nil, fmt.Errorf("record length larger than the limit %d", maxLen)
}
if len(b) < int(length)+tlsRecordHeaderSize {
// Record is not complete yet.
return nil, b, nil
}
return b[:tlsRecordHeaderSize+length], b[tlsRecordHeaderSize+length:], nil
}
// splitAndValidateHeader splits the header from the payload in the TLS 1.3
// record and returns them. Note that the header is checked for validity, and an
// error is returned when an invalid header is parsed. Also note that this
// function does not allocate or copy any buffers.
func splitAndValidateHeader(record []byte) (header, payload []byte, err error) {
if len(record) < tlsRecordHeaderSize {
return nil, nil, fmt.Errorf("record was smaller than the header size")
}
header = record[:tlsRecordHeaderSize]
payload = record[tlsRecordHeaderSize:]
if header[0] != tlsApplicationData {
return nil, nil, fmt.Errorf("incorrect type in the header")
}
// Check the legacy record version, which should be 0x03, 0x03.
if header[1] != 0x03 || header[2] != 0x03 {
return nil, nil, fmt.Errorf("incorrect legacy record version in the header")
}
return header, payload, nil
}
// handleAlertMessage handles an alert message.
func (p *conn) handleAlertMessage() error {
if len(p.pendingApplicationData) != tlsAlertSize {
return errors.New("invalid alert message size")
}
alertType := p.pendingApplicationData[1]
// Clear the body of the alert message.
p.pendingApplicationData = p.pendingApplicationData[:0]
if alertType == byte(closeNotify) {
return errors.New("received a close notify alert")
}
// TODO(matthewstevenson88): Add support for more alert types.
return fmt.Errorf("received an unrecognized alert type: %v", alertType)
}
// parseHandshakeHeader parses a handshake message from the handshake buffer.
// It returns the message type, the message length, the message, the raw message
// that includes the type and length bytes and a flag indicating whether the
// handshake message has been fully parsed. i.e. whether the entire handshake
// message was in the handshake buffer.
func (p *conn) parseHandshakeMsg() (msgType byte, msgLen uint32, msg []byte, rawMsg []byte, ok bool) {
// Handle the case where the 4 byte handshake header is fragmented.
if len(p.handshakeBuf) < tlsHandshakePrefixSize {
return 0, 0, nil, nil, false
}
msgType = p.handshakeBuf[0]
msgLen = bigEndianInt24(p.handshakeBuf[tlsHandshakeMsgTypeSize : tlsHandshakeMsgTypeSize+tlsHandshakeLengthSize])
if msgLen > uint32(len(p.handshakeBuf)-tlsHandshakePrefixSize) {
return 0, 0, nil, nil, false
}
msg = p.handshakeBuf[tlsHandshakePrefixSize : tlsHandshakePrefixSize+msgLen]
rawMsg = p.handshakeBuf[:tlsHandshakeMsgTypeSize+tlsHandshakeLengthSize+msgLen]
p.handshakeBuf = p.handshakeBuf[tlsHandshakePrefixSize+msgLen:]
return msgType, msgLen, msg, rawMsg, true
}
// handleHandshakeMessage handles a handshake message. Note that the first
// complete handshake message from the handshake buffer is removed, if it
// exists.
func (p *conn) handleHandshakeMessage() error {
// Copy the pending application data to the handshake buffer. At this point,
// we are guaranteed that the pending application data contains only parts
// of a handshake message.
p.handshakeBuf = append(p.handshakeBuf, p.pendingApplicationData...)
p.pendingApplicationData = p.pendingApplicationData[:0]
// Several handshake messages may be coalesced into a single record.
// Continue reading them until the handshake buffer is empty.
for len(p.handshakeBuf) > 0 {
handshakeMsgType, msgLen, msg, rawMsg, ok := p.parseHandshakeMsg()
if !ok {
// The handshake could not be fully parsed, so read in another
// record and try again later.
break
}
switch handshakeMsgType {
case tlsHandshakeKeyUpdateType:
if msgLen != tlsHandshakeKeyUpdateMsgSize {
return errors.New("invalid handshake key update message length")
}
if len(p.handshakeBuf) != 0 {
return errors.New("key update message must be the last message of a handshake record")
}
if err := p.handleKeyUpdateMsg(msg); err != nil {
return err
}
case tlsHandshakeNewSessionTicketType:
// Ignore tickets that are received after a batch of tickets has
// been sent to S2A.
if p.ticketState == notReceivingTickets {
continue
}
if p.ticketState == ticketsNotYetReceived {
p.ticketState = receivingTickets
}
p.sessionTickets = append(p.sessionTickets, rawMsg)
if len(p.sessionTickets) == maxAllowedTickets {
p.ticketState = notReceivingTickets
grpclog.Infof("Sending session tickets to S2A.")
p.ticketSender.sendTicketsToS2A(p.sessionTickets, p.callComplete)
}
default:
return errors.New("unknown handshake message type")
}
}
return nil
}
func buildKeyUpdateRequest() []byte {
b := make([]byte, tlsHandshakePrefixSize+tlsHandshakeKeyUpdateMsgSize)
b[0] = tlsHandshakeKeyUpdateType
b[1] = 0
b[2] = 0
b[3] = tlsHandshakeKeyUpdateMsgSize
b[4] = byte(updateNotRequested)
return b
}
// handleKeyUpdateMsg handles a key update message.
func (p *conn) handleKeyUpdateMsg(msg []byte) error {
keyUpdateRequest := msg[0]
if keyUpdateRequest != byte(updateNotRequested) &&
keyUpdateRequest != byte(updateRequested) {
return errors.New("invalid handshake key update message")
}
if err := p.inConn.UpdateKey(); err != nil {
return err
}
// Send a key update message back to the peer if requested.
if keyUpdateRequest == byte(updateRequested) {
p.writeMutex.Lock()
defer p.writeMutex.Unlock()
n, err := p.writeTLSRecord(preConstructedKeyUpdateMsg, byte(handshake))
if err != nil {
return err
}
if n != tlsHandshakePrefixSize+tlsHandshakeKeyUpdateMsgSize {
return errors.New("key update request message wrote less bytes than expected")
}
if err = p.outConn.UpdateKey(); err != nil {
return err
}
}
return nil
}
// bidEndianInt24 converts the given byte buffer of at least size 3 and
// outputs the resulting 24 bit integer as a uint32. This is needed because
// TLS 1.3 requires 3 byte integers, and the binary.BigEndian package does
// not provide a way to transform a byte buffer into a 3 byte integer.
func bigEndianInt24(b []byte) uint32 {
_ = b[2] // bounds check hint to compiler; see golang.org/issue/14808
return uint32(b[2]) | uint32(b[1])<<8 | uint32(b[0])<<16
}
func min(a, b int) int {
if a < b {
return a
}
return b
}

View File

@@ -0,0 +1,178 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package record
import (
"context"
"fmt"
"sync"
"time"
"github.com/google/s2a-go/internal/handshaker/service"
commonpb "github.com/google/s2a-go/internal/proto/common_go_proto"
s2apb "github.com/google/s2a-go/internal/proto/s2a_go_proto"
"github.com/google/s2a-go/internal/tokenmanager"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
)
// sessionTimeout is the timeout for creating a session with the S2A handshaker
// service.
const sessionTimeout = time.Second * 5
// s2aTicketSender sends session tickets to the S2A handshaker service.
type s2aTicketSender interface {
// sendTicketsToS2A sends the given session tickets to the S2A handshaker
// service.
sendTicketsToS2A(sessionTickets [][]byte, callComplete chan bool)
}
// ticketStream is the stream used to send and receive session information.
type ticketStream interface {
Send(*s2apb.SessionReq) error
Recv() (*s2apb.SessionResp, error)
}
type ticketSender struct {
// hsAddr stores the address of the S2A handshaker service.
hsAddr string
// connectionID is the connection identifier that was created and sent by
// S2A at the end of a handshake.
connectionID uint64
// localIdentity is the local identity that was used by S2A during session
// setup and included in the session result.
localIdentity *commonpb.Identity
// tokenManager manages access tokens for authenticating to S2A.
tokenManager tokenmanager.AccessTokenManager
// ensureProcessSessionTickets allows users to wait and ensure that all
// available session tickets are sent to S2A before a process completes.
ensureProcessSessionTickets *sync.WaitGroup
}
// sendTicketsToS2A sends the given sessionTickets to the S2A handshaker
// service. This is done asynchronously and writes to the error logs if an error
// occurs.
func (t *ticketSender) sendTicketsToS2A(sessionTickets [][]byte, callComplete chan bool) {
// Note that the goroutine is in the function rather than at the caller
// because the fake ticket sender used for testing must run synchronously
// so that the session tickets can be accessed from it after the tests have
// been run.
if t.ensureProcessSessionTickets != nil {
t.ensureProcessSessionTickets.Add(1)
}
go func() {
if err := func() error {
defer func() {
if t.ensureProcessSessionTickets != nil {
t.ensureProcessSessionTickets.Done()
}
}()
ctx, cancel := context.WithTimeout(context.Background(), sessionTimeout)
defer cancel()
// The transportCreds only needs to be set when talking to S2AV2 and also
// if mTLS is required.
hsConn, err := service.Dial(ctx, t.hsAddr, nil)
if err != nil {
return err
}
client := s2apb.NewS2AServiceClient(hsConn)
session, err := client.SetUpSession(ctx)
if err != nil {
return err
}
defer func() {
if err := session.CloseSend(); err != nil {
grpclog.Error(err)
}
}()
return t.writeTicketsToStream(session, sessionTickets)
}(); err != nil {
grpclog.Errorf("failed to send resumption tickets to S2A with identity: %v, %v",
t.localIdentity, err)
}
callComplete <- true
close(callComplete)
}()
}
// writeTicketsToStream writes the given session tickets to the given stream.
func (t *ticketSender) writeTicketsToStream(stream ticketStream, sessionTickets [][]byte) error {
if err := stream.Send(
&s2apb.SessionReq{
ReqOneof: &s2apb.SessionReq_ResumptionTicket{
ResumptionTicket: &s2apb.ResumptionTicketReq{
InBytes: sessionTickets,
ConnectionId: t.connectionID,
LocalIdentity: t.localIdentity,
},
},
AuthMechanisms: t.getAuthMechanisms(),
},
); err != nil {
return err
}
sessionResp, err := stream.Recv()
if err != nil {
return err
}
if sessionResp.GetStatus().GetCode() != uint32(codes.OK) {
return fmt.Errorf("s2a session ticket response had error status: %v, %v",
sessionResp.GetStatus().GetCode(), sessionResp.GetStatus().GetDetails())
}
return nil
}
func (t *ticketSender) getAuthMechanisms() []*s2apb.AuthenticationMechanism {
if t.tokenManager == nil {
return nil
}
// First handle the special case when no local identity has been provided
// by the application. In this case, an AuthenticationMechanism with no local
// identity will be sent.
if t.localIdentity == nil {
token, err := t.tokenManager.DefaultToken()
if err != nil {
grpclog.Infof("unable to get token for empty local identity: %v", err)
return nil
}
return []*s2apb.AuthenticationMechanism{
{
MechanismOneof: &s2apb.AuthenticationMechanism_Token{
Token: token,
},
},
}
}
// Next, handle the case where the application (or the S2A) has specified
// a local identity.
token, err := t.tokenManager.Token(t.localIdentity)
if err != nil {
grpclog.Infof("unable to get token for local identity %v: %v", t.localIdentity, err)
return nil
}
return []*s2apb.AuthenticationMechanism{
{
Identity: t.localIdentity,
MechanismOneof: &s2apb.AuthenticationMechanism_Token{
Token: token,
},
},
}
}

View File

@@ -0,0 +1,70 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package tokenmanager provides tokens for authenticating to S2A.
package tokenmanager
import (
"fmt"
"os"
commonpb "github.com/google/s2a-go/internal/proto/common_go_proto"
)
const (
s2aAccessTokenEnvironmentVariable = "S2A_ACCESS_TOKEN"
)
// AccessTokenManager manages tokens for authenticating to S2A.
type AccessTokenManager interface {
// DefaultToken returns a token that an application with no specified local
// identity must use to authenticate to S2A.
DefaultToken() (token string, err error)
// Token returns a token that an application with local identity equal to
// identity must use to authenticate to S2A.
Token(identity *commonpb.Identity) (token string, err error)
}
type singleTokenAccessTokenManager struct {
token string
}
// NewSingleTokenAccessTokenManager returns a new AccessTokenManager instance
// that will always manage the same token.
//
// The token to be managed is read from the s2aAccessTokenEnvironmentVariable
// environment variable. If this environment variable is not set, then this
// function returns an error.
func NewSingleTokenAccessTokenManager() (AccessTokenManager, error) {
token, variableExists := os.LookupEnv(s2aAccessTokenEnvironmentVariable)
if !variableExists {
return nil, fmt.Errorf("%s environment variable is not set", s2aAccessTokenEnvironmentVariable)
}
return &singleTokenAccessTokenManager{token: token}, nil
}
// DefaultToken always returns the token managed by the
// singleTokenAccessTokenManager.
func (m *singleTokenAccessTokenManager) DefaultToken() (string, error) {
return m.token, nil
}
// Token always returns the token managed by the singleTokenAccessTokenManager.
func (m *singleTokenAccessTokenManager) Token(*commonpb.Identity) (string, error) {
return m.token, nil
}

View File

@@ -0,0 +1 @@
**This directory has the implementation of the S2Av2's gRPC-Go client libraries**

View File

@@ -0,0 +1,122 @@
/*
*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package certverifier offloads verifications to S2Av2.
package certverifier
import (
"crypto/x509"
"fmt"
"github.com/google/s2a-go/stream"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
s2av2pb "github.com/google/s2a-go/internal/proto/v2/s2a_go_proto"
)
// VerifyClientCertificateChain builds a SessionReq, sends it to S2Av2 and
// receives a SessionResp.
func VerifyClientCertificateChain(verificationMode s2av2pb.ValidatePeerCertificateChainReq_VerificationMode, s2AStream stream.S2AStream) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// Offload verification to S2Av2.
if grpclog.V(1) {
grpclog.Infof("Sending request to S2Av2 for client peer cert chain validation.")
}
if err := s2AStream.Send(&s2av2pb.SessionReq{
ReqOneof: &s2av2pb.SessionReq_ValidatePeerCertificateChainReq{
ValidatePeerCertificateChainReq: &s2av2pb.ValidatePeerCertificateChainReq{
Mode: verificationMode,
PeerOneof: &s2av2pb.ValidatePeerCertificateChainReq_ClientPeer_{
ClientPeer: &s2av2pb.ValidatePeerCertificateChainReq_ClientPeer{
CertificateChain: rawCerts,
},
},
},
},
}); err != nil {
grpclog.Infof("Failed to send request to S2Av2 for client peer cert chain validation.")
return err
}
// Get the response from S2Av2.
resp, err := s2AStream.Recv()
if err != nil {
grpclog.Infof("Failed to receive client peer cert chain validation response from S2Av2.")
return err
}
// Parse the response.
if (resp.GetStatus() != nil) && (resp.GetStatus().Code != uint32(codes.OK)) {
return fmt.Errorf("failed to offload client cert verification to S2A: %d, %v", resp.GetStatus().Code, resp.GetStatus().Details)
}
if resp.GetValidatePeerCertificateChainResp().ValidationResult != s2av2pb.ValidatePeerCertificateChainResp_SUCCESS {
return fmt.Errorf("client cert verification failed: %v", resp.GetValidatePeerCertificateChainResp().ValidationDetails)
}
return nil
}
}
// VerifyServerCertificateChain builds a SessionReq, sends it to S2Av2 and
// receives a SessionResp.
func VerifyServerCertificateChain(hostname string, verificationMode s2av2pb.ValidatePeerCertificateChainReq_VerificationMode, s2AStream stream.S2AStream, serverAuthorizationPolicy []byte) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
// Offload verification to S2Av2.
if grpclog.V(1) {
grpclog.Infof("Sending request to S2Av2 for server peer cert chain validation.")
}
if err := s2AStream.Send(&s2av2pb.SessionReq{
ReqOneof: &s2av2pb.SessionReq_ValidatePeerCertificateChainReq{
ValidatePeerCertificateChainReq: &s2av2pb.ValidatePeerCertificateChainReq{
Mode: verificationMode,
PeerOneof: &s2av2pb.ValidatePeerCertificateChainReq_ServerPeer_{
ServerPeer: &s2av2pb.ValidatePeerCertificateChainReq_ServerPeer{
CertificateChain: rawCerts,
ServerHostname: hostname,
SerializedUnrestrictedClientPolicy: serverAuthorizationPolicy,
},
},
},
},
}); err != nil {
grpclog.Infof("Failed to send request to S2Av2 for server peer cert chain validation.")
return err
}
// Get the response from S2Av2.
resp, err := s2AStream.Recv()
if err != nil {
grpclog.Infof("Failed to receive server peer cert chain validation response from S2Av2.")
return err
}
// Parse the response.
if (resp.GetStatus() != nil) && (resp.GetStatus().Code != uint32(codes.OK)) {
return fmt.Errorf("failed to offload server cert verification to S2A: %d, %v", resp.GetStatus().Code, resp.GetStatus().Details)
}
if resp.GetValidatePeerCertificateChainResp().ValidationResult != s2av2pb.ValidatePeerCertificateChainResp_SUCCESS {
return fmt.Errorf("server cert verification failed: %v", resp.GetValidatePeerCertificateChainResp().ValidationDetails)
}
return nil
}
}

View File

@@ -0,0 +1,186 @@
/*
*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package remotesigner offloads private key operations to S2Av2.
package remotesigner
import (
"crypto"
"crypto/rsa"
"crypto/x509"
"fmt"
"io"
"github.com/google/s2a-go/stream"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
s2av2pb "github.com/google/s2a-go/internal/proto/v2/s2a_go_proto"
)
// remoteSigner implementes the crypto.Signer interface.
type remoteSigner struct {
leafCert *x509.Certificate
s2AStream stream.S2AStream
}
// New returns an instance of RemoteSigner, an implementation of the
// crypto.Signer interface.
func New(leafCert *x509.Certificate, s2AStream stream.S2AStream) crypto.Signer {
return &remoteSigner{leafCert, s2AStream}
}
func (s *remoteSigner) Public() crypto.PublicKey {
return s.leafCert.PublicKey
}
func (s *remoteSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
signatureAlgorithm, err := getSignatureAlgorithm(opts, s.leafCert)
if err != nil {
return nil, err
}
req, err := getSignReq(signatureAlgorithm, digest)
if err != nil {
return nil, err
}
if grpclog.V(1) {
grpclog.Infof("Sending request to S2Av2 for signing operation.")
}
if err := s.s2AStream.Send(&s2av2pb.SessionReq{
ReqOneof: &s2av2pb.SessionReq_OffloadPrivateKeyOperationReq{
OffloadPrivateKeyOperationReq: req,
},
}); err != nil {
grpclog.Infof("Failed to send request to S2Av2 for signing operation.")
return nil, err
}
resp, err := s.s2AStream.Recv()
if err != nil {
grpclog.Infof("Failed to receive signing operation response from S2Av2.")
return nil, err
}
if (resp.GetStatus() != nil) && (resp.GetStatus().Code != uint32(codes.OK)) {
return nil, fmt.Errorf("failed to offload signing with private key to S2A: %d, %v", resp.GetStatus().Code, resp.GetStatus().Details)
}
return resp.GetOffloadPrivateKeyOperationResp().GetOutBytes(), nil
}
// getCert returns the leafCert field in s.
func (s *remoteSigner) getCert() *x509.Certificate {
return s.leafCert
}
// getStream returns the s2AStream field in s.
func (s *remoteSigner) getStream() stream.S2AStream {
return s.s2AStream
}
func getSignReq(signatureAlgorithm s2av2pb.SignatureAlgorithm, digest []byte) (*s2av2pb.OffloadPrivateKeyOperationReq, error) {
if (signatureAlgorithm == s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_RSA_PKCS1_SHA256) || (signatureAlgorithm == s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_ECDSA_SECP256R1_SHA256) || (signatureAlgorithm == s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_RSA_PSS_RSAE_SHA256) {
return &s2av2pb.OffloadPrivateKeyOperationReq{
Operation: s2av2pb.OffloadPrivateKeyOperationReq_SIGN,
SignatureAlgorithm: signatureAlgorithm,
InBytes: &s2av2pb.OffloadPrivateKeyOperationReq_Sha256Digest{
Sha256Digest: digest,
},
}, nil
} else if (signatureAlgorithm == s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_RSA_PKCS1_SHA384) || (signatureAlgorithm == s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_ECDSA_SECP384R1_SHA384) || (signatureAlgorithm == s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_RSA_PSS_RSAE_SHA384) {
return &s2av2pb.OffloadPrivateKeyOperationReq{
Operation: s2av2pb.OffloadPrivateKeyOperationReq_SIGN,
SignatureAlgorithm: signatureAlgorithm,
InBytes: &s2av2pb.OffloadPrivateKeyOperationReq_Sha384Digest{
Sha384Digest: digest,
},
}, nil
} else if (signatureAlgorithm == s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_RSA_PKCS1_SHA512) || (signatureAlgorithm == s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_ECDSA_SECP521R1_SHA512) || (signatureAlgorithm == s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_RSA_PSS_RSAE_SHA512) || (signatureAlgorithm == s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_ED25519) {
return &s2av2pb.OffloadPrivateKeyOperationReq{
Operation: s2av2pb.OffloadPrivateKeyOperationReq_SIGN,
SignatureAlgorithm: signatureAlgorithm,
InBytes: &s2av2pb.OffloadPrivateKeyOperationReq_Sha512Digest{
Sha512Digest: digest,
},
}, nil
} else {
return nil, fmt.Errorf("unknown signature algorithm: %v", signatureAlgorithm)
}
}
// getSignatureAlgorithm returns the signature algorithm that S2A must use when
// performing a signing operation that has been offloaded by an application
// using the crypto/tls libraries.
func getSignatureAlgorithm(opts crypto.SignerOpts, leafCert *x509.Certificate) (s2av2pb.SignatureAlgorithm, error) {
if opts == nil || leafCert == nil {
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_UNSPECIFIED, fmt.Errorf("unknown signature algorithm")
}
switch leafCert.PublicKeyAlgorithm {
case x509.RSA:
if rsaPSSOpts, ok := opts.(*rsa.PSSOptions); ok {
return rsaPSSAlgorithm(rsaPSSOpts)
}
return rsaPPKCS1Algorithm(opts)
case x509.ECDSA:
return ecdsaAlgorithm(opts)
case x509.Ed25519:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_ED25519, nil
default:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_UNSPECIFIED, fmt.Errorf("unknown signature algorithm: %q", leafCert.PublicKeyAlgorithm)
}
}
func rsaPSSAlgorithm(opts *rsa.PSSOptions) (s2av2pb.SignatureAlgorithm, error) {
switch opts.HashFunc() {
case crypto.SHA256:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_RSA_PSS_RSAE_SHA256, nil
case crypto.SHA384:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_RSA_PSS_RSAE_SHA384, nil
case crypto.SHA512:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_RSA_PSS_RSAE_SHA512, nil
default:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_UNSPECIFIED, fmt.Errorf("unknown signature algorithm")
}
}
func rsaPPKCS1Algorithm(opts crypto.SignerOpts) (s2av2pb.SignatureAlgorithm, error) {
switch opts.HashFunc() {
case crypto.SHA256:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_RSA_PKCS1_SHA256, nil
case crypto.SHA384:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_RSA_PKCS1_SHA384, nil
case crypto.SHA512:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_RSA_PKCS1_SHA512, nil
default:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_UNSPECIFIED, fmt.Errorf("unknown signature algorithm")
}
}
func ecdsaAlgorithm(opts crypto.SignerOpts) (s2av2pb.SignatureAlgorithm, error) {
switch opts.HashFunc() {
case crypto.SHA256:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_ECDSA_SECP256R1_SHA256, nil
case crypto.SHA384:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_ECDSA_SECP384R1_SHA384, nil
case crypto.SHA512:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_ECDSA_SECP521R1_SHA512, nil
default:
return s2av2pb.SignatureAlgorithm_S2A_SSL_SIGN_UNSPECIFIED, fmt.Errorf("unknown signature algorithm")
}
}

Binary file not shown.

View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID8TCCAtmgAwIBAgIUKXNlBRVe6UepjQUijIFPZBd/4qYwDQYJKoZIhvcNAQEL
BQAwgYcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU3Vubnl2
YWxlMRAwDgYDVQQKDAdDb21wYW55MREwDwYDVQQLDAhEaXZpc2lvbjEWMBQGA1UE
AwwNczJhX3Rlc3RfY2VydDEaMBgGCSqGSIb3DQEJARYLeHl6QHh5ei5jb20wHhcN
MjIwNTMxMjAwMzE1WhcNNDIwNTI2MjAwMzE1WjCBhzELMAkGA1UEBhMCVVMxCzAJ
BgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxEDAOBgNVBAoMB0NvbXBhbnkx
ETAPBgNVBAsMCERpdmlzaW9uMRYwFAYDVQQDDA1zMmFfdGVzdF9jZXJ0MRowGAYJ
KoZIhvcNAQkBFgt4eXpAeHl6LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAOOFuIucH7XXfohGxKd3uR/ihUA/LdduR9I8kfpUEbq5BOt8xZe5/Yn9
a1ozEHVW6cOAbHbnwAR8tkSgZ/t42QIA2k77HWU1Jh2xiEIsJivo3imm4/kZWuR0
OqPh7MhzxpR/hvNwpI5mJsAVBWFMa5KtecFZLnyZtwHylrRN1QXzuLrOxuKFufK3
RKbTABScn5RbZL976H/jgfSeXrbt242NrIoBnVe6fRbekbq2DQ6zFArbQMUgHjHK
P0UqBgdr1QmHfi9KytFyx9BTP3gXWnWIu+bY7/v7qKJMHFwGETo+dCLWYevJL316
HnLfhApDMfP8U+Yv/y1N/YvgaSOSlEcCAwEAAaNTMFEwHQYDVR0OBBYEFKhAU4nu
0h/lrnggbIGvx4ej0WklMB8GA1UdIwQYMBaAFKhAU4nu0h/lrnggbIGvx4ej0Wkl
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAE/6NghzQ5fu6yR6
EHKbj/YMrFdT7aGn5n2sAf7wJ33LIhiFHkpWBsVlm7rDtZtwhe891ZK/P60anlg9
/P0Ua53tSRVRmCvTnEbXWOVMN4is6MsR7BlmzUxl4AtIn7jbeifEwRL7B4xDYmdA
QrQnsqoz45dLgS5xK4WDqXATP09Q91xQDuhud/b+A4jrvgwFASmL7rMIZbp4f1JQ
nlnl/9VoTBQBvJiWkDUtQDMpRLtauddEkv4AGz75p5IspXWD6cOemuh2iQec11xD
X20rs2WZbAcAiUa3nmy8OKYw435vmpj8gp39WYbX/Yx9TymrFFbVY92wYn+quTco
pKklVz0=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA44W4i5wftdd+iEbEp3e5H+KFQD8t125H0jyR+lQRurkE63zF
l7n9if1rWjMQdVbpw4BsdufABHy2RKBn+3jZAgDaTvsdZTUmHbGIQiwmK+jeKabj
+Rla5HQ6o+HsyHPGlH+G83CkjmYmwBUFYUxrkq15wVkufJm3AfKWtE3VBfO4us7G
4oW58rdEptMAFJyflFtkv3vof+OB9J5etu3bjY2sigGdV7p9Ft6RurYNDrMUCttA
xSAeMco/RSoGB2vVCYd+L0rK0XLH0FM/eBdadYi75tjv+/uookwcXAYROj50ItZh
68kvfXoect+ECkMx8/xT5i//LU39i+BpI5KURwIDAQABAoIBABgyjo/6iLzUMFbZ
/+w3pW6orrdIgN2akvTfED9pVYFgUA+jc3hRhY95bkNnjuaL2cy7Cc4Tk65mfRQL
Y0OxdJLr+EvSFSxAXM9npDA1ddHRsF8JqtFBSxNk8R+g1Yf0GDiO35Fgd3/ViWWA
VtQkRoSRApP3oiQKTRZd8H04keFR+PvmDk/Lq11l3Kc24A1PevKIPX1oI990ggw9
9i4uSV+cnuMxmcI9xxJtgwdDFdjr39l2arLOHr4s6LGoV2IOdXHNlv5xRqWUZ0FH
MDHowkLgwDrdSTnNeaVNkce14Gqx+bd4hNaLCdKXMpedBTEmrut3f3hdV1kKjaKt
aqRYr8ECgYEA/YDGZY2jvFoHHBywlqmEMFrrCvQGH51m5R1Ntpkzr+Rh3YCmrpvq
xgwJXING0PUw3dz+xrH5lJICrfNE5Kt3fPu1rAEy+13mYsNowghtUq2Rtu0Hsjjx
2E3Bf8vEB6RNBMmGkUpTTIAroGF5tpJoRvfnWax+k4pFdrKYFtyZdNcCgYEA5cNv
EPltvOobjTXlUmtVP3n27KZN2aXexTcagLzRxE9CV4cYySENl3KuOMmccaZpIl6z
aHk6BT4X+M0LqElNUczrInfVqI+SGAFLGy7W6CJaqSr6cpyFUP/fosKpm6wKGgLq
udHfpvz5rckhKd8kJxFLvhGOK9yN5qpzih0gfhECgYAJfwRvk3G5wYmYpP58dlcs
VIuPenqsPoI3PPTHTU/hW+XKnWIhElgmGRdUrto9Q6IT/Y5RtSMLTLjq+Tzwb/fm
56rziYv2XJsfwgAvnI8z1Kqrto9ePsHYf3krJ1/thVsZPc9bq/QY3ohD1sLvcuaT
GgBBnLOVJU3a12/ZE2RwOwKBgF0csWMAoj8/5IB6if+3ral2xOGsl7oPZVMo/J2V
Z7EVqb4M6rd/pKFugTpUQgkwtkSOekhpcGD1hAN5HTNK2YG/+L5UMAsKe9sskwJm
HgOfAHy0BSDzW3ey6i9skg2bT9Cww+0gJ3Hl7U1HSCBO5LjMYpSZSrNtwzfqdb5Q
BX3xAoGARZdR28Ej3+/+0+fz47Yu2h4z0EI/EbrudLOWY936jIeAVwHckI3+BuqH
qR4poj1gfbnMxNuI9UzIXzjEmGewx9kDZ7IYnvloZKqoVQODO5GlKF2ja6IcMNlh
GCNdD6PSAS6HcmalmWo9sj+1YMkrl+GJikKZqVBHrHNwMGAG67w=
-----END RSA PRIVATE KEY-----

Binary file not shown.

View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID8TCCAtmgAwIBAgIUKCoDuLtiZXvhsBY2RoDm0ugizJ8wDQYJKoZIhvcNAQEL
BQAwgYcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU3Vubnl2
YWxlMRAwDgYDVQQKDAdDb21wYW55MREwDwYDVQQLDAhEaXZpc2lvbjEWMBQGA1UE
AwwNczJhX3Rlc3RfY2VydDEaMBgGCSqGSIb3DQEJARYLeHl6QHh5ei5jb20wHhcN
MjIwNTMxMjAwODI1WhcNNDIwNTI2MjAwODI1WjCBhzELMAkGA1UEBhMCVVMxCzAJ
BgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxEDAOBgNVBAoMB0NvbXBhbnkx
ETAPBgNVBAsMCERpdmlzaW9uMRYwFAYDVQQDDA1zMmFfdGVzdF9jZXJ0MRowGAYJ
KoZIhvcNAQkBFgt4eXpAeHl6LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAKK1++PXQ+M3hjYH/v0K4UEYl5ljzpNM1i52eQM+gFooojT87PDSaphT
fs0PXy/PTAjHBEvPhWpOpmQXfJNYzjwcCvg66hbqkv++/VTZiFLAsHagzkEz+FRJ
qT5Eq7G5FLyw1izX1uxyPN7tAEWEEg7eqsiaXD3Cq8+TYN9cjirPeF7RZF8yFCYE
xqvbo+Yc6RL6xw19iXVTfctRgQe581KQuIY5/LXo3dWDEilFdsADAe8XAEcO64es
Ow0g1UvXLnpXSE151kXBFb3sKH/ZjCecDYMCIMEb4sWLSblkSxJ5sNSmXIG4wtr2
Qnii7CXZgnVYraQE/Jyh+NMQANuoSdMCAwEAAaNTMFEwHQYDVR0OBBYEFAyQQQuM
ab+YUQqjK8dVVOoHVFmXMB8GA1UdIwQYMBaAFAyQQQuMab+YUQqjK8dVVOoHVFmX
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADj0vQ6ykWhicoqR
e6VZMwlEJV7/DSvWWKBd9MUjfKye0A4565ya5lmnzP3DiD3nqGe3miqmLsXKDs+X
POqlPXTWIamP7D4MJ32XtSLwZB4ru+I+Ao/P/VngPepoRPQoBnzHe7jww0rokqxl
AZERjlbTUwUAy/BPWPSzSJZ2j0tcs6ZLDNyYzpK4ao8R9/1VmQ92Tcp3feJs1QTg
odRQc3om/AkWOwsll+oyX0UbJeHkFHiLanUPXbdh+/BkSvZJ8ynL+feSDdaurPe+
PSfnqLtQft9/neecGRdEaQzzzSFVQUVQzTdK1Q7hA7b55b2HvIa3ktDiks+sJsYN
Dhm6uZM=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAorX749dD4zeGNgf+/QrhQRiXmWPOk0zWLnZ5Az6AWiiiNPzs
8NJqmFN+zQ9fL89MCMcES8+Fak6mZBd8k1jOPBwK+DrqFuqS/779VNmIUsCwdqDO
QTP4VEmpPkSrsbkUvLDWLNfW7HI83u0ARYQSDt6qyJpcPcKrz5Ng31yOKs94XtFk
XzIUJgTGq9uj5hzpEvrHDX2JdVN9y1GBB7nzUpC4hjn8tejd1YMSKUV2wAMB7xcA
Rw7rh6w7DSDVS9cueldITXnWRcEVvewof9mMJ5wNgwIgwRvixYtJuWRLEnmw1KZc
gbjC2vZCeKLsJdmCdVitpAT8nKH40xAA26hJ0wIDAQABAoIBACaNR+lsD8G+XiZf
LqN1+HkcAo9tfnyYMAdCOtnx7SdviT9Uzi8hK/B7mAeuJLeHPlS2EuaDfPD7QaFl
jza6S+MiIdc+3kgfvESsVAnOoOY6kZUJ9NSuI6CU82y1iJjLaYZrv9NQMLRFPPb0
4KOX709mosB1EnXvshW0rbc+jtDFhrm1SxMt+k9TuzmMxjbOeW4LOLXPgU8X1T3Q
Xy0hMZZtcgBs9wFIo8yCtmOixax9pnFE8rRltgDxTodn9LLdz1FieyntNgDksZ0P
nt4kV7Mqly7ELaea+Foaj244mKsesic2e3GhAlMRLun/VSunSf7mOCxfpITB8dp1
drDhOYECgYEA19151dVxRcviuovN6Dar+QszMTnU8pDJ8BjLFjXjP/hNBBwMTHDE
duMuWk2qnwZqMooI/shxrF/ufmTgS0CFrh2+ANBZu27vWConJNXcyNtdigI4wt50
L0Y2qcZn2mg67qFXHwoR3QNwrwnPwEjRXA09at9CSRZzcwDQ0ETXhYsCgYEAwPaG
06QdK8Zyly7TTzZJwxzv9uGiqzodmGtX6NEKjgij2JaCxHpukqZBJoqa0jKeK1cm
eNVkOvT5ff9TMzarSHQLr3pZen2/oVLb5gaFkbcJt/klv9Fd+ZRilHY3i6QwS6pD
uMiPOWS4DrLHDRVoVlAZTDjT1RVwwTs+P2NhJdkCgYEAsriXysbxBYyMp05gqEW7
lHIFbFgpSrs9th+Q5U6wW6JEgYaHWDJ1NslY80MiZI93FWjbkbZ7BvBWESeL3EIL
a+EMErht0pVCbIhZ6FF4foPAqia0wAJVx14mm+G80kNBp5jE/NnleEsE3KcO7nBb
hg8gLn+x7bk81JZ0TDrzBYkCgYEAuQKluv47SeF3tSScTfKLPpvcKCWmxe1uutkQ
7JShPhVioyOMNb39jnYBOWbjkm4d4QgqRuiytSR0oi3QI+Ziy5EYMyNn713qAk9j
r2TJZDDPDKnBW+zt4YI4EohWMXk3JRUW4XDKggjjwJQA7bZ812TtHHvP/xoThfG7
eSNb3eECgYBw6ssgCtMrdvQiEmjKVX/9yI38mvC2kSGyzbrQnGUfgqRGomRpeZuD
B5E3kysA4td5pT5lvcLgSW0TbOz+YbiriXjwOihPIelCvc9gE2eOUI71/byUWPFz
7u5F/xQ4NaGr5suLF+lBC6h7pSbM4El9lIHQAQadpuEdzHqrw+hs3g==
-----END RSA PRIVATE KEY-----

391
vendor/github.com/google/s2a-go/internal/v2/s2av2.go generated vendored Normal file
View File

@@ -0,0 +1,391 @@
/*
*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package v2 provides the S2Av2 transport credentials used by a gRPC
// application.
package v2
import (
"context"
"crypto/tls"
"errors"
"net"
"os"
"time"
"github.com/golang/protobuf/proto"
"github.com/google/s2a-go/fallback"
"github.com/google/s2a-go/internal/handshaker/service"
"github.com/google/s2a-go/internal/tokenmanager"
"github.com/google/s2a-go/internal/v2/tlsconfigstore"
"github.com/google/s2a-go/retry"
"github.com/google/s2a-go/stream"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
commonpbv1 "github.com/google/s2a-go/internal/proto/common_go_proto"
s2av2pb "github.com/google/s2a-go/internal/proto/v2/s2a_go_proto"
)
const (
s2aSecurityProtocol = "tls"
defaultS2ATimeout = 6 * time.Second
)
// An environment variable, which sets the timeout enforced on the connection to the S2A service for handshake.
const s2aTimeoutEnv = "S2A_TIMEOUT"
type s2av2TransportCreds struct {
info *credentials.ProtocolInfo
isClient bool
serverName string
s2av2Address string
transportCreds credentials.TransportCredentials
tokenManager *tokenmanager.AccessTokenManager
// localIdentity should only be used by the client.
localIdentity *commonpbv1.Identity
// localIdentities should only be used by the server.
localIdentities []*commonpbv1.Identity
verificationMode s2av2pb.ValidatePeerCertificateChainReq_VerificationMode
fallbackClientHandshake fallback.ClientHandshake
getS2AStream func(ctx context.Context, s2av2Address string) (stream.S2AStream, error)
serverAuthorizationPolicy []byte
}
// NewClientCreds returns a client-side transport credentials object that uses
// the S2Av2 to establish a secure connection with a server.
func NewClientCreds(s2av2Address string, transportCreds credentials.TransportCredentials, localIdentity *commonpbv1.Identity, verificationMode s2av2pb.ValidatePeerCertificateChainReq_VerificationMode, fallbackClientHandshakeFunc fallback.ClientHandshake, getS2AStream func(ctx context.Context, s2av2Address string) (stream.S2AStream, error), serverAuthorizationPolicy []byte) (credentials.TransportCredentials, error) {
// Create an AccessTokenManager instance to use to authenticate to S2Av2.
accessTokenManager, err := tokenmanager.NewSingleTokenAccessTokenManager()
creds := &s2av2TransportCreds{
info: &credentials.ProtocolInfo{
SecurityProtocol: s2aSecurityProtocol,
},
isClient: true,
serverName: "",
s2av2Address: s2av2Address,
transportCreds: transportCreds,
localIdentity: localIdentity,
verificationMode: verificationMode,
fallbackClientHandshake: fallbackClientHandshakeFunc,
getS2AStream: getS2AStream,
serverAuthorizationPolicy: serverAuthorizationPolicy,
}
if err != nil {
creds.tokenManager = nil
} else {
creds.tokenManager = &accessTokenManager
}
if grpclog.V(1) {
grpclog.Info("Created client S2Av2 transport credentials.")
}
return creds, nil
}
// NewServerCreds returns a server-side transport credentials object that uses
// the S2Av2 to establish a secure connection with a client.
func NewServerCreds(s2av2Address string, transportCreds credentials.TransportCredentials, localIdentities []*commonpbv1.Identity, verificationMode s2av2pb.ValidatePeerCertificateChainReq_VerificationMode, getS2AStream func(ctx context.Context, s2av2Address string) (stream.S2AStream, error)) (credentials.TransportCredentials, error) {
// Create an AccessTokenManager instance to use to authenticate to S2Av2.
accessTokenManager, err := tokenmanager.NewSingleTokenAccessTokenManager()
creds := &s2av2TransportCreds{
info: &credentials.ProtocolInfo{
SecurityProtocol: s2aSecurityProtocol,
},
isClient: false,
s2av2Address: s2av2Address,
transportCreds: transportCreds,
localIdentities: localIdentities,
verificationMode: verificationMode,
getS2AStream: getS2AStream,
}
if err != nil {
creds.tokenManager = nil
} else {
creds.tokenManager = &accessTokenManager
}
if grpclog.V(1) {
grpclog.Info("Created server S2Av2 transport credentials.")
}
return creds, nil
}
// ClientHandshake performs a client-side mTLS handshake using the S2Av2.
func (c *s2av2TransportCreds) ClientHandshake(ctx context.Context, serverAuthority string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
if !c.isClient {
return nil, nil, errors.New("client handshake called using server transport credentials")
}
// Remove the port from serverAuthority.
serverName := removeServerNamePort(serverAuthority)
timeoutCtx, cancel := context.WithTimeout(ctx, GetS2ATimeout())
defer cancel()
var s2AStream stream.S2AStream
var err error
retry.Run(timeoutCtx,
func() error {
s2AStream, err = createStream(timeoutCtx, c.s2av2Address, c.transportCreds, c.getS2AStream)
return err
})
if err != nil {
grpclog.Infof("Failed to connect to S2Av2: %v", err)
if c.fallbackClientHandshake != nil {
return c.fallbackClientHandshake(ctx, serverAuthority, rawConn, err)
}
return nil, nil, err
}
defer s2AStream.CloseSend()
if grpclog.V(1) {
grpclog.Infof("Connected to S2Av2.")
}
var config *tls.Config
var tokenManager tokenmanager.AccessTokenManager
if c.tokenManager == nil {
tokenManager = nil
} else {
tokenManager = *c.tokenManager
}
sn := serverName
if c.serverName != "" {
sn = c.serverName
}
retry.Run(timeoutCtx,
func() error {
config, err = tlsconfigstore.GetTLSConfigurationForClient(sn, s2AStream, tokenManager, c.localIdentity, c.verificationMode, c.serverAuthorizationPolicy)
return err
})
if err != nil {
grpclog.Info("Failed to get client TLS config from S2Av2: %v", err)
if c.fallbackClientHandshake != nil {
return c.fallbackClientHandshake(ctx, serverAuthority, rawConn, err)
}
return nil, nil, err
}
if grpclog.V(1) {
grpclog.Infof("Got client TLS config from S2Av2.")
}
creds := credentials.NewTLS(config)
var conn net.Conn
var authInfo credentials.AuthInfo
retry.Run(timeoutCtx,
func() error {
conn, authInfo, err = creds.ClientHandshake(timeoutCtx, serverName, rawConn)
return err
})
if err != nil {
grpclog.Infof("Failed to do client handshake using S2Av2: %v", err)
if c.fallbackClientHandshake != nil {
return c.fallbackClientHandshake(ctx, serverAuthority, rawConn, err)
}
return nil, nil, err
}
grpclog.Infof("Successfully done client handshake using S2Av2 to: %s", serverName)
return conn, authInfo, err
}
// ServerHandshake performs a server-side mTLS handshake using the S2Av2.
func (c *s2av2TransportCreds) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
if c.isClient {
return nil, nil, errors.New("server handshake called using client transport credentials")
}
ctx, cancel := context.WithTimeout(context.Background(), GetS2ATimeout())
defer cancel()
var s2AStream stream.S2AStream
var err error
retry.Run(ctx,
func() error {
s2AStream, err = createStream(ctx, c.s2av2Address, c.transportCreds, c.getS2AStream)
return err
})
if err != nil {
grpclog.Infof("Failed to connect to S2Av2: %v", err)
return nil, nil, err
}
defer s2AStream.CloseSend()
if grpclog.V(1) {
grpclog.Infof("Connected to S2Av2.")
}
var tokenManager tokenmanager.AccessTokenManager
if c.tokenManager == nil {
tokenManager = nil
} else {
tokenManager = *c.tokenManager
}
var config *tls.Config
retry.Run(ctx,
func() error {
config, err = tlsconfigstore.GetTLSConfigurationForServer(s2AStream, tokenManager, c.localIdentities, c.verificationMode)
return err
})
if err != nil {
grpclog.Infof("Failed to get server TLS config from S2Av2: %v", err)
return nil, nil, err
}
if grpclog.V(1) {
grpclog.Infof("Got server TLS config from S2Av2.")
}
creds := credentials.NewTLS(config)
var conn net.Conn
var authInfo credentials.AuthInfo
retry.Run(ctx,
func() error {
conn, authInfo, err = creds.ServerHandshake(rawConn)
return err
})
if err != nil {
grpclog.Infof("Failed to do server handshake using S2Av2: %v", err)
return nil, nil, err
}
return conn, authInfo, err
}
// Info returns protocol info of s2av2TransportCreds.
func (c *s2av2TransportCreds) Info() credentials.ProtocolInfo {
return *c.info
}
// Clone makes a deep copy of s2av2TransportCreds.
func (c *s2av2TransportCreds) Clone() credentials.TransportCredentials {
info := *c.info
serverName := c.serverName
fallbackClientHandshake := c.fallbackClientHandshake
s2av2Address := c.s2av2Address
var tokenManager tokenmanager.AccessTokenManager
if c.tokenManager == nil {
tokenManager = nil
} else {
tokenManager = *c.tokenManager
}
verificationMode := c.verificationMode
var localIdentity *commonpbv1.Identity
if c.localIdentity != nil {
localIdentity = proto.Clone(c.localIdentity).(*commonpbv1.Identity)
}
var localIdentities []*commonpbv1.Identity
if c.localIdentities != nil {
localIdentities = make([]*commonpbv1.Identity, len(c.localIdentities))
for i, localIdentity := range c.localIdentities {
localIdentities[i] = proto.Clone(localIdentity).(*commonpbv1.Identity)
}
}
creds := &s2av2TransportCreds{
info: &info,
isClient: c.isClient,
serverName: serverName,
fallbackClientHandshake: fallbackClientHandshake,
s2av2Address: s2av2Address,
localIdentity: localIdentity,
localIdentities: localIdentities,
verificationMode: verificationMode,
}
if c.tokenManager == nil {
creds.tokenManager = nil
} else {
creds.tokenManager = &tokenManager
}
return creds
}
// NewClientTLSConfig returns a tls.Config instance that uses S2Av2 to establish a TLS connection as
// a client. The tls.Config MUST only be used to establish a single TLS connection.
func NewClientTLSConfig(
ctx context.Context,
s2av2Address string,
transportCreds credentials.TransportCredentials,
tokenManager tokenmanager.AccessTokenManager,
verificationMode s2av2pb.ValidatePeerCertificateChainReq_VerificationMode,
serverName string,
serverAuthorizationPolicy []byte) (*tls.Config, error) {
s2AStream, err := createStream(ctx, s2av2Address, transportCreds, nil)
if err != nil {
grpclog.Infof("Failed to connect to S2Av2: %v", err)
return nil, err
}
return tlsconfigstore.GetTLSConfigurationForClient(removeServerNamePort(serverName), s2AStream, tokenManager, nil, verificationMode, serverAuthorizationPolicy)
}
// OverrideServerName sets the ServerName in the s2av2TransportCreds protocol
// info. The ServerName MUST be a hostname.
func (c *s2av2TransportCreds) OverrideServerName(serverNameOverride string) error {
serverName := removeServerNamePort(serverNameOverride)
c.info.ServerName = serverName
c.serverName = serverName
return nil
}
// Remove the trailing port from server name.
func removeServerNamePort(serverName string) string {
name, _, err := net.SplitHostPort(serverName)
if err != nil {
name = serverName
}
return name
}
type s2AGrpcStream struct {
stream s2av2pb.S2AService_SetUpSessionClient
}
func (x s2AGrpcStream) Send(m *s2av2pb.SessionReq) error {
return x.stream.Send(m)
}
func (x s2AGrpcStream) Recv() (*s2av2pb.SessionResp, error) {
return x.stream.Recv()
}
func (x s2AGrpcStream) CloseSend() error {
return x.stream.CloseSend()
}
func createStream(ctx context.Context, s2av2Address string, transportCreds credentials.TransportCredentials, getS2AStream func(ctx context.Context, s2av2Address string) (stream.S2AStream, error)) (stream.S2AStream, error) {
if getS2AStream != nil {
return getS2AStream(ctx, s2av2Address)
}
// TODO(rmehta19): Consider whether to close the connection to S2Av2.
conn, err := service.Dial(ctx, s2av2Address, transportCreds)
if err != nil {
return nil, err
}
client := s2av2pb.NewS2AServiceClient(conn)
gRPCStream, err := client.SetUpSession(ctx, []grpc.CallOption{}...)
if err != nil {
return nil, err
}
return &s2AGrpcStream{
stream: gRPCStream,
}, nil
}
// GetS2ATimeout returns the timeout enforced on the connection to the S2A service for handshake.
func GetS2ATimeout() time.Duration {
timeout, err := time.ParseDuration(os.Getenv(s2aTimeoutEnv))
if err != nil {
return defaultS2ATimeout
}
return timeout
}

View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID8TCCAtmgAwIBAgIUKXNlBRVe6UepjQUijIFPZBd/4qYwDQYJKoZIhvcNAQEL
BQAwgYcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU3Vubnl2
YWxlMRAwDgYDVQQKDAdDb21wYW55MREwDwYDVQQLDAhEaXZpc2lvbjEWMBQGA1UE
AwwNczJhX3Rlc3RfY2VydDEaMBgGCSqGSIb3DQEJARYLeHl6QHh5ei5jb20wHhcN
MjIwNTMxMjAwMzE1WhcNNDIwNTI2MjAwMzE1WjCBhzELMAkGA1UEBhMCVVMxCzAJ
BgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxEDAOBgNVBAoMB0NvbXBhbnkx
ETAPBgNVBAsMCERpdmlzaW9uMRYwFAYDVQQDDA1zMmFfdGVzdF9jZXJ0MRowGAYJ
KoZIhvcNAQkBFgt4eXpAeHl6LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAOOFuIucH7XXfohGxKd3uR/ihUA/LdduR9I8kfpUEbq5BOt8xZe5/Yn9
a1ozEHVW6cOAbHbnwAR8tkSgZ/t42QIA2k77HWU1Jh2xiEIsJivo3imm4/kZWuR0
OqPh7MhzxpR/hvNwpI5mJsAVBWFMa5KtecFZLnyZtwHylrRN1QXzuLrOxuKFufK3
RKbTABScn5RbZL976H/jgfSeXrbt242NrIoBnVe6fRbekbq2DQ6zFArbQMUgHjHK
P0UqBgdr1QmHfi9KytFyx9BTP3gXWnWIu+bY7/v7qKJMHFwGETo+dCLWYevJL316
HnLfhApDMfP8U+Yv/y1N/YvgaSOSlEcCAwEAAaNTMFEwHQYDVR0OBBYEFKhAU4nu
0h/lrnggbIGvx4ej0WklMB8GA1UdIwQYMBaAFKhAU4nu0h/lrnggbIGvx4ej0Wkl
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAE/6NghzQ5fu6yR6
EHKbj/YMrFdT7aGn5n2sAf7wJ33LIhiFHkpWBsVlm7rDtZtwhe891ZK/P60anlg9
/P0Ua53tSRVRmCvTnEbXWOVMN4is6MsR7BlmzUxl4AtIn7jbeifEwRL7B4xDYmdA
QrQnsqoz45dLgS5xK4WDqXATP09Q91xQDuhud/b+A4jrvgwFASmL7rMIZbp4f1JQ
nlnl/9VoTBQBvJiWkDUtQDMpRLtauddEkv4AGz75p5IspXWD6cOemuh2iQec11xD
X20rs2WZbAcAiUa3nmy8OKYw435vmpj8gp39WYbX/Yx9TymrFFbVY92wYn+quTco
pKklVz0=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA44W4i5wftdd+iEbEp3e5H+KFQD8t125H0jyR+lQRurkE63zF
l7n9if1rWjMQdVbpw4BsdufABHy2RKBn+3jZAgDaTvsdZTUmHbGIQiwmK+jeKabj
+Rla5HQ6o+HsyHPGlH+G83CkjmYmwBUFYUxrkq15wVkufJm3AfKWtE3VBfO4us7G
4oW58rdEptMAFJyflFtkv3vof+OB9J5etu3bjY2sigGdV7p9Ft6RurYNDrMUCttA
xSAeMco/RSoGB2vVCYd+L0rK0XLH0FM/eBdadYi75tjv+/uookwcXAYROj50ItZh
68kvfXoect+ECkMx8/xT5i//LU39i+BpI5KURwIDAQABAoIBABgyjo/6iLzUMFbZ
/+w3pW6orrdIgN2akvTfED9pVYFgUA+jc3hRhY95bkNnjuaL2cy7Cc4Tk65mfRQL
Y0OxdJLr+EvSFSxAXM9npDA1ddHRsF8JqtFBSxNk8R+g1Yf0GDiO35Fgd3/ViWWA
VtQkRoSRApP3oiQKTRZd8H04keFR+PvmDk/Lq11l3Kc24A1PevKIPX1oI990ggw9
9i4uSV+cnuMxmcI9xxJtgwdDFdjr39l2arLOHr4s6LGoV2IOdXHNlv5xRqWUZ0FH
MDHowkLgwDrdSTnNeaVNkce14Gqx+bd4hNaLCdKXMpedBTEmrut3f3hdV1kKjaKt
aqRYr8ECgYEA/YDGZY2jvFoHHBywlqmEMFrrCvQGH51m5R1Ntpkzr+Rh3YCmrpvq
xgwJXING0PUw3dz+xrH5lJICrfNE5Kt3fPu1rAEy+13mYsNowghtUq2Rtu0Hsjjx
2E3Bf8vEB6RNBMmGkUpTTIAroGF5tpJoRvfnWax+k4pFdrKYFtyZdNcCgYEA5cNv
EPltvOobjTXlUmtVP3n27KZN2aXexTcagLzRxE9CV4cYySENl3KuOMmccaZpIl6z
aHk6BT4X+M0LqElNUczrInfVqI+SGAFLGy7W6CJaqSr6cpyFUP/fosKpm6wKGgLq
udHfpvz5rckhKd8kJxFLvhGOK9yN5qpzih0gfhECgYAJfwRvk3G5wYmYpP58dlcs
VIuPenqsPoI3PPTHTU/hW+XKnWIhElgmGRdUrto9Q6IT/Y5RtSMLTLjq+Tzwb/fm
56rziYv2XJsfwgAvnI8z1Kqrto9ePsHYf3krJ1/thVsZPc9bq/QY3ohD1sLvcuaT
GgBBnLOVJU3a12/ZE2RwOwKBgF0csWMAoj8/5IB6if+3ral2xOGsl7oPZVMo/J2V
Z7EVqb4M6rd/pKFugTpUQgkwtkSOekhpcGD1hAN5HTNK2YG/+L5UMAsKe9sskwJm
HgOfAHy0BSDzW3ey6i9skg2bT9Cww+0gJ3Hl7U1HSCBO5LjMYpSZSrNtwzfqdb5Q
BX3xAoGARZdR28Ej3+/+0+fz47Yu2h4z0EI/EbrudLOWY936jIeAVwHckI3+BuqH
qR4poj1gfbnMxNuI9UzIXzjEmGewx9kDZ7IYnvloZKqoVQODO5GlKF2ja6IcMNlh
GCNdD6PSAS6HcmalmWo9sj+1YMkrl+GJikKZqVBHrHNwMGAG67w=
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID8TCCAtmgAwIBAgIUKCoDuLtiZXvhsBY2RoDm0ugizJ8wDQYJKoZIhvcNAQEL
BQAwgYcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU3Vubnl2
YWxlMRAwDgYDVQQKDAdDb21wYW55MREwDwYDVQQLDAhEaXZpc2lvbjEWMBQGA1UE
AwwNczJhX3Rlc3RfY2VydDEaMBgGCSqGSIb3DQEJARYLeHl6QHh5ei5jb20wHhcN
MjIwNTMxMjAwODI1WhcNNDIwNTI2MjAwODI1WjCBhzELMAkGA1UEBhMCVVMxCzAJ
BgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxEDAOBgNVBAoMB0NvbXBhbnkx
ETAPBgNVBAsMCERpdmlzaW9uMRYwFAYDVQQDDA1zMmFfdGVzdF9jZXJ0MRowGAYJ
KoZIhvcNAQkBFgt4eXpAeHl6LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAKK1++PXQ+M3hjYH/v0K4UEYl5ljzpNM1i52eQM+gFooojT87PDSaphT
fs0PXy/PTAjHBEvPhWpOpmQXfJNYzjwcCvg66hbqkv++/VTZiFLAsHagzkEz+FRJ
qT5Eq7G5FLyw1izX1uxyPN7tAEWEEg7eqsiaXD3Cq8+TYN9cjirPeF7RZF8yFCYE
xqvbo+Yc6RL6xw19iXVTfctRgQe581KQuIY5/LXo3dWDEilFdsADAe8XAEcO64es
Ow0g1UvXLnpXSE151kXBFb3sKH/ZjCecDYMCIMEb4sWLSblkSxJ5sNSmXIG4wtr2
Qnii7CXZgnVYraQE/Jyh+NMQANuoSdMCAwEAAaNTMFEwHQYDVR0OBBYEFAyQQQuM
ab+YUQqjK8dVVOoHVFmXMB8GA1UdIwQYMBaAFAyQQQuMab+YUQqjK8dVVOoHVFmX
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADj0vQ6ykWhicoqR
e6VZMwlEJV7/DSvWWKBd9MUjfKye0A4565ya5lmnzP3DiD3nqGe3miqmLsXKDs+X
POqlPXTWIamP7D4MJ32XtSLwZB4ru+I+Ao/P/VngPepoRPQoBnzHe7jww0rokqxl
AZERjlbTUwUAy/BPWPSzSJZ2j0tcs6ZLDNyYzpK4ao8R9/1VmQ92Tcp3feJs1QTg
odRQc3om/AkWOwsll+oyX0UbJeHkFHiLanUPXbdh+/BkSvZJ8ynL+feSDdaurPe+
PSfnqLtQft9/neecGRdEaQzzzSFVQUVQzTdK1Q7hA7b55b2HvIa3ktDiks+sJsYN
Dhm6uZM=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAorX749dD4zeGNgf+/QrhQRiXmWPOk0zWLnZ5Az6AWiiiNPzs
8NJqmFN+zQ9fL89MCMcES8+Fak6mZBd8k1jOPBwK+DrqFuqS/779VNmIUsCwdqDO
QTP4VEmpPkSrsbkUvLDWLNfW7HI83u0ARYQSDt6qyJpcPcKrz5Ng31yOKs94XtFk
XzIUJgTGq9uj5hzpEvrHDX2JdVN9y1GBB7nzUpC4hjn8tejd1YMSKUV2wAMB7xcA
Rw7rh6w7DSDVS9cueldITXnWRcEVvewof9mMJ5wNgwIgwRvixYtJuWRLEnmw1KZc
gbjC2vZCeKLsJdmCdVitpAT8nKH40xAA26hJ0wIDAQABAoIBACaNR+lsD8G+XiZf
LqN1+HkcAo9tfnyYMAdCOtnx7SdviT9Uzi8hK/B7mAeuJLeHPlS2EuaDfPD7QaFl
jza6S+MiIdc+3kgfvESsVAnOoOY6kZUJ9NSuI6CU82y1iJjLaYZrv9NQMLRFPPb0
4KOX709mosB1EnXvshW0rbc+jtDFhrm1SxMt+k9TuzmMxjbOeW4LOLXPgU8X1T3Q
Xy0hMZZtcgBs9wFIo8yCtmOixax9pnFE8rRltgDxTodn9LLdz1FieyntNgDksZ0P
nt4kV7Mqly7ELaea+Foaj244mKsesic2e3GhAlMRLun/VSunSf7mOCxfpITB8dp1
drDhOYECgYEA19151dVxRcviuovN6Dar+QszMTnU8pDJ8BjLFjXjP/hNBBwMTHDE
duMuWk2qnwZqMooI/shxrF/ufmTgS0CFrh2+ANBZu27vWConJNXcyNtdigI4wt50
L0Y2qcZn2mg67qFXHwoR3QNwrwnPwEjRXA09at9CSRZzcwDQ0ETXhYsCgYEAwPaG
06QdK8Zyly7TTzZJwxzv9uGiqzodmGtX6NEKjgij2JaCxHpukqZBJoqa0jKeK1cm
eNVkOvT5ff9TMzarSHQLr3pZen2/oVLb5gaFkbcJt/klv9Fd+ZRilHY3i6QwS6pD
uMiPOWS4DrLHDRVoVlAZTDjT1RVwwTs+P2NhJdkCgYEAsriXysbxBYyMp05gqEW7
lHIFbFgpSrs9th+Q5U6wW6JEgYaHWDJ1NslY80MiZI93FWjbkbZ7BvBWESeL3EIL
a+EMErht0pVCbIhZ6FF4foPAqia0wAJVx14mm+G80kNBp5jE/NnleEsE3KcO7nBb
hg8gLn+x7bk81JZ0TDrzBYkCgYEAuQKluv47SeF3tSScTfKLPpvcKCWmxe1uutkQ
7JShPhVioyOMNb39jnYBOWbjkm4d4QgqRuiytSR0oi3QI+Ziy5EYMyNn713qAk9j
r2TJZDDPDKnBW+zt4YI4EohWMXk3JRUW4XDKggjjwJQA7bZ812TtHHvP/xoThfG7
eSNb3eECgYBw6ssgCtMrdvQiEmjKVX/9yI38mvC2kSGyzbrQnGUfgqRGomRpeZuD
B5E3kysA4td5pT5lvcLgSW0TbOz+YbiriXjwOihPIelCvc9gE2eOUI71/byUWPFz
7u5F/xQ4NaGr5suLF+lBC6h7pSbM4El9lIHQAQadpuEdzHqrw+hs3g==
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID8TCCAtmgAwIBAgIUKXNlBRVe6UepjQUijIFPZBd/4qYwDQYJKoZIhvcNAQEL
BQAwgYcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU3Vubnl2
YWxlMRAwDgYDVQQKDAdDb21wYW55MREwDwYDVQQLDAhEaXZpc2lvbjEWMBQGA1UE
AwwNczJhX3Rlc3RfY2VydDEaMBgGCSqGSIb3DQEJARYLeHl6QHh5ei5jb20wHhcN
MjIwNTMxMjAwMzE1WhcNNDIwNTI2MjAwMzE1WjCBhzELMAkGA1UEBhMCVVMxCzAJ
BgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxEDAOBgNVBAoMB0NvbXBhbnkx
ETAPBgNVBAsMCERpdmlzaW9uMRYwFAYDVQQDDA1zMmFfdGVzdF9jZXJ0MRowGAYJ
KoZIhvcNAQkBFgt4eXpAeHl6LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAOOFuIucH7XXfohGxKd3uR/ihUA/LdduR9I8kfpUEbq5BOt8xZe5/Yn9
a1ozEHVW6cOAbHbnwAR8tkSgZ/t42QIA2k77HWU1Jh2xiEIsJivo3imm4/kZWuR0
OqPh7MhzxpR/hvNwpI5mJsAVBWFMa5KtecFZLnyZtwHylrRN1QXzuLrOxuKFufK3
RKbTABScn5RbZL976H/jgfSeXrbt242NrIoBnVe6fRbekbq2DQ6zFArbQMUgHjHK
P0UqBgdr1QmHfi9KytFyx9BTP3gXWnWIu+bY7/v7qKJMHFwGETo+dCLWYevJL316
HnLfhApDMfP8U+Yv/y1N/YvgaSOSlEcCAwEAAaNTMFEwHQYDVR0OBBYEFKhAU4nu
0h/lrnggbIGvx4ej0WklMB8GA1UdIwQYMBaAFKhAU4nu0h/lrnggbIGvx4ej0Wkl
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAE/6NghzQ5fu6yR6
EHKbj/YMrFdT7aGn5n2sAf7wJ33LIhiFHkpWBsVlm7rDtZtwhe891ZK/P60anlg9
/P0Ua53tSRVRmCvTnEbXWOVMN4is6MsR7BlmzUxl4AtIn7jbeifEwRL7B4xDYmdA
QrQnsqoz45dLgS5xK4WDqXATP09Q91xQDuhud/b+A4jrvgwFASmL7rMIZbp4f1JQ
nlnl/9VoTBQBvJiWkDUtQDMpRLtauddEkv4AGz75p5IspXWD6cOemuh2iQec11xD
X20rs2WZbAcAiUa3nmy8OKYw435vmpj8gp39WYbX/Yx9TymrFFbVY92wYn+quTco
pKklVz0=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA44W4i5wftdd+iEbEp3e5H+KFQD8t125H0jyR+lQRurkE63zF
l7n9if1rWjMQdVbpw4BsdufABHy2RKBn+3jZAgDaTvsdZTUmHbGIQiwmK+jeKabj
+Rla5HQ6o+HsyHPGlH+G83CkjmYmwBUFYUxrkq15wVkufJm3AfKWtE3VBfO4us7G
4oW58rdEptMAFJyflFtkv3vof+OB9J5etu3bjY2sigGdV7p9Ft6RurYNDrMUCttA
xSAeMco/RSoGB2vVCYd+L0rK0XLH0FM/eBdadYi75tjv+/uookwcXAYROj50ItZh
68kvfXoect+ECkMx8/xT5i//LU39i+BpI5KURwIDAQABAoIBABgyjo/6iLzUMFbZ
/+w3pW6orrdIgN2akvTfED9pVYFgUA+jc3hRhY95bkNnjuaL2cy7Cc4Tk65mfRQL
Y0OxdJLr+EvSFSxAXM9npDA1ddHRsF8JqtFBSxNk8R+g1Yf0GDiO35Fgd3/ViWWA
VtQkRoSRApP3oiQKTRZd8H04keFR+PvmDk/Lq11l3Kc24A1PevKIPX1oI990ggw9
9i4uSV+cnuMxmcI9xxJtgwdDFdjr39l2arLOHr4s6LGoV2IOdXHNlv5xRqWUZ0FH
MDHowkLgwDrdSTnNeaVNkce14Gqx+bd4hNaLCdKXMpedBTEmrut3f3hdV1kKjaKt
aqRYr8ECgYEA/YDGZY2jvFoHHBywlqmEMFrrCvQGH51m5R1Ntpkzr+Rh3YCmrpvq
xgwJXING0PUw3dz+xrH5lJICrfNE5Kt3fPu1rAEy+13mYsNowghtUq2Rtu0Hsjjx
2E3Bf8vEB6RNBMmGkUpTTIAroGF5tpJoRvfnWax+k4pFdrKYFtyZdNcCgYEA5cNv
EPltvOobjTXlUmtVP3n27KZN2aXexTcagLzRxE9CV4cYySENl3KuOMmccaZpIl6z
aHk6BT4X+M0LqElNUczrInfVqI+SGAFLGy7W6CJaqSr6cpyFUP/fosKpm6wKGgLq
udHfpvz5rckhKd8kJxFLvhGOK9yN5qpzih0gfhECgYAJfwRvk3G5wYmYpP58dlcs
VIuPenqsPoI3PPTHTU/hW+XKnWIhElgmGRdUrto9Q6IT/Y5RtSMLTLjq+Tzwb/fm
56rziYv2XJsfwgAvnI8z1Kqrto9ePsHYf3krJ1/thVsZPc9bq/QY3ohD1sLvcuaT
GgBBnLOVJU3a12/ZE2RwOwKBgF0csWMAoj8/5IB6if+3ral2xOGsl7oPZVMo/J2V
Z7EVqb4M6rd/pKFugTpUQgkwtkSOekhpcGD1hAN5HTNK2YG/+L5UMAsKe9sskwJm
HgOfAHy0BSDzW3ey6i9skg2bT9Cww+0gJ3Hl7U1HSCBO5LjMYpSZSrNtwzfqdb5Q
BX3xAoGARZdR28Ej3+/+0+fz47Yu2h4z0EI/EbrudLOWY936jIeAVwHckI3+BuqH
qR4poj1gfbnMxNuI9UzIXzjEmGewx9kDZ7IYnvloZKqoVQODO5GlKF2ja6IcMNlh
GCNdD6PSAS6HcmalmWo9sj+1YMkrl+GJikKZqVBHrHNwMGAG67w=
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID8TCCAtmgAwIBAgIUKCoDuLtiZXvhsBY2RoDm0ugizJ8wDQYJKoZIhvcNAQEL
BQAwgYcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU3Vubnl2
YWxlMRAwDgYDVQQKDAdDb21wYW55MREwDwYDVQQLDAhEaXZpc2lvbjEWMBQGA1UE
AwwNczJhX3Rlc3RfY2VydDEaMBgGCSqGSIb3DQEJARYLeHl6QHh5ei5jb20wHhcN
MjIwNTMxMjAwODI1WhcNNDIwNTI2MjAwODI1WjCBhzELMAkGA1UEBhMCVVMxCzAJ
BgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxEDAOBgNVBAoMB0NvbXBhbnkx
ETAPBgNVBAsMCERpdmlzaW9uMRYwFAYDVQQDDA1zMmFfdGVzdF9jZXJ0MRowGAYJ
KoZIhvcNAQkBFgt4eXpAeHl6LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAKK1++PXQ+M3hjYH/v0K4UEYl5ljzpNM1i52eQM+gFooojT87PDSaphT
fs0PXy/PTAjHBEvPhWpOpmQXfJNYzjwcCvg66hbqkv++/VTZiFLAsHagzkEz+FRJ
qT5Eq7G5FLyw1izX1uxyPN7tAEWEEg7eqsiaXD3Cq8+TYN9cjirPeF7RZF8yFCYE
xqvbo+Yc6RL6xw19iXVTfctRgQe581KQuIY5/LXo3dWDEilFdsADAe8XAEcO64es
Ow0g1UvXLnpXSE151kXBFb3sKH/ZjCecDYMCIMEb4sWLSblkSxJ5sNSmXIG4wtr2
Qnii7CXZgnVYraQE/Jyh+NMQANuoSdMCAwEAAaNTMFEwHQYDVR0OBBYEFAyQQQuM
ab+YUQqjK8dVVOoHVFmXMB8GA1UdIwQYMBaAFAyQQQuMab+YUQqjK8dVVOoHVFmX
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADj0vQ6ykWhicoqR
e6VZMwlEJV7/DSvWWKBd9MUjfKye0A4565ya5lmnzP3DiD3nqGe3miqmLsXKDs+X
POqlPXTWIamP7D4MJ32XtSLwZB4ru+I+Ao/P/VngPepoRPQoBnzHe7jww0rokqxl
AZERjlbTUwUAy/BPWPSzSJZ2j0tcs6ZLDNyYzpK4ao8R9/1VmQ92Tcp3feJs1QTg
odRQc3om/AkWOwsll+oyX0UbJeHkFHiLanUPXbdh+/BkSvZJ8ynL+feSDdaurPe+
PSfnqLtQft9/neecGRdEaQzzzSFVQUVQzTdK1Q7hA7b55b2HvIa3ktDiks+sJsYN
Dhm6uZM=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAorX749dD4zeGNgf+/QrhQRiXmWPOk0zWLnZ5Az6AWiiiNPzs
8NJqmFN+zQ9fL89MCMcES8+Fak6mZBd8k1jOPBwK+DrqFuqS/779VNmIUsCwdqDO
QTP4VEmpPkSrsbkUvLDWLNfW7HI83u0ARYQSDt6qyJpcPcKrz5Ng31yOKs94XtFk
XzIUJgTGq9uj5hzpEvrHDX2JdVN9y1GBB7nzUpC4hjn8tejd1YMSKUV2wAMB7xcA
Rw7rh6w7DSDVS9cueldITXnWRcEVvewof9mMJ5wNgwIgwRvixYtJuWRLEnmw1KZc
gbjC2vZCeKLsJdmCdVitpAT8nKH40xAA26hJ0wIDAQABAoIBACaNR+lsD8G+XiZf
LqN1+HkcAo9tfnyYMAdCOtnx7SdviT9Uzi8hK/B7mAeuJLeHPlS2EuaDfPD7QaFl
jza6S+MiIdc+3kgfvESsVAnOoOY6kZUJ9NSuI6CU82y1iJjLaYZrv9NQMLRFPPb0
4KOX709mosB1EnXvshW0rbc+jtDFhrm1SxMt+k9TuzmMxjbOeW4LOLXPgU8X1T3Q
Xy0hMZZtcgBs9wFIo8yCtmOixax9pnFE8rRltgDxTodn9LLdz1FieyntNgDksZ0P
nt4kV7Mqly7ELaea+Foaj244mKsesic2e3GhAlMRLun/VSunSf7mOCxfpITB8dp1
drDhOYECgYEA19151dVxRcviuovN6Dar+QszMTnU8pDJ8BjLFjXjP/hNBBwMTHDE
duMuWk2qnwZqMooI/shxrF/ufmTgS0CFrh2+ANBZu27vWConJNXcyNtdigI4wt50
L0Y2qcZn2mg67qFXHwoR3QNwrwnPwEjRXA09at9CSRZzcwDQ0ETXhYsCgYEAwPaG
06QdK8Zyly7TTzZJwxzv9uGiqzodmGtX6NEKjgij2JaCxHpukqZBJoqa0jKeK1cm
eNVkOvT5ff9TMzarSHQLr3pZen2/oVLb5gaFkbcJt/klv9Fd+ZRilHY3i6QwS6pD
uMiPOWS4DrLHDRVoVlAZTDjT1RVwwTs+P2NhJdkCgYEAsriXysbxBYyMp05gqEW7
lHIFbFgpSrs9th+Q5U6wW6JEgYaHWDJ1NslY80MiZI93FWjbkbZ7BvBWESeL3EIL
a+EMErht0pVCbIhZ6FF4foPAqia0wAJVx14mm+G80kNBp5jE/NnleEsE3KcO7nBb
hg8gLn+x7bk81JZ0TDrzBYkCgYEAuQKluv47SeF3tSScTfKLPpvcKCWmxe1uutkQ
7JShPhVioyOMNb39jnYBOWbjkm4d4QgqRuiytSR0oi3QI+Ziy5EYMyNn713qAk9j
r2TJZDDPDKnBW+zt4YI4EohWMXk3JRUW4XDKggjjwJQA7bZ812TtHHvP/xoThfG7
eSNb3eECgYBw6ssgCtMrdvQiEmjKVX/9yI38mvC2kSGyzbrQnGUfgqRGomRpeZuD
B5E3kysA4td5pT5lvcLgSW0TbOz+YbiriXjwOihPIelCvc9gE2eOUI71/byUWPFz
7u5F/xQ4NaGr5suLF+lBC6h7pSbM4El9lIHQAQadpuEdzHqrw+hs3g==
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,404 @@
/*
*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package tlsconfigstore offloads operations to S2Av2.
package tlsconfigstore
import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"github.com/google/s2a-go/internal/tokenmanager"
"github.com/google/s2a-go/internal/v2/certverifier"
"github.com/google/s2a-go/internal/v2/remotesigner"
"github.com/google/s2a-go/stream"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
commonpbv1 "github.com/google/s2a-go/internal/proto/common_go_proto"
commonpb "github.com/google/s2a-go/internal/proto/v2/common_go_proto"
s2av2pb "github.com/google/s2a-go/internal/proto/v2/s2a_go_proto"
)
const (
// HTTP/2
h2 = "h2"
)
// GetTLSConfigurationForClient returns a tls.Config instance for use by a client application.
func GetTLSConfigurationForClient(serverHostname string, s2AStream stream.S2AStream, tokenManager tokenmanager.AccessTokenManager, localIdentity *commonpbv1.Identity, verificationMode s2av2pb.ValidatePeerCertificateChainReq_VerificationMode, serverAuthorizationPolicy []byte) (*tls.Config, error) {
authMechanisms := getAuthMechanisms(tokenManager, []*commonpbv1.Identity{localIdentity})
if grpclog.V(1) {
grpclog.Infof("Sending request to S2Av2 for client TLS config.")
}
// Send request to S2Av2 for config.
if err := s2AStream.Send(&s2av2pb.SessionReq{
LocalIdentity: localIdentity,
AuthenticationMechanisms: authMechanisms,
ReqOneof: &s2av2pb.SessionReq_GetTlsConfigurationReq{
GetTlsConfigurationReq: &s2av2pb.GetTlsConfigurationReq{
ConnectionSide: commonpb.ConnectionSide_CONNECTION_SIDE_CLIENT,
},
},
}); err != nil {
grpclog.Infof("Failed to send request to S2Av2 for client TLS config")
return nil, err
}
// Get the response containing config from S2Av2.
resp, err := s2AStream.Recv()
if err != nil {
grpclog.Infof("Failed to receive client TLS config response from S2Av2.")
return nil, err
}
// TODO(rmehta19): Add unit test for this if statement.
if (resp.GetStatus() != nil) && (resp.GetStatus().Code != uint32(codes.OK)) {
return nil, fmt.Errorf("failed to get TLS configuration from S2A: %d, %v", resp.GetStatus().Code, resp.GetStatus().Details)
}
// Extract TLS configiguration from SessionResp.
tlsConfig := resp.GetGetTlsConfigurationResp().GetClientTlsConfiguration()
var cert tls.Certificate
for i, v := range tlsConfig.CertificateChain {
// Populate Certificates field.
block, _ := pem.Decode([]byte(v))
if block == nil {
return nil, errors.New("certificate in CertificateChain obtained from S2Av2 is empty")
}
x509Cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
cert.Certificate = append(cert.Certificate, x509Cert.Raw)
if i == 0 {
cert.Leaf = x509Cert
}
}
if len(tlsConfig.CertificateChain) > 0 {
cert.PrivateKey = remotesigner.New(cert.Leaf, s2AStream)
if cert.PrivateKey == nil {
return nil, errors.New("failed to retrieve Private Key from Remote Signer Library")
}
}
minVersion, maxVersion, err := getTLSMinMaxVersionsClient(tlsConfig)
if err != nil {
return nil, err
}
// Create mTLS credentials for client.
config := &tls.Config{
VerifyPeerCertificate: certverifier.VerifyServerCertificateChain(serverHostname, verificationMode, s2AStream, serverAuthorizationPolicy),
ServerName: serverHostname,
InsecureSkipVerify: true, // NOLINT
ClientSessionCache: nil,
SessionTicketsDisabled: true,
MinVersion: minVersion,
MaxVersion: maxVersion,
NextProtos: []string{h2},
}
if len(tlsConfig.CertificateChain) > 0 {
config.Certificates = []tls.Certificate{cert}
}
return config, nil
}
// GetTLSConfigurationForServer returns a tls.Config instance for use by a server application.
func GetTLSConfigurationForServer(s2AStream stream.S2AStream, tokenManager tokenmanager.AccessTokenManager, localIdentities []*commonpbv1.Identity, verificationMode s2av2pb.ValidatePeerCertificateChainReq_VerificationMode) (*tls.Config, error) {
return &tls.Config{
GetConfigForClient: ClientConfig(tokenManager, localIdentities, verificationMode, s2AStream),
}, nil
}
// ClientConfig builds a TLS config for a server to establish a secure
// connection with a client, based on SNI communicated during ClientHello.
// Ensures that server presents the correct certificate to establish a TLS
// connection.
func ClientConfig(tokenManager tokenmanager.AccessTokenManager, localIdentities []*commonpbv1.Identity, verificationMode s2av2pb.ValidatePeerCertificateChainReq_VerificationMode, s2AStream stream.S2AStream) func(chi *tls.ClientHelloInfo) (*tls.Config, error) {
return func(chi *tls.ClientHelloInfo) (*tls.Config, error) {
tlsConfig, err := getServerConfigFromS2Av2(tokenManager, localIdentities, chi.ServerName, s2AStream)
if err != nil {
return nil, err
}
var cert tls.Certificate
for i, v := range tlsConfig.CertificateChain {
// Populate Certificates field.
block, _ := pem.Decode([]byte(v))
if block == nil {
return nil, errors.New("certificate in CertificateChain obtained from S2Av2 is empty")
}
x509Cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
cert.Certificate = append(cert.Certificate, x509Cert.Raw)
if i == 0 {
cert.Leaf = x509Cert
}
}
cert.PrivateKey = remotesigner.New(cert.Leaf, s2AStream)
if cert.PrivateKey == nil {
return nil, errors.New("failed to retrieve Private Key from Remote Signer Library")
}
minVersion, maxVersion, err := getTLSMinMaxVersionsServer(tlsConfig)
if err != nil {
return nil, err
}
clientAuth := getTLSClientAuthType(tlsConfig)
var cipherSuites []uint16
cipherSuites = getCipherSuites(tlsConfig.Ciphersuites)
// Create mTLS credentials for server.
return &tls.Config{
Certificates: []tls.Certificate{cert},
VerifyPeerCertificate: certverifier.VerifyClientCertificateChain(verificationMode, s2AStream),
ClientAuth: clientAuth,
CipherSuites: cipherSuites,
SessionTicketsDisabled: true,
MinVersion: minVersion,
MaxVersion: maxVersion,
NextProtos: []string{h2},
}, nil
}
}
func getCipherSuites(tlsConfigCipherSuites []commonpb.Ciphersuite) []uint16 {
var tlsGoCipherSuites []uint16
for _, v := range tlsConfigCipherSuites {
s := getTLSCipherSuite(v)
if s != 0xffff {
tlsGoCipherSuites = append(tlsGoCipherSuites, s)
}
}
return tlsGoCipherSuites
}
func getTLSCipherSuite(tlsCipherSuite commonpb.Ciphersuite) uint16 {
switch tlsCipherSuite {
case commonpb.Ciphersuite_CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
return tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
case commonpb.Ciphersuite_CIPHERSUITE_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
return tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
case commonpb.Ciphersuite_CIPHERSUITE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
return tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
case commonpb.Ciphersuite_CIPHERSUITE_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
return tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
case commonpb.Ciphersuite_CIPHERSUITE_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
return tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
case commonpb.Ciphersuite_CIPHERSUITE_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
return tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
default:
return 0xffff
}
}
func getServerConfigFromS2Av2(tokenManager tokenmanager.AccessTokenManager, localIdentities []*commonpbv1.Identity, sni string, s2AStream stream.S2AStream) (*s2av2pb.GetTlsConfigurationResp_ServerTlsConfiguration, error) {
authMechanisms := getAuthMechanisms(tokenManager, localIdentities)
var locID *commonpbv1.Identity
if localIdentities != nil {
locID = localIdentities[0]
}
if err := s2AStream.Send(&s2av2pb.SessionReq{
LocalIdentity: locID,
AuthenticationMechanisms: authMechanisms,
ReqOneof: &s2av2pb.SessionReq_GetTlsConfigurationReq{
GetTlsConfigurationReq: &s2av2pb.GetTlsConfigurationReq{
ConnectionSide: commonpb.ConnectionSide_CONNECTION_SIDE_SERVER,
Sni: sni,
},
},
}); err != nil {
return nil, err
}
resp, err := s2AStream.Recv()
if err != nil {
return nil, err
}
// TODO(rmehta19): Add unit test for this if statement.
if (resp.GetStatus() != nil) && (resp.GetStatus().Code != uint32(codes.OK)) {
return nil, fmt.Errorf("failed to get TLS configuration from S2A: %d, %v", resp.GetStatus().Code, resp.GetStatus().Details)
}
return resp.GetGetTlsConfigurationResp().GetServerTlsConfiguration(), nil
}
func getTLSClientAuthType(tlsConfig *s2av2pb.GetTlsConfigurationResp_ServerTlsConfiguration) tls.ClientAuthType {
var clientAuth tls.ClientAuthType
switch x := tlsConfig.RequestClientCertificate; x {
case s2av2pb.GetTlsConfigurationResp_ServerTlsConfiguration_DONT_REQUEST_CLIENT_CERTIFICATE:
clientAuth = tls.NoClientCert
case s2av2pb.GetTlsConfigurationResp_ServerTlsConfiguration_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
clientAuth = tls.RequestClientCert
case s2av2pb.GetTlsConfigurationResp_ServerTlsConfiguration_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY:
// This case actually maps to tls.VerifyClientCertIfGiven. However this
// mapping triggers normal verification, followed by custom verification,
// specified in VerifyPeerCertificate. To bypass normal verification, and
// only do custom verification we set clientAuth to RequireAnyClientCert or
// RequestClientCert. See https://github.com/google/s2a-go/pull/43 for full
// discussion.
clientAuth = tls.RequireAnyClientCert
case s2av2pb.GetTlsConfigurationResp_ServerTlsConfiguration_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
clientAuth = tls.RequireAnyClientCert
case s2av2pb.GetTlsConfigurationResp_ServerTlsConfiguration_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY:
// This case actually maps to tls.RequireAndVerifyClientCert. However this
// mapping triggers normal verification, followed by custom verification,
// specified in VerifyPeerCertificate. To bypass normal verification, and
// only do custom verification we set clientAuth to RequireAnyClientCert or
// RequestClientCert. See https://github.com/google/s2a-go/pull/43 for full
// discussion.
clientAuth = tls.RequireAnyClientCert
default:
clientAuth = tls.RequireAnyClientCert
}
return clientAuth
}
func getAuthMechanisms(tokenManager tokenmanager.AccessTokenManager, localIdentities []*commonpbv1.Identity) []*s2av2pb.AuthenticationMechanism {
if tokenManager == nil {
return nil
}
if len(localIdentities) == 0 {
token, err := tokenManager.DefaultToken()
if err != nil {
grpclog.Infof("Unable to get token for empty local identity: %v", err)
return nil
}
return []*s2av2pb.AuthenticationMechanism{
{
MechanismOneof: &s2av2pb.AuthenticationMechanism_Token{
Token: token,
},
},
}
}
var authMechanisms []*s2av2pb.AuthenticationMechanism
for _, localIdentity := range localIdentities {
if localIdentity == nil {
token, err := tokenManager.DefaultToken()
if err != nil {
grpclog.Infof("Unable to get default token for local identity %v: %v", localIdentity, err)
continue
}
authMechanisms = append(authMechanisms, &s2av2pb.AuthenticationMechanism{
Identity: localIdentity,
MechanismOneof: &s2av2pb.AuthenticationMechanism_Token{
Token: token,
},
})
} else {
token, err := tokenManager.Token(localIdentity)
if err != nil {
grpclog.Infof("Unable to get token for local identity %v: %v", localIdentity, err)
continue
}
authMechanisms = append(authMechanisms, &s2av2pb.AuthenticationMechanism{
Identity: localIdentity,
MechanismOneof: &s2av2pb.AuthenticationMechanism_Token{
Token: token,
},
})
}
}
return authMechanisms
}
// TODO(rmehta19): refactor switch statements into a helper function.
func getTLSMinMaxVersionsClient(tlsConfig *s2av2pb.GetTlsConfigurationResp_ClientTlsConfiguration) (uint16, uint16, error) {
// Map S2Av2 TLSVersion to consts defined in tls package.
var minVersion uint16
var maxVersion uint16
switch x := tlsConfig.MinTlsVersion; x {
case commonpb.TLSVersion_TLS_VERSION_1_0:
minVersion = tls.VersionTLS10
case commonpb.TLSVersion_TLS_VERSION_1_1:
minVersion = tls.VersionTLS11
case commonpb.TLSVersion_TLS_VERSION_1_2:
minVersion = tls.VersionTLS12
case commonpb.TLSVersion_TLS_VERSION_1_3:
minVersion = tls.VersionTLS13
default:
return minVersion, maxVersion, fmt.Errorf("S2Av2 provided invalid MinTlsVersion: %v", x)
}
switch x := tlsConfig.MaxTlsVersion; x {
case commonpb.TLSVersion_TLS_VERSION_1_0:
maxVersion = tls.VersionTLS10
case commonpb.TLSVersion_TLS_VERSION_1_1:
maxVersion = tls.VersionTLS11
case commonpb.TLSVersion_TLS_VERSION_1_2:
maxVersion = tls.VersionTLS12
case commonpb.TLSVersion_TLS_VERSION_1_3:
maxVersion = tls.VersionTLS13
default:
return minVersion, maxVersion, fmt.Errorf("S2Av2 provided invalid MaxTlsVersion: %v", x)
}
if minVersion > maxVersion {
return minVersion, maxVersion, errors.New("S2Av2 provided minVersion > maxVersion")
}
return minVersion, maxVersion, nil
}
func getTLSMinMaxVersionsServer(tlsConfig *s2av2pb.GetTlsConfigurationResp_ServerTlsConfiguration) (uint16, uint16, error) {
// Map S2Av2 TLSVersion to consts defined in tls package.
var minVersion uint16
var maxVersion uint16
switch x := tlsConfig.MinTlsVersion; x {
case commonpb.TLSVersion_TLS_VERSION_1_0:
minVersion = tls.VersionTLS10
case commonpb.TLSVersion_TLS_VERSION_1_1:
minVersion = tls.VersionTLS11
case commonpb.TLSVersion_TLS_VERSION_1_2:
minVersion = tls.VersionTLS12
case commonpb.TLSVersion_TLS_VERSION_1_3:
minVersion = tls.VersionTLS13
default:
return minVersion, maxVersion, fmt.Errorf("S2Av2 provided invalid MinTlsVersion: %v", x)
}
switch x := tlsConfig.MaxTlsVersion; x {
case commonpb.TLSVersion_TLS_VERSION_1_0:
maxVersion = tls.VersionTLS10
case commonpb.TLSVersion_TLS_VERSION_1_1:
maxVersion = tls.VersionTLS11
case commonpb.TLSVersion_TLS_VERSION_1_2:
maxVersion = tls.VersionTLS12
case commonpb.TLSVersion_TLS_VERSION_1_3:
maxVersion = tls.VersionTLS13
default:
return minVersion, maxVersion, fmt.Errorf("S2Av2 provided invalid MaxTlsVersion: %v", x)
}
if minVersion > maxVersion {
return minVersion, maxVersion, errors.New("S2Av2 provided minVersion > maxVersion")
}
return minVersion, maxVersion, nil
}

144
vendor/github.com/google/s2a-go/retry/retry.go generated vendored Normal file
View File

@@ -0,0 +1,144 @@
/*
*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package retry provides a retry helper for talking to S2A gRPC server.
// The implementation is modeled after
// https://github.com/googleapis/google-cloud-go/blob/main/compute/metadata/retry.go
package retry
import (
"context"
"math/rand"
"time"
"google.golang.org/grpc/grpclog"
)
const (
maxRetryAttempts = 5
maxRetryForLoops = 10
)
type defaultBackoff struct {
max time.Duration
mul float64
cur time.Duration
}
// Pause returns a duration, which is used as the backoff wait time
// before the next retry.
func (b *defaultBackoff) Pause() time.Duration {
d := time.Duration(1 + rand.Int63n(int64(b.cur)))
b.cur = time.Duration(float64(b.cur) * b.mul)
if b.cur > b.max {
b.cur = b.max
}
return d
}
// Sleep will wait for the specified duration or return on context
// expiration.
func Sleep(ctx context.Context, d time.Duration) error {
t := time.NewTimer(d)
select {
case <-ctx.Done():
t.Stop()
return ctx.Err()
case <-t.C:
return nil
}
}
// NewRetryer creates an instance of S2ARetryer using the defaultBackoff
// implementation.
var NewRetryer = func() *S2ARetryer {
return &S2ARetryer{bo: &defaultBackoff{
cur: 100 * time.Millisecond,
max: 30 * time.Second,
mul: 2,
}}
}
type backoff interface {
Pause() time.Duration
}
// S2ARetryer implements a retry helper for talking to S2A gRPC server.
type S2ARetryer struct {
bo backoff
attempts int
}
// Attempts return the number of retries attempted.
func (r *S2ARetryer) Attempts() int {
return r.attempts
}
// Retry returns a boolean indicating whether retry should be performed
// and the backoff duration.
func (r *S2ARetryer) Retry(err error) (time.Duration, bool) {
if err == nil {
return 0, false
}
if r.attempts >= maxRetryAttempts {
return 0, false
}
r.attempts++
return r.bo.Pause(), true
}
// Run uses S2ARetryer to execute the function passed in, until success or reaching
// max number of retry attempts.
func Run(ctx context.Context, f func() error) {
retryer := NewRetryer()
forLoopCnt := 0
var err error
for {
err = f()
if bo, shouldRetry := retryer.Retry(err); shouldRetry {
if grpclog.V(1) {
grpclog.Infof("will attempt retry: %v", err)
}
if ctx.Err() != nil {
if grpclog.V(1) {
grpclog.Infof("exit retry loop due to context error: %v", ctx.Err())
}
break
}
if errSleep := Sleep(ctx, bo); errSleep != nil {
if grpclog.V(1) {
grpclog.Infof("exit retry loop due to sleep error: %v", errSleep)
}
break
}
// This shouldn't happen, just make sure we are not stuck in the for loops.
forLoopCnt++
if forLoopCnt > maxRetryForLoops {
if grpclog.V(1) {
grpclog.Infof("exit the for loop after too many retries")
}
break
}
continue
}
if grpclog.V(1) {
grpclog.Infof("retry conditions not met, exit the loop")
}
break
}
}

427
vendor/github.com/google/s2a-go/s2a.go generated vendored Normal file
View File

@@ -0,0 +1,427 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package s2a provides the S2A transport credentials used by a gRPC
// application.
package s2a
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"sync"
"time"
"github.com/golang/protobuf/proto"
"github.com/google/s2a-go/fallback"
"github.com/google/s2a-go/internal/handshaker"
"github.com/google/s2a-go/internal/handshaker/service"
"github.com/google/s2a-go/internal/tokenmanager"
"github.com/google/s2a-go/internal/v2"
"github.com/google/s2a-go/retry"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
commonpb "github.com/google/s2a-go/internal/proto/common_go_proto"
s2av2pb "github.com/google/s2a-go/internal/proto/v2/s2a_go_proto"
)
const (
s2aSecurityProtocol = "tls"
// defaultTimeout specifies the default server handshake timeout.
defaultTimeout = 30.0 * time.Second
)
// s2aTransportCreds are the transport credentials required for establishing
// a secure connection using the S2A. They implement the
// credentials.TransportCredentials interface.
type s2aTransportCreds struct {
info *credentials.ProtocolInfo
minTLSVersion commonpb.TLSVersion
maxTLSVersion commonpb.TLSVersion
// tlsCiphersuites contains the ciphersuites used in the S2A connection.
// Note that these are currently unconfigurable.
tlsCiphersuites []commonpb.Ciphersuite
// localIdentity should only be used by the client.
localIdentity *commonpb.Identity
// localIdentities should only be used by the server.
localIdentities []*commonpb.Identity
// targetIdentities should only be used by the client.
targetIdentities []*commonpb.Identity
isClient bool
s2aAddr string
ensureProcessSessionTickets *sync.WaitGroup
}
// NewClientCreds returns a client-side transport credentials object that uses
// the S2A to establish a secure connection with a server.
func NewClientCreds(opts *ClientOptions) (credentials.TransportCredentials, error) {
if opts == nil {
return nil, errors.New("nil client options")
}
var targetIdentities []*commonpb.Identity
for _, targetIdentity := range opts.TargetIdentities {
protoTargetIdentity, err := toProtoIdentity(targetIdentity)
if err != nil {
return nil, err
}
targetIdentities = append(targetIdentities, protoTargetIdentity)
}
localIdentity, err := toProtoIdentity(opts.LocalIdentity)
if err != nil {
return nil, err
}
if opts.EnableLegacyMode {
return &s2aTransportCreds{
info: &credentials.ProtocolInfo{
SecurityProtocol: s2aSecurityProtocol,
},
minTLSVersion: commonpb.TLSVersion_TLS1_3,
maxTLSVersion: commonpb.TLSVersion_TLS1_3,
tlsCiphersuites: []commonpb.Ciphersuite{
commonpb.Ciphersuite_AES_128_GCM_SHA256,
commonpb.Ciphersuite_AES_256_GCM_SHA384,
commonpb.Ciphersuite_CHACHA20_POLY1305_SHA256,
},
localIdentity: localIdentity,
targetIdentities: targetIdentities,
isClient: true,
s2aAddr: opts.S2AAddress,
ensureProcessSessionTickets: opts.EnsureProcessSessionTickets,
}, nil
}
verificationMode := getVerificationMode(opts.VerificationMode)
var fallbackFunc fallback.ClientHandshake
if opts.FallbackOpts != nil && opts.FallbackOpts.FallbackClientHandshakeFunc != nil {
fallbackFunc = opts.FallbackOpts.FallbackClientHandshakeFunc
}
return v2.NewClientCreds(opts.S2AAddress, opts.TransportCreds, localIdentity, verificationMode, fallbackFunc, opts.getS2AStream, opts.serverAuthorizationPolicy)
}
// NewServerCreds returns a server-side transport credentials object that uses
// the S2A to establish a secure connection with a client.
func NewServerCreds(opts *ServerOptions) (credentials.TransportCredentials, error) {
if opts == nil {
return nil, errors.New("nil server options")
}
var localIdentities []*commonpb.Identity
for _, localIdentity := range opts.LocalIdentities {
protoLocalIdentity, err := toProtoIdentity(localIdentity)
if err != nil {
return nil, err
}
localIdentities = append(localIdentities, protoLocalIdentity)
}
if opts.EnableLegacyMode {
return &s2aTransportCreds{
info: &credentials.ProtocolInfo{
SecurityProtocol: s2aSecurityProtocol,
},
minTLSVersion: commonpb.TLSVersion_TLS1_3,
maxTLSVersion: commonpb.TLSVersion_TLS1_3,
tlsCiphersuites: []commonpb.Ciphersuite{
commonpb.Ciphersuite_AES_128_GCM_SHA256,
commonpb.Ciphersuite_AES_256_GCM_SHA384,
commonpb.Ciphersuite_CHACHA20_POLY1305_SHA256,
},
localIdentities: localIdentities,
isClient: false,
s2aAddr: opts.S2AAddress,
}, nil
}
verificationMode := getVerificationMode(opts.VerificationMode)
return v2.NewServerCreds(opts.S2AAddress, opts.TransportCreds, localIdentities, verificationMode, opts.getS2AStream)
}
// ClientHandshake initiates a client-side TLS handshake using the S2A.
func (c *s2aTransportCreds) ClientHandshake(ctx context.Context, serverAuthority string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
if !c.isClient {
return nil, nil, errors.New("client handshake called using server transport credentials")
}
var cancel context.CancelFunc
ctx, cancel = context.WithCancel(ctx)
defer cancel()
// Connect to the S2A.
hsConn, err := service.Dial(ctx, c.s2aAddr, nil)
if err != nil {
grpclog.Infof("Failed to connect to S2A: %v", err)
return nil, nil, err
}
opts := &handshaker.ClientHandshakerOptions{
MinTLSVersion: c.minTLSVersion,
MaxTLSVersion: c.maxTLSVersion,
TLSCiphersuites: c.tlsCiphersuites,
TargetIdentities: c.targetIdentities,
LocalIdentity: c.localIdentity,
TargetName: serverAuthority,
EnsureProcessSessionTickets: c.ensureProcessSessionTickets,
}
chs, err := handshaker.NewClientHandshaker(ctx, hsConn, rawConn, c.s2aAddr, opts)
if err != nil {
grpclog.Infof("Call to handshaker.NewClientHandshaker failed: %v", err)
return nil, nil, err
}
defer func() {
if err != nil {
if closeErr := chs.Close(); closeErr != nil {
grpclog.Infof("Close failed unexpectedly: %v", err)
err = fmt.Errorf("%v: close unexpectedly failed: %v", err, closeErr)
}
}
}()
secConn, authInfo, err := chs.ClientHandshake(context.Background())
if err != nil {
grpclog.Infof("Handshake failed: %v", err)
return nil, nil, err
}
return secConn, authInfo, nil
}
// ServerHandshake initiates a server-side TLS handshake using the S2A.
func (c *s2aTransportCreds) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
if c.isClient {
return nil, nil, errors.New("server handshake called using client transport credentials")
}
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
defer cancel()
// Connect to the S2A.
hsConn, err := service.Dial(ctx, c.s2aAddr, nil)
if err != nil {
grpclog.Infof("Failed to connect to S2A: %v", err)
return nil, nil, err
}
opts := &handshaker.ServerHandshakerOptions{
MinTLSVersion: c.minTLSVersion,
MaxTLSVersion: c.maxTLSVersion,
TLSCiphersuites: c.tlsCiphersuites,
LocalIdentities: c.localIdentities,
}
shs, err := handshaker.NewServerHandshaker(ctx, hsConn, rawConn, c.s2aAddr, opts)
if err != nil {
grpclog.Infof("Call to handshaker.NewServerHandshaker failed: %v", err)
return nil, nil, err
}
defer func() {
if err != nil {
if closeErr := shs.Close(); closeErr != nil {
grpclog.Infof("Close failed unexpectedly: %v", err)
err = fmt.Errorf("%v: close unexpectedly failed: %v", err, closeErr)
}
}
}()
secConn, authInfo, err := shs.ServerHandshake(context.Background())
if err != nil {
grpclog.Infof("Handshake failed: %v", err)
return nil, nil, err
}
return secConn, authInfo, nil
}
func (c *s2aTransportCreds) Info() credentials.ProtocolInfo {
return *c.info
}
func (c *s2aTransportCreds) Clone() credentials.TransportCredentials {
info := *c.info
var localIdentity *commonpb.Identity
if c.localIdentity != nil {
localIdentity = proto.Clone(c.localIdentity).(*commonpb.Identity)
}
var localIdentities []*commonpb.Identity
if c.localIdentities != nil {
localIdentities = make([]*commonpb.Identity, len(c.localIdentities))
for i, localIdentity := range c.localIdentities {
localIdentities[i] = proto.Clone(localIdentity).(*commonpb.Identity)
}
}
var targetIdentities []*commonpb.Identity
if c.targetIdentities != nil {
targetIdentities = make([]*commonpb.Identity, len(c.targetIdentities))
for i, targetIdentity := range c.targetIdentities {
targetIdentities[i] = proto.Clone(targetIdentity).(*commonpb.Identity)
}
}
return &s2aTransportCreds{
info: &info,
minTLSVersion: c.minTLSVersion,
maxTLSVersion: c.maxTLSVersion,
tlsCiphersuites: c.tlsCiphersuites,
localIdentity: localIdentity,
localIdentities: localIdentities,
targetIdentities: targetIdentities,
isClient: c.isClient,
s2aAddr: c.s2aAddr,
}
}
func (c *s2aTransportCreds) OverrideServerName(serverNameOverride string) error {
c.info.ServerName = serverNameOverride
return nil
}
// TLSClientConfigOptions specifies parameters for creating client TLS config.
type TLSClientConfigOptions struct {
// ServerName is required by s2a as the expected name when verifying the hostname found in server's certificate.
// tlsConfig, _ := factory.Build(ctx, &s2a.TLSClientConfigOptions{
// ServerName: "example.com",
// })
ServerName string
}
// TLSClientConfigFactory defines the interface for a client TLS config factory.
type TLSClientConfigFactory interface {
Build(ctx context.Context, opts *TLSClientConfigOptions) (*tls.Config, error)
}
// NewTLSClientConfigFactory returns an instance of s2aTLSClientConfigFactory.
func NewTLSClientConfigFactory(opts *ClientOptions) (TLSClientConfigFactory, error) {
if opts == nil {
return nil, fmt.Errorf("opts must be non-nil")
}
if opts.EnableLegacyMode {
return nil, fmt.Errorf("NewTLSClientConfigFactory only supports S2Av2")
}
tokenManager, err := tokenmanager.NewSingleTokenAccessTokenManager()
if err != nil {
// The only possible error is: access token not set in the environment,
// which is okay in environments other than serverless.
grpclog.Infof("Access token manager not initialized: %v", err)
return &s2aTLSClientConfigFactory{
s2av2Address: opts.S2AAddress,
transportCreds: opts.TransportCreds,
tokenManager: nil,
verificationMode: getVerificationMode(opts.VerificationMode),
serverAuthorizationPolicy: opts.serverAuthorizationPolicy,
}, nil
}
return &s2aTLSClientConfigFactory{
s2av2Address: opts.S2AAddress,
transportCreds: opts.TransportCreds,
tokenManager: tokenManager,
verificationMode: getVerificationMode(opts.VerificationMode),
serverAuthorizationPolicy: opts.serverAuthorizationPolicy,
}, nil
}
type s2aTLSClientConfigFactory struct {
s2av2Address string
transportCreds credentials.TransportCredentials
tokenManager tokenmanager.AccessTokenManager
verificationMode s2av2pb.ValidatePeerCertificateChainReq_VerificationMode
serverAuthorizationPolicy []byte
}
func (f *s2aTLSClientConfigFactory) Build(
ctx context.Context, opts *TLSClientConfigOptions) (*tls.Config, error) {
serverName := ""
if opts != nil && opts.ServerName != "" {
serverName = opts.ServerName
}
return v2.NewClientTLSConfig(ctx, f.s2av2Address, f.transportCreds, f.tokenManager, f.verificationMode, serverName, f.serverAuthorizationPolicy)
}
func getVerificationMode(verificationMode VerificationModeType) s2av2pb.ValidatePeerCertificateChainReq_VerificationMode {
switch verificationMode {
case ConnectToGoogle:
return s2av2pb.ValidatePeerCertificateChainReq_CONNECT_TO_GOOGLE
case Spiffe:
return s2av2pb.ValidatePeerCertificateChainReq_SPIFFE
default:
return s2av2pb.ValidatePeerCertificateChainReq_UNSPECIFIED
}
}
// NewS2ADialTLSContextFunc returns a dialer which establishes an MTLS connection using S2A.
// Example use with http.RoundTripper:
//
// dialTLSContext := s2a.NewS2aDialTLSContextFunc(&s2a.ClientOptions{
// S2AAddress: s2aAddress, // required
// })
// transport := http.DefaultTransport
// transport.DialTLSContext = dialTLSContext
func NewS2ADialTLSContextFunc(opts *ClientOptions) func(ctx context.Context, network, addr string) (net.Conn, error) {
return func(ctx context.Context, network, addr string) (net.Conn, error) {
fallback := func(err error) (net.Conn, error) {
if opts.FallbackOpts != nil && opts.FallbackOpts.FallbackDialer != nil &&
opts.FallbackOpts.FallbackDialer.Dialer != nil && opts.FallbackOpts.FallbackDialer.ServerAddr != "" {
fbDialer := opts.FallbackOpts.FallbackDialer
grpclog.Infof("fall back to dial: %s", fbDialer.ServerAddr)
fbConn, fbErr := fbDialer.Dialer.DialContext(ctx, network, fbDialer.ServerAddr)
if fbErr != nil {
return nil, fmt.Errorf("error fallback to %s: %v; S2A error: %w", fbDialer.ServerAddr, fbErr, err)
}
return fbConn, nil
}
return nil, err
}
factory, err := NewTLSClientConfigFactory(opts)
if err != nil {
grpclog.Infof("error creating S2A client config factory: %v", err)
return fallback(err)
}
serverName, _, err := net.SplitHostPort(addr)
if err != nil {
serverName = addr
}
timeoutCtx, cancel := context.WithTimeout(ctx, v2.GetS2ATimeout())
defer cancel()
var s2aTLSConfig *tls.Config
retry.Run(timeoutCtx,
func() error {
s2aTLSConfig, err = factory.Build(timeoutCtx, &TLSClientConfigOptions{
ServerName: serverName,
})
return err
})
if err != nil {
grpclog.Infof("error building S2A TLS config: %v", err)
return fallback(err)
}
s2aDialer := &tls.Dialer{
Config: s2aTLSConfig,
}
var c net.Conn
retry.Run(timeoutCtx,
func() error {
c, err = s2aDialer.DialContext(timeoutCtx, network, addr)
return err
})
if err != nil {
grpclog.Infof("error dialing with S2A to %s: %v", addr, err)
return fallback(err)
}
grpclog.Infof("success dialing MTLS to %s with S2A", addr)
return c, nil
}
}

215
vendor/github.com/google/s2a-go/s2a_options.go generated vendored Normal file
View File

@@ -0,0 +1,215 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package s2a
import (
"context"
"crypto/tls"
"errors"
"sync"
"github.com/google/s2a-go/fallback"
"github.com/google/s2a-go/stream"
"google.golang.org/grpc/credentials"
s2apb "github.com/google/s2a-go/internal/proto/common_go_proto"
)
// Identity is the interface for S2A identities.
type Identity interface {
// Name returns the name of the identity.
Name() string
}
type spiffeID struct {
spiffeID string
}
func (s *spiffeID) Name() string { return s.spiffeID }
// NewSpiffeID creates a SPIFFE ID from id.
func NewSpiffeID(id string) Identity {
return &spiffeID{spiffeID: id}
}
type hostname struct {
hostname string
}
func (h *hostname) Name() string { return h.hostname }
// NewHostname creates a hostname from name.
func NewHostname(name string) Identity {
return &hostname{hostname: name}
}
type uid struct {
uid string
}
func (h *uid) Name() string { return h.uid }
// NewUID creates a UID from name.
func NewUID(name string) Identity {
return &uid{uid: name}
}
// VerificationModeType specifies the mode that S2A must use to verify the peer
// certificate chain.
type VerificationModeType int
// Three types of verification modes.
const (
Unspecified = iota
ConnectToGoogle
Spiffe
)
// ClientOptions contains the client-side options used to establish a secure
// channel using the S2A handshaker service.
type ClientOptions struct {
// TargetIdentities contains a list of allowed server identities. One of the
// target identities should match the peer identity in the handshake
// result; otherwise, the handshake fails.
TargetIdentities []Identity
// LocalIdentity is the local identity of the client application. If none is
// provided, then the S2A will choose the default identity, if one exists.
LocalIdentity Identity
// S2AAddress is the address of the S2A.
S2AAddress string
// Optional transport credentials.
// If set, this will be used for the gRPC connection to the S2A server.
TransportCreds credentials.TransportCredentials
// EnsureProcessSessionTickets waits for all session tickets to be sent to
// S2A before a process completes.
//
// This functionality is crucial for processes that complete very soon after
// using S2A to establish a TLS connection, but it can be ignored for longer
// lived processes.
//
// Usage example:
// func main() {
// var ensureProcessSessionTickets sync.WaitGroup
// clientOpts := &s2a.ClientOptions{
// EnsureProcessSessionTickets: &ensureProcessSessionTickets,
// // Set other members.
// }
// creds, _ := s2a.NewClientCreds(clientOpts)
// conn, _ := grpc.Dial(serverAddr, grpc.WithTransportCredentials(creds))
// defer conn.Close()
//
// // Make RPC call.
//
// // The process terminates right after the RPC call ends.
// // ensureProcessSessionTickets can be used to ensure resumption
// // tickets are fully processed. If the process is long-lived, using
// // ensureProcessSessionTickets is not necessary.
// ensureProcessSessionTickets.Wait()
// }
EnsureProcessSessionTickets *sync.WaitGroup
// If true, enables the use of legacy S2Av1.
EnableLegacyMode bool
// VerificationMode specifies the mode that S2A must use to verify the
// peer certificate chain.
VerificationMode VerificationModeType
// Optional fallback after dialing with S2A fails.
FallbackOpts *FallbackOptions
// Generates an S2AStream interface for talking to the S2A server.
getS2AStream func(ctx context.Context, s2av2Address string) (stream.S2AStream, error)
// Serialized user specified policy for server authorization.
serverAuthorizationPolicy []byte
}
// FallbackOptions prescribes the fallback logic that should be taken if the application fails to connect with S2A.
type FallbackOptions struct {
// FallbackClientHandshakeFunc is used to specify fallback behavior when calling s2a.NewClientCreds().
// It will be called by ClientHandshake function, after handshake with S2A fails.
// s2a.NewClientCreds() ignores the other FallbackDialer field.
FallbackClientHandshakeFunc fallback.ClientHandshake
// FallbackDialer is used to specify fallback behavior when calling s2a.NewS2aDialTLSContextFunc().
// It passes in a custom fallback dialer and server address to use after dialing with S2A fails.
// s2a.NewS2aDialTLSContextFunc() ignores the other FallbackClientHandshakeFunc field.
FallbackDialer *FallbackDialer
}
// FallbackDialer contains a fallback tls.Dialer and a server address to connect to.
type FallbackDialer struct {
// Dialer specifies a fallback tls.Dialer.
Dialer *tls.Dialer
// ServerAddr is used by Dialer to establish fallback connection.
ServerAddr string
}
// DefaultClientOptions returns the default client options.
func DefaultClientOptions(s2aAddress string) *ClientOptions {
return &ClientOptions{
S2AAddress: s2aAddress,
VerificationMode: ConnectToGoogle,
}
}
// ServerOptions contains the server-side options used to establish a secure
// channel using the S2A handshaker service.
type ServerOptions struct {
// LocalIdentities is the list of local identities that may be assumed by
// the server. If no local identity is specified, then the S2A chooses a
// default local identity, if one exists.
LocalIdentities []Identity
// S2AAddress is the address of the S2A.
S2AAddress string
// Optional transport credentials.
// If set, this will be used for the gRPC connection to the S2A server.
TransportCreds credentials.TransportCredentials
// If true, enables the use of legacy S2Av1.
EnableLegacyMode bool
// VerificationMode specifies the mode that S2A must use to verify the
// peer certificate chain.
VerificationMode VerificationModeType
// Generates an S2AStream interface for talking to the S2A server.
getS2AStream func(ctx context.Context, s2av2Address string) (stream.S2AStream, error)
}
// DefaultServerOptions returns the default server options.
func DefaultServerOptions(s2aAddress string) *ServerOptions {
return &ServerOptions{
S2AAddress: s2aAddress,
VerificationMode: ConnectToGoogle,
}
}
func toProtoIdentity(identity Identity) (*s2apb.Identity, error) {
if identity == nil {
return nil, nil
}
switch id := identity.(type) {
case *spiffeID:
return &s2apb.Identity{IdentityOneof: &s2apb.Identity_SpiffeId{SpiffeId: id.Name()}}, nil
case *hostname:
return &s2apb.Identity{IdentityOneof: &s2apb.Identity_Hostname{Hostname: id.Name()}}, nil
case *uid:
return &s2apb.Identity{IdentityOneof: &s2apb.Identity_Uid{Uid: id.Name()}}, nil
default:
return nil, errors.New("unrecognized identity type")
}
}

79
vendor/github.com/google/s2a-go/s2a_utils.go generated vendored Normal file
View File

@@ -0,0 +1,79 @@
/*
*
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package s2a
import (
"context"
"errors"
commonpb "github.com/google/s2a-go/internal/proto/common_go_proto"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/peer"
)
// AuthInfo exposes security information from the S2A to the application.
type AuthInfo interface {
// AuthType returns the authentication type.
AuthType() string
// ApplicationProtocol returns the application protocol, e.g. "grpc".
ApplicationProtocol() string
// TLSVersion returns the TLS version negotiated during the handshake.
TLSVersion() commonpb.TLSVersion
// Ciphersuite returns the ciphersuite negotiated during the handshake.
Ciphersuite() commonpb.Ciphersuite
// PeerIdentity returns the authenticated identity of the peer.
PeerIdentity() *commonpb.Identity
// LocalIdentity returns the local identity of the application used during
// session setup.
LocalIdentity() *commonpb.Identity
// PeerCertFingerprint returns the SHA256 hash of the peer certificate used in
// the S2A handshake.
PeerCertFingerprint() []byte
// LocalCertFingerprint returns the SHA256 hash of the local certificate used
// in the S2A handshake.
LocalCertFingerprint() []byte
// IsHandshakeResumed returns true if a cached session was used to resume
// the handshake.
IsHandshakeResumed() bool
// SecurityLevel returns the security level of the connection.
SecurityLevel() credentials.SecurityLevel
}
// AuthInfoFromPeer extracts the authinfo.S2AAuthInfo object from the given
// peer, if it exists. This API should be used by gRPC clients after
// obtaining a peer object using the grpc.Peer() CallOption.
func AuthInfoFromPeer(p *peer.Peer) (AuthInfo, error) {
s2aAuthInfo, ok := p.AuthInfo.(AuthInfo)
if !ok {
return nil, errors.New("no S2AAuthInfo found in Peer")
}
return s2aAuthInfo, nil
}
// AuthInfoFromContext extracts the authinfo.S2AAuthInfo object from the given
// context, if it exists. This API should be used by gRPC server RPC handlers
// to get information about the peer. On the client-side, use the grpc.Peer()
// CallOption and the AuthInfoFromPeer function.
func AuthInfoFromContext(ctx context.Context) (AuthInfo, error) {
p, ok := peer.FromContext(ctx)
if !ok {
return nil, errors.New("no Peer found in Context")
}
return AuthInfoFromPeer(p)
}

34
vendor/github.com/google/s2a-go/stream/s2a_stream.go generated vendored Normal file
View File

@@ -0,0 +1,34 @@
/*
*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// Package stream provides an interface for bidirectional streaming to the S2A server.
package stream
import (
s2av2pb "github.com/google/s2a-go/internal/proto/v2/s2a_go_proto"
)
// S2AStream defines the operation for communicating with the S2A server over a bidirectional stream.
type S2AStream interface {
// Send sends the message to the S2A server.
Send(*s2av2pb.SessionReq) error
// Recv receives the message from the S2A server.
Recv() (*s2av2pb.SessionResp, error)
// Closes the channel to the S2A server.
CloseSend() error
}

View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID8TCCAtmgAwIBAgIUKXNlBRVe6UepjQUijIFPZBd/4qYwDQYJKoZIhvcNAQEL
BQAwgYcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU3Vubnl2
YWxlMRAwDgYDVQQKDAdDb21wYW55MREwDwYDVQQLDAhEaXZpc2lvbjEWMBQGA1UE
AwwNczJhX3Rlc3RfY2VydDEaMBgGCSqGSIb3DQEJARYLeHl6QHh5ei5jb20wHhcN
MjIwNTMxMjAwMzE1WhcNNDIwNTI2MjAwMzE1WjCBhzELMAkGA1UEBhMCVVMxCzAJ
BgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxEDAOBgNVBAoMB0NvbXBhbnkx
ETAPBgNVBAsMCERpdmlzaW9uMRYwFAYDVQQDDA1zMmFfdGVzdF9jZXJ0MRowGAYJ
KoZIhvcNAQkBFgt4eXpAeHl6LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAOOFuIucH7XXfohGxKd3uR/ihUA/LdduR9I8kfpUEbq5BOt8xZe5/Yn9
a1ozEHVW6cOAbHbnwAR8tkSgZ/t42QIA2k77HWU1Jh2xiEIsJivo3imm4/kZWuR0
OqPh7MhzxpR/hvNwpI5mJsAVBWFMa5KtecFZLnyZtwHylrRN1QXzuLrOxuKFufK3
RKbTABScn5RbZL976H/jgfSeXrbt242NrIoBnVe6fRbekbq2DQ6zFArbQMUgHjHK
P0UqBgdr1QmHfi9KytFyx9BTP3gXWnWIu+bY7/v7qKJMHFwGETo+dCLWYevJL316
HnLfhApDMfP8U+Yv/y1N/YvgaSOSlEcCAwEAAaNTMFEwHQYDVR0OBBYEFKhAU4nu
0h/lrnggbIGvx4ej0WklMB8GA1UdIwQYMBaAFKhAU4nu0h/lrnggbIGvx4ej0Wkl
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAE/6NghzQ5fu6yR6
EHKbj/YMrFdT7aGn5n2sAf7wJ33LIhiFHkpWBsVlm7rDtZtwhe891ZK/P60anlg9
/P0Ua53tSRVRmCvTnEbXWOVMN4is6MsR7BlmzUxl4AtIn7jbeifEwRL7B4xDYmdA
QrQnsqoz45dLgS5xK4WDqXATP09Q91xQDuhud/b+A4jrvgwFASmL7rMIZbp4f1JQ
nlnl/9VoTBQBvJiWkDUtQDMpRLtauddEkv4AGz75p5IspXWD6cOemuh2iQec11xD
X20rs2WZbAcAiUa3nmy8OKYw435vmpj8gp39WYbX/Yx9TymrFFbVY92wYn+quTco
pKklVz0=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA44W4i5wftdd+iEbEp3e5H+KFQD8t125H0jyR+lQRurkE63zF
l7n9if1rWjMQdVbpw4BsdufABHy2RKBn+3jZAgDaTvsdZTUmHbGIQiwmK+jeKabj
+Rla5HQ6o+HsyHPGlH+G83CkjmYmwBUFYUxrkq15wVkufJm3AfKWtE3VBfO4us7G
4oW58rdEptMAFJyflFtkv3vof+OB9J5etu3bjY2sigGdV7p9Ft6RurYNDrMUCttA
xSAeMco/RSoGB2vVCYd+L0rK0XLH0FM/eBdadYi75tjv+/uookwcXAYROj50ItZh
68kvfXoect+ECkMx8/xT5i//LU39i+BpI5KURwIDAQABAoIBABgyjo/6iLzUMFbZ
/+w3pW6orrdIgN2akvTfED9pVYFgUA+jc3hRhY95bkNnjuaL2cy7Cc4Tk65mfRQL
Y0OxdJLr+EvSFSxAXM9npDA1ddHRsF8JqtFBSxNk8R+g1Yf0GDiO35Fgd3/ViWWA
VtQkRoSRApP3oiQKTRZd8H04keFR+PvmDk/Lq11l3Kc24A1PevKIPX1oI990ggw9
9i4uSV+cnuMxmcI9xxJtgwdDFdjr39l2arLOHr4s6LGoV2IOdXHNlv5xRqWUZ0FH
MDHowkLgwDrdSTnNeaVNkce14Gqx+bd4hNaLCdKXMpedBTEmrut3f3hdV1kKjaKt
aqRYr8ECgYEA/YDGZY2jvFoHHBywlqmEMFrrCvQGH51m5R1Ntpkzr+Rh3YCmrpvq
xgwJXING0PUw3dz+xrH5lJICrfNE5Kt3fPu1rAEy+13mYsNowghtUq2Rtu0Hsjjx
2E3Bf8vEB6RNBMmGkUpTTIAroGF5tpJoRvfnWax+k4pFdrKYFtyZdNcCgYEA5cNv
EPltvOobjTXlUmtVP3n27KZN2aXexTcagLzRxE9CV4cYySENl3KuOMmccaZpIl6z
aHk6BT4X+M0LqElNUczrInfVqI+SGAFLGy7W6CJaqSr6cpyFUP/fosKpm6wKGgLq
udHfpvz5rckhKd8kJxFLvhGOK9yN5qpzih0gfhECgYAJfwRvk3G5wYmYpP58dlcs
VIuPenqsPoI3PPTHTU/hW+XKnWIhElgmGRdUrto9Q6IT/Y5RtSMLTLjq+Tzwb/fm
56rziYv2XJsfwgAvnI8z1Kqrto9ePsHYf3krJ1/thVsZPc9bq/QY3ohD1sLvcuaT
GgBBnLOVJU3a12/ZE2RwOwKBgF0csWMAoj8/5IB6if+3ral2xOGsl7oPZVMo/J2V
Z7EVqb4M6rd/pKFugTpUQgkwtkSOekhpcGD1hAN5HTNK2YG/+L5UMAsKe9sskwJm
HgOfAHy0BSDzW3ey6i9skg2bT9Cww+0gJ3Hl7U1HSCBO5LjMYpSZSrNtwzfqdb5Q
BX3xAoGARZdR28Ej3+/+0+fz47Yu2h4z0EI/EbrudLOWY936jIeAVwHckI3+BuqH
qR4poj1gfbnMxNuI9UzIXzjEmGewx9kDZ7IYnvloZKqoVQODO5GlKF2ja6IcMNlh
GCNdD6PSAS6HcmalmWo9sj+1YMkrl+GJikKZqVBHrHNwMGAG67w=
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDCDCCAfACFFlYsYCFit01ZpYmfjxpo7/6wMEbMA0GCSqGSIb3DQEBCwUAMEgx
CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UECgwGR29vZ2xlMRswGQYD
VQQDDBJ0ZXN0LXMyYS1tdGxzLXJvb3QwHhcNMjMwODIyMTY0NTE4WhcNNDMwODIy
MTY0NTE4WjA5MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExHTAbBgNVBAMMFHRl
c3QtczJhLW10bHMtY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAqrQQMyxNtmdCB+uY3szgRsfPrKC+TV9Fusnd8PfaCVuGTGcSBKM018nV2TDn
3IYFQ1HgLpGwGwOFDBb3y0o9i2/l2VJySriX1GSNX6nDmVasQlO1wuOLCP7/LRmO
7b6Kise5W0IFhYaptKyWnekn2pS0tAjimqpfn2w0U6FDGtQUqg/trQQmGtTSJHjb
A+OFd0EFC18KGP8Q+jOMaMkJRmpeEiAPyHPDoMhqQNT26RApv9j2Uzo4SuXzHH6T
cAdm1+zG+EXY/UZKX9oDkSbwIJvN+gCmNyORLalJ12gsGYOCjMd8K0mlXBqrmmbO
VHVbUm9062lhE7x59AA8DK4DoQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCPOvtL
dq2hxFHlIy0YUK8jp/DtwJZPwzx1id5FtWwd0CxBS1StIgmkHMxtkJGz1iyQLplI
je+Msd4sTsb5zZi/8kGKehi8Wj4lghp4oP30cpob41OvM68M9RC/wSOVk9igSww+
l3zof6wKRIswsi5VHrL16ruIVVoDlyFbKr8yk+cp9OPOV8hNNN7ewY9xC8OgnTt8
YtdaLe6uTplKBLW+j3GtshigRhyfkGJyPFYL4LAeDJCHlC1qmBnkyP0ijMp6vneM
E8TLavnMTMcpihWTWpyKeRkO6HDRsP4AofQAp7VAiAdSOplga+w2qgrVICV+m8MK
BTq2PBvc59T6OFLq
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCqtBAzLE22Z0IH
65jezOBGx8+soL5NX0W6yd3w99oJW4ZMZxIEozTXydXZMOfchgVDUeAukbAbA4UM
FvfLSj2Lb+XZUnJKuJfUZI1fqcOZVqxCU7XC44sI/v8tGY7tvoqKx7lbQgWFhqm0
rJad6SfalLS0COKaql+fbDRToUMa1BSqD+2tBCYa1NIkeNsD44V3QQULXwoY/xD6
M4xoyQlGal4SIA/Ic8OgyGpA1PbpECm/2PZTOjhK5fMcfpNwB2bX7Mb4Rdj9Rkpf
2gORJvAgm836AKY3I5EtqUnXaCwZg4KMx3wrSaVcGquaZs5UdVtSb3TraWETvHn0
ADwMrgOhAgMBAAECggEAUccupZ1ZY4OHTi0PkNk8rpwFwTFGyeFVEf2ofkr24RnA
NnUAXEllxOUUNlcoFOz9s3kTeavg3qgqgpa0QmdAIb9LMXg+ec6CKkW7trMpGho8
LxBUWNfSoU4sKEqAvyPT0lWJVo9D/up6/avbAi6TIbOw+Djzel4ZrlHTpabxc3WT
EilXzn4q54b3MzxCQeQjcnzTieW4Q5semG2kLiXFToHIY2di01P/O8awUjgrD+uW
/Cb6H49MnHm9VPkqea1iwZeMQd6Gh5FrC7RezsBjdB1JBcfsv6PFt2ySInjB8SF+
XR5Gr3Cc5sh9s0LfprZ9Dq0rlSWmwasPMI1COK6SswKBgQDczgeWd3erQ1JX9LEI
wollawqC9y7uJhEsw1hrPqA3uqZYiLUc7Nmi4laZ12mcGoXNDS3R3XmD58qGmGaU
lxEVTb8KDVWBgw450VoBKzSMQnCP6zn4nZxTYxeqMKjDGf6TRB6TZc843qsG3eRC
k91yxrCQ/0HV6PT48C+lieDzLwKBgQDF6aNKiyrswr457undBnM1H8q/Y6xC5ZlK
UtiQdhuyBnicvz0U8WPxBY/8gha0OXWuSnBqq/z77iFVNv/zT6p9K7kM7nBGd8cB
8KO6FNbyaHWFrhCI5zNzRTH4oha0hfvUOoti09vqavCtWD4L+D/63ba1wNLKPO9o
4gWbCnUCLwKBgQC/vus372csgrnvR761LLrEJ8BpGt7WUJh5luoht7DKtHvgRleB
Vu1oVcV+s2Iy/ZVUDC3OIdZ0hcWKPK5YOxfKuEk+IXYvke+4peTTPwHTC59UW6Fs
FPK8N0FFuhvT0a8RlAY5WiAp8rPysp6WcnHMSl7qi8BQUozp4Sp/RsziYQKBgBXv
r4mzoy5a53rEYGd/L4XT4EUWZyGDEVqLlDVu4eL5lKTLDZokp08vrqXuRVX0iHap
CYzJQ2EpI8iuL/BoBB2bmwcz5n3pCMXORld5t9lmeqA2it6hwbIlGUTVsm6P6zm6
w3hQwy9YaxTLkxUAjxbfPEEo/jQsTNzzMGve3NlBAoGAbgJExpDyMDnaD2Vi5eyr
63b54BsqeLHqxJmADifyRCj7G1SJMm3zMKkNNOS0vsXgoiId973STFf1XQiojiv8
Slbxyv5rczcY0n3LOuQYcM5OzsjzpNFZsT2dDnMfNRUF3rx3Geu/FuJ9scF1b00r
fVMrcL3jSf/W1Xh4TgtyoU8=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDcTCCAlmgAwIBAgIUDUkgI+2FZtuUHyUUi0ZBH7JvN00wDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZHb29nbGUx
GzAZBgNVBAMMEnRlc3QtczJhLW10bHMtcm9vdDAeFw0yMzA4MjEyMTI5MTVaFw00
MzA4MjEyMTI5MTVaMEgxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UE
CgwGR29vZ2xlMRswGQYDVQQDDBJ0ZXN0LXMyYS1tdGxzLXJvb3QwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbFEQfpvla27bATedrN4BAWsI9GSwSnJLW
QWzXcnAk6cKxQBAhnaKHRxHY8ttLhNTtxQeub894CLzJvHE/0xDhuMzjtCCCZ7i2
r08tKZ1KcEzPJCPNlxlzAXPA45XU3LRlbGvju/PBPhm6n1hCEKTNI/KETJ5DEaYg
Cf2LcXVsl/zW20MwDZ+e2w/9a2a6n6DdpW1ekOR550hXAUOIxvmXRBeYeGLFvp1n
rQgZBhRaxP03UB+PQD2oMi/4mfsS96uGCXdzzX8qV46O8m132HUbnA/wagIwboEe
d7Bx237dERDyHw5GFnll7orgA0FOtoEufXdeQxWVvTjO0+PVPgsvAgMBAAGjUzBR
MB0GA1UdDgQWBBRyMtg/yutV8hw8vOq0i8x0eBQi7DAfBgNVHSMEGDAWgBRyMtg/
yutV8hw8vOq0i8x0eBQi7DAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
A4IBAQArN/gdqWMxd5Rvq2eJMTp6I4RepJOT7Go4sMsRsy1caJqqcoS2EvREDZMN
XNEBcyQBB5kYd6TCcZGoLnEtWYXQ4jjEiXG1g7/+rWxyqw0ZYuP7FWzuHg3Uor/x
fApbEKwptP5ywVc+33h4qreGcqXkVCCn+sAcstGgrqubdGZW2T5gazUMyammOOuN
9IWL1PbvXmgEKD+80NUIrk09zanYyrElGdU/zw/kUbZ3Jf6WUBtJGhTzRQ1qZeKa
VnpCbLoG3vObEB8mxDUAlIzwAtfvw4U32BVIZA8xrocz6OOoAnSW1bTlo3EOIo/G
MTV7jmY9TBPtfhRuO/cG650+F+cw
-----END CERTIFICATE-----

View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDbjCCAlagAwIBAgIUbexZ5sZl86Al9dsI2PkOgtqKnkgwDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZHb29nbGUx
GzAZBgNVBAMMEnRlc3QtczJhLW10bHMtcm9vdDAeFw0yMzA4MjIwMDMyMDRaFw00
MzA4MjIwMDMyMDRaMDkxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEdMBsGA1UE
AwwUdGVzdC1zMmEtbXRscy1zZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCMEzybsGPqfh92GLwy43mt8kQDF3ztr8y06RwU1hVnY7QqYK4obpvh
HkJVnTz9gwNBF3n5nUalqRzactlf2PCydN9oSYNCO8svVmo7vw1CleKAKFAiV5Qn
H76QlqD15oJreh7nSM8R4qj5KukIHvt0cN0gD6CJQzIURDtsKJwkW3yQjYyT/FAK
GYtFrB6buDn3Eg3Hsw6z7uj7CzLBsSl7BIGrQILbpbI9nFNT3rUTUhXZKY/3UtJA
Ob66AjTmMbD16RGYZR4JsPx6CstheifJ6YSI79r5KgD37zX0jMXFWimvb2SmZmFe
LoohtC8K7uTyjm/dROx6nHXdDt5TQYXHAgMBAAGjXzBdMBsGA1UdEQQUMBKHEAAA
AAAAAAAAAAAAAAAAAAAwHQYDVR0OBBYEFI3i2+tIk6YYn0MIxC0q93jk1VsUMB8G
A1UdIwQYMBaAFHIy2D/K61XyHDy86rSLzHR4FCLsMA0GCSqGSIb3DQEBCwUAA4IB
AQAUhk+s/lrIAULBbU7E22C8f93AzTxE1mhyHGNlfPPJP3t1Dl+h4X4WkFpkz5gT
EcNXB//Vvoq99HbEK5/92sxsIPexKdJBdcggeHXIgLDkOrEZEb0Nnh9eaAuU2QDn
JW44hMB+aF6mEaJvOHE6DRkQw3hwFYFisFKKHtlQ3TyOhw5CHGzSExPZusdSFNIe
2E7V/0QzGPJEFnEFUNe9N8nTH2P385Paoi+5+Iizlp/nztVXfzv0Cj/i+qGgtDUs
HB+gBU2wxMw8eYyuNzACH70wqGR1Parj8/JoyYhx0S4+Gjzy3JH3CcAMaxyfH/dI
4Wcvfz/isxgmH1UqIt3oc6ad
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCMEzybsGPqfh92
GLwy43mt8kQDF3ztr8y06RwU1hVnY7QqYK4obpvhHkJVnTz9gwNBF3n5nUalqRza
ctlf2PCydN9oSYNCO8svVmo7vw1CleKAKFAiV5QnH76QlqD15oJreh7nSM8R4qj5
KukIHvt0cN0gD6CJQzIURDtsKJwkW3yQjYyT/FAKGYtFrB6buDn3Eg3Hsw6z7uj7
CzLBsSl7BIGrQILbpbI9nFNT3rUTUhXZKY/3UtJAOb66AjTmMbD16RGYZR4JsPx6
CstheifJ6YSI79r5KgD37zX0jMXFWimvb2SmZmFeLoohtC8K7uTyjm/dROx6nHXd
Dt5TQYXHAgMBAAECggEAIB5zGdIG/yh/Z1GBqfuOFaxFGx5iJ5BVlLAVH9P9IXFz
yPnVRXEjbinFlSMSbqEBeIX9EpcVMXxHIPIP1RIGEy2IYr3kiqXyT771ahDDZh6/
Spqz0UQatSPqyvW3H9uE0Uc12dvQm23JSCUmPRX5m7gbhDQBIChXzdzdcU4Yi59V
4xmJUvbsAcLw5CBM6kwV+1NGVH9+3mUdhrr9M6B6+sVB/xnaqMGEDfQGiwL8U7EY
QOuc46KXu3Pd/qCdVLn60IrdjSzDJKeC5UZZ+ejNAo+DfbtOovBj3qu3OCUg4XVy
0CDBJ1sTdLvUfF4Gb+crjPsd+qBbXcjVfqdadwhsoQKBgQDBF1Pys/NitW8okJwp
2fiDIASP3TiI+MthWHGyuoZGPvmXQ3H6iuLSm8c/iYI2WPTf53Xff1VcFm1GmQms
GCsYM8Ax94zCeO6Ei1sYYxwcBloEZfOeV37MPA4pjJF4Lt+n5nveNxP+lrsjksJz
wToSEgWPDT1b/xcdt4/5j9J85wKBgQC5tiLx+33mwH4DoaFRmSl0+VuSNYFw6DTQ
SQ+kWqWGH4NENc9wf4Dj2VUZQhpXNhXVSxj+aP2d/ck1NrTJAWqYEXCDtFQOGSa2
cGPRr+Fhy5NIEaEvR7IXcMBZzx3koYmWVBHricyrXs5FvHrT3N14mGDUG8n24U3f
R799bau0IQKBgQC97UM+lHCPJCWNggiJRgSifcje9VtZp1btjoBvq/bNe74nYkjn
htsrC91Fiu1Qpdlfr50K1IXSyaB886VG6JLjAGxI+dUzqJ38M9LLvxj0G+9JKjsi
AbAQFfZcOg8QZxLJZPVsE0MQhZTXndC06VhEVAOxvPUg214Sde8hK61/+wKBgCRw
O10VhnePT2pw/VEgZ0T/ZFtEylgYB7zSiRIrgwzVBBGPKVueePC8BPmGwdpYz2Hh
cU8B1Ll6QU+Co2hJMdwSl+wPpup5PuJPHRbYlrV0lzpt0x2OyL/WrLcyb2Ab3f40
EqwPhqwdVwXR3JvTW1U9OMqFhVQ+kuP7lPQMX8NhAoGBAJOgZ7Tokipc4Mi68Olw
SCaOPvjjy4sW2rTRuKyjc1wTAzy7SJ3vXHfGkkN99nTLJFwAyJhWUpnRdwAXGi+x
gyOa95ImsEfRSwEjbluWfF8/P0IU8GR+ZTqT4NnNCOsi8T/xst4Szd1ECJNnnZDe
1ChfPP1AH+/75MJCvu6wQBQv
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDITCCAgkCFBS8mLoytMpMWBwpAtnRaq3eIKnsMA0GCSqGSIb3DQEBCwUAME0x
CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTENMAsGA1UECgwEVGVzdDEiMCAGA1UE
AwwZdGVzdC1zMmEtbXRscy1zZWxmLXNpZ25lZDAeFw0yMzA4MjIyMTE2MDFaFw00
MzA4MjIyMTE2MDFaME0xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTENMAsGA1UE
CgwEVGVzdDEiMCAGA1UEAwwZdGVzdC1zMmEtbXRscy1zZWxmLXNpZ25lZDCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKFFPsYasKZeCFLEXl3RpE/ZOXFe
2lhutIalSpZvCmso+mQGoZ4cHK7At+kDjBi5CrnXkYcw7quQAhHgU0frhWdj7tsW
HUUtq7T8eaGWKBnVD9fl+MjtAl1BmhXwV9qRBbj4EesSKGDSGpKf66dOtzw83JbB
cU7XlPAH1c1zo2GXC1himcZ+SVGHVrOjn4NmeFs8g94/Dke8dWkHwv5YTMVugFK4
5KxKgSOKkr4ka7PCBzgxCnW4wYSZNRHcxrqkiArO2HAQq0ACr7u+fVDYH//9mP2Z
ADo/zch7O5yhkiNbjXJIRrptDWEuVYMRloYDhT773h7bV/Q0Wo0NQGtasJ8CAwEA
ATANBgkqhkiG9w0BAQsFAAOCAQEAPjbH0TMyegF/MDvglkc0sXr6DqlmTxDCZZmG
lYPZ5Xy062+rxIHghMARbvO4BxepiG37KsP2agvOldm4TtU8nQ8LyswmSIFm4BQ+
XQWwdsWyYyd8l0d5sXAdaN6AXwy50fvqCepmEqyreMY6dtLzlwo9gVCBFB7QuAPt
Nc14phpEUZt/KPNuY6cUlB7bz3tmnFbwxUrWj1p0KBEYsr7+KEVZxR+z0wtlU7S9
ZBrmUvx0fq5Ef7JWtHW0w4ofg1op742sdYl+53C26GZ76ts4MmqVz2/94DScgRaU
gT0GLVuuCZXRDVeTXqTb4mditRCfzFPe9cCegYhGhSqBs8yh5A==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQChRT7GGrCmXghS
xF5d0aRP2TlxXtpYbrSGpUqWbwprKPpkBqGeHByuwLfpA4wYuQq515GHMO6rkAIR
4FNH64VnY+7bFh1FLau0/HmhligZ1Q/X5fjI7QJdQZoV8FfakQW4+BHrEihg0hqS
n+unTrc8PNyWwXFO15TwB9XNc6NhlwtYYpnGfklRh1azo5+DZnhbPIPePw5HvHVp
B8L+WEzFboBSuOSsSoEjipK+JGuzwgc4MQp1uMGEmTUR3Ma6pIgKzthwEKtAAq+7
vn1Q2B///Zj9mQA6P83IezucoZIjW41ySEa6bQ1hLlWDEZaGA4U++94e21f0NFqN
DUBrWrCfAgMBAAECggEAR8e8YwyqJ8KezcgdgIC5M9kp2i4v3UCZFX0or8CI0J2S
pUbWVLuKgLXCpfIwPyjNf15Vpei/spkMcsx4BQDthdFTFSzIpmvni0z9DlD5VFYj
ESOJElV7wepbHPy2/c+izmuL/ic81aturGiFyRgeMq+cN3WuaztFTXkPTrzzsZGF
p/Mx3gqm7Hoc3d2xlv+8L5GjCtEJPlQgZJV+s3ennBjOAd8CC7d9qJetE3Er46pn
r5jedV3bQRZYBzmooYNHjbAs26++wYac/jTE0/U6nKS17eWq4BQZUtlMXUw5N81B
7LKn7C03rj2KCn+Nf5uin9ALmoy888LXCDdvL/NZkQKBgQDduv1Heu+tOZuNYUdQ
Hswmd8sVNAAWGZxdxixHMv58zrgbLFXSX6K89X2l5Sj9XON8TH46MuSFdjSwwWw5
fBrhVEhA5srcqpvVWIBE05yqPpt0s1NQktMWJKELWlG8jOhVKwM5OYDpdxtwehpz
1g70XJz+nF/LTV8RdTK+OWDDpQKBgQC6MhdbGHUz/56dY3gZpE5TXnN2hkNbZCgk
emr6z85VHhQflZbedhCzB9PUnZnCKWOGQHQdxRTtRfd46LVboZqCdYO1ZNQv6toP
ysS7dTpZZFy7CpQaW0Y6/jS65jW6xIDKR1W40vgltZ3sfpG37JaowpzWdw2WuOnw
Bg0rcJAf8wKBgQCqE+p/z97UwuF8eufWnyj9QNo382E1koOMspv4KTdnyLETtthF
vDH6O1wbykG8xmmASLRyM+NyNA+KnXNETNvZh2q8zctBpGRQK8iIAsGjHM7ln0AD
B/x+ea5GJQuZU4RK/+lDFca6TjBwAFkWDVX/PqL18kDQkxKfM4SuwRhmOQKBgDGh
eoJIsa0LnP787Z2AI3Srf4F/ZmLs/ppCm1OBotEjdF+64v0nYWonUvqgi8SqfaHi
elEZIGvis4ViGj1zhRjzNAlc+AZRxpBhDzGcnNIJI4Kj3jhsTfsZmXqcNIQ1LtM8
Uogyi/yZPaA1WKg7Aym2vlGYaGHdplXZdxc2KOSrAoGABRkD9l2OVcwK7RyNgFxo
mjxx0tfUdDBhHIi2igih1FiHpeP9E+4/kE/K7PnU9DoDrL1jW1MTpXaYV4seOylk
k9z/9QfcRa9ePD2N4FqbHWSYp5n3aLoIcGq/9jyjTwayZbbIhWO+vNuHE9wIvecZ
8x3gNkxJRb4NaLIoNzAhCoo=
-----END PRIVATE KEY-----

View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID8TCCAtmgAwIBAgIUKCoDuLtiZXvhsBY2RoDm0ugizJ8wDQYJKoZIhvcNAQEL
BQAwgYcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJU3Vubnl2
YWxlMRAwDgYDVQQKDAdDb21wYW55MREwDwYDVQQLDAhEaXZpc2lvbjEWMBQGA1UE
AwwNczJhX3Rlc3RfY2VydDEaMBgGCSqGSIb3DQEJARYLeHl6QHh5ei5jb20wHhcN
MjIwNTMxMjAwODI1WhcNNDIwNTI2MjAwODI1WjCBhzELMAkGA1UEBhMCVVMxCzAJ
BgNVBAgMAkNBMRIwEAYDVQQHDAlTdW5ueXZhbGUxEDAOBgNVBAoMB0NvbXBhbnkx
ETAPBgNVBAsMCERpdmlzaW9uMRYwFAYDVQQDDA1zMmFfdGVzdF9jZXJ0MRowGAYJ
KoZIhvcNAQkBFgt4eXpAeHl6LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAKK1++PXQ+M3hjYH/v0K4UEYl5ljzpNM1i52eQM+gFooojT87PDSaphT
fs0PXy/PTAjHBEvPhWpOpmQXfJNYzjwcCvg66hbqkv++/VTZiFLAsHagzkEz+FRJ
qT5Eq7G5FLyw1izX1uxyPN7tAEWEEg7eqsiaXD3Cq8+TYN9cjirPeF7RZF8yFCYE
xqvbo+Yc6RL6xw19iXVTfctRgQe581KQuIY5/LXo3dWDEilFdsADAe8XAEcO64es
Ow0g1UvXLnpXSE151kXBFb3sKH/ZjCecDYMCIMEb4sWLSblkSxJ5sNSmXIG4wtr2
Qnii7CXZgnVYraQE/Jyh+NMQANuoSdMCAwEAAaNTMFEwHQYDVR0OBBYEFAyQQQuM
ab+YUQqjK8dVVOoHVFmXMB8GA1UdIwQYMBaAFAyQQQuMab+YUQqjK8dVVOoHVFmX
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBADj0vQ6ykWhicoqR
e6VZMwlEJV7/DSvWWKBd9MUjfKye0A4565ya5lmnzP3DiD3nqGe3miqmLsXKDs+X
POqlPXTWIamP7D4MJ32XtSLwZB4ru+I+Ao/P/VngPepoRPQoBnzHe7jww0rokqxl
AZERjlbTUwUAy/BPWPSzSJZ2j0tcs6ZLDNyYzpK4ao8R9/1VmQ92Tcp3feJs1QTg
odRQc3om/AkWOwsll+oyX0UbJeHkFHiLanUPXbdh+/BkSvZJ8ynL+feSDdaurPe+
PSfnqLtQft9/neecGRdEaQzzzSFVQUVQzTdK1Q7hA7b55b2HvIa3ktDiks+sJsYN
Dhm6uZM=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAorX749dD4zeGNgf+/QrhQRiXmWPOk0zWLnZ5Az6AWiiiNPzs
8NJqmFN+zQ9fL89MCMcES8+Fak6mZBd8k1jOPBwK+DrqFuqS/779VNmIUsCwdqDO
QTP4VEmpPkSrsbkUvLDWLNfW7HI83u0ARYQSDt6qyJpcPcKrz5Ng31yOKs94XtFk
XzIUJgTGq9uj5hzpEvrHDX2JdVN9y1GBB7nzUpC4hjn8tejd1YMSKUV2wAMB7xcA
Rw7rh6w7DSDVS9cueldITXnWRcEVvewof9mMJ5wNgwIgwRvixYtJuWRLEnmw1KZc
gbjC2vZCeKLsJdmCdVitpAT8nKH40xAA26hJ0wIDAQABAoIBACaNR+lsD8G+XiZf
LqN1+HkcAo9tfnyYMAdCOtnx7SdviT9Uzi8hK/B7mAeuJLeHPlS2EuaDfPD7QaFl
jza6S+MiIdc+3kgfvESsVAnOoOY6kZUJ9NSuI6CU82y1iJjLaYZrv9NQMLRFPPb0
4KOX709mosB1EnXvshW0rbc+jtDFhrm1SxMt+k9TuzmMxjbOeW4LOLXPgU8X1T3Q
Xy0hMZZtcgBs9wFIo8yCtmOixax9pnFE8rRltgDxTodn9LLdz1FieyntNgDksZ0P
nt4kV7Mqly7ELaea+Foaj244mKsesic2e3GhAlMRLun/VSunSf7mOCxfpITB8dp1
drDhOYECgYEA19151dVxRcviuovN6Dar+QszMTnU8pDJ8BjLFjXjP/hNBBwMTHDE
duMuWk2qnwZqMooI/shxrF/ufmTgS0CFrh2+ANBZu27vWConJNXcyNtdigI4wt50
L0Y2qcZn2mg67qFXHwoR3QNwrwnPwEjRXA09at9CSRZzcwDQ0ETXhYsCgYEAwPaG
06QdK8Zyly7TTzZJwxzv9uGiqzodmGtX6NEKjgij2JaCxHpukqZBJoqa0jKeK1cm
eNVkOvT5ff9TMzarSHQLr3pZen2/oVLb5gaFkbcJt/klv9Fd+ZRilHY3i6QwS6pD
uMiPOWS4DrLHDRVoVlAZTDjT1RVwwTs+P2NhJdkCgYEAsriXysbxBYyMp05gqEW7
lHIFbFgpSrs9th+Q5U6wW6JEgYaHWDJ1NslY80MiZI93FWjbkbZ7BvBWESeL3EIL
a+EMErht0pVCbIhZ6FF4foPAqia0wAJVx14mm+G80kNBp5jE/NnleEsE3KcO7nBb
hg8gLn+x7bk81JZ0TDrzBYkCgYEAuQKluv47SeF3tSScTfKLPpvcKCWmxe1uutkQ
7JShPhVioyOMNb39jnYBOWbjkm4d4QgqRuiytSR0oi3QI+Ziy5EYMyNn713qAk9j
r2TJZDDPDKnBW+zt4YI4EohWMXk3JRUW4XDKggjjwJQA7bZ812TtHHvP/xoThfG7
eSNb3eECgYBw6ssgCtMrdvQiEmjKVX/9yI38mvC2kSGyzbrQnGUfgqRGomRpeZuD
B5E3kysA4td5pT5lvcLgSW0TbOz+YbiriXjwOihPIelCvc9gE2eOUI71/byUWPFz
7u5F/xQ4NaGr5suLF+lBC6h7pSbM4El9lIHQAQadpuEdzHqrw+hs3g==
-----END RSA PRIVATE KEY-----

View File

@@ -1,3 +1,3 @@
{
"v2": "2.7.1"
"v2": "2.11.0"
}

View File

@@ -1,5 +1,50 @@
# Changelog
## [2.11.0](https://github.com/googleapis/gax-go/compare/v2.10.0...v2.11.0) (2023-06-13)
### Features
* **v2:** add GoVersion package variable ([#283](https://github.com/googleapis/gax-go/issues/283)) ([26553cc](https://github.com/googleapis/gax-go/commit/26553ccadb4016b189881f52e6c253b68bb3e3d5))
### Bug Fixes
* **v2:** handle space in non-devel go version ([#288](https://github.com/googleapis/gax-go/issues/288)) ([fd7bca0](https://github.com/googleapis/gax-go/commit/fd7bca029a1c5e63def8f0a5fd1ec3f725d92f75))
## [2.10.0](https://github.com/googleapis/gax-go/compare/v2.9.1...v2.10.0) (2023-05-30)
### Features
* update dependencies ([#280](https://github.com/googleapis/gax-go/issues/280)) ([4514281](https://github.com/googleapis/gax-go/commit/4514281058590f3637c36bfd49baa65c4d3cfb21))
## [2.9.1](https://github.com/googleapis/gax-go/compare/v2.9.0...v2.9.1) (2023-05-23)
### Bug Fixes
* **v2:** drop cloud lro test dep ([#276](https://github.com/googleapis/gax-go/issues/276)) ([c67eeba](https://github.com/googleapis/gax-go/commit/c67eeba0f10a3294b1d93c1b8fbe40211a55ae5f)), refs [#270](https://github.com/googleapis/gax-go/issues/270)
## [2.9.0](https://github.com/googleapis/gax-go/compare/v2.8.0...v2.9.0) (2023-05-22)
### Features
* **apierror:** add method to return HTTP status code conditionally ([#274](https://github.com/googleapis/gax-go/issues/274)) ([5874431](https://github.com/googleapis/gax-go/commit/587443169acd10f7f86d1989dc8aaf189e645e98)), refs [#229](https://github.com/googleapis/gax-go/issues/229)
### Documentation
* add ref to usage with clients ([#272](https://github.com/googleapis/gax-go/issues/272)) ([ea4d72d](https://github.com/googleapis/gax-go/commit/ea4d72d514beba4de450868b5fb028601a29164e)), refs [#228](https://github.com/googleapis/gax-go/issues/228)
## [2.8.0](https://github.com/googleapis/gax-go/compare/v2.7.1...v2.8.0) (2023-03-15)
### Features
* **v2:** add WithTimeout option ([#259](https://github.com/googleapis/gax-go/issues/259)) ([9a8da43](https://github.com/googleapis/gax-go/commit/9a8da43693002448b1e8758023699387481866d1))
## [2.7.1](https://github.com/googleapis/gax-go/compare/v2.7.0...v2.7.1) (2023-03-06)

View File

@@ -29,6 +29,10 @@
// Package apierror implements a wrapper error for parsing error details from
// API calls. Both HTTP & gRPC status errors are supported.
//
// For examples of how to use [APIError] with client libraries please reference
// [Inspecting errors](https://pkg.go.dev/cloud.google.com/go#hdr-Inspecting_errors)
// in the client library documentation.
package apierror
import (
@@ -345,3 +349,13 @@ func parseHTTPDetails(gae *googleapi.Error) ErrDetails {
return parseDetails(details)
}
// HTTPCode returns the underlying HTTP response status code. This method returns
// `-1` if the underlying error is a [google.golang.org/grpc/status.Status]. To
// check gRPC error codes use [google.golang.org/grpc/status.Code].
func (a *APIError) HTTPCode() int {
if a.httpErr == nil {
return -1
}
return a.httpErr.Code
}

View File

@@ -218,6 +218,14 @@ func (p pathOpt) Resolve(s *CallSettings) {
s.Path = p.p
}
type timeoutOpt struct {
t time.Duration
}
func (t timeoutOpt) Resolve(s *CallSettings) {
s.timeout = t.t
}
// WithPath applies a Path override to the HTTP-based APICall.
//
// This is for internal use only.
@@ -230,6 +238,15 @@ func WithGRPCOptions(opt ...grpc.CallOption) CallOption {
return grpcOpt(append([]grpc.CallOption(nil), opt...))
}
// WithTimeout is a convenience option for setting a context.WithTimeout on the
// singular context.Context used for **all** APICall attempts. Calculated from
// the start of the first APICall attempt.
// If the context.Context provided to Invoke already has a Deadline set, that
// will always be respected over the deadline calculated using this option.
func WithTimeout(t time.Duration) CallOption {
return &timeoutOpt{t: t}
}
// CallSettings allow fine-grained control over how calls are made.
type CallSettings struct {
// Retry returns a Retryer to be used to control retry logic of a method call.
@@ -241,4 +258,8 @@ type CallSettings struct {
// Path is an HTTP override for an APICall.
Path string
// Timeout defines the amount of time that Invoke has to complete.
// Unexported so it cannot be changed by the code in an APICall.
timeout time.Duration
}

View File

@@ -29,7 +29,73 @@
package gax
import "bytes"
import (
"bytes"
"runtime"
"strings"
"unicode"
)
var (
// GoVersion is a header-safe representation of the current runtime
// environment's Go version. This is for GAX consumers that need to
// report the Go runtime version in API calls.
GoVersion string
// version is a package internal global variable for testing purposes.
version = runtime.Version
)
// versionUnknown is only used when the runtime version cannot be determined.
const versionUnknown = "UNKNOWN"
func init() {
GoVersion = goVersion()
}
// goVersion returns a Go runtime version derived from the runtime environment
// that is modified to be suitable for reporting in a header, meaning it has no
// whitespace. If it is unable to determine the Go runtime version, it returns
// versionUnknown.
func goVersion() string {
const develPrefix = "devel +"
s := version()
if strings.HasPrefix(s, develPrefix) {
s = s[len(develPrefix):]
if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
s = s[:p]
}
return s
} else if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
s = s[:p]
}
notSemverRune := func(r rune) bool {
return !strings.ContainsRune("0123456789.", r)
}
if strings.HasPrefix(s, "go1") {
s = s[2:]
var prerelease string
if p := strings.IndexFunc(s, notSemverRune); p >= 0 {
s, prerelease = s[:p], s[p:]
}
if strings.HasSuffix(s, ".") {
s += "0"
} else if strings.Count(s, ".") < 2 {
s += ".0"
}
if prerelease != "" {
// Some release candidates already have a dash in them.
if !strings.HasPrefix(prerelease, "-") {
prerelease = "-" + prerelease
}
s += prerelease
}
return s
}
return "UNKNOWN"
}
// XGoogHeader is for use by the Google Cloud Libraries only.
//

View File

@@ -30,4 +30,4 @@
package internal
// Version is the current tagged release of the library.
const Version = "2.7.1"
const Version = "2.11.0"

View File

@@ -68,6 +68,16 @@ type sleeper func(ctx context.Context, d time.Duration) error
// invoke implements Invoke, taking an additional sleeper argument for testing.
func invoke(ctx context.Context, call APICall, settings CallSettings, sp sleeper) error {
var retryer Retryer
// Only use the value provided via WithTimeout if the context doesn't
// already have a deadline. This is important for backwards compatibility if
// the user already set a deadline on the context given to Invoke.
if _, ok := ctx.Deadline(); !ok && settings.timeout != 0 {
c, cc := context.WithTimeout(ctx, settings.timeout)
defer cc()
ctx = c
}
for {
err := call(ctx, settings)
if err == nil {

View File

@@ -1,10 +1,10 @@
//go:build gofuzz
// +build gofuzz
package httprule
func Fuzz(data []byte) int {
_, err := Parse(string(data))
if err != nil {
if _, err := Parse(string(data)); err != nil {
return 0
}
return 0

View File

@@ -1,6 +1,7 @@
package httprule
import (
"errors"
"fmt"
"strings"
)
@@ -164,9 +165,9 @@ func (p *parser) segment() (segment, error) {
v, err := p.variable()
if err != nil {
return nil, fmt.Errorf("segment neither wildcards, literal or variable: %v", err)
return nil, fmt.Errorf("segment neither wildcards, literal or variable: %w", err)
}
return v, err
return v, nil
}
func (p *parser) literal() (segment, error) {
@@ -191,7 +192,7 @@ func (p *parser) variable() (segment, error) {
if _, err := p.accept("="); err == nil {
segs, err = p.segments()
if err != nil {
return nil, fmt.Errorf("invalid segment in variable %q: %v", path, err)
return nil, fmt.Errorf("invalid segment in variable %q: %w", path, err)
}
} else {
segs = []segment{wildcard{}}
@@ -213,12 +214,12 @@ func (p *parser) fieldPath() (string, error) {
}
components := []string{c}
for {
if _, err = p.accept("."); err != nil {
if _, err := p.accept("."); err != nil {
return strings.Join(components, "."), nil
}
c, err := p.accept(typeIdent)
if err != nil {
return "", fmt.Errorf("invalid field path component: %v", err)
return "", fmt.Errorf("invalid field path component: %w", err)
}
components = append(components, c)
}
@@ -237,10 +238,8 @@ const (
typeEOF = termType("$")
)
const (
// eof is the terminal symbol which always appears at the end of token sequence.
eof = "\u0000"
)
// eof is the terminal symbol which always appears at the end of token sequence.
const eof = "\u0000"
// accept tries to accept a token in "p".
// This function consumes a token and returns it if it matches to the specified "term".
@@ -275,11 +274,12 @@ func (p *parser) accept(term termType) (string, error) {
// expectPChars determines if "t" consists of only pchars defined in RFC3986.
//
// https://www.ietf.org/rfc/rfc3986.txt, P.49
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
// / "*" / "+" / "," / ";" / "="
// pct-encoded = "%" HEXDIG HEXDIG
//
// pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
// / "*" / "+" / "," / ";" / "="
// pct-encoded = "%" HEXDIG HEXDIG
func expectPChars(t string) error {
const (
init = iota
@@ -333,7 +333,7 @@ func expectPChars(t string) error {
// expectIdent determines if "ident" is a valid identifier in .proto schema ([[:alpha:]_][[:alphanum:]_]*).
func expectIdent(ident string) error {
if ident == "" {
return fmt.Errorf("empty identifier")
return errors.New("empty identifier")
}
for pos, r := range ident {
switch {

View File

@@ -27,9 +27,9 @@ go_library(
"//internal/httprule",
"//utilities",
"@go_googleapis//google/api:httpbody_go_proto",
"@io_bazel_rules_go//proto/wkt:field_mask_go_proto",
"@org_golang_google_grpc//codes",
"@org_golang_google_grpc//grpclog",
"@org_golang_google_grpc//health/grpc_health_v1",
"@org_golang_google_grpc//metadata",
"@org_golang_google_grpc//status",
"@org_golang_google_protobuf//encoding/protojson",
@@ -37,6 +37,8 @@ go_library(
"@org_golang_google_protobuf//reflect/protoreflect",
"@org_golang_google_protobuf//reflect/protoregistry",
"@org_golang_google_protobuf//types/known/durationpb",
"@org_golang_google_protobuf//types/known/fieldmaskpb",
"@org_golang_google_protobuf//types/known/structpb",
"@org_golang_google_protobuf//types/known/timestamppb",
"@org_golang_google_protobuf//types/known/wrapperspb",
],
@@ -56,8 +58,10 @@ go_test(
"marshal_jsonpb_test.go",
"marshal_proto_test.go",
"marshaler_registry_test.go",
"mux_internal_test.go",
"mux_test.go",
"pattern_test.go",
"query_fuzz_test.go",
"query_test.go",
],
embed = [":runtime"],
@@ -69,8 +73,9 @@ go_test(
"@go_googleapis//google/api:httpbody_go_proto",
"@go_googleapis//google/rpc:errdetails_go_proto",
"@go_googleapis//google/rpc:status_go_proto",
"@io_bazel_rules_go//proto/wkt:field_mask_go_proto",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//codes",
"@org_golang_google_grpc//health/grpc_health_v1",
"@org_golang_google_grpc//metadata",
"@org_golang_google_grpc//status",
"@org_golang_google_protobuf//encoding/protojson",
@@ -78,6 +83,7 @@ go_test(
"@org_golang_google_protobuf//testing/protocmp",
"@org_golang_google_protobuf//types/known/durationpb",
"@org_golang_google_protobuf//types/known/emptypb",
"@org_golang_google_protobuf//types/known/fieldmaskpb",
"@org_golang_google_protobuf//types/known/structpb",
"@org_golang_google_protobuf//types/known/timestamppb",
"@org_golang_google_protobuf//types/known/wrapperspb",

View File

@@ -13,6 +13,7 @@ import (
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
@@ -35,11 +36,15 @@ const metadataHeaderBinarySuffix = "-Bin"
const xForwardedFor = "X-Forwarded-For"
const xForwardedHost = "X-Forwarded-Host"
var (
// DefaultContextTimeout is used for gRPC call context.WithTimeout whenever a Grpc-Timeout inbound
// header isn't present. If the value is 0 the sent `context` will not have a timeout.
DefaultContextTimeout = 0 * time.Second
)
// DefaultContextTimeout is used for gRPC call context.WithTimeout whenever a Grpc-Timeout inbound
// header isn't present. If the value is 0 the sent `context` will not have a timeout.
var DefaultContextTimeout = 0 * time.Second
// malformedHTTPHeaders lists the headers that the gRPC server may reject outright as malformed.
// See https://github.com/grpc/grpc-go/pull/4803#issuecomment-986093310 for more context.
var malformedHTTPHeaders = map[string]struct{}{
"connection": {},
}
type (
rpcMethodKey struct{}
@@ -95,12 +100,43 @@ func AnnotateIncomingContext(ctx context.Context, mux *ServeMux, req *http.Reque
return metadata.NewIncomingContext(ctx, md), nil
}
func isValidGRPCMetadataKey(key string) bool {
// Must be a valid gRPC "Header-Name" as defined here:
// https://github.com/grpc/grpc/blob/4b05dc88b724214d0c725c8e7442cbc7a61b1374/doc/PROTOCOL-HTTP2.md
// This means 0-9 a-z _ - .
// Only lowercase letters are valid in the wire protocol, but the client library will normalize
// uppercase ASCII to lowercase, so uppercase ASCII is also acceptable.
bytes := []byte(key) // gRPC validates strings on the byte level, not Unicode.
for _, ch := range bytes {
validLowercaseLetter := ch >= 'a' && ch <= 'z'
validUppercaseLetter := ch >= 'A' && ch <= 'Z'
validDigit := ch >= '0' && ch <= '9'
validOther := ch == '.' || ch == '-' || ch == '_'
if !validLowercaseLetter && !validUppercaseLetter && !validDigit && !validOther {
return false
}
}
return true
}
func isValidGRPCMetadataTextValue(textValue string) bool {
// Must be a valid gRPC "ASCII-Value" as defined here:
// https://github.com/grpc/grpc/blob/4b05dc88b724214d0c725c8e7442cbc7a61b1374/doc/PROTOCOL-HTTP2.md
// This means printable ASCII (including/plus spaces); 0x20 to 0x7E inclusive.
bytes := []byte(textValue) // gRPC validates strings on the byte level, not Unicode.
for _, ch := range bytes {
if ch < 0x20 || ch > 0x7E {
return false
}
}
return true
}
func annotateContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcMethodName string, options ...AnnotateContextOption) (context.Context, metadata.MD, error) {
ctx = withRPCMethod(ctx, rpcMethodName)
for _, o := range options {
ctx = o(ctx)
}
var pairs []string
timeout := DefaultContextTimeout
if tm := req.Header.Get(metadataGrpcTimeout); tm != "" {
var err error
@@ -109,7 +145,7 @@ func annotateContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcM
return nil, nil, status.Errorf(codes.InvalidArgument, "invalid grpc-timeout: %s", tm)
}
}
var pairs []string
for key, vals := range req.Header {
key = textproto.CanonicalMIMEHeaderKey(key)
for _, val := range vals {
@@ -118,6 +154,10 @@ func annotateContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcM
pairs = append(pairs, "authorization", val)
}
if h, ok := mux.incomingHeaderMatcher(key); ok {
if !isValidGRPCMetadataKey(h) {
grpclog.Errorf("HTTP header name %q is not valid as gRPC metadata key; skipping", h)
continue
}
// Handles "-bin" metadata in grpc, since grpc will do another base64
// encode before sending to server, we need to decode it first.
if strings.HasSuffix(key, metadataHeaderBinarySuffix) {
@@ -127,6 +167,9 @@ func annotateContext(ctx context.Context, mux *ServeMux, req *http.Request, rpcM
}
val = string(b)
} else if !isValidGRPCMetadataTextValue(val) {
grpclog.Errorf("Value of HTTP header %q contains non-ASCII value (not valid as gRPC metadata): skipping", h)
continue
}
pairs = append(pairs, h, val)
}
@@ -172,11 +215,17 @@ type serverMetadataKey struct{}
// NewServerMetadataContext creates a new context with ServerMetadata
func NewServerMetadataContext(ctx context.Context, md ServerMetadata) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, serverMetadataKey{}, md)
}
// ServerMetadataFromContext returns the ServerMetadata in ctx
func ServerMetadataFromContext(ctx context.Context) (md ServerMetadata, ok bool) {
if ctx == nil {
return md, false
}
md, ok = ctx.Value(serverMetadataKey{}).(ServerMetadata)
return
}
@@ -269,8 +318,8 @@ func timeoutUnitToDuration(u uint8) (d time.Duration, ok bool) {
case 'n':
return time.Nanosecond, true
default:
return
}
return
}
// isPermanentHTTPHeader checks whether hdr belongs to the list of
@@ -308,6 +357,13 @@ func isPermanentHTTPHeader(hdr string) bool {
return false
}
// isMalformedHTTPHeader checks whether header belongs to the list of
// "malformed headers" and would be rejected by the gRPC server.
func isMalformedHTTPHeader(header string) bool {
_, isMalformed := malformedHTTPHeaders[strings.ToLower(header)]
return isMalformed
}
// RPCMethod returns the method string for the server context. The returned
// string is in the format of "/package.service/method".
func RPCMethod(ctx context.Context) (string, bool) {

View File

@@ -37,7 +37,7 @@ func BoolSlice(val, sep string) ([]bool, error) {
for i, v := range s {
value, err := Bool(v)
if err != nil {
return values, err
return nil, err
}
values[i] = value
}
@@ -57,7 +57,7 @@ func Float64Slice(val, sep string) ([]float64, error) {
for i, v := range s {
value, err := Float64(v)
if err != nil {
return values, err
return nil, err
}
values[i] = value
}
@@ -81,7 +81,7 @@ func Float32Slice(val, sep string) ([]float32, error) {
for i, v := range s {
value, err := Float32(v)
if err != nil {
return values, err
return nil, err
}
values[i] = value
}
@@ -101,7 +101,7 @@ func Int64Slice(val, sep string) ([]int64, error) {
for i, v := range s {
value, err := Int64(v)
if err != nil {
return values, err
return nil, err
}
values[i] = value
}
@@ -125,7 +125,7 @@ func Int32Slice(val, sep string) ([]int32, error) {
for i, v := range s {
value, err := Int32(v)
if err != nil {
return values, err
return nil, err
}
values[i] = value
}
@@ -145,7 +145,7 @@ func Uint64Slice(val, sep string) ([]uint64, error) {
for i, v := range s {
value, err := Uint64(v)
if err != nil {
return values, err
return nil, err
}
values[i] = value
}
@@ -169,7 +169,7 @@ func Uint32Slice(val, sep string) ([]uint32, error) {
for i, v := range s {
value, err := Uint32(v)
if err != nil {
return values, err
return nil, err
}
values[i] = value
}
@@ -197,7 +197,7 @@ func BytesSlice(val, sep string) ([][]byte, error) {
for i, v := range s {
value, err := Bytes(v)
if err != nil {
return values, err
return nil, err
}
values[i] = value
}
@@ -209,8 +209,7 @@ func Timestamp(val string) (*timestamppb.Timestamp, error) {
var r timestamppb.Timestamp
val = strconv.Quote(strings.Trim(val, `"`))
unmarshaler := &protojson.UnmarshalOptions{}
err := unmarshaler.Unmarshal([]byte(val), &r)
if err != nil {
if err := unmarshaler.Unmarshal([]byte(val), &r); err != nil {
return nil, err
}
return &r, nil
@@ -221,8 +220,7 @@ func Duration(val string) (*durationpb.Duration, error) {
var r durationpb.Duration
val = strconv.Quote(strings.Trim(val, `"`))
unmarshaler := &protojson.UnmarshalOptions{}
err := unmarshaler.Unmarshal([]byte(val), &r)
if err != nil {
if err := unmarshaler.Unmarshal([]byte(val), &r); err != nil {
return nil, err
}
return &r, nil
@@ -257,66 +255,64 @@ func EnumSlice(val, sep string, enumValMap map[string]int32) ([]int32, error) {
for i, v := range s {
value, err := Enum(v, enumValMap)
if err != nil {
return values, err
return nil, err
}
values[i] = value
}
return values, nil
}
/*
Support fot google.protobuf.wrappers on top of primitive types
*/
// Support for google.protobuf.wrappers on top of primitive types
// StringValue well-known type support as wrapper around string type
func StringValue(val string) (*wrapperspb.StringValue, error) {
return &wrapperspb.StringValue{Value: val}, nil
return wrapperspb.String(val), nil
}
// FloatValue well-known type support as wrapper around float32 type
func FloatValue(val string) (*wrapperspb.FloatValue, error) {
parsedVal, err := Float32(val)
return &wrapperspb.FloatValue{Value: parsedVal}, err
return wrapperspb.Float(parsedVal), err
}
// DoubleValue well-known type support as wrapper around float64 type
func DoubleValue(val string) (*wrapperspb.DoubleValue, error) {
parsedVal, err := Float64(val)
return &wrapperspb.DoubleValue{Value: parsedVal}, err
return wrapperspb.Double(parsedVal), err
}
// BoolValue well-known type support as wrapper around bool type
func BoolValue(val string) (*wrapperspb.BoolValue, error) {
parsedVal, err := Bool(val)
return &wrapperspb.BoolValue{Value: parsedVal}, err
return wrapperspb.Bool(parsedVal), err
}
// Int32Value well-known type support as wrapper around int32 type
func Int32Value(val string) (*wrapperspb.Int32Value, error) {
parsedVal, err := Int32(val)
return &wrapperspb.Int32Value{Value: parsedVal}, err
return wrapperspb.Int32(parsedVal), err
}
// UInt32Value well-known type support as wrapper around uint32 type
func UInt32Value(val string) (*wrapperspb.UInt32Value, error) {
parsedVal, err := Uint32(val)
return &wrapperspb.UInt32Value{Value: parsedVal}, err
return wrapperspb.UInt32(parsedVal), err
}
// Int64Value well-known type support as wrapper around int64 type
func Int64Value(val string) (*wrapperspb.Int64Value, error) {
parsedVal, err := Int64(val)
return &wrapperspb.Int64Value{Value: parsedVal}, err
return wrapperspb.Int64(parsedVal), err
}
// UInt64Value well-known type support as wrapper around uint64 type
func UInt64Value(val string) (*wrapperspb.UInt64Value, error) {
parsedVal, err := Uint64(val)
return &wrapperspb.UInt64Value{Value: parsedVal}, err
return wrapperspb.UInt64(parsedVal), err
}
// BytesValue well-known type support as wrapper around bytes[] type
func BytesValue(val string) (*wrapperspb.BytesValue, error) {
parsedVal, err := Bytes(val)
return &wrapperspb.BytesValue{Value: parsedVal}, err
return wrapperspb.Bytes(parsedVal), err
}

View File

@@ -38,7 +38,7 @@ func HTTPStatusFromCode(code codes.Code) int {
case codes.OK:
return http.StatusOK
case codes.Canceled:
return http.StatusRequestTimeout
return 499
case codes.Unknown:
return http.StatusInternalServerError
case codes.InvalidArgument:
@@ -70,10 +70,10 @@ func HTTPStatusFromCode(code codes.Code) int {
return http.StatusServiceUnavailable
case codes.DataLoss:
return http.StatusInternalServerError
default:
grpclog.Infof("Unknown gRPC error code: %v", code)
return http.StatusInternalServerError
}
grpclog.Infof("Unknown gRPC error code: %v", code)
return http.StatusInternalServerError
}
// HTTPError uses the mux-configured error handler.
@@ -162,10 +162,11 @@ func DefaultStreamErrorHandler(_ context.Context, err error) *status.Status {
// DefaultRoutingErrorHandler is our default handler for routing errors.
// By default http error codes mapped on the following error codes:
// NotFound -> grpc.NotFound
// StatusBadRequest -> grpc.InvalidArgument
// MethodNotAllowed -> grpc.Unimplemented
// Other -> grpc.Internal, method is not expecting to be called for anything else
//
// NotFound -> grpc.NotFound
// StatusBadRequest -> grpc.InvalidArgument
// MethodNotAllowed -> grpc.Unimplemented
// Other -> grpc.Internal, method is not expecting to be called for anything else
func DefaultRoutingErrorHandler(ctx context.Context, mux *ServeMux, marshaler Marshaler, w http.ResponseWriter, r *http.Request, httpStatus int) {
sterr := status.Error(codes.Internal, "Unexpected routing error")
switch httpStatus {

View File

@@ -2,13 +2,14 @@ package runtime
import (
"encoding/json"
"errors"
"fmt"
"io"
"sort"
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
field_mask "google.golang.org/protobuf/types/known/fieldmaskpb"
)
func getFieldByName(fields protoreflect.FieldDescriptors, name string) protoreflect.FieldDescriptor {
@@ -44,7 +45,7 @@ func FieldMaskFromRequestBody(r io.Reader, msg proto.Message) (*field_mask.Field
// if the item is an object, then enqueue all of its children
for k, v := range m {
if item.msg == nil {
return nil, fmt.Errorf("JSON structure did not match request type")
return nil, errors.New("JSON structure did not match request type")
}
fd := getFieldByName(item.msg.Descriptor().Fields(), k)
@@ -53,7 +54,7 @@ func FieldMaskFromRequestBody(r io.Reader, msg proto.Message) (*field_mask.Field
}
if isDynamicProtoMessage(fd.Message()) {
for _, p := range buildPathsBlindly(k, v) {
for _, p := range buildPathsBlindly(string(fd.FullName().Name()), v) {
newPath := p
if item.path != "" {
newPath = item.path + "." + newPath
@@ -63,7 +64,7 @@ func FieldMaskFromRequestBody(r io.Reader, msg proto.Message) (*field_mask.Field
continue
}
if isProtobufAnyMessage(fd.Message()) {
if isProtobufAnyMessage(fd.Message()) && !fd.IsList() {
_, hasTypeField := v.(map[string]interface{})["@type"]
if hasTypeField {
queue = append(queue, fieldMaskPathItem{path: k})

View File

@@ -52,11 +52,11 @@ func ForwardResponseStream(ctx context.Context, mux *ServeMux, marshaler Marshal
return
}
if err != nil {
handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err)
handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err, delimiter)
return
}
if err := handleForwardResponseOptions(ctx, w, resp, opts); err != nil {
handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err)
handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err, delimiter)
return
}
@@ -82,15 +82,15 @@ func ForwardResponseStream(ctx context.Context, mux *ServeMux, marshaler Marshal
if err != nil {
grpclog.Infof("Failed to marshal response chunk: %v", err)
handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err)
handleForwardResponseStreamError(ctx, wroteHeader, marshaler, w, req, mux, err, delimiter)
return
}
if _, err = w.Write(buf); err != nil {
if _, err := w.Write(buf); err != nil {
grpclog.Infof("Failed to send response chunk: %v", err)
return
}
wroteHeader = true
if _, err = w.Write(delimiter); err != nil {
if _, err := w.Write(delimiter); err != nil {
grpclog.Infof("Failed to send delimiter chunk: %v", err)
return
}
@@ -200,20 +200,24 @@ func handleForwardResponseOptions(ctx context.Context, w http.ResponseWriter, re
return nil
}
func handleForwardResponseStreamError(ctx context.Context, wroteHeader bool, marshaler Marshaler, w http.ResponseWriter, req *http.Request, mux *ServeMux, err error) {
func handleForwardResponseStreamError(ctx context.Context, wroteHeader bool, marshaler Marshaler, w http.ResponseWriter, req *http.Request, mux *ServeMux, err error, delimiter []byte) {
st := mux.streamErrorHandler(ctx, err)
msg := errorChunk(st)
if !wroteHeader {
w.Header().Set("Content-Type", marshaler.ContentType(msg))
w.WriteHeader(HTTPStatusFromCode(st.Code()))
}
buf, merr := marshaler.Marshal(msg)
if merr != nil {
grpclog.Infof("Failed to marshal an error: %v", merr)
buf, err := marshaler.Marshal(msg)
if err != nil {
grpclog.Infof("Failed to marshal an error: %v", err)
return
}
if _, werr := w.Write(buf); werr != nil {
grpclog.Infof("Failed to notify error to client: %v", werr)
if _, err := w.Write(buf); err != nil {
grpclog.Infof("Failed to notify error to client: %v", err)
return
}
if _, err := w.Write(delimiter); err != nil {
grpclog.Infof("Failed to send delimiter chunk: %v", err)
return
}
}

View File

@@ -92,23 +92,20 @@ func (j *JSONPb) marshalNonProtoField(v interface{}) ([]byte, error) {
if rv.Type().Elem().Implements(protoMessageType) {
var buf bytes.Buffer
err := buf.WriteByte('[')
if err != nil {
if err := buf.WriteByte('['); err != nil {
return nil, err
}
for i := 0; i < rv.Len(); i++ {
if i != 0 {
err = buf.WriteByte(',')
if err != nil {
if err := buf.WriteByte(','); err != nil {
return nil, err
}
}
if err = j.marshalTo(&buf, rv.Index(i).Interface().(proto.Message)); err != nil {
if err := j.marshalTo(&buf, rv.Index(i).Interface().(proto.Message)); err != nil {
return nil, err
}
}
err = buf.WriteByte(']')
if err != nil {
if err := buf.WriteByte(']'); err != nil {
return nil, err
}
@@ -117,17 +114,16 @@ func (j *JSONPb) marshalNonProtoField(v interface{}) ([]byte, error) {
if rv.Type().Elem().Implements(typeProtoEnum) {
var buf bytes.Buffer
err := buf.WriteByte('[')
if err != nil {
if err := buf.WriteByte('['); err != nil {
return nil, err
}
for i := 0; i < rv.Len(); i++ {
if i != 0 {
err = buf.WriteByte(',')
if err != nil {
if err := buf.WriteByte(','); err != nil {
return nil, err
}
}
var err error
if j.UseEnumNumbers {
_, err = buf.WriteString(strconv.FormatInt(rv.Index(i).Int(), 10))
} else {
@@ -137,8 +133,7 @@ func (j *JSONPb) marshalNonProtoField(v interface{}) ([]byte, error) {
return nil, err
}
}
err = buf.WriteByte(']')
if err != nil {
if err := buf.WriteByte(']'); err != nil {
return nil, err
}
@@ -219,8 +214,7 @@ func decodeJSONPb(d *json.Decoder, unmarshaler protojson.UnmarshalOptions, v int
// Decode into bytes for marshalling
var b json.RawMessage
err := d.Decode(&b)
if err != nil {
if err := d.Decode(&b); err != nil {
return err
}
@@ -239,8 +233,7 @@ func decodeNonProtoField(d *json.Decoder, unmarshaler protojson.UnmarshalOptions
if rv.Type().ConvertibleTo(typeProtoMessage) {
// Decode into bytes for marshalling
var b json.RawMessage
err := d.Decode(&b)
if err != nil {
if err := d.Decode(&b); err != nil {
return err
}
@@ -280,6 +273,17 @@ func decodeNonProtoField(d *json.Decoder, unmarshaler protojson.UnmarshalOptions
return nil
}
if rv.Kind() == reflect.Slice {
if rv.Type().Elem().Kind() == reflect.Uint8 {
var sl []byte
if err := d.Decode(&sl); err != nil {
return err
}
if sl != nil {
rv.SetBytes(sl)
}
return nil
}
var sl []json.RawMessage
if err := d.Decode(&sl); err != nil {
return err

View File

@@ -1,10 +1,8 @@
package runtime
import (
"io"
"errors"
"io/ioutil"
"io"
"google.golang.org/protobuf/proto"
)
@@ -38,7 +36,7 @@ func (*ProtoMarshaller) Unmarshal(data []byte, value interface{}) error {
// NewDecoder returns a Decoder which reads proto stream from "reader".
func (marshaller *ProtoMarshaller) NewDecoder(reader io.Reader) Decoder {
return DecoderFunc(func(value interface{}) error {
buffer, err := ioutil.ReadAll(reader)
buffer, err := io.ReadAll(reader)
if err != nil {
return err
}
@@ -53,8 +51,7 @@ func (marshaller *ProtoMarshaller) NewEncoder(writer io.Writer) Encoder {
if err != nil {
return err
}
_, err = writer.Write(buffer)
if err != nil {
if _, err := writer.Write(buffer); err != nil {
return err
}

View File

@@ -6,10 +6,13 @@ import (
"fmt"
"net/http"
"net/textproto"
"regexp"
"strings"
"github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
@@ -23,15 +26,15 @@ const (
// path string before doing any routing.
UnescapingModeLegacy UnescapingMode = iota
// EscapingTypeExceptReserved unescapes all path parameters except RFC 6570
// UnescapingModeAllExceptReserved unescapes all path parameters except RFC 6570
// reserved characters.
UnescapingModeAllExceptReserved
// EscapingTypeExceptSlash unescapes URL path parameters except path
// seperators, which will be left as "%2F".
// UnescapingModeAllExceptSlash unescapes URL path parameters except path
// separators, which will be left as "%2F".
UnescapingModeAllExceptSlash
// URL path parameters will be fully decoded.
// UnescapingModeAllCharacters unescapes all URL path parameters.
UnescapingModeAllCharacters
// UnescapingModeDefault is the default escaping type.
@@ -40,6 +43,8 @@ const (
UnescapingModeDefault = UnescapingModeLegacy
)
var encodedPathSplitter = regexp.MustCompile("(/|%2F)")
// A HandlerFunc handles a specific pair of path pattern and HTTP method.
type HandlerFunc func(w http.ResponseWriter, r *http.Request, pathParams map[string]string)
@@ -75,7 +80,7 @@ func WithForwardResponseOption(forwardResponseOption func(context.Context, http.
}
}
// WithEscapingType sets the escaping type. See the definitions of UnescapingMode
// WithUnescapingMode sets the escaping type. See the definitions of UnescapingMode
// for more information.
func WithUnescapingMode(mode UnescapingMode) ServeMuxOption {
return func(serveMux *ServeMux) {
@@ -96,13 +101,14 @@ func SetQueryParameterParser(queryParameterParser QueryParameterParser) ServeMux
type HeaderMatcherFunc func(string) (string, bool)
// DefaultHeaderMatcher is used to pass http request headers to/from gRPC context. This adds permanent HTTP header
// keys (as specified by the IANA) to gRPC context with grpcgateway- prefix. HTTP headers that start with
// 'Grpc-Metadata-' are mapped to gRPC metadata after removing prefix 'Grpc-Metadata-'.
// keys (as specified by the IANA, e.g: Accept, Cookie, Host) to the gRPC metadata with the grpcgateway- prefix. If you want to know which headers are considered permanent, you can view the isPermanentHTTPHeader function.
// HTTP headers that start with 'Grpc-Metadata-' are mapped to gRPC metadata after removing the prefix 'Grpc-Metadata-'.
// Other headers are not added to the gRPC metadata.
func DefaultHeaderMatcher(key string) (string, bool) {
key = textproto.CanonicalMIMEHeaderKey(key)
if isPermanentHTTPHeader(key) {
switch key = textproto.CanonicalMIMEHeaderKey(key); {
case isPermanentHTTPHeader(key):
return MetadataPrefix + key, true
} else if strings.HasPrefix(key, MetadataHeaderPrefix) {
case strings.HasPrefix(key, MetadataHeaderPrefix):
return key[len(MetadataHeaderPrefix):], true
}
return "", false
@@ -113,11 +119,30 @@ func DefaultHeaderMatcher(key string) (string, bool) {
// This matcher will be called with each header in http.Request. If matcher returns true, that header will be
// passed to gRPC context. To transform the header before passing to gRPC context, matcher should return modified header.
func WithIncomingHeaderMatcher(fn HeaderMatcherFunc) ServeMuxOption {
for _, header := range fn.matchedMalformedHeaders() {
grpclog.Warningf("The configured forwarding filter would allow %q to be sent to the gRPC server, which will likely cause errors. See https://github.com/grpc/grpc-go/pull/4803#issuecomment-986093310 for more information.", header)
}
return func(mux *ServeMux) {
mux.incomingHeaderMatcher = fn
}
}
// matchedMalformedHeaders returns the malformed headers that would be forwarded to gRPC server.
func (fn HeaderMatcherFunc) matchedMalformedHeaders() []string {
if fn == nil {
return nil
}
headers := make([]string, 0)
for header := range malformedHTTPHeaders {
out, accept := fn(header)
if accept && isMalformedHTTPHeader(out) {
headers = append(headers, out)
}
}
return headers
}
// WithOutgoingHeaderMatcher returns a ServeMuxOption representing a headerMatcher for outgoing response from gateway.
//
// This matcher will be called with each header in response header metadata. If matcher returns true, that header will be
@@ -179,6 +204,56 @@ func WithDisablePathLengthFallback() ServeMuxOption {
}
}
// WithHealthEndpointAt returns a ServeMuxOption that will add an endpoint to the created ServeMux at the path specified by endpointPath.
// When called the handler will forward the request to the upstream grpc service health check (defined in the
// gRPC Health Checking Protocol).
//
// See here https://grpc-ecosystem.github.io/grpc-gateway/docs/operations/health_check/ for more information on how
// to setup the protocol in the grpc server.
//
// If you define a service as query parameter, this will also be forwarded as service in the HealthCheckRequest.
func WithHealthEndpointAt(healthCheckClient grpc_health_v1.HealthClient, endpointPath string) ServeMuxOption {
return func(s *ServeMux) {
// error can be ignored since pattern is definitely valid
_ = s.HandlePath(
http.MethodGet, endpointPath, func(w http.ResponseWriter, r *http.Request, _ map[string]string,
) {
_, outboundMarshaler := MarshalerForRequest(s, r)
resp, err := healthCheckClient.Check(r.Context(), &grpc_health_v1.HealthCheckRequest{
Service: r.URL.Query().Get("service"),
})
if err != nil {
s.errorHandler(r.Context(), s, outboundMarshaler, w, r, err)
return
}
w.Header().Set("Content-Type", "application/json")
if resp.GetStatus() != grpc_health_v1.HealthCheckResponse_SERVING {
switch resp.GetStatus() {
case grpc_health_v1.HealthCheckResponse_NOT_SERVING, grpc_health_v1.HealthCheckResponse_UNKNOWN:
err = status.Error(codes.Unavailable, resp.String())
case grpc_health_v1.HealthCheckResponse_SERVICE_UNKNOWN:
err = status.Error(codes.NotFound, resp.String())
}
s.errorHandler(r.Context(), s, outboundMarshaler, w, r, err)
return
}
_ = outboundMarshaler.NewEncoder(w).Encode(resp)
})
}
}
// WithHealthzEndpoint returns a ServeMuxOption that will add a /healthz endpoint to the created ServeMux.
//
// See WithHealthEndpointAt for the general implementation.
func WithHealthzEndpoint(healthCheckClient grpc_health_v1.HealthClient) ServeMuxOption {
return WithHealthEndpointAt(healthCheckClient, "/healthz")
}
// NewServeMux returns a new ServeMux whose internal mapping is empty.
func NewServeMux(opts ...ServeMuxOption) *ServeMux {
serveMux := &ServeMux{
@@ -229,7 +304,7 @@ func (s *ServeMux) HandlePath(meth string, pathPattern string, h HandlerFunc) er
return nil
}
// ServeHTTP dispatches the request to the first handler whose pattern matches to r.Method and r.Path.
// ServeHTTP dispatches the request to the first handler whose pattern matches to r.Method and r.URL.Path.
func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
@@ -245,8 +320,6 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path = r.URL.RawPath
}
components := strings.Split(path[1:], "/")
if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && s.isPathLengthFallback(r) {
r.Method = strings.ToUpper(override)
if err := r.ParseForm(); err != nil {
@@ -257,8 +330,18 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
// Verb out here is to memoize for the fallback case below
var verb string
var pathComponents []string
// since in UnescapeModeLegacy, the URL will already have been fully unescaped, if we also split on "%2F"
// in this escaping mode we would be double unescaping but in UnescapingModeAllCharacters, we still do as the
// path is the RawPath (i.e. unescaped). That does mean that the behavior of this function will change its default
// behavior when the UnescapingModeDefault gets changed from UnescapingModeLegacy to UnescapingModeAllExceptReserved
if s.unescapingMode == UnescapingModeAllCharacters {
pathComponents = encodedPathSplitter.Split(path[1:], -1)
} else {
pathComponents = strings.Split(path[1:], "/")
}
lastPathComponent := pathComponents[len(pathComponents)-1]
for _, h := range s.handlers[r.Method] {
// If the pattern has a verb, explicitly look for a suffix in the last
@@ -269,23 +352,28 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// parser because we know what verb we're looking for, however, there
// are still some cases that the parser itself cannot disambiguate. See
// the comment there if interested.
var verb string
patVerb := h.pat.Verb()
l := len(components)
lastComponent := components[l-1]
var idx int = -1
if patVerb != "" && strings.HasSuffix(lastComponent, ":"+patVerb) {
idx = len(lastComponent) - len(patVerb) - 1
idx := -1
if patVerb != "" && strings.HasSuffix(lastPathComponent, ":"+patVerb) {
idx = len(lastPathComponent) - len(patVerb) - 1
}
if idx == 0 {
_, outboundMarshaler := MarshalerForRequest(s, r)
s.routingErrorHandler(ctx, s, outboundMarshaler, w, r, http.StatusNotFound)
return
}
comps := make([]string, len(pathComponents))
copy(comps, pathComponents)
if idx > 0 {
components[l-1], verb = lastComponent[:idx], lastComponent[idx+1:]
comps[len(comps)-1], verb = lastPathComponent[:idx], lastPathComponent[idx+1:]
}
pathParams, err := h.pat.MatchAndEscape(components, verb, s.unescapingMode)
pathParams, err := h.pat.MatchAndEscape(comps, verb, s.unescapingMode)
if err != nil {
var mse MalformedSequenceError
if ok := errors.As(err, &mse); ok {
@@ -301,14 +389,33 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
// lookup other methods to handle fallback from GET to POST and
// to determine if it is NotImplemented or NotFound.
// if no handler has found for the request, lookup for other methods
// to handle POST -> GET fallback if the request is subject to path
// length fallback.
// Note we are not eagerly checking the request here as we want to return the
// right HTTP status code, and we need to process the fallback candidates in
// order to do that.
for m, handlers := range s.handlers {
if m == r.Method {
continue
}
for _, h := range handlers {
pathParams, err := h.pat.MatchAndEscape(components, verb, s.unescapingMode)
var verb string
patVerb := h.pat.Verb()
idx := -1
if patVerb != "" && strings.HasSuffix(lastPathComponent, ":"+patVerb) {
idx = len(lastPathComponent) - len(patVerb) - 1
}
comps := make([]string, len(pathComponents))
copy(comps, pathComponents)
if idx > 0 {
comps[len(comps)-1], verb = lastPathComponent[:idx], lastPathComponent[idx+1:]
}
pathParams, err := h.pat.MatchAndEscape(comps, verb, s.unescapingMode)
if err != nil {
var mse MalformedSequenceError
if ok := errors.As(err, &mse); ok {
@@ -320,8 +427,11 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
continue
}
// X-HTTP-Method-Override is optional. Always allow fallback to POST.
if s.isPathLengthFallback(r) {
// Also, only consider POST -> GET fallbacks, and avoid falling back to
// potentially dangerous operations like DELETE.
if s.isPathLengthFallback(r) && m == http.MethodGet {
if err := r.ParseForm(); err != nil {
_, outboundMarshaler := MarshalerForRequest(s, r)
sterr := status.Error(codes.InvalidArgument, err.Error())

View File

@@ -15,8 +15,6 @@ var (
ErrNotMatch = errors.New("not match to the path pattern")
// ErrInvalidPattern indicates that the given definition of Pattern is not valid.
ErrInvalidPattern = errors.New("invalid pattern")
// ErrMalformedSequence indicates that an escape sequence was malformed.
ErrMalformedSequence = errors.New("malformed escape sequence")
)
type MalformedSequenceError string

View File

@@ -1,7 +1,6 @@
package runtime
import (
"encoding/base64"
"errors"
"fmt"
"net/url"
@@ -11,19 +10,21 @@ import (
"time"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/grpc/grpclog"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/known/durationpb"
field_mask "google.golang.org/protobuf/types/known/fieldmaskpb"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/timestamppb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
var valuesKeyRegexp = regexp.MustCompile(`^(.*)\[(.*)\]$`)
var currentQueryParser QueryParameterParser = &defaultQueryParser{}
var currentQueryParser QueryParameterParser = &DefaultQueryParser{}
// QueryParameterParser defines interface for all query parameter parsers
type QueryParameterParser interface {
@@ -36,14 +37,17 @@ func PopulateQueryParameters(msg proto.Message, values url.Values, filter *utili
return currentQueryParser.Parse(msg, values, filter)
}
type defaultQueryParser struct{}
// DefaultQueryParser is a QueryParameterParser which implements the default
// query parameters parsing behavior.
//
// See https://github.com/grpc-ecosystem/grpc-gateway/issues/2632 for more context.
type DefaultQueryParser struct{}
// Parse populates "values" into "msg".
// A value is ignored if its key starts with one of the elements in "filter".
func (*defaultQueryParser) Parse(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error {
func (*DefaultQueryParser) Parse(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error {
for key, values := range values {
match := valuesKeyRegexp.FindStringSubmatch(key)
if len(match) == 3 {
if match := valuesKeyRegexp.FindStringSubmatch(key); len(match) == 3 {
key = match[1]
values = append([]string{match[2]}, values...)
}
@@ -175,10 +179,10 @@ func parseField(fieldDescriptor protoreflect.FieldDescriptor, value string) (pro
return protoreflect.ValueOfBool(v), nil
case protoreflect.EnumKind:
enum, err := protoregistry.GlobalTypes.FindEnumByName(fieldDescriptor.Enum().FullName())
switch {
case errors.Is(err, protoregistry.NotFound):
return protoreflect.Value{}, fmt.Errorf("enum %q is not registered", fieldDescriptor.Enum().FullName())
case err != nil:
if err != nil {
if errors.Is(err, protoregistry.NotFound) {
return protoreflect.Value{}, fmt.Errorf("enum %q is not registered", fieldDescriptor.Enum().FullName())
}
return protoreflect.Value{}, fmt.Errorf("failed to look up enum: %w", err)
}
// Look for enum by name
@@ -189,8 +193,7 @@ func parseField(fieldDescriptor protoreflect.FieldDescriptor, value string) (pro
return protoreflect.Value{}, fmt.Errorf("%q is not a valid value", value)
}
// Look for enum by number
v = enum.Descriptor().Values().ByNumber(protoreflect.EnumNumber(i))
if v == nil {
if v = enum.Descriptor().Values().ByNumber(protoreflect.EnumNumber(i)); v == nil {
return protoreflect.Value{}, fmt.Errorf("%q is not a valid value", value)
}
}
@@ -234,7 +237,7 @@ func parseField(fieldDescriptor protoreflect.FieldDescriptor, value string) (pro
case protoreflect.StringKind:
return protoreflect.ValueOfString(value), nil
case protoreflect.BytesKind:
v, err := base64.URLEncoding.DecodeString(value)
v, err := Bytes(value)
if err != nil {
return protoreflect.Value{}, err
}
@@ -250,18 +253,12 @@ func parseMessage(msgDescriptor protoreflect.MessageDescriptor, value string) (p
var msg proto.Message
switch msgDescriptor.FullName() {
case "google.protobuf.Timestamp":
if value == "null" {
break
}
t, err := time.Parse(time.RFC3339Nano, value)
if err != nil {
return protoreflect.Value{}, err
}
msg = timestamppb.New(t)
case "google.protobuf.Duration":
if value == "null" {
break
}
d, err := time.ParseDuration(value)
if err != nil {
return protoreflect.Value{}, err
@@ -272,55 +269,67 @@ func parseMessage(msgDescriptor protoreflect.MessageDescriptor, value string) (p
if err != nil {
return protoreflect.Value{}, err
}
msg = &wrapperspb.DoubleValue{Value: v}
msg = wrapperspb.Double(v)
case "google.protobuf.FloatValue":
v, err := strconv.ParseFloat(value, 32)
if err != nil {
return protoreflect.Value{}, err
}
msg = &wrapperspb.FloatValue{Value: float32(v)}
msg = wrapperspb.Float(float32(v))
case "google.protobuf.Int64Value":
v, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return protoreflect.Value{}, err
}
msg = &wrapperspb.Int64Value{Value: v}
msg = wrapperspb.Int64(v)
case "google.protobuf.Int32Value":
v, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return protoreflect.Value{}, err
}
msg = &wrapperspb.Int32Value{Value: int32(v)}
msg = wrapperspb.Int32(int32(v))
case "google.protobuf.UInt64Value":
v, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return protoreflect.Value{}, err
}
msg = &wrapperspb.UInt64Value{Value: v}
msg = wrapperspb.UInt64(v)
case "google.protobuf.UInt32Value":
v, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return protoreflect.Value{}, err
}
msg = &wrapperspb.UInt32Value{Value: uint32(v)}
msg = wrapperspb.UInt32(uint32(v))
case "google.protobuf.BoolValue":
v, err := strconv.ParseBool(value)
if err != nil {
return protoreflect.Value{}, err
}
msg = &wrapperspb.BoolValue{Value: v}
msg = wrapperspb.Bool(v)
case "google.protobuf.StringValue":
msg = &wrapperspb.StringValue{Value: value}
msg = wrapperspb.String(value)
case "google.protobuf.BytesValue":
v, err := base64.URLEncoding.DecodeString(value)
v, err := Bytes(value)
if err != nil {
return protoreflect.Value{}, err
}
msg = &wrapperspb.BytesValue{Value: v}
msg = wrapperspb.Bytes(v)
case "google.protobuf.FieldMask":
fm := &field_mask.FieldMask{}
fm.Paths = append(fm.Paths, strings.Split(value, ",")...)
msg = fm
case "google.protobuf.Value":
var v structpb.Value
if err := protojson.Unmarshal([]byte(value), &v); err != nil {
return protoreflect.Value{}, err
}
msg = &v
case "google.protobuf.Struct":
var v structpb.Struct
if err := protojson.Unmarshal([]byte(value), &v); err != nil {
return protoreflect.Value{}, err
}
msg = &v
default:
return protoreflect.Value{}, fmt.Errorf("unsupported message type: %q", string(msgDescriptor.FullName()))
}

View File

@@ -8,6 +8,7 @@ go_library(
"doc.go",
"pattern.go",
"readerfactory.go",
"string_array_flag.go",
"trie.go",
],
importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/utilities",
@@ -16,7 +17,10 @@ go_library(
go_test(
name = "utilities_test",
size = "small",
srcs = ["trie_test.go"],
srcs = [
"string_array_flag_test.go",
"trie_test.go",
],
deps = [":utilities"],
)

View File

@@ -3,13 +3,12 @@ package utilities
import (
"bytes"
"io"
"io/ioutil"
)
// IOReaderFactory takes in an io.Reader and returns a function that will allow you to create a new reader that begins
// at the start of the stream
func IOReaderFactory(r io.Reader) (func() io.Reader, error) {
b, err := ioutil.ReadAll(r)
b, err := io.ReadAll(r)
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,33 @@
package utilities
import (
"flag"
"strings"
)
// flagInterface is an cut down interface to `flag`
type flagInterface interface {
Var(value flag.Value, name string, usage string)
}
// StringArrayFlag defines a flag with the specified name and usage string.
// The return value is the address of a `StringArrayFlags` variable that stores the repeated values of the flag.
func StringArrayFlag(f flagInterface, name string, usage string) *StringArrayFlags {
value := &StringArrayFlags{}
f.Var(value, name, usage)
return value
}
// StringArrayFlags is a wrapper of `[]string` to provider an interface for `flag.Var`
type StringArrayFlags []string
// String returns a string representation of `StringArrayFlags`
func (i *StringArrayFlags) String() string {
return strings.Join(*i, ",")
}
// Set appends a value to `StringArrayFlags`
func (i *StringArrayFlags) Set(value string) error {
*i = append(*i, value)
return nil
}

View File

@@ -40,7 +40,7 @@ func NewDoubleArray(seqs [][]string) *DoubleArray {
func registerTokens(da *DoubleArray, seqs [][]string) [][]int {
var result [][]int
for _, seq := range seqs {
var encoded []int
encoded := make([]int, 0, len(seq))
for _, token := range seq {
if _, ok := da.Encoding[token]; !ok {
da.Encoding[token] = len(da.Encoding)