Skip to content

Commit 711a416

Browse files
authored
Merge pull request #2517 from abrightwell/abrightwell-scram-plus
Add `SCRAM-SHA-256-PLUS` support.
2 parents 1e9d0e9 + 5028389 commit 711a416

File tree

9 files changed

+810
-37
lines changed

9 files changed

+810
-37
lines changed

.devcontainer/docker-compose.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ services:
2929
PGX_TEST_UNIX_SOCKET_CONN_STRING: "host=/var/run/postgresql port=5432 user=postgres dbname=pgx_test"
3030
PGX_TEST_TCP_CONN_STRING: "host=127.0.0.1 port=5432 user=pgx_md5 password=secret dbname=pgx_test"
3131
PGX_TEST_MD5_PASSWORD_CONN_STRING: "host=127.0.0.1 port=5432 user=pgx_md5 password=secret dbname=pgx_test"
32-
PGX_TEST_SCRAM_PASSWORD_CONN_STRING: "host=127.0.0.1 port=5432 user=pgx_scram password=secret dbname=pgx_test"
32+
PGX_TEST_SCRAM_PASSWORD_CONN_STRING: "host=localhost port=5432 user=pgx_scram password=secret dbname=pgx_test channel_binding=disable"
33+
PGX_TEST_SCRAM_PLUS_CONN_STRING: "host=127.0.0.1 port=5432 user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=require"
3334
PGX_TEST_PLAIN_PASSWORD_CONN_STRING: "host=127.0.0.1 port=5432 user=pgx_pw password=secret dbname=pgx_test"
34-
PGX_TEST_TLS_CONN_STRING: "host=localhost port=5432 user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test"
35+
PGX_TEST_TLS_CONN_STRING: "host=localhost port=5432 user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=disable"
3536
PGX_TEST_TLS_CLIENT_CONN_STRING: "host=localhost port=5432 user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test"
3637
PGX_SSL_PASSWORD: certpw
3738

.github/workflows/ci.yml

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,50 +20,55 @@ jobs:
2020
pgx-test-database: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
2121
pgx-test-unix-socket-conn-string: "host=/var/run/postgresql dbname=pgx_test"
2222
pgx-test-tcp-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
23-
pgx-test-scram-password-conn-string: "host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test"
23+
pgx-test-scram-password-conn-string: "host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test channel_binding=disable"
24+
pgx-test-scram-plus-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=require"
2425
pgx-test-md5-password-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
2526
pgx-test-plain-password-conn-string: "host=127.0.0.1 user=pgx_pw password=secret dbname=pgx_test"
26-
pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test"
27+
pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=disable"
2728
pgx-ssl-password: certpw
2829
pgx-test-tls-client-conn-string: "host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test"
2930
- pg-version: 15
3031
pgx-test-database: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
3132
pgx-test-unix-socket-conn-string: "host=/var/run/postgresql dbname=pgx_test"
3233
pgx-test-tcp-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
33-
pgx-test-scram-password-conn-string: "host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test"
34+
pgx-test-scram-password-conn-string: "host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test channel_binding=disable"
35+
pgx-test-scram-plus-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=require"
3436
pgx-test-md5-password-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
3537
pgx-test-plain-password-conn-string: "host=127.0.0.1 user=pgx_pw password=secret dbname=pgx_test"
36-
pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test"
38+
pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=disable"
3739
pgx-ssl-password: certpw
3840
pgx-test-tls-client-conn-string: "host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test"
3941
- pg-version: 16
4042
pgx-test-database: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
4143
pgx-test-unix-socket-conn-string: "host=/var/run/postgresql dbname=pgx_test"
4244
pgx-test-tcp-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
43-
pgx-test-scram-password-conn-string: "host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test"
45+
pgx-test-scram-password-conn-string: "host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test channel_binding=disable"
46+
pgx-test-scram-plus-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=require"
4447
pgx-test-md5-password-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
4548
pgx-test-plain-password-conn-string: "host=127.0.0.1 user=pgx_pw password=secret dbname=pgx_test"
46-
pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test"
49+
pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=disable"
4750
pgx-ssl-password: certpw
4851
pgx-test-tls-client-conn-string: "host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test"
4952
- pg-version: 17
5053
pgx-test-database: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
5154
pgx-test-unix-socket-conn-string: "host=/var/run/postgresql dbname=pgx_test"
5255
pgx-test-tcp-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
53-
pgx-test-scram-password-conn-string: "host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test"
56+
pgx-test-scram-password-conn-string: "host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test channel_binding=disable"
57+
pgx-test-scram-plus-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=require"
5458
pgx-test-md5-password-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
5559
pgx-test-plain-password-conn-string: "host=127.0.0.1 user=pgx_pw password=secret dbname=pgx_test"
56-
pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test"
60+
pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=disable"
5761
pgx-ssl-password: certpw
5862
pgx-test-tls-client-conn-string: "host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test"
5963
- pg-version: 18
6064
pgx-test-database: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
6165
pgx-test-unix-socket-conn-string: "host=/var/run/postgresql dbname=pgx_test"
6266
pgx-test-tcp-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
63-
pgx-test-scram-password-conn-string: "host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test"
67+
pgx-test-scram-password-conn-string: "host=127.0.0.1 user=pgx_scram password=secret dbname=pgx_test channel_binding=disable"
68+
pgx-test-scram-plus-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=require"
6469
pgx-test-md5-password-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
6570
pgx-test-plain-password-conn-string: "host=127.0.0.1 user=pgx_pw password=secret dbname=pgx_test"
66-
pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test"
71+
pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test channel_binding=disable"
6772
pgx-test-oauth: "true"
6873
pgx-ssl-password: certpw
6974
pgx-test-tls-client-conn-string: "host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test"
@@ -119,6 +124,7 @@ jobs:
119124
PGX_TEST_OAUTH: ${{ matrix.pgx-test-oauth }}
120125
# TestConnectTLS fails. However, it succeeds if I connect to the CI server with upterm and run it. Give up on that test for now.
121126
# PGX_TEST_TLS_CONN_STRING: ${{ matrix.pgx-test-tls-conn-string }}
127+
# PGX_TEST_SCRAM_PLUS_CONN_STRING: ${{ matrix.pgx-test-scram-plus-conn-string }}
122128
PGX_SSL_PASSWORD: ${{ matrix.pgx-ssl-password }}
123129
PGX_TEST_TLS_CLIENT_CONN_STRING: ${{ matrix.pgx-test-tls-client-conn-string }}
124130

CONTRIBUTING.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,11 @@ export POSTGRESQL_DATA_DIR=postgresql
8080
export PGX_TEST_DATABASE="host=127.0.0.1 database=pgx_test user=pgx_md5 password=secret"
8181
export PGX_TEST_UNIX_SOCKET_CONN_STRING="host=/private/tmp database=pgx_test"
8282
export PGX_TEST_TCP_CONN_STRING="host=127.0.0.1 database=pgx_test user=pgx_md5 password=secret"
83-
export PGX_TEST_SCRAM_PASSWORD_CONN_STRING="host=127.0.0.1 user=pgx_scram password=secret database=pgx_test"
83+
export PGX_TEST_SCRAM_PASSWORD_CONN_STRING="host=127.0.0.1 user=pgx_scram password=secret database=pgx_test channel_binding=disable"
84+
export PGX_TEST_SCRAM_PLUS_CONN_STRING="host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=`pwd`/.testdb/ca.pem database=pgx_test channel_binding=require"
8485
export PGX_TEST_MD5_PASSWORD_CONN_STRING="host=127.0.0.1 database=pgx_test user=pgx_md5 password=secret"
8586
export PGX_TEST_PLAIN_PASSWORD_CONN_STRING="host=127.0.0.1 user=pgx_pw password=secret"
86-
export PGX_TEST_TLS_CONN_STRING="host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=`pwd`/.testdb/ca.pem"
87+
export PGX_TEST_TLS_CONN_STRING="host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=`pwd`/.testdb/ca.pem channel_binding=disable"
8788
export PGX_SSL_PASSWORD=certpw
8889
export PGX_TEST_TLS_CLIENT_CONN_STRING="host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=`pwd`/.testdb/ca.pem database=pgx_test sslcert=`pwd`/.testdb/pgx_sslcert.crt sslkey=`pwd`/.testdb/pgx_sslcert.key"
8990
```

pgconn/auth_scram.go

Lines changed: 136 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
// SCRAM-SHA-256 authentication
1+
// SCRAM-SHA-256 and SCRAM-SHA-256-PLUS authentication
22
//
33
// Resources:
44
// https://tools.ietf.org/html/rfc5802
5+
// https://tools.ietf.org/html/rfc5929
56
// https://tools.ietf.org/html/rfc8265
67
// https://www.postgresql.org/docs/current/sasl-authentication.html
78
//
@@ -18,17 +19,25 @@ import (
1819
"crypto/pbkdf2"
1920
"crypto/rand"
2021
"crypto/sha256"
22+
"crypto/sha512"
23+
"crypto/tls"
24+
"crypto/x509"
2125
"encoding/base64"
2226
"errors"
2327
"fmt"
28+
"hash"
2429
"slices"
2530
"strconv"
2631

2732
"github.com/jackc/pgx/v5/pgproto3"
2833
"golang.org/x/text/secure/precis"
2934
)
3035

31-
const clientNonceLen = 18
36+
const (
37+
clientNonceLen = 18
38+
scramSHA256Name = "SCRAM-SHA-256"
39+
scramSHA256PlusName = "SCRAM-SHA-256-PLUS"
40+
)
3241

3342
// Perform SCRAM authentication.
3443
func (c *PgConn) scramAuth(serverAuthMechanisms []string) error {
@@ -37,9 +46,35 @@ func (c *PgConn) scramAuth(serverAuthMechanisms []string) error {
3746
return err
3847
}
3948

49+
serverHasPlus := slices.Contains(sc.serverAuthMechanisms, scramSHA256PlusName)
50+
if c.config.ChannelBinding == "require" && !serverHasPlus {
51+
return errors.New("channel binding required but server does not support SCRAM-SHA-256-PLUS")
52+
}
53+
54+
// If we have a TLS connection and channel binding is not disabled, attempt to
55+
// extract the server certificate hash for tls-server-end-point channel binding.
56+
if tlsConn, ok := c.conn.(*tls.Conn); ok && c.config.ChannelBinding != "disable" {
57+
certHash, err := getTLSCertificateHash(tlsConn)
58+
if err != nil && c.config.ChannelBinding == "require" {
59+
return fmt.Errorf("channel binding required but failed to get server certificate hash: %w", err)
60+
}
61+
62+
// Upgrade to SCRAM-SHA-256-PLUS if we have binding data and the server supports it.
63+
if certHash != nil && serverHasPlus {
64+
sc.authMechanism = scramSHA256PlusName
65+
}
66+
67+
sc.channelBindingData = certHash
68+
sc.hasTLS = true
69+
}
70+
71+
if c.config.ChannelBinding == "require" && sc.channelBindingData == nil {
72+
return errors.New("channel binding required but channel binding data is not available")
73+
}
74+
4075
// Send client-first-message in a SASLInitialResponse
4176
saslInitialResponse := &pgproto3.SASLInitialResponse{
42-
AuthMechanism: "SCRAM-SHA-256",
77+
AuthMechanism: sc.authMechanism,
4378
Data: sc.clientFirstMessage(),
4479
}
4580
c.frontend.Send(saslInitialResponse)
@@ -111,7 +146,28 @@ type scramClient struct {
111146
password string
112147
clientNonce []byte
113148

149+
// authMechanism is the selected SASL mechanism for the client. Must be
150+
// either SCRAM-SHA-256 (default) or SCRAM-SHA-256-PLUS.
151+
//
152+
// Upgraded to SCRAM-SHA-256-PLUS during authentication when channel binding
153+
// is not disabled, channel binding data is available (TLS connection with
154+
// an obtainable server certificate hash) and the server advertises
155+
// SCRAM-SHA-256-PLUS.
156+
authMechanism string
157+
158+
// hasTLS indicates whether the connection is using TLS. This is
159+
// needed because the GS2 header must distinguish between a client that
160+
// supports channel binding but the server does not ("y,,") versus one
161+
// that does not support it at all ("n,,").
162+
hasTLS bool
163+
164+
// channelBindingData is the hash of the server's TLS certificate, computed
165+
// per the tls-server-end-point channel binding type (RFC 5929). Used as
166+
// the binding input in SCRAM-SHA-256-PLUS. nil when not in use.
167+
channelBindingData []byte
168+
114169
clientFirstMessageBare []byte
170+
clientGS2Header []byte
115171

116172
serverFirstMessage []byte
117173
clientAndServerNonce []byte
@@ -125,11 +181,14 @@ type scramClient struct {
125181
func newScramClient(serverAuthMechanisms []string, password string) (*scramClient, error) {
126182
sc := &scramClient{
127183
serverAuthMechanisms: serverAuthMechanisms,
184+
authMechanism: scramSHA256Name,
128185
}
129186

130-
// Ensure server supports SCRAM-SHA-256
131-
hasScramSHA256 := slices.Contains(sc.serverAuthMechanisms, "SCRAM-SHA-256")
132-
if !hasScramSHA256 {
187+
// Ensure the server supports SCRAM-SHA-256. SCRAM-SHA-256-PLUS is the
188+
// channel binding variant and is only advertised when the server supports
189+
// SSL. PostgreSQL always advertises the base SCRAM-SHA-256 mechanism
190+
// regardless of SSL.
191+
if !slices.Contains(sc.serverAuthMechanisms, scramSHA256Name) {
133192
return nil, errors.New("server does not support SCRAM-SHA-256")
134193
}
135194

@@ -153,8 +212,32 @@ func newScramClient(serverAuthMechanisms []string, password string) (*scramClien
153212
}
154213

155214
func (sc *scramClient) clientFirstMessage() []byte {
215+
// The client-first-message is the GS2 header concatenated with the bare
216+
// message (username + client nonce). The GS2 header communicates the
217+
// client's channel binding capability to the server:
218+
//
219+
// "n,," - client is not using TLS (channel binding not possible)
220+
// "y,," - client is using TLS but channel binding is not
221+
// in use (e.g., server did not advertise SCRAM-SHA-256-PLUS
222+
// or the server certificate hash was not obtainable)
223+
// "p=tls-server-end-point,," - channel binding is active via SCRAM-SHA-256-PLUS
224+
//
225+
// See:
226+
// https://www.rfc-editor.org/rfc/rfc5802#section-6
227+
// https://www.rfc-editor.org/rfc/rfc5929#section-4
228+
// https://www.postgresql.org/docs/current/sasl-authentication.html#SASL-SCRAM-SHA-256
229+
156230
sc.clientFirstMessageBare = fmt.Appendf(nil, "n=,r=%s", sc.clientNonce)
157-
return fmt.Appendf(nil, "n,,%s", sc.clientFirstMessageBare)
231+
232+
if sc.authMechanism == scramSHA256PlusName {
233+
sc.clientGS2Header = []byte("p=tls-server-end-point,,")
234+
} else if sc.hasTLS {
235+
sc.clientGS2Header = []byte("y,,")
236+
} else {
237+
sc.clientGS2Header = []byte("n,,")
238+
}
239+
240+
return append(sc.clientGS2Header, sc.clientFirstMessageBare...)
158241
}
159242

160243
func (sc *scramClient) recvServerFirstMessage(serverFirstMessage []byte) error {
@@ -213,7 +296,19 @@ func (sc *scramClient) recvServerFirstMessage(serverFirstMessage []byte) error {
213296
}
214297

215298
func (sc *scramClient) clientFinalMessage() string {
216-
clientFinalMessageWithoutProof := fmt.Appendf(nil, "c=biws,r=%s", sc.clientAndServerNonce)
299+
// The c= attribute carries the base64-encoded channel binding input.
300+
//
301+
// Without channel binding this is just the GS2 header alone ("biws" for
302+
// "n,," or "eSws" for "y,,").
303+
//
304+
// With channel binding, this is the GS2 header with the channel binding data
305+
// (certificate hash) appended.
306+
channelBindInput := sc.clientGS2Header
307+
if sc.authMechanism == scramSHA256PlusName {
308+
channelBindInput = slices.Concat(sc.clientGS2Header, sc.channelBindingData)
309+
}
310+
channelBindingEncoded := base64.StdEncoding.EncodeToString(channelBindInput)
311+
clientFinalMessageWithoutProof := fmt.Appendf(nil, "c=%s,r=%s", channelBindingEncoded, sc.clientAndServerNonce)
217312

218313
var err error
219314
sc.saltedPassword, err = pbkdf2.Key(sha256.New, sc.password, sc.salt, sc.iterations, 32)
@@ -269,3 +364,36 @@ func computeServerSignature(saltedPassword, authMessage []byte) []byte {
269364
base64.StdEncoding.Encode(buf, serverSignature)
270365
return buf
271366
}
367+
368+
// Get the server certificate hash for SCRAM channel binding type
369+
// tls-server-end-point.
370+
func getTLSCertificateHash(conn *tls.Conn) ([]byte, error) {
371+
state := conn.ConnectionState()
372+
if len(state.PeerCertificates) == 0 {
373+
return nil, errors.New("no peer certificates for channel binding")
374+
}
375+
376+
cert := state.PeerCertificates[0]
377+
378+
// Per RFC 5929 section 4.1: If the certificate's signatureAlgorithm uses
379+
// MD5 or SHA-1, use SHA-256. Otherwise use the hash from the signature
380+
// algorithm.
381+
//
382+
// See: https://www.rfc-editor.org/rfc/rfc5929.html#section-4.1
383+
var h hash.Hash
384+
switch cert.SignatureAlgorithm {
385+
case x509.MD5WithRSA, x509.SHA1WithRSA, x509.ECDSAWithSHA1:
386+
h = sha256.New()
387+
case x509.SHA256WithRSA, x509.SHA256WithRSAPSS, x509.ECDSAWithSHA256:
388+
h = sha256.New()
389+
case x509.SHA384WithRSA, x509.SHA384WithRSAPSS, x509.ECDSAWithSHA384:
390+
h = sha512.New384()
391+
case x509.SHA512WithRSA, x509.SHA512WithRSAPSS, x509.ECDSAWithSHA512:
392+
h = sha512.New()
393+
default:
394+
return nil, fmt.Errorf("tls-server-end-point channel binding is undefined for certificate signature algorithm %v", cert.SignatureAlgorithm)
395+
}
396+
397+
h.Write(cert.Raw)
398+
return h.Sum(nil), nil
399+
}

0 commit comments

Comments
 (0)