Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
627cb894b6 | ||
|
|
dca4914afe | ||
|
|
1336e21bd6 | ||
|
|
2beaa59a3c | ||
|
|
66c3d8ce71 | ||
|
|
ddb4e80c6a | ||
|
|
f4d47b0db5 | ||
|
|
96dc232fbd | ||
|
|
8c9ba31836 | ||
|
|
559e062ce8 |
@@ -561,9 +561,9 @@ func TestHTTPHandlerDefaultFallback(t *testing.T) {
|
||||
{"GET", "http://example.org/foo?a=b", 302, "https://example.org/foo?a=b"},
|
||||
{"GET", "http://example.org:80/foo?a=b", 302, "https://example.org:443/foo?a=b"},
|
||||
{"GET", "http://example.org:80/foo%20bar", 302, "https://example.org:443/foo%20bar"},
|
||||
{"GET", "http://[2602:d1:xxxx::c60a]:1234", 302, "https://[2602:d1:xxxx::c60a]:443/"},
|
||||
{"GET", "http://[2602:d1:xxxx::c60a]", 302, "https://[2602:d1:xxxx::c60a]/"},
|
||||
{"GET", "http://[2602:d1:xxxx::c60a]/foo?a=b", 302, "https://[2602:d1:xxxx::c60a]/foo?a=b"},
|
||||
{"GET", "http://[2602:d1:abcd::c60a]:1234", 302, "https://[2602:d1:abcd::c60a]:443/"},
|
||||
{"GET", "http://[2602:d1:abcd::c60a]", 302, "https://[2602:d1:abcd::c60a]/"},
|
||||
{"GET", "http://[2602:d1:abcd::c60a]/foo?a=b", 302, "https://[2602:d1:abcd::c60a]/foo?a=b"},
|
||||
{"HEAD", "http://example.org", 302, "https://example.org/"},
|
||||
{"HEAD", "http://example.org/foo", 302, "https://example.org/foo"},
|
||||
{"HEAD", "http://example.org/foo/bar/", 302, "https://example.org/foo/bar/"},
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package curve25519 provides an implementation of the X25519 function, which
|
||||
// performs scalar multiplication on the elliptic curve known as Curve25519.
|
||||
// See RFC 7748.
|
||||
// performs scalar multiplication on the elliptic curve known as Curve25519
|
||||
// according to [RFC 7748].
|
||||
//
|
||||
// This package is a wrapper for the X25519 implementation
|
||||
// in the crypto/ecdh package.
|
||||
// The curve25519 package is a wrapper for the X25519 implementation in the
|
||||
// crypto/ecdh package. It is [frozen] and is not accepting new features.
|
||||
//
|
||||
// [RFC 7748]: https://datatracker.ietf.org/doc/html/rfc7748
|
||||
// [frozen]: https://go.dev/wiki/Frozen
|
||||
package curve25519
|
||||
|
||||
import "crypto/ecdh"
|
||||
|
||||
+7
-4
@@ -2,16 +2,19 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ed25519 implements the Ed25519 signature algorithm. See
|
||||
// https://ed25519.cr.yp.to/.
|
||||
// Package ed25519 implements the Ed25519 signature algorithm.
|
||||
//
|
||||
// These functions are also compatible with the “Ed25519” function defined in
|
||||
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
|
||||
// [RFC 8032]. However, unlike RFC 8032's formulation, this package's private key
|
||||
// representation includes a public key suffix to make multiple signing
|
||||
// operations with the same key more efficient. This package refers to the RFC
|
||||
// 8032 private key as the “seed”.
|
||||
//
|
||||
// This package is a wrapper around the standard library crypto/ed25519 package.
|
||||
// The ed25519 package is a wrapper for the Ed25519 implementation in the
|
||||
// crypto/ed25519 package. It is [frozen] and is not accepting new features.
|
||||
//
|
||||
// [RFC 8032]: https://datatracker.ietf.org/doc/html/rfc8032
|
||||
// [frozen]: https://go.dev/wiki/Frozen
|
||||
package ed25519
|
||||
|
||||
import (
|
||||
|
||||
@@ -3,9 +3,9 @@ module golang.org/x/crypto
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.43.0 // tagx:ignore
|
||||
golang.org/x/sys v0.36.0
|
||||
golang.org/x/term v0.35.0
|
||||
golang.org/x/net v0.45.0 // tagx:ignore
|
||||
golang.org/x/sys v0.37.0
|
||||
golang.org/x/term v0.36.0
|
||||
)
|
||||
|
||||
require golang.org/x/text v0.29.0 // indirect
|
||||
require golang.org/x/text v0.30.0 // indirect
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
|
||||
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
||||
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
|
||||
+10
-19
@@ -2,25 +2,16 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package auth authenticates a message using a secret key.
|
||||
|
||||
The Sum function, viewed as a function of the message for a uniform random
|
||||
key, is designed to meet the standard notion of unforgeability. This means
|
||||
that an attacker cannot find authenticators for any messages not authenticated
|
||||
by the sender, even if the attacker has adaptively influenced the messages
|
||||
authenticated by the sender. For a formal definition see, e.g., Section 2.4
|
||||
of Bellare, Kilian, and Rogaway, "The security of the cipher block chaining
|
||||
message authentication code," Journal of Computer and System Sciences 61 (2000),
|
||||
362–399; http://www-cse.ucsd.edu/~mihir/papers/cbc.html.
|
||||
|
||||
auth does not make any promises regarding "strong" unforgeability; perhaps
|
||||
one valid authenticator can be converted into another valid authenticator for
|
||||
the same message. NaCl also does not make any promises regarding "truncated
|
||||
unforgeability."
|
||||
|
||||
This package is interoperable with NaCl: https://nacl.cr.yp.to/auth.html.
|
||||
*/
|
||||
// Package auth authenticates a message using a secret key.
|
||||
//
|
||||
// This package is interoperable with [NaCl].
|
||||
//
|
||||
// The auth package is essentially a wrapper for HMAC-SHA-512 (implemented by
|
||||
// crypto/hmac and crypto/sha512), truncated to 32 bytes. It is [frozen] and is
|
||||
// not accepting new features.
|
||||
//
|
||||
// [NaCl]: https://nacl.cr.yp.to/auth.html
|
||||
// [frozen]: https://go.dev/wiki/Frozen
|
||||
package auth
|
||||
|
||||
import (
|
||||
|
||||
+8
-13
@@ -4,20 +4,15 @@
|
||||
|
||||
// Package sign signs small messages using public-key cryptography.
|
||||
//
|
||||
// Sign uses Ed25519 to sign messages. The length of messages is not hidden.
|
||||
// Messages should be small because:
|
||||
// 1. The whole message needs to be held in memory to be processed.
|
||||
// 2. Using large messages pressures implementations on small machines to process
|
||||
// plaintext without verifying the signature. This is very dangerous, and this API
|
||||
// discourages it, but a protocol that uses excessive message sizes might present
|
||||
// some implementations with no other choice.
|
||||
// 3. Performance may be improved by working with messages that fit into data caches.
|
||||
// Thus large amounts of data should be chunked so that each message is small.
|
||||
// This package is interoperable with [libsodium], as well as [TweetNaCl].
|
||||
//
|
||||
// This package is not interoperable with the current release of NaCl
|
||||
// (https://nacl.cr.yp.to/sign.html), which does not support Ed25519 yet. However,
|
||||
// it is compatible with the NaCl fork libsodium (https://www.libsodium.org), as well
|
||||
// as TweetNaCl (https://tweetnacl.cr.yp.to/).
|
||||
// The sign package is essentially a wrapper for the Ed25519 signature
|
||||
// algorithm (implemented by crypto/ed25519). It is [frozen] and is not accepting
|
||||
// new features.
|
||||
//
|
||||
// [libsodium]: https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures
|
||||
// [TweetNaCl]: https://tweetnacl.cr.yp.to/
|
||||
// [frozen]: https://go.dev/wiki/Frozen
|
||||
package sign
|
||||
|
||||
import (
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
// The version of OTR implemented by this package has been deprecated
|
||||
// (https://bugs.otr.im/lib/libotr/issues/140). An implementation of OTRv3 is
|
||||
// available at https://github.com/coyim/otr3.
|
||||
//
|
||||
// The otr package is [frozen] and is not accepting new features.
|
||||
//
|
||||
// [frozen]: https://go.dev/wiki/Frozen
|
||||
package otr
|
||||
|
||||
import (
|
||||
|
||||
+9
-5
@@ -4,12 +4,16 @@
|
||||
|
||||
// Package pkcs12 implements some of PKCS#12.
|
||||
//
|
||||
// This implementation is distilled from https://tools.ietf.org/html/rfc7292
|
||||
// and referenced documents. It is intended for decoding P12/PFX-stored
|
||||
// certificates and keys for use with the crypto/tls package.
|
||||
// This implementation is distilled from [RFC 7292] and referenced documents.
|
||||
// It is intended for decoding P12/PFX-stored certificates and keys for use
|
||||
// with the crypto/tls package.
|
||||
//
|
||||
// This package is frozen. If it's missing functionality you need, consider
|
||||
// an alternative like software.sslmate.com/src/go-pkcs12.
|
||||
// The pkcs12 package is [frozen] and is not accepting new features.
|
||||
// If it's missing functionality you need, consider an alternative like
|
||||
// software.sslmate.com/src/go-pkcs12.
|
||||
//
|
||||
// [RFC 7292]: https://datatracker.ietf.org/doc/html/rfc7292
|
||||
// [frozen]: https://go.dev/wiki/Frozen
|
||||
package pkcs12
|
||||
|
||||
import (
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package salsa provides low-level access to functions in the Salsa family.
|
||||
//
|
||||
// Deprecated: this package exposes unsafe low-level operations. New applications
|
||||
// should consider using the AEAD construction in golang.org/x/crypto/chacha20poly1305
|
||||
// instead. Existing users should migrate to golang.org/x/crypto/salsa20.
|
||||
package salsa
|
||||
|
||||
import "math/bits"
|
||||
|
||||
+4
-2
@@ -430,8 +430,9 @@ func (c *client) List() ([]*Key, error) {
|
||||
return keys, nil
|
||||
case *failureAgentMsg:
|
||||
return nil, errors.New("agent: failed to list keys")
|
||||
default:
|
||||
return nil, fmt.Errorf("agent: failed to list keys, unexpected message type %T", msg)
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// Sign has the agent sign the data using a protocol 2 key as defined
|
||||
@@ -462,8 +463,9 @@ func (c *client) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFl
|
||||
return &sig, nil
|
||||
case *failureAgentMsg:
|
||||
return nil, errors.New("agent: failed to sign challenge")
|
||||
default:
|
||||
return nil, fmt.Errorf("agent: failed to sign challenge, unexpected message type %T", msg)
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// unmarshal parses an agent message in packet, returning the parsed
|
||||
|
||||
@@ -7,6 +7,7 @@ package agent
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
@@ -347,6 +348,53 @@ func TestServerResponseTooLarge(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidResponses(t *testing.T) {
|
||||
a, b, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
done := make(chan struct{})
|
||||
defer func() { <-done }()
|
||||
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
|
||||
agent := NewClient(a)
|
||||
go func() {
|
||||
defer close(done)
|
||||
|
||||
resp := []byte{agentSuccess}
|
||||
msg := make([]byte, 4+len(resp))
|
||||
binary.BigEndian.PutUint32(msg[:4], uint32(len(resp)))
|
||||
copy(msg[4:], resp)
|
||||
|
||||
if _, err := b.Write(msg); err != nil {
|
||||
t.Errorf("unexpected error sending agent reply: %v", err)
|
||||
b.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := b.Write(msg); err != nil {
|
||||
t.Errorf("unexpected error sending agent reply: %v", err)
|
||||
b.Close()
|
||||
}
|
||||
}()
|
||||
_, err = agent.List()
|
||||
if err == nil {
|
||||
t.Fatal("error expected")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "failed to list keys, unexpected message type") {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
_, err = agent.Sign(testPublicKeys["rsa"], []byte("message"))
|
||||
if err == nil {
|
||||
t.Fatal("error expected")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "failed to sign challenge, unexpected message type") {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuth(t *testing.T) {
|
||||
agent, _, cleanup := startOpenSSHAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
+3
-1
@@ -338,7 +338,9 @@ func TestCertTypes(t *testing.T) {
|
||||
CertType: UserCert,
|
||||
Key: priv.PublicKey(),
|
||||
}
|
||||
cert.SignCert(rand.Reader, priv)
|
||||
if err := cert.SignCert(rand.Reader, priv); err != nil {
|
||||
t.Fatalf("error signing certificate: %v", err)
|
||||
}
|
||||
|
||||
certSigner, err := NewCertSigner(cert, priv)
|
||||
if err != nil {
|
||||
|
||||
+29
-27
@@ -8,6 +8,7 @@ import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/fips140"
|
||||
"crypto/rc4"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/crypto/chacha20"
|
||||
"golang.org/x/crypto/internal/poly1305"
|
||||
@@ -93,41 +95,41 @@ func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream,
|
||||
}
|
||||
|
||||
// cipherModes documents properties of supported ciphers. Ciphers not included
|
||||
// are not supported and will not be negotiated, even if explicitly requested in
|
||||
// ClientConfig.Crypto.Ciphers.
|
||||
var cipherModes = map[string]*cipherMode{
|
||||
// Ciphers from RFC 4344, which introduced many CTR-based ciphers. Algorithms
|
||||
// are defined in the order specified in the RFC.
|
||||
CipherAES128CTR: {16, aes.BlockSize, streamCipherMode(0, newAESCTR)},
|
||||
CipherAES192CTR: {24, aes.BlockSize, streamCipherMode(0, newAESCTR)},
|
||||
CipherAES256CTR: {32, aes.BlockSize, streamCipherMode(0, newAESCTR)},
|
||||
// are not supported and will not be negotiated, even if explicitly configured.
|
||||
// When FIPS mode is enabled, only FIPS-approved algorithms are included.
|
||||
var cipherModes = map[string]*cipherMode{}
|
||||
|
||||
// Ciphers from RFC 4345, which introduces security-improved arcfour ciphers.
|
||||
// They are defined in the order specified in the RFC.
|
||||
InsecureCipherRC4128: {16, 0, streamCipherMode(1536, newRC4)},
|
||||
InsecureCipherRC4256: {32, 0, streamCipherMode(1536, newRC4)},
|
||||
func init() {
|
||||
cipherModes[CipherAES128CTR] = &cipherMode{16, aes.BlockSize, streamCipherMode(0, newAESCTR)}
|
||||
cipherModes[CipherAES192CTR] = &cipherMode{24, aes.BlockSize, streamCipherMode(0, newAESCTR)}
|
||||
cipherModes[CipherAES256CTR] = &cipherMode{32, aes.BlockSize, streamCipherMode(0, newAESCTR)}
|
||||
// Use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode,
|
||||
// we'll wire it up to NewGCMForSSH in Go 1.26.
|
||||
//
|
||||
// For now it means we'll work with fips140=on but not fips140=only.
|
||||
cipherModes[CipherAES128GCM] = &cipherMode{16, 12, newGCMCipher}
|
||||
cipherModes[CipherAES256GCM] = &cipherMode{32, 12, newGCMCipher}
|
||||
|
||||
// Cipher defined in RFC 4253, which describes SSH Transport Layer Protocol.
|
||||
// Note that this cipher is not safe, as stated in RFC 4253: "Arcfour (and
|
||||
// RC4) has problems with weak keys, and should be used with caution."
|
||||
// RFC 4345 introduces improved versions of Arcfour.
|
||||
InsecureCipherRC4: {16, 0, streamCipherMode(0, newRC4)},
|
||||
|
||||
// AEAD ciphers
|
||||
CipherAES128GCM: {16, 12, newGCMCipher},
|
||||
CipherAES256GCM: {32, 12, newGCMCipher},
|
||||
CipherChaCha20Poly1305: {64, 0, newChaCha20Cipher},
|
||||
if fips140.Enabled() {
|
||||
defaultCiphers = slices.DeleteFunc(defaultCiphers, func(algo string) bool {
|
||||
_, ok := cipherModes[algo]
|
||||
return !ok
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
cipherModes[CipherChaCha20Poly1305] = &cipherMode{64, 0, newChaCha20Cipher}
|
||||
// Insecure ciphers not included in the default configuration.
|
||||
cipherModes[InsecureCipherRC4128] = &cipherMode{16, 0, streamCipherMode(1536, newRC4)}
|
||||
cipherModes[InsecureCipherRC4256] = &cipherMode{32, 0, streamCipherMode(1536, newRC4)}
|
||||
cipherModes[InsecureCipherRC4] = &cipherMode{16, 0, streamCipherMode(0, newRC4)}
|
||||
// CBC mode is insecure and so is not included in the default config.
|
||||
// (See https://www.ieee-security.org/TC/SP2013/papers/4977a526.pdf). If absolutely
|
||||
// needed, it's possible to specify a custom Config to enable it.
|
||||
// You should expect that an active attacker can recover plaintext if
|
||||
// you do.
|
||||
InsecureCipherAES128CBC: {16, aes.BlockSize, newAESCBCCipher},
|
||||
|
||||
// 3des-cbc is insecure and is not included in the default
|
||||
// config.
|
||||
InsecureCipherTripleDESCBC: {24, des.BlockSize, newTripleDESCBCCipher},
|
||||
cipherModes[InsecureCipherAES128CBC] = &cipherMode{16, aes.BlockSize, newAESCBCCipher}
|
||||
cipherModes[InsecureCipherTripleDESCBC] = &cipherMode{24, des.BlockSize, newTripleDESCBCCipher}
|
||||
}
|
||||
|
||||
// prefixLen is the length of the packet prefix that contains the packet length
|
||||
|
||||
+8
-16
@@ -9,6 +9,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -83,7 +84,7 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
||||
// success
|
||||
return nil
|
||||
} else if ok == authFailure {
|
||||
if m := auth.method(); !contains(tried, m) {
|
||||
if m := auth.method(); !slices.Contains(tried, m) {
|
||||
tried = append(tried, m)
|
||||
}
|
||||
}
|
||||
@@ -97,7 +98,7 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
||||
findNext:
|
||||
for _, a := range config.Auth {
|
||||
candidateMethod := a.method()
|
||||
if contains(tried, candidateMethod) {
|
||||
if slices.Contains(tried, candidateMethod) {
|
||||
continue
|
||||
}
|
||||
for _, meth := range methods {
|
||||
@@ -117,15 +118,6 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error {
|
||||
return fmt.Errorf("ssh: unable to authenticate, attempted methods %v, no supported methods remain", tried)
|
||||
}
|
||||
|
||||
func contains(list []string, e string) bool {
|
||||
for _, s := range list {
|
||||
if s == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// An AuthMethod represents an instance of an RFC 4252 authentication method.
|
||||
type AuthMethod interface {
|
||||
// auth authenticates user over transport t.
|
||||
@@ -255,7 +247,7 @@ func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (MultiA
|
||||
// Fallback to use if there is no "server-sig-algs" extension or a
|
||||
// common algorithm cannot be found. We use the public key format if the
|
||||
// MultiAlgorithmSigner supports it, otherwise we return an error.
|
||||
if !contains(as.Algorithms(), underlyingAlgo(keyFormat)) {
|
||||
if !slices.Contains(as.Algorithms(), underlyingAlgo(keyFormat)) {
|
||||
return "", fmt.Errorf("ssh: no common public key signature algorithm, server only supports %q for key type %q, signer only supports %v",
|
||||
underlyingAlgo(keyFormat), keyFormat, as.Algorithms())
|
||||
}
|
||||
@@ -284,7 +276,7 @@ func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (MultiA
|
||||
// Filter algorithms based on those supported by MultiAlgorithmSigner.
|
||||
var keyAlgos []string
|
||||
for _, algo := range algorithmsForKeyFormat(keyFormat) {
|
||||
if contains(as.Algorithms(), underlyingAlgo(algo)) {
|
||||
if slices.Contains(as.Algorithms(), underlyingAlgo(algo)) {
|
||||
keyAlgos = append(keyAlgos, algo)
|
||||
}
|
||||
}
|
||||
@@ -334,7 +326,7 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
||||
// the key try to use the obtained algorithm as if "server-sig-algs" had
|
||||
// not been implemented if supported from the algorithm signer.
|
||||
if !ok && idx < origSignersLen && isRSACert(algo) && algo != CertAlgoRSAv01 {
|
||||
if contains(as.Algorithms(), KeyAlgoRSA) {
|
||||
if slices.Contains(as.Algorithms(), KeyAlgoRSA) {
|
||||
// We retry using the compat algorithm after all signers have
|
||||
// been tried normally.
|
||||
signers = append(signers, &multiAlgorithmSigner{
|
||||
@@ -385,7 +377,7 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
||||
// contain the "publickey" method, do not attempt to authenticate with any
|
||||
// other keys. According to RFC 4252 Section 7, the latter can occur when
|
||||
// additional authentication methods are required.
|
||||
if success == authSuccess || !contains(methods, cb.method()) {
|
||||
if success == authSuccess || !slices.Contains(methods, cb.method()) {
|
||||
return success, methods, err
|
||||
}
|
||||
}
|
||||
@@ -434,7 +426,7 @@ func confirmKeyAck(key PublicKey, c packetConn) (bool, error) {
|
||||
// servers send the key type instead. OpenSSH allows any algorithm
|
||||
// that matches the public key, so we do the same.
|
||||
// https://github.com/openssh/openssh-portable/blob/86bdd385/sshconnect2.c#L709
|
||||
if !contains(algorithmsForKeyFormat(key.Type()), msg.Algo) {
|
||||
if !slices.Contains(algorithmsForKeyFormat(key.Type()), msg.Algo) {
|
||||
return false, nil
|
||||
}
|
||||
if !bytes.Equal(msg.PubKey, pubKey) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -1214,7 +1215,7 @@ func (cb configurablePublicKeyCallback) auth(session []byte, user string, c pack
|
||||
if err != nil {
|
||||
return authFailure, nil, err
|
||||
}
|
||||
if success == authSuccess || !contains(methods, cb.method()) {
|
||||
if success == authSuccess || !slices.Contains(methods, cb.method()) {
|
||||
return success, methods, err
|
||||
}
|
||||
|
||||
|
||||
+66
-17
@@ -6,6 +6,7 @@ package ssh
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/fips140"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -256,6 +257,40 @@ type Algorithms struct {
|
||||
PublicKeyAuths []string
|
||||
}
|
||||
|
||||
func init() {
|
||||
if fips140.Enabled() {
|
||||
defaultHostKeyAlgos = slices.DeleteFunc(defaultHostKeyAlgos, func(algo string) bool {
|
||||
_, err := hashFunc(underlyingAlgo(algo))
|
||||
return err != nil
|
||||
})
|
||||
defaultPubKeyAuthAlgos = slices.DeleteFunc(defaultPubKeyAuthAlgos, func(algo string) bool {
|
||||
_, err := hashFunc(underlyingAlgo(algo))
|
||||
return err != nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func hashFunc(format string) (crypto.Hash, error) {
|
||||
switch format {
|
||||
case KeyAlgoRSASHA256, KeyAlgoECDSA256, KeyAlgoSKED25519, KeyAlgoSKECDSA256:
|
||||
return crypto.SHA256, nil
|
||||
case KeyAlgoECDSA384:
|
||||
return crypto.SHA384, nil
|
||||
case KeyAlgoRSASHA512, KeyAlgoECDSA521:
|
||||
return crypto.SHA512, nil
|
||||
case KeyAlgoED25519:
|
||||
// KeyAlgoED25519 doesn't pre-hash.
|
||||
return 0, nil
|
||||
case KeyAlgoRSA, InsecureKeyAlgoDSA:
|
||||
if fips140.Enabled() {
|
||||
return 0, fmt.Errorf("ssh: hash algorithm for format %q not allowed in FIPS 140 mode", format)
|
||||
}
|
||||
return crypto.SHA1, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("ssh: hash algorithm for format %q not mapped", format)
|
||||
}
|
||||
}
|
||||
|
||||
// SupportedAlgorithms returns algorithms currently implemented by this package,
|
||||
// excluding those with security issues, which are returned by
|
||||
// InsecureAlgorithms. The algorithms listed here are in preference order.
|
||||
@@ -283,21 +318,6 @@ func InsecureAlgorithms() Algorithms {
|
||||
|
||||
var supportedCompressions = []string{compressionNone}
|
||||
|
||||
// hashFuncs keeps the mapping of supported signature algorithms to their
|
||||
// respective hashes needed for signing and verification.
|
||||
var hashFuncs = map[string]crypto.Hash{
|
||||
KeyAlgoRSA: crypto.SHA1,
|
||||
KeyAlgoRSASHA256: crypto.SHA256,
|
||||
KeyAlgoRSASHA512: crypto.SHA512,
|
||||
InsecureKeyAlgoDSA: crypto.SHA1,
|
||||
KeyAlgoECDSA256: crypto.SHA256,
|
||||
KeyAlgoECDSA384: crypto.SHA384,
|
||||
KeyAlgoECDSA521: crypto.SHA512,
|
||||
// KeyAlgoED25519 doesn't pre-hash.
|
||||
KeyAlgoSKECDSA256: crypto.SHA256,
|
||||
KeyAlgoSKED25519: crypto.SHA256,
|
||||
}
|
||||
|
||||
// algorithmsForKeyFormat returns the supported signature algorithms for a given
|
||||
// public key format (PublicKey.Type), in order of preference. See RFC 8332,
|
||||
// Section 2. See also the note in sendKexInit on backwards compatibility.
|
||||
@@ -312,11 +332,40 @@ func algorithmsForKeyFormat(keyFormat string) []string {
|
||||
}
|
||||
}
|
||||
|
||||
// keyFormatForAlgorithm returns the key format corresponding to the given
|
||||
// signature algorithm. It returns an empty string if the signature algorithm is
|
||||
// invalid or unsupported.
|
||||
func keyFormatForAlgorithm(sigAlgo string) string {
|
||||
switch sigAlgo {
|
||||
case KeyAlgoRSA, KeyAlgoRSASHA256, KeyAlgoRSASHA512:
|
||||
return KeyAlgoRSA
|
||||
case CertAlgoRSAv01, CertAlgoRSASHA256v01, CertAlgoRSASHA512v01:
|
||||
return CertAlgoRSAv01
|
||||
case KeyAlgoED25519,
|
||||
KeyAlgoSKED25519,
|
||||
KeyAlgoSKECDSA256,
|
||||
KeyAlgoECDSA256,
|
||||
KeyAlgoECDSA384,
|
||||
KeyAlgoECDSA521,
|
||||
InsecureKeyAlgoDSA,
|
||||
InsecureCertAlgoDSAv01,
|
||||
CertAlgoECDSA256v01,
|
||||
CertAlgoECDSA384v01,
|
||||
CertAlgoECDSA521v01,
|
||||
CertAlgoSKECDSA256v01,
|
||||
CertAlgoED25519v01,
|
||||
CertAlgoSKED25519v01:
|
||||
return sigAlgo
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// isRSA returns whether algo is a supported RSA algorithm, including certificate
|
||||
// algorithms.
|
||||
func isRSA(algo string) bool {
|
||||
algos := algorithmsForKeyFormat(KeyAlgoRSA)
|
||||
return contains(algos, underlyingAlgo(algo))
|
||||
return slices.Contains(algos, underlyingAlgo(algo))
|
||||
}
|
||||
|
||||
func isRSACert(algo string) bool {
|
||||
@@ -515,7 +564,7 @@ func (c *Config) SetDefaults() {
|
||||
if kexAlgoMap[k] != nil {
|
||||
// Ignore the KEX if we have no kexAlgoMap definition.
|
||||
kexs = append(kexs, k)
|
||||
if k == KeyExchangeCurve25519 && !contains(c.KeyExchanges, keyExchangeCurve25519LibSSH) {
|
||||
if k == KeyExchangeCurve25519 && !slices.Contains(c.KeyExchanges, keyExchangeCurve25519LibSSH) {
|
||||
kexs = append(kexs, keyExchangeCurve25519LibSSH)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"reflect"
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -174,3 +176,21 @@ func TestFindAgreedAlgorithms(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyFormatAlgorithms(t *testing.T) {
|
||||
supportedAlgos := SupportedAlgorithms()
|
||||
insecureAlgos := InsecureAlgorithms()
|
||||
algoritms := append(supportedAlgos.PublicKeyAuths, insecureAlgos.PublicKeyAuths...)
|
||||
algoritms = append(algoritms, slices.Collect(maps.Keys(certKeyAlgoNames))...)
|
||||
|
||||
for _, algo := range algoritms {
|
||||
keyFormat := keyFormatForAlgorithm(algo)
|
||||
if keyFormat == "" {
|
||||
t.Errorf("got empty key format for algorithm %q", algo)
|
||||
}
|
||||
if !slices.Contains(algorithmsForKeyFormat(keyFormat), algo) {
|
||||
t.Errorf("algorithms for key format %q, does not contain %q", keyFormat, algo)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -17,8 +17,18 @@ References:
|
||||
[PROTOCOL.certkeys]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=HEAD
|
||||
[SSH-PARAMETERS]: http://www.iana.org/assignments/ssh-parameters/ssh-parameters.xml#ssh-parameters-1
|
||||
[SSH-CERTS]: https://datatracker.ietf.org/doc/html/draft-miller-ssh-cert-01
|
||||
[FIPS 140-3 mode]: https://go.dev/doc/security/fips140
|
||||
|
||||
This package does not fall under the stability promise of the Go language itself,
|
||||
so its API may be changed when pressing needs arise.
|
||||
|
||||
# FIPS 140-3 mode
|
||||
|
||||
When the program is in [FIPS 140-3 mode], this package behaves as if only SP
|
||||
800-140C and SP 800-140D approved cipher suites, signature algorithms,
|
||||
certificate public key types and sizes, and key exchange and derivation
|
||||
algorithms were implemented. Others are silently ignored and not negotiated, or
|
||||
rejected. This set may depend on the algorithms supported by the FIPS 140-3 Go
|
||||
Cryptographic Module selected with GOFIPS140, and may change across Go versions.
|
||||
*/
|
||||
package ssh
|
||||
|
||||
+5
-4
@@ -10,6 +10,7 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
@@ -527,7 +528,7 @@ func (t *handshakeTransport) sendKexInit() error {
|
||||
switch s := k.(type) {
|
||||
case MultiAlgorithmSigner:
|
||||
for _, algo := range algorithmsForKeyFormat(keyFormat) {
|
||||
if contains(s.Algorithms(), underlyingAlgo(algo)) {
|
||||
if slices.Contains(s.Algorithms(), underlyingAlgo(algo)) {
|
||||
msg.ServerHostKeyAlgos = append(msg.ServerHostKeyAlgos, algo)
|
||||
}
|
||||
}
|
||||
@@ -679,7 +680,7 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if t.sessionID == nil && ((isClient && contains(serverInit.KexAlgos, kexStrictServer)) || (!isClient && contains(clientInit.KexAlgos, kexStrictClient))) {
|
||||
if t.sessionID == nil && ((isClient && slices.Contains(serverInit.KexAlgos, kexStrictServer)) || (!isClient && slices.Contains(clientInit.KexAlgos, kexStrictClient))) {
|
||||
t.strictMode = true
|
||||
if err := t.conn.setStrictMode(); err != nil {
|
||||
return err
|
||||
@@ -736,7 +737,7 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
||||
// 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, and [PROTOCOL], Section 1.9.
|
||||
if !isClient && firstKeyExchange && contains(clientInit.KexAlgos, "ext-info-c") {
|
||||
if !isClient && firstKeyExchange && slices.Contains(clientInit.KexAlgos, "ext-info-c") {
|
||||
supportedPubKeyAuthAlgosList := strings.Join(t.publicKeyAuthAlgorithms, ",")
|
||||
extInfo := &extInfoMsg{
|
||||
NumExtensions: 2,
|
||||
@@ -790,7 +791,7 @@ func (a algorithmSignerWrapper) SignWithAlgorithm(rand io.Reader, data []byte, a
|
||||
func pickHostKey(hostKeys []Signer, algo string) AlgorithmSigner {
|
||||
for _, k := range hostKeys {
|
||||
if s, ok := k.(MultiAlgorithmSigner); ok {
|
||||
if !contains(s.Algorithms(), underlyingAlgo(algo)) {
|
||||
if !slices.Contains(s.Algorithms(), underlyingAlgo(algo)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
+20
-4
@@ -8,12 +8,14 @@ import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/fips140"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"slices"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
@@ -395,9 +397,27 @@ func ecHash(curve elliptic.Curve) crypto.Hash {
|
||||
return crypto.SHA512
|
||||
}
|
||||
|
||||
// kexAlgoMap defines the supported KEXs. KEXs not included are not supported
|
||||
// and will not be negotiated, even if explicitly configured. When FIPS mode is
|
||||
// enabled, only FIPS-approved algorithms are included.
|
||||
var kexAlgoMap = map[string]kexAlgorithm{}
|
||||
|
||||
func init() {
|
||||
// mlkem768x25519-sha256 we'll work with fips140=on but not fips140=only
|
||||
// until Go 1.26.
|
||||
kexAlgoMap[KeyExchangeMLKEM768X25519] = &mlkem768WithCurve25519sha256{}
|
||||
kexAlgoMap[KeyExchangeECDHP521] = &ecdh{elliptic.P521()}
|
||||
kexAlgoMap[KeyExchangeECDHP384] = &ecdh{elliptic.P384()}
|
||||
kexAlgoMap[KeyExchangeECDHP256] = &ecdh{elliptic.P256()}
|
||||
|
||||
if fips140.Enabled() {
|
||||
defaultKexAlgos = slices.DeleteFunc(defaultKexAlgos, func(algo string) bool {
|
||||
_, ok := kexAlgoMap[algo]
|
||||
return !ok
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
p, _ := new(big.Int).SetString(oakleyGroup2, 16)
|
||||
kexAlgoMap[InsecureKeyExchangeDH1SHA1] = &dhGroup{
|
||||
g: new(big.Int).SetInt64(2),
|
||||
@@ -431,14 +451,10 @@ func init() {
|
||||
hashFunc: crypto.SHA512,
|
||||
}
|
||||
|
||||
kexAlgoMap[KeyExchangeECDHP521] = &ecdh{elliptic.P521()}
|
||||
kexAlgoMap[KeyExchangeECDHP384] = &ecdh{elliptic.P384()}
|
||||
kexAlgoMap[KeyExchangeECDHP256] = &ecdh{elliptic.P256()}
|
||||
kexAlgoMap[KeyExchangeCurve25519] = &curve25519sha256{}
|
||||
kexAlgoMap[keyExchangeCurve25519LibSSH] = &curve25519sha256{}
|
||||
kexAlgoMap[InsecureKeyExchangeDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1}
|
||||
kexAlgoMap[KeyExchangeDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256}
|
||||
kexAlgoMap[KeyExchangeMLKEM768X25519] = &mlkem768WithCurve25519sha256{}
|
||||
}
|
||||
|
||||
// curve25519sha256 implements the curve25519-sha256 (formerly known as
|
||||
|
||||
+54
-15
@@ -27,6 +27,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/ssh/internal/bcrypt_pbkdf"
|
||||
@@ -89,6 +90,11 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
|
||||
}
|
||||
return cert, nil, nil
|
||||
}
|
||||
if keyFormat := keyFormatForAlgorithm(algo); keyFormat != "" {
|
||||
return nil, nil, fmt.Errorf("ssh: signature algorithm %q isn't a key format; key is malformed and should be re-encoded with type %q",
|
||||
algo, keyFormat)
|
||||
}
|
||||
|
||||
return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", algo)
|
||||
}
|
||||
|
||||
@@ -191,9 +197,10 @@ func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey
|
||||
return "", nil, nil, "", nil, io.EOF
|
||||
}
|
||||
|
||||
// ParseAuthorizedKey parses a public key from an authorized_keys
|
||||
// file used in OpenSSH according to the sshd(8) manual page.
|
||||
// ParseAuthorizedKey parses a public key from an authorized_keys file used in
|
||||
// OpenSSH according to the sshd(8) manual page. Invalid lines are ignored.
|
||||
func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) {
|
||||
var lastErr error
|
||||
for len(in) > 0 {
|
||||
end := bytes.IndexByte(in, '\n')
|
||||
if end != -1 {
|
||||
@@ -222,6 +229,8 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str
|
||||
|
||||
if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
|
||||
return out, comment, options, rest, nil
|
||||
} else {
|
||||
lastErr = err
|
||||
}
|
||||
|
||||
// No key type recognised. Maybe there's an options field at
|
||||
@@ -264,12 +273,18 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str
|
||||
if out, comment, err = parseAuthorizedKey(in[i:]); err == nil {
|
||||
options = candidateOptions
|
||||
return out, comment, options, rest, nil
|
||||
} else {
|
||||
lastErr = err
|
||||
}
|
||||
|
||||
in = rest
|
||||
continue
|
||||
}
|
||||
|
||||
if lastErr != nil {
|
||||
return nil, "", nil, nil, fmt.Errorf("ssh: no key found; last parsing error for ignored line: %w", lastErr)
|
||||
}
|
||||
|
||||
return nil, "", nil, nil, errors.New("ssh: no key found")
|
||||
}
|
||||
|
||||
@@ -395,11 +410,11 @@ func NewSignerWithAlgorithms(signer AlgorithmSigner, algorithms []string) (Multi
|
||||
}
|
||||
|
||||
for _, algo := range algorithms {
|
||||
if !contains(supportedAlgos, algo) {
|
||||
if !slices.Contains(supportedAlgos, algo) {
|
||||
return nil, fmt.Errorf("ssh: algorithm %q is not supported for key type %q",
|
||||
algo, signer.PublicKey().Type())
|
||||
}
|
||||
if !contains(signerAlgos, algo) {
|
||||
if !slices.Contains(signerAlgos, algo) {
|
||||
return nil, fmt.Errorf("ssh: algorithm %q is restricted for the provided signer", algo)
|
||||
}
|
||||
}
|
||||
@@ -486,10 +501,13 @@ func (r *rsaPublicKey) Marshal() []byte {
|
||||
|
||||
func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error {
|
||||
supportedAlgos := algorithmsForKeyFormat(r.Type())
|
||||
if !contains(supportedAlgos, sig.Format) {
|
||||
if !slices.Contains(supportedAlgos, sig.Format) {
|
||||
return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, r.Type())
|
||||
}
|
||||
hash := hashFuncs[sig.Format]
|
||||
hash, err := hashFunc(sig.Format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := hash.New()
|
||||
h.Write(data)
|
||||
digest := h.Sum(nil)
|
||||
@@ -606,7 +624,11 @@ func (k *dsaPublicKey) Verify(data []byte, sig *Signature) error {
|
||||
if sig.Format != k.Type() {
|
||||
return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type())
|
||||
}
|
||||
h := hashFuncs[sig.Format].New()
|
||||
hash, err := hashFunc(sig.Format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := hash.New()
|
||||
h.Write(data)
|
||||
digest := h.Sum(nil)
|
||||
|
||||
@@ -651,7 +673,11 @@ func (k *dsaPrivateKey) SignWithAlgorithm(rand io.Reader, data []byte, algorithm
|
||||
return nil, fmt.Errorf("ssh: unsupported signature algorithm %s", algorithm)
|
||||
}
|
||||
|
||||
h := hashFuncs[k.PublicKey().Type()].New()
|
||||
hash, err := hashFunc(k.PublicKey().Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h := hash.New()
|
||||
h.Write(data)
|
||||
digest := h.Sum(nil)
|
||||
r, s, err := dsa.Sign(rand, k.PrivateKey, digest)
|
||||
@@ -801,8 +827,11 @@ func (k *ecdsaPublicKey) Verify(data []byte, sig *Signature) error {
|
||||
if sig.Format != k.Type() {
|
||||
return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type())
|
||||
}
|
||||
|
||||
h := hashFuncs[sig.Format].New()
|
||||
hash, err := hashFunc(sig.Format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := hash.New()
|
||||
h.Write(data)
|
||||
digest := h.Sum(nil)
|
||||
|
||||
@@ -905,8 +934,11 @@ func (k *skECDSAPublicKey) Verify(data []byte, sig *Signature) error {
|
||||
if sig.Format != k.Type() {
|
||||
return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, k.Type())
|
||||
}
|
||||
|
||||
h := hashFuncs[sig.Format].New()
|
||||
hash, err := hashFunc(sig.Format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := hash.New()
|
||||
h.Write([]byte(k.application))
|
||||
appDigest := h.Sum(nil)
|
||||
|
||||
@@ -1009,7 +1041,11 @@ func (k *skEd25519PublicKey) Verify(data []byte, sig *Signature) error {
|
||||
return fmt.Errorf("invalid size %d for Ed25519 public key", l)
|
||||
}
|
||||
|
||||
h := hashFuncs[sig.Format].New()
|
||||
hash, err := hashFunc(sig.Format)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := hash.New()
|
||||
h.Write([]byte(k.application))
|
||||
appDigest := h.Sum(nil)
|
||||
|
||||
@@ -1112,11 +1148,14 @@ func (s *wrappedSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm
|
||||
algorithm = s.pubKey.Type()
|
||||
}
|
||||
|
||||
if !contains(s.Algorithms(), algorithm) {
|
||||
if !slices.Contains(s.Algorithms(), algorithm) {
|
||||
return nil, fmt.Errorf("ssh: unsupported signature algorithm %q for key format %q", algorithm, s.pubKey.Type())
|
||||
}
|
||||
|
||||
hashFunc := hashFuncs[algorithm]
|
||||
hashFunc, err := hashFunc(algorithm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var digest []byte
|
||||
if hashFunc != 0 {
|
||||
h := hashFunc.New()
|
||||
|
||||
@@ -59,6 +59,17 @@ func TestKeyMarshalParse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePublicKeyWithSigningAlgoAsKeyFormat(t *testing.T) {
|
||||
key := []byte(`rsa-sha2-256 AAAADHJzYS1zaGEyLTI1NgAAAAMBAAEAAAEBAJ7qMyjLXEJCCJmRknuCLo0uPi5GrPY5pQYr84lhlN8Gor5KVL2LKYCW4e70r5xzj7SrHHSCft1FMlYg1KDO9xrprJh733kQqAPWETmSuH0EfRtGtcH6EarKyVxk6As076/yNiiMKVBtG0RPa1L7FviTfcYK4vnCCVrbv3RmA5CCzuG5BSMbRLxzVb4Ri3p8jhxYT8N4QGe/2yqvJLys5vQ9szpZR3tcFp3DJIVZhBRfR6LnoY23XZniAAMQaUVBX86dXQ++dNwAwZSXSt9Og+AniOCiBYqhNVa5n3DID/H7YtEtG+CbZr3r2KD3fv8AfSLRar4XOp8rsRdD31h/kr8=`)
|
||||
_, _, _, _, err := ParseAuthorizedKey(key)
|
||||
if err == nil {
|
||||
t.Fatal("parsing a public key using a signature algorithm as the key format succeeded unexpectedly")
|
||||
}
|
||||
if !strings.Contains(err.Error(), `signature algorithm "rsa-sha2-256" isn't a key format`) {
|
||||
t.Errorf(`got %v, expected 'signature algorithm "rsa-sha2-256" isn't a key format'`, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnsupportedCurves(t *testing.T) {
|
||||
raw, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
||||
if err != nil {
|
||||
|
||||
+29
-13
@@ -7,11 +7,13 @@ package ssh
|
||||
// Message authentication support
|
||||
|
||||
import (
|
||||
"crypto/fips140"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"hash"
|
||||
"slices"
|
||||
)
|
||||
|
||||
type macMode struct {
|
||||
@@ -46,23 +48,37 @@ func (t truncatingMAC) Size() int {
|
||||
|
||||
func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() }
|
||||
|
||||
var macModes = map[string]*macMode{
|
||||
HMACSHA512ETM: {64, true, func(key []byte) hash.Hash {
|
||||
// macModes defines the supported MACs. MACs not included are not supported
|
||||
// and will not be negotiated, even if explicitly configured. When FIPS mode is
|
||||
// enabled, only FIPS-approved algorithms are included.
|
||||
var macModes = map[string]*macMode{}
|
||||
|
||||
func init() {
|
||||
macModes[HMACSHA512ETM] = &macMode{64, true, func(key []byte) hash.Hash {
|
||||
return hmac.New(sha512.New, key)
|
||||
}},
|
||||
HMACSHA256ETM: {32, true, func(key []byte) hash.Hash {
|
||||
}}
|
||||
macModes[HMACSHA256ETM] = &macMode{32, true, func(key []byte) hash.Hash {
|
||||
return hmac.New(sha256.New, key)
|
||||
}},
|
||||
HMACSHA512: {64, false, func(key []byte) hash.Hash {
|
||||
}}
|
||||
macModes[HMACSHA512] = &macMode{64, false, func(key []byte) hash.Hash {
|
||||
return hmac.New(sha512.New, key)
|
||||
}},
|
||||
HMACSHA256: {32, false, func(key []byte) hash.Hash {
|
||||
}}
|
||||
macModes[HMACSHA256] = &macMode{32, false, func(key []byte) hash.Hash {
|
||||
return hmac.New(sha256.New, key)
|
||||
}},
|
||||
HMACSHA1: {20, false, func(key []byte) hash.Hash {
|
||||
}}
|
||||
|
||||
if fips140.Enabled() {
|
||||
defaultMACs = slices.DeleteFunc(defaultMACs, func(algo string) bool {
|
||||
_, ok := macModes[algo]
|
||||
return !ok
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
macModes[HMACSHA1] = &macMode{20, false, func(key []byte) hash.Hash {
|
||||
return hmac.New(sha1.New, key)
|
||||
}},
|
||||
InsecureHMACSHA196: {20, false, func(key []byte) hash.Hash {
|
||||
}}
|
||||
macModes[InsecureHMACSHA196] = &macMode{20, false, func(key []byte) hash.Hash {
|
||||
return truncatingMAC{12, hmac.New(sha1.New, key)}
|
||||
}},
|
||||
}}
|
||||
}
|
||||
|
||||
+32
-4
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -43,6 +44,9 @@ type Permissions struct {
|
||||
// pass data from the authentication callbacks to the server
|
||||
// application layer.
|
||||
Extensions map[string]string
|
||||
|
||||
// ExtraData allows to store user defined data.
|
||||
ExtraData map[any]any
|
||||
}
|
||||
|
||||
type GSSAPIWithMICConfig struct {
|
||||
@@ -126,6 +130,21 @@ type ServerConfig struct {
|
||||
// Permissions.Extensions entry.
|
||||
PublicKeyCallback func(conn ConnMetadata, key PublicKey) (*Permissions, error)
|
||||
|
||||
// VerifiedPublicKeyCallback, if non-nil, is called after a client
|
||||
// successfully confirms having control over a key that was previously
|
||||
// approved by PublicKeyCallback. The permissions object passed to the
|
||||
// callback is the one returned by PublicKeyCallback for the given public
|
||||
// key and its ownership is transferred to the callback. The returned
|
||||
// Permissions object can be the same object, optionally modified, or a
|
||||
// completely new object. If VerifiedPublicKeyCallback is non-nil,
|
||||
// PublicKeyCallback is not allowed to return a PartialSuccessError, which
|
||||
// can instead be returned by VerifiedPublicKeyCallback.
|
||||
//
|
||||
// VerifiedPublicKeyCallback does not affect which authentication methods
|
||||
// are included in the list of methods that can be attempted by the client.
|
||||
VerifiedPublicKeyCallback func(conn ConnMetadata, key PublicKey, permissions *Permissions,
|
||||
signatureAlgorithm string) (*Permissions, error)
|
||||
|
||||
// KeyboardInteractiveCallback, if non-nil, is called when
|
||||
// keyboard-interactive authentication is selected (RFC
|
||||
// 4256). The client object's Challenge function should be
|
||||
@@ -246,7 +265,7 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha
|
||||
fullConf.PublicKeyAuthAlgorithms = defaultPubKeyAuthAlgos
|
||||
} else {
|
||||
for _, algo := range fullConf.PublicKeyAuthAlgorithms {
|
||||
if !contains(SupportedAlgorithms().PublicKeyAuths, algo) && !contains(InsecureAlgorithms().PublicKeyAuths, algo) {
|
||||
if !slices.Contains(SupportedAlgorithms().PublicKeyAuths, algo) && !slices.Contains(InsecureAlgorithms().PublicKeyAuths, algo) {
|
||||
c.Close()
|
||||
return nil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", algo)
|
||||
}
|
||||
@@ -631,7 +650,7 @@ userAuthLoop:
|
||||
return nil, parseError(msgUserAuthRequest)
|
||||
}
|
||||
algo := string(algoBytes)
|
||||
if !contains(config.PublicKeyAuthAlgorithms, underlyingAlgo(algo)) {
|
||||
if !slices.Contains(config.PublicKeyAuthAlgorithms, underlyingAlgo(algo)) {
|
||||
authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
|
||||
break
|
||||
}
|
||||
@@ -652,6 +671,9 @@ userAuthLoop:
|
||||
candidate.pubKeyData = pubKeyData
|
||||
candidate.perms, candidate.result = authConfig.PublicKeyCallback(s, pubKey)
|
||||
_, isPartialSuccessError := candidate.result.(*PartialSuccessError)
|
||||
if isPartialSuccessError && config.VerifiedPublicKeyCallback != nil {
|
||||
return nil, errors.New("ssh: invalid library usage: PublicKeyCallback must not return partial success when VerifiedPublicKeyCallback is defined")
|
||||
}
|
||||
|
||||
if (candidate.result == nil || isPartialSuccessError) &&
|
||||
candidate.perms != nil &&
|
||||
@@ -695,7 +717,7 @@ userAuthLoop:
|
||||
// ssh-rsa-cert-v01@openssh.com algorithm with ssh-rsa public
|
||||
// key type. The algorithm and public key type must be
|
||||
// consistent: both must be certificate algorithms, or neither.
|
||||
if !contains(algorithmsForKeyFormat(pubKey.Type()), algo) {
|
||||
if !slices.Contains(algorithmsForKeyFormat(pubKey.Type()), algo) {
|
||||
authErr = fmt.Errorf("ssh: public key type %q not compatible with selected algorithm %q",
|
||||
pubKey.Type(), algo)
|
||||
break
|
||||
@@ -705,7 +727,7 @@ userAuthLoop:
|
||||
// algorithm name that corresponds to algo with
|
||||
// sig.Format. This is usually the same, but
|
||||
// for certs, the names differ.
|
||||
if !contains(config.PublicKeyAuthAlgorithms, sig.Format) {
|
||||
if !slices.Contains(config.PublicKeyAuthAlgorithms, sig.Format) {
|
||||
authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
|
||||
break
|
||||
}
|
||||
@@ -722,6 +744,12 @@ userAuthLoop:
|
||||
|
||||
authErr = candidate.result
|
||||
perms = candidate.perms
|
||||
if authErr == nil && config.VerifiedPublicKeyCallback != nil {
|
||||
// Only call VerifiedPublicKeyCallback after the key has been accepted
|
||||
// and successfully verified. If authErr is non-nil, the key is not
|
||||
// considered verified and the callback must not run.
|
||||
perms, authErr = config.VerifiedPublicKeyCallback(s, pubKey, perms, algo)
|
||||
}
|
||||
}
|
||||
case "gssapi-with-mic":
|
||||
if authConfig.GSSAPIWithMICConfig == nil {
|
||||
|
||||
@@ -434,6 +434,380 @@ func TestPreAuthConnAndBanners(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifiedPublicKeyCallback(t *testing.T) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
extraKey := "extra"
|
||||
extraDataString := "just a string"
|
||||
|
||||
serverConf := &ServerConfig{
|
||||
VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
|
||||
if permissions != nil && permissions.ExtraData != nil {
|
||||
if !reflect.DeepEqual(map[any]any{extraKey: extraDataString}, permissions.ExtraData) {
|
||||
t.Errorf("expected extra data: %v; got: %v", extraDataString, permissions.ExtraData)
|
||||
}
|
||||
} else {
|
||||
t.Error("expected extra data is missing")
|
||||
}
|
||||
if signatureAlgorithm != KeyAlgoRSASHA256 {
|
||||
t.Errorf("expected signature algorithm: %q; got: %q", KeyAlgoRSASHA256, signatureAlgorithm)
|
||||
}
|
||||
return permissions, nil
|
||||
},
|
||||
PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
||||
return &Permissions{ExtraData: map[any]any{extraKey: extraDataString}}, nil
|
||||
},
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
conn, _, _, err := NewServerConn(c1, serverConf)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected server error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(map[any]any{extraKey: extraDataString}, conn.Permissions.ExtraData) {
|
||||
t.Errorf("expected extra data: %v; got: %v", extraDataString, conn.Permissions.ExtraData)
|
||||
}
|
||||
}()
|
||||
|
||||
clientConf := ClientConfig{
|
||||
User: "user",
|
||||
Auth: []AuthMethod{
|
||||
PublicKeys(testSigners["rsa"]),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
_, _, _, err = NewClientConn(c2, "", &clientConf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestVerifiedPublicCallbackPartialSuccess(t *testing.T) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
serverConf := &ServerConfig{
|
||||
PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
||||
if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.New("invalid credentials")
|
||||
},
|
||||
VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
|
||||
if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
|
||||
return nil, &PartialSuccessError{
|
||||
Next: ServerAuthCallbacks{
|
||||
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||
if string(password) == clientPassword {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil, errors.New("invalid credentials")
|
||||
},
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
|
||||
clientConf := ClientConfig{
|
||||
User: "user",
|
||||
Auth: []AuthMethod{
|
||||
PublicKeys(testSigners["rsa"]),
|
||||
Password(clientPassword),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
go NewServerConn(c1, serverConf)
|
||||
|
||||
_, _, _, err = NewClientConn(c2, "", &clientConf)
|
||||
if err != nil {
|
||||
t.Fatalf("client login error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifiedPublicKeyCallbackPwdAndKey(t *testing.T) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
serverConf := &ServerConfig{
|
||||
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||
if string(password) == clientPassword {
|
||||
return nil, &PartialSuccessError{
|
||||
Next: ServerAuthCallbacks{
|
||||
PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
||||
if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.New("invalid credentials")
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil, errors.New("invalid credentials")
|
||||
|
||||
},
|
||||
VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
|
||||
if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.New("invalid credentials")
|
||||
},
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
|
||||
clientConf := ClientConfig{
|
||||
User: "user",
|
||||
Auth: []AuthMethod{
|
||||
Password(clientPassword),
|
||||
PublicKeys(testSigners["rsa"]),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
go NewServerConn(c1, serverConf)
|
||||
|
||||
_, _, _, err = NewClientConn(c2, "", &clientConf)
|
||||
if err != nil {
|
||||
t.Fatalf("client login error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifiedPubKeyCallbackAuthMethods(t *testing.T) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
serverConf := &ServerConfig{
|
||||
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||
return nil, nil
|
||||
},
|
||||
VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
|
||||
if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.New("invalid credentials")
|
||||
},
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
|
||||
clientConf := ClientConfig{
|
||||
User: "user",
|
||||
Auth: []AuthMethod{
|
||||
PublicKeys(testSigners["rsa"]),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
go NewServerConn(c1, serverConf)
|
||||
|
||||
_, _, _, err = NewClientConn(c2, "", &clientConf)
|
||||
if err == nil {
|
||||
t.Fatal("client login succeed with only VerifiedPublicKeyCallback defined")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifiedPubKeyCallbackError(t *testing.T) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
serverConf := &ServerConfig{
|
||||
PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
||||
return nil, nil
|
||||
},
|
||||
VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
|
||||
return nil, errors.New("invalid credentials")
|
||||
},
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
|
||||
clientConf := ClientConfig{
|
||||
User: "user",
|
||||
Auth: []AuthMethod{
|
||||
PublicKeys(testSigners["rsa"]),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
go NewServerConn(c1, serverConf)
|
||||
|
||||
_, _, _, err = NewClientConn(c2, "", &clientConf)
|
||||
if err == nil {
|
||||
t.Fatal("client login succeed with VerifiedPublicKeyCallback returning an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifiedPublicCallbackPartialSuccessBadUsage(t *testing.T) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
serverConf := &ServerConfig{
|
||||
PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
||||
if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
|
||||
// Returning PartialSuccessError is not permitted when
|
||||
// VerifiedPublicKeyCallback is defined. This callback is
|
||||
// invoked for both query requests and real authentications,
|
||||
// while VerifiedPublicKeyCallback is only triggered if the
|
||||
// client has proven control of the key.
|
||||
return nil, &PartialSuccessError{
|
||||
Next: ServerAuthCallbacks{
|
||||
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||
if string(password) == clientPassword {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil, errors.New("invalid credentials")
|
||||
},
|
||||
VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
|
||||
if bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
|
||||
return nil, &PartialSuccessError{
|
||||
Next: ServerAuthCallbacks{
|
||||
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
|
||||
if string(password) == clientPassword {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return nil, errors.New("invalid credentials")
|
||||
},
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
|
||||
clientConf := ClientConfig{
|
||||
User: "user",
|
||||
Auth: []AuthMethod{
|
||||
PublicKeys(testSigners["rsa"]),
|
||||
Password(clientPassword),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
go NewServerConn(c1, serverConf)
|
||||
|
||||
_, _, _, err = NewClientConn(c2, "", &clientConf)
|
||||
if err == nil {
|
||||
t.Fatal("authentication suceeded with PartialSuccess returned from PublicKeyCallback and VerifiedPublicKeyCallback defined")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifiedPublicKeyCallbackOnError(t *testing.T) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
var verifiedCallbackCalled bool
|
||||
|
||||
serverConf := &ServerConfig{
|
||||
VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
|
||||
verifiedCallbackCalled = true
|
||||
return nil, nil
|
||||
},
|
||||
PublicKeyCallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
||||
return nil, errors.New("invalid key")
|
||||
},
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
NewServerConn(c1, serverConf)
|
||||
}()
|
||||
|
||||
clientConf := ClientConfig{
|
||||
User: "user",
|
||||
Auth: []AuthMethod{
|
||||
PublicKeys(testSigners["rsa"]),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
_, _, _, err = NewClientConn(c2, "", &clientConf)
|
||||
if err == nil {
|
||||
t.Fatal("authentication should fail")
|
||||
}
|
||||
<-done
|
||||
if verifiedCallbackCalled {
|
||||
t.Error("VerifiedPublicKeyCallback called after PublicKeyCallback returned an error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifiedPublicKeyCallbackOnly(t *testing.T) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
serverConf := &ServerConfig{
|
||||
VerifiedPublicKeyCallback: func(conn ConnMetadata, key PublicKey, permissions *Permissions, signatureAlgorithm string) (*Permissions, error) {
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer close(done)
|
||||
NewServerConn(c1, serverConf)
|
||||
}()
|
||||
|
||||
clientConf := ClientConfig{
|
||||
User: "user",
|
||||
Auth: []AuthMethod{
|
||||
PublicKeys(testSigners["rsa"]),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
_, _, _, err = NewClientConn(c2, "", &clientConf)
|
||||
if err == nil {
|
||||
t.Fatal("authentication suceeded with only VerifiedPublicKeyCallback defined")
|
||||
}
|
||||
<-done
|
||||
}
|
||||
|
||||
type markerConn struct {
|
||||
closed uint32
|
||||
used uint32
|
||||
|
||||
@@ -4,4 +4,6 @@
|
||||
|
||||
// Package test contains integration tests for the
|
||||
// golang.org/x/crypto/ssh package.
|
||||
//
|
||||
// Deprecated: this package is for internal use only.
|
||||
package test
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
@@ -254,6 +255,9 @@ var (
|
||||
// (to setup server->client keys) or clientKeys (for client->server keys).
|
||||
func newPacketCipher(d direction, algs DirectionAlgorithms, kex *kexResult) (packetCipher, error) {
|
||||
cipherMode := cipherModes[algs.Cipher]
|
||||
if cipherMode == nil {
|
||||
return nil, fmt.Errorf("ssh: unsupported cipher %v", algs.Cipher)
|
||||
}
|
||||
|
||||
iv := make([]byte, cipherMode.ivSize)
|
||||
key := make([]byte, cipherMode.keySize)
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
// Code generated by gen_fallback_bundle.go; DO NOT EDIT.
|
||||
|
||||
package fallback
|
||||
package bundle
|
||||
|
||||
var unparsedCertificates = []unparsedCertificate{
|
||||
{
|
||||
@@ -521,365 +521,377 @@ var unparsedCertificates = []unparsedCertificate{
|
||||
certStartOff: 88949,
|
||||
certLength: 1049,
|
||||
},
|
||||
{
|
||||
cn: "CN=OISTE Server Root ECC G1,O=OISTE Foundation,C=CH",
|
||||
sha256Hash: "eec997c0c30f216f7e3b8b307d2bae42412d753fc8219dafd1520b2572850f49",
|
||||
certStartOff: 89998,
|
||||
certLength: 569,
|
||||
},
|
||||
{
|
||||
cn: "CN=OISTE Server Root RSA G1,O=OISTE Foundation,C=CH",
|
||||
sha256Hash: "9ae36232a5189ffddb353dfd26520c015395d22777dac59db57b98c089a651e6",
|
||||
certStartOff: 90567,
|
||||
certLength: 1415,
|
||||
},
|
||||
{
|
||||
cn: "CN=OISTE WISeKey Global Root GB CA,OU=OISTE Foundation Endorsed,O=WISeKey,C=CH",
|
||||
sha256Hash: "6b9c08e86eb0f767cfad65cd98b62149e5494a67f5845e7bd1ed019f27b86bd6",
|
||||
certStartOff: 89998,
|
||||
certStartOff: 91982,
|
||||
certLength: 953,
|
||||
},
|
||||
{
|
||||
cn: "CN=OISTE WISeKey Global Root GC CA,OU=OISTE Foundation Endorsed,O=WISeKey,C=CH",
|
||||
sha256Hash: "8560f91c3624daba9570b5fea0dbe36ff11a8323be9486854fb3f34a5571198d",
|
||||
certStartOff: 90951,
|
||||
certStartOff: 92935,
|
||||
certLength: 621,
|
||||
},
|
||||
{
|
||||
cn: "CN=QuoVadis Root CA 1 G3,O=QuoVadis Limited,C=BM",
|
||||
sha256Hash: "8a866fd1b276b57e578e921c65828a2bed58e9f2f288054134b7f1f4bfc9cc74",
|
||||
certStartOff: 91572,
|
||||
certStartOff: 93556,
|
||||
certLength: 1380,
|
||||
},
|
||||
{
|
||||
cn: "CN=QuoVadis Root CA 2 G3,O=QuoVadis Limited,C=BM",
|
||||
sha256Hash: "8fe4fb0af93a4d0d67db0bebb23e37c71bf325dcbcdd240ea04daf58b47e1840",
|
||||
certStartOff: 92952,
|
||||
certStartOff: 94936,
|
||||
certLength: 1380,
|
||||
},
|
||||
{
|
||||
cn: "CN=QuoVadis Root CA 2,O=QuoVadis Limited,C=BM",
|
||||
sha256Hash: "85a0dd7dd720adb7ff05f83d542b209dc7ff4528f7d677b18389fea5e5c49e86",
|
||||
certStartOff: 94332,
|
||||
certStartOff: 96316,
|
||||
certLength: 1467,
|
||||
},
|
||||
{
|
||||
cn: "CN=QuoVadis Root CA 3 G3,O=QuoVadis Limited,C=BM",
|
||||
sha256Hash: "88ef81de202eb018452e43f864725cea5fbd1fc2d9d205730709c5d8b8690f46",
|
||||
certStartOff: 95799,
|
||||
certStartOff: 97783,
|
||||
certLength: 1380,
|
||||
},
|
||||
{
|
||||
cn: "CN=QuoVadis Root CA 3,O=QuoVadis Limited,C=BM",
|
||||
sha256Hash: "18f1fc7f205df8adddeb7fe007dd57e3af375a9c4d8d73546bf4f1fed1e18d35",
|
||||
certStartOff: 97179,
|
||||
certStartOff: 99163,
|
||||
certLength: 1697,
|
||||
},
|
||||
{
|
||||
cn: "CN=SSL.com EV Root Certification Authority ECC,O=SSL Corporation,L=Houston,ST=Texas,C=US",
|
||||
sha256Hash: "22a2c1f7bded704cc1e701b5f408c310880fe956b5de2a4a44f99c873a25a7c8",
|
||||
certStartOff: 98876,
|
||||
certStartOff: 100860,
|
||||
certLength: 664,
|
||||
},
|
||||
{
|
||||
cn: "CN=SSL.com EV Root Certification Authority RSA R2,O=SSL Corporation,L=Houston,ST=Texas,C=US",
|
||||
sha256Hash: "2e7bf16cc22485a7bbe2aa8696750761b0ae39be3b2fe9d0cc6d4ef73491425c",
|
||||
certStartOff: 99540,
|
||||
certStartOff: 101524,
|
||||
certLength: 1519,
|
||||
},
|
||||
{
|
||||
cn: "CN=SSL.com Root Certification Authority ECC,O=SSL Corporation,L=Houston,ST=Texas,C=US",
|
||||
sha256Hash: "3417bb06cc6007da1b961c920b8ab4ce3fad820e4aa30b9acbc4a74ebdcebc65",
|
||||
certStartOff: 101059,
|
||||
certStartOff: 103043,
|
||||
certLength: 657,
|
||||
},
|
||||
{
|
||||
cn: "CN=SSL.com Root Certification Authority RSA,O=SSL Corporation,L=Houston,ST=Texas,C=US",
|
||||
sha256Hash: "85666a562ee0be5ce925c1d8890a6f76a87ec16d4d7d5f29ea7419cf20123b69",
|
||||
certStartOff: 101716,
|
||||
certStartOff: 103700,
|
||||
certLength: 1505,
|
||||
},
|
||||
{
|
||||
cn: "CN=SSL.com TLS ECC Root CA 2022,O=SSL Corporation,C=US",
|
||||
sha256Hash: "c32ffd9f46f936d16c3673990959434b9ad60aafbb9e7cf33654f144cc1ba143",
|
||||
certStartOff: 103221,
|
||||
certStartOff: 105205,
|
||||
certLength: 574,
|
||||
},
|
||||
{
|
||||
cn: "CN=SSL.com TLS RSA Root CA 2022,O=SSL Corporation,C=US",
|
||||
sha256Hash: "8faf7d2e2cb4709bb8e0b33666bf75a5dd45b5de480f8ea8d4bfe6bebc17f2ed",
|
||||
certStartOff: 103795,
|
||||
certStartOff: 105779,
|
||||
certLength: 1421,
|
||||
},
|
||||
{
|
||||
cn: "CN=SZAFIR ROOT CA2,O=Krajowa Izba Rozliczeniowa S.A.,C=PL",
|
||||
sha256Hash: "a1339d33281a0b56e557d3d32b1ce7f9367eb094bd5fa72a7e5004c8ded7cafe",
|
||||
certStartOff: 105216,
|
||||
certStartOff: 107200,
|
||||
certLength: 886,
|
||||
},
|
||||
{
|
||||
cn: "CN=Sectigo Public Server Authentication Root E46,O=Sectigo Limited,C=GB",
|
||||
sha256Hash: "c90f26f0fb1b4018b22227519b5ca2b53e2ca5b3be5cf18efe1bef47380c5383",
|
||||
certStartOff: 106102,
|
||||
certStartOff: 108086,
|
||||
certLength: 574,
|
||||
},
|
||||
{
|
||||
cn: "CN=Sectigo Public Server Authentication Root R46,O=Sectigo Limited,C=GB",
|
||||
sha256Hash: "7bb647a62aeeac88bf257aa522d01ffea395e0ab45c73f93f65654ec38f25a06",
|
||||
certStartOff: 106676,
|
||||
certStartOff: 108660,
|
||||
certLength: 1422,
|
||||
},
|
||||
{
|
||||
cn: "CN=Secure Global CA,O=SecureTrust Corporation,C=US",
|
||||
sha256Hash: "4200f5043ac8590ebb527d209ed1503029fbcbd41ca1b506ec27f15ade7dac69",
|
||||
certStartOff: 108098,
|
||||
certStartOff: 110082,
|
||||
certLength: 960,
|
||||
},
|
||||
{
|
||||
cn: "CN=SecureSign Root CA12,O=Cybertrust Japan Co.\\, Ltd.,C=JP",
|
||||
sha256Hash: "3f034bb5704d44b2d08545a02057de93ebf3905fce721acbc730c06ddaee904e",
|
||||
certStartOff: 109058,
|
||||
certStartOff: 111042,
|
||||
certLength: 886,
|
||||
},
|
||||
{
|
||||
cn: "CN=SecureSign Root CA14,O=Cybertrust Japan Co.\\, Ltd.,C=JP",
|
||||
sha256Hash: "4b009c1034494f9ab56bba3ba1d62731fc4d20d8955adcec10a925607261e338",
|
||||
certStartOff: 109944,
|
||||
certStartOff: 111928,
|
||||
certLength: 1398,
|
||||
},
|
||||
{
|
||||
cn: "CN=SecureSign Root CA15,O=Cybertrust Japan Co.\\, Ltd.,C=JP",
|
||||
sha256Hash: "e778f0f095fe843729cd1a0082179e5314a9c291442805e1fb1d8fb6b8886c3a",
|
||||
certStartOff: 111342,
|
||||
certStartOff: 113326,
|
||||
certLength: 551,
|
||||
},
|
||||
{
|
||||
cn: "CN=SecureTrust CA,O=SecureTrust Corporation,C=US",
|
||||
sha256Hash: "f1c1b50ae5a20dd8030ec9f6bc24823dd367b5255759b4e71b61fce9f7375d73",
|
||||
certStartOff: 111893,
|
||||
certStartOff: 113877,
|
||||
certLength: 956,
|
||||
},
|
||||
{
|
||||
cn: "CN=Security Communication ECC RootCA1,O=SECOM Trust Systems CO.\\,LTD.,C=JP",
|
||||
sha256Hash: "e74fbda55bd564c473a36b441aa799c8a68e077440e8288b9fa1e50e4bbaca11",
|
||||
certStartOff: 112849,
|
||||
certStartOff: 114833,
|
||||
certLength: 572,
|
||||
},
|
||||
{
|
||||
cn: "CN=Starfield Root Certificate Authority - G2,O=Starfield Technologies\\, Inc.,L=Scottsdale,ST=Arizona,C=US",
|
||||
sha256Hash: "2ce1cb0bf9d2f9e102993fbe215152c3b2dd0cabde1c68e5319b839154dbb7f5",
|
||||
certStartOff: 113421,
|
||||
certStartOff: 115405,
|
||||
certLength: 993,
|
||||
},
|
||||
{
|
||||
cn: "CN=Starfield Services Root Certificate Authority - G2,O=Starfield Technologies\\, Inc.,L=Scottsdale,ST=Arizona,C=US",
|
||||
sha256Hash: "568d6905a2c88708a4b3025190edcfedb1974a606a13c6e5290fcb2ae63edab5",
|
||||
certStartOff: 114414,
|
||||
certStartOff: 116398,
|
||||
certLength: 1011,
|
||||
},
|
||||
{
|
||||
cn: "CN=SwissSign Gold CA - G2,O=SwissSign AG,C=CH",
|
||||
sha256Hash: "62dd0be9b9f50a163ea0f8e75c053b1eca57ea55c8688f647c6881f2c8357b95",
|
||||
certStartOff: 115425,
|
||||
certStartOff: 117409,
|
||||
certLength: 1470,
|
||||
},
|
||||
{
|
||||
cn: "CN=SwissSign RSA TLS Root CA 2022 - 1,O=SwissSign AG,C=CH",
|
||||
sha256Hash: "193144f431e0fddb740717d4de926a571133884b4360d30e272913cbe660ce41",
|
||||
certStartOff: 116895,
|
||||
certStartOff: 118879,
|
||||
certLength: 1431,
|
||||
},
|
||||
{
|
||||
cn: "CN=T-TeleSec GlobalRoot Class 2,OU=T-Systems Trust Center,O=T-Systems Enterprise Services GmbH,C=DE",
|
||||
sha256Hash: "91e2f5788d5810eba7ba58737de1548a8ecacd014598bc0b143e041b17052552",
|
||||
certStartOff: 118326,
|
||||
certStartOff: 120310,
|
||||
certLength: 967,
|
||||
},
|
||||
{
|
||||
cn: "CN=T-TeleSec GlobalRoot Class 3,OU=T-Systems Trust Center,O=T-Systems Enterprise Services GmbH,C=DE",
|
||||
sha256Hash: "fd73dad31c644ff1b43bef0ccdda96710b9cd9875eca7e31707af3e96d522bbd",
|
||||
certStartOff: 119293,
|
||||
certStartOff: 121277,
|
||||
certLength: 967,
|
||||
},
|
||||
{
|
||||
cn: "CN=TWCA CYBER Root CA,OU=Root CA,O=TAIWAN-CA,C=TW",
|
||||
sha256Hash: "3f63bb2814be174ec8b6439cf08d6d56f0b7c405883a5648a334424d6b3ec558",
|
||||
certStartOff: 120260,
|
||||
certStartOff: 122244,
|
||||
certLength: 1425,
|
||||
},
|
||||
{
|
||||
cn: "CN=TWCA Global Root CA,OU=Root CA,O=TAIWAN-CA,C=TW",
|
||||
sha256Hash: "59769007f7685d0fcd50872f9f95d5755a5b2b457d81f3692b610a98672f0e1b",
|
||||
certStartOff: 121685,
|
||||
certStartOff: 123669,
|
||||
certLength: 1349,
|
||||
},
|
||||
{
|
||||
cn: "CN=TWCA Root Certification Authority,OU=Root CA,O=TAIWAN-CA,C=TW",
|
||||
sha256Hash: "bfd88fe1101c41ae3e801bf8be56350ee9bad1a6b9bd515edc5c6d5b8711ac44",
|
||||
certStartOff: 123034,
|
||||
certStartOff: 125018,
|
||||
certLength: 895,
|
||||
},
|
||||
{
|
||||
cn: "CN=Telekom Security TLS ECC Root 2020,O=Deutsche Telekom Security GmbH,C=DE",
|
||||
sha256Hash: "578af4ded0853f4e5998db4aeaf9cbea8d945f60b620a38d1a3c13b2bc7ba8e1",
|
||||
certStartOff: 123929,
|
||||
certStartOff: 125913,
|
||||
certLength: 582,
|
||||
},
|
||||
{
|
||||
cn: "CN=Telekom Security TLS RSA Root 2023,O=Deutsche Telekom Security GmbH,C=DE",
|
||||
sha256Hash: "efc65cadbb59adb6efe84da22311b35624b71b3b1ea0da8b6655174ec8978646",
|
||||
certStartOff: 124511,
|
||||
certStartOff: 126495,
|
||||
certLength: 1463,
|
||||
},
|
||||
{
|
||||
cn: "CN=Telia Root CA v2,O=Telia Finland Oyj,C=FI",
|
||||
sha256Hash: "242b69742fcb1e5b2abf98898b94572187544e5b4d9911786573621f6a74b82c",
|
||||
certStartOff: 125974,
|
||||
certStartOff: 127958,
|
||||
certLength: 1400,
|
||||
},
|
||||
{
|
||||
cn: "CN=TeliaSonera Root CA v1,O=TeliaSonera",
|
||||
sha256Hash: "dd6936fe21f8f077c123a1a521c12224f72255b73e03a7260693e8a24b0fa389",
|
||||
certStartOff: 127374,
|
||||
certStartOff: 129358,
|
||||
certLength: 1340,
|
||||
},
|
||||
{
|
||||
cn: "CN=TrustAsia Global Root CA G3,O=TrustAsia Technologies\\, Inc.,C=CN",
|
||||
sha256Hash: "e0d3226aeb1163c2e48ff9be3b50b4c6431be7bb1eacc5c36b5d5ec509039a08",
|
||||
certStartOff: 128714,
|
||||
certStartOff: 130698,
|
||||
certLength: 1449,
|
||||
},
|
||||
{
|
||||
cn: "CN=TrustAsia Global Root CA G4,O=TrustAsia Technologies\\, Inc.,C=CN",
|
||||
sha256Hash: "be4b56cb5056c0136a526df444508daa36a0b54f42e4ac38f72af470e479654c",
|
||||
certStartOff: 130163,
|
||||
certStartOff: 132147,
|
||||
certLength: 601,
|
||||
},
|
||||
{
|
||||
cn: "CN=TrustAsia TLS ECC Root CA,O=TrustAsia Technologies\\, Inc.,C=CN",
|
||||
sha256Hash: "c0076b9ef0531fb1a656d67c4ebe97cd5dbaa41ef44598acc2489878c92d8711",
|
||||
certStartOff: 130764,
|
||||
certStartOff: 132748,
|
||||
certLength: 565,
|
||||
},
|
||||
{
|
||||
cn: "CN=TrustAsia TLS RSA Root CA,O=TrustAsia Technologies\\, Inc.,C=CN",
|
||||
sha256Hash: "06c08d7dafd876971eb1124fe67f847ec0c7a158d3ea53cbe940e2ea9791f4c3",
|
||||
certStartOff: 131329,
|
||||
certStartOff: 133313,
|
||||
certLength: 1412,
|
||||
},
|
||||
{
|
||||
cn: "CN=Trustwave Global Certification Authority,O=Trustwave Holdings\\, Inc.,L=Chicago,ST=Illinois,C=US",
|
||||
sha256Hash: "97552015f5ddfc3c8788c006944555408894450084f100867086bc1a2bb58dc8",
|
||||
certStartOff: 132741,
|
||||
certStartOff: 134725,
|
||||
certLength: 1502,
|
||||
},
|
||||
{
|
||||
cn: "CN=Trustwave Global ECC P256 Certification Authority,O=Trustwave Holdings\\, Inc.,L=Chicago,ST=Illinois,C=US",
|
||||
sha256Hash: "945bbc825ea554f489d1fd51a73ddf2ea624ac7019a05205225c22a78ccfa8b4",
|
||||
certStartOff: 134243,
|
||||
certStartOff: 136227,
|
||||
certLength: 612,
|
||||
},
|
||||
{
|
||||
cn: "CN=Trustwave Global ECC P384 Certification Authority,O=Trustwave Holdings\\, Inc.,L=Chicago,ST=Illinois,C=US",
|
||||
sha256Hash: "55903859c8c0c3ebb8759ece4e2557225ff5758bbd38ebd48276601e1bd58097",
|
||||
certStartOff: 134855,
|
||||
certStartOff: 136839,
|
||||
certLength: 673,
|
||||
},
|
||||
{
|
||||
cn: "CN=TunTrust Root CA,O=Agence Nationale de Certification Electronique,C=TN",
|
||||
sha256Hash: "2e44102ab58cb85419451c8e19d9acf3662cafbc614b6a53960a30f7d0e2eb41",
|
||||
certStartOff: 135528,
|
||||
certStartOff: 137512,
|
||||
certLength: 1463,
|
||||
},
|
||||
{
|
||||
cn: "CN=UCA Extended Validation Root,O=UniTrust,C=CN",
|
||||
sha256Hash: "d43af9b35473755c9684fc06d7d8cb70ee5c28e773fb294eb41ee71722924d24",
|
||||
certStartOff: 136991,
|
||||
certStartOff: 138975,
|
||||
certLength: 1374,
|
||||
},
|
||||
{
|
||||
cn: "CN=UCA Global G2 Root,O=UniTrust,C=CN",
|
||||
sha256Hash: "9bea11c976fe014764c1be56a6f914b5a560317abd9988393382e5161aa0493c",
|
||||
certStartOff: 138365,
|
||||
certStartOff: 140349,
|
||||
certLength: 1354,
|
||||
},
|
||||
{
|
||||
cn: "CN=USERTrust ECC Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US",
|
||||
sha256Hash: "4ff460d54b9c86dabfbcfc5712e0400d2bed3fbc4d4fbdaa86e06adcd2a9ad7a",
|
||||
certStartOff: 139719,
|
||||
certStartOff: 141703,
|
||||
certLength: 659,
|
||||
},
|
||||
{
|
||||
cn: "CN=USERTrust RSA Certification Authority,O=The USERTRUST Network,L=Jersey City,ST=New Jersey,C=US",
|
||||
sha256Hash: "e793c9b02fd8aa13e21c31228accb08119643b749c898964b1746d46c3d4cbd2",
|
||||
certStartOff: 140378,
|
||||
certStartOff: 142362,
|
||||
certLength: 1506,
|
||||
},
|
||||
{
|
||||
cn: "CN=e-Szigno Root CA 2017,O=Microsec Ltd.,L=Budapest,C=HU,2.5.4.97=#130e56415448552d3233353834343937",
|
||||
sha256Hash: "beb00b30839b9bc32c32e4447905950641f26421b15ed089198b518ae2ea1b99",
|
||||
certStartOff: 141884,
|
||||
certStartOff: 143868,
|
||||
certLength: 580,
|
||||
},
|
||||
{
|
||||
cn: "CN=emSign ECC Root CA - C3,OU=emSign PKI,O=eMudhra Inc,C=US",
|
||||
sha256Hash: "bc4d809b15189d78db3e1d8cf4f9726a795da1643ca5f1358e1ddb0edc0d7eb3",
|
||||
certStartOff: 142464,
|
||||
certStartOff: 144448,
|
||||
certLength: 559,
|
||||
},
|
||||
{
|
||||
cn: "CN=emSign ECC Root CA - G3,OU=emSign PKI,O=eMudhra Technologies Limited,C=IN",
|
||||
sha256Hash: "86a1ecba089c4a8d3bbe2734c612ba341d813e043cf9e8a862cd5c57a36bbe6b",
|
||||
certStartOff: 143023,
|
||||
certStartOff: 145007,
|
||||
certLength: 594,
|
||||
},
|
||||
{
|
||||
cn: "CN=emSign Root CA - C1,OU=emSign PKI,O=eMudhra Inc,C=US",
|
||||
sha256Hash: "125609aa301da0a249b97a8239cb6a34216f44dcac9f3954b14292f2e8c8608f",
|
||||
certStartOff: 143617,
|
||||
certStartOff: 145601,
|
||||
certLength: 887,
|
||||
},
|
||||
{
|
||||
cn: "CN=emSign Root CA - G1,OU=emSign PKI,O=eMudhra Technologies Limited,C=IN",
|
||||
sha256Hash: "40f6af0346a99aa1cd1d555a4e9cce62c7f9634603ee406615833dc8c8d00367",
|
||||
certStartOff: 144504,
|
||||
certStartOff: 146488,
|
||||
certLength: 920,
|
||||
},
|
||||
{
|
||||
cn: "CN=vTrus ECC Root CA,O=iTrusChina Co.\\,Ltd.,C=CN",
|
||||
sha256Hash: "30fbba2c32238e2a98547af97931e550428b9b3f1c8eeb6633dcfa86c5b27dd3",
|
||||
certStartOff: 145424,
|
||||
certStartOff: 147408,
|
||||
certLength: 531,
|
||||
},
|
||||
{
|
||||
cn: "CN=vTrus Root CA,O=iTrusChina Co.\\,Ltd.,C=CN",
|
||||
sha256Hash: "8a71de6559336f426c26e53880d00d88a18da4c6a91f0dcb6194e206c5c96387",
|
||||
certStartOff: 145955,
|
||||
certStartOff: 147939,
|
||||
certLength: 1370,
|
||||
},
|
||||
{
|
||||
cn: "OU=AC RAIZ FNMT-RCM,O=FNMT-RCM,C=ES",
|
||||
sha256Hash: "ebc5570c29018c4d67b1aa127baf12f703b4611ebc17b7dab5573894179b93fa",
|
||||
certStartOff: 147325,
|
||||
certStartOff: 149309,
|
||||
certLength: 1415,
|
||||
},
|
||||
{
|
||||
cn: "OU=Security Communication RootCA2,O=SECOM Trust Systems CO.\\,LTD.,C=JP",
|
||||
sha256Hash: "513b2cecb810d4cde5dd85391adfc6c2dd60d87bb736d2b521484aa47a0ebef6",
|
||||
certStartOff: 148740,
|
||||
certStartOff: 150724,
|
||||
certLength: 891,
|
||||
},
|
||||
{
|
||||
cn: "OU=certSIGN ROOT CA G2,O=CERTSIGN SA,C=RO",
|
||||
sha256Hash: "657cfe2fa73faa38462571f332a2363a46fce7020951710702cdfbb6eeda3305",
|
||||
certStartOff: 149631,
|
||||
certStartOff: 151615,
|
||||
certLength: 1355,
|
||||
},
|
||||
{
|
||||
cn: "OU=certSIGN ROOT CA,O=certSIGN,C=RO",
|
||||
sha256Hash: "eaa962c4fa4a6bafebe415196d351ccd888d4f53f3fa8ae6d7c466a94e6042bb",
|
||||
certStartOff: 150986,
|
||||
certStartOff: 152970,
|
||||
certLength: 828,
|
||||
},
|
||||
{
|
||||
cn: "OU=ePKI Root Certification Authority,O=Chunghwa Telecom Co.\\, Ltd.,C=TW",
|
||||
sha256Hash: "c0a6f4dc63a24bfdcf54ef2a6a082a0a72de35803e2ff5ff527ae5d87206dfd5",
|
||||
certStartOff: 151814,
|
||||
certStartOff: 153798,
|
||||
certLength: 1460,
|
||||
distrustAfter: "2025-04-15T23:59:59Z",
|
||||
},
|
||||
{
|
||||
cn: "SERIALNUMBER=G63287510,CN=ANF Secure Server Root CA,OU=ANF CA Raiz,O=ANF Autoridad de Certificacion,C=ES",
|
||||
sha256Hash: "fb8fec759169b9106b1e511644c618c51304373f6c0643088d8beffd1b997599",
|
||||
certStartOff: 153274,
|
||||
certStartOff: 155258,
|
||||
certLength: 1523,
|
||||
},
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fallback
|
||||
package bundle
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright 2025 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 bundle contains the bundle of root certificates parsed from the NSS
|
||||
// trust store, using x509roots/nss.
|
||||
package bundle
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"iter"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:embed bundle.der
|
||||
var rawCerts []byte
|
||||
|
||||
// Root represents a root certificate parsed from the NSS trust store.
|
||||
type Root struct {
|
||||
// Certificate is the DER-encoded certificate (read-only; do not modify!).
|
||||
Certificate []byte
|
||||
|
||||
// Constraint is nil if the root is unconstrained. If Constraint is non-nil,
|
||||
// the certificate has additional constraints that cannot be encoded in
|
||||
// X.509, and when building a certificate chain anchored with this root the
|
||||
// chain should be passed to this function to check its validity. If using a
|
||||
// [crypto/x509.CertPool] the root should be added using
|
||||
// [crypto/x509.CertPool.AddCertWithConstraint].
|
||||
Constraint func([]*x509.Certificate) error
|
||||
}
|
||||
|
||||
// Roots returns the bundle of root certificates from the NSS trust store. The
|
||||
// [Root.Certificate] slice must be treated as read-only and should not be
|
||||
// modified.
|
||||
func Roots() iter.Seq[Root] {
|
||||
return func(yield func(Root) bool) {
|
||||
for _, unparsed := range unparsedCertificates {
|
||||
root := Root{
|
||||
Certificate: rawCerts[unparsed.certStartOff : unparsed.certStartOff+unparsed.certLength],
|
||||
}
|
||||
// parse possible constraints, this should check all fields of unparsedCertificate.
|
||||
if unparsed.distrustAfter != "" {
|
||||
distrustAfter, err := time.Parse(time.RFC3339, unparsed.distrustAfter)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to parse distrustAfter %q: %s", unparsed.distrustAfter, err))
|
||||
}
|
||||
root.Constraint = func(chain []*x509.Certificate) error {
|
||||
for _, c := range chain {
|
||||
if c.NotBefore.After(distrustAfter) {
|
||||
return fmt.Errorf("certificate issued after distrust-after date %q", distrustAfter)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if !yield(root) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type unparsedCertificate struct {
|
||||
cn string
|
||||
sha256Hash string
|
||||
certStartOff int
|
||||
certLength int
|
||||
|
||||
// possible constraints
|
||||
distrustAfter string
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2025 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 bundle
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRootsCanBeParsed(t *testing.T) {
|
||||
for root := range Roots() {
|
||||
if _, err := x509.ParseCertificate(root.Certificate); err != nil {
|
||||
t.Fatalf("Could not parse root certificate: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,13 +20,9 @@ package fallback
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:embed bundle.der
|
||||
var rawCerts []byte
|
||||
"golang.org/x/crypto/x509roots/fallback/bundle"
|
||||
)
|
||||
|
||||
func init() {
|
||||
x509.SetFallbackRoots(newFallbackCertPool())
|
||||
@@ -34,62 +30,16 @@ func init() {
|
||||
|
||||
func newFallbackCertPool() *x509.CertPool {
|
||||
p := x509.NewCertPool()
|
||||
for _, c := range mustParse(unparsedCertificates) {
|
||||
if len(c.constraints) == 0 {
|
||||
p.AddCert(c.cert)
|
||||
for c := range bundle.Roots() {
|
||||
cert, err := x509.ParseCertificate(c.Certificate)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if c.Constraint == nil {
|
||||
p.AddCert(cert)
|
||||
} else {
|
||||
p.AddCertWithConstraint(c.cert, func(chain []*x509.Certificate) error {
|
||||
for _, constraint := range c.constraints {
|
||||
if err := constraint(chain); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
p.AddCertWithConstraint(cert, c.Constraint)
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
type unparsedCertificate struct {
|
||||
cn string
|
||||
sha256Hash string
|
||||
certStartOff int
|
||||
certLength int
|
||||
|
||||
// possible constraints
|
||||
distrustAfter string
|
||||
}
|
||||
|
||||
type parsedCertificate struct {
|
||||
cert *x509.Certificate
|
||||
constraints []func([]*x509.Certificate) error
|
||||
}
|
||||
|
||||
func mustParse(unparsedCerts []unparsedCertificate) []parsedCertificate {
|
||||
b := make([]parsedCertificate, 0, len(unparsedCerts))
|
||||
for _, unparsed := range unparsedCerts {
|
||||
cert, err := x509.ParseCertificate(rawCerts[unparsed.certStartOff : unparsed.certStartOff+unparsed.certLength])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
parsed := parsedCertificate{cert: cert}
|
||||
// parse possible constraints, this should check all fields of unparsedCertificate.
|
||||
if unparsed.distrustAfter != "" {
|
||||
distrustAfter, err := time.Parse(time.RFC3339, unparsed.distrustAfter)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to parse distrustAfter %q: %s", unparsed.distrustAfter, err))
|
||||
}
|
||||
parsed.constraints = append(parsed.constraints, func(chain []*x509.Certificate) error {
|
||||
for _, c := range chain {
|
||||
if c.NotBefore.After(distrustAfter) {
|
||||
return fmt.Errorf("certificate issued after distrust-after date %q", distrustAfter)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
b = append(b, parsed)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
|
||||
const tmpl = `// Code generated by gen_fallback_bundle.go; DO NOT EDIT.
|
||||
|
||||
package fallback
|
||||
package bundle
|
||||
|
||||
var unparsedCertificates = []unparsedCertificate{
|
||||
`
|
||||
@@ -35,8 +35,8 @@ var unparsedCertificates = []unparsedCertificate{
|
||||
var (
|
||||
certDataURL = flag.String("certdata-url", "https://hg.mozilla.org/mozilla-central/raw-file/tip/security/nss/lib/ckfw/builtins/certdata.txt", "URL to the raw certdata.txt file to parse (certdata-path overrides this, if provided)")
|
||||
certDataPath = flag.String("certdata-path", "", "Path to the NSS certdata.txt file to parse (this overrides certdata-url, if provided)")
|
||||
output = flag.String("output", "fallback/bundle.go", "Path to file to write output to")
|
||||
derOutput = flag.String("deroutput", "fallback/bundle.der", "Path to file to write output to (DER certificate bundle)")
|
||||
output = flag.String("output", "fallback/bundle/bundle.go", "Path to file to write output to")
|
||||
derOutput = flag.String("deroutput", "fallback/bundle/bundle.der", "Path to file to write output to (DER certificate bundle)")
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
//
|
||||
// Note that XTS is usually not appropriate for any use besides disk encryption.
|
||||
// Most users should use an AEAD mode like GCM (from crypto/cipher.NewGCM) instead.
|
||||
//
|
||||
// The xts package is [frozen] and is not accepting new features.
|
||||
//
|
||||
// [frozen]: https://go.dev/wiki/Frozen
|
||||
package xts
|
||||
|
||||
import (
|
||||
|
||||
Reference in New Issue
Block a user