Compare commits

...
10 Commits
Author SHA1 Message Date
Gopher Robot 3d872d0428 go.mod: update golang.org/x dependencies
Update golang.org/x dependencies to their latest tagged versions.
Once this CL is submitted, and post-submit testing succeeds on all
first-class ports across all supported Go versions, this repository
will be tagged with its next minor version.

Change-Id: I25128883772569c8f729b091b0efcbc4afcbea67
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/460500
Run-TryBot: Gopher Robot <gobot@golang.org>
Auto-Submit: Gopher Robot <gobot@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2023-01-04 16:09:43 +00:00
Roland Shoemaker bc7d1d1eb5 bcrypt: reject passwords longer than 72 bytes
By design, bcrypt only uses the first 72 bytes of a password when
generating a hash. Most implementations, including the reference one,
simply silently ignore any trailing input when provided passwords longer
than 72 bytes. This can cause confusion for users who expect the entire
password to be used to generate the hash.

In GenerateFromPassword, reject passwords longer than 72 bytes.
CompareHashAndPassword will still accept these passwords, since we
cannot break hashes that have already been stored.

Fixes golang/go#36546

Change-Id: I039addd2a2961a7fa9d1e4a3e892a9e3c8bf4c9a
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/450415
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Jason McNeil <jmcneil@x2studios.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
2022-12-21 17:19:30 +00:00
Roland Shoemaker 7e3ac2043e internal/wycheproof: also use Verify in TestECDSA
Check both Verify and VerifyASN1 in the ECDSA tests.

Change-Id: Id767354484a7da18ae4e00cd6f2a01a2909e6732
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/453755
Reviewed-by: Than McIntosh <thanm@google.com>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
2022-12-21 16:58:14 +00:00
Bryan C. Mills 23edec0b38 ssh: ensure that handshakeTransport goroutines have finished before Close returns
This fixes a data race in the tests for x/crypto/ssh, which expects to
be able to examine a transport's read and write counters without
locking after closing it.

(Given the number of goroutines, channels, and mutexes used in this
package, I wouldn't be surprised if other concurrency bugs remain.
I would suggest simplifying the concurrency in this package, but I
don't intend to follow up on that myself at the moment.)

Fixes golang/go#56957.

Change-Id: Ib1f1390b66707c66a3608e48f3f52483cff3c1f5
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/456758
Reviewed-by: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
2022-12-12 22:32:12 +00:00
Bryan C. Mills f495dc37d5 acme: eliminate arbitrary timeouts in tests
Fixes golang/go#57107.

Change-Id: I20b1f6ca85170c6b4731d7c7ea06f4db742526cc
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/456123
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Auto-Submit: Bryan Mills <bcmills@google.com>
2022-12-08 18:21:33 +00:00
Gopher Robot eb2c406296 go.mod: update golang.org/x dependencies
Update golang.org/x dependencies to their latest tagged versions.
Once this CL is submitted, and post-submit testing succeeds on all
first-class ports across all supported Go versions, this repository
will be tagged with its next minor version.

Change-Id: If72a913d54ec282d75e270409971b148df4b417c
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/455436
Reviewed-by: Carlos Amedee <carlos@golang.org>
Run-TryBot: Gopher Robot <gobot@golang.org>
Auto-Submit: Gopher Robot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-12-06 15:29:57 +00:00
Filippo Valsorda 2c476679df cryptobyte: add support for ReadASN1Integer into []byte
This lets us extract large integers without involving math/big.

While at it, drop some use of reflect where a type switch will do.

Change-Id: Iebe2fb2267610bf95cf9747ba1d49b5ac9e62cda
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/451515
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
2022-11-17 19:18:49 +00:00
Filippo Valsorda 0ec7e8322c internal/wycheproof: update Go 1.20 crypto/ecdh API
For golang/go#56052

Change-Id: If34d01132e221ff525319e43d127ef14579f9054
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/451095
Reviewed-by: Bryan Mills <bcmills@google.com>
Reviewed-by: Joedian Reid <joedian@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Joedian Reid <joedian@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-11-16 16:25:40 +00:00
6fad3dfc18 ssh: support rsa-sha2-256/512 on the server side
This lets clients know we support rsa-sha2-256/512 signatures from
ssh-rsa public keys. OpenSSH prefers to break the connection rather than
attempting trial and error, apparently.

We don't enable support for the "ext-info-s" because we're not
interested in any client->server extensions.

This also replaces isAcceptableAlgo which was rejecting the
rsa-sha2-256/512-cert-v01@openssh.com public key algorithms.

Tested with OpenSSH 9.1 on macOS Ventura.

Fixes golang/go#49269
Updates golang/go#49952

Co-authored-by: Nicola Murino <nicola.murino@gmail.com>
Co-authored-by: Kristin Davidson <kdavidson@atlassian.com>
Change-Id: I4955c3b12bb45575e9977ac657bb5805b49d00c3
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/447757
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2022-11-12 16:25:23 +00:00
cui fliter 21d60a1521 all: remove redundant type conversion
Change-Id: Ic6b210c1e5b99eef5c6e38d96feaf40e7e6033bb
GitHub-Last-Rev: b8ecf761efe6a2eec78a805a99d778bdcdb938f9
GitHub-Pull-Request: golang/crypto#229
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/429016
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2022-11-09 16:50:04 +00:00
16 changed files with 240 additions and 183 deletions
+9 -22
View File
@@ -354,11 +354,12 @@ func TestWaitAuthorization(t *testing.T) {
}) })
} }
t.Run("context cancel", func(t *testing.T) { t.Run("context cancel", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
_, err := runWaitAuthorization(ctx, t, func(w http.ResponseWriter, r *http.Request) { _, err := runWaitAuthorization(ctx, t, func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Retry-After", "60") w.Header().Set("Retry-After", "60")
fmt.Fprintf(w, `{"status":"pending"}`) fmt.Fprintf(w, `{"status":"pending"}`)
time.AfterFunc(1*time.Millisecond, cancel)
}) })
if err == nil { if err == nil {
t.Error("err is nil") t.Error("err is nil")
@@ -373,28 +374,14 @@ func runWaitAuthorization(ctx context.Context, t *testing.T, h http.HandlerFunc)
h(w, r) h(w, r)
})) }))
defer ts.Close() defer ts.Close()
type res struct {
authz *Authorization client := &Client{
err error Key: testKey,
DirectoryURL: ts.URL,
dir: &Directory{},
KID: "some-key-id", // set to avoid lookup attempt
} }
ch := make(chan res, 1) return client.WaitAuthorization(ctx, ts.URL)
go func() {
client := &Client{
Key: testKey,
DirectoryURL: ts.URL,
dir: &Directory{},
KID: "some-key-id", // set to avoid lookup attempt
}
a, err := client.WaitAuthorization(ctx, ts.URL)
ch <- res{a, err}
}()
select {
case <-time.After(3 * time.Second):
t.Fatal("WaitAuthorization took too long to return")
case v := <-ch:
return v.authz, v.err
}
panic("runWaitAuthorization: out of select")
} }
func TestRevokeAuthorization(t *testing.T) { func TestRevokeAuthorization(t *testing.T) {
+16 -25
View File
@@ -427,18 +427,7 @@ func TestGetCertificate(t *testing.T) {
man.Client = &acme.Client{DirectoryURL: s.URL()} man.Client = &acme.Client{DirectoryURL: s.URL()}
var tlscert *tls.Certificate tlscert, err := man.GetCertificate(tt.hello)
var err error
done := make(chan struct{})
go func() {
tlscert, err = man.GetCertificate(tt.hello)
close(done)
}()
select {
case <-time.After(time.Minute):
t.Fatal("man.GetCertificate took too long to return")
case <-done:
}
if tt.expectError != "" { if tt.expectError != "" {
if err == nil { if err == nil {
t.Fatal("expected error, got certificate") t.Fatal("expected error, got certificate")
@@ -515,15 +504,12 @@ func TestGetCertificate_failedAttempt(t *testing.T) {
if _, err := man.GetCertificate(hello); err == nil { if _, err := man.GetCertificate(hello); err == nil {
t.Error("GetCertificate: err is nil") t.Error("GetCertificate: err is nil")
} }
select {
case <-time.After(5 * time.Second): <-done
t.Errorf("took too long to remove the %q state", exampleCertKey) man.stateMu.Lock()
case <-done: defer man.stateMu.Unlock()
man.stateMu.Lock() if v, exist := man.state[exampleCertKey]; exist {
defer man.stateMu.Unlock() t.Errorf("state exists for %v: %+v", exampleCertKey, v)
if v, exist := man.state[exampleCertKey]; exist {
t.Errorf("state exists for %v: %+v", exampleCertKey, v)
}
} }
} }
@@ -542,8 +528,9 @@ func TestRevokeFailedAuthz(t *testing.T) {
t.Fatal("expected GetCertificate to fail") t.Fatal("expected GetCertificate to fail")
} }
start := time.Now() logTicker := time.NewTicker(3 * time.Second)
for time.Since(start) < 3*time.Second { defer logTicker.Stop()
for {
authz, err := m.Client.GetAuthorization(context.Background(), ca.URL()+"/authz/0") authz, err := m.Client.GetAuthorization(context.Background(), ca.URL()+"/authz/0")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@@ -551,10 +538,14 @@ func TestRevokeFailedAuthz(t *testing.T) {
if authz.Status == acme.StatusDeactivated { if authz.Status == acme.StatusDeactivated {
return return
} }
select {
case <-logTicker.C:
t.Logf("still waiting on revocations")
default:
}
time.Sleep(50 * time.Millisecond) time.Sleep(50 * time.Millisecond)
} }
t.Error("revocations took too long")
} }
func TestHTTPHandlerDefaultFallback(t *testing.T) { func TestHTTPHandlerDefaultFallback(t *testing.T) {
+25 -50
View File
@@ -177,8 +177,7 @@ func TestRFC_postKID(t *testing.T) {
})) }))
defer ts.Close() defer ts.Close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx := context.Background()
defer cancel()
cl := &Client{ cl := &Client{
Key: testKey, Key: testKey,
DirectoryURL: ts.URL, DirectoryURL: ts.URL,
@@ -316,8 +315,7 @@ func TestRFC_Register(t *testing.T) {
s.start() s.start()
defer s.close() defer s.close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx := context.Background()
defer cancel()
cl := &Client{ cl := &Client{
Key: testKeyEC, Key: testKeyEC,
DirectoryURL: s.url("/"), DirectoryURL: s.url("/"),
@@ -454,8 +452,7 @@ func TestRFC_RegisterExternalAccountBinding(t *testing.T) {
s.start() s.start()
defer s.close() defer s.close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx := context.Background()
defer cancel()
cl := &Client{ cl := &Client{
Key: testKeyEC, Key: testKeyEC,
DirectoryURL: s.url("/"), DirectoryURL: s.url("/"),
@@ -867,24 +864,13 @@ func testWaitOrderStatus(t *testing.T, okStatus string) {
s.start() s.start()
defer s.close() defer s.close()
var order *Order cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
var err error order, err := cl.WaitOrder(context.Background(), s.url("/orders/1"))
done := make(chan struct{}) if err != nil {
go func() { t.Fatalf("WaitOrder: %v", err)
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")} }
order, err = cl.WaitOrder(context.Background(), s.url("/orders/1")) if order.Status != okStatus {
close(done) t.Errorf("order.Status = %q; want %q", order.Status, okStatus)
}()
select {
case <-time.After(3 * time.Second):
t.Fatal("WaitOrder took too long to return")
case <-done:
if err != nil {
t.Fatalf("WaitOrder: %v", err)
}
if order.Status != okStatus {
t.Errorf("order.Status = %q; want %q", order.Status, okStatus)
}
} }
} }
@@ -909,30 +895,20 @@ func TestRFC_WaitOrderError(t *testing.T) {
s.start() s.start()
defer s.close() defer s.close()
var err error cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
done := make(chan struct{}) _, err := cl.WaitOrder(context.Background(), s.url("/orders/1"))
go func() { if err == nil {
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")} t.Fatal("WaitOrder returned nil error")
_, err = cl.WaitOrder(context.Background(), s.url("/orders/1")) }
close(done) e, ok := err.(*OrderError)
}() if !ok {
select { t.Fatalf("err = %v (%T); want OrderError", err, err)
case <-time.After(3 * time.Second): }
t.Fatal("WaitOrder took too long to return") if e.OrderURL != s.url("/orders/1") {
case <-done: t.Errorf("e.OrderURL = %q; want %q", e.OrderURL, s.url("/orders/1"))
if err == nil { }
t.Fatal("WaitOrder returned nil error") if e.Status != StatusInvalid {
} t.Errorf("e.Status = %q; want %q", e.Status, StatusInvalid)
e, ok := err.(*OrderError)
if !ok {
t.Fatalf("err = %v (%T); want OrderError", err, err)
}
if e.OrderURL != s.url("/orders/1") {
t.Errorf("e.OrderURL = %q; want %q", e.OrderURL, s.url("/orders/1"))
}
if e.Status != StatusInvalid {
t.Errorf("e.Status = %q; want %q", e.Status, StatusInvalid)
}
} }
} }
@@ -972,8 +948,7 @@ func TestRFC_CreateOrderCert(t *testing.T) {
}) })
s.start() s.start()
defer s.close() defer s.close()
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) ctx := context.Background()
defer cancel()
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")} cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
cert, curl, err := cl.CreateOrderCert(ctx, s.url("/pleaseissue"), csr, true) cert, curl, err := cl.CreateOrderCert(ctx, s.url("/pleaseissue"), csr, true)
+10 -1
View File
@@ -50,7 +50,7 @@ func (ih InvalidHashPrefixError) Error() string {
type InvalidCostError int type InvalidCostError int
func (ic InvalidCostError) Error() string { func (ic InvalidCostError) Error() string {
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost)) return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), MinCost, MaxCost)
} }
const ( const (
@@ -82,11 +82,20 @@ type hashed struct {
minor byte minor byte
} }
// ErrPasswordTooLong is returned when the password passed to
// GenerateFromPassword is too long (i.e. > 72 bytes).
var ErrPasswordTooLong = errors.New("bcrypt: password length exceeds 72 bytes")
// GenerateFromPassword returns the bcrypt hash of the password at the given // GenerateFromPassword returns the bcrypt hash of the password at the given
// cost. If the cost given is less than MinCost, the cost will be set to // cost. If the cost given is less than MinCost, the cost will be set to
// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package, // DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
// to compare the returned hashed password with its cleartext version. // to compare the returned hashed password with its cleartext version.
// GenerateFromPassword does not accept passwords longer than 72 bytes, which
// is the longest password bcrypt will operate on.
func GenerateFromPassword(password []byte, cost int) ([]byte, error) { func GenerateFromPassword(password []byte, cost int) ([]byte, error) {
if len(password) > 72 {
return nil, ErrPasswordTooLong
}
p, err := newFromPassword(password, cost) p, err := newFromPassword(password, cost)
if err != nil { if err != nil {
return nil, err return nil, err
+7
View File
@@ -241,3 +241,10 @@ func TestNoSideEffectsFromCompare(t *testing.T) {
t.Errorf("got=%q want=%q", got, want) t.Errorf("got=%q want=%q", got, want)
} }
} }
func TestPasswordTooLong(t *testing.T) {
_, err := GenerateFromPassword(make([]byte, 73), 1)
if err != ErrPasswordTooLong {
t.Errorf("unexpected error: got %q, want %q", err, ErrPasswordTooLong)
}
}
+42 -35
View File
@@ -264,36 +264,35 @@ func (s *String) ReadASN1Boolean(out *bool) bool {
return true return true
} }
var bigIntType = reflect.TypeOf((*big.Int)(nil)).Elem()
// ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does // ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does
// not point to an integer or to a big.Int, it panics. It reports whether the // not point to an integer, to a big.Int, or to a []byte it panics. Only
// read was successful. // positive and zero values can be decoded into []byte, and they are returned as
// big-endian binary values that share memory with s. Positive values will have
// no leading zeroes, and zero will be returned as a single zero byte.
// ReadASN1Integer reports whether the read was successful.
func (s *String) ReadASN1Integer(out interface{}) bool { func (s *String) ReadASN1Integer(out interface{}) bool {
if reflect.TypeOf(out).Kind() != reflect.Ptr { switch out := out.(type) {
panic("out is not a pointer") case *int, *int8, *int16, *int32, *int64:
}
switch reflect.ValueOf(out).Elem().Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
var i int64 var i int64
if !s.readASN1Int64(&i) || reflect.ValueOf(out).Elem().OverflowInt(i) { if !s.readASN1Int64(&i) || reflect.ValueOf(out).Elem().OverflowInt(i) {
return false return false
} }
reflect.ValueOf(out).Elem().SetInt(i) reflect.ValueOf(out).Elem().SetInt(i)
return true return true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: case *uint, *uint8, *uint16, *uint32, *uint64:
var u uint64 var u uint64
if !s.readASN1Uint64(&u) || reflect.ValueOf(out).Elem().OverflowUint(u) { if !s.readASN1Uint64(&u) || reflect.ValueOf(out).Elem().OverflowUint(u) {
return false return false
} }
reflect.ValueOf(out).Elem().SetUint(u) reflect.ValueOf(out).Elem().SetUint(u)
return true return true
case reflect.Struct: case *big.Int:
if reflect.TypeOf(out).Elem() == bigIntType { return s.readASN1BigInt(out)
return s.readASN1BigInt(out.(*big.Int)) case *[]byte:
} return s.readASN1Bytes(out)
default:
panic("out does not point to an integer type")
} }
panic("out does not point to an integer type")
} }
func checkASN1Integer(bytes []byte) bool { func checkASN1Integer(bytes []byte) bool {
@@ -333,6 +332,21 @@ func (s *String) readASN1BigInt(out *big.Int) bool {
return true return true
} }
func (s *String) readASN1Bytes(out *[]byte) bool {
var bytes String
if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) {
return false
}
if bytes[0]&0x80 == 0x80 {
return false
}
for len(bytes) > 1 && bytes[0] == 0 {
bytes = bytes[1:]
}
*out = bytes
return true
}
func (s *String) readASN1Int64(out *int64) bool { func (s *String) readASN1Int64(out *int64) bool {
var bytes String var bytes String
if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Signed(out, bytes) { if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Signed(out, bytes) {
@@ -532,7 +546,7 @@ func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool {
return false return false
} }
paddingBits := uint8(bytes[0]) paddingBits := bytes[0]
bytes = bytes[1:] bytes = bytes[1:]
if paddingBits > 7 || if paddingBits > 7 ||
len(bytes) == 0 && paddingBits != 0 || len(bytes) == 0 && paddingBits != 0 ||
@@ -554,7 +568,7 @@ func (s *String) ReadASN1BitStringAsBytes(out *[]byte) bool {
return false return false
} }
paddingBits := uint8(bytes[0]) paddingBits := bytes[0]
if paddingBits != 0 { if paddingBits != 0 {
return false return false
} }
@@ -654,34 +668,27 @@ func (s *String) SkipOptionalASN1(tag asn1.Tag) bool {
return s.ReadASN1(&unused, tag) return s.ReadASN1(&unused, tag)
} }
// ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER // ReadOptionalASN1Integer attempts to read an optional ASN.1 INTEGER explicitly
// explicitly tagged with tag into out and advances. If no element with a // tagged with tag into out and advances. If no element with a matching tag is
// matching tag is present, it writes defaultValue into out instead. If out // present, it writes defaultValue into out instead. Otherwise, it behaves like
// does not point to an integer or to a big.Int, it panics. It reports // ReadASN1Integer.
// whether the read was successful.
func (s *String) ReadOptionalASN1Integer(out interface{}, tag asn1.Tag, defaultValue interface{}) bool { func (s *String) ReadOptionalASN1Integer(out interface{}, tag asn1.Tag, defaultValue interface{}) bool {
if reflect.TypeOf(out).Kind() != reflect.Ptr {
panic("out is not a pointer")
}
var present bool var present bool
var i String var i String
if !s.ReadOptionalASN1(&i, &present, tag) { if !s.ReadOptionalASN1(&i, &present, tag) {
return false return false
} }
if !present { if !present {
switch reflect.ValueOf(out).Elem().Kind() { switch out.(type) {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, case *int, *int8, *int16, *int32, *int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: *uint, *uint8, *uint16, *uint32, *uint64, *[]byte:
reflect.ValueOf(out).Elem().Set(reflect.ValueOf(defaultValue)) reflect.ValueOf(out).Elem().Set(reflect.ValueOf(defaultValue))
case reflect.Struct: case *big.Int:
if reflect.TypeOf(out).Elem() != bigIntType { if defaultValue, ok := defaultValue.(*big.Int); ok {
panic("invalid integer type") out.(*big.Int).Set(defaultValue)
} } else {
if reflect.TypeOf(defaultValue).Kind() != reflect.Ptr ||
reflect.TypeOf(defaultValue).Elem() != bigIntType {
panic("out points to big.Int, but defaultValue does not") panic("out points to big.Int, but defaultValue does not")
} }
out.(*big.Int).Set(defaultValue.(*big.Int))
default: default:
panic("invalid integer type") panic("invalid integer type")
} }
+26
View File
@@ -154,6 +154,32 @@ func TestReadASN1IntegerSigned(t *testing.T) {
} }
}) })
// Repeat the same cases, reading into a []byte.
t.Run("bytes", func(t *testing.T) {
for i, test := range testData64 {
in := String(test.in)
var out []byte
ok := in.ReadASN1Integer(&out)
if test.out < 0 {
if ok {
t.Errorf("#%d: in.ReadASN1Integer(%d) = %v, want false", i, test.out, ok)
}
continue
}
if !ok {
t.Errorf("#%d: in.ReadASN1Integer() = %v, want true", i, ok)
continue
}
n := new(big.Int).SetBytes(out).Int64()
if n != test.out {
t.Errorf("#%d: in.ReadASN1Integer() = %v, want true; out = %x, want %d", i, ok, out, test.out)
}
if out[0] == 0 && len(out) > 1 {
t.Errorf("#%d: in.ReadASN1Integer() = %v; out = %x, has leading zeroes", i, ok, out)
}
}
})
// Repeat with the implicit-tagging functions // Repeat with the implicit-tagging functions
t.Run("WithTag", func(t *testing.T) { t.Run("WithTag", func(t *testing.T) {
for i, test := range testData64 { for i, test := range testData64 {
+4 -4
View File
@@ -3,9 +3,9 @@ module golang.org/x/crypto
go 1.17 go 1.17
require ( require (
golang.org/x/net v0.2.0 golang.org/x/net v0.5.0
golang.org/x/sys v0.2.0 golang.org/x/sys v0.4.0
golang.org/x/term v0.2.0 golang.org/x/term v0.4.0
) )
require golang.org/x/text v0.4.0 // indirect require golang.org/x/text v0.6.0 // indirect
+8 -8
View File
@@ -5,8 +5,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -14,17 +14,17 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+1 -1
View File
@@ -118,7 +118,7 @@ func TestECDHStdLib(t *testing.T) {
} }
shared := decodeHex(tt.Shared) shared := decodeHex(tt.Shared)
x, err := curve.ECDH(priv, pub) x, err := priv.ECDH(pub)
if err != nil { if err != nil {
if tg.Curve == "curve25519" && !shouldPass { if tg.Curve == "curve25519" && !shouldPass {
// ECDH is expected to only return an error when using X25519, // ECDH is expected to only return an error when using X25519,
+22 -2
View File
@@ -6,7 +6,11 @@ package wycheproof
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"math/big"
"testing" "testing"
"golang.org/x/crypto/cryptobyte"
"golang.org/x/crypto/cryptobyte/asn1"
) )
func TestECDSA(t *testing.T) { func TestECDSA(t *testing.T) {
@@ -76,9 +80,25 @@ func TestECDSA(t *testing.T) {
h.Reset() h.Reset()
h.Write(decodeHex(sig.Msg)) h.Write(decodeHex(sig.Msg))
hashed := h.Sum(nil) hashed := h.Sum(nil)
got := ecdsa.VerifyASN1(pub, hashed, decodeHex(sig.Sig)) sigBytes := decodeHex(sig.Sig)
got := ecdsa.VerifyASN1(pub, hashed, sigBytes)
if want := shouldPass(sig.Result, sig.Flags, flagsShouldPass); got != want { if want := shouldPass(sig.Result, sig.Flags, flagsShouldPass); got != want {
t.Errorf("tcid: %d, type: %s, comment: %q, wanted success: %t", sig.TcID, sig.Result, sig.Comment, want) t.Errorf("tcid: %d, type: %s, comment: %q, VerifyASN1 wanted success: %t", sig.TcID, sig.Result, sig.Comment, want)
}
var r, s big.Int
var inner cryptobyte.String
input := cryptobyte.String(sigBytes)
if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
!input.Empty() ||
!inner.ReadASN1Integer(&r) ||
!inner.ReadASN1Integer(&s) ||
!inner.Empty() {
continue
}
got = ecdsa.Verify(pub, hashed, &r, &s)
if want := shouldPass(sig.Result, sig.Flags, flagsShouldPass); got != want {
t.Errorf("tcid: %d, type: %s, comment: %q, Verify wanted success: %t", sig.TcID, sig.Result, sig.Comment, want)
} }
} }
} }
+1 -1
View File
@@ -156,7 +156,7 @@ func (r *openpgpReader) Read(p []byte) (n int, err error) {
n, err = r.b64Reader.Read(p) n, err = r.b64Reader.Read(p)
r.currentCRC = crc24(r.currentCRC, p[:n]) r.currentCRC = crc24(r.currentCRC, p[:n])
if err == io.EOF && r.lReader.crcSet && r.lReader.crc != uint32(r.currentCRC&crc24Mask) { if err == io.EOF && r.lReader.crcSet && r.lReader.crc != r.currentCRC&crc24Mask {
return 0, ArmorCorrupt return 0, ArmorCorrupt
} }
+1 -3
View File
@@ -132,9 +132,7 @@ func TestClientAuthPublicKey(t *testing.T) {
if err := tryAuth(t, config); err != nil { if err := tryAuth(t, config); err != nil {
t.Fatalf("unable to dial remote side: %s", err) t.Fatalf("unable to dial remote side: %s", err)
} }
// Once the server implements the server-sig-algs extension, this will turn if len(signer.used) != 1 || signer.used[0] != KeyAlgoRSASHA256 {
// into KeyAlgoRSASHA256.
if len(signer.used) != 1 || signer.used[0] != KeyAlgoRSA {
t.Errorf("unexpected Sign/SignWithAlgorithm calls: %q", signer.used) t.Errorf("unexpected Sign/SignWithAlgorithm calls: %q", signer.used)
} }
} }
+15
View File
@@ -10,6 +10,7 @@ import (
"fmt" "fmt"
"io" "io"
"math" "math"
"strings"
"sync" "sync"
_ "crypto/sha1" _ "crypto/sha1"
@@ -118,6 +119,20 @@ func algorithmsForKeyFormat(keyFormat string) []string {
} }
} }
// supportedPubKeyAuthAlgos specifies the supported client public key
// authentication algorithms. Note that this doesn't include certificate types
// since those use the underlying algorithm. This list is sent to the client if
// it supports the server-sig-algs extension. Order is irrelevant.
var supportedPubKeyAuthAlgos = []string{
KeyAlgoED25519,
KeyAlgoSKED25519, KeyAlgoSKECDSA256,
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA,
KeyAlgoDSA,
}
var supportedPubKeyAuthAlgosList = strings.Join(supportedPubKeyAuthAlgos, ",")
// unexpectedMessageError results when the SSH message that we received didn't // unexpectedMessageError results when the SSH message that we received didn't
// match what we wanted. // match what we wanted.
func unexpectedMessageError(expected, got uint8) error { func unexpectedMessageError(expected, got uint8) error {
+51 -20
View File
@@ -58,11 +58,13 @@ type handshakeTransport struct {
incoming chan []byte incoming chan []byte
readError error readError error
mu sync.Mutex mu sync.Mutex
writeError error writeError error
sentInitPacket []byte sentInitPacket []byte
sentInitMsg *kexInitMsg sentInitMsg *kexInitMsg
pendingPackets [][]byte // Used when a key exchange is in progress. pendingPackets [][]byte // Used when a key exchange is in progress.
writePacketsLeft uint32
writeBytesLeft int64
// If the read loop wants to schedule a kex, it pings this // If the read loop wants to schedule a kex, it pings this
// channel, and the write loop will send out a kex // channel, and the write loop will send out a kex
@@ -71,7 +73,8 @@ type handshakeTransport struct {
// If the other side requests or confirms a kex, its kexInit // If the other side requests or confirms a kex, its kexInit
// packet is sent here for the write loop to find it. // packet is sent here for the write loop to find it.
startKex chan *pendingKex startKex chan *pendingKex
kexLoopDone chan struct{} // closed (with writeError non-nil) when kexLoop exits
// data for host key checking // data for host key checking
hostKeyCallback HostKeyCallback hostKeyCallback HostKeyCallback
@@ -86,12 +89,10 @@ type handshakeTransport struct {
// Algorithms agreed in the last key exchange. // Algorithms agreed in the last key exchange.
algorithms *algorithms algorithms *algorithms
// Counters exclusively owned by readLoop.
readPacketsLeft uint32 readPacketsLeft uint32
readBytesLeft int64 readBytesLeft int64
writePacketsLeft uint32
writeBytesLeft int64
// The session ID or nil if first kex did not complete yet. // The session ID or nil if first kex did not complete yet.
sessionID []byte sessionID []byte
} }
@@ -108,7 +109,8 @@ func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion,
clientVersion: clientVersion, clientVersion: clientVersion,
incoming: make(chan []byte, chanSize), incoming: make(chan []byte, chanSize),
requestKex: make(chan struct{}, 1), requestKex: make(chan struct{}, 1),
startKex: make(chan *pendingKex, 1), startKex: make(chan *pendingKex),
kexLoopDone: make(chan struct{}),
config: config, config: config,
} }
@@ -340,16 +342,17 @@ write:
t.mu.Unlock() t.mu.Unlock()
} }
// drain startKex channel. We don't service t.requestKex
// because nobody does blocking sends there.
go func() {
for init := range t.startKex {
init.done <- t.writeError
}
}()
// Unblock reader. // Unblock reader.
t.conn.Close() t.conn.Close()
// drain startKex channel. We don't service t.requestKex
// because nobody does blocking sends there.
for request := range t.startKex {
request.done <- t.getWriteError()
}
// Mark that the loop is done so that Close can return.
close(t.kexLoopDone)
} }
// The protocol uses uint32 for packet counters, so we can't let them // The protocol uses uint32 for packet counters, so we can't let them
@@ -545,7 +548,16 @@ func (t *handshakeTransport) writePacket(p []byte) error {
} }
func (t *handshakeTransport) Close() error { func (t *handshakeTransport) Close() error {
return t.conn.Close() // Close the connection. This should cause the readLoop goroutine to wake up
// and close t.startKex, which will shut down kexLoop if running.
err := t.conn.Close()
// Wait for the kexLoop goroutine to complete.
// At that point we know that the readLoop goroutine is complete too,
// because kexLoop itself waits for readLoop to close the startKex channel.
<-t.kexLoopDone
return err
} }
func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error { func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
@@ -615,7 +627,8 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
return err return err
} }
if t.sessionID == nil { firstKeyExchange := t.sessionID == nil
if firstKeyExchange {
t.sessionID = result.H t.sessionID = result.H
} }
result.SessionID = t.sessionID result.SessionID = t.sessionID
@@ -626,6 +639,24 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil { if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil {
return err return err
} }
// On the server side, after the first SSH_MSG_NEWKEYS, send a SSH_MSG_EXT_INFO
// message with the server-sig-algs extension if the client supports it. See
// RFC 8308, Sections 2.4 and 3.1.
if !isClient && firstKeyExchange && contains(clientInit.KexAlgos, "ext-info-c") {
extInfo := &extInfoMsg{
NumExtensions: 1,
Payload: make([]byte, 0, 4+15+4+len(supportedPubKeyAuthAlgosList)),
}
extInfo.Payload = appendInt(extInfo.Payload, len("server-sig-algs"))
extInfo.Payload = append(extInfo.Payload, "server-sig-algs"...)
extInfo.Payload = appendInt(extInfo.Payload, len(supportedPubKeyAuthAlgosList))
extInfo.Payload = append(extInfo.Payload, supportedPubKeyAuthAlgosList...)
if err := t.conn.writePacket(Marshal(extInfo)); err != nil {
return err
}
}
if packet, err := t.conn.readPacket(); err != nil { if packet, err := t.conn.readPacket(); err != nil {
return err return err
} else if packet[0] != msgNewKeys { } else if packet[0] != msgNewKeys {
+2 -11
View File
@@ -291,15 +291,6 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
return perms, err return perms, err
} }
func isAcceptableAlgo(algo string) bool {
switch algo {
case KeyAlgoRSA, KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoSKECDSA256, KeyAlgoED25519, KeyAlgoSKED25519,
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
return true
}
return false
}
func checkSourceAddress(addr net.Addr, sourceAddrs string) error { func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
if addr == nil { if addr == nil {
return errors.New("ssh: no address known for client, but source-address match required") return errors.New("ssh: no address known for client, but source-address match required")
@@ -514,7 +505,7 @@ userAuthLoop:
return nil, parseError(msgUserAuthRequest) return nil, parseError(msgUserAuthRequest)
} }
algo := string(algoBytes) algo := string(algoBytes)
if !isAcceptableAlgo(algo) { if !contains(supportedPubKeyAuthAlgos, underlyingAlgo(algo)) {
authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo) authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
break break
} }
@@ -572,7 +563,7 @@ userAuthLoop:
// algorithm name that corresponds to algo with // algorithm name that corresponds to algo with
// sig.Format. This is usually the same, but // sig.Format. This is usually the same, but
// for certs, the names differ. // for certs, the names differ.
if !isAcceptableAlgo(sig.Format) { if !contains(supportedPubKeyAuthAlgos, sig.Format) {
authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format) authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
break break
} }