Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5307a0ce6d | ||
|
|
9d779377cf | ||
|
|
8f580defa0 | ||
|
|
a4d1237429 | ||
|
|
b8d8dae13d | ||
|
|
f5a2eabcab | ||
|
|
44ecf3af99 | ||
|
|
ef5341b706 | ||
|
|
b999374650 | ||
|
|
c247dead11 | ||
|
|
1fda73153f | ||
|
|
1b4c3d2e8c | ||
|
|
b903b535d3 |
+25
-5
@@ -382,7 +382,12 @@ func testIssuance(t *testing.T, env *environment, challSrv challengeServer) {
|
|||||||
// Wait for the order to become ready for finalization.
|
// Wait for the order to become ready for finalization.
|
||||||
order, err = client.WaitOrder(ctx, order.URI)
|
order, err = client.WaitOrder(ctx, order.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to wait for order %s: %s", orderURL, err)
|
var orderErr *acme.OrderError
|
||||||
|
if errors.Is(err, orderErr) {
|
||||||
|
t.Fatalf("failed to wait for order %s: %s: %s", orderURL, err, orderErr.Problem)
|
||||||
|
} else {
|
||||||
|
t.Fatalf("failed to wait for order %s: %s", orderURL, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if order.Status != acme.StatusReady {
|
if order.Status != acme.StatusReady {
|
||||||
t.Fatalf("expected order %s status to be ready, got %v",
|
t.Fatalf("expected order %s status to be ready, got %v",
|
||||||
@@ -692,14 +697,14 @@ func startPebbleEnvironment(t *testing.T, config *environmentConfig) environment
|
|||||||
func waitForServer(t *testing.T, addr string) {
|
func waitForServer(t *testing.T, addr string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
if conn, err := net.Dial("tcp", addr); err == nil {
|
if conn, err := net.Dial("tcp", addr); err == nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
time.Sleep(time.Duration(i*100) * time.Millisecond)
|
time.Sleep(time.Duration(i*100) * time.Millisecond)
|
||||||
}
|
}
|
||||||
t.Fatalf("failed to connect to %q after 10 tries", addr)
|
t.Fatalf("failed to connect to %q after 20 tries", addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchModule fetches the module at the given version and returns the directory
|
// fetchModule fetches the module at the given version and returns the directory
|
||||||
@@ -776,14 +781,29 @@ func prepareBinaries(t *testing.T, pebbleDir string) string {
|
|||||||
func spawnServerProcess(t *testing.T, dir string, cmd string, args ...string) {
|
func spawnServerProcess(t *testing.T, dir string, cmd string, args ...string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
|
||||||
cmdInstance := exec.Command("./"+cmd, args...)
|
cmdInstance := exec.Command("./"+cmd, args...)
|
||||||
cmdInstance.Dir = dir
|
cmdInstance.Dir = dir
|
||||||
cmdInstance.Stdout = os.Stdout
|
cmdInstance.Stdout = &stdout
|
||||||
cmdInstance.Stderr = os.Stderr
|
cmdInstance.Stderr = &stderr
|
||||||
|
|
||||||
if err := cmdInstance.Start(); err != nil {
|
if err := cmdInstance.Start(); err != nil {
|
||||||
t.Fatalf("failed to start %s: %v", cmd, err)
|
t.Fatalf("failed to start %s: %v", cmd, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
cmdInstance.Process.Kill()
|
cmdInstance.Process.Kill()
|
||||||
|
cmdInstance.Wait()
|
||||||
|
|
||||||
|
if t.Failed() || testing.Verbose() {
|
||||||
|
t.Logf("=== %s output ===", cmd)
|
||||||
|
if stdout.Len() > 0 {
|
||||||
|
t.Logf("stdout:\n%s", strings.TrimSpace(stdout.String()))
|
||||||
|
}
|
||||||
|
if stderr.Len() > 0 {
|
||||||
|
t.Logf("stderr:\n%s", strings.TrimSpace(stderr.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -272,7 +272,7 @@ func (c *Client) WaitOrder(ctx context.Context, url string) (*Order, error) {
|
|||||||
case err != nil:
|
case err != nil:
|
||||||
// Skip and retry.
|
// Skip and retry.
|
||||||
case o.Status == StatusInvalid:
|
case o.Status == StatusInvalid:
|
||||||
return nil, &OrderError{OrderURL: o.URI, Status: o.Status}
|
return nil, &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error}
|
||||||
case o.Status == StatusReady || o.Status == StatusValid:
|
case o.Status == StatusReady || o.Status == StatusValid:
|
||||||
return o, nil
|
return o, nil
|
||||||
}
|
}
|
||||||
@@ -369,7 +369,7 @@ func (c *Client) CreateOrderCert(ctx context.Context, url string, csr []byte, bu
|
|||||||
}
|
}
|
||||||
// The only acceptable status post finalize and WaitOrder is "valid".
|
// The only acceptable status post finalize and WaitOrder is "valid".
|
||||||
if o.Status != StatusValid {
|
if o.Status != StatusValid {
|
||||||
return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status}
|
return nil, "", &OrderError{OrderURL: o.URI, Status: o.Status, Problem: o.Error}
|
||||||
}
|
}
|
||||||
crt, err := c.fetchCertRFC(ctx, o.CertURL, bundle)
|
crt, err := c.fetchCertRFC(ctx, o.CertURL, bundle)
|
||||||
return crt, o.CertURL, err
|
return crt, o.CertURL, err
|
||||||
|
|||||||
+16
-3
@@ -885,11 +885,17 @@ func TestRFC_WaitOrderError(t *testing.T) {
|
|||||||
s.handle("/orders/1", func(w http.ResponseWriter, r *http.Request) {
|
s.handle("/orders/1", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Location", s.url("/orders/1"))
|
w.Header().Set("Location", s.url("/orders/1"))
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
s := StatusPending
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
s = StatusInvalid
|
// https://www.rfc-editor.org/rfc/rfc8555#section-7.3.3
|
||||||
|
errorData := `{
|
||||||
|
"type": "urn:ietf:params:acme:error:userActionRequired",
|
||||||
|
"detail": "Terms of service have changed",
|
||||||
|
"instance": "https://example.com/acme/agreement/?token=W8Ih3PswD-8"
|
||||||
|
}`
|
||||||
|
fmt.Fprintf(w, `{"status": %q, "error": %s}`, StatusInvalid, errorData)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, `{"status": %q}`, StatusPending)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, `{"status": %q}`, s)
|
|
||||||
count++
|
count++
|
||||||
})
|
})
|
||||||
s.start()
|
s.start()
|
||||||
@@ -910,6 +916,13 @@ func TestRFC_WaitOrderError(t *testing.T) {
|
|||||||
if e.Status != StatusInvalid {
|
if e.Status != StatusInvalid {
|
||||||
t.Errorf("e.Status = %q; want %q", e.Status, StatusInvalid)
|
t.Errorf("e.Status = %q; want %q", e.Status, StatusInvalid)
|
||||||
}
|
}
|
||||||
|
if e.Problem == nil {
|
||||||
|
t.Errorf("e.Problem = nil")
|
||||||
|
}
|
||||||
|
expectedProbType := "urn:ietf:params:acme:error:userActionRequired"
|
||||||
|
if e.Problem.ProblemType != expectedProbType {
|
||||||
|
t.Errorf("e.Problem.ProblemType = %q; want %q", e.Problem.ProblemType, expectedProbType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRFC_CreateOrderCert(t *testing.T) {
|
func TestRFC_CreateOrderCert(t *testing.T) {
|
||||||
|
|||||||
+4
-1
@@ -154,13 +154,16 @@ func (a *AuthorizationError) Error() string {
|
|||||||
|
|
||||||
// OrderError is returned from Client's order related methods.
|
// OrderError is returned from Client's order related methods.
|
||||||
// It indicates the order is unusable and the clients should start over with
|
// It indicates the order is unusable and the clients should start over with
|
||||||
// AuthorizeOrder.
|
// AuthorizeOrder. A Problem description may be provided with details on
|
||||||
|
// what caused the order to become unusable.
|
||||||
//
|
//
|
||||||
// The clients can still fetch the order object from CA using GetOrder
|
// The clients can still fetch the order object from CA using GetOrder
|
||||||
// to inspect its state.
|
// to inspect its state.
|
||||||
type OrderError struct {
|
type OrderError struct {
|
||||||
OrderURL string
|
OrderURL string
|
||||||
Status string
|
Status string
|
||||||
|
// Problem is the error that occurred while processing the order.
|
||||||
|
Problem *Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (oe *OrderError) Error() string {
|
func (oe *OrderError) Error() string {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func ScalarBaseMult(dst, scalar *[32]byte) {
|
|||||||
curve := ecdh.X25519()
|
curve := ecdh.X25519()
|
||||||
priv, err := curve.NewPrivateKey(scalar[:])
|
priv, err := curve.NewPrivateKey(scalar[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("curve25519: internal error: scalarBaseMult was not 32 bytes")
|
panic("curve25519: " + err.Error())
|
||||||
}
|
}
|
||||||
copy(dst[:], priv.PublicKey().Bytes())
|
copy(dst[:], priv.PublicKey().Bytes())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
module golang.org/x/crypto
|
module golang.org/x/crypto
|
||||||
|
|
||||||
go 1.23.0
|
go 1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/net v0.41.0 // tagx:ignore
|
golang.org/x/net v0.43.0 // tagx:ignore
|
||||||
golang.org/x/sys v0.34.0
|
golang.org/x/sys v0.36.0
|
||||||
golang.org/x/term v0.33.0
|
golang.org/x/term v0.35.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require golang.org/x/text v0.27.0 // indirect
|
require golang.org/x/text v0.29.0 // indirect
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ var (
|
|||||||
// supportedKexAlgos specifies key-exchange algorithms implemented by this
|
// supportedKexAlgos specifies key-exchange algorithms implemented by this
|
||||||
// package in preference order, excluding those with security issues.
|
// package in preference order, excluding those with security issues.
|
||||||
supportedKexAlgos = []string{
|
supportedKexAlgos = []string{
|
||||||
|
KeyExchangeMLKEM768X25519,
|
||||||
KeyExchangeCurve25519,
|
KeyExchangeCurve25519,
|
||||||
KeyExchangeECDHP256,
|
KeyExchangeECDHP256,
|
||||||
KeyExchangeECDHP384,
|
KeyExchangeECDHP384,
|
||||||
@@ -94,6 +95,7 @@ var (
|
|||||||
// defaultKexAlgos specifies the default preference for key-exchange
|
// defaultKexAlgos specifies the default preference for key-exchange
|
||||||
// algorithms in preference order.
|
// algorithms in preference order.
|
||||||
defaultKexAlgos = []string{
|
defaultKexAlgos = []string{
|
||||||
|
KeyExchangeMLKEM768X25519,
|
||||||
KeyExchangeCurve25519,
|
KeyExchangeCurve25519,
|
||||||
KeyExchangeECDHP256,
|
KeyExchangeECDHP256,
|
||||||
KeyExchangeECDHP384,
|
KeyExchangeECDHP384,
|
||||||
|
|||||||
+15
-17
@@ -9,7 +9,6 @@ import (
|
|||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/subtle"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -439,6 +438,7 @@ func init() {
|
|||||||
kexAlgoMap[keyExchangeCurve25519LibSSH] = &curve25519sha256{}
|
kexAlgoMap[keyExchangeCurve25519LibSSH] = &curve25519sha256{}
|
||||||
kexAlgoMap[InsecureKeyExchangeDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1}
|
kexAlgoMap[InsecureKeyExchangeDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1}
|
||||||
kexAlgoMap[KeyExchangeDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256}
|
kexAlgoMap[KeyExchangeDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256}
|
||||||
|
kexAlgoMap[KeyExchangeMLKEM768X25519] = &mlkem768WithCurve25519sha256{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// curve25519sha256 implements the curve25519-sha256 (formerly known as
|
// curve25519sha256 implements the curve25519-sha256 (formerly known as
|
||||||
@@ -454,15 +454,17 @@ func (kp *curve25519KeyPair) generate(rand io.Reader) error {
|
|||||||
if _, err := io.ReadFull(rand, kp.priv[:]); err != nil {
|
if _, err := io.ReadFull(rand, kp.priv[:]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
curve25519.ScalarBaseMult(&kp.pub, &kp.priv)
|
p, err := curve25519.X25519(kp.priv[:], curve25519.Basepoint)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("curve25519: %w", err)
|
||||||
|
}
|
||||||
|
if len(p) != 32 {
|
||||||
|
return fmt.Errorf("curve25519: internal error: X25519 returned %d bytes, expected 32", len(p))
|
||||||
|
}
|
||||||
|
copy(kp.pub[:], p)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// curve25519Zeros is just an array of 32 zero bytes so that we have something
|
|
||||||
// convenient to compare against in order to reject curve25519 points with the
|
|
||||||
// wrong order.
|
|
||||||
var curve25519Zeros [32]byte
|
|
||||||
|
|
||||||
func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
|
func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
|
||||||
var kp curve25519KeyPair
|
var kp curve25519KeyPair
|
||||||
if err := kp.generate(rand); err != nil {
|
if err := kp.generate(rand); err != nil {
|
||||||
@@ -485,11 +487,9 @@ func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handsh
|
|||||||
return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
|
return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
|
||||||
}
|
}
|
||||||
|
|
||||||
var servPub, secret [32]byte
|
secret, err := curve25519.X25519(kp.priv[:], reply.EphemeralPubKey)
|
||||||
copy(servPub[:], reply.EphemeralPubKey)
|
if err != nil {
|
||||||
curve25519.ScalarMult(&secret, &kp.priv, &servPub)
|
return nil, fmt.Errorf("ssh: peer's curve25519 public value is not valid: %w", err)
|
||||||
if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
|
|
||||||
return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h := crypto.SHA256.New()
|
h := crypto.SHA256.New()
|
||||||
@@ -531,11 +531,9 @@ func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handsh
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientPub, secret [32]byte
|
secret, err := curve25519.X25519(kp.priv[:], kexInit.ClientPubKey)
|
||||||
copy(clientPub[:], kexInit.ClientPubKey)
|
if err != nil {
|
||||||
curve25519.ScalarMult(&secret, &kp.priv, &clientPub)
|
return nil, fmt.Errorf("ssh: peer's curve25519 public value is not valid: %w", err)
|
||||||
if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
|
|
||||||
return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hostKeyBytes := priv.PublicKey().Marshal()
|
hostKeyBytes := priv.PublicKey().Marshal()
|
||||||
|
|||||||
@@ -421,20 +421,26 @@ func New(files ...string) (ssh.HostKeyCallback, error) {
|
|||||||
return certChecker.CheckHostKey, nil
|
return certChecker.CheckHostKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize normalizes an address into the form used in known_hosts
|
// Normalize normalizes an address into the form used in known_hosts. Supports
|
||||||
|
// IPv4, hostnames, bracketed IPv6. Any other non-standard formats are returned
|
||||||
|
// with minimal transformation.
|
||||||
func Normalize(address string) string {
|
func Normalize(address string) string {
|
||||||
|
const defaultSSHPort = "22"
|
||||||
|
|
||||||
host, port, err := net.SplitHostPort(address)
|
host, port, err := net.SplitHostPort(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
host = address
|
host = address
|
||||||
port = "22"
|
port = defaultSSHPort
|
||||||
}
|
}
|
||||||
entry := host
|
|
||||||
if port != "22" {
|
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||||
entry = "[" + entry + "]:" + port
|
host = host[1 : len(host)-1]
|
||||||
} else if strings.Contains(host, ":") && !strings.HasPrefix(host, "[") {
|
|
||||||
entry = "[" + entry + "]"
|
|
||||||
}
|
}
|
||||||
return entry
|
|
||||||
|
if port == defaultSSHPort {
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
return "[" + host + "]:" + port
|
||||||
}
|
}
|
||||||
|
|
||||||
// Line returns a line to add append to the known_hosts files.
|
// Line returns a line to add append to the known_hosts files.
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ func TestLine(t *testing.T) {
|
|||||||
"server.org": "server.org " + edKeyStr,
|
"server.org": "server.org " + edKeyStr,
|
||||||
"server.org:22": "server.org " + edKeyStr,
|
"server.org:22": "server.org " + edKeyStr,
|
||||||
"server.org:23": "[server.org]:23 " + edKeyStr,
|
"server.org:23": "[server.org]:23 " + edKeyStr,
|
||||||
"[c629:1ec4:102:304:102:304:102:304]:22": "[c629:1ec4:102:304:102:304:102:304] " + edKeyStr,
|
"[c629:1ec4:102:304:102:304:102:304]:22": "c629:1ec4:102:304:102:304:102:304 " + edKeyStr,
|
||||||
"[c629:1ec4:102:304:102:304:102:304]:23": "[c629:1ec4:102:304:102:304:102:304]:23 " + edKeyStr,
|
"[c629:1ec4:102:304:102:304:102:304]:23": "[c629:1ec4:102:304:102:304:102:304]:23 " + edKeyStr,
|
||||||
} {
|
} {
|
||||||
if got := Line([]string{in}, edKey); got != want {
|
if got := Line([]string{in}, edKey); got != want {
|
||||||
@@ -310,14 +310,25 @@ func testHostHash(t *testing.T, hostname, encoded string) {
|
|||||||
|
|
||||||
func TestNormalize(t *testing.T) {
|
func TestNormalize(t *testing.T) {
|
||||||
for in, want := range map[string]string{
|
for in, want := range map[string]string{
|
||||||
"127.0.0.1:22": "127.0.0.1",
|
"127.0.0.1": "127.0.0.1",
|
||||||
"[127.0.0.1]:22": "127.0.0.1",
|
"127.0.0.1:22": "127.0.0.1",
|
||||||
"[127.0.0.1]:23": "[127.0.0.1]:23",
|
"[127.0.0.1]:22": "127.0.0.1",
|
||||||
"127.0.0.1:23": "[127.0.0.1]:23",
|
"[127.0.0.1]:23": "[127.0.0.1]:23",
|
||||||
"[a.b.c]:22": "a.b.c",
|
"127.0.0.1:23": "[127.0.0.1]:23",
|
||||||
"[abcd:abcd:abcd:abcd]": "[abcd:abcd:abcd:abcd]",
|
"[a.b.c]:22": "a.b.c",
|
||||||
"[abcd:abcd:abcd:abcd]:22": "[abcd:abcd:abcd:abcd]",
|
"[a.b.c]:23": "[a.b.c]:23",
|
||||||
"[abcd:abcd:abcd:abcd]:23": "[abcd:abcd:abcd:abcd]:23",
|
"abcd::abcd:abcd:abcd": "abcd::abcd:abcd:abcd",
|
||||||
|
"[abcd::abcd:abcd:abcd]": "abcd::abcd:abcd:abcd",
|
||||||
|
"[abcd::abcd:abcd:abcd]:22": "abcd::abcd:abcd:abcd",
|
||||||
|
"[abcd::abcd:abcd:abcd]:23": "[abcd::abcd:abcd:abcd]:23",
|
||||||
|
"2001:db8::1": "2001:db8::1",
|
||||||
|
"2001:db8::1:22": "2001:db8::1:22",
|
||||||
|
"[2001:db8::1]:22": "2001:db8::1",
|
||||||
|
"2001:db8::1:2200": "2001:db8::1:2200",
|
||||||
|
"a.b.c.d.com:2200": "[a.b.c.d.com]:2200",
|
||||||
|
"2001::db8:1": "2001::db8:1",
|
||||||
|
"2001::db8:1:22": "2001::db8:1:22",
|
||||||
|
"2001::db8:1:2200": "2001::db8:1:2200",
|
||||||
} {
|
} {
|
||||||
got := Normalize(in)
|
got := Normalize(in)
|
||||||
if got != want {
|
if got != want {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
// 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 go1.24
|
|
||||||
|
|
||||||
package ssh
|
package ssh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -13,23 +11,10 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"runtime"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/curve25519"
|
"golang.org/x/crypto/curve25519"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
// After Go 1.24rc1 mlkem swapped the order of return values of Encapsulate.
|
|
||||||
// See #70950.
|
|
||||||
if runtime.Version() == "go1.24rc1" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
supportedKexAlgos = slices.Insert(supportedKexAlgos, 0, KeyExchangeMLKEM768X25519)
|
|
||||||
defaultKexAlgos = slices.Insert(defaultKexAlgos, 0, KeyExchangeMLKEM768X25519)
|
|
||||||
kexAlgoMap[KeyExchangeMLKEM768X25519] = &mlkem768WithCurve25519sha256{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mlkem768WithCurve25519sha256 implements the hybrid ML-KEM768 with
|
// mlkem768WithCurve25519sha256 implements the hybrid ML-KEM768 with
|
||||||
// curve25519-sha256 key exchange method, as described by
|
// curve25519-sha256 key exchange method, as described by
|
||||||
// draft-kampanakis-curdle-ssh-pq-ke-05 section 2.3.3.
|
// draft-kampanakis-curdle-ssh-pq-ke-05 section 2.3.3.
|
||||||
|
|||||||
Binary file not shown.
+753
-4060
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
|||||||
|
// 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 fallback
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBundle(t *testing.T) {
|
||||||
|
for i, unparsed := range unparsedCertificates {
|
||||||
|
cert, err := x509.ParseCertificate(rawCerts[unparsed.certStartOff : unparsed.certStartOff+unparsed.certLength])
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("ParseCertificate(unparsedCertificates[%v]) unexpected error: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if unparsed.cn != cert.Subject.String() {
|
||||||
|
t.Errorf("unparsedCertificates[%v].cn = %q; want = %q", i, unparsed.cn, cert.Subject.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
sum := sha256.Sum256(cert.Raw)
|
||||||
|
sumHex := hex.EncodeToString(sum[:])
|
||||||
|
if sumHex != unparsed.sha256Hash {
|
||||||
|
t.Errorf("unparsedCertificates[%v].sha256Hash = %q; want = %q", i, unparsed.sha256Hash, sumHex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,11 +20,14 @@ package fallback
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
_ "embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed bundle.der
|
||||||
|
var rawCerts []byte
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
x509.SetFallbackRoots(newFallbackCertPool())
|
x509.SetFallbackRoots(newFallbackCertPool())
|
||||||
}
|
}
|
||||||
@@ -49,9 +52,10 @@ func newFallbackCertPool() *x509.CertPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type unparsedCertificate struct {
|
type unparsedCertificate struct {
|
||||||
cn string
|
cn string
|
||||||
sha256Hash string
|
sha256Hash string
|
||||||
pem string
|
certStartOff int
|
||||||
|
certLength int
|
||||||
|
|
||||||
// possible constraints
|
// possible constraints
|
||||||
distrustAfter string
|
distrustAfter string
|
||||||
@@ -63,19 +67,9 @@ type parsedCertificate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mustParse(unparsedCerts []unparsedCertificate) []parsedCertificate {
|
func mustParse(unparsedCerts []unparsedCertificate) []parsedCertificate {
|
||||||
var b []parsedCertificate
|
b := make([]parsedCertificate, 0, len(unparsedCerts))
|
||||||
for _, unparsed := range unparsedCerts {
|
for _, unparsed := range unparsedCerts {
|
||||||
block, rest := pem.Decode([]byte(unparsed.pem))
|
cert, err := x509.ParseCertificate(rawCerts[unparsed.certStartOff : unparsed.certStartOff+unparsed.certLength])
|
||||||
if block == nil {
|
|
||||||
panic(fmt.Sprintf("unexpected nil PEM block for %q", unparsed.cn))
|
|
||||||
}
|
|
||||||
if len(rest) != 0 {
|
|
||||||
panic(fmt.Sprintf("unexpected trailing data in PEM for %q", unparsed.cn))
|
|
||||||
}
|
|
||||||
if block.Type != "CERTIFICATE" {
|
|
||||||
panic(fmt.Sprintf("unexpected PEM block type for %q: %s", unparsed.cn, block.Type))
|
|
||||||
}
|
|
||||||
cert, err := x509.ParseCertificate(block.Bytes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
module golang.org/x/crypto/x509roots/fallback
|
module golang.org/x/crypto/x509roots/fallback
|
||||||
|
|
||||||
go 1.23.0
|
go 1.24.0
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/pem"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/format"
|
"go/format"
|
||||||
@@ -37,6 +36,7 @@ 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)")
|
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)")
|
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")
|
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)")
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -92,8 +92,9 @@ func main() {
|
|||||||
return subjI < subjJ
|
return subjI < subjJ
|
||||||
})
|
})
|
||||||
|
|
||||||
b := new(bytes.Buffer)
|
rawCertsData := new(bytes.Buffer)
|
||||||
b.WriteString(tmpl)
|
goSrcOut := new(bytes.Buffer)
|
||||||
|
goSrcOut.WriteString(tmpl)
|
||||||
for _, c := range certs {
|
for _, c := range certs {
|
||||||
var constraints []string
|
var constraints []string
|
||||||
var skip bool
|
var skip bool
|
||||||
@@ -110,19 +111,24 @@ func main() {
|
|||||||
if skip {
|
if skip {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Fprintf(b, "{\ncn: %q,\nsha256Hash: \"%x\",\npem: `%s`,\n",
|
|
||||||
|
off := rawCertsData.Len()
|
||||||
|
rawCertsData.Write(c.X509.Raw)
|
||||||
|
|
||||||
|
fmt.Fprintf(goSrcOut, "{\ncn: %q,\nsha256Hash: \"%x\",\ncertStartOff: %v,\ncertLength: %v,\n",
|
||||||
c.X509.Subject.String(),
|
c.X509.Subject.String(),
|
||||||
sha256.Sum256(c.X509.Raw),
|
sha256.Sum256(c.X509.Raw),
|
||||||
string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: c.X509.Raw})),
|
off,
|
||||||
|
len(c.X509.Raw),
|
||||||
)
|
)
|
||||||
for _, constraint := range constraints {
|
for _, constraint := range constraints {
|
||||||
fmt.Fprintln(b, constraint)
|
fmt.Fprintln(goSrcOut, constraint)
|
||||||
}
|
}
|
||||||
fmt.Fprintln(b, "},")
|
fmt.Fprintln(goSrcOut, "},")
|
||||||
}
|
}
|
||||||
fmt.Fprintln(b, "}")
|
fmt.Fprintln(goSrcOut, "}")
|
||||||
|
|
||||||
formatted, err := format.Source(b.Bytes())
|
formatted, err := format.Source(goSrcOut.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to format source: %s", err)
|
log.Fatalf("failed to format source: %s", err)
|
||||||
}
|
}
|
||||||
@@ -130,4 +136,8 @@ func main() {
|
|||||||
if err := os.WriteFile(*output, formatted, 0644); err != nil {
|
if err := os.WriteFile(*output, formatted, 0644); err != nil {
|
||||||
log.Fatalf("failed to write to %q: %s", *output, err)
|
log.Fatalf("failed to write to %q: %s", *output, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(*derOutput, rawCertsData.Bytes(), 0644); err != nil {
|
||||||
|
log.Fatalf("failed to write to %q: %s", *output, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user