Compare commits

...
8 Commits
Author SHA1 Message Date
Gopher Robot 332fd656f4 go.mod: update golang.org/x dependencies
Update golang.org/x dependencies to their latest tagged versions.

Change-Id: I105ee0f343768881d4fe3a2bfd1fcbaa7e1fd705
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/590218
Auto-Submit: Gopher Robot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
2024-06-04 16:30:12 +00:00
Gopher Robot 0b431c7de3 x509roots/fallback: update bundle
This is an automated CL which updates the NSS root bundle.

Change-Id: I8a1b9637e83214674e6fe82ebf584e9b90446ca3
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/589875
Reviewed-by: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Gopher Robot <gobot@golang.org>
Reviewed-by: Damien Neil <dneil@google.com>
2024-06-03 23:40:54 +00:00
Mariano Cano 349231f7e4 ssh: implement CryptoPublicKey on sk keys
This commit implements the CryptoPublicKey interface for the
skECDSAPublicKey and skEd25519PublicKey types.

Fixes golang/go#62518

Change-Id: I2b8ac89196fbb3614bf5c675127bed23f1cf6b26
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/526875
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Nicola Murino <nicola.murino@gmail.com>
2024-05-29 18:20:30 +00:00
Andrew Lytvynov 44c9b0ff9e ssh: allow server auth callbacks to send additional banners
Add a new BannerError error type that auth callbacks can return to send
banner to the client. While the BannerCallback can send the initial
banner message, auth callbacks might want to communicate more
information to the client to help them diagnose failures.

Updates golang/go#64962

Change-Id: I97a26480ff4064b95a0a26042b0a5e19737cfb62
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/558695
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Nicola Murino <nicola.murino@gmail.com>
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
2024-05-22 20:07:48 +00:00
Mauri de Souza Meneguzzo 67b13616a5 sha3: reenable s390x assembly
Fixes golang/go#64897

Change-Id: I0c8c52d73a7d2df0f44fee36d407a87213f59bff
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/554435
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2024-05-07 22:33:54 +00:00
Filippo Valsorda 477a5b4c32 sha3: make APIs usable with zero allocations
The "buf points into storage" pattern is nice, but causes the whole
state struct to escape, since escape analysis can't track the pointer
once it's assigned to buf.

Change-Id: I31c0e83f946d66bedb5a180e96ab5d5e936eb322
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/544817
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Mauri de Souza Meneguzzo <mauri870@gmail.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
2024-05-07 22:04:09 +00:00
Filippo Valsorda 59b5a86796 sha3: disable s390x assembly
It was integrated in such a way that it made devirtualization
impossible, leading to allocations on every platform.

It can be reintroduced according to AssemblyPolicy and TargetSpecific.

Updates #64897

Change-Id: I3a4edc91185c2928b2c9b80655a2bc8daa6b44e3
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/544816
Reviewed-by: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Mauri de Souza Meneguzzo <mauri870@gmail.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
2024-05-07 22:03:33 +00:00
Filippo Valsorda 10f366e7a2 sha3: simplify XOR functions
name                   old time/op   new time/op   delta
PermutationFunction-4    398ns ± 0%    399ns ± 1%    ~     (p=0.508 n=9+10)
Sha3_512_MTU-4          8.34µs ± 1%   8.36µs ± 1%    ~     (p=0.101 n=10+10)
Sha3_384_MTU-4          6.00µs ± 0%   6.02µs ± 1%  +0.47%  (p=0.000 n=8+10)
Sha3_256_MTU-4          4.78µs ± 0%   4.79µs ± 1%    ~     (p=0.324 n=10+10)
Sha3_224_MTU-4          4.57µs ± 1%   4.57µs ± 1%    ~     (p=0.288 n=10+10)
Shake128_MTU-4          3.87µs ± 0%   3.86µs ± 1%  -0.22%  (p=0.008 n=9+9)
Shake256_MTU-4          4.17µs ± 0%   4.17µs ± 0%    ~     (p=0.474 n=10+8)
Shake256_16x-4          59.4µs ± 0%   59.7µs ± 0%  +0.48%  (p=0.000 n=9+8)
Shake256_1MiB-4         3.19ms ± 1%   3.20ms ± 0%    ~     (p=0.105 n=10+10)
Sha3_512_1MiB-4         5.97ms ± 0%   6.01ms ± 0%  +0.75%  (p=0.000 n=10+10)

name                   old speed     new speed     delta
PermutationFunction-4  502MB/s ± 0%  502MB/s ± 0%    ~     (p=0.497 n=9+10)
Sha3_512_MTU-4         162MB/s ± 1%  161MB/s ± 1%    ~     (p=0.101 n=10+10)
Sha3_384_MTU-4         225MB/s ± 0%  224MB/s ± 1%  -0.47%  (p=0.000 n=8+10)
Sha3_256_MTU-4         282MB/s ± 0%  282MB/s ± 1%    ~     (p=0.325 n=10+10)
Sha3_224_MTU-4         296MB/s ± 1%  295MB/s ± 1%    ~     (p=0.280 n=10+10)
Shake128_MTU-4         349MB/s ± 0%  350MB/s ± 1%  +0.22%  (p=0.008 n=9+9)
Shake256_MTU-4         324MB/s ± 0%  324MB/s ± 0%    ~     (p=0.459 n=10+8)
Shake256_16x-4         276MB/s ± 0%  274MB/s ± 0%  -0.48%  (p=0.000 n=9+8)
Shake256_1MiB-4        328MB/s ± 1%  327MB/s ± 0%    ~     (p=0.105 n=10+10)
Sha3_512_1MiB-4        176MB/s ± 0%  174MB/s ± 0%  -0.74%  (p=0.000 n=10+10)

Change-Id: Ib8e571f3c9a0f84096df2f38ca96da197ad5be30
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/544815
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Mauri de Souza Meneguzzo <mauri870@gmail.com>
2024-05-06 15:29:57 +00:00
20 changed files with 531 additions and 406 deletions
+3 -3
View File
@@ -4,8 +4,8 @@ go 1.18
require ( require (
golang.org/x/net v0.21.0 // tagx:ignore golang.org/x/net v0.21.0 // tagx:ignore
golang.org/x/sys v0.20.0 golang.org/x/sys v0.21.0
golang.org/x/term v0.20.0 golang.org/x/term v0.21.0
) )
require golang.org/x/text v0.15.0 // indirect require golang.org/x/text v0.16.0 // indirect
+6 -6
View File
@@ -1,8 +1,8 @@
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+61
View File
@@ -0,0 +1,61 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !noopt
package sha3_test
import (
"runtime"
"testing"
"golang.org/x/crypto/sha3"
)
var sink byte
func TestAllocations(t *testing.T) {
want := 0.0
if runtime.GOARCH == "s390x" {
// On s390x the returned hash.Hash is conditional so it escapes.
want = 3.0
}
t.Run("New", func(t *testing.T) {
if allocs := testing.AllocsPerRun(10, func() {
h := sha3.New256()
b := []byte("ABC")
h.Write(b)
out := make([]byte, 0, 32)
out = h.Sum(out)
sink ^= out[0]
}); allocs > want {
t.Errorf("expected zero allocations, got %0.1f", allocs)
}
})
t.Run("NewShake", func(t *testing.T) {
if allocs := testing.AllocsPerRun(10, func() {
h := sha3.NewShake128()
b := []byte("ABC")
h.Write(b)
out := make([]byte, 0, 32)
out = h.Sum(out)
sink ^= out[0]
h.Read(out)
sink ^= out[0]
}); allocs > want {
t.Errorf("expected zero allocations, got %0.1f", allocs)
}
})
t.Run("Sum", func(t *testing.T) {
if allocs := testing.AllocsPerRun(10, func() {
b := []byte("ABC")
out := sha3.Sum256(b)
sink ^= out[0]
}); allocs > want {
t.Errorf("expected zero allocations, got %0.1f", allocs)
}
})
}
+19 -15
View File
@@ -16,39 +16,43 @@ import (
// Its generic security strength is 224 bits against preimage attacks, // Its generic security strength is 224 bits against preimage attacks,
// and 112 bits against collision attacks. // and 112 bits against collision attacks.
func New224() hash.Hash { func New224() hash.Hash {
if h := new224Asm(); h != nil { return new224()
return h
}
return &state{rate: 144, outputLen: 28, dsbyte: 0x06}
} }
// New256 creates a new SHA3-256 hash. // New256 creates a new SHA3-256 hash.
// Its generic security strength is 256 bits against preimage attacks, // Its generic security strength is 256 bits against preimage attacks,
// and 128 bits against collision attacks. // and 128 bits against collision attacks.
func New256() hash.Hash { func New256() hash.Hash {
if h := new256Asm(); h != nil { return new256()
return h
}
return &state{rate: 136, outputLen: 32, dsbyte: 0x06}
} }
// New384 creates a new SHA3-384 hash. // New384 creates a new SHA3-384 hash.
// Its generic security strength is 384 bits against preimage attacks, // Its generic security strength is 384 bits against preimage attacks,
// and 192 bits against collision attacks. // and 192 bits against collision attacks.
func New384() hash.Hash { func New384() hash.Hash {
if h := new384Asm(); h != nil { return new384()
return h
}
return &state{rate: 104, outputLen: 48, dsbyte: 0x06}
} }
// New512 creates a new SHA3-512 hash. // New512 creates a new SHA3-512 hash.
// Its generic security strength is 512 bits against preimage attacks, // Its generic security strength is 512 bits against preimage attacks,
// and 256 bits against collision attacks. // and 256 bits against collision attacks.
func New512() hash.Hash { func New512() hash.Hash {
if h := new512Asm(); h != nil { return new512()
return h }
}
func new224Generic() *state {
return &state{rate: 144, outputLen: 28, dsbyte: 0x06}
}
func new256Generic() *state {
return &state{rate: 136, outputLen: 32, dsbyte: 0x06}
}
func new384Generic() *state {
return &state{rate: 104, outputLen: 48, dsbyte: 0x06}
}
func new512Generic() *state {
return &state{rate: 72, outputLen: 64, dsbyte: 0x06} return &state{rate: 72, outputLen: 64, dsbyte: 0x06}
} }
-27
View File
@@ -1,27 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !gc || purego || !s390x
package sha3
import (
"hash"
)
// new224Asm returns an assembly implementation of SHA3-224 if available,
// otherwise it returns nil.
func new224Asm() hash.Hash { return nil }
// new256Asm returns an assembly implementation of SHA3-256 if available,
// otherwise it returns nil.
func new256Asm() hash.Hash { return nil }
// new384Asm returns an assembly implementation of SHA3-384 if available,
// otherwise it returns nil.
func new384Asm() hash.Hash { return nil }
// new512Asm returns an assembly implementation of SHA3-512 if available,
// otherwise it returns nil.
func new512Asm() hash.Hash { return nil }
+23
View File
@@ -0,0 +1,23 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !gc || purego || !s390x
package sha3
func new224() *state {
return new224Generic()
}
func new256() *state {
return new256Generic()
}
func new384() *state {
return new384Generic()
}
func new512() *state {
return new512Generic()
}
+25 -37
View File
@@ -23,7 +23,6 @@ const (
type state struct { type state struct {
// Generic sponge components. // Generic sponge components.
a [25]uint64 // main state of the hash a [25]uint64 // main state of the hash
buf []byte // points into storage
rate int // the number of bytes of state to use rate int // the number of bytes of state to use
// dsbyte contains the "domain separation" bits and the first bit of // dsbyte contains the "domain separation" bits and the first bit of
@@ -40,7 +39,8 @@ type state struct {
// Extendable-Output Functions (May 2014)" // Extendable-Output Functions (May 2014)"
dsbyte byte dsbyte byte
storage storageBuf i, n int // storage[i:n] is the buffer, i is only used while squeezing
storage [maxRate]byte
// Specific to SHA-3 and SHAKE. // Specific to SHA-3 and SHAKE.
outputLen int // the default output size in bytes outputLen int // the default output size in bytes
@@ -54,24 +54,18 @@ func (d *state) BlockSize() int { return d.rate }
func (d *state) Size() int { return d.outputLen } func (d *state) Size() int { return d.outputLen }
// Reset clears the internal state by zeroing the sponge state and // Reset clears the internal state by zeroing the sponge state and
// the byte buffer, and setting Sponge.state to absorbing. // the buffer indexes, and setting Sponge.state to absorbing.
func (d *state) Reset() { func (d *state) Reset() {
// Zero the permutation's state. // Zero the permutation's state.
for i := range d.a { for i := range d.a {
d.a[i] = 0 d.a[i] = 0
} }
d.state = spongeAbsorbing d.state = spongeAbsorbing
d.buf = d.storage.asBytes()[:0] d.i, d.n = 0, 0
} }
func (d *state) clone() *state { func (d *state) clone() *state {
ret := *d ret := *d
if ret.state == spongeAbsorbing {
ret.buf = ret.storage.asBytes()[:len(ret.buf)]
} else {
ret.buf = ret.storage.asBytes()[d.rate-cap(d.buf) : d.rate]
}
return &ret return &ret
} }
@@ -82,43 +76,40 @@ func (d *state) permute() {
case spongeAbsorbing: case spongeAbsorbing:
// If we're absorbing, we need to xor the input into the state // If we're absorbing, we need to xor the input into the state
// before applying the permutation. // before applying the permutation.
xorIn(d, d.buf) xorIn(d, d.storage[:d.rate])
d.buf = d.storage.asBytes()[:0] d.n = 0
keccakF1600(&d.a) keccakF1600(&d.a)
case spongeSqueezing: case spongeSqueezing:
// If we're squeezing, we need to apply the permutation before // If we're squeezing, we need to apply the permutation before
// copying more output. // copying more output.
keccakF1600(&d.a) keccakF1600(&d.a)
d.buf = d.storage.asBytes()[:d.rate] d.i = 0
copyOut(d, d.buf) copyOut(d, d.storage[:d.rate])
} }
} }
// pads appends the domain separation bits in dsbyte, applies // pads appends the domain separation bits in dsbyte, applies
// the multi-bitrate 10..1 padding rule, and permutes the state. // the multi-bitrate 10..1 padding rule, and permutes the state.
func (d *state) padAndPermute(dsbyte byte) { func (d *state) padAndPermute() {
if d.buf == nil {
d.buf = d.storage.asBytes()[:0]
}
// Pad with this instance's domain-separator bits. We know that there's // Pad with this instance's domain-separator bits. We know that there's
// at least one byte of space in d.buf because, if it were full, // at least one byte of space in d.buf because, if it were full,
// permute would have been called to empty it. dsbyte also contains the // permute would have been called to empty it. dsbyte also contains the
// first one bit for the padding. See the comment in the state struct. // first one bit for the padding. See the comment in the state struct.
d.buf = append(d.buf, dsbyte) d.storage[d.n] = d.dsbyte
zerosStart := len(d.buf) d.n++
d.buf = d.storage.asBytes()[:d.rate] for d.n < d.rate {
for i := zerosStart; i < d.rate; i++ { d.storage[d.n] = 0
d.buf[i] = 0 d.n++
} }
// This adds the final one bit for the padding. Because of the way that // This adds the final one bit for the padding. Because of the way that
// bits are numbered from the LSB upwards, the final bit is the MSB of // bits are numbered from the LSB upwards, the final bit is the MSB of
// the last byte. // the last byte.
d.buf[d.rate-1] ^= 0x80 d.storage[d.rate-1] ^= 0x80
// Apply the permutation // Apply the permutation
d.permute() d.permute()
d.state = spongeSqueezing d.state = spongeSqueezing
d.buf = d.storage.asBytes()[:d.rate] d.n = d.rate
copyOut(d, d.buf) copyOut(d, d.storage[:d.rate])
} }
// Write absorbs more data into the hash's state. It panics if any // Write absorbs more data into the hash's state. It panics if any
@@ -127,28 +118,25 @@ func (d *state) Write(p []byte) (written int, err error) {
if d.state != spongeAbsorbing { if d.state != spongeAbsorbing {
panic("sha3: Write after Read") panic("sha3: Write after Read")
} }
if d.buf == nil {
d.buf = d.storage.asBytes()[:0]
}
written = len(p) written = len(p)
for len(p) > 0 { for len(p) > 0 {
if len(d.buf) == 0 && len(p) >= d.rate { if d.n == 0 && len(p) >= d.rate {
// The fast path; absorb a full "rate" bytes of input and apply the permutation. // The fast path; absorb a full "rate" bytes of input and apply the permutation.
xorIn(d, p[:d.rate]) xorIn(d, p[:d.rate])
p = p[d.rate:] p = p[d.rate:]
keccakF1600(&d.a) keccakF1600(&d.a)
} else { } else {
// The slow path; buffer the input until we can fill the sponge, and then xor it in. // The slow path; buffer the input until we can fill the sponge, and then xor it in.
todo := d.rate - len(d.buf) todo := d.rate - d.n
if todo > len(p) { if todo > len(p) {
todo = len(p) todo = len(p)
} }
d.buf = append(d.buf, p[:todo]...) d.n += copy(d.storage[d.n:], p[:todo])
p = p[todo:] p = p[todo:]
// If the sponge is full, apply the permutation. // If the sponge is full, apply the permutation.
if len(d.buf) == d.rate { if d.n == d.rate {
d.permute() d.permute()
} }
} }
@@ -161,19 +149,19 @@ func (d *state) Write(p []byte) (written int, err error) {
func (d *state) Read(out []byte) (n int, err error) { func (d *state) Read(out []byte) (n int, err error) {
// If we're still absorbing, pad and apply the permutation. // If we're still absorbing, pad and apply the permutation.
if d.state == spongeAbsorbing { if d.state == spongeAbsorbing {
d.padAndPermute(d.dsbyte) d.padAndPermute()
} }
n = len(out) n = len(out)
// Now, do the squeezing. // Now, do the squeezing.
for len(out) > 0 { for len(out) > 0 {
n := copy(out, d.buf) n := copy(out, d.storage[d.i:d.n])
d.buf = d.buf[n:] d.i += n
out = out[n:] out = out[n:]
// Apply the permutation if we've squeezed the sponge dry. // Apply the permutation if we've squeezed the sponge dry.
if len(d.buf) == 0 { if d.i == d.rate {
d.permute() d.permute()
} }
} }
+24 -24
View File
@@ -248,56 +248,56 @@ func (s *asmState) Clone() ShakeHash {
return s.clone() return s.clone()
} }
// new224Asm returns an assembly implementation of SHA3-224 if available, // new224 returns an assembly implementation of SHA3-224 if available,
// otherwise it returns nil. // otherwise it returns a generic implementation.
func new224Asm() hash.Hash { func new224() hash.Hash {
if cpu.S390X.HasSHA3 { if cpu.S390X.HasSHA3 {
return newAsmState(sha3_224) return newAsmState(sha3_224)
} }
return nil return new224Generic()
} }
// new256Asm returns an assembly implementation of SHA3-256 if available, // new256 returns an assembly implementation of SHA3-256 if available,
// otherwise it returns nil. // otherwise it returns a generic implementation.
func new256Asm() hash.Hash { func new256() hash.Hash {
if cpu.S390X.HasSHA3 { if cpu.S390X.HasSHA3 {
return newAsmState(sha3_256) return newAsmState(sha3_256)
} }
return nil return new256Generic()
} }
// new384Asm returns an assembly implementation of SHA3-384 if available, // new384 returns an assembly implementation of SHA3-384 if available,
// otherwise it returns nil. // otherwise it returns a generic implementation.
func new384Asm() hash.Hash { func new384() hash.Hash {
if cpu.S390X.HasSHA3 { if cpu.S390X.HasSHA3 {
return newAsmState(sha3_384) return newAsmState(sha3_384)
} }
return nil return new384Generic()
} }
// new512Asm returns an assembly implementation of SHA3-512 if available, // new512 returns an assembly implementation of SHA3-512 if available,
// otherwise it returns nil. // otherwise it returns a generic implementation.
func new512Asm() hash.Hash { func new512() hash.Hash {
if cpu.S390X.HasSHA3 { if cpu.S390X.HasSHA3 {
return newAsmState(sha3_512) return newAsmState(sha3_512)
} }
return nil return new512Generic()
} }
// newShake128Asm returns an assembly implementation of SHAKE-128 if available, // newShake128 returns an assembly implementation of SHAKE-128 if available,
// otherwise it returns nil. // otherwise it returns a generic implementation.
func newShake128Asm() ShakeHash { func newShake128() ShakeHash {
if cpu.S390X.HasSHA3 { if cpu.S390X.HasSHA3 {
return newAsmState(shake_128) return newAsmState(shake_128)
} }
return nil return newShake128Generic()
} }
// newShake256Asm returns an assembly implementation of SHAKE-256 if available, // newShake256 returns an assembly implementation of SHAKE-256 if available,
// otherwise it returns nil. // otherwise it returns a generic implementation.
func newShake256Asm() ShakeHash { func newShake256() ShakeHash {
if cpu.S390X.HasSHA3 { if cpu.S390X.HasSHA3 {
return newAsmState(shake_256) return newAsmState(shake_256)
} }
return nil return newShake256Generic()
} }
+139 -160
View File
@@ -76,86 +76,73 @@ type KeccakKats struct {
} }
} }
func testUnalignedAndGeneric(t *testing.T, testf func(impl string)) {
xorInOrig, copyOutOrig := xorIn, copyOut
xorIn, copyOut = xorInGeneric, copyOutGeneric
testf("generic")
if xorImplementationUnaligned != "generic" {
xorIn, copyOut = xorInUnaligned, copyOutUnaligned
testf("unaligned")
}
xorIn, copyOut = xorInOrig, copyOutOrig
}
// TestKeccakKats tests the SHA-3 and Shake implementations against all the // TestKeccakKats tests the SHA-3 and Shake implementations against all the
// ShortMsgKATs from https://github.com/gvanas/KeccakCodePackage // ShortMsgKATs from https://github.com/gvanas/KeccakCodePackage
// (The testvectors are stored in keccakKats.json.deflate due to their length.) // (The testvectors are stored in keccakKats.json.deflate due to their length.)
func TestKeccakKats(t *testing.T) { func TestKeccakKats(t *testing.T) {
testUnalignedAndGeneric(t, func(impl string) { // Read the KATs.
// Read the KATs. deflated, err := os.Open(katFilename)
deflated, err := os.Open(katFilename) if err != nil {
if err != nil { t.Errorf("error opening %s: %s", katFilename, err)
t.Errorf("error opening %s: %s", katFilename, err) }
} file := flate.NewReader(deflated)
file := flate.NewReader(deflated) dec := json.NewDecoder(file)
dec := json.NewDecoder(file) var katSet KeccakKats
var katSet KeccakKats err = dec.Decode(&katSet)
err = dec.Decode(&katSet) if err != nil {
if err != nil { t.Errorf("error decoding KATs: %s", err)
t.Errorf("error decoding KATs: %s", err) }
}
for algo, function := range testDigests { for algo, function := range testDigests {
d := function() d := function()
for _, kat := range katSet.Kats[algo] { for _, kat := range katSet.Kats[algo] {
d.Reset() d.Reset()
in, err := hex.DecodeString(kat.Message) in, err := hex.DecodeString(kat.Message)
if err != nil { if err != nil {
t.Errorf("error decoding KAT: %s", err) t.Errorf("error decoding KAT: %s", err)
}
d.Write(in[:kat.Length/8])
got := strings.ToUpper(hex.EncodeToString(d.Sum(nil)))
if got != kat.Digest {
t.Errorf("function=%s, implementation=%s, length=%d\nmessage:\n %s\ngot:\n %s\nwanted:\n %s",
algo, impl, kat.Length, kat.Message, got, kat.Digest)
t.Logf("wanted %+v", kat)
t.FailNow()
}
continue
} }
} d.Write(in[:kat.Length/8])
got := strings.ToUpper(hex.EncodeToString(d.Sum(nil)))
for algo, v := range testShakes { if got != kat.Digest {
for _, kat := range katSet.Kats[algo] { t.Errorf("function=%s, length=%d\nmessage:\n %s\ngot:\n %s\nwanted:\n %s",
N, err := hex.DecodeString(kat.N) algo, kat.Length, kat.Message, got, kat.Digest)
if err != nil { t.Logf("wanted %+v", kat)
t.Errorf("error decoding KAT: %s", err) t.FailNow()
}
S, err := hex.DecodeString(kat.S)
if err != nil {
t.Errorf("error decoding KAT: %s", err)
}
d := v.constructor(N, S)
in, err := hex.DecodeString(kat.Message)
if err != nil {
t.Errorf("error decoding KAT: %s", err)
}
d.Write(in[:kat.Length/8])
out := make([]byte, len(kat.Digest)/2)
d.Read(out)
got := strings.ToUpper(hex.EncodeToString(out))
if got != kat.Digest {
t.Errorf("function=%s, implementation=%s, length=%d N:%s\n S:%s\nmessage:\n %s \ngot:\n %s\nwanted:\n %s",
algo, impl, kat.Length, kat.N, kat.S, kat.Message, got, kat.Digest)
t.Logf("wanted %+v", kat)
t.FailNow()
}
continue
} }
continue
} }
}) }
for algo, v := range testShakes {
for _, kat := range katSet.Kats[algo] {
N, err := hex.DecodeString(kat.N)
if err != nil {
t.Errorf("error decoding KAT: %s", err)
}
S, err := hex.DecodeString(kat.S)
if err != nil {
t.Errorf("error decoding KAT: %s", err)
}
d := v.constructor(N, S)
in, err := hex.DecodeString(kat.Message)
if err != nil {
t.Errorf("error decoding KAT: %s", err)
}
d.Write(in[:kat.Length/8])
out := make([]byte, len(kat.Digest)/2)
d.Read(out)
got := strings.ToUpper(hex.EncodeToString(out))
if got != kat.Digest {
t.Errorf("function=%s, length=%d N:%s\n S:%s\nmessage:\n %s \ngot:\n %s\nwanted:\n %s",
algo, kat.Length, kat.N, kat.S, kat.Message, got, kat.Digest)
t.Logf("wanted %+v", kat)
t.FailNow()
}
continue
}
}
} }
// TestKeccak does a basic test of the non-standardized Keccak hash functions. // TestKeccak does a basic test of the non-standardized Keccak hash functions.
@@ -219,119 +206,111 @@ func TestShakeSum(t *testing.T) {
// TestUnalignedWrite tests that writing data in an arbitrary pattern with // TestUnalignedWrite tests that writing data in an arbitrary pattern with
// small input buffers. // small input buffers.
func TestUnalignedWrite(t *testing.T) { func TestUnalignedWrite(t *testing.T) {
testUnalignedAndGeneric(t, func(impl string) { buf := sequentialBytes(0x10000)
buf := sequentialBytes(0x10000) for alg, df := range testDigests {
for alg, df := range testDigests { d := df()
d := df() d.Reset()
d.Reset() d.Write(buf)
d.Write(buf) want := d.Sum(nil)
want := d.Sum(nil) d.Reset()
d.Reset() for i := 0; i < len(buf); {
for i := 0; i < len(buf); { // Cycle through offsets which make a 137 byte sequence.
// Cycle through offsets which make a 137 byte sequence. // Because 137 is prime this sequence should exercise all corner cases.
// Because 137 is prime this sequence should exercise all corner cases. offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1}
offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1} for _, j := range offsets {
for _, j := range offsets { if v := len(buf) - i; v < j {
if v := len(buf) - i; v < j { j = v
j = v
}
d.Write(buf[i : i+j])
i += j
} }
} d.Write(buf[i : i+j])
got := d.Sum(nil) i += j
if !bytes.Equal(got, want) {
t.Errorf("Unaligned writes, implementation=%s, alg=%s\ngot %q, want %q", impl, alg, got, want)
} }
} }
got := d.Sum(nil)
if !bytes.Equal(got, want) {
t.Errorf("Unaligned writes, alg=%s\ngot %q, want %q", alg, got, want)
}
}
// Same for SHAKE // Same for SHAKE
for alg, df := range testShakes { for alg, df := range testShakes {
want := make([]byte, 16) want := make([]byte, 16)
got := make([]byte, 16) got := make([]byte, 16)
d := df.constructor([]byte(df.defAlgoName), []byte(df.defCustomStr)) d := df.constructor([]byte(df.defAlgoName), []byte(df.defCustomStr))
d.Reset() d.Reset()
d.Write(buf) d.Write(buf)
d.Read(want) d.Read(want)
d.Reset() d.Reset()
for i := 0; i < len(buf); { for i := 0; i < len(buf); {
// Cycle through offsets which make a 137 byte sequence. // Cycle through offsets which make a 137 byte sequence.
// Because 137 is prime this sequence should exercise all corner cases. // Because 137 is prime this sequence should exercise all corner cases.
offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1} offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1}
for _, j := range offsets { for _, j := range offsets {
if v := len(buf) - i; v < j { if v := len(buf) - i; v < j {
j = v j = v
}
d.Write(buf[i : i+j])
i += j
} }
} d.Write(buf[i : i+j])
d.Read(got) i += j
if !bytes.Equal(got, want) {
t.Errorf("Unaligned writes, implementation=%s, alg=%s\ngot %q, want %q", impl, alg, got, want)
} }
} }
}) d.Read(got)
if !bytes.Equal(got, want) {
t.Errorf("Unaligned writes, alg=%s\ngot %q, want %q", alg, got, want)
}
}
} }
// TestAppend checks that appending works when reallocation is necessary. // TestAppend checks that appending works when reallocation is necessary.
func TestAppend(t *testing.T) { func TestAppend(t *testing.T) {
testUnalignedAndGeneric(t, func(impl string) { d := New224()
d := New224()
for capacity := 2; capacity <= 66; capacity += 64 { for capacity := 2; capacity <= 66; capacity += 64 {
// The first time around the loop, Sum will have to reallocate. // The first time around the loop, Sum will have to reallocate.
// The second time, it will not. // The second time, it will not.
buf := make([]byte, 2, capacity) buf := make([]byte, 2, capacity)
d.Reset() d.Reset()
d.Write([]byte{0xcc}) d.Write([]byte{0xcc})
buf = d.Sum(buf) buf = d.Sum(buf)
expected := "0000DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39" expected := "0000DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"
if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected { if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected {
t.Errorf("got %s, want %s", got, expected) t.Errorf("got %s, want %s", got, expected)
}
} }
}) }
} }
// TestAppendNoRealloc tests that appending works when no reallocation is necessary. // TestAppendNoRealloc tests that appending works when no reallocation is necessary.
func TestAppendNoRealloc(t *testing.T) { func TestAppendNoRealloc(t *testing.T) {
testUnalignedAndGeneric(t, func(impl string) { buf := make([]byte, 1, 200)
buf := make([]byte, 1, 200) d := New224()
d := New224() d.Write([]byte{0xcc})
d.Write([]byte{0xcc}) buf = d.Sum(buf)
buf = d.Sum(buf) expected := "00DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"
expected := "00DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39" if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected {
if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected { t.Errorf("got %s, want %s", got, expected)
t.Errorf("%s: got %s, want %s", impl, got, expected) }
}
})
} }
// TestSqueezing checks that squeezing the full output a single time produces // TestSqueezing checks that squeezing the full output a single time produces
// the same output as repeatedly squeezing the instance. // the same output as repeatedly squeezing the instance.
func TestSqueezing(t *testing.T) { func TestSqueezing(t *testing.T) {
testUnalignedAndGeneric(t, func(impl string) { for algo, v := range testShakes {
for algo, v := range testShakes { d0 := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr))
d0 := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr)) d0.Write([]byte(testString))
d0.Write([]byte(testString)) ref := make([]byte, 32)
ref := make([]byte, 32) d0.Read(ref)
d0.Read(ref)
d1 := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr)) d1 := v.constructor([]byte(v.defAlgoName), []byte(v.defCustomStr))
d1.Write([]byte(testString)) d1.Write([]byte(testString))
var multiple []byte var multiple []byte
for range ref { for range ref {
one := make([]byte, 1) one := make([]byte, 1)
d1.Read(one) d1.Read(one)
multiple = append(multiple, one...) multiple = append(multiple, one...)
}
if !bytes.Equal(ref, multiple) {
t.Errorf("%s (%s): squeezing %d bytes one at a time failed", algo, impl, len(ref))
}
} }
}) if !bytes.Equal(ref, multiple) {
t.Errorf("%s: squeezing %d bytes one at a time failed", algo, len(ref))
}
}
} }
// sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing. // sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing.
+9 -7
View File
@@ -115,19 +115,21 @@ func (c *state) Clone() ShakeHash {
// Its generic security strength is 128 bits against all attacks if at // Its generic security strength is 128 bits against all attacks if at
// least 32 bytes of its output are used. // least 32 bytes of its output are used.
func NewShake128() ShakeHash { func NewShake128() ShakeHash {
if h := newShake128Asm(); h != nil { return newShake128()
return h
}
return &state{rate: rate128, outputLen: 32, dsbyte: dsbyteShake}
} }
// NewShake256 creates a new SHAKE256 variable-output-length ShakeHash. // NewShake256 creates a new SHAKE256 variable-output-length ShakeHash.
// Its generic security strength is 256 bits against all attacks if // Its generic security strength is 256 bits against all attacks if
// at least 64 bytes of its output are used. // at least 64 bytes of its output are used.
func NewShake256() ShakeHash { func NewShake256() ShakeHash {
if h := newShake256Asm(); h != nil { return newShake256()
return h }
}
func newShake128Generic() *state {
return &state{rate: rate128, outputLen: 32, dsbyte: dsbyteShake}
}
func newShake256Generic() *state {
return &state{rate: rate256, outputLen: 64, dsbyte: dsbyteShake} return &state{rate: rate256, outputLen: 64, dsbyte: dsbyteShake}
} }
-19
View File
@@ -1,19 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !gc || purego || !s390x
package sha3
// newShake128Asm returns an assembly implementation of SHAKE-128 if available,
// otherwise it returns nil.
func newShake128Asm() ShakeHash {
return nil
}
// newShake256Asm returns an assembly implementation of SHAKE-256 if available,
// otherwise it returns nil.
func newShake256Asm() ShakeHash {
return nil
}
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !gc || purego || !s390x
package sha3
func newShake128() *state {
return newShake128Generic()
}
func newShake256() *state {
return newShake256Generic()
}
+31 -14
View File
@@ -2,22 +2,39 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build (!amd64 && !386 && !ppc64le) || purego
package sha3 package sha3
// A storageBuf is an aligned array of maxRate bytes. import (
type storageBuf [maxRate]byte "crypto/subtle"
"encoding/binary"
"unsafe"
func (b *storageBuf) asBytes() *[maxRate]byte { "golang.org/x/sys/cpu"
return (*[maxRate]byte)(b)
}
var (
xorIn = xorInGeneric
copyOut = copyOutGeneric
xorInUnaligned = xorInGeneric
copyOutUnaligned = copyOutGeneric
) )
const xorImplementationUnaligned = "generic" // xorIn xors the bytes in buf into the state.
func xorIn(d *state, buf []byte) {
if cpu.IsBigEndian {
for i := 0; len(buf) >= 8; i++ {
a := binary.LittleEndian.Uint64(buf)
d.a[i] ^= a
buf = buf[8:]
}
} else {
ab := (*[25 * 64 / 8]byte)(unsafe.Pointer(&d.a))
subtle.XORBytes(ab[:], ab[:], buf)
}
}
// copyOut copies uint64s to a byte buffer.
func copyOut(d *state, b []byte) {
if cpu.IsBigEndian {
for i := 0; len(b) >= 8; i++ {
binary.LittleEndian.PutUint64(b, d.a[i])
b = b[8:]
}
} else {
ab := (*[25 * 64 / 8]byte)(unsafe.Pointer(&d.a))
copy(b, ab[:])
}
}
-28
View File
@@ -1,28 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sha3
import "encoding/binary"
// xorInGeneric xors the bytes in buf into the state; it
// makes no non-portable assumptions about memory layout
// or alignment.
func xorInGeneric(d *state, buf []byte) {
n := len(buf) / 8
for i := 0; i < n; i++ {
a := binary.LittleEndian.Uint64(buf)
d.a[i] ^= a
buf = buf[8:]
}
}
// copyOutGeneric copies uint64s to a byte buffer.
func copyOutGeneric(d *state, b []byte) {
for i := 0; len(b) >= 8; i++ {
binary.LittleEndian.PutUint64(b, d.a[i])
b = b[8:]
}
}
-66
View File
@@ -1,66 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build (amd64 || 386 || ppc64le) && !purego
package sha3
import "unsafe"
// A storageBuf is an aligned array of maxRate bytes.
type storageBuf [maxRate / 8]uint64
func (b *storageBuf) asBytes() *[maxRate]byte {
return (*[maxRate]byte)(unsafe.Pointer(b))
}
// xorInUnaligned uses unaligned reads and writes to update d.a to contain d.a
// XOR buf.
func xorInUnaligned(d *state, buf []byte) {
n := len(buf)
bw := (*[maxRate / 8]uint64)(unsafe.Pointer(&buf[0]))[: n/8 : n/8]
if n >= 72 {
d.a[0] ^= bw[0]
d.a[1] ^= bw[1]
d.a[2] ^= bw[2]
d.a[3] ^= bw[3]
d.a[4] ^= bw[4]
d.a[5] ^= bw[5]
d.a[6] ^= bw[6]
d.a[7] ^= bw[7]
d.a[8] ^= bw[8]
}
if n >= 104 {
d.a[9] ^= bw[9]
d.a[10] ^= bw[10]
d.a[11] ^= bw[11]
d.a[12] ^= bw[12]
}
if n >= 136 {
d.a[13] ^= bw[13]
d.a[14] ^= bw[14]
d.a[15] ^= bw[15]
d.a[16] ^= bw[16]
}
if n >= 144 {
d.a[17] ^= bw[17]
}
if n >= 168 {
d.a[18] ^= bw[18]
d.a[19] ^= bw[19]
d.a[20] ^= bw[20]
}
}
func copyOutUnaligned(d *state, buf []byte) {
ab := (*[maxRate]uint8)(unsafe.Pointer(&d.a[0]))
copy(buf, ab[:])
}
var (
xorIn = xorInUnaligned
copyOut = copyOutUnaligned
)
const xorImplementationUnaligned = "unaligned"
+8
View File
@@ -904,6 +904,10 @@ func (k *skECDSAPublicKey) Verify(data []byte, sig *Signature) error {
return errors.New("ssh: signature did not verify") return errors.New("ssh: signature did not verify")
} }
func (k *skECDSAPublicKey) CryptoPublicKey() crypto.PublicKey {
return &k.PublicKey
}
type skEd25519PublicKey struct { type skEd25519PublicKey struct {
// application is a URL-like string, typically "ssh:" for SSH. // application is a URL-like string, typically "ssh:" for SSH.
// see openssh/PROTOCOL.u2f for details. // see openssh/PROTOCOL.u2f for details.
@@ -1000,6 +1004,10 @@ func (k *skEd25519PublicKey) Verify(data []byte, sig *Signature) error {
return nil return nil
} }
func (k *skEd25519PublicKey) CryptoPublicKey() crypto.PublicKey {
return k.PublicKey
}
// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey, // NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey,
// *ecdsa.PrivateKey or any other crypto.Signer and returns a // *ecdsa.PrivateKey or any other crypto.Signer and returns a
// corresponding Signer instance. ECDSA keys must use P-256, P-384 or // corresponding Signer instance. ECDSA keys must use P-256, P-384 or
+46
View File
@@ -726,3 +726,49 @@ func TestNewSignerWithAlgos(t *testing.T) {
t.Error("signer with algos created with restricted algorithms") t.Error("signer with algos created with restricted algorithms")
} }
} }
func TestCryptoPublicKey(t *testing.T) {
for _, priv := range testSigners {
p1 := priv.PublicKey()
key, ok := p1.(CryptoPublicKey)
if !ok {
continue
}
p2, err := NewPublicKey(key.CryptoPublicKey())
if err != nil {
t.Fatalf("NewPublicKey(CryptoPublicKey) failed for %s, got: %v", p1.Type(), err)
}
if !reflect.DeepEqual(p1, p2) {
t.Errorf("got %#v in NewPublicKey, want %#v", p2, p1)
}
}
for _, d := range testdata.SKData {
p1, _, _, _, err := ParseAuthorizedKey(d.PubKey)
if err != nil {
t.Fatalf("parseAuthorizedKey returned error: %v", err)
}
k1, ok := p1.(CryptoPublicKey)
if !ok {
t.Fatalf("%T does not implement CryptoPublicKey", p1)
}
var p2 PublicKey
switch pub := k1.CryptoPublicKey().(type) {
case *ecdsa.PublicKey:
p2 = &skECDSAPublicKey{
application: "ssh:",
PublicKey: *pub,
}
case ed25519.PublicKey:
p2 = &skEd25519PublicKey{
application: "ssh:",
PublicKey: pub,
}
default:
t.Fatalf("unexpected type %T from CryptoPublicKey()", pub)
}
if !reflect.DeepEqual(p1, p2) {
t.Errorf("got %#v, want %#v", p2, p1)
}
}
}
+30
View File
@@ -462,6 +462,24 @@ func (p *PartialSuccessError) Error() string {
// It is returned in ServerAuthError.Errors from NewServerConn. // It is returned in ServerAuthError.Errors from NewServerConn.
var ErrNoAuth = errors.New("ssh: no auth passed yet") var ErrNoAuth = errors.New("ssh: no auth passed yet")
// BannerError is an error that can be returned by authentication handlers in
// ServerConfig to send a banner message to the client.
type BannerError struct {
Err error
Message string
}
func (b *BannerError) Unwrap() error {
return b.Err
}
func (b *BannerError) Error() string {
if b.Err == nil {
return b.Message
}
return b.Err.Error()
}
func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
sessionID := s.transport.getSessionID() sessionID := s.transport.getSessionID()
var cache pubKeyCache var cache pubKeyCache
@@ -734,6 +752,18 @@ userAuthLoop:
config.AuthLogCallback(s, userAuthReq.Method, authErr) config.AuthLogCallback(s, userAuthReq.Method, authErr)
} }
var bannerErr *BannerError
if errors.As(authErr, &bannerErr) {
if bannerErr.Message != "" {
bannerMsg := &userAuthBannerMsg{
Message: bannerErr.Message,
}
if err := s.transport.writePacket(Marshal(bannerMsg)); err != nil {
return nil, err
}
}
}
if authErr == nil { if authErr == nil {
break userAuthLoop break userAuthLoop
} }
+74
View File
@@ -6,8 +6,10 @@ package ssh
import ( import (
"errors" "errors"
"fmt"
"io" "io"
"net" "net"
"slices"
"strings" "strings"
"sync/atomic" "sync/atomic"
"testing" "testing"
@@ -225,6 +227,78 @@ func TestNewServerConnValidationErrors(t *testing.T) {
} }
} }
func TestBannerError(t *testing.T) {
serverConfig := &ServerConfig{
BannerCallback: func(ConnMetadata) string {
return "banner from BannerCallback"
},
NoClientAuth: true,
NoClientAuthCallback: func(ConnMetadata) (*Permissions, error) {
err := &BannerError{
Err: errors.New("error from NoClientAuthCallback"),
Message: "banner from NoClientAuthCallback",
}
return nil, fmt.Errorf("wrapped: %w", err)
},
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
return &Permissions{}, nil
},
PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
return nil, &BannerError{
Err: errors.New("error from PublicKeyCallback"),
Message: "banner from PublicKeyCallback",
}
},
KeyboardInteractiveCallback: func(conn ConnMetadata, client KeyboardInteractiveChallenge) (*Permissions, error) {
return nil, &BannerError{
Err: nil, // make sure that a nil inner error is allowed
Message: "banner from KeyboardInteractiveCallback",
}
},
}
serverConfig.AddHostKey(testSigners["rsa"])
var banners []string
clientConfig := &ClientConfig{
User: "test",
Auth: []AuthMethod{
PublicKeys(testSigners["rsa"]),
KeyboardInteractive(func(name, instruction string, questions []string, echos []bool) ([]string, error) {
return []string{"letmein"}, nil
}),
Password(clientPassword),
},
HostKeyCallback: InsecureIgnoreHostKey(),
BannerCallback: func(msg string) error {
banners = append(banners, msg)
return nil
},
}
c1, c2, err := netPipe()
if err != nil {
t.Fatalf("netPipe: %v", err)
}
defer c1.Close()
defer c2.Close()
go newServer(c1, serverConfig)
c, _, _, err := NewClientConn(c2, "", clientConfig)
if err != nil {
t.Fatalf("client connection failed: %v", err)
}
defer c.Close()
wantBanners := []string{
"banner from BannerCallback",
"banner from NoClientAuthCallback",
"banner from PublicKeyCallback",
"banner from KeyboardInteractiveCallback",
}
if !slices.Equal(banners, wantBanners) {
t.Errorf("got banners:\n%q\nwant banners:\n%q", banners, wantBanners)
}
}
type markerConn struct { type markerConn struct {
closed uint32 closed uint32
used uint32 used uint32
+18
View File
@@ -1526,6 +1526,24 @@ u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+
bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er
fF6adulZkMV8gzURZVE= fF6adulZkMV8gzURZVE=
-----END CERTIFICATE----- -----END CERTIFICATE-----
# CN=FIRMAPROFESIONAL CA ROOT-A WEB,O=Firmaprofesional SA,C=ES,2.5.4.97=#130f56415445532d413632363334303638
# bef256daf26e9c69bdec1602359798f3caf71821a03e018257c53c65617f3d4a
-----BEGIN CERTIFICATE-----
MIICejCCAgCgAwIBAgIQMZch7a+JQn81QYehZ1ZMbTAKBggqhkjOPQQDAzBuMQsw
CQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UE
YQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENB
IFJPT1QtQSBXRUIwHhcNMjIwNDA2MDkwMTM2WhcNNDcwMzMxMDkwMTM2WjBuMQsw
CQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UE
YQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENB
IFJPT1QtQSBXRUIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARHU+osEaR3xyrq89Zf
e9MEkVz6iMYiuYMQYneEMy3pA4jU4DP37XcsSmDq5G+tbbT4TIqk5B/K6k84Si6C
cyvHZpsKjECcfIr28jlgst7L7Ljkb+qbXbdTkBgyVcUgt5SjYzBhMA8GA1UdEwEB
/wQFMAMBAf8wHwYDVR0jBBgwFoAUk+FDY1w8ndYn81LsF7Kpryz3dvgwHQYDVR0O
BBYEFJPhQ2NcPJ3WJ/NS7Beyqa8s93b4MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjO
PQQDAwNoADBlAjAdfKR7w4l1M+E7qUW/Runpod3JIha3RxEL2Jq68cgLcFBTApFw
hVmpHqTm6iMxoAACMQD94vizrxa5HnPEluPBMBnYfubDl94cT7iJLzPrSA8Z94dG
XSaQpYXFuXqUPoeovQA=
-----END CERTIFICATE-----
# CN=GDCA TrustAUTH R5 ROOT,O=GUANG DONG CERTIFICATE AUTHORITY CO.\,LTD.,C=CN # CN=GDCA TrustAUTH R5 ROOT,O=GUANG DONG CERTIFICATE AUTHORITY CO.\,LTD.,C=CN
# bfff8fd04433487d6a8aa60c1a29767a9fc2bbb05e420f713a13b992891d3893 # bfff8fd04433487d6a8aa60c1a29767a9fc2bbb05e420f713a13b992891d3893
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----