Compare commits

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

Change-Id: I93de641462a54b0ae565bb60e2a0e6e7c2c3b883
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/693999
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: David Chase <drchase@google.com>
2025-08-07 10:21:04 -07:00
Daniel McCarney b999374650 acme: fix pebble subprocess output data race
Wait for process completion before reading stdout/stderr buffers
to eliminate race between I/O Go routines and test cleanup.

Updates golang/go#74437

Cq-Include-Trybots: luci.golang.try:x_crypto-gotip-linux-amd64-longtest-race
Change-Id: I2e650c04db5be0d7a1e858ce40e25f13ad12223c
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/693596
Auto-Submit: Daniel McCarney <daniel@binaryparadox.net>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-08-06 13:33:03 -07:00
Mateusz Poliwczak c247dead11 x509roots/fallback: store bundle certs directly in DER
goos: linux
goarch: amd64
pkg: golang.org/x/crypto/x509roots/fallback
cpu: AMD Ryzen 5 4600G with Radeon Graphics
            │ /tmp/before │             /tmp/after              │
            │   sec/op    │   sec/op     vs base                │
InitTime-12   1.726m ± 0%   1.101m ± 1%  -36.20% (p=0.000 n=30)

            │  /tmp/before  │              /tmp/after              │
            │     B/op      │     B/op      vs base                │
InitTime-12   1178.2Ki ± 0%   779.8Ki ± 0%  -33.81% (p=0.000 n=30)

            │ /tmp/before │             /tmp/after             │
            │  allocs/op  │  allocs/op   vs base               │
InitTime-12   11.35k ± 0%   10.64k ± 0%  -6.32% (p=0.000 n=30)

Updates golang/go#73691

Change-Id: Ic33f2fdfc65001c41afeb3b6af8a383288d10de6
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/676217
Reviewed-by: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Mark Freeman <mark@golang.org>
2025-08-03 12:47:17 -07:00
Daniel McCarney 1fda73153f acme: increase pebble test waitForServer attempts
In CI it seems that occasionally we can't connect to the test servers
within 10 tries, and the test flakes. Let's give the process more
attempts.

Updates golang/go#74437

Change-Id: I74d6cea83468a3a572ec4b52ff7314c778c664cf
Cq-Include-Trybots: luci.golang.try:x_crypto-gotip-linux-amd64-longtest
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/692075
Auto-Submit: Daniel McCarney <daniel@binaryparadox.net>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Mark Freeman <mark@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-08-01 08:44:22 -07:00
Gopher Robot 1b4c3d2e8c x509roots/fallback: update bundle
This is an automated CL which updates the NSS root bundle.

[git-generate]
go generate ./x509roots

Change-Id: Ib30b702d41dedacce835628a9dab456098be0703
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/687895
Reviewed-by: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Gopher Robot <gobot@golang.org>
2025-07-30 16:29:44 -07:00
Daniel McCarney b903b535d3 acme: capture pebble test subprocess stdout/stderr
When spawning the pebble and pebble-challtestserv processes redirect
stdout/stderr to bytes.Buffer instances and print their content at test
end as appropriate.

The stdout/stderr content for each process is printed if the test
failed, or if testing is being done in verbose mode. Otherwise the
output is swallowed.

This makes debugging test failures much easier as output from the
subprocesses from independent tests isn't intermingled.

Updates golang/go#74437

Cq-Include-Trybots: luci.golang.try:x_crypto-gotip-linux-amd64-longtest
Change-Id: Ia79a3609ce3522ef6248442de247554c39367162
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/686935
Auto-Submit: Daniel McCarney <daniel@binaryparadox.net>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Carlos Amedee <carlos@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-07-11 12:27:10 -07:00
Gopher Robot 459a9db11b go.mod: update golang.org/x dependencies
Update golang.org/x dependencies to their latest tagged versions.

Change-Id: I3a89b1890ad2f7d2b2c23e1efce60c19e43dd381
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/687017
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Gopher Robot <gobot@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2025-07-10 10:21:43 -07:00
Nicola Murino 74e709ad8a ssh: add AlgorithmNegotiationError
Fixes golang/go#61536

Change-Id: Id38cc6d46879dbe2bdea04dec061596387ec6cfe
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/559056
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
2025-07-09 08:43:26 -07:00
Daniel McCarney b3790b8d91 acme: fix TLSALPN01ChallengeCert for IP address identifiers
When creating a TLS-ALPN-01 challenge response certificate for an IP
address identifier we need to configure the template IPAddresses field,
not the DNSNames/Subject.CommonName.

Along the way we can do some small tidying:
* Updating the draft TLS-ALPN-01 reference to the finalized RFC
* Adding a reference to the IP address identifier ACME RFC
* Adding a mention of the form the challenge validation request's SNI
  will take when verifying an IP address identifier
* Tidying the private tlsChallengeCert() function to take a single
  identifier as arg since the only call-sites provide singular values
  since the removal of the TLS-SNI-[01|02] challenge helpers.

This allows enabling an IP address identifier in the Pebble integration
tests that otherwise caused a validation failure for TLS-ALPN-01
challenge types because the IP address was used as a DNS SAN.

Updates golang/go#73914

Cq-Include-Trybots: luci.golang.try:x_crypto-gotip-linux-amd64-longtest
Change-Id: Ic671e41b585f424f821db65206c7ffcc6dd386a0
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/677576
Reviewed-by: Ian Stapleton Cordasco <graffatcolmingov@gmail.com>
Auto-Submit: Daniel McCarney <daniel@binaryparadox.net>
Reviewed-by: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
2025-06-30 12:50:50 -07:00
Daniel McCarney 1dc4269656 acme: add Pebble integration testing
This commit adds integration test coverage for a complete TLS-ALPN-01
and HTTP-01 based issuance flow.

For each tested challenge type we:

* Spin up a pebble/pebble-challtestsrv environment
* Spin up a small challenge response server
* Create an ACME account
* Create an order for multiple DNS type identifiers
* Provision challenge responses based on the challenge type under test
* Wait for the order to become ready for issuance
* Finalize the order, issuing a certificate
* Check the newly issued certificate chain validates with the Pebble
  trust anchor, and that the certificate is valid for each of the names
  from our initial order

These tests are skipped in short mode (Pebble has variable delays for
validation requests).

The Pebble source is fetched through the Go module proxy (unless
a local directory is specified to aid development), similar to how the
stdlib crypto packages fetch BoGo tooling.

More test coverage for various other parts of the protocol (key
rollover, account/authz deactivation, revocation, etc) can be added as
follow-up work now that the groundwork for integration testing is laid.

Fixes golang/go#73914

Cq-Include-Trybots: luci.golang.try:x_crypto-gotip-linux-amd64-longtest
Change-Id: I4e79f4858f31ef290a0c91d345e15fbdc510e9ab
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/677575
Reviewed-by: Roland Shoemaker <roland@golang.org>
Auto-Submit: Daniel McCarney <daniel@binaryparadox.net>
Reviewed-by: Ian Stapleton Cordasco <graffatcolmingov@gmail.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-06-30 09:51:57 -07:00
qiulaidongfeng 97bf787255 blake2b: implement hash.XOF
Fixes golang/go#69518

Change-Id: Id9989ac9b28262df77017e97f985f67c1571c3ce
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/644255
Reviewed-by: Austin Clements <austin@google.com>
Auto-Submit: Austin Clements <austin@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-06-30 09:39:26 -07:00
Gopher Robot 952517d181 x509roots/fallback: update bundle
This is an automated CL which updates the NSS root bundle.

[git-generate]
go generate ./x509roots

Change-Id: Icb71f9f7c509dc6f49ad4385aa287bd6a8966523
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/681915
Auto-Submit: Gopher Robot <gobot@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2025-06-30 09:08:05 -07:00
Nicola Murino c6fce02826 ssh: refuse to parse certificates that use a certificate as signing key
According to draft-miller-ssh-cert-01, Section 2.1.1, certificates with
certificate keys as signature keys are invalid

Change-Id: I474524ea444deb78f2fa7c2682e47c0fd057f0b8
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/678716
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
2025-06-30 09:02:50 -07:00
Nicola Murino 0ae49b8145 ssh: reject certificate keys used as signature keys for SSH certs
As specified in draft-miller-ssh-cert-01, Section 2.1.1:
Implementations MUST NOT accept certificate keys as CA keys.

Change-Id: I2e559a8a58b7bceccd0d8c6b80803abdbe281067
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/678715
Reviewed-by: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: David Chase <drchase@google.com>
2025-06-30 09:02:45 -07:00
Nicola Murino 3bf9d2afd4 ssh/test: skip KEX test if unsupported by system SSH client
Skip the key exchange test when using the system's ssh CLI if the
required KEX algorithm (e.g., mlkem768x25519-sha256) is not supported.
This is determined by running ssh -Q kex and checking for the presence
of the target algorithm.
Prevents false test failures in CI environments with older or limited
SSH implementations.

Cq-Include-Trybots: luci.golang.try:x_crypto-gotip-darwin-amd64-longtest,x_crypto-gotip-linux-amd64-longtest,x_crypto-gotip-windows-amd64-longtest
Change-Id: I3fac703ec70559e18b30d5fff88274335a7c3952
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/679195
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
2025-06-05 10:42:09 -07:00
Gopher Robot 9bab96736c go.mod: update golang.org/x dependencies
Update golang.org/x dependencies to their latest tagged versions.

Change-Id: Icbe0bf922207aefa5ee047a35dfb71843ec75306
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/679135
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: David Chase <drchase@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Gopher Robot <gobot@golang.org>
2025-06-05 08:51:54 -07:00
Mateusz Poliwczak 4f9f0ca9fc x509roots/fallback: add init time benchmark
goos: linux
goarch: amd64
pkg: golang.org/x/crypto/x509roots/fallback
cpu: AMD Ryzen 5 4600G with Radeon Graphics
            │ /tmp/before │
            │   sec/op    │
InitTime-12   1.726m ± 0%

            │ /tmp/before  │
            │     B/op     │
InitTime-12   1.151Mi ± 0%

            │ /tmp/before │
            │  allocs/op  │
InitTime-12   11.35k ± 0%

For golang/go#73691

Change-Id: Ic932bd7835e50dd5c6adbdf684644afa49bddebc
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/676216
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Sean Liao <sean@liao.dev>
Auto-Submit: Sean Liao <sean@liao.dev>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2025-05-31 02:59:11 -07:00
Mateusz Poliwczak eac7cf0d78 x509roots/fallback: move parsing code to a non-generated file
For golang/go#73691

Change-Id: I3e2b09055c39286d863fe70ca3bd72a839e25d0a
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/676215
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Sean Liao <sean@liao.dev>
Auto-Submit: Sean Liao <sean@liao.dev>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-31 02:59:06 -07:00
Daniel McCarney 18228cd6f1 acme: return err from deprecated TLS-SNI-[01|02] functions
The TLSSNI01ChallengeCert and TLSSNI02ChallengeCert functions have been
marked deprecated since 2022. The package documentation indicates
pre-RFC 8555 functionality is retained for compilation success, but will
return errors. This commit makes these two deprecated functions match
that description.

No meaningful support for these draft standard challenge types exists
in the ACME ecosystem, and they are insecure for use in shared hosting
environments.

Change-Id: I1c17980a0630092c70eb971b3453a0f115834be0
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/676835
Auto-Submit: Daniel McCarney <daniel@binaryparadox.net>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2025-05-29 10:16:04 -07:00
Daniel McCarney 73f6362471 acme: remove dead code
The private encodePem() and unique() functions under the acme package
had no call-sites and so can be removed.

Change-Id: Ic617392e7cc9b9e795456626a623ba02df108694
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/676875
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Auto-Submit: Daniel McCarney <daniel@binaryparadox.net>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2025-05-28 10:23:52 -07:00
Nicola Murino ebc8e46315 ssh: add server side support for Diffie Hellman Group Exchange
We add this support for the following reasons:

- We are planning to expose recommended (secure) vs. supported (works,
  not necessarily recommended) algorithms. The DHGEX kex is currently
  only exposed as a client-side kex. To simplify the calling convention
  for this follow-on, we expose the server side too.
- Some clients are quite inflexible with reference to kex algorithms
  choice, for example they offer:

  diffie-hellman-group-exchange-sha256, diffie-hellman-group-exchange-sha1,
  diffie-hellman-group14-sha1, diffie-hellman-group1-sha1

  therefore DHGEX helps interoperability.

We do not recommend the DHGEX kex as a whole:

- the negotiation requires an extra round trip
- the server must generate parameters (slow) or hardcode them, which
  defeats the security benefit over traditional DH.

In this implementation we hardcode sending Oakley Group 14, Oakley
Group 15 or Oakley Group 16 based on the requested max size.

Users that are concerned with security of classical DH kex should
migrate to kex based on EC or Ed25519.

Fixes golang/go#54743

Change-Id: I127822e90efc36821af4aca679931f40a2023021
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/532415
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Han-Wen Nienhuys <hanwen@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-15 10:47:05 -07:00
Nicola Murino e944286e33 ssh: expose negotiated algorithms
Fixes golang/go#58523
Fixes golang/go#46638

Change-Id: Ic64bd2fdd6e9ec96acac3ed4be842e2fbb15231d
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/538235
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-15 10:44:04 -07:00
Nicola Murino 78a1fd7893 ssh: automatically add curve25519-sha256@libssh.org KEX alias
If the key exchange method curve25519-sha256 is configured, automatically
add the curve25519-sha256@libssh.org alias to ensure compatibility with
OpenSSH versions up to 7.2, which recognize only the older vendor-specific
name.

Change-Id: If50ab2c49179db949ba1b986f7bb0e153cc7f897
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/669716
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2025-05-15 10:43:39 -07:00
Nicola Murino ac58737d25 ssh: export supported algorithms
Fixes golang/go#61537

Change-Id: If3478121e3ae445391e3faeceeb889d75e9e3214
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/531935
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
2025-05-15 10:43:30 -07:00
redwrasse 9dbbcf002b bcrypt: update InvalidCostError message to describe cost range inclusive
Change InvalidCostError string to explicitly state cost bounds
range as inclusive, rather than using parentheses that might
imply an exclusive range.

Change-Id: Ie5e7d53ef4217c8560e6a7fe23e3d65ecc24a2a3
GitHub-Last-Rev: b49002b6b2cff0939858553a28e3f255bde397b6
GitHub-Pull-Request: golang/crypto#300
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/609455
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2025-05-12 11:46:18 -07:00
Gopher Robot 9f6bf8449a x509roots/fallback: update bundle
This is an automated CL which updates the NSS root bundle.

[git-generate]
go generate ./x509roots

Change-Id: If1970af8da68ead595dc3fa7dd79a8555a5f09c5
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/668576
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Gopher Robot <gobot@golang.org>
2025-05-12 08:41:11 -07:00
49 changed files with 3209 additions and 5471 deletions
+36 -54
View File
@@ -31,12 +31,11 @@ import (
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64"
"encoding/hex"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"math/big"
"net"
"net/http"
"strings"
"sync"
@@ -471,7 +470,7 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
// while waiting for a final authorization status.
d := retryAfter(res.Header.Get("Retry-After"))
if d == 0 {
// Given that the fastest challenges TLS-SNI and HTTP-01
// Given that the fastest challenges TLS-ALPN and HTTP-01
// require a CA to make at least 1 network round trip
// and most likely persist a challenge state,
// this default delay seems reasonable.
@@ -572,50 +571,28 @@ func (c *Client) HTTP01ChallengePath(token string) string {
}
// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
// Always returns an error.
//
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of the ACME spec.
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
ka, err := keyAuth(c.Key.Public(), token)
if err != nil {
return tls.Certificate{}, "", err
}
b := sha256.Sum256([]byte(ka))
h := hex.EncodeToString(b[:])
name = fmt.Sprintf("%s.%s.acme.invalid", h[:32], h[32:])
cert, err = tlsChallengeCert([]string{name}, opt)
if err != nil {
return tls.Certificate{}, "", err
}
return cert, name, nil
// Deprecated: This challenge type was only present in pre-standardized ACME
// protocol drafts and is insecure for use in shared hosting environments.
func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (tls.Certificate, string, error) {
return tls.Certificate{}, "", errPreRFC
}
// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
// Always returns an error.
//
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of the ACME spec.
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tls.Certificate, name string, err error) {
b := sha256.Sum256([]byte(token))
h := hex.EncodeToString(b[:])
sanA := fmt.Sprintf("%s.%s.token.acme.invalid", h[:32], h[32:])
ka, err := keyAuth(c.Key.Public(), token)
if err != nil {
return tls.Certificate{}, "", err
}
b = sha256.Sum256([]byte(ka))
h = hex.EncodeToString(b[:])
sanB := fmt.Sprintf("%s.%s.ka.acme.invalid", h[:32], h[32:])
cert, err = tlsChallengeCert([]string{sanA, sanB}, opt)
if err != nil {
return tls.Certificate{}, "", err
}
return cert, sanA, nil
// Deprecated: This challenge type was only present in pre-standardized ACME
// protocol drafts and is insecure for use in shared hosting environments.
func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (tls.Certificate, string, error) {
return tls.Certificate{}, "", errPreRFC
}
// TLSALPN01ChallengeCert creates a certificate for TLS-ALPN-01 challenge response.
// Servers can present the certificate to validate the challenge and prove control
// over a domain name. For more details on TLS-ALPN-01 see
// https://tools.ietf.org/html/draft-shoemaker-acme-tls-alpn-00#section-3
// over an identifier (either a DNS name or the textual form of an IPv4 or IPv6
// address). For more details on TLS-ALPN-01 see
// https://www.rfc-editor.org/rfc/rfc8737 and https://www.rfc-editor.org/rfc/rfc8738
//
// The token argument is a Challenge.Token value.
// If a WithKey option is provided, its private part signs the returned cert,
@@ -623,9 +600,13 @@ func (c *Client) TLSSNI02ChallengeCert(token string, opt ...CertOption) (cert tl
// If no WithKey option is provided, a new ECDSA key is generated using P-256 curve.
//
// The returned certificate is valid for the next 24 hours and must be presented only when
// the server name in the TLS ClientHello matches the domain, and the special acme-tls/1 ALPN protocol
// the server name in the TLS ClientHello matches the identifier, and the special acme-tls/1 ALPN protocol
// has been specified.
func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption) (cert tls.Certificate, err error) {
//
// Validation requests for IP address identifiers will use the reverse DNS form in the server name
// in the TLS ClientHello since the SNI extension is not supported for IP addresses.
// See RFC 8738 Section 6 for more information.
func (c *Client) TLSALPN01ChallengeCert(token, identifier string, opt ...CertOption) (cert tls.Certificate, err error) {
ka, err := keyAuth(c.Key.Public(), token)
if err != nil {
return tls.Certificate{}, err
@@ -655,7 +636,7 @@ func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption)
}
tmpl.ExtraExtensions = append(tmpl.ExtraExtensions, acmeExtension)
newOpt = append(newOpt, WithTemplate(tmpl))
return tlsChallengeCert([]string{domain}, newOpt)
return tlsChallengeCert(identifier, newOpt)
}
// popNonce returns a nonce value previously stored with c.addNonce
@@ -773,11 +754,15 @@ func defaultTLSChallengeCertTemplate() *x509.Certificate {
}
}
// tlsChallengeCert creates a temporary certificate for TLS-SNI challenges
// with the given SANs and auto-generated public/private key pair.
// The Subject Common Name is set to the first SAN to aid debugging.
// tlsChallengeCert creates a temporary certificate for TLS-ALPN challenges
// for the given identifier, using an auto-generated public/private key pair.
//
// If the provided identifier is a domain name, it will be used as a DNS type SAN and for the
// subject common name. If the provided identifier is an IP address it will be used as an IP type
// SAN.
//
// To create a cert with a custom key pair, specify WithKey option.
func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
func tlsChallengeCert(identifier string, opt []CertOption) (tls.Certificate, error) {
var key crypto.Signer
tmpl := defaultTLSChallengeCertTemplate()
for _, o := range opt {
@@ -801,9 +786,12 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
return tls.Certificate{}, err
}
}
tmpl.DNSNames = san
if len(san) > 0 {
tmpl.Subject.CommonName = san[0]
if ip := net.ParseIP(identifier); ip != nil {
tmpl.IPAddresses = []net.IP{ip}
} else {
tmpl.DNSNames = []string{identifier}
tmpl.Subject.CommonName = identifier
}
der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
@@ -816,11 +804,5 @@ func tlsChallengeCert(san []string, opt []CertOption) (tls.Certificate, error) {
}, nil
}
// encodePEM returns b encoded as PEM with block of type typ.
func encodePEM(typ string, b []byte) []byte {
pb := &pem.Block{Type: typ, Bytes: b}
return pem.EncodeToMemory(pb)
}
// timeNow is time.Now, except in tests which can mess with it.
var timeNow = time.Now
+34 -110
View File
@@ -9,7 +9,6 @@ import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
@@ -22,8 +21,6 @@ import (
"net/http"
"net/http/httptest"
"reflect"
"sort"
"strings"
"testing"
"time"
)
@@ -692,71 +689,6 @@ func TestLinkHeader(t *testing.T) {
}
}
func TestTLSSNI01ChallengeCert(t *testing.T) {
const (
token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
// echo -n <token.testKeyECThumbprint> | shasum -a 256
san = "dbbd5eefe7b4d06eb9d1d9f5acb4c7cd.a27d320e4b30332f0b6cb441734ad7b0.acme.invalid"
)
tlscert, name, err := newTestClient().TLSSNI01ChallengeCert(token)
if err != nil {
t.Fatal(err)
}
if n := len(tlscert.Certificate); n != 1 {
t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
}
cert, err := x509.ParseCertificate(tlscert.Certificate[0])
if err != nil {
t.Fatal(err)
}
if len(cert.DNSNames) != 1 || cert.DNSNames[0] != san {
t.Fatalf("cert.DNSNames = %v; want %q", cert.DNSNames, san)
}
if cert.DNSNames[0] != name {
t.Errorf("cert.DNSNames[0] != name: %q vs %q", cert.DNSNames[0], name)
}
if cn := cert.Subject.CommonName; cn != san {
t.Errorf("cert.Subject.CommonName = %q; want %q", cn, san)
}
}
func TestTLSSNI02ChallengeCert(t *testing.T) {
const (
token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
// echo -n evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA | shasum -a 256
sanA = "7ea0aaa69214e71e02cebb18bb867736.09b730209baabf60e43d4999979ff139.token.acme.invalid"
// echo -n <token.testKeyECThumbprint> | shasum -a 256
sanB = "dbbd5eefe7b4d06eb9d1d9f5acb4c7cd.a27d320e4b30332f0b6cb441734ad7b0.ka.acme.invalid"
)
tlscert, name, err := newTestClient().TLSSNI02ChallengeCert(token)
if err != nil {
t.Fatal(err)
}
if n := len(tlscert.Certificate); n != 1 {
t.Fatalf("len(tlscert.Certificate) = %d; want 1", n)
}
cert, err := x509.ParseCertificate(tlscert.Certificate[0])
if err != nil {
t.Fatal(err)
}
names := []string{sanA, sanB}
if !reflect.DeepEqual(cert.DNSNames, names) {
t.Fatalf("cert.DNSNames = %v;\nwant %v", cert.DNSNames, names)
}
sort.Strings(cert.DNSNames)
i := sort.SearchStrings(cert.DNSNames, name)
if i >= len(cert.DNSNames) || cert.DNSNames[i] != name {
t.Errorf("%v doesn't have %q", cert.DNSNames, name)
}
if cn := cert.Subject.CommonName; cn != sanA {
t.Errorf("CommonName = %q; want %q", cn, sanA)
}
}
func TestTLSALPN01ChallengeCert(t *testing.T) {
const (
token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
@@ -813,6 +745,7 @@ func TestTLSChallengeCertOpt(t *testing.T) {
if err != nil {
t.Fatal(err)
}
domain := "example.com"
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(2),
Subject: pkix.Name{Organization: []string{"Test"}},
@@ -821,52 +754,43 @@ func TestTLSChallengeCertOpt(t *testing.T) {
opts := []CertOption{WithKey(key), WithTemplate(tmpl)}
client := newTestClient()
cert1, _, err := client.TLSSNI01ChallengeCert("token", opts...)
if err != nil {
t.Fatal(err)
}
cert2, _, err := client.TLSSNI02ChallengeCert("token", opts...)
cert, err := client.TLSALPN01ChallengeCert("token", domain, opts...)
if err != nil {
t.Fatal(err)
}
for i, tlscert := range []tls.Certificate{cert1, cert2} {
// verify generated cert private key
tlskey, ok := tlscert.PrivateKey.(*rsa.PrivateKey)
if !ok {
t.Errorf("%d: tlscert.PrivateKey is %T; want *rsa.PrivateKey", i, tlscert.PrivateKey)
continue
}
if tlskey.D.Cmp(key.D) != 0 {
t.Errorf("%d: tlskey.D = %v; want %v", i, tlskey.D, key.D)
}
// verify generated cert public key
x509Cert, err := x509.ParseCertificate(tlscert.Certificate[0])
if err != nil {
t.Errorf("%d: %v", i, err)
continue
}
tlspub, ok := x509Cert.PublicKey.(*rsa.PublicKey)
if !ok {
t.Errorf("%d: x509Cert.PublicKey is %T; want *rsa.PublicKey", i, x509Cert.PublicKey)
continue
}
if tlspub.N.Cmp(key.N) != 0 {
t.Errorf("%d: tlspub.N = %v; want %v", i, tlspub.N, key.N)
}
// verify template option
sn := big.NewInt(2)
if x509Cert.SerialNumber.Cmp(sn) != 0 {
t.Errorf("%d: SerialNumber = %v; want %v", i, x509Cert.SerialNumber, sn)
}
org := []string{"Test"}
if !reflect.DeepEqual(x509Cert.Subject.Organization, org) {
t.Errorf("%d: Subject.Organization = %+v; want %+v", i, x509Cert.Subject.Organization, org)
}
for _, v := range x509Cert.DNSNames {
if !strings.HasSuffix(v, ".acme.invalid") {
t.Errorf("%d: invalid DNSNames element: %q", i, v)
}
// verify generated cert private key
tlskey, ok := cert.PrivateKey.(*rsa.PrivateKey)
if !ok {
t.Fatalf("tlscert.PrivateKey is %T; want *rsa.PrivateKey", cert.PrivateKey)
}
if tlskey.D.Cmp(key.D) != 0 {
t.Errorf("tlskey.D = %v; want %v", tlskey.D, key.D)
}
// verify generated cert public key
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
if err != nil {
t.Fatal(err)
}
tlspub, ok := x509Cert.PublicKey.(*rsa.PublicKey)
if !ok {
t.Fatalf("x509Cert.PublicKey is %T; want *rsa.PublicKey", x509Cert.PublicKey)
}
if tlspub.N.Cmp(key.N) != 0 {
t.Errorf("tlspub.N = %v; want %v", tlspub.N, key.N)
}
// verify template option
sn := big.NewInt(2)
if x509Cert.SerialNumber.Cmp(sn) != 0 {
t.Errorf("SerialNumber = %v; want %v", x509Cert.SerialNumber, sn)
}
org := []string{"Test"}
if !reflect.DeepEqual(x509Cert.Subject.Organization, org) {
t.Errorf("Subject.Organization = %+v; want %+v", x509Cert.Subject.Organization, org)
}
for _, v := range x509Cert.DNSNames {
if v != domain {
t.Errorf("invalid DNSNames element: %q", v)
}
}
}
-12
View File
@@ -782,15 +782,3 @@ func decodePayload(v interface{}, r io.Reader) error {
func challengeToken(domain, challType string, authzID int) string {
return fmt.Sprintf("token-%s-%s-%d", domain, challType, authzID)
}
func unique(a []string) []string {
seen := make(map[string]bool)
var res []string
for _, s := range a {
if s != "" && !seen[s] {
seen[s] = true
res = append(res, s)
}
}
return res
}
+804
View File
@@ -0,0 +1,804 @@
// 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 acme_test
import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"flag"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
"golang.org/x/crypto/acme"
)
const (
// pebbleModVersion is the module version used for Pebble and Pebble's
// challenge test server. It is ignored if `-pebble-local-dir` is provided.
pebbleModVersion = "v2.7.0"
// startingPort is the first port number used for binding interface
// addresses. Each call to takeNextPort() will increment a port number
// starting at this value.
startingPort = 5555
)
var (
pebbleLocalDir = flag.String(
"pebble-local-dir",
"",
"Local Pebble to use, instead of fetching from source",
)
nextPort atomic.Uint32
)
func init() {
nextPort.Store(startingPort)
}
func TestWithPebble(t *testing.T) {
// We want to use process groups w/ syscall.Kill, and the acme package
// is very platform-agnostic, so skip on non-Linux.
if runtime.GOOS != "linux" {
t.Skip("skipping pebble tests on non-linux OS")
}
if testing.Short() {
t.Skip("skipping pebble tests in short mode")
}
tests := []struct {
name string
challSrv func(*environment) (challengeServer, string)
}{
{
name: "TLSALPN01-Issuance",
challSrv: func(env *environment) (challengeServer, string) {
bindAddr := fmt.Sprintf(":%d", env.config.TLSPort)
return newChallTLSServer(bindAddr), bindAddr
},
},
{
name: "HTTP01-Issuance",
challSrv: func(env *environment) (challengeServer, string) {
bindAddr := fmt.Sprintf(":%d", env.config.HTTPPort)
return newChallHTTPServer(bindAddr), bindAddr
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
env := startPebbleEnvironment(t, nil)
challSrv, challSrvAddr := tt.challSrv(&env)
challSrv.Run()
t.Cleanup(func() {
challSrv.Shutdown()
})
waitForServer(t, challSrvAddr)
testIssuance(t, &env, challSrv)
})
}
}
// challengeServer abstracts over the details of running a challenge response
// server for some supported acme.Challenge type. Responses are provisioned
// during the test issuance process to be presented to the ACME server's
// validation authority.
type challengeServer interface {
Run()
Shutdown() error
Supported(chal *acme.Challenge) bool
Provision(client *acme.Client, ident acme.AuthzID, chal *acme.Challenge) error
}
// challTLSServer is a simple challenge response server that listens for TLS
// connections on a specific port and if they are TLS-ALPN-01 challenge
// requests, completes the handshake using the configured challenge response
// certificate for the SNI value provided.
type challTLSServer struct {
*http.Server
// mu protects challCerts.
mu sync.RWMutex
// challCerts is a map from SNI domain name to challenge response certificate.
challCerts map[string]*tls.Certificate
}
// https://datatracker.ietf.org/doc/html/rfc8737#section-4
const acmeTLSAlpnProtocol = "acme-tls/1"
func newChallTLSServer(address string) *challTLSServer {
challServer := &challTLSServer{Server: &http.Server{
Addr: address,
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
}, challCerts: make(map[string]*tls.Certificate)}
// Configure the server to support the TLS-ALPN-01 challenge protocol
// and to use a callback for selecting the handshake certificate.
challServer.Server.TLSConfig = &tls.Config{
NextProtos: []string{acmeTLSAlpnProtocol},
GetCertificate: challServer.getCertificate,
}
return challServer
}
func (c *challTLSServer) Shutdown() error {
log.Printf("challTLSServer: shutting down")
ctx, cancel := context.WithTimeout(context.Background(), 10)
defer cancel()
return c.Server.Shutdown(ctx)
}
func (c *challTLSServer) Run() {
go func() {
// Note: certFile and keyFile are empty because our config uses a
// GetCertificate callback.
if err := c.Server.ListenAndServeTLS("", ""); err != nil {
if !errors.Is(err, http.ErrServerClosed) {
log.Printf("challTLSServer error: %v", err)
}
}
}()
}
func (c *challTLSServer) Supported(chal *acme.Challenge) bool {
return chal.Type == "tls-alpn-01"
}
func (c *challTLSServer) Provision(client *acme.Client, ident acme.AuthzID, chal *acme.Challenge) error {
respCert, err := client.TLSALPN01ChallengeCert(chal.Token, ident.Value)
if err != nil {
return fmt.Errorf("challTLSServer: failed to generate challlenge response cert for %s: %w",
ident.Value, err)
}
log.Printf("challTLSServer: setting challenge response certificate for %s", ident.Value)
c.mu.Lock()
defer c.mu.Unlock()
c.challCerts[ident.Value] = &respCert
return nil
}
func (c *challTLSServer) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
// Verify the request looks like a TLS-ALPN-01 challenge request.
if len(clientHello.SupportedProtos) != 1 || clientHello.SupportedProtos[0] != acmeTLSAlpnProtocol {
return nil, fmt.Errorf(
"challTLSServer: non-TLS-ALPN-01 challenge request received with SupportedProtos: %s",
clientHello.SupportedProtos)
}
serverName := clientHello.ServerName
// TLS-ALPN-01 challenge requests for IP addresses are encoded in the SNI
// using the reverse-DNS notation. See RFC 8738 Section 6:
// https://www.rfc-editor.org/rfc/rfc8738.html#section-6
if strings.HasSuffix(serverName, ".in-addr.arpa") {
serverName = strings.TrimSuffix(serverName, ".in-addr.arpa")
parts := strings.Split(serverName, ".")
for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 {
parts[i], parts[j] = parts[j], parts[i]
}
serverName = strings.Join(parts, ".")
}
log.Printf("challTLSServer: selecting certificate for request from %s for %s",
clientHello.Conn.RemoteAddr(), serverName)
c.mu.RLock()
defer c.mu.RUnlock()
cert := c.challCerts[serverName]
if cert == nil {
return nil, fmt.Errorf("challTLSServer: no challenge response certificate configured for %s", serverName)
}
return cert, nil
}
// challHTTPServer is a simple challenge response server that listens for HTTP
// connections on a specific port and if they are HTTP-01 challenge requests,
// serves the challenge response key authorization.
type challHTTPServer struct {
*http.Server
// mu protects challMap
mu sync.RWMutex
// challMap is a mapping from request path to response body.
challMap map[string]string
}
func newChallHTTPServer(address string) *challHTTPServer {
challServer := &challHTTPServer{
Server: &http.Server{
Addr: address,
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Second,
},
challMap: make(map[string]string),
}
challServer.Server.Handler = challServer
return challServer
}
func (c *challHTTPServer) Supported(chal *acme.Challenge) bool {
return chal.Type == "http-01"
}
func (c *challHTTPServer) Provision(client *acme.Client, ident acme.AuthzID, chall *acme.Challenge) error {
path := client.HTTP01ChallengePath(chall.Token)
body, err := client.HTTP01ChallengeResponse(chall.Token)
if err != nil {
return fmt.Errorf("failed to generate HTTP-01 challenge response for %v challenge %s token %s: %w",
ident, chall.URI, chall.Token, err)
}
c.mu.Lock()
defer c.mu.Unlock()
log.Printf("challHTTPServer: setting challenge response for %s", path)
c.challMap[path] = body
return nil
}
func (c *challHTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf("challHTTPServer: handling %s to %s from %s", r.Method, r.URL.Path, r.RemoteAddr)
if r.Method != http.MethodGet {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
c.mu.RLock()
defer c.mu.RUnlock()
response, exists := c.challMap[r.URL.Path]
if !exists {
http.NotFound(w, r)
return
}
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte(response))
}
func (c *challHTTPServer) Shutdown() error {
log.Printf("challHTTPServer: shutting down")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
return c.Server.Shutdown(ctx)
}
func (c *challHTTPServer) Run() {
go func() {
if err := c.Server.ListenAndServe(); err != nil {
if !errors.Is(err, http.ErrServerClosed) {
log.Printf("challHTTPServer error: %v", err)
}
}
}()
}
func testIssuance(t *testing.T, env *environment, challSrv challengeServer) {
t.Helper()
// Bound the total issuance process by a timeout of 60 seconds.
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// Create a new ACME account.
client := env.client
acct, err := client.Register(ctx, &acme.Account{}, acme.AcceptTOS)
if err != nil {
t.Fatalf("failed to register account: %v", err)
}
if acct.Status != acme.StatusValid {
t.Fatalf("expected new account status to be valid, got %v", acct.Status)
}
log.Printf("registered account: %s", acct.URI)
// Create a new order for some example identifiers
identifiers := []acme.AuthzID{
{
Type: "dns",
Value: "example.com",
},
{
Type: "dns",
Value: "www.example.com",
},
{
Type: "ip",
Value: "127.0.0.1",
},
}
order, err := client.AuthorizeOrder(ctx, identifiers)
if err != nil {
t.Fatalf("failed to create order for %v: %v", identifiers, err)
}
if order.Status != acme.StatusPending {
t.Fatalf("expected new order status to be pending, got %v", order.Status)
}
orderURL := order.URI
log.Printf("created order: %v", orderURL)
// For each pending authz provision a supported challenge type's response
// with the test challenge server, and tell the ACME server to verify it.
for _, authzURL := range order.AuthzURLs {
authz, err := client.GetAuthorization(ctx, authzURL)
if err != nil {
t.Fatalf("failed to get order %s authorization %s: %v",
orderURL, authzURL, err)
}
if authz.Status != acme.StatusPending {
continue
}
for _, challenge := range authz.Challenges {
if challenge.Status != acme.StatusPending || !challSrv.Supported(challenge) {
continue
}
if err := challSrv.Provision(client, authz.Identifier, challenge); err != nil {
t.Fatalf("failed to provision challenge %s: %v", challenge.URI, err)
}
_, err = client.Accept(ctx, challenge)
if err != nil {
t.Fatalf("failed to accept order %s challenge %s: %v",
orderURL, challenge.URI, err)
}
}
}
// Wait for the order to become ready for finalization.
order, err = client.WaitOrder(ctx, order.URI)
if err != nil {
t.Fatalf("failed to wait for order %s: %s", orderURL, err)
}
if order.Status != acme.StatusReady {
t.Fatalf("expected order %s status to be ready, got %v",
orderURL, order.Status)
}
// Generate a certificate keypair and a CSR for the order identifiers.
certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("failed to generate certificate key: %v", err)
}
var dnsNames []string
var ipAddresses []net.IP
for _, ident := range identifiers {
switch ident.Type {
case "dns":
dnsNames = append(dnsNames, ident.Value)
case "ip":
ipAddresses = append(ipAddresses, net.ParseIP(ident.Value))
default:
t.Fatalf("unsupported identifier type: %s", ident.Type)
}
}
csrDer, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{
DNSNames: dnsNames,
IPAddresses: ipAddresses,
}, certKey)
if err != nil {
t.Fatalf("failed to create CSR: %v", err)
}
// Finalize the order by creating a certificate with our CSR.
chain, _, err := client.CreateOrderCert(ctx, order.FinalizeURL, csrDer, true)
if err != nil {
t.Fatalf("failed to finalize order %s with finalize URL %s: %v",
orderURL, order.FinalizeURL, err)
}
// Split the chain into the leaf and any intermediates.
leaf := chain[0]
intermediatesDER := chain[1:]
leafCert, err := x509.ParseCertificate(leaf)
if err != nil {
t.Fatalf("failed to parse order %s leaf certificate: %v", orderURL, err)
}
intermediates := x509.NewCertPool()
for i, intermediateDER := range intermediatesDER {
intermediate, err := x509.ParseCertificate(intermediateDER)
if err != nil {
t.Fatalf("failed to parse intermediate %d: %v", i, err)
}
intermediates.AddCert(intermediate)
}
// Verify there is a valid path from the leaf certificate to Pebble's
// issuing root using the provided intermediate certificates.
roots, err := env.RootCert()
if err != nil {
t.Fatalf("failed to get Pebble issuer root certs: %v", err)
}
paths, err := leafCert.Verify(x509.VerifyOptions{
Intermediates: intermediates,
Roots: roots,
})
if err != nil {
t.Fatalf("failed to verify order %s leaf certificate: %v", orderURL, err)
}
log.Printf("verified %d path(s) from issued leaf certificate to Pebble root CA", len(paths))
// Also verify that the leaf cert is valid for each of the DNS names
// and IP addresses from our order's identifiers.
for _, name := range dnsNames {
if err := leafCert.VerifyHostname(name); err != nil {
t.Fatalf("failed to verify order %s leaf certificate for order DNS name %s: %v",
orderURL, name, err)
}
}
for _, ip := range ipAddresses {
if err := leafCert.VerifyHostname(ip.String()); err != nil {
t.Fatalf("failed to verify order %s leaf certificate for order IP address %s: %v",
orderURL, ip, err)
}
}
}
type environment struct {
config *environmentConfig
client *acme.Client
}
// RootCert returns the Pebble CA's primary issuing hierarchy root certificate.
// This is generated randomly at each startup and can be used to verify
// certificate chains issued by Pebble's ACME interface. Note that this
// is separate from the static root certificate used by the Pebble ACME
// HTTPS interface.
func (e *environment) RootCert() (*x509.CertPool, error) {
// NOTE: in the future we may want to consider the alternative chains
// returned as Link alternative headers.
rootURL := fmt.Sprintf("https://%s/roots/0", e.config.pebbleConfig.ManagementListenAddress)
resp, err := e.client.HTTPClient.Get(rootURL)
if err != nil || resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to GET Pebble root CA from %s: %v", rootURL, err)
}
roots := x509.NewCertPool()
rootPEM, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to parse Pebble root CA PEM: %v", err)
}
rootDERBlock, _ := pem.Decode(rootPEM)
rootCA, err := x509.ParseCertificate(rootDERBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse Pebble root CA DER: %v", err)
}
roots.AddCert(rootCA)
return roots, nil
}
// environmentConfig describes the Pebble configuration, and configuration
// shared between pebble and pebble-challtestsrv.
type environmentConfig struct {
pebbleConfig
dnsPort uint32
}
// defaultConfig returns an environmentConfig populated with default values.
// The provided pebbleDir is used to specify certificate/private key paths
// for the HTTPS ACME interface.
func defaultConfig(pebbleDir string) environmentConfig {
return environmentConfig{
pebbleConfig: pebbleConfig{
ListenAddress: fmt.Sprintf("127.0.0.1:%d", takeNextPort()),
ManagementListenAddress: fmt.Sprintf("127.0.0.1:%d", takeNextPort()),
HTTPPort: takeNextPort(),
TLSPort: takeNextPort(),
Certificate: fmt.Sprintf("%s/test/certs/localhost/cert.pem", pebbleDir),
PrivateKey: fmt.Sprintf("%s/test/certs/localhost/key.pem", pebbleDir),
OCSPResponderURL: "",
ExternalAccountBindingRequired: false,
ExternalAccountMACKeys: make(map[string]string),
DomainBlocklist: []string{"blocked-domain.example"},
Profiles: map[string]struct {
Description string
ValidityPeriod uint64
}{
"default": {
Description: "default profile",
ValidityPeriod: 3600,
},
},
RetryAfter: struct {
Authz int
Order int
}{
3,
5,
},
},
dnsPort: takeNextPort(),
}
}
// pebbleConfig matches the JSON structure of the Pebble configuration file.
type pebbleConfig struct {
ListenAddress string
ManagementListenAddress string
HTTPPort uint32
TLSPort uint32
Certificate string
PrivateKey string
OCSPResponderURL string
ExternalAccountBindingRequired bool
ExternalAccountMACKeys map[string]string
DomainBlocklist []string
Profiles map[string]struct {
Description string
ValidityPeriod uint64
}
RetryAfter struct {
Authz int
Order int
}
}
func takeNextPort() uint32 {
return nextPort.Add(1) - 1
}
// startPebbleEnvironment is a test helper that spawns Pebble and Pebble
// challenge test server processes based on the provided environmentConfig. The
// processes will be torn down when the test ends.
func startPebbleEnvironment(t *testing.T, config *environmentConfig) environment {
t.Helper()
var pebbleDir string
if *pebbleLocalDir != "" {
pebbleDir = *pebbleLocalDir
} else {
pebbleDir = fetchModule(t, "github.com/letsencrypt/pebble/v2", pebbleModVersion)
}
binDir := prepareBinaries(t, pebbleDir)
if config == nil {
cfg := defaultConfig(pebbleDir)
config = &cfg
}
marshalConfig := struct {
Pebble pebbleConfig
}{
Pebble: config.pebbleConfig,
}
configData, err := json.Marshal(marshalConfig)
if err != nil {
t.Fatalf("failed to marshal config: %v", err)
}
configFile, err := os.CreateTemp("", "pebble-config-*.json")
if err != nil {
t.Fatalf("failed to create temp config file: %v", err)
}
t.Cleanup(func() { os.Remove(configFile.Name()) })
if _, err := configFile.Write(configData); err != nil {
t.Fatalf("failed to write config file: %v", err)
}
configFile.Close()
log.Printf("pebble dir: %s", pebbleDir)
log.Printf("config file: %s", configFile.Name())
// Spawn the Pebble CA server. It answers ACME requests and performs
// outbound validations. We configure it to use a mock DNS server that
// always answers 127.0.0.1 for all A queries so that validation
// requests for any domain name will resolve to our local challenge
// server instances.
spawnServerProcess(t, binDir, "pebble", "-config", configFile.Name(),
"-dnsserver", fmt.Sprintf("127.0.0.1:%d", config.dnsPort),
"-strict")
// Spawn the Pebble challenge test server. We'll use it to mock DNS
// responses but disable all the other interfaces. We want to stand
// up our own challenge response servers for TLS-ALPN-01,
// etc.
// Note: we specify -defaultIPv6 "" so that no AAAA records are served.
// The LUCI CI runners have issues with IPv6 connectivity on localhost.
spawnServerProcess(t, binDir, "pebble-challtestsrv",
"-dns01", fmt.Sprintf(":%d", config.dnsPort),
"-defaultIPv6", "",
"-management", fmt.Sprintf(":%d", takeNextPort()),
"-doh", "",
"-http01", "",
"-tlsalpn01", "",
"-https01", "")
waitForServer(t, config.pebbleConfig.ListenAddress)
waitForServer(t, fmt.Sprintf("127.0.0.1:%d", config.dnsPort))
log.Printf("pebble environment ready")
// Construct a cert pool that contains the CA certificate used by the ACME
// interface's certificate chain. This is separate from the issuing
// hierarchy and is used for the ACME client to interact with the ACME
// interface without cert verification error.
caCertPath := filepath.Join(pebbleDir, "test/certs/pebble.minica.pem")
caCert, err := os.ReadFile(caCertPath)
if err != nil {
t.Fatalf("failed to read CA certificate %s: %v", caCertPath, err)
}
caCertPool := x509.NewCertPool()
if !caCertPool.AppendCertsFromPEM(caCert) {
t.Fatalf("failed to parse CA certificate %s", caCertPath)
}
httpClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
},
},
}
// Create an ACME account keypair/client and verify it can discover
// the Pebble server's ACME directory without error.
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("failed to generate account key: %v", err)
}
client := &acme.Client{
Key: key,
HTTPClient: httpClient,
DirectoryURL: fmt.Sprintf("https://%s/dir", config.ListenAddress),
}
_, err = client.Discover(context.TODO())
if err != nil {
t.Fatalf("failed to discover ACME directory: %v", err)
}
return environment{
config: config,
client: client,
}
}
func waitForServer(t *testing.T, addr string) {
t.Helper()
for i := 0; i < 20; i++ {
if conn, err := net.Dial("tcp", addr); err == nil {
conn.Close()
return
}
time.Sleep(time.Duration(i*100) * time.Millisecond)
}
t.Fatalf("failed to connect to %q after 20 tries", addr)
}
// fetchModule fetches the module at the given version and returns the directory
// containing its source tree. It skips the test if fetching modules is not
// possible in this environment.
//
// Copied from the stdlib cryptotest.FetchModule and adapted to not rely on the
// stdlib internal testenv package.
func fetchModule(t *testing.T, module, version string) string {
// If the default GOMODCACHE doesn't exist, use a temporary directory
// instead. (For example, run.bash sets GOPATH=/nonexist-gopath.)
out, err := exec.Command("go", "env", "GOMODCACHE").Output()
if err != nil {
t.Errorf("go env GOMODCACHE: %v\n%s", err, out)
if ee, ok := err.(*exec.ExitError); ok {
t.Logf("%s", ee.Stderr)
}
t.FailNow()
}
modcacheOk := false
if gomodcache := string(bytes.TrimSpace(out)); gomodcache != "" {
if _, err := os.Stat(gomodcache); err == nil {
modcacheOk = true
}
}
if !modcacheOk {
t.Setenv("GOMODCACHE", t.TempDir())
// Allow t.TempDir() to clean up subdirectories.
t.Setenv("GOFLAGS", os.Getenv("GOFLAGS")+" -modcacherw")
}
t.Logf("fetching %s@%s\n", module, version)
output, err := exec.Command("go", "mod", "download", "-json", module+"@"+version).CombinedOutput()
if err != nil {
t.Fatalf("failed to download %s@%s: %s\n%s\n", module, version, err, output)
}
var j struct {
Dir string
}
if err := json.Unmarshal(output, &j); err != nil {
t.Fatalf("failed to parse 'go mod download': %s\n%s\n", err, output)
}
return j.Dir
}
func prepareBinaries(t *testing.T, pebbleDir string) string {
t.Helper()
// We don't want to build in the module cache dir, which might not be
// writable or to pollute the user's clone with binaries if pebbleLocalDir
//is used.
binDir := t.TempDir()
build := func(cmd string) {
log.Printf("building %s", cmd)
buildCmd := exec.Command(
"go",
"build", "-o", filepath.Join(binDir, cmd), "-mod", "mod", "./cmd/"+cmd)
buildCmd.Dir = pebbleDir
output, err := buildCmd.CombinedOutput()
if err != nil {
t.Fatalf("failed to build %s: %s\n%s\n", cmd, err, output)
}
}
build("pebble")
build("pebble-challtestsrv")
return binDir
}
func spawnServerProcess(t *testing.T, dir string, cmd string, args ...string) {
t.Helper()
var stdout, stderr bytes.Buffer
cmdInstance := exec.Command("./"+cmd, args...)
cmdInstance.Dir = dir
cmdInstance.Stdout = &stdout
cmdInstance.Stderr = &stderr
if err := cmdInstance.Start(); err != nil {
t.Fatalf("failed to start %s: %v", cmd, err)
}
t.Cleanup(func() {
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()))
}
}
})
}
+1 -1
View File
@@ -619,7 +619,7 @@ func (*certOptKey) privateCertOpt() {}
//
// In TLS ChallengeCert methods, the template is also used as parent,
// resulting in a self-signed certificate.
// The DNSNames field of t is always overwritten for tls-sni challenge certs.
// The DNSNames or IPAddresses fields of t are always overwritten for tls-alpn challenge certs.
func WithTemplate(t *x509.Certificate) CertOption {
return (*certOptTemplate)(t)
}
+1 -1
View File
@@ -50,7 +50,7 @@ func (ih InvalidHashPrefixError) Error() string {
type InvalidCostError int
func (ic InvalidCostError) Error() string {
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), MinCost, MaxCost)
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed inclusive range %d..%d", int(ic), MinCost, MaxCost)
}
const (
+8
View File
@@ -12,6 +12,8 @@ import (
// XOF defines the interface to hash functions that
// support arbitrary-length output.
//
// New callers should prefer the standard library [hash.XOF].
type XOF interface {
// Write absorbs more data into the hash's state. It panics if called
// after Read.
@@ -47,6 +49,8 @@ const maxOutputLength = (1 << 32) * 64
//
// A non-nil key turns the hash into a MAC. The key must between
// zero and 32 bytes long.
//
// The result can be safely interface-upgraded to [hash.XOF].
func NewXOF(size uint32, key []byte) (XOF, error) {
if len(key) > Size {
return nil, errKeySize
@@ -93,6 +97,10 @@ func (x *xof) Clone() XOF {
return &clone
}
func (x *xof) BlockSize() int {
return x.d.BlockSize()
}
func (x *xof) Reset() {
x.cfg[0] = byte(Size)
binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length
+11
View File
@@ -0,0 +1,11 @@
// 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.
//go:build go1.25
package blake2b
import "hash"
var _ hash.XOF = (*xof)(nil)
+4 -4
View File
@@ -3,9 +3,9 @@ module golang.org/x/crypto
go 1.23.0
require (
golang.org/x/net v0.21.0 // tagx:ignore
golang.org/x/sys v0.33.0
golang.org/x/term v0.32.0
golang.org/x/net v0.42.0 // tagx:ignore
golang.org/x/sys v0.35.0
golang.org/x/term v0.34.0
)
require golang.org/x/text v0.25.0 // indirect
require golang.org/x/text v0.28.0 // indirect
+8 -8
View File
@@ -1,8 +1,8 @@
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
+11 -11
View File
@@ -555,7 +555,7 @@ func (c *client) insertKey(s interface{}, comment string, constraints []byte) er
})
case *dsa.PrivateKey:
req = ssh.Marshal(dsaKeyMsg{
Type: ssh.KeyAlgoDSA,
Type: ssh.InsecureKeyAlgoDSA,
P: k.P,
Q: k.Q,
G: k.G,
@@ -803,16 +803,16 @@ var _ ssh.AlgorithmSigner = &agentKeyringSigner{}
//
// This map must be kept in sync with the one in certs.go.
var certKeyAlgoNames = map[string]string{
ssh.CertAlgoRSAv01: ssh.KeyAlgoRSA,
ssh.CertAlgoRSASHA256v01: ssh.KeyAlgoRSASHA256,
ssh.CertAlgoRSASHA512v01: ssh.KeyAlgoRSASHA512,
ssh.CertAlgoDSAv01: ssh.KeyAlgoDSA,
ssh.CertAlgoECDSA256v01: ssh.KeyAlgoECDSA256,
ssh.CertAlgoECDSA384v01: ssh.KeyAlgoECDSA384,
ssh.CertAlgoECDSA521v01: ssh.KeyAlgoECDSA521,
ssh.CertAlgoSKECDSA256v01: ssh.KeyAlgoSKECDSA256,
ssh.CertAlgoED25519v01: ssh.KeyAlgoED25519,
ssh.CertAlgoSKED25519v01: ssh.KeyAlgoSKED25519,
ssh.CertAlgoRSAv01: ssh.KeyAlgoRSA,
ssh.CertAlgoRSASHA256v01: ssh.KeyAlgoRSASHA256,
ssh.CertAlgoRSASHA512v01: ssh.KeyAlgoRSASHA512,
ssh.InsecureCertAlgoDSAv01: ssh.InsecureKeyAlgoDSA,
ssh.CertAlgoECDSA256v01: ssh.KeyAlgoECDSA256,
ssh.CertAlgoECDSA384v01: ssh.KeyAlgoECDSA384,
ssh.CertAlgoECDSA521v01: ssh.KeyAlgoECDSA521,
ssh.CertAlgoSKECDSA256v01: ssh.KeyAlgoSKECDSA256,
ssh.CertAlgoED25519v01: ssh.KeyAlgoED25519,
ssh.CertAlgoSKED25519v01: ssh.KeyAlgoSKED25519,
}
// underlyingAlgo returns the signature algorithm associated with algo (which is
+2 -2
View File
@@ -506,7 +506,7 @@ func (s *server) insertIdentity(req []byte) error {
switch record.Type {
case ssh.KeyAlgoRSA:
addedKey, err = parseRSAKey(req)
case ssh.KeyAlgoDSA:
case ssh.InsecureKeyAlgoDSA:
addedKey, err = parseDSAKey(req)
case ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521:
addedKey, err = parseECDSAKey(req)
@@ -514,7 +514,7 @@ func (s *server) insertIdentity(req []byte) error {
addedKey, err = parseEd25519Key(req)
case ssh.CertAlgoRSAv01:
addedKey, err = parseRSACert(req)
case ssh.CertAlgoDSAv01:
case ssh.InsecureCertAlgoDSAv01:
addedKey, err = parseDSACert(req)
case ssh.CertAlgoECDSA256v01, ssh.CertAlgoECDSA384v01, ssh.CertAlgoECDSA521v01:
addedKey, err = parseECDSACert(req)
+40 -27
View File
@@ -20,14 +20,19 @@ import (
// returned by MultiAlgorithmSigner and don't appear in the Signature.Format
// field.
const (
CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
CertAlgoSKECDSA256v01 = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com"
CertAlgoSKED25519v01 = "sk-ssh-ed25519-cert-v01@openssh.com"
CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
// Deprecated: DSA is only supported at insecure key sizes, and was removed
// from major implementations.
CertAlgoDSAv01 = InsecureCertAlgoDSAv01
// Deprecated: DSA is only supported at insecure key sizes, and was removed
// from major implementations.
InsecureCertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
CertAlgoSKECDSA256v01 = "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com"
CertAlgoSKED25519v01 = "sk-ssh-ed25519-cert-v01@openssh.com"
// CertAlgoRSASHA256v01 and CertAlgoRSASHA512v01 can't appear as a
// Certificate.Type (or PublicKey.Type), but only in
@@ -228,7 +233,11 @@ func parseCert(in []byte, privAlgo string) (*Certificate, error) {
if err != nil {
return nil, err
}
// The Type() function is intended to return only certificate key types, but
// we use certKeyAlgoNames anyway for safety, to match [Certificate.Type].
if _, ok := certKeyAlgoNames[k.Type()]; ok {
return nil, fmt.Errorf("ssh: the signature key type %q is invalid for certificates", k.Type())
}
c.SignatureKey = k
c.Signature, rest, ok = parseSignatureBody(g.Signature)
if !ok || len(rest) > 0 {
@@ -296,16 +305,13 @@ type CertChecker struct {
SupportedCriticalOptions []string
// IsUserAuthority should return true if the key is recognized as an
// authority for the given user certificate. This allows for
// certificates to be signed by other certificates. This must be set
// if this CertChecker will be checking user certificates.
// authority for user certificate. This must be set if this CertChecker
// will be checking user certificates.
IsUserAuthority func(auth PublicKey) bool
// IsHostAuthority should report whether the key is recognized as
// an authority for this host. This allows for certificates to be
// signed by other keys, and for those other keys to only be valid
// signers for particular hostnames. This must be set if this
// CertChecker will be checking host certificates.
// an authority for this host. This must be set if this CertChecker
// will be checking host certificates.
IsHostAuthority func(auth PublicKey, address string) bool
// Clock is used for verifying time stamps. If nil, time.Now
@@ -442,12 +448,19 @@ func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
// SignCert signs the certificate with an authority, setting the Nonce,
// SignatureKey, and Signature fields. If the authority implements the
// MultiAlgorithmSigner interface the first algorithm in the list is used. This
// is useful if you want to sign with a specific algorithm.
// is useful if you want to sign with a specific algorithm. As specified in
// [SSH-CERTS], Section 2.1.1, authority can't be a [Certificate].
func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
c.Nonce = make([]byte, 32)
if _, err := io.ReadFull(rand, c.Nonce); err != nil {
return err
}
// The Type() function is intended to return only certificate key types, but
// we use certKeyAlgoNames anyway for safety, to match [Certificate.Type].
if _, ok := certKeyAlgoNames[authority.PublicKey().Type()]; ok {
return fmt.Errorf("ssh: certificates cannot be used as authority (public key type %q)",
authority.PublicKey().Type())
}
c.SignatureKey = authority.PublicKey()
if v, ok := authority.(MultiAlgorithmSigner); ok {
@@ -485,16 +498,16 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
//
// This map must be kept in sync with the one in agent/client.go.
var certKeyAlgoNames = map[string]string{
CertAlgoRSAv01: KeyAlgoRSA,
CertAlgoRSASHA256v01: KeyAlgoRSASHA256,
CertAlgoRSASHA512v01: KeyAlgoRSASHA512,
CertAlgoDSAv01: KeyAlgoDSA,
CertAlgoECDSA256v01: KeyAlgoECDSA256,
CertAlgoECDSA384v01: KeyAlgoECDSA384,
CertAlgoECDSA521v01: KeyAlgoECDSA521,
CertAlgoSKECDSA256v01: KeyAlgoSKECDSA256,
CertAlgoED25519v01: KeyAlgoED25519,
CertAlgoSKED25519v01: KeyAlgoSKED25519,
CertAlgoRSAv01: KeyAlgoRSA,
CertAlgoRSASHA256v01: KeyAlgoRSASHA256,
CertAlgoRSASHA512v01: KeyAlgoRSASHA512,
InsecureCertAlgoDSAv01: InsecureKeyAlgoDSA,
CertAlgoECDSA256v01: KeyAlgoECDSA256,
CertAlgoECDSA384v01: KeyAlgoECDSA384,
CertAlgoECDSA521v01: KeyAlgoECDSA521,
CertAlgoSKECDSA256v01: KeyAlgoSKECDSA256,
CertAlgoED25519v01: KeyAlgoED25519,
CertAlgoSKED25519v01: KeyAlgoSKED25519,
}
// underlyingAlgo returns the signature algorithm associated with algo (which is
+30 -1
View File
@@ -297,7 +297,7 @@ func TestCertTypes(t *testing.T) {
{"legacyRSASigner", &legacyRSASigner{testSigners["rsa"]}, KeyAlgoRSA},
{"multiAlgoRSASignerSHA256", multiAlgoSignerSHA256, KeyAlgoRSASHA256},
{"multiAlgoRSASignerSHA512", multiAlgoSignerSHA512, KeyAlgoRSASHA512},
{CertAlgoDSAv01, testSigners["dsa"], ""},
{InsecureCertAlgoDSAv01, testSigners["dsa"], ""},
}
k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
@@ -404,3 +404,32 @@ func TestCertSignWithMultiAlgorithmSigner(t *testing.T) {
})
}
}
func TestCertSignWithCertificate(t *testing.T) {
cert := &Certificate{
Key: testPublicKeys["rsa"],
ValidBefore: CertTimeInfinity,
CertType: UserCert,
}
if err := cert.SignCert(rand.Reader, testSigners["ecdsa"]); err != nil {
t.Fatalf("SignCert: %v", err)
}
signer, err := NewSignerWithAlgorithms(testSigners["rsa"].(AlgorithmSigner), []string{KeyAlgoRSASHA256})
if err != nil {
t.Fatal(err)
}
certSigner, err := NewCertSigner(cert, signer)
if err != nil {
t.Fatalf("NewCertSigner: %v", err)
}
cert1 := &Certificate{
Key: testPublicKeys["ecdsa"],
ValidBefore: CertTimeInfinity,
CertType: UserCert,
}
if err := cert1.SignCert(rand.Reader, certSigner); err == nil {
t.Fatal("successfully signed a certificate using another certificate, it is expected to fail")
}
}
+19 -21
View File
@@ -58,11 +58,11 @@ func newRC4(key, iv []byte) (cipher.Stream, error) {
type cipherMode struct {
keySize int
ivSize int
create func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error)
create func(key, iv []byte, macKey []byte, algs DirectionAlgorithms) (packetCipher, error)
}
func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream, error)) func(key, iv []byte, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
return func(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream, error)) func(key, iv []byte, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
return func(key, iv, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
stream, err := createFunc(key, iv)
if err != nil {
return nil, err
@@ -98,36 +98,36 @@ func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream,
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.
"aes128-ctr": {16, aes.BlockSize, streamCipherMode(0, newAESCTR)},
"aes192-ctr": {24, aes.BlockSize, streamCipherMode(0, newAESCTR)},
"aes256-ctr": {32, aes.BlockSize, streamCipherMode(0, newAESCTR)},
CipherAES128CTR: {16, aes.BlockSize, streamCipherMode(0, newAESCTR)},
CipherAES192CTR: {24, aes.BlockSize, streamCipherMode(0, newAESCTR)},
CipherAES256CTR: {32, aes.BlockSize, streamCipherMode(0, newAESCTR)},
// Ciphers from RFC 4345, which introduces security-improved arcfour ciphers.
// They are defined in the order specified in the RFC.
"arcfour128": {16, 0, streamCipherMode(1536, newRC4)},
"arcfour256": {32, 0, streamCipherMode(1536, newRC4)},
InsecureCipherRC4128: {16, 0, streamCipherMode(1536, newRC4)},
InsecureCipherRC4256: {32, 0, streamCipherMode(1536, newRC4)},
// 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.
"arcfour": {16, 0, streamCipherMode(0, newRC4)},
InsecureCipherRC4: {16, 0, streamCipherMode(0, newRC4)},
// AEAD ciphers
gcm128CipherID: {16, 12, newGCMCipher},
gcm256CipherID: {32, 12, newGCMCipher},
chacha20Poly1305ID: {64, 0, newChaCha20Cipher},
CipherAES128GCM: {16, 12, newGCMCipher},
CipherAES256GCM: {32, 12, newGCMCipher},
CipherChaCha20Poly1305: {64, 0, newChaCha20Cipher},
// 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.
aes128cbcID: {16, aes.BlockSize, newAESCBCCipher},
InsecureCipherAES128CBC: {16, aes.BlockSize, newAESCBCCipher},
// 3des-cbc is insecure and is not included in the default
// config.
tripledescbcID: {24, des.BlockSize, newTripleDESCBCCipher},
InsecureCipherTripleDESCBC: {24, des.BlockSize, newTripleDESCBCCipher},
}
// prefixLen is the length of the packet prefix that contains the packet length
@@ -307,7 +307,7 @@ type gcmCipher struct {
buf []byte
}
func newGCMCipher(key, iv, unusedMacKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) {
func newGCMCipher(key, iv, unusedMacKey []byte, unusedAlgs DirectionAlgorithms) (packetCipher, error) {
c, err := aes.NewCipher(key)
if err != nil {
return nil, err
@@ -429,7 +429,7 @@ type cbcCipher struct {
oracleCamouflage uint32
}
func newCBCCipher(c cipher.Block, key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
func newCBCCipher(c cipher.Block, key, iv, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
cbc := &cbcCipher{
mac: macModes[algs.MAC].new(macKey),
decrypter: cipher.NewCBCDecrypter(c, iv),
@@ -443,7 +443,7 @@ func newCBCCipher(c cipher.Block, key, iv, macKey []byte, algs directionAlgorith
return cbc, nil
}
func newAESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
func newAESCBCCipher(key, iv, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
c, err := aes.NewCipher(key)
if err != nil {
return nil, err
@@ -457,7 +457,7 @@ func newAESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCi
return cbc, nil
}
func newTripleDESCBCCipher(key, iv, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
func newTripleDESCBCCipher(key, iv, macKey []byte, algs DirectionAlgorithms) (packetCipher, error) {
c, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
@@ -635,8 +635,6 @@ func (c *cbcCipher) writeCipherPacket(seqNum uint32, w io.Writer, rand io.Reader
return nil
}
const chacha20Poly1305ID = "chacha20-poly1305@openssh.com"
// chacha20Poly1305Cipher implements the chacha20-poly1305@openssh.com
// AEAD, which is described here:
//
@@ -650,7 +648,7 @@ type chacha20Poly1305Cipher struct {
buf []byte
}
func newChaCha20Cipher(key, unusedIV, unusedMACKey []byte, unusedAlgs directionAlgorithms) (packetCipher, error) {
func newChaCha20Cipher(key, unusedIV, unusedMACKey []byte, unusedAlgs DirectionAlgorithms) (packetCipher, error) {
if len(key) != 64 {
panic(len(key))
}
+14 -14
View File
@@ -22,7 +22,7 @@ func TestDefaultCiphersExist(t *testing.T) {
t.Errorf("supported cipher %q is unknown", cipherAlgo)
}
}
for _, cipherAlgo := range preferredCiphers {
for _, cipherAlgo := range defaultCiphers {
if _, ok := cipherModes[cipherAlgo]; !ok {
t.Errorf("preferred cipher %q is unknown", cipherAlgo)
}
@@ -30,8 +30,8 @@ func TestDefaultCiphersExist(t *testing.T) {
}
func TestPacketCiphers(t *testing.T) {
defaultMac := "hmac-sha2-256"
defaultCipher := "aes128-ctr"
defaultMac := HMACSHA256
defaultCipher := CipherAES128CTR
for cipher := range cipherModes {
t.Run("cipher="+cipher,
func(t *testing.T) { testPacketCipher(t, cipher, defaultMac) })
@@ -44,10 +44,10 @@ func TestPacketCiphers(t *testing.T) {
func testPacketCipher(t *testing.T, cipher, mac string) {
kr := &kexResult{Hash: crypto.SHA1}
algs := directionAlgorithms{
algs := DirectionAlgorithms{
Cipher: cipher,
MAC: mac,
Compression: "none",
compression: compressionNone,
}
client, err := newPacketCipher(clientKeys, algs, kr)
if err != nil {
@@ -77,10 +77,10 @@ func testPacketCipher(t *testing.T, cipher, mac string) {
func TestCBCOracleCounterMeasure(t *testing.T) {
kr := &kexResult{Hash: crypto.SHA1}
algs := directionAlgorithms{
Cipher: aes128cbcID,
MAC: "hmac-sha1",
Compression: "none",
algs := DirectionAlgorithms{
Cipher: InsecureCipherAES128CBC,
MAC: HMACSHA1,
compression: compressionNone,
}
client, err := newPacketCipher(clientKeys, algs, kr)
if err != nil {
@@ -141,7 +141,7 @@ func TestCVE202143565(t *testing.T) {
constructPacket func(packetCipher) io.Reader
}{
{
cipher: gcm128CipherID,
cipher: CipherAES128GCM,
constructPacket: func(client packetCipher) io.Reader {
internalCipher := client.(*gcmCipher)
b := &bytes.Buffer{}
@@ -159,7 +159,7 @@ func TestCVE202143565(t *testing.T) {
},
},
{
cipher: chacha20Poly1305ID,
cipher: CipherChaCha20Poly1305,
constructPacket: func(client packetCipher) io.Reader {
internalCipher := client.(*chacha20Poly1305Cipher)
b := &bytes.Buffer{}
@@ -201,13 +201,13 @@ func TestCVE202143565(t *testing.T) {
}
for _, tc := range tests {
mac := "hmac-sha2-256"
mac := HMACSHA256
kr := &kexResult{Hash: crypto.SHA1}
algs := directionAlgorithms{
algs := DirectionAlgorithms{
Cipher: tc.cipher,
MAC: mac,
Compression: "none",
compression: compressionNone,
}
client, err := newPacketCipher(clientKeys, algs, kr)
if err != nil {
+1
View File
@@ -110,6 +110,7 @@ func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) e
}
c.sessionID = c.transport.getSessionID()
c.algorithms = c.transport.getAlgorithms()
return c.clientAuthenticate(config)
}
+1 -1
View File
@@ -289,7 +289,7 @@ func pickSignatureAlgorithm(signer Signer, extensions map[string][]byte) (MultiA
}
}
algo, err := findCommon("public key signature algorithm", keyAlgos, serverAlgos)
algo, err := findCommon("public key signature algorithm", keyAlgos, serverAlgos, true)
if err != nil {
// If there is no overlap, return the fallback algorithm to support
// servers that fail to list all supported algorithms.
+4 -1
View File
@@ -324,6 +324,9 @@ func TestMethodInvalidAlgorithm(t *testing.T) {
}
func TestClientHMAC(t *testing.T) {
supportedAlgos := SupportedAlgorithms()
insecureAlgos := InsecureAlgorithms()
supportedMACs := append(supportedAlgos.MACs, insecureAlgos.MACs...)
for _, mac := range supportedMACs {
config := &ClientConfig{
User: "testuser",
@@ -349,7 +352,7 @@ func TestClientUnsupportedCipher(t *testing.T) {
PublicKeys(),
},
Config: Config{
Ciphers: []string{"aes128-cbc"}, // not currently supported
Ciphers: []string{"unsupported-cipher"}, // not currently supported
},
}
if err := tryAuth(t, config); err == nil {
+5 -5
View File
@@ -292,7 +292,7 @@ func TestUnsupportedAlgorithm(t *testing.T) {
{
"unsupported and supported KEXs",
Config{
KeyExchanges: []string{"unsupported", kexAlgoCurve25519SHA256},
KeyExchanges: []string{"unsupported", KeyExchangeCurve25519},
},
"",
},
@@ -306,7 +306,7 @@ func TestUnsupportedAlgorithm(t *testing.T) {
{
"unsupported and supported ciphers",
Config{
Ciphers: []string{"unsupported", chacha20Poly1305ID},
Ciphers: []string{"unsupported", CipherChaCha20Poly1305},
},
"",
},
@@ -315,16 +315,16 @@ func TestUnsupportedAlgorithm(t *testing.T) {
Config{
MACs: []string{"unsupported"},
// MAC is used for non AAED ciphers.
Ciphers: []string{"aes256-ctr"},
Ciphers: []string{CipherAES256CTR},
},
"no common algorithm",
},
{
"unsupported and supported MACs",
Config{
MACs: []string{"unsupported", "hmac-sha2-256-etm@openssh.com"},
MACs: []string{"unsupported", HMACSHA256ETM},
// MAC is used for non AAED ciphers.
Ciphers: []string{"aes256-ctr"},
Ciphers: []string{CipherAES256CTR},
},
"",
},
+307 -107
View File
@@ -10,6 +10,7 @@ import (
"fmt"
"io"
"math"
"slices"
"sync"
_ "crypto/sha1"
@@ -24,69 +25,258 @@ const (
serviceSSH = "ssh-connection"
)
// supportedCiphers lists ciphers we support but might not recommend.
var supportedCiphers = []string{
"aes128-ctr", "aes192-ctr", "aes256-ctr",
"aes128-gcm@openssh.com", gcm256CipherID,
chacha20Poly1305ID,
"arcfour256", "arcfour128", "arcfour",
aes128cbcID,
tripledescbcID,
// The ciphers currently or previously implemented by this library, to use in
// [Config.Ciphers]. For a list, see the [Algorithms.Ciphers] returned by
// [SupportedAlgorithms] or [InsecureAlgorithms].
const (
CipherAES128GCM = "aes128-gcm@openssh.com"
CipherAES256GCM = "aes256-gcm@openssh.com"
CipherChaCha20Poly1305 = "chacha20-poly1305@openssh.com"
CipherAES128CTR = "aes128-ctr"
CipherAES192CTR = "aes192-ctr"
CipherAES256CTR = "aes256-ctr"
InsecureCipherAES128CBC = "aes128-cbc"
InsecureCipherTripleDESCBC = "3des-cbc"
InsecureCipherRC4 = "arcfour"
InsecureCipherRC4128 = "arcfour128"
InsecureCipherRC4256 = "arcfour256"
)
// The key exchanges currently or previously implemented by this library, to use
// in [Config.KeyExchanges]. For a list, see the
// [Algorithms.KeyExchanges] returned by [SupportedAlgorithms] or
// [InsecureAlgorithms].
const (
InsecureKeyExchangeDH1SHA1 = "diffie-hellman-group1-sha1"
InsecureKeyExchangeDH14SHA1 = "diffie-hellman-group14-sha1"
KeyExchangeDH14SHA256 = "diffie-hellman-group14-sha256"
KeyExchangeDH16SHA512 = "diffie-hellman-group16-sha512"
KeyExchangeECDHP256 = "ecdh-sha2-nistp256"
KeyExchangeECDHP384 = "ecdh-sha2-nistp384"
KeyExchangeECDHP521 = "ecdh-sha2-nistp521"
KeyExchangeCurve25519 = "curve25519-sha256"
InsecureKeyExchangeDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
KeyExchangeDHGEXSHA256 = "diffie-hellman-group-exchange-sha256"
// KeyExchangeMLKEM768X25519 is supported from Go 1.24.
KeyExchangeMLKEM768X25519 = "mlkem768x25519-sha256"
// An alias for KeyExchangeCurve25519SHA256. This kex ID will be added if
// KeyExchangeCurve25519SHA256 is requested for backward compatibility with
// OpenSSH versions up to 7.2.
keyExchangeCurve25519LibSSH = "curve25519-sha256@libssh.org"
)
// The message authentication code (MAC) currently or previously implemented by
// this library, to use in [Config.MACs]. For a list, see the
// [Algorithms.MACs] returned by [SupportedAlgorithms] or
// [InsecureAlgorithms].
const (
HMACSHA256ETM = "hmac-sha2-256-etm@openssh.com"
HMACSHA512ETM = "hmac-sha2-512-etm@openssh.com"
HMACSHA256 = "hmac-sha2-256"
HMACSHA512 = "hmac-sha2-512"
HMACSHA1 = "hmac-sha1"
InsecureHMACSHA196 = "hmac-sha1-96"
)
var (
// supportedKexAlgos specifies key-exchange algorithms implemented by this
// package in preference order, excluding those with security issues.
supportedKexAlgos = []string{
KeyExchangeCurve25519,
KeyExchangeECDHP256,
KeyExchangeECDHP384,
KeyExchangeECDHP521,
KeyExchangeDH14SHA256,
KeyExchangeDH16SHA512,
KeyExchangeDHGEXSHA256,
}
// defaultKexAlgos specifies the default preference for key-exchange
// algorithms in preference order.
defaultKexAlgos = []string{
KeyExchangeCurve25519,
KeyExchangeECDHP256,
KeyExchangeECDHP384,
KeyExchangeECDHP521,
KeyExchangeDH14SHA256,
InsecureKeyExchangeDH14SHA1,
}
// insecureKexAlgos specifies key-exchange algorithms implemented by this
// package and which have security issues.
insecureKexAlgos = []string{
InsecureKeyExchangeDH14SHA1,
InsecureKeyExchangeDH1SHA1,
InsecureKeyExchangeDHGEXSHA1,
}
// supportedCiphers specifies cipher algorithms implemented by this package
// in preference order, excluding those with security issues.
supportedCiphers = []string{
CipherAES128GCM,
CipherAES256GCM,
CipherChaCha20Poly1305,
CipherAES128CTR,
CipherAES192CTR,
CipherAES256CTR,
}
// defaultCiphers specifies the default preference for ciphers algorithms
// in preference order.
defaultCiphers = supportedCiphers
// insecureCiphers specifies cipher algorithms implemented by this
// package and which have security issues.
insecureCiphers = []string{
InsecureCipherAES128CBC,
InsecureCipherTripleDESCBC,
InsecureCipherRC4256,
InsecureCipherRC4128,
InsecureCipherRC4,
}
// supportedMACs specifies MAC algorithms implemented by this package in
// preference order, excluding those with security issues.
supportedMACs = []string{
HMACSHA256ETM,
HMACSHA512ETM,
HMACSHA256,
HMACSHA512,
HMACSHA1,
}
// defaultMACs specifies the default preference for MAC algorithms in
// preference order.
defaultMACs = []string{
HMACSHA256ETM,
HMACSHA512ETM,
HMACSHA256,
HMACSHA512,
HMACSHA1,
InsecureHMACSHA196,
}
// insecureMACs specifies MAC algorithms implemented by this
// package and which have security issues.
insecureMACs = []string{
InsecureHMACSHA196,
}
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e.
// methods of authenticating servers) implemented by this package in
// preference order, excluding those with security issues.
supportedHostKeyAlgos = []string{
CertAlgoRSASHA256v01,
CertAlgoRSASHA512v01,
CertAlgoECDSA256v01,
CertAlgoECDSA384v01,
CertAlgoECDSA521v01,
CertAlgoED25519v01,
KeyAlgoRSASHA256,
KeyAlgoRSASHA512,
KeyAlgoECDSA256,
KeyAlgoECDSA384,
KeyAlgoECDSA521,
KeyAlgoED25519,
}
// defaultHostKeyAlgos specifies the default preference for host-key
// algorithms in preference order.
defaultHostKeyAlgos = []string{
CertAlgoRSASHA256v01,
CertAlgoRSASHA512v01,
CertAlgoRSAv01,
InsecureCertAlgoDSAv01,
CertAlgoECDSA256v01,
CertAlgoECDSA384v01,
CertAlgoECDSA521v01,
CertAlgoED25519v01,
KeyAlgoECDSA256,
KeyAlgoECDSA384,
KeyAlgoECDSA521,
KeyAlgoRSASHA256,
KeyAlgoRSASHA512,
KeyAlgoRSA,
InsecureKeyAlgoDSA,
KeyAlgoED25519,
}
// insecureHostKeyAlgos specifies host-key algorithms implemented by this
// package and which have security issues.
insecureHostKeyAlgos = []string{
KeyAlgoRSA,
InsecureKeyAlgoDSA,
CertAlgoRSAv01,
InsecureCertAlgoDSAv01,
}
// supportedPubKeyAuthAlgos specifies the supported client public key
// authentication algorithms. Note that this doesn't include certificate
// types since those use the underlying algorithm. Order is irrelevant.
supportedPubKeyAuthAlgos = []string{
KeyAlgoED25519,
KeyAlgoSKED25519,
KeyAlgoSKECDSA256,
KeyAlgoECDSA256,
KeyAlgoECDSA384,
KeyAlgoECDSA521,
KeyAlgoRSASHA256,
KeyAlgoRSASHA512,
}
// defaultPubKeyAuthAlgos specifies the preferred client public key
// authentication algorithms. This list is sent to the client if it supports
// the server-sig-algs extension. Order is irrelevant.
defaultPubKeyAuthAlgos = []string{
KeyAlgoED25519,
KeyAlgoSKED25519,
KeyAlgoSKECDSA256,
KeyAlgoECDSA256,
KeyAlgoECDSA384,
KeyAlgoECDSA521,
KeyAlgoRSASHA256,
KeyAlgoRSASHA512,
KeyAlgoRSA,
InsecureKeyAlgoDSA,
}
// insecurePubKeyAuthAlgos specifies client public key authentication
// algorithms implemented by this package and which have security issues.
insecurePubKeyAuthAlgos = []string{
KeyAlgoRSA,
InsecureKeyAlgoDSA,
}
)
// NegotiatedAlgorithms defines algorithms negotiated between client and server.
type NegotiatedAlgorithms struct {
KeyExchange string
HostKey string
Read DirectionAlgorithms
Write DirectionAlgorithms
}
// preferredCiphers specifies the default preference for ciphers.
var preferredCiphers = []string{
"aes128-gcm@openssh.com", gcm256CipherID,
chacha20Poly1305ID,
"aes128-ctr", "aes192-ctr", "aes256-ctr",
// Algorithms defines a set of algorithms that can be configured in the client
// or server config for negotiation during a handshake.
type Algorithms struct {
KeyExchanges []string
Ciphers []string
MACs []string
HostKeys []string
PublicKeyAuths []string
}
// supportedKexAlgos specifies the supported key-exchange algorithms in
// preference order.
var supportedKexAlgos = []string{
kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
// P384 and P521 are not constant-time yet, but since we don't
// reuse ephemeral keys, using them for ECDH should be OK.
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
kexAlgoDH14SHA256, kexAlgoDH16SHA512, kexAlgoDH14SHA1,
kexAlgoDH1SHA1,
// 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.
func SupportedAlgorithms() Algorithms {
return Algorithms{
Ciphers: slices.Clone(supportedCiphers),
MACs: slices.Clone(supportedMACs),
KeyExchanges: slices.Clone(supportedKexAlgos),
HostKeys: slices.Clone(supportedHostKeyAlgos),
PublicKeyAuths: slices.Clone(supportedPubKeyAuthAlgos),
}
}
// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
// for the server half.
var serverForbiddenKexAlgos = map[string]struct{}{
kexAlgoDHGEXSHA1: {}, // server half implementation is only minimal to satisfy the automated tests
kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
}
// preferredKexAlgos specifies the default preference for key-exchange
// algorithms in preference order. The diffie-hellman-group16-sha512 algorithm
// is disabled by default because it is a bit slower than the others.
var preferredKexAlgos = []string{
kexAlgoCurve25519SHA256, kexAlgoCurve25519SHA256LibSSH,
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
kexAlgoDH14SHA256, kexAlgoDH14SHA1,
}
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
// of authenticating servers) in preference order.
var supportedHostKeyAlgos = []string{
CertAlgoRSASHA256v01, CertAlgoRSASHA512v01,
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
KeyAlgoRSASHA256, KeyAlgoRSASHA512,
KeyAlgoRSA, KeyAlgoDSA,
KeyAlgoED25519,
}
// supportedMACs specifies a default set of MAC algorithms in preference order.
// This is based on RFC 4253, section 6.4, but with hmac-md5 variants removed
// because they have reached the end of their useful life.
var supportedMACs = []string{
"hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", "hmac-sha1-96",
// InsecureAlgorithms returns algorithms currently implemented by this package
// and which have security issues.
func InsecureAlgorithms() Algorithms {
return Algorithms{
KeyExchanges: slices.Clone(insecureKexAlgos),
Ciphers: slices.Clone(insecureCiphers),
MACs: slices.Clone(insecureMACs),
HostKeys: slices.Clone(insecureHostKeyAlgos),
PublicKeyAuths: slices.Clone(insecurePubKeyAuthAlgos),
}
}
var supportedCompressions = []string{compressionNone}
@@ -94,13 +284,13 @@ 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,
KeyAlgoDSA: crypto.SHA1,
KeyAlgoECDSA256: crypto.SHA256,
KeyAlgoECDSA384: crypto.SHA384,
KeyAlgoECDSA521: crypto.SHA512,
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,
@@ -135,18 +325,6 @@ func isRSACert(algo string) bool {
return isRSA(algo)
}
// supportedPubKeyAuthAlgos specifies the supported client public key
// authentication algorithms. Note that this doesn't include certificate types
// since those use the underlying algorithm. This list is sent to the client if
// it supports the server-sig-algs extension. Order is irrelevant.
var supportedPubKeyAuthAlgos = []string{
KeyAlgoED25519,
KeyAlgoSKED25519, KeyAlgoSKECDSA256,
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA,
KeyAlgoDSA,
}
// unexpectedMessageError results when the SSH message that we received didn't
// match what we wanted.
func unexpectedMessageError(expected, got uint8) error {
@@ -158,7 +336,7 @@ func parseError(tag uint8) error {
return fmt.Errorf("ssh: parse error in message type %d", tag)
}
func findCommon(what string, client []string, server []string) (common string, err error) {
func findCommon(what string, client []string, server []string, isClient bool) (string, error) {
for _, c := range client {
for _, s := range server {
if c == s {
@@ -166,23 +344,49 @@ func findCommon(what string, client []string, server []string) (common string, e
}
}
}
return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server)
err := &AlgorithmNegotiationError{
What: what,
}
if isClient {
err.SupportedAlgorithms = client
err.RequestedAlgorithms = server
} else {
err.SupportedAlgorithms = server
err.RequestedAlgorithms = client
}
return "", err
}
// directionAlgorithms records algorithm choices in one direction (either read or write)
type directionAlgorithms struct {
// AlgorithmNegotiationError defines the error returned if the client and the
// server cannot agree on an algorithm for key exchange, host key, cipher, MAC.
type AlgorithmNegotiationError struct {
What string
// RequestedAlgorithms lists the algorithms supported by the peer.
RequestedAlgorithms []string
// SupportedAlgorithms lists the algorithms supported on our side.
SupportedAlgorithms []string
}
func (a *AlgorithmNegotiationError) Error() string {
return fmt.Sprintf("ssh: no common algorithm for %s; we offered: %v, peer offered: %v",
a.What, a.SupportedAlgorithms, a.RequestedAlgorithms)
}
// DirectionAlgorithms defines the algorithms negotiated in one direction
// (either read or write).
type DirectionAlgorithms struct {
Cipher string
MAC string
Compression string
compression string
}
// rekeyBytes returns a rekeying intervals in bytes.
func (a *directionAlgorithms) rekeyBytes() int64 {
func (a *DirectionAlgorithms) rekeyBytes() int64 {
// According to RFC 4344 block ciphers should rekey after
// 2^(BLOCKSIZE/4) blocks. For all AES flavors BLOCKSIZE is
// 128.
switch a.Cipher {
case "aes128-ctr", "aes192-ctr", "aes256-ctr", gcm128CipherID, gcm256CipherID, aes128cbcID:
case CipherAES128CTR, CipherAES192CTR, CipherAES256CTR, CipherAES128GCM, CipherAES256GCM, InsecureCipherAES128CBC:
return 16 * (1 << 32)
}
@@ -192,66 +396,59 @@ func (a *directionAlgorithms) rekeyBytes() int64 {
}
var aeadCiphers = map[string]bool{
gcm128CipherID: true,
gcm256CipherID: true,
chacha20Poly1305ID: true,
CipherAES128GCM: true,
CipherAES256GCM: true,
CipherChaCha20Poly1305: true,
}
type algorithms struct {
kex string
hostKey string
w directionAlgorithms
r directionAlgorithms
}
func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *NegotiatedAlgorithms, err error) {
result := &NegotiatedAlgorithms{}
func findAgreedAlgorithms(isClient bool, clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
result := &algorithms{}
result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos)
result.KeyExchange, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos, isClient)
if err != nil {
return
}
result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
result.HostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos, isClient)
if err != nil {
return
}
stoc, ctos := &result.w, &result.r
stoc, ctos := &result.Write, &result.Read
if isClient {
ctos, stoc = stoc, ctos
}
ctos.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
ctos.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer, isClient)
if err != nil {
return
}
stoc.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
stoc.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient, isClient)
if err != nil {
return
}
if !aeadCiphers[ctos.Cipher] {
ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
ctos.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer, isClient)
if err != nil {
return
}
}
if !aeadCiphers[stoc.Cipher] {
stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
stoc.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient, isClient)
if err != nil {
return
}
}
ctos.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
ctos.compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer, isClient)
if err != nil {
return
}
stoc.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
stoc.compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient, isClient)
if err != nil {
return
}
@@ -297,7 +494,7 @@ func (c *Config) SetDefaults() {
c.Rand = rand.Reader
}
if c.Ciphers == nil {
c.Ciphers = preferredCiphers
c.Ciphers = defaultCiphers
}
var ciphers []string
for _, c := range c.Ciphers {
@@ -309,19 +506,22 @@ func (c *Config) SetDefaults() {
c.Ciphers = ciphers
if c.KeyExchanges == nil {
c.KeyExchanges = preferredKexAlgos
c.KeyExchanges = defaultKexAlgos
}
var kexs []string
for _, k := range c.KeyExchanges {
if kexAlgoMap[k] != nil {
// Ignore the KEX if we have no kexAlgoMap definition.
kexs = append(kexs, k)
if k == KeyExchangeCurve25519 && !contains(c.KeyExchanges, keyExchangeCurve25519LibSSH) {
kexs = append(kexs, keyExchangeCurve25519LibSSH)
}
}
}
c.KeyExchanges = kexs
if c.MACs == nil {
c.MACs = supportedMACs
c.MACs = defaultMACs
}
var macs []string
for _, m := range c.MACs {
+17 -17
View File
@@ -51,33 +51,33 @@ func TestFindAgreedAlgorithms(t *testing.T) {
}
}
initDirAlgs := func(a *directionAlgorithms) {
initDirAlgs := func(a *DirectionAlgorithms) {
if a.Cipher == "" {
a.Cipher = "cipher1"
}
if a.MAC == "" {
a.MAC = "mac1"
}
if a.Compression == "" {
a.Compression = "compression1"
if a.compression == "" {
a.compression = "compression1"
}
}
initAlgs := func(a *algorithms) {
if a.kex == "" {
a.kex = "kex1"
initAlgs := func(a *NegotiatedAlgorithms) {
if a.KeyExchange == "" {
a.KeyExchange = "kex1"
}
if a.hostKey == "" {
a.hostKey = "hostkey1"
if a.HostKey == "" {
a.HostKey = "hostkey1"
}
initDirAlgs(&a.r)
initDirAlgs(&a.w)
initDirAlgs(&a.Read)
initDirAlgs(&a.Write)
}
type testcase struct {
name string
clientIn, serverIn kexInitMsg
wantClient, wantServer algorithms
wantClient, wantServer NegotiatedAlgorithms
wantErr bool
}
@@ -120,19 +120,19 @@ func TestFindAgreedAlgorithms(t *testing.T) {
CiphersClientServer: []string{"cipher2", "cipher1"},
CiphersServerClient: []string{"cipher3", "cipher2"},
},
wantClient: algorithms{
r: directionAlgorithms{
wantClient: NegotiatedAlgorithms{
Read: DirectionAlgorithms{
Cipher: "cipher3",
},
w: directionAlgorithms{
Write: DirectionAlgorithms{
Cipher: "cipher2",
},
},
wantServer: algorithms{
w: directionAlgorithms{
wantServer: NegotiatedAlgorithms{
Write: DirectionAlgorithms{
Cipher: "cipher3",
},
r: directionAlgorithms{
Read: DirectionAlgorithms{
Cipher: "cipher2",
},
},
+12
View File
@@ -74,6 +74,13 @@ type Conn interface {
// Disconnect
}
// AlgorithmsConnMetadata is a ConnMetadata that can return the algorithms
// negotiated between client and server.
type AlgorithmsConnMetadata interface {
ConnMetadata
Algorithms() NegotiatedAlgorithms
}
// DiscardRequests consumes and rejects all requests from the
// passed-in channel.
func DiscardRequests(in <-chan *Request) {
@@ -106,6 +113,7 @@ type sshConn struct {
sessionID []byte
clientVersion []byte
serverVersion []byte
algorithms NegotiatedAlgorithms
}
func dup(src []byte) []byte {
@@ -141,3 +149,7 @@ func (c *sshConn) ClientVersion() []byte {
func (c *sshConn) ServerVersion() []byte {
return dup(c.serverVersion)
}
func (c *sshConn) Algorithms() NegotiatedAlgorithms {
return c.algorithms
}
+1
View File
@@ -16,6 +16,7 @@ References:
[PROTOCOL]: https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL?rev=HEAD
[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
This package does not fall under the stability promise of the Go language itself,
so its API may be changed when pressing needs arise.
+110
View File
@@ -154,6 +154,75 @@ func ExampleNewServerConn() {
}
}
func ExampleServerConfig() {
// Minimal ServerConfig with SHA-1 algoritms disabled and supporting only
// public key authentication.
// The algorithms returned by ssh.SupportedAlgorithms() are different from
// the default ones and do not include algorithms that are considered
// insecure, such as those using SHA-1, returned by
// ssh.InsecureAlgorithms().
algorithms := ssh.SupportedAlgorithms()
// Public key authentication is done by comparing
// the public key of a received connection
// with the entries in the authorized_keys file.
authorizedKeysBytes, err := os.ReadFile("authorized_keys")
if err != nil {
log.Fatalf("Failed to load authorized_keys, err: %v", err)
}
authorizedKeysMap := map[string]bool{}
for len(authorizedKeysBytes) > 0 {
pubKey, _, _, rest, err := ssh.ParseAuthorizedKey(authorizedKeysBytes)
if err != nil {
log.Fatal(err)
}
authorizedKeysMap[string(pubKey.Marshal())] = true
authorizedKeysBytes = rest
}
config := &ssh.ServerConfig{
Config: ssh.Config{
KeyExchanges: algorithms.KeyExchanges,
Ciphers: algorithms.Ciphers,
MACs: algorithms.MACs,
},
// The configured PublicKeyAuthAlgorithms do not contain SHA-1 based
// signature formats, so if the client attempts to use them the
// authentication will fail before calling the defined callback.
PublicKeyAuthAlgorithms: algorithms.PublicKeyAuths,
PublicKeyCallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
if authorizedKeysMap[string(pubKey.Marshal())] {
return &ssh.Permissions{
// Record the public key used for authentication.
Extensions: map[string]string{
"pubkey-fp": ssh.FingerprintSHA256(pubKey),
},
}, nil
}
return nil, fmt.Errorf("unknown public key for %q", c.User())
},
}
privateBytes, err := os.ReadFile("id_rsa")
if err != nil {
log.Fatal("Failed to load private key: ", err)
}
private, err := ssh.ParsePrivateKey(privateBytes)
if err != nil {
log.Fatal("Failed to parse private key: ", err)
}
// Restrict host key algorithms to disable ssh-rsa.
signer, err := ssh.NewSignerWithAlgorithms(private.(ssh.AlgorithmSigner), []string{ssh.KeyAlgoRSASHA256, ssh.KeyAlgoRSASHA512})
if err != nil {
log.Fatal("Failed to create private key with restricted algorithms: ", err)
}
config.AddHostKey(signer)
}
func ExampleServerConfig_AddHostKey() {
// Minimal ServerConfig supporting only password authentication.
config := &ssh.ServerConfig{
@@ -262,6 +331,47 @@ func ExampleDial() {
fmt.Println(b.String())
}
func ExampleClientConfig() {
var hostKey ssh.PublicKey
key, err := os.ReadFile("/home/user/.ssh/id_rsa")
if err != nil {
log.Fatalf("unable to read private key: %v", err)
}
// Create the Signer for this private key.
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
log.Fatalf("unable to parse private key: %v", err)
}
// Minimal ClientConfig with SHA-1 algoritms disabled.
// The algorithms returned by ssh.SupportedAlgorithms() are different from
// the default ones and do not include algorithms that are considered
// insecure, such as those using SHA-1, returned by
// ssh.InsecureAlgorithms().
algorithms := ssh.SupportedAlgorithms()
config := &ssh.ClientConfig{
Config: ssh.Config{
KeyExchanges: algorithms.KeyExchanges,
Ciphers: algorithms.Ciphers,
MACs: algorithms.MACs,
},
User: "username",
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.FixedHostKey(hostKey),
// We should check that hostKey algorithm is inlcuded in
// algorithms.HostKeys.
HostKeyAlgorithms: algorithms.HostKeys,
}
client, err := ssh.Dial("tcp", "yourserver.com:22", config)
if err != nil {
log.Fatal("Failed to dial: ", err)
}
defer client.Close()
}
func ExamplePublicKeys() {
var hostKey ssh.PublicKey
// A public key may be used to authenticate against the remote
+14 -10
View File
@@ -38,7 +38,7 @@ type keyingTransport interface {
// prepareKeyChange sets up a key change. The key change for a
// direction will be effected if a msgNewKeys message is sent
// or received.
prepareKeyChange(*algorithms, *kexResult) error
prepareKeyChange(*NegotiatedAlgorithms, *kexResult) error
// setStrictMode sets the strict KEX mode, notably triggering
// sequence number resets on sending or receiving msgNewKeys.
@@ -115,7 +115,7 @@ type handshakeTransport struct {
bannerCallback BannerCallback
// Algorithms agreed in the last key exchange.
algorithms *algorithms
algorithms *NegotiatedAlgorithms
// Counters exclusively owned by readLoop.
readPacketsLeft uint32
@@ -164,7 +164,7 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt
if config.HostKeyAlgorithms != nil {
t.hostKeyAlgorithms = config.HostKeyAlgorithms
} else {
t.hostKeyAlgorithms = supportedHostKeyAlgos
t.hostKeyAlgorithms = defaultHostKeyAlgos
}
go t.readLoop()
go t.kexLoop()
@@ -184,6 +184,10 @@ func (t *handshakeTransport) getSessionID() []byte {
return t.sessionID
}
func (t *handshakeTransport) getAlgorithms() NegotiatedAlgorithms {
return *t.algorithms
}
// waitSession waits for the session to be established. This should be
// the first thing to call after instantiating handshakeTransport.
func (t *handshakeTransport) waitSession() error {
@@ -290,7 +294,7 @@ func (t *handshakeTransport) resetWriteThresholds() {
if t.config.RekeyThreshold > 0 {
t.writeBytesLeft = int64(t.config.RekeyThreshold)
} else if t.algorithms != nil {
t.writeBytesLeft = t.algorithms.w.rekeyBytes()
t.writeBytesLeft = t.algorithms.Write.rekeyBytes()
} else {
t.writeBytesLeft = 1 << 30
}
@@ -407,7 +411,7 @@ func (t *handshakeTransport) resetReadThresholds() {
if t.config.RekeyThreshold > 0 {
t.readBytesLeft = int64(t.config.RekeyThreshold)
} else if t.algorithms != nil {
t.readBytesLeft = t.algorithms.r.rekeyBytes()
t.readBytesLeft = t.algorithms.Read.rekeyBytes()
} else {
t.readBytesLeft = 1 << 30
}
@@ -700,9 +704,9 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
}
}
kex, ok := kexAlgoMap[t.algorithms.kex]
kex, ok := kexAlgoMap[t.algorithms.KeyExchange]
if !ok {
return fmt.Errorf("ssh: unexpected key exchange algorithm %v", t.algorithms.kex)
return fmt.Errorf("ssh: unexpected key exchange algorithm %v", t.algorithms.KeyExchange)
}
var result *kexResult
@@ -809,12 +813,12 @@ func pickHostKey(hostKeys []Signer, algo string) AlgorithmSigner {
}
func (t *handshakeTransport) server(kex kexAlgorithm, magics *handshakeMagics) (*kexResult, error) {
hostKey := pickHostKey(t.hostKeys, t.algorithms.hostKey)
hostKey := pickHostKey(t.hostKeys, t.algorithms.HostKey)
if hostKey == nil {
return nil, errors.New("ssh: internal error: negotiated unsupported signature type")
}
r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey, t.algorithms.hostKey)
r, err := kex.Server(t.conn, t.config.Rand, magics, hostKey, t.algorithms.HostKey)
return r, err
}
@@ -829,7 +833,7 @@ func (t *handshakeTransport) client(kex kexAlgorithm, magics *handshakeMagics) (
return nil, err
}
if err := verifyHostKeySignature(hostKey, t.algorithms.hostKey, result); err != nil {
if err := verifyHostKeySignature(hostKey, t.algorithms.HostKey, result); err != nil {
return nil, err
}
+129 -4
View File
@@ -367,7 +367,7 @@ type errorKeyingTransport struct {
readLeft, writeLeft int
}
func (n *errorKeyingTransport) prepareKeyChange(*algorithms, *kexResult) error {
func (n *errorKeyingTransport) prepareKeyChange(*NegotiatedAlgorithms, *kexResult) error {
return nil
}
@@ -545,7 +545,7 @@ type mockKeyingTransport struct {
kexInitSent chan struct{}
}
func (n *mockKeyingTransport) prepareKeyChange(*algorithms, *kexResult) error {
func (n *mockKeyingTransport) prepareKeyChange(*NegotiatedAlgorithms, *kexResult) error {
return nil
}
@@ -762,7 +762,7 @@ func TestHandshakePendingPacketsError(t *testing.T) {
func TestHandshakeRekeyDefault(t *testing.T) {
clientConf := &ClientConfig{
Config: Config{
Ciphers: []string{"aes128-ctr"},
Ciphers: []string{CipherAES128CTR},
},
HostKeyCallback: InsecureIgnoreHostKey(),
}
@@ -788,7 +788,7 @@ func TestHandshakeRekeyDefault(t *testing.T) {
}
func TestHandshakeAEADCipherNoMAC(t *testing.T) {
for _, cipher := range []string{chacha20Poly1305ID, gcm128CipherID} {
for _, cipher := range []string{CipherChaCha20Poly1305, CipherAES128GCM} {
checker := &syncChecker{
called: make(chan int, 1),
}
@@ -1239,3 +1239,128 @@ func TestStrictKEXMixed(t *testing.T) {
t.Fatalf("client.waitSession: %v", err)
}
}
func TestNegotiatedAlgorithms(t *testing.T) {
c1, c2, err := netPipe()
if err != nil {
t.Fatalf("netPipe: %v", err)
}
defer c1.Close()
defer c2.Close()
var serverAlgorithms NegotiatedAlgorithms
serverConf := &ServerConfig{
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
if algorithmConn, ok := conn.(AlgorithmsConnMetadata); ok {
serverAlgorithms = algorithmConn.Algorithms()
return &Permissions{}, nil
}
return nil, errors.New("server conn does not implement AlgorithmsConnMetadata")
},
}
serverConf.AddHostKey(testSigners["rsa"])
go NewServerConn(c1, serverConf)
clientConf := &ClientConfig{
User: "test",
Auth: []AuthMethod{Password("testpw")},
HostKeyCallback: FixedHostKey(testSigners["rsa"].PublicKey()),
}
if conn, _, _, err := NewClientConn(c2, "", clientConf); err != nil {
t.Fatal(err)
} else {
if algorithmConn, ok := conn.(AlgorithmsConnMetadata); ok {
clientAlgorithms := algorithmConn.Algorithms()
if clientAlgorithms.HostKey == "" {
t.Fatal("negotiated client host key is empty")
}
if clientAlgorithms.KeyExchange == "" {
t.Fatal("negotiated client KEX is empty")
}
if clientAlgorithms.Read.Cipher == "" {
t.Fatal("negotiated client read cipher is empty")
}
if clientAlgorithms.Write.Cipher == "" {
t.Fatal("negotiated client write cipher is empty")
}
if !reflect.DeepEqual(clientAlgorithms, serverAlgorithms) {
t.Fatalf("negotiated client algorithms: %+v differs from negotiated server algorithms: %+v",
clientAlgorithms, serverAlgorithms)
}
} else {
t.Fatal("client conn does not implement AlgorithmsConnMetadata")
}
}
}
func TestAlgorithmNegotiationError(t *testing.T) {
c1, c2, err := netPipe()
if err != nil {
t.Fatalf("netPipe: %v", err)
}
defer c1.Close()
defer c2.Close()
serverConf := &ServerConfig{
Config: Config{
Ciphers: []string{CipherAES128CTR, CipherAES256CTR},
},
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
return &Permissions{}, nil
},
}
serverConf.AddHostKey(testSigners["rsa"])
srvErrCh := make(chan error, 1)
go func() {
_, _, _, err := NewServerConn(c1, serverConf)
srvErrCh <- err
}()
clientConf := &ClientConfig{
Config: Config{
Ciphers: []string{CipherAES128GCM, CipherAES256GCM},
},
User: "test",
Auth: []AuthMethod{Password("testpw")},
HostKeyCallback: FixedHostKey(testSigners["rsa"].PublicKey()),
}
_, _, _, err = NewClientConn(c2, "", clientConf)
if err == nil {
t.Fatal("client connection succeded expected algorithm negotiation error")
}
var negotiationError *AlgorithmNegotiationError
if !errors.As(err, &negotiationError) {
t.Fatalf("expected algorithm negotiation error, got %v", err)
}
expectedErrorString := fmt.Sprintf("ssh: handshake failed: ssh: no common algorithm for client to server cipher; we offered: %v, peer offered: %v",
clientConf.Ciphers, serverConf.Ciphers)
if err.Error() != expectedErrorString {
t.Fatalf("expected error string %q, got %q", expectedErrorString, err.Error())
}
if !reflect.DeepEqual(negotiationError.RequestedAlgorithms, serverConf.Ciphers) {
t.Fatalf("expected requested algorithms %v, got %v", serverConf.Ciphers, negotiationError.RequestedAlgorithms)
}
if !reflect.DeepEqual(negotiationError.SupportedAlgorithms, clientConf.Ciphers) {
t.Fatalf("expected supported algorithms %v, got %v", clientConf.Ciphers, negotiationError.SupportedAlgorithms)
}
err = <-srvErrCh
negotiationError = nil
if !errors.As(err, &negotiationError) {
t.Fatalf("expected algorithm negotiation error, got %v", err)
}
expectedErrorString = fmt.Sprintf("ssh: no common algorithm for client to server cipher; we offered: %v, peer offered: %v",
serverConf.Ciphers, clientConf.Ciphers)
if err.Error() != expectedErrorString {
t.Fatalf("expected error string %q, got %q", expectedErrorString, err.Error())
}
if !reflect.DeepEqual(negotiationError.RequestedAlgorithms, clientConf.Ciphers) {
t.Fatalf("expected requested algorithms %v, got %v", clientConf.Ciphers, negotiationError.RequestedAlgorithms)
}
if !reflect.DeepEqual(negotiationError.SupportedAlgorithms, serverConf.Ciphers) {
t.Fatalf("expected supported algorithms %v, got %v", serverConf.Ciphers, negotiationError.SupportedAlgorithms)
}
}
+57 -50
View File
@@ -20,21 +20,18 @@ import (
)
const (
kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
kexAlgoDH14SHA256 = "diffie-hellman-group14-sha256"
kexAlgoDH16SHA512 = "diffie-hellman-group16-sha512"
kexAlgoECDH256 = "ecdh-sha2-nistp256"
kexAlgoECDH384 = "ecdh-sha2-nistp384"
kexAlgoECDH521 = "ecdh-sha2-nistp521"
kexAlgoCurve25519SHA256LibSSH = "curve25519-sha256@libssh.org"
kexAlgoCurve25519SHA256 = "curve25519-sha256"
// For the following kex only the client half contains a production
// ready implementation. The server half only consists of a minimal
// implementation to satisfy the automated tests.
kexAlgoDHGEXSHA1 = "diffie-hellman-group-exchange-sha1"
kexAlgoDHGEXSHA256 = "diffie-hellman-group-exchange-sha256"
// This is the group called diffie-hellman-group1-sha1 in RFC 4253 and
// Oakley Group 2 in RFC 2409.
oakleyGroup2 = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"
// This is the group called diffie-hellman-group14-sha1 in RFC 4253 and
// Oakley Group 14 in RFC 3526.
oakleyGroup14 = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF"
// This is the group called diffie-hellman-group15-sha512 in RFC 8268 and
// Oakley Group 15 in RFC 3526.
oakleyGroup15 = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF"
// This is the group called diffie-hellman-group16-sha512 in RFC 8268 and
// Oakley Group 16 in RFC 3526.
oakleyGroup16 = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF"
)
// kexResult captures the outcome of a key exchange.
@@ -402,53 +399,46 @@ func ecHash(curve elliptic.Curve) crypto.Hash {
var kexAlgoMap = map[string]kexAlgorithm{}
func init() {
// This is the group called diffie-hellman-group1-sha1 in
// RFC 4253 and Oakley Group 2 in RFC 2409.
p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{
p, _ := new(big.Int).SetString(oakleyGroup2, 16)
kexAlgoMap[InsecureKeyExchangeDH1SHA1] = &dhGroup{
g: new(big.Int).SetInt64(2),
p: p,
pMinus1: new(big.Int).Sub(p, bigOne),
hashFunc: crypto.SHA1,
}
// This are the groups called diffie-hellman-group14-sha1 and
// diffie-hellman-group14-sha256 in RFC 4253 and RFC 8268,
// and Oakley Group 14 in RFC 3526.
p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
p, _ = new(big.Int).SetString(oakleyGroup14, 16)
group14 := &dhGroup{
g: new(big.Int).SetInt64(2),
p: p,
pMinus1: new(big.Int).Sub(p, bigOne),
}
kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
kexAlgoMap[InsecureKeyExchangeDH14SHA1] = &dhGroup{
g: group14.g, p: group14.p, pMinus1: group14.pMinus1,
hashFunc: crypto.SHA1,
}
kexAlgoMap[kexAlgoDH14SHA256] = &dhGroup{
kexAlgoMap[KeyExchangeDH14SHA256] = &dhGroup{
g: group14.g, p: group14.p, pMinus1: group14.pMinus1,
hashFunc: crypto.SHA256,
}
// This is the group called diffie-hellman-group16-sha512 in RFC
// 8268 and Oakley Group 16 in RFC 3526.
p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF", 16)
p, _ = new(big.Int).SetString(oakleyGroup16, 16)
kexAlgoMap[kexAlgoDH16SHA512] = &dhGroup{
kexAlgoMap[KeyExchangeDH16SHA512] = &dhGroup{
g: new(big.Int).SetInt64(2),
p: p,
pMinus1: new(big.Int).Sub(p, bigOne),
hashFunc: crypto.SHA512,
}
kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
kexAlgoMap[kexAlgoCurve25519SHA256LibSSH] = &curve25519sha256{}
kexAlgoMap[kexAlgoDHGEXSHA1] = &dhGEXSHA{hashFunc: crypto.SHA1}
kexAlgoMap[kexAlgoDHGEXSHA256] = &dhGEXSHA{hashFunc: crypto.SHA256}
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}
}
// curve25519sha256 implements the curve25519-sha256 (formerly known as
@@ -601,9 +591,9 @@ const (
func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshakeMagics) (*kexResult, error) {
// Send GexRequest
kexDHGexRequest := kexDHGexRequestMsg{
MinBits: dhGroupExchangeMinimumBits,
PreferedBits: dhGroupExchangePreferredBits,
MaxBits: dhGroupExchangeMaximumBits,
MinBits: dhGroupExchangeMinimumBits,
PreferredBits: dhGroupExchangePreferredBits,
MaxBits: dhGroupExchangeMaximumBits,
}
if err := c.writePacket(Marshal(&kexDHGexRequest)); err != nil {
return nil, err
@@ -690,9 +680,7 @@ func (gex *dhGEXSHA) Client(c packetConn, randSource io.Reader, magics *handshak
}
// Server half implementation of the Diffie Hellman Key Exchange with SHA1 and SHA256.
//
// This is a minimal implementation to satisfy the automated tests.
func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv AlgorithmSigner, algo string) (result *kexResult, err error) {
func (gex *dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshakeMagics, priv AlgorithmSigner, algo string) (result *kexResult, err error) {
// Receive GexRequest
packet, err := c.readPacket()
if err != nil {
@@ -702,13 +690,32 @@ func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshake
if err = Unmarshal(packet, &kexDHGexRequest); err != nil {
return
}
// We check that the request received is valid and that the MaxBits
// requested are at least equal to our supported minimum. This is the same
// check done in OpenSSH:
// https://github.com/openssh/openssh-portable/blob/80a2f64b/kexgexs.c#L94
//
// Furthermore, we also check that the required MinBits are less than or
// equal to 4096 because we can use up to Oakley Group 16.
if kexDHGexRequest.MaxBits < kexDHGexRequest.MinBits || kexDHGexRequest.PreferredBits < kexDHGexRequest.MinBits ||
kexDHGexRequest.MaxBits < kexDHGexRequest.PreferredBits || kexDHGexRequest.MaxBits < dhGroupExchangeMinimumBits ||
kexDHGexRequest.MinBits > 4096 {
return nil, fmt.Errorf("ssh: DH GEX request out of range, min: %d, max: %d, preferred: %d", kexDHGexRequest.MinBits,
kexDHGexRequest.MaxBits, kexDHGexRequest.PreferredBits)
}
var p *big.Int
// We hardcode sending Oakley Group 14 (2048 bits), Oakley Group 15 (3072
// bits) or Oakley Group 16 (4096 bits), based on the requested max size.
if kexDHGexRequest.MaxBits < 3072 {
p, _ = new(big.Int).SetString(oakleyGroup14, 16)
} else if kexDHGexRequest.MaxBits < 4096 {
p, _ = new(big.Int).SetString(oakleyGroup15, 16)
} else {
p, _ = new(big.Int).SetString(oakleyGroup16, 16)
}
// Send GexGroup
// This is the group called diffie-hellman-group14-sha1 in RFC
// 4253 and Oakley Group 14 in RFC 3526.
p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
g := big.NewInt(2)
msg := &kexDHGexGroupMsg{
P: p,
G: g,
@@ -746,9 +753,9 @@ func (gex dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshake
h := gex.hashFunc.New()
magics.write(h)
writeString(h, hostKeyBytes)
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMinimumBits))
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangePreferredBits))
binary.Write(h, binary.BigEndian, uint32(dhGroupExchangeMaximumBits))
binary.Write(h, binary.BigEndian, kexDHGexRequest.MinBits)
binary.Write(h, binary.BigEndian, kexDHGexRequest.PreferredBits)
binary.Write(h, binary.BigEndian, kexDHGexRequest.MaxBits)
writeInt(h, p)
writeInt(h, g)
writeInt(h, kexDHGexInit.X)
+16 -11
View File
@@ -36,14 +36,19 @@ import (
// ClientConfig.HostKeyAlgorithms, Signature.Format, or as AlgorithmSigner
// arguments.
const (
KeyAlgoRSA = "ssh-rsa"
KeyAlgoDSA = "ssh-dss"
KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
KeyAlgoSKECDSA256 = "sk-ecdsa-sha2-nistp256@openssh.com"
KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
KeyAlgoED25519 = "ssh-ed25519"
KeyAlgoSKED25519 = "sk-ssh-ed25519@openssh.com"
KeyAlgoRSA = "ssh-rsa"
// Deprecated: DSA is only supported at insecure key sizes, and was removed
// from major implementations.
KeyAlgoDSA = InsecureKeyAlgoDSA
// Deprecated: DSA is only supported at insecure key sizes, and was removed
// from major implementations.
InsecureKeyAlgoDSA = "ssh-dss"
KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
KeyAlgoSKECDSA256 = "sk-ecdsa-sha2-nistp256@openssh.com"
KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
KeyAlgoED25519 = "ssh-ed25519"
KeyAlgoSKED25519 = "sk-ssh-ed25519@openssh.com"
// KeyAlgoRSASHA256 and KeyAlgoRSASHA512 are only public key algorithms, not
// public key formats, so they can't appear as a PublicKey.Type. The
@@ -67,7 +72,7 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
switch algo {
case KeyAlgoRSA:
return parseRSA(in)
case KeyAlgoDSA:
case InsecureKeyAlgoDSA:
return parseDSA(in)
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
return parseECDSA(in)
@@ -77,7 +82,7 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
return parseED25519(in)
case KeyAlgoSKED25519:
return parseSKEd25519(in)
case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
case CertAlgoRSAv01, InsecureCertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
cert, err := parseCert(in, certKeyAlgoNames[algo])
if err != nil {
return nil, nil, err
@@ -268,7 +273,7 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str
return nil, "", nil, nil, errors.New("ssh: no key found")
}
// ParsePublicKey parses an SSH public key formatted for use in
// ParsePublicKey parses an SSH public key or certificate formatted for use in
// the SSH wire protocol according to RFC 4253, section 6.6.
func ParsePublicKey(in []byte) (out PublicKey, err error) {
algo, in, ok := parseString(in)
+27
View File
@@ -810,3 +810,30 @@ func TestCryptoPublicKey(t *testing.T) {
}
}
}
func TestParseCertWithCertSignatureKey(t *testing.T) {
certBytes := []byte(`-----BEGIN SSH CERTIFICATE-----
AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIPSp27hvNSB0
IotJnVhjC4zxNgNS8BHlUCxD0VJi4D/eAAAAIIJMi1e5qfx+IFuKD/p/Ssqcb3os
CpOw/4wBs1pQ53zwAAAAAAAAAAEAAAACAAAAAAAAABMAAAAPZm9vLmV4YW1wbGUu
Y29tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT0AAAAgc3NoLWVkMjU1
MTktY2VydC12MDFAb3BlbnNzaC5jb20AAAAg+sNYhCO35mQT1UBMpmMk8ey+culd
IU8vBlPEl4B07swAAAAggiv+RLnboS4znGCVl/n1jDg2uD0h15tW4s/04eS2mLQA
AAAAAAAAAQAAAAIAAAAAAAAAEwAAAA9mb28uZXhhbXBsZS5jb20AAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACCV2wETgLKL
Kt0bRl3YUnd/ZYSlq0xJMbn4Jj3cdPWykQAAAFMAAAALc3NoLWVkMjU1MTkAAABA
WOdbRGEzyRAhiIK227CLUQD5caXYMV8FvSIB7toEE2M/8HnWdG9H3Rsg/v3unruQ
JrQldnuPJNe7KOP2+zvUDgAAAFMAAAALc3NoLWVkMjU1MTkAAABAm3bIPp85ZpIe
D+izJcUqlcAOri7HO8bULFNHT6LVegvB06xQ5TLwMlrxWUF4cafl1tSe8JQck4a6
cLYUOHfQDw==
-----END SSH CERTIFICATE-----
`)
block, _ := pem.Decode(certBytes)
if block == nil {
t.Fatal("invalid test certificate")
}
if _, err := ParsePublicKey(block.Bytes); err == nil {
t.Fatal("parsing an SSH certificate using another certificate as signature key succeeded; expected failure")
}
}
+6 -6
View File
@@ -47,22 +47,22 @@ func (t truncatingMAC) Size() int {
func (t truncatingMAC) BlockSize() int { return t.hmac.BlockSize() }
var macModes = map[string]*macMode{
"hmac-sha2-512-etm@openssh.com": {64, true, func(key []byte) hash.Hash {
HMACSHA512ETM: {64, true, func(key []byte) hash.Hash {
return hmac.New(sha512.New, key)
}},
"hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash {
HMACSHA256ETM: {32, true, func(key []byte) hash.Hash {
return hmac.New(sha256.New, key)
}},
"hmac-sha2-512": {64, false, func(key []byte) hash.Hash {
HMACSHA512: {64, false, func(key []byte) hash.Hash {
return hmac.New(sha512.New, key)
}},
"hmac-sha2-256": {32, false, func(key []byte) hash.Hash {
HMACSHA256: {32, false, func(key []byte) hash.Hash {
return hmac.New(sha256.New, key)
}},
"hmac-sha1": {20, false, func(key []byte) hash.Hash {
HMACSHA1: {20, false, func(key []byte) hash.Hash {
return hmac.New(sha1.New, key)
}},
"hmac-sha1-96": {20, false, func(key []byte) hash.Hash {
InsecureHMACSHA196: {20, false, func(key []byte) hash.Hash {
return truncatingMAC{12, hmac.New(sha1.New, key)}
}},
}
+3 -3
View File
@@ -122,9 +122,9 @@ type kexDHGexReplyMsg struct {
const msgKexDHGexRequest = 34
type kexDHGexRequestMsg struct {
MinBits uint32 `sshtype:"34"`
PreferedBits uint32
MaxBits uint32
MinBits uint32 `sshtype:"34"`
PreferredBits uint32
MaxBits uint32
}
// See RFC 4253, section 10.
+3 -7
View File
@@ -19,19 +19,15 @@ import (
"golang.org/x/crypto/curve25519"
)
const (
kexAlgoMLKEM768xCurve25519SHA256 = "mlkem768x25519-sha256"
)
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, kexAlgoMLKEM768xCurve25519SHA256)
preferredKexAlgos = slices.Insert(preferredKexAlgos, 0, kexAlgoMLKEM768xCurve25519SHA256)
kexAlgoMap[kexAlgoMLKEM768xCurve25519SHA256] = &mlkem768WithCurve25519sha256{}
supportedKexAlgos = slices.Insert(supportedKexAlgos, 0, KeyExchangeMLKEM768X25519)
defaultKexAlgos = slices.Insert(defaultKexAlgos, 0, KeyExchangeMLKEM768X25519)
kexAlgoMap[KeyExchangeMLKEM768X25519] = &mlkem768WithCurve25519sha256{}
}
// mlkem768WithCurve25519sha256 implements the hybrid ML-KEM768 with
+3 -9
View File
@@ -243,22 +243,15 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha
fullConf.MaxAuthTries = 6
}
if len(fullConf.PublicKeyAuthAlgorithms) == 0 {
fullConf.PublicKeyAuthAlgorithms = supportedPubKeyAuthAlgos
fullConf.PublicKeyAuthAlgorithms = defaultPubKeyAuthAlgos
} else {
for _, algo := range fullConf.PublicKeyAuthAlgorithms {
if !contains(supportedPubKeyAuthAlgos, algo) {
if !contains(SupportedAlgorithms().PublicKeyAuths, algo) && !contains(InsecureAlgorithms().PublicKeyAuths, algo) {
c.Close()
return nil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", algo)
}
}
}
// Check if the config contains any unsupported key exchanges
for _, kex := range fullConf.KeyExchanges {
if _, ok := serverForbiddenKexAlgos[kex]; ok {
c.Close()
return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex)
}
}
s := &connection{
sshConn: sshConn{conn: c},
@@ -315,6 +308,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
// We just did the key change, so the session ID is established.
s.sessionID = s.transport.getSessionID()
s.algorithms = s.transport.getAlgorithms()
var packet []byte
if packet, err = s.transport.readPacket(); err != nil {
+1 -1
View File
@@ -212,7 +212,7 @@ func TestNewServerConnValidationErrors(t *testing.T) {
serverConf = &ServerConfig{
Config: Config{
KeyExchanges: []string{kexAlgoDHGEXSHA256},
KeyExchanges: []string{KeyExchangeDHGEXSHA256},
},
}
c = &markerConn{}
+2 -2
View File
@@ -236,7 +236,7 @@ func recordingsClientConfig() *ssh.ClientConfig {
config := clientConfig()
config.SetDefaults()
// Remove ML-KEM since it only works with Go 1.24.
if config.KeyExchanges[0] == "mlkem768x25519-sha256" {
if config.KeyExchanges[0] == ssh.KeyExchangeMLKEM768X25519 {
config.KeyExchanges = config.KeyExchanges[1:]
}
config.Auth = []ssh.AuthMethod{
@@ -352,7 +352,7 @@ func TestHostKeyCheck(t *testing.T) {
// change the keys.
hostDB.keys[ssh.KeyAlgoRSA][25]++
hostDB.keys[ssh.KeyAlgoDSA][25]++
hostDB.keys[ssh.InsecureKeyAlgoDSA][25]++
hostDB.keys[ssh.KeyAlgoECDSA256][25]++
test := clientTest{
+1 -1
View File
@@ -219,7 +219,7 @@ func recordingsServerConfig() *ssh.ServerConfig {
config.SetDefaults()
// Remove ML-KEM since it only works with Go 1.24.
config.SetDefaults()
if config.KeyExchanges[0] == "mlkem768x25519-sha256" {
if config.KeyExchanges[0] == ssh.KeyExchangeMLKEM768X25519 {
config.KeyExchanges = config.KeyExchanges[1:]
}
config.AddHostKey(testSigners["rsa"])
+1 -1
View File
@@ -128,7 +128,7 @@ func clientConfig() *ssh.ClientConfig {
HostKeyCallback: hostKeyDB().Check,
HostKeyAlgorithms: []string{ // by default, don't allow certs as this affects the hostKeyDB checker
ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521,
ssh.KeyAlgoRSA, ssh.KeyAlgoDSA,
ssh.KeyAlgoRSA, ssh.InsecureKeyAlgoDSA,
ssh.KeyAlgoED25519,
},
}
+2 -2
View File
@@ -224,8 +224,8 @@ func testOneCipher(t *testing.T, cipher string, cipherOrder []string) {
}
var deprecatedCiphers = []string{
"aes128-cbc", "3des-cbc",
"arcfour128", "arcfour256",
ssh.InsecureCipherAES128CBC, ssh.InsecureCipherTripleDESCBC,
ssh.InsecureCipherRC4128, ssh.InsecureCipherRC4256,
}
func TestCiphers(t *testing.T) {
+74 -11
View File
@@ -34,23 +34,29 @@ func sshClient(t *testing.T) string {
return sshCLI
}
// setupSSHCLIKeys writes the provided key files to a temporary directory and
// returns the path to the private key.
func setupSSHCLIKeys(t *testing.T, keyFiles map[string][]byte, privKeyName string) string {
tmpDir := t.TempDir()
for fn, content := range keyFiles {
if err := os.WriteFile(filepath.Join(tmpDir, fn), content, 0600); err != nil {
t.Fatalf("WriteFile(%q): %v", fn, err)
}
}
return filepath.Join(tmpDir, privKeyName)
}
func TestSSHCLIAuth(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skipf("always fails on Windows, see #64403")
}
sshCLI := sshClient(t)
dir := t.TempDir()
keyPrivPath := filepath.Join(dir, "rsa")
for fn, content := range map[string][]byte{
keyPrivPath: testdata.PEMBytes["rsa"],
keyPrivPath + ".pub": ssh.MarshalAuthorizedKey(testPublicKeys["rsa"]),
filepath.Join(dir, "rsa-cert.pub"): testdata.SSHCertificates["rsa-user-testcertificate"],
} {
if err := os.WriteFile(fn, content, 0600); err != nil {
t.Fatalf("WriteFile(%q): %v", fn, err)
}
keyFiles := map[string][]byte{
"rsa": testdata.PEMBytes["rsa"],
"rsa.pub": ssh.MarshalAuthorizedKey(testPublicKeys["rsa"]),
"rsa-cert.pub": testdata.SSHCertificates["rsa-user-testcertificate"],
}
keyPrivPath := setupSSHCLIKeys(t, keyFiles, "rsa")
certChecker := ssh.CertChecker{
IsUserAuthority: func(k ssh.PublicKey) bool {
@@ -98,3 +104,60 @@ func TestSSHCLIAuth(t *testing.T) {
t.Fatalf("user certificate authentication failed, error: %v, command output %q", err, string(out))
}
}
func TestSSHCLIKeyExchanges(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skipf("always fails on Windows, see #64403")
}
sshCLI := sshClient(t)
keyFiles := map[string][]byte{
"rsa": testdata.PEMBytes["rsa"],
"rsa.pub": ssh.MarshalAuthorizedKey(testPublicKeys["rsa"]),
}
keyPrivPath := setupSSHCLIKeys(t, keyFiles, "rsa")
keyExchanges := append(ssh.SupportedAlgorithms().KeyExchanges, ssh.InsecureAlgorithms().KeyExchanges...)
for _, kex := range keyExchanges {
t.Run(kex, func(t *testing.T) {
cmd := testenv.Command(t, sshCLI, "-Q", "kex")
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("%s failed to check if the KEX is supported, error: %v, command output %q", kex, err, string(out))
}
if !bytes.Contains(out, []byte(kex)) {
t.Skipf("KEX %q is not supported in the installed ssh CLI", kex)
}
config := &ssh.ServerConfig{
Config: ssh.Config{
KeyExchanges: []string{kex},
},
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
if conn.User() == "testpubkey" && bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
return nil, nil
}
return nil, fmt.Errorf("pubkey for %q not acceptable", conn.User())
},
}
config.AddHostKey(testSigners["rsa"])
server, err := newTestServer(config)
if err != nil {
t.Fatalf("unable to start test server: %v", err)
}
defer server.Close()
port, err := server.port()
if err != nil {
t.Fatalf("unable to get server port: %v", err)
}
cmd = testenv.Command(t, sshCLI, "-vvv", "-i", keyPrivPath, "-o", "StrictHostKeyChecking=no",
"-o", fmt.Sprintf("KexAlgorithms=%s", kex), "-p", port, "testpubkey@127.0.0.1", "true")
out, err = cmd.CombinedOutput()
if err != nil {
t.Fatalf("%s failed, error: %v, command output %q", kex, err, string(out))
}
})
}
}
+254 -255
View File
@@ -2,58 +2,59 @@
00000000 53 53 48 2d 32 2e 30 2d 47 6f 0d 0a |SSH-2.0-Go..|
>>> Flow 2 (server to client)
00000000 53 53 48 2d 32 2e 30 2d 4f 70 65 6e 53 53 48 5f |SSH-2.0-OpenSSH_|
00000010 39 2e 39 0d 0a |9.9..|
00000010 31 30 2e 30 0d 0a |10.0..|
>>> Flow 3 (client to server)
00000000 00 00 02 9c 11 14 7f 9c 2b a4 e8 8f 82 7d 61 60 |........+....}a`|
00000010 45 50 76 05 85 3e 00 00 00 39 63 75 72 76 65 32 |EPv..>...9curve2|
00000020 35 35 31 39 2d 73 68 61 32 35 36 2c 65 78 74 2d |5519-sha256,ext-|
00000030 69 6e 66 6f 2d 63 2c 6b 65 78 2d 73 74 72 69 63 |info-c,kex-stric|
00000040 74 2d 63 2d 76 30 30 40 6f 70 65 6e 73 73 68 2e |t-c-v00@openssh.|
00000050 63 6f 6d 00 00 00 57 65 63 64 73 61 2d 73 68 61 |com...Wecdsa-sha|
00000060 32 2d 6e 69 73 74 70 32 35 36 2c 65 63 64 73 61 |2-nistp256,ecdsa|
00000070 2d 73 68 61 32 2d 6e 69 73 74 70 33 38 34 2c 65 |-sha2-nistp384,e|
00000080 63 64 73 61 2d 73 68 61 32 2d 6e 69 73 74 70 35 |cdsa-sha2-nistp5|
00000090 32 31 2c 73 73 68 2d 72 73 61 2c 73 73 68 2d 64 |21,ssh-rsa,ssh-d|
000000a0 73 73 2c 73 73 68 2d 65 64 32 35 35 31 39 00 00 |ss,ssh-ed25519..|
000000b0 00 6c 61 65 73 31 32 38 2d 67 63 6d 40 6f 70 65 |.laes128-gcm@ope|
000000c0 6e 73 73 68 2e 63 6f 6d 2c 61 65 73 32 35 36 2d |nssh.com,aes256-|
000000d0 67 63 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c |gcm@openssh.com,|
000000e0 63 68 61 63 68 61 32 30 2d 70 6f 6c 79 31 33 30 |chacha20-poly130|
000000f0 35 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 61 65 |5@openssh.com,ae|
00000100 73 31 32 38 2d 63 74 72 2c 61 65 73 31 39 32 2d |s128-ctr,aes192-|
00000110 63 74 72 2c 61 65 73 32 35 36 2d 63 74 72 00 00 |ctr,aes256-ctr..|
00000120 00 6c 61 65 73 31 32 38 2d 67 63 6d 40 6f 70 65 |.laes128-gcm@ope|
00000130 6e 73 73 68 2e 63 6f 6d 2c 61 65 73 32 35 36 2d |nssh.com,aes256-|
00000140 67 63 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c |gcm@openssh.com,|
00000150 63 68 61 63 68 61 32 30 2d 70 6f 6c 79 31 33 30 |chacha20-poly130|
00000160 35 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 61 65 |5@openssh.com,ae|
00000170 73 31 32 38 2d 63 74 72 2c 61 65 73 31 39 32 2d |s128-ctr,aes192-|
00000180 63 74 72 2c 61 65 73 32 35 36 2d 63 74 72 00 00 |ctr,aes256-ctr..|
00000190 00 6e 68 6d 61 63 2d 73 68 61 32 2d 32 35 36 2d |.nhmac-sha2-256-|
000001a0 65 74 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c |etm@openssh.com,|
000001b0 68 6d 61 63 2d 73 68 61 32 2d 35 31 32 2d 65 74 |hmac-sha2-512-et|
000001c0 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 6d |m@openssh.com,hm|
000001d0 61 63 2d 73 68 61 32 2d 32 35 36 2c 68 6d 61 63 |ac-sha2-256,hmac|
000001e0 2d 73 68 61 32 2d 35 31 32 2c 68 6d 61 63 2d 73 |-sha2-512,hmac-s|
000001f0 68 61 31 2c 68 6d 61 63 2d 73 68 61 31 2d 39 36 |ha1,hmac-sha1-96|
00000200 00 00 00 6e 68 6d 61 63 2d 73 68 61 32 2d 32 35 |...nhmac-sha2-25|
00000210 36 2d 65 74 6d 40 6f 70 65 6e 73 73 68 2e 63 6f |6-etm@openssh.co|
00000220 6d 2c 68 6d 61 63 2d 73 68 61 32 2d 35 31 32 2d |m,hmac-sha2-512-|
00000230 65 74 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c |etm@openssh.com,|
00000240 68 6d 61 63 2d 73 68 61 32 2d 32 35 36 2c 68 6d |hmac-sha2-256,hm|
00000250 61 63 2d 73 68 61 32 2d 35 31 32 2c 68 6d 61 63 |ac-sha2-512,hmac|
00000260 2d 73 68 61 31 2c 68 6d 61 63 2d 73 68 61 31 2d |-sha1,hmac-sha1-|
00000270 39 36 00 00 00 04 6e 6f 6e 65 00 00 00 04 6e 6f |96....none....no|
00000280 6e 65 00 00 00 00 00 00 00 00 00 00 00 00 00 d7 |ne..............|
00000290 3b 80 93 f6 ef bc 88 eb 1a 6e ac fa 66 ef 26 3c |;........n..f.&<|
00000000 00 00 02 ac 04 14 7f 9c 2b a4 e8 8f 82 7d 61 60 |........+....}a`|
00000010 45 50 76 05 85 3e 00 00 00 56 63 75 72 76 65 32 |EPv..>...Vcurve2|
00000020 35 35 31 39 2d 73 68 61 32 35 36 2c 63 75 72 76 |5519-sha256,curv|
00000030 65 32 35 35 31 39 2d 73 68 61 32 35 36 40 6c 69 |e25519-sha256@li|
00000040 62 73 73 68 2e 6f 72 67 2c 65 78 74 2d 69 6e 66 |bssh.org,ext-inf|
00000050 6f 2d 63 2c 6b 65 78 2d 73 74 72 69 63 74 2d 63 |o-c,kex-strict-c|
00000060 2d 76 30 30 40 6f 70 65 6e 73 73 68 2e 63 6f 6d |-v00@openssh.com|
00000070 00 00 00 57 65 63 64 73 61 2d 73 68 61 32 2d 6e |...Wecdsa-sha2-n|
00000080 69 73 74 70 32 35 36 2c 65 63 64 73 61 2d 73 68 |istp256,ecdsa-sh|
00000090 61 32 2d 6e 69 73 74 70 33 38 34 2c 65 63 64 73 |a2-nistp384,ecds|
000000a0 61 2d 73 68 61 32 2d 6e 69 73 74 70 35 32 31 2c |a-sha2-nistp521,|
000000b0 73 73 68 2d 72 73 61 2c 73 73 68 2d 64 73 73 2c |ssh-rsa,ssh-dss,|
000000c0 73 73 68 2d 65 64 32 35 35 31 39 00 00 00 6c 61 |ssh-ed25519...la|
000000d0 65 73 31 32 38 2d 67 63 6d 40 6f 70 65 6e 73 73 |es128-gcm@openss|
000000e0 68 2e 63 6f 6d 2c 61 65 73 32 35 36 2d 67 63 6d |h.com,aes256-gcm|
000000f0 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 63 68 61 |@openssh.com,cha|
00000100 63 68 61 32 30 2d 70 6f 6c 79 31 33 30 35 40 6f |cha20-poly1305@o|
00000110 70 65 6e 73 73 68 2e 63 6f 6d 2c 61 65 73 31 32 |penssh.com,aes12|
00000120 38 2d 63 74 72 2c 61 65 73 31 39 32 2d 63 74 72 |8-ctr,aes192-ctr|
00000130 2c 61 65 73 32 35 36 2d 63 74 72 00 00 00 6c 61 |,aes256-ctr...la|
00000140 65 73 31 32 38 2d 67 63 6d 40 6f 70 65 6e 73 73 |es128-gcm@openss|
00000150 68 2e 63 6f 6d 2c 61 65 73 32 35 36 2d 67 63 6d |h.com,aes256-gcm|
00000160 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 63 68 61 |@openssh.com,cha|
00000170 63 68 61 32 30 2d 70 6f 6c 79 31 33 30 35 40 6f |cha20-poly1305@o|
00000180 70 65 6e 73 73 68 2e 63 6f 6d 2c 61 65 73 31 32 |penssh.com,aes12|
00000190 38 2d 63 74 72 2c 61 65 73 31 39 32 2d 63 74 72 |8-ctr,aes192-ctr|
000001a0 2c 61 65 73 32 35 36 2d 63 74 72 00 00 00 6e 68 |,aes256-ctr...nh|
000001b0 6d 61 63 2d 73 68 61 32 2d 32 35 36 2d 65 74 6d |mac-sha2-256-etm|
000001c0 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 6d 61 |@openssh.com,hma|
000001d0 63 2d 73 68 61 32 2d 35 31 32 2d 65 74 6d 40 6f |c-sha2-512-etm@o|
000001e0 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 6d 61 63 2d |penssh.com,hmac-|
000001f0 73 68 61 32 2d 32 35 36 2c 68 6d 61 63 2d 73 68 |sha2-256,hmac-sh|
00000200 61 32 2d 35 31 32 2c 68 6d 61 63 2d 73 68 61 31 |a2-512,hmac-sha1|
00000210 2c 68 6d 61 63 2d 73 68 61 31 2d 39 36 00 00 00 |,hmac-sha1-96...|
00000220 6e 68 6d 61 63 2d 73 68 61 32 2d 32 35 36 2d 65 |nhmac-sha2-256-e|
00000230 74 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 |tm@openssh.com,h|
00000240 6d 61 63 2d 73 68 61 32 2d 35 31 32 2d 65 74 6d |mac-sha2-512-etm|
00000250 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 6d 61 |@openssh.com,hma|
00000260 63 2d 73 68 61 32 2d 32 35 36 2c 68 6d 61 63 2d |c-sha2-256,hmac-|
00000270 73 68 61 32 2d 35 31 32 2c 68 6d 61 63 2d 73 68 |sha2-512,hmac-sh|
00000280 61 31 2c 68 6d 61 63 2d 73 68 61 31 2d 39 36 00 |a1,hmac-sha1-96.|
00000290 00 00 04 6e 6f 6e 65 00 00 00 04 6e 6f 6e 65 00 |...none....none.|
000002a0 00 00 00 00 00 00 00 00 00 00 00 00 d7 3b 80 93 |.............;..|
>>> Flow 4 (server to client)
00000000 00 00 04 9c 0a 14 86 1d 13 33 bb 65 c5 7e 3d 8b |.........3.e.~=.|
00000010 4f 63 27 f5 d1 59 00 00 01 7a 73 6e 74 72 75 70 |Oc'..Y...zsntrup|
00000020 37 36 31 78 32 35 35 31 39 2d 73 68 61 35 31 32 |761x25519-sha512|
00000030 2c 73 6e 74 72 75 70 37 36 31 78 32 35 35 31 39 |,sntrup761x25519|
00000040 2d 73 68 61 35 31 32 40 6f 70 65 6e 73 73 68 2e |-sha512@openssh.|
00000050 63 6f 6d 2c 6d 6c 6b 65 6d 37 36 38 78 32 35 35 |com,mlkem768x255|
00000060 31 39 2d 73 68 61 32 35 36 2c 63 75 72 76 65 32 |19-sha256,curve2|
00000000 00 00 04 7c 08 14 d7 da a8 cd 40 76 e6 8a 12 b5 |...|......@v....|
00000010 5f ff a4 a5 57 4b 00 00 01 5c 6d 6c 6b 65 6d 37 |_...WK...\mlkem7|
00000020 36 38 78 32 35 35 31 39 2d 73 68 61 32 35 36 2c |68x25519-sha256,|
00000030 73 6e 74 72 75 70 37 36 31 78 32 35 35 31 39 2d |sntrup761x25519-|
00000040 73 68 61 35 31 32 2c 73 6e 74 72 75 70 37 36 31 |sha512,sntrup761|
00000050 78 32 35 35 31 39 2d 73 68 61 35 31 32 40 6f 70 |x25519-sha512@op|
00000060 65 6e 73 73 68 2e 63 6f 6d 2c 63 75 72 76 65 32 |enssh.com,curve2|
00000070 35 35 31 39 2d 73 68 61 32 35 36 2c 63 75 72 76 |5519-sha256,curv|
00000080 65 32 35 35 31 39 2d 73 68 61 32 35 36 40 6c 69 |e25519-sha256@li|
00000090 62 73 73 68 2e 6f 72 67 2c 65 63 64 68 2d 73 68 |bssh.org,ecdh-sh|
@@ -61,229 +62,227 @@
000000b0 2d 73 68 61 32 2d 6e 69 73 74 70 33 38 34 2c 65 |-sha2-nistp384,e|
000000c0 63 64 68 2d 73 68 61 32 2d 6e 69 73 74 70 35 32 |cdh-sha2-nistp52|
000000d0 31 2c 64 69 66 66 69 65 2d 68 65 6c 6c 6d 61 6e |1,diffie-hellman|
000000e0 2d 67 72 6f 75 70 2d 65 78 63 68 61 6e 67 65 2d |-group-exchange-|
000000f0 73 68 61 32 35 36 2c 64 69 66 66 69 65 2d 68 65 |sha256,diffie-he|
00000100 6c 6c 6d 61 6e 2d 67 72 6f 75 70 31 36 2d 73 68 |llman-group16-sh|
00000110 61 35 31 32 2c 64 69 66 66 69 65 2d 68 65 6c 6c |a512,diffie-hell|
00000120 6d 61 6e 2d 67 72 6f 75 70 31 38 2d 73 68 61 35 |man-group18-sha5|
00000130 31 32 2c 64 69 66 66 69 65 2d 68 65 6c 6c 6d 61 |12,diffie-hellma|
00000140 6e 2d 67 72 6f 75 70 31 34 2d 73 68 61 32 35 36 |n-group14-sha256|
00000150 2c 64 69 66 66 69 65 2d 68 65 6c 6c 6d 61 6e 2d |,diffie-hellman-|
00000160 67 72 6f 75 70 31 34 2d 73 68 61 31 2c 65 78 74 |group14-sha1,ext|
00000170 2d 69 6e 66 6f 2d 73 2c 6b 65 78 2d 73 74 72 69 |-info-s,kex-stri|
00000180 63 74 2d 73 2d 76 30 30 40 6f 70 65 6e 73 73 68 |ct-s-v00@openssh|
00000190 2e 63 6f 6d 00 00 00 2d 72 73 61 2d 73 68 61 32 |.com...-rsa-sha2|
000001a0 2d 35 31 32 2c 72 73 61 2d 73 68 61 32 2d 32 35 |-512,rsa-sha2-25|
000001b0 36 2c 65 63 64 73 61 2d 73 68 61 32 2d 6e 69 73 |6,ecdsa-sha2-nis|
000001c0 74 70 32 35 36 00 00 00 6c 63 68 61 63 68 61 32 |tp256...lchacha2|
000001d0 30 2d 70 6f 6c 79 31 33 30 35 40 6f 70 65 6e 73 |0-poly1305@opens|
000001e0 73 68 2e 63 6f 6d 2c 61 65 73 31 32 38 2d 63 74 |sh.com,aes128-ct|
000001f0 72 2c 61 65 73 31 39 32 2d 63 74 72 2c 61 65 73 |r,aes192-ctr,aes|
00000200 32 35 36 2d 63 74 72 2c 61 65 73 31 32 38 2d 67 |256-ctr,aes128-g|
00000210 63 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 61 |cm@openssh.com,a|
00000220 65 73 32 35 36 2d 67 63 6d 40 6f 70 65 6e 73 73 |es256-gcm@openss|
00000230 68 2e 63 6f 6d 00 00 00 6c 63 68 61 63 68 61 32 |h.com...lchacha2|
00000240 30 2d 70 6f 6c 79 31 33 30 35 40 6f 70 65 6e 73 |0-poly1305@opens|
00000250 73 68 2e 63 6f 6d 2c 61 65 73 31 32 38 2d 63 74 |sh.com,aes128-ct|
00000260 72 2c 61 65 73 31 39 32 2d 63 74 72 2c 61 65 73 |r,aes192-ctr,aes|
00000270 32 35 36 2d 63 74 72 2c 61 65 73 31 32 38 2d 67 |256-ctr,aes128-g|
00000280 63 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 61 |cm@openssh.com,a|
00000290 65 73 32 35 36 2d 67 63 6d 40 6f 70 65 6e 73 73 |es256-gcm@openss|
000002a0 68 2e 63 6f 6d 00 00 00 d5 75 6d 61 63 2d 36 34 |h.com....umac-64|
000002b0 2d 65 74 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d |-etm@openssh.com|
000002c0 2c 75 6d 61 63 2d 31 32 38 2d 65 74 6d 40 6f 70 |,umac-128-etm@op|
000000e0 2d 67 72 6f 75 70 31 34 2d 73 68 61 31 2c 64 69 |-group14-sha1,di|
000000f0 66 66 69 65 2d 68 65 6c 6c 6d 61 6e 2d 67 72 6f |ffie-hellman-gro|
00000100 75 70 31 34 2d 73 68 61 32 35 36 2c 64 69 66 66 |up14-sha256,diff|
00000110 69 65 2d 68 65 6c 6c 6d 61 6e 2d 67 72 6f 75 70 |ie-hellman-group|
00000120 31 36 2d 73 68 61 35 31 32 2c 64 69 66 66 69 65 |16-sha512,diffie|
00000130 2d 68 65 6c 6c 6d 61 6e 2d 67 72 6f 75 70 2d 65 |-hellman-group-e|
00000140 78 63 68 61 6e 67 65 2d 73 68 61 32 35 36 2c 65 |xchange-sha256,e|
00000150 78 74 2d 69 6e 66 6f 2d 73 2c 6b 65 78 2d 73 74 |xt-info-s,kex-st|
00000160 72 69 63 74 2d 73 2d 76 30 30 40 6f 70 65 6e 73 |rict-s-v00@opens|
00000170 73 68 2e 63 6f 6d 00 00 00 2d 72 73 61 2d 73 68 |sh.com...-rsa-sh|
00000180 61 32 2d 35 31 32 2c 72 73 61 2d 73 68 61 32 2d |a2-512,rsa-sha2-|
00000190 32 35 36 2c 65 63 64 73 61 2d 73 68 61 32 2d 6e |256,ecdsa-sha2-n|
000001a0 69 73 74 70 32 35 36 00 00 00 6c 63 68 61 63 68 |istp256...lchach|
000001b0 61 32 30 2d 70 6f 6c 79 31 33 30 35 40 6f 70 65 |a20-poly1305@ope|
000001c0 6e 73 73 68 2e 63 6f 6d 2c 61 65 73 31 32 38 2d |nssh.com,aes128-|
000001d0 67 63 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c |gcm@openssh.com,|
000001e0 61 65 73 32 35 36 2d 67 63 6d 40 6f 70 65 6e 73 |aes256-gcm@opens|
000001f0 73 68 2e 63 6f 6d 2c 61 65 73 31 32 38 2d 63 74 |sh.com,aes128-ct|
00000200 72 2c 61 65 73 31 39 32 2d 63 74 72 2c 61 65 73 |r,aes192-ctr,aes|
00000210 32 35 36 2d 63 74 72 00 00 00 6c 63 68 61 63 68 |256-ctr...lchach|
00000220 61 32 30 2d 70 6f 6c 79 31 33 30 35 40 6f 70 65 |a20-poly1305@ope|
00000230 6e 73 73 68 2e 63 6f 6d 2c 61 65 73 31 32 38 2d |nssh.com,aes128-|
00000240 67 63 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c |gcm@openssh.com,|
00000250 61 65 73 32 35 36 2d 67 63 6d 40 6f 70 65 6e 73 |aes256-gcm@opens|
00000260 73 68 2e 63 6f 6d 2c 61 65 73 31 32 38 2d 63 74 |sh.com,aes128-ct|
00000270 72 2c 61 65 73 31 39 32 2d 63 74 72 2c 61 65 73 |r,aes192-ctr,aes|
00000280 32 35 36 2d 63 74 72 00 00 00 d5 75 6d 61 63 2d |256-ctr....umac-|
00000290 36 34 2d 65 74 6d 40 6f 70 65 6e 73 73 68 2e 63 |64-etm@openssh.c|
000002a0 6f 6d 2c 75 6d 61 63 2d 31 32 38 2d 65 74 6d 40 |om,umac-128-etm@|
000002b0 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 6d 61 63 |openssh.com,hmac|
000002c0 2d 73 68 61 32 2d 32 35 36 2d 65 74 6d 40 6f 70 |-sha2-256-etm@op|
000002d0 65 6e 73 73 68 2e 63 6f 6d 2c 68 6d 61 63 2d 73 |enssh.com,hmac-s|
000002e0 68 61 32 2d 32 35 36 2d 65 74 6d 40 6f 70 65 6e |ha2-256-etm@open|
000002e0 68 61 32 2d 35 31 32 2d 65 74 6d 40 6f 70 65 6e |ha2-512-etm@open|
000002f0 73 73 68 2e 63 6f 6d 2c 68 6d 61 63 2d 73 68 61 |ssh.com,hmac-sha|
00000300 32 2d 35 31 32 2d 65 74 6d 40 6f 70 65 6e 73 73 |2-512-etm@openss|
00000310 68 2e 63 6f 6d 2c 68 6d 61 63 2d 73 68 61 31 2d |h.com,hmac-sha1-|
00000320 65 74 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c |etm@openssh.com,|
00000330 75 6d 61 63 2d 36 34 40 6f 70 65 6e 73 73 68 2e |umac-64@openssh.|
00000340 63 6f 6d 2c 75 6d 61 63 2d 31 32 38 40 6f 70 65 |com,umac-128@ope|
00000350 6e 73 73 68 2e 63 6f 6d 2c 68 6d 61 63 2d 73 68 |nssh.com,hmac-sh|
00000360 61 32 2d 32 35 36 2c 68 6d 61 63 2d 73 68 61 32 |a2-256,hmac-sha2|
00000370 2d 35 31 32 2c 68 6d 61 63 2d 73 68 61 31 00 00 |-512,hmac-sha1..|
00000380 00 d5 75 6d 61 63 2d 36 34 2d 65 74 6d 40 6f 70 |..umac-64-etm@op|
00000390 65 6e 73 73 68 2e 63 6f 6d 2c 75 6d 61 63 2d 31 |enssh.com,umac-1|
000003a0 32 38 2d 65 74 6d 40 6f 70 65 6e 73 73 68 2e 63 |28-etm@openssh.c|
000003b0 6f 6d 2c 68 6d 61 63 2d 73 68 61 32 2d 32 35 36 |om,hmac-sha2-256|
00000300 31 2d 65 74 6d 40 6f 70 65 6e 73 73 68 2e 63 6f |1-etm@openssh.co|
00000310 6d 2c 75 6d 61 63 2d 36 34 40 6f 70 65 6e 73 73 |m,umac-64@openss|
00000320 68 2e 63 6f 6d 2c 75 6d 61 63 2d 31 32 38 40 6f |h.com,umac-128@o|
00000330 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 6d 61 63 2d |penssh.com,hmac-|
00000340 73 68 61 32 2d 32 35 36 2c 68 6d 61 63 2d 73 68 |sha2-256,hmac-sh|
00000350 61 32 2d 35 31 32 2c 68 6d 61 63 2d 73 68 61 31 |a2-512,hmac-sha1|
00000360 00 00 00 d5 75 6d 61 63 2d 36 34 2d 65 74 6d 40 |....umac-64-etm@|
00000370 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 75 6d 61 63 |openssh.com,umac|
00000380 2d 31 32 38 2d 65 74 6d 40 6f 70 65 6e 73 73 68 |-128-etm@openssh|
00000390 2e 63 6f 6d 2c 68 6d 61 63 2d 73 68 61 32 2d 32 |.com,hmac-sha2-2|
000003a0 35 36 2d 65 74 6d 40 6f 70 65 6e 73 73 68 2e 63 |56-etm@openssh.c|
000003b0 6f 6d 2c 68 6d 61 63 2d 73 68 61 32 2d 35 31 32 |om,hmac-sha2-512|
000003c0 2d 65 74 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d |-etm@openssh.com|
000003d0 2c 68 6d 61 63 2d 73 68 61 32 2d 35 31 32 2d 65 |,hmac-sha2-512-e|
000003e0 74 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 |tm@openssh.com,h|
000003f0 6d 61 63 2d 73 68 61 31 2d 65 74 6d 40 6f 70 65 |mac-sha1-etm@ope|
00000400 6e 73 73 68 2e 63 6f 6d 2c 75 6d 61 63 2d 36 34 |nssh.com,umac-64|
00000410 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 75 6d 61 |@openssh.com,uma|
00000420 63 2d 31 32 38 40 6f 70 65 6e 73 73 68 2e 63 6f |c-128@openssh.co|
00000430 6d 2c 68 6d 61 63 2d 73 68 61 32 2d 32 35 36 2c |m,hmac-sha2-256,|
00000440 68 6d 61 63 2d 73 68 61 32 2d 35 31 32 2c 68 6d |hmac-sha2-512,hm|
00000450 61 63 2d 73 68 61 31 00 00 00 15 6e 6f 6e 65 2c |ac-sha1....none,|
00000460 7a 6c 69 62 40 6f 70 65 6e 73 73 68 2e 63 6f 6d |zlib@openssh.com|
00000470 00 00 00 15 6e 6f 6e 65 2c 7a 6c 69 62 40 6f 70 |....none,zlib@op|
00000480 65 6e 73 73 68 2e 63 6f 6d 00 00 00 00 00 00 00 |enssh.com.......|
00000490 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000003d0 2c 68 6d 61 63 2d 73 68 61 31 2d 65 74 6d 40 6f |,hmac-sha1-etm@o|
000003e0 70 65 6e 73 73 68 2e 63 6f 6d 2c 75 6d 61 63 2d |penssh.com,umac-|
000003f0 36 34 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 75 |64@openssh.com,u|
00000400 6d 61 63 2d 31 32 38 40 6f 70 65 6e 73 73 68 2e |mac-128@openssh.|
00000410 63 6f 6d 2c 68 6d 61 63 2d 73 68 61 32 2d 32 35 |com,hmac-sha2-25|
00000420 36 2c 68 6d 61 63 2d 73 68 61 32 2d 35 31 32 2c |6,hmac-sha2-512,|
00000430 68 6d 61 63 2d 73 68 61 31 00 00 00 15 6e 6f 6e |hmac-sha1....non|
00000440 65 2c 7a 6c 69 62 40 6f 70 65 6e 73 73 68 2e 63 |e,zlib@openssh.c|
00000450 6f 6d 00 00 00 15 6e 6f 6e 65 2c 7a 6c 69 62 40 |om....none,zlib@|
00000460 6f 70 65 6e 73 73 68 2e 63 6f 6d 00 00 00 00 00 |openssh.com.....|
00000470 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
>>> Flow 5 (client to server)
00000000 00 00 00 2c 06 1e 00 00 00 20 aa 80 4b 53 a8 4b |...,..... ..KS.K|
00000010 4c 0f fa ac a3 b8 5f 64 7d 36 42 e7 1d 56 45 7e |L....._d}6B..VE~|
00000020 2b ac e0 f9 e7 60 f5 d7 55 37 b8 cc 87 3c 23 dc |+....`..U7...<#.|
00000000 00 00 00 2c 06 1e 00 00 00 20 13 87 32 34 3e 68 |...,..... ..24>h|
00000010 e6 9b 9a cb 23 22 06 11 e8 24 71 e5 d7 96 79 83 |....#"...$q...y.|
00000020 48 59 1a 95 f2 b0 86 1c 76 54 58 e8 a3 63 9c a8 |HY......vTX..c..|
>>> Flow 6 (server to client)
00000000 00 00 01 04 0b 1f 00 00 00 68 00 00 00 13 65 63 |.........h....ec|
00000000 00 00 01 04 0a 1f 00 00 00 68 00 00 00 13 65 63 |.........h....ec|
00000010 64 73 61 2d 73 68 61 32 2d 6e 69 73 74 70 32 35 |dsa-sha2-nistp25|
00000020 36 00 00 00 08 6e 69 73 74 70 32 35 36 00 00 00 |6....nistp256...|
00000030 41 04 8b d1 dd c3 a2 af 65 c5 b1 7e 0d 88 0e 10 |A.......e..~....|
00000040 3b 52 4a 43 b7 3c ed e9 9a 89 5d 2b 05 74 b7 7e |;RJC.<....]+.t.~|
00000050 2b 1e 12 dd 2c 78 71 53 be eb f6 4e 5d 19 cf 98 |+...,xqS...N]...|
00000060 d0 25 2d 4a a3 4a 15 2c 50 10 67 80 6d 2e d9 fa |.%-J.J.,P.g.m...|
00000070 84 a8 00 00 00 20 e8 01 e7 b0 7b 47 62 2e ac 12 |..... ....{Gb...|
00000080 80 ba 70 5b a4 65 6a 25 50 1c 59 6c f3 12 ed 64 |..p[.ej%P.Yl...d|
00000090 2c f3 7a 3b 0e 53 00 00 00 63 00 00 00 13 65 63 |,.z;.S...c....ec|
00000070 84 a8 00 00 00 20 85 12 a8 ca dd 22 7d 74 89 87 |..... ....."}t..|
00000080 df 36 23 6e fb 59 c0 75 4b 38 1e 49 d6 fa 5e 8c |.6#n.Y.uK8.I..^.|
00000090 cd 6c eb fd 26 34 00 00 00 64 00 00 00 13 65 63 |.l..&4...d....ec|
000000a0 64 73 61 2d 73 68 61 32 2d 6e 69 73 74 70 32 35 |dsa-sha2-nistp25|
000000b0 36 00 00 00 48 00 00 00 1f 1f dd 4b cc 7e f3 af |6...H......K.~..|
000000c0 63 93 77 64 79 a8 77 02 96 98 cb 96 59 a6 a6 a2 |c.wdy.w.....Y...|
000000d0 9b 23 08 d0 f5 2e 13 04 00 00 00 21 00 e9 d8 dc |.#.........!....|
000000e0 27 de b1 e8 bc ee 5e ca 9e 62 20 09 d3 08 43 f6 |'.....^..b ...C.|
000000f0 d0 7d a1 6a f7 6d 64 64 81 ea 63 82 f8 00 00 00 |.}.j.mdd..c.....|
000000b0 36 00 00 00 49 00 00 00 21 00 f4 8e 10 b5 77 04 |6...I...!.....w.|
000000c0 3e 1b 59 06 4f ef 48 08 2b 15 07 4a 69 0f 30 a2 |>.Y.O.H.+..Ji.0.|
000000d0 a4 37 7b 5e f9 de 15 0a ca 1a 00 00 00 20 6a 97 |.7{^......... j.|
000000e0 a3 23 e3 7c 92 67 28 0d 02 9e 36 e0 ae b1 f3 37 |.#.|.g(...6....7|
000000f0 19 34 12 cd bf db 84 31 8e 97 72 a5 e1 3f 00 00 |.4.....1..r..?..|
00000100 00 00 00 00 00 00 00 00 00 00 00 0c 0a 15 00 00 |................|
00000110 00 00 00 00 00 00 00 00 00 00 01 40 62 ac b9 7c |...........@b..||
00000120 06 dc 0b 16 24 e2 56 cd b2 3a f9 51 ab e8 62 3e |....$.V..:.Q..b>|
00000130 72 a8 fa 95 2d af 2d d0 54 e1 de 08 a3 97 11 08 |r...-.-.T.......|
00000140 52 6b d5 dd 7e 81 be a5 f9 71 83 9e 68 f8 7e d1 |Rk..~....q..h.~.|
00000150 e8 22 8b b3 ff b2 2e 67 16 af d8 df 74 3f bf d6 |.".....g....t?..|
00000160 aa 46 3d 49 90 3d fa 12 6a 6f 75 98 1b b8 67 22 |.F=I.=..jou...g"|
00000170 d0 06 31 61 eb 29 aa 21 fb 7d e4 7e c7 ff b4 e5 |..1a.).!.}.~....|
00000180 a1 0f da 75 74 6f c0 45 31 3f e1 7a 29 d0 ff 25 |...uto.E1?.z)..%|
00000190 64 8c 49 5c c5 bf 9c 44 36 67 e1 86 5c e9 13 23 |d.I\...D6g..\..#|
000001a0 b8 d5 85 5d 33 4b 19 a5 fa bc 50 86 08 32 08 48 |...]3K....P..2.H|
000001b0 5e 0e 2e 68 9a ff 96 6a 00 51 d9 16 87 a6 88 c1 |^..h...j.Q......|
000001c0 dd b5 9c 18 3b fc 66 6a ff a7 60 f6 4a 29 f8 5d |....;.fj..`.J).]|
000001d0 fd 8d 5e 6a f0 1a 70 21 d6 4e c0 92 e3 95 96 b8 |..^j..p!.N......|
000001e0 fa 50 90 58 c5 59 6a 20 12 3d ce d4 49 0e da 2e |.P.X.Yj .=..I...|
000001f0 1d 2c b9 28 d1 71 52 49 43 25 90 24 b2 16 a9 58 |.,.(.qRIC%.$...X|
00000200 0f f3 07 94 92 83 8e 1a 3f 2a 92 a3 42 80 0e 3c |........?*..B..<|
00000210 42 7a 7c 65 3a f0 86 cf 40 49 9b f7 e6 a5 3d 36 |Bz|e:...@I....=6|
00000220 f6 ae db 6e 6c 39 cc 5e 47 f4 eb d2 c6 61 5e 61 |...nl9.^G....a^a|
00000230 e0 58 6c 16 13 c4 74 db e1 be 29 fc 84 be 42 22 |.Xl...t...)...B"|
00000240 14 72 ab 29 f1 ea 10 2c 34 1c 5e d2 a4 19 0d cb |.r.)...,4.^.....|
00000250 23 e0 d3 db 4d 5d 50 c7 a0 82 11 ed 1d 49 d3 4e |#...M]P......I.N|
00000260 32 be db 14 c1 fc 4b dc 97 3a 38 9b |2.....K..:8.|
00000110 00 00 00 00 00 00 00 00 00 00 01 40 03 fb e9 4c |...........@...L|
00000120 15 76 5c bf 4b d7 6a 8b 2a 83 6f df bd 5e f6 45 |.v\.K.j.*.o..^.E|
00000130 b2 2b bc bf 38 9f f0 30 13 e1 03 67 d3 4f e6 6f |.+..8..0...g.O.o|
00000140 88 97 39 8e a6 2d df 6f a7 d2 90 df c2 9a 87 93 |..9..-.o........|
00000150 ad 91 12 2e 92 c1 c4 bb 93 b7 7d 6e fd 7c c2 10 |..........}n.|..|
00000160 fe fa b6 22 7e a5 a7 64 c4 25 ce cc bb 5d a5 f0 |..."~..d.%...]..|
00000170 ad 41 c7 8c a1 59 c3 a9 23 f0 69 38 4a 64 2a a1 |.A...Y..#.i8Jd*.|
00000180 62 8d bc 69 1c c1 01 ce ac a9 30 58 a1 d0 68 92 |b..i......0X..h.|
00000190 35 e8 7b 3a bf 1b 68 82 f6 90 8e dd 01 e6 92 4b |5.{:..h........K|
000001a0 bc ce 50 a8 96 2a 7d a5 4d 61 13 98 67 83 11 70 |..P..*}.Ma..g..p|
000001b0 ef f2 af 94 a6 0c 34 78 05 a8 39 d3 44 f6 c4 13 |......4x..9.D...|
000001c0 76 59 77 38 72 83 c4 92 a7 4c 7f 5e 25 08 9b a0 |vYw8r....L.^%...|
000001d0 6f eb 02 4d 4d c6 0a 07 09 2d c6 80 d3 05 8b c2 |o..MM....-......|
000001e0 e7 de 37 16 5a db 1c 02 2e 35 fb 3a 93 ce 79 4e |..7.Z....5.:..yN|
000001f0 0c 25 05 c9 b7 be b7 47 31 8c 18 d8 37 ba 66 34 |.%.....G1...7.f4|
00000200 bb 03 15 0d 0d cc da ad 2d 2c 36 0f 88 3f 5a 70 |........-,6..?Zp|
00000210 21 5f f1 cc 8a 21 e7 5a af c7 7c a0 e3 51 04 4a |!_...!.Z..|..Q.J|
00000220 d3 cb bb a4 60 2f d1 28 7b 6f 52 10 e8 76 fa c2 |....`/.({oR..v..|
00000230 86 9b 08 3f b2 72 e5 f0 0a 7c e0 19 ce 6f d2 74 |...?.r...|...o.t|
00000240 3e 12 8f 7d 1e 21 73 25 57 b8 c9 96 ad 35 f3 b6 |>..}.!s%W....5..|
00000250 5f b0 ae 82 90 4b 05 10 de 11 46 ea ad d7 3b 08 |_....K....F...;.|
00000260 a6 0d b5 24 f6 e3 ae 3b 67 3c a0 44 |...$...;g<.D|
>>> Flow 7 (client to server)
00000000 00 00 00 0c 0a 15 62 b8 d2 60 16 9a fa 2f 75 ab |......b..`.../u.|
00000010 00 00 00 20 d9 dc 27 14 2c 6d 93 10 bd 87 c5 06 |... ..'.,m......|
00000020 90 80 21 40 13 e5 86 f4 39 f1 5b b5 75 ed 31 19 |..!@....9.[.u.1.|
00000030 87 05 83 a7 5f 28 81 02 d0 6e b4 f0 bd 89 0a e5 |...._(...n......|
00000040 fa 1e ac 07 |....|
00000000 00 00 00 0c 0a 15 a1 e3 f9 ae 57 e2 35 b8 cc 87 |..........W.5...|
00000010 00 00 00 20 ba d9 4a 00 05 48 df ec d8 c5 bf f8 |... ..J..H......|
00000020 7d ed c8 ac a8 5f 1c 46 a0 21 c3 26 7e c6 51 2d |}...._.F.!.&~.Q-|
00000030 b8 e8 12 50 38 3a 08 8c a8 0e 98 e7 8c 27 4f 4d |...P8:.......'OM|
00000040 76 42 94 1f |vB..|
>>> Flow 8 (server to client)
00000000 00 00 00 20 03 52 b6 aa 7d 30 65 ad 08 b8 dd d1 |... .R..}0e.....|
00000010 7f f0 3e b5 69 43 fa 0f 06 2a 18 5d 9d 4e b0 13 |..>.iC...*.].N..|
00000020 d8 4a 06 ca e1 d7 99 f9 59 14 d0 ef 0b a1 1a 47 |.J......Y......G|
00000030 8f c5 4b e5 |..K.|
00000000 00 00 00 20 53 c2 11 dc 60 cb f1 e0 55 eb f8 50 |... S...`...U..P|
00000010 38 11 aa 98 fc 4c 2a d1 36 0f 57 c2 c9 55 cb 04 |8....L*.6.W..U..|
00000020 dc 2f 79 36 28 5d fa 67 08 01 0a 1b fc a5 57 cd |./y6(].g......W.|
00000030 7f 67 ea 81 |.g..|
>>> Flow 9 (client to server)
00000000 00 00 00 30 34 a3 2c 36 49 de 9d 6c 4b 74 c4 0d |...04.,6I..lKt..|
00000010 2d 25 98 e4 6b 48 58 c5 e0 e6 04 90 31 11 59 4f |-%..kHX.....1.YO|
00000020 f0 33 8d e8 1a 9b e9 b5 02 fb 26 32 0e 5a 09 7b |.3........&2.Z.{|
00000030 5f 2a 80 dd 72 17 cd ed cb 54 ca 3b 50 44 ed 1f |_*..r....T.;PD..|
00000040 d4 4b b4 9b |.K..|
00000000 00 00 00 30 c5 dd dd 05 d4 df 9e 48 d6 58 76 a1 |...0.......H.Xv.|
00000010 8a 7e 04 c6 51 26 44 1a cb 85 8b 9a 51 44 bd 57 |.~..Q&D.....QD.W|
00000020 bc 4f 2e a0 0a b9 e0 ee 00 b6 57 d3 2f 52 71 8e |.O........W./Rq.|
00000030 b6 4e f6 f7 34 7c 9f fe c7 c7 ec ca 08 6f d9 b0 |.N..4|.......o..|
00000040 e3 85 81 49 |...I|
>>> Flow 10 (server to client)
00000000 00 00 00 20 7e c0 e9 9e 89 d7 16 2d 48 c0 e0 bf |... ~......-H...|
00000010 dd c3 89 1e 08 3e ee ab 73 77 77 2e 5a 62 14 02 |.....>..sww.Zb..|
00000020 6a 7f c2 e9 19 c0 df 69 43 60 82 d6 ea ff da f0 |j......iC`......|
00000030 fa 40 2b b6 00 00 00 40 3a 06 de c5 ac 63 46 77 |.@+....@:....cFw|
00000040 a8 8e a1 d0 be 35 cb 27 34 d1 63 a1 0c b2 c3 30 |.....5.'4.c....0|
00000050 c6 d8 bd 92 7a f7 48 4b 95 a5 5f 6e 4e ca 6f 60 |....z.HK.._nN.o`|
00000060 32 11 4d d0 15 2e 31 22 03 1a 89 fa 91 e0 ea 10 |2.M...1"........|
00000070 7b e4 4e d8 a8 a7 a4 35 3c b0 76 39 6e e9 ef c9 |{.N....5<.v9n...|
00000080 69 02 c8 01 51 bc fb ad |i...Q...|
00000000 00 00 00 20 4e d8 10 08 39 ab 47 f6 49 42 4e bd |... N...9.G.IBN.|
00000010 2f 69 f7 bc 48 26 1b 5f ee ee 08 25 2e b8 8d 1f |/i..H&._...%....|
00000020 a0 e0 fa 11 28 26 9c af f0 6b 7e e0 f0 00 8f 92 |....(&...k~.....|
00000030 08 96 73 64 00 00 00 40 60 bb 40 d2 c4 8f 5e 09 |..sd...@`.@...^.|
00000040 e6 d9 13 04 fc 4e 21 81 51 dc ad 06 48 51 fc 35 |.....N!.Q...HQ.5|
00000050 b2 cd bb ba df 66 c4 20 b1 c0 b0 7e 7b c0 a7 41 |.....f. ...~{..A|
00000060 6f 16 7a fe 01 fe 3a be 45 4a b9 94 d1 46 a1 82 |o.z...:.EJ...F..|
00000070 09 24 97 57 17 83 9e e6 1b 4e 04 83 a0 73 55 08 |.$.W.....N...sU.|
00000080 2f e6 de 6b 43 77 84 31 |/..kCw.1|
>>> Flow 11 (client to server)
00000000 00 00 01 60 3a fe b0 e3 19 9e ef b8 4b 47 ac 18 |...`:.......KG..|
00000010 52 0f 93 e5 04 69 58 d5 51 b9 82 ab 56 41 5c 73 |R....iX.Q...VA\s|
00000020 e2 e7 d1 98 41 f2 73 30 13 b5 7d ca 0e 0d 78 7b |....A.s0..}...x{|
00000030 01 83 ab e7 ce 1d 3b 8c 14 ed c5 0d 39 cf 82 f3 |......;.....9...|
00000040 f6 eb 9e 17 c4 36 61 78 5b 50 66 2d 8c 37 cf fb |.....6ax[Pf-.7..|
00000050 42 44 45 fc 94 a8 e2 d2 9e 5e 5f 12 85 f2 4a 46 |BDE......^_...JF|
00000060 50 fc 5c 04 80 2e 17 ce 7e f2 a6 ee 94 7c e7 99 |P.\.....~....|..|
00000070 6c a3 b3 5d 08 23 f6 10 6f 79 8b a0 56 20 d3 ed |l..].#..oy..V ..|
00000080 e0 44 07 fb dc d3 b6 64 5b c2 d4 66 77 3e 39 a2 |.D.....d[..fw>9.|
00000090 57 4b 44 95 96 33 4b 51 51 40 3f f0 9a db 95 96 |WKD..3KQQ@?.....|
000000a0 41 3c f2 d6 7c d3 bb 6b a5 b1 b3 67 0b 94 38 e1 |A<..|..k...g..8.|
000000b0 4d e5 68 68 f4 e0 93 57 d7 01 7b 4d 9c 8c 18 32 |M.hh...W..{M...2|
000000c0 44 3a 93 7b 38 3b 19 0f 63 98 04 15 22 1e ce 09 |D:.{8;..c..."...|
000000d0 db f5 80 2e 42 bb 22 59 9d 04 05 7e 67 32 c8 52 |....B."Y...~g2.R|
000000e0 9f 2d d0 02 e9 ad d0 7f 1d b3 f8 64 f2 4b ff 03 |.-.........d.K..|
000000f0 56 6b 28 b5 bb 2e 3c eb 47 5b 95 96 26 64 8a 8e |Vk(...<.G[..&d..|
00000100 96 e6 fe bd ee 71 e7 53 6b 27 a6 b7 38 ba 8c 5b |.....q.Sk'..8..[|
00000110 14 14 08 ec 59 e7 a5 42 b4 0e 0e 39 09 c1 25 bc |....Y..B...9..%.|
00000120 6f 9b 94 85 14 ae 76 6c 94 f2 58 d5 ad 73 01 fc |o.....vl..X..s..|
00000130 f1 6c 0d e7 13 f1 9b 86 54 33 b3 d2 c0 78 3e 9b |.l......T3...x>.|
00000140 fb aa e2 ce c8 1f 3e f5 19 2e 22 78 fb 02 4d ab |......>..."x..M.|
00000150 49 52 3e 17 6c 22 a7 4a 19 aa 68 a1 12 d6 24 4e |IR>.l".J..h...$N|
00000160 e7 0d 39 cc a5 1a 90 f6 26 15 75 9f 80 88 74 11 |..9.....&.u...t.|
00000170 9f d8 2b 2e |..+.|
00000000 00 00 01 60 f2 7d b2 4f d9 db ec 78 6b a2 c2 77 |...`.}.O...xk..w|
00000010 6a a1 f8 d0 3c 5c e0 3b d3 99 33 ac 54 4b f7 e2 |j...<\.;..3.TK..|
00000020 3d 1f 6e 51 6f 4c c2 7e d5 3a 79 1e 9f 51 70 75 |=.nQoL.~.:y..Qpu|
00000030 67 35 8c f8 5d 62 35 9a ff fe b2 46 6f 72 47 0b |g5..]b5....ForG.|
00000040 5b 78 30 5b ce 54 5c ab 46 a4 a5 12 8b ef 32 2e |[x0[.T\.F.....2.|
00000050 1e a9 4a 25 07 9e a3 87 42 f9 69 ae c6 9d 83 68 |..J%....B.i....h|
00000060 e8 dd 19 a3 ef 4e 97 c9 ef 19 be a7 08 77 28 7d |.....N.......w(}|
00000070 ee aa b4 a4 3a e5 c1 4e 78 ee b2 b7 50 6d f2 eb |....:..Nx...Pm..|
00000080 2b df 38 22 a3 c9 be f7 ea 36 2f 7d f7 25 fd f4 |+.8".....6/}.%..|
00000090 9f 54 ec cf d5 82 5c 4b e5 e0 3f 74 da 9d d0 32 |.T....\K..?t...2|
000000a0 06 f6 a1 13 15 f4 11 24 e9 74 4c 9b 5e e2 c8 5b |.......$.tL.^..[|
000000b0 32 6b 89 87 d4 d4 ee e3 0c 81 16 d4 54 b7 97 ef |2k..........T...|
000000c0 c6 a4 d7 16 2d 87 be 2c 19 b8 48 9d d6 82 3c 82 |....-..,..H...<.|
000000d0 88 de bc 18 5b 64 ab aa d7 ac 8b 64 6a ad a8 4d |....[d.....dj..M|
000000e0 5a 20 a3 6e ee 7b 7f bf dc 6d 69 6a b0 8b ee 26 |Z .n.{...mij...&|
000000f0 0b 34 f0 22 a6 81 0f d3 b5 15 e8 86 1b 71 1a 60 |.4.".........q.`|
00000100 16 dc 2f 6b ca 63 6c 52 b3 33 42 46 c0 ea 63 50 |../k.clR.3BF..cP|
00000110 bb 23 29 69 79 0c 23 3f fb da 29 dd 94 44 f1 7e |.#)iy.#?..)..D.~|
00000120 58 1c 12 0f df a7 80 3c 69 8a 44 f7 4d 2e 7b db |X......<i.D.M.{.|
00000130 c2 5a 01 c0 0c 57 8e ff 4f 98 74 71 c3 3b 61 fc |.Z...W..O.tq.;a.|
00000140 1b e6 fb 4c 75 33 ff 6b 0e 83 52 40 4a 7f 35 2f |...Lu3.k..R@J.5/|
00000150 82 38 40 22 22 bc 51 6e 21 ce f9 bb 02 bf 26 a5 |.8@"".Qn!.....&.|
00000160 63 9b 91 c3 37 46 6d f6 fe 19 27 5f 91 68 a7 29 |c...7Fm...'_.h.)|
00000170 b7 00 27 37 |..'7|
>>> Flow 12 (server to client)
00000000 00 00 01 40 07 30 22 23 53 70 62 fd 53 15 21 49 |...@.0"#Spb.S.!I|
00000010 b7 ff 50 fc c0 4e 4d 8f ef 4b 09 8a 9f e0 0e de |..P..NM..K......|
00000020 de 3d 06 cf e2 8c f1 cc 92 c4 84 78 6f 1e 4a cd |.=.........xo.J.|
00000030 8b 8a 61 a9 ff 6e 3c c2 3c d0 0e 70 5d 6e 4e 40 |..a..n<.<..p]nN@|
00000040 c0 8a 4f 3b e4 25 05 15 21 63 09 fd f7 fd ee d9 |..O;.%..!c......|
00000050 92 10 e1 e6 6a 27 ae ff fe 1e 66 7a 00 e7 27 95 |....j'....fz..'.|
00000060 d2 fe 32 5f f8 79 89 ea 25 19 62 44 04 aa 84 8c |..2_.y..%.bD....|
00000070 2b 46 b4 73 e3 4f 9b d9 bd 39 7d 13 91 5c 55 44 |+F.s.O...9}..\UD|
00000080 70 67 8e 50 5b 12 23 b4 39 82 cd 15 ac 77 91 bf |pg.P[.#.9....w..|
00000090 4d ea d7 4c e2 ef 7b 0a f6 4e 77 aa 47 49 fd a2 |M..L..{..Nw.GI..|
000000a0 bd ae 0e 06 d8 23 df f2 c7 62 8a c0 13 24 3e 30 |.....#...b...$>0|
000000b0 5d ba 3e e1 3f 5d 80 34 54 ab e0 00 7c 61 5b 17 |].>.?].4T...|a[.|
000000c0 23 c6 7e c6 d2 d1 01 0a 32 a5 d5 85 3e ec dd a8 |#.~.....2...>...|
000000d0 13 dc 52 e8 c4 85 56 04 3d 98 2d 72 6a f2 bb 52 |..R...V.=.-rj..R|
000000e0 ae f0 86 ff 99 9e a9 b9 23 4c 27 65 41 5b 27 37 |........#L'eA['7|
000000f0 93 ee ac 33 f8 c9 23 e9 3e 5a 4d d3 58 51 e8 e4 |...3..#.>ZM.XQ..|
00000100 7e 3f 45 d3 d0 02 ab 5b 9d 12 5d 16 34 7b 01 dd |~?E....[..].4{..|
00000110 fb 77 cf 24 55 f5 0d 00 56 9c 94 4c 96 af eb 68 |.w.$U...V..L...h|
00000120 af 80 d8 20 8a 7f 26 6b ec 47 64 0c 9d 05 9c 8f |... ..&k.Gd.....|
00000130 93 3a 2c 89 81 ae 37 6f f5 53 43 19 cf 7d c7 95 |.:,...7o.SC..}..|
00000140 6d f1 25 ff c1 8f 26 5b 4f b1 a0 99 cf 29 b7 cc |m.%...&[O....)..|
00000150 72 b2 49 83 |r.I.|
00000000 00 00 01 40 ed 8c ad 60 63 f2 de d8 77 4e ea e5 |...@...`c...wN..|
00000010 7f 0e e7 65 d5 07 51 66 b5 f6 60 1f ba f0 00 59 |...e..Qf..`....Y|
00000020 ed b3 87 97 9f 52 71 a1 60 9b 3b ef 16 5c 69 2b |.....Rq.`.;..\i+|
00000030 73 1b 35 01 61 52 d6 36 50 5b 61 04 da e3 eb 2f |s.5.aR.6P[a..../|
00000040 b1 5f e4 b2 20 fa f9 52 76 ff 90 66 fe 69 98 26 |._.. ..Rv..f.i.&|
00000050 c2 72 9f 93 aa e3 bb 9a b4 28 62 e3 ea 7f 94 c6 |.r.......(b.....|
00000060 92 e3 2a 81 99 6c 08 f1 df 99 dc d1 a0 cd d0 e6 |..*..l..........|
00000070 31 21 7b d4 c0 e7 85 66 2c 3f 0e 9e 8f 8b 49 11 |1!{....f,?....I.|
00000080 64 6b 61 c4 39 15 d2 c0 83 76 f5 cf f5 5d 0c dc |dka.9....v...]..|
00000090 03 37 22 a7 fa f5 3c 8b b2 a4 2f 1b c3 57 7f 9a |.7"...<.../..W..|
000000a0 4b eb 80 dc e2 17 fd 8f 55 90 b9 03 2d 7f 1e e8 |K.......U...-...|
000000b0 5a 2b 08 57 76 a1 c7 7b 46 97 68 6e af b4 42 8a |Z+.Wv..{F.hn..B.|
000000c0 ea 13 b1 d7 7a 88 c1 f1 9d 45 ec 41 aa ed ef a0 |....z....E.A....|
000000d0 40 3b b8 74 e4 80 88 a9 6a bf d4 a8 9c 27 3d 21 |@;.t....j....'=!|
000000e0 65 8f 28 8a e5 4e 33 a7 fd 9b 78 08 07 ee d0 cf |e.(..N3...x.....|
000000f0 51 f9 b7 5c e3 6c cb 07 5a 74 55 f6 0f ef 42 2b |Q..\.l..ZtU...B+|
00000100 da 28 ac 34 e7 70 64 9b 4d 35 dd a0 d6 94 4f 48 |.(.4.pd.M5....OH|
00000110 d0 91 d5 6b 36 64 9e ce 88 c7 07 9b b7 c4 2e e3 |...k6d..........|
00000120 3b a9 0e 2e b2 63 f6 f6 0c 38 48 71 a0 a4 de 1b |;....c...8Hq....|
00000130 42 61 82 1e d8 1f d0 6b 83 71 ff e8 d5 c6 bc 7b |Ba.....k.q.....{|
00000140 ee 53 5b 87 b5 38 cf 07 5d 73 6e 59 cb d5 d7 d6 |.S[..8..]snY....|
00000150 bc 76 4e e8 |.vN.|
>>> Flow 13 (client to server)
00000000 00 00 02 80 28 72 8e 75 6f ff ec ad 60 fe ff b9 |....(r.uo...`...|
00000010 e9 92 32 54 9f 41 16 05 7d ea 7d 65 36 d9 90 d1 |..2T.A..}.}e6...|
00000020 e1 0b 5d 7c c9 86 08 de 9b fb 7d 45 6f 08 3f b0 |..]|......}Eo.?.|
00000030 74 54 75 53 0f d2 03 fa d8 d4 d7 98 db e3 ed 2c |tTuS...........,|
00000040 76 21 10 53 84 ed 08 53 98 50 5b 93 38 cd 82 35 |v!.S...S.P[.8..5|
00000050 f3 f6 06 cb fd 67 06 4f 4c a9 86 1a bd 27 40 77 |.....g.OL....'@w|
00000060 87 04 da b8 d5 3a bc c6 18 4b 41 54 d4 bc fc 49 |.....:...KAT...I|
00000070 26 e0 4b e1 e9 3b 1a 05 c9 22 94 8e 52 40 30 1d |&.K..;..."..R@0.|
00000080 bb 53 c6 96 2a 38 24 3b e1 23 4d 9f 53 27 b1 3b |.S..*8$;.#M.S'.;|
00000090 39 49 89 a8 d3 02 59 90 fa ee 6f e1 63 d1 6a 86 |9I....Y...o.c.j.|
000000a0 c0 c0 f0 78 2f 5c d7 19 42 59 9d 72 03 96 f8 cd |...x/\..BY.r....|
000000b0 d1 4f 0a 7f c2 e7 cb 87 6e 9a 68 45 05 03 b5 66 |.O......n.hE...f|
000000c0 4f d1 de 9b 97 b4 79 d3 ad b9 b9 ab 49 9a fb 38 |O.....y.....I..8|
000000d0 1e cc bb ce 8a 14 f5 0c 4b b5 6e ce 87 18 d0 bd |........K.n.....|
000000e0 4f 57 28 bf a5 06 1a d2 85 92 61 45 d8 38 c4 5b |OW(.......aE.8.[|
000000f0 6e 0e 65 64 4e e9 dc f6 a4 2c 1c 31 c5 d9 28 fd |n.edN....,.1..(.|
00000100 71 19 c3 2b f0 23 6f f7 c8 2a 6e 4d 1d 5c 93 94 |q..+.#o..*nM.\..|
00000110 ff 9b 5f 33 7f 6c 18 e2 de f4 08 ea 99 ac 83 39 |.._3.l.........9|
00000120 25 6c 16 eb 7f da 70 f8 9e 89 76 5f 4c b1 86 6f |%l....p...v_L..o|
00000130 0e 0e cb 62 af dc 35 e4 20 83 7e e3 37 ca 67 2e |...b..5. .~.7.g.|
00000140 9c e3 97 e3 d0 09 fd 6a 1c 70 e0 ca c8 a4 1e 9d |.......j.p......|
00000150 67 10 07 ef ec 98 c9 7e c2 b2 7f f6 ba 30 34 e0 |g......~.....04.|
00000160 a7 1c da 9b cf f5 5b 11 5a 43 97 47 ad e6 d7 da |......[.ZC.G....|
00000170 28 4a 17 92 58 1a ae a7 c2 29 2c 29 5c c4 fd 41 |(J..X....),)\..A|
00000180 43 99 27 dc a9 5a 06 fd d8 50 36 95 73 42 8a e6 |C.'..Z...P6.sB..|
00000190 a9 9b 66 a6 d4 b6 56 4b 3e b7 a5 cb f4 82 e6 93 |..f...VK>.......|
000001a0 40 e6 f2 0c ed d5 d0 bf be 30 5a 3c c2 18 9f 8d |@........0Z<....|
000001b0 35 f8 d8 26 60 a4 84 d8 3b 22 96 ad 58 6e 62 15 |5..&`...;"..Xnb.|
000001c0 aa 23 66 4b d7 78 63 7a 95 02 41 45 9d 70 47 ef |.#fK.xcz..AE.pG.|
000001d0 54 0e 60 61 9a 18 f9 6e 1e e4 35 55 ef cf d2 33 |T.`a...n..5U...3|
000001e0 92 0f 28 a2 74 81 c1 62 4e 14 a3 6a 4b b1 d7 32 |..(.t..bN..jK..2|
000001f0 95 3a e8 4b 00 61 19 3a 57 a0 a0 a6 a2 85 3d d9 |.:.K.a.:W.....=.|
00000200 47 5f 79 26 c6 21 c3 94 27 c2 9e 57 57 2f 43 1a |G_y&.!..'..WW/C.|
00000210 5e ea da 9b c3 69 29 90 d4 17 63 41 4b 24 cc ed |^....i)...cAK$..|
00000220 37 3c d2 d3 4a 87 29 25 4a 83 03 0f 48 9e 86 80 |7<..J.)%J...H...|
00000230 54 e1 01 df 83 8c 83 49 c1 18 ff a8 22 12 73 da |T......I....".s.|
00000240 e9 d7 d1 76 04 74 f3 eb 6e f8 15 07 92 21 67 28 |...v.t..n....!g(|
00000250 b9 9f 07 71 e5 83 78 1f 27 6e 92 82 48 7b 70 ce |...q..x.'n..H{p.|
00000260 46 d9 e3 6a e2 9c 77 cf 45 e3 5f e5 ab 44 ca 51 |F..j..w.E._..D.Q|
00000270 ea 40 81 4a 85 89 0d 7c ab 4d 09 81 2e d7 7d 3e |.@.J...|.M....}>|
00000280 fc 43 ca ad 9f 4f fc f4 52 cb 4e 40 97 9f e4 3d |.C...O..R.N@...=|
00000290 ef 02 58 d5 |..X.|
00000000 00 00 02 80 62 66 4c ce e6 ed b9 56 ed 11 4a 46 |....bfL....V..JF|
00000010 93 45 c7 16 71 c0 e2 c4 fc bf 06 58 fb 20 3d ca |.E..q......X. =.|
00000020 2f 85 8f 6f da 51 9a 0d 90 3e 0b e0 af d5 00 5d |/..o.Q...>.....]|
00000030 f2 17 db fb 3c e9 4a 93 df a7 f2 a9 e1 d0 65 33 |....<.J.......e3|
00000040 18 f2 45 09 28 4e eb d2 58 22 b7 9e a9 df b4 48 |..E.(N..X".....H|
00000050 8e 60 e0 b3 b6 78 e9 e5 1c 37 26 1a a1 82 4b e8 |.`...x...7&...K.|
00000060 c3 9d 06 69 f5 13 59 18 2a 78 f6 2c 36 f0 96 0a |...i..Y.*x.,6...|
00000070 99 c5 f0 90 de 38 c4 58 b7 0d 42 0f e1 5d 11 2f |.....8.X..B..]./|
00000080 1f 56 da d6 d9 94 86 68 df bb 4b df ce ea 31 86 |.V.....h..K...1.|
00000090 32 cc 7f 50 75 ac 03 f3 48 c1 7b 58 7f db f8 3b |2..Pu...H.{X...;|
000000a0 5e 02 79 9d 59 38 9d 7f 7c ee 24 8b a1 22 92 a0 |^.y.Y8..|.$.."..|
000000b0 a1 52 2a 09 3a a1 b8 fb 3e be 40 63 7d af cf 21 |.R*.:...>.@c}..!|
000000c0 c8 a0 b6 86 94 af d8 bc 21 3b 83 77 55 c4 0a d0 |........!;.wU...|
000000d0 8e ea 4b 19 04 e7 88 2d 6f ff 9f 79 32 8a 7a 7d |..K....-o..y2.z}|
000000e0 20 09 9b 52 05 d7 a2 95 53 0d 7b 57 b0 92 be 0e | ..R....S.{W....|
000000f0 52 c2 0e c2 71 a4 d2 67 f5 80 e9 89 7b 6c d1 ea |R...q..g....{l..|
00000100 7c 83 f1 af 13 3e b6 69 62 e7 10 ed 75 c8 f8 cf ||....>.ib...u...|
00000110 60 07 d8 9a 46 e6 4f b4 22 f2 f7 ca ba ed 36 f9 |`...F.O.".....6.|
00000120 f3 ac a9 e0 f3 e5 37 b0 b0 7d 23 ed b9 7f 1f b3 |......7..}#.....|
00000130 57 38 7c c3 34 b0 78 a5 e8 29 a9 cb 35 17 68 4d |W8|.4.x..)..5.hM|
00000140 dd 8f 35 9c f1 b5 e6 e6 29 00 3b 1c ac 5d d0 cd |..5.....).;..]..|
00000150 1f a0 a8 a1 25 b0 97 53 ed 9a 72 a8 f0 61 0b 66 |....%..S..r..a.f|
00000160 2c 67 c9 e1 dd 86 6a 14 54 00 7b 90 7e 3c d2 5c |,g....j.T.{.~<.\|
00000170 d8 d3 a2 a0 cd c9 b2 82 8f 48 a0 22 49 63 54 6d |.........H."IcTm|
00000180 9f 04 f9 47 27 db 4f 3d 41 82 4a 1a be 4a b7 2f |...G'.O=A.J..J./|
00000190 81 c5 ad 12 cc 1e 45 f8 bb 42 93 ea 4b 24 97 44 |......E..B..K$.D|
000001a0 6b c4 4c 4d f4 61 08 0c a3 37 20 48 d3 be 47 71 |k.LM.a...7 H..Gq|
000001b0 1b b2 8a a1 eb 9f b9 17 61 07 64 15 29 6e fc 93 |........a.d.)n..|
000001c0 7d 21 fd 7d f2 09 6b 9c 5f 05 79 2b dc e0 c6 d4 |}!.}..k._.y+....|
000001d0 03 d4 9e 23 f2 3e 4b 23 11 5e 58 ec a8 5e 96 63 |...#.>K#.^X..^.c|
000001e0 6d 51 22 d5 3e e3 de ee 1a c5 d5 85 c3 c5 c4 05 |mQ".>...........|
000001f0 38 4c bd 54 b4 1e 6a 64 5a 9a 3e 48 82 ec e7 fe |8L.T..jdZ.>H....|
00000200 6a 67 a5 30 c0 fc fd c6 7e d4 d0 68 a7 30 40 e0 |jg.0....~..h.0@.|
00000210 87 c1 5d 75 0b a0 4f 90 f7 46 51 18 8b 02 36 c5 |..]u..O..FQ...6.|
00000220 fb b5 54 bd 32 75 dc 63 84 6f 61 a5 36 fe a6 23 |..T.2u.c.oa.6..#|
00000230 f8 a5 b9 cc af 00 e4 83 c0 11 50 ad 81 2b e2 bd |..........P..+..|
00000240 20 08 85 11 40 5e 66 25 d8 ac 06 a3 a2 10 dc 52 | ...@^f%.......R|
00000250 7a d5 13 61 82 b5 48 83 02 32 7b 20 96 78 40 14 |z..a..H..2{ .x@.|
00000260 38 10 9f 88 25 d3 60 3c 7e e4 11 da a9 6e 1e 58 |8...%.`<~....n.X|
00000270 13 de d8 d4 74 78 b2 5f 8c ce 9c ce 33 5c 1a 6e |....tx._....3\.n|
00000280 76 56 49 a4 54 0c ab 20 b9 c6 20 d4 fa b8 6f 41 |vVI.T.. .. ...oA|
00000290 81 1a 7e f4 |..~.|
>>> Flow 14 (server to client)
00000000 00 00 00 10 bc 56 ff 49 8f 1b c2 79 5b 65 67 84 |.....V.I...y[eg.|
00000010 fb 19 0c 51 ef 4f 2c e8 58 01 84 90 5c 31 78 d4 |...Q.O,.X...\1x.|
00000020 ab 3e cb 21 |.>.!|
00000000 00 00 00 10 6e 70 e8 37 1b 64 e5 98 df 83 52 b2 |....np.7.d....R.|
00000010 40 ca 82 24 96 8c 21 c4 df d2 ec a8 d8 c7 b1 48 |@..$..!........H|
00000020 c2 48 90 90 |.H..|
+258 -257
View File
@@ -2,54 +2,55 @@
00000000 53 53 48 2d 32 2e 30 2d 47 6f 0d 0a |SSH-2.0-Go..|
>>> Flow 2 (client to server)
00000000 53 53 48 2d 32 2e 30 2d 4f 70 65 6e 53 53 48 5f |SSH-2.0-OpenSSH_|
00000010 39 2e 39 0d 0a |9.9..|
00000010 31 30 2e 30 0d 0a |10.0..|
>>> Flow 3 (server to client)
00000000 00 00 02 5c 12 14 7f 9c 2b a4 e8 8f 82 7d 61 60 |...\....+....}a`|
00000010 45 50 76 05 85 3e 00 00 00 2e 63 75 72 76 65 32 |EPv..>....curve2|
00000020 35 35 31 39 2d 73 68 61 32 35 36 2c 6b 65 78 2d |5519-sha256,kex-|
00000030 73 74 72 69 63 74 2d 73 2d 76 30 30 40 6f 70 65 |strict-s-v00@ope|
00000040 6e 73 73 68 2e 63 6f 6d 00 00 00 21 72 73 61 2d |nssh.com...!rsa-|
00000050 73 68 61 32 2d 32 35 36 2c 72 73 61 2d 73 68 61 |sha2-256,rsa-sha|
00000060 32 2d 35 31 32 2c 73 73 68 2d 72 73 61 00 00 00 |2-512,ssh-rsa...|
00000070 6c 61 65 73 31 32 38 2d 67 63 6d 40 6f 70 65 6e |laes128-gcm@open|
00000080 73 73 68 2e 63 6f 6d 2c 61 65 73 32 35 36 2d 67 |ssh.com,aes256-g|
00000090 63 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 63 |cm@openssh.com,c|
000000a0 68 61 63 68 61 32 30 2d 70 6f 6c 79 31 33 30 35 |hacha20-poly1305|
000000b0 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 61 65 73 |@openssh.com,aes|
000000c0 31 32 38 2d 63 74 72 2c 61 65 73 31 39 32 2d 63 |128-ctr,aes192-c|
000000d0 74 72 2c 61 65 73 32 35 36 2d 63 74 72 00 00 00 |tr,aes256-ctr...|
000000e0 6c 61 65 73 31 32 38 2d 67 63 6d 40 6f 70 65 6e |laes128-gcm@open|
000000f0 73 73 68 2e 63 6f 6d 2c 61 65 73 32 35 36 2d 67 |ssh.com,aes256-g|
00000100 63 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 63 |cm@openssh.com,c|
00000110 68 61 63 68 61 32 30 2d 70 6f 6c 79 31 33 30 35 |hacha20-poly1305|
00000120 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 61 65 73 |@openssh.com,aes|
00000130 31 32 38 2d 63 74 72 2c 61 65 73 31 39 32 2d 63 |128-ctr,aes192-c|
00000140 74 72 2c 61 65 73 32 35 36 2d 63 74 72 00 00 00 |tr,aes256-ctr...|
00000150 6e 68 6d 61 63 2d 73 68 61 32 2d 32 35 36 2d 65 |nhmac-sha2-256-e|
00000160 74 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 |tm@openssh.com,h|
00000170 6d 61 63 2d 73 68 61 32 2d 35 31 32 2d 65 74 6d |mac-sha2-512-etm|
00000180 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 6d 61 |@openssh.com,hma|
00000190 63 2d 73 68 61 32 2d 32 35 36 2c 68 6d 61 63 2d |c-sha2-256,hmac-|
000001a0 73 68 61 32 2d 35 31 32 2c 68 6d 61 63 2d 73 68 |sha2-512,hmac-sh|
000001b0 61 31 2c 68 6d 61 63 2d 73 68 61 31 2d 39 36 00 |a1,hmac-sha1-96.|
000001c0 00 00 6e 68 6d 61 63 2d 73 68 61 32 2d 32 35 36 |..nhmac-sha2-256|
000001d0 2d 65 74 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d |-etm@openssh.com|
000001e0 2c 68 6d 61 63 2d 73 68 61 32 2d 35 31 32 2d 65 |,hmac-sha2-512-e|
000001f0 74 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 |tm@openssh.com,h|
00000200 6d 61 63 2d 73 68 61 32 2d 32 35 36 2c 68 6d 61 |mac-sha2-256,hma|
00000210 63 2d 73 68 61 32 2d 35 31 32 2c 68 6d 61 63 2d |c-sha2-512,hmac-|
00000220 73 68 61 31 2c 68 6d 61 63 2d 73 68 61 31 2d 39 |sha1,hmac-sha1-9|
00000230 36 00 00 00 04 6e 6f 6e 65 00 00 00 04 6e 6f 6e |6....none....non|
00000240 65 00 00 00 00 00 00 00 00 00 00 00 00 00 d7 3b |e..............;|
00000250 80 93 f6 ef bc 88 eb 1a 6e ac fa 66 ef 26 3c b1 |........n..f.&<.|
00000000 00 00 02 6c 05 14 7f 9c 2b a4 e8 8f 82 7d 61 60 |...l....+....}a`|
00000010 45 50 76 05 85 3e 00 00 00 4b 63 75 72 76 65 32 |EPv..>...Kcurve2|
00000020 35 35 31 39 2d 73 68 61 32 35 36 2c 63 75 72 76 |5519-sha256,curv|
00000030 65 32 35 35 31 39 2d 73 68 61 32 35 36 40 6c 69 |e25519-sha256@li|
00000040 62 73 73 68 2e 6f 72 67 2c 6b 65 78 2d 73 74 72 |bssh.org,kex-str|
00000050 69 63 74 2d 73 2d 76 30 30 40 6f 70 65 6e 73 73 |ict-s-v00@openss|
00000060 68 2e 63 6f 6d 00 00 00 21 72 73 61 2d 73 68 61 |h.com...!rsa-sha|
00000070 32 2d 32 35 36 2c 72 73 61 2d 73 68 61 32 2d 35 |2-256,rsa-sha2-5|
00000080 31 32 2c 73 73 68 2d 72 73 61 00 00 00 6c 61 65 |12,ssh-rsa...lae|
00000090 73 31 32 38 2d 67 63 6d 40 6f 70 65 6e 73 73 68 |s128-gcm@openssh|
000000a0 2e 63 6f 6d 2c 61 65 73 32 35 36 2d 67 63 6d 40 |.com,aes256-gcm@|
000000b0 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 63 68 61 63 |openssh.com,chac|
000000c0 68 61 32 30 2d 70 6f 6c 79 31 33 30 35 40 6f 70 |ha20-poly1305@op|
000000d0 65 6e 73 73 68 2e 63 6f 6d 2c 61 65 73 31 32 38 |enssh.com,aes128|
000000e0 2d 63 74 72 2c 61 65 73 31 39 32 2d 63 74 72 2c |-ctr,aes192-ctr,|
000000f0 61 65 73 32 35 36 2d 63 74 72 00 00 00 6c 61 65 |aes256-ctr...lae|
00000100 73 31 32 38 2d 67 63 6d 40 6f 70 65 6e 73 73 68 |s128-gcm@openssh|
00000110 2e 63 6f 6d 2c 61 65 73 32 35 36 2d 67 63 6d 40 |.com,aes256-gcm@|
00000120 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 63 68 61 63 |openssh.com,chac|
00000130 68 61 32 30 2d 70 6f 6c 79 31 33 30 35 40 6f 70 |ha20-poly1305@op|
00000140 65 6e 73 73 68 2e 63 6f 6d 2c 61 65 73 31 32 38 |enssh.com,aes128|
00000150 2d 63 74 72 2c 61 65 73 31 39 32 2d 63 74 72 2c |-ctr,aes192-ctr,|
00000160 61 65 73 32 35 36 2d 63 74 72 00 00 00 6e 68 6d |aes256-ctr...nhm|
00000170 61 63 2d 73 68 61 32 2d 32 35 36 2d 65 74 6d 40 |ac-sha2-256-etm@|
00000180 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 6d 61 63 |openssh.com,hmac|
00000190 2d 73 68 61 32 2d 35 31 32 2d 65 74 6d 40 6f 70 |-sha2-512-etm@op|
000001a0 65 6e 73 73 68 2e 63 6f 6d 2c 68 6d 61 63 2d 73 |enssh.com,hmac-s|
000001b0 68 61 32 2d 32 35 36 2c 68 6d 61 63 2d 73 68 61 |ha2-256,hmac-sha|
000001c0 32 2d 35 31 32 2c 68 6d 61 63 2d 73 68 61 31 2c |2-512,hmac-sha1,|
000001d0 68 6d 61 63 2d 73 68 61 31 2d 39 36 00 00 00 6e |hmac-sha1-96...n|
000001e0 68 6d 61 63 2d 73 68 61 32 2d 32 35 36 2d 65 74 |hmac-sha2-256-et|
000001f0 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 6d |m@openssh.com,hm|
00000200 61 63 2d 73 68 61 32 2d 35 31 32 2d 65 74 6d 40 |ac-sha2-512-etm@|
00000210 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 6d 61 63 |openssh.com,hmac|
00000220 2d 73 68 61 32 2d 32 35 36 2c 68 6d 61 63 2d 73 |-sha2-256,hmac-s|
00000230 68 61 32 2d 35 31 32 2c 68 6d 61 63 2d 73 68 61 |ha2-512,hmac-sha|
00000240 31 2c 68 6d 61 63 2d 73 68 61 31 2d 39 36 00 00 |1,hmac-sha1-96..|
00000250 00 04 6e 6f 6e 65 00 00 00 04 6e 6f 6e 65 00 00 |..none....none..|
00000260 00 00 00 00 00 00 00 00 00 00 00 d7 3b 80 93 f6 |............;...|
>>> Flow 4 (client to server)
00000000 00 00 06 3c 08 14 34 e2 44 2d 08 8d 33 01 02 63 |...<..4.D-..3..c|
00000010 68 be 4c 40 4d e6 00 00 01 7a 73 6e 74 72 75 70 |h.L@M....zsntrup|
00000020 37 36 31 78 32 35 35 31 39 2d 73 68 61 35 31 32 |761x25519-sha512|
00000030 2c 73 6e 74 72 75 70 37 36 31 78 32 35 35 31 39 |,sntrup761x25519|
00000040 2d 73 68 61 35 31 32 40 6f 70 65 6e 73 73 68 2e |-sha512@openssh.|
00000050 63 6f 6d 2c 6d 6c 6b 65 6d 37 36 38 78 32 35 35 |com,mlkem768x255|
00000060 31 39 2d 73 68 61 32 35 36 2c 63 75 72 76 65 32 |19-sha256,curve2|
00000000 00 00 06 3c 08 14 c5 d5 30 62 29 26 f3 01 f2 08 |...<....0b)&....|
00000010 8a 6e d9 d5 8e 80 00 00 01 7a 6d 6c 6b 65 6d 37 |.n.......zmlkem7|
00000020 36 38 78 32 35 35 31 39 2d 73 68 61 32 35 36 2c |68x25519-sha256,|
00000030 73 6e 74 72 75 70 37 36 31 78 32 35 35 31 39 2d |sntrup761x25519-|
00000040 73 68 61 35 31 32 2c 73 6e 74 72 75 70 37 36 31 |sha512,sntrup761|
00000050 78 32 35 35 31 39 2d 73 68 61 35 31 32 40 6f 70 |x25519-sha512@op|
00000060 65 6e 73 73 68 2e 63 6f 6d 2c 63 75 72 76 65 32 |enssh.com,curve2|
00000070 35 35 31 39 2d 73 68 61 32 35 36 2c 63 75 72 76 |5519-sha256,curv|
00000080 65 32 35 35 31 39 2d 73 68 61 32 35 36 40 6c 69 |e25519-sha256@li|
00000090 62 73 73 68 2e 6f 72 67 2c 65 63 64 68 2d 73 68 |bssh.org,ecdh-sh|
@@ -68,50 +69,50 @@
00000160 67 72 6f 75 70 31 34 2d 73 68 61 31 2c 65 78 74 |group14-sha1,ext|
00000170 2d 69 6e 66 6f 2d 63 2c 6b 65 78 2d 73 74 72 69 |-info-c,kex-stri|
00000180 63 74 2d 63 2d 76 30 30 40 6f 70 65 6e 73 73 68 |ct-c-v00@openssh|
00000190 2e 63 6f 6d 00 00 01 cf 72 73 61 2d 73 68 61 32 |.com....rsa-sha2|
000001a0 2d 35 31 32 2d 63 65 72 74 2d 76 30 31 40 6f 70 |-512-cert-v01@op|
000001b0 65 6e 73 73 68 2e 63 6f 6d 2c 72 73 61 2d 73 68 |enssh.com,rsa-sh|
000001c0 61 32 2d 32 35 36 2d 63 65 72 74 2d 76 30 31 40 |a2-256-cert-v01@|
000001d0 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 72 73 61 2d |openssh.com,rsa-|
000001e0 73 68 61 32 2d 35 31 32 2c 72 73 61 2d 73 68 61 |sha2-512,rsa-sha|
000001f0 32 2d 32 35 36 2c 73 73 68 2d 65 64 32 35 35 31 |2-256,ssh-ed2551|
00000200 39 2d 63 65 72 74 2d 76 30 31 40 6f 70 65 6e 73 |9-cert-v01@opens|
00000210 73 68 2e 63 6f 6d 2c 65 63 64 73 61 2d 73 68 61 |sh.com,ecdsa-sha|
00000220 32 2d 6e 69 73 74 70 32 35 36 2d 63 65 72 74 2d |2-nistp256-cert-|
00000230 76 30 31 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c |v01@openssh.com,|
00000240 65 63 64 73 61 2d 73 68 61 32 2d 6e 69 73 74 70 |ecdsa-sha2-nistp|
00000250 33 38 34 2d 63 65 72 74 2d 76 30 31 40 6f 70 65 |384-cert-v01@ope|
00000260 6e 73 73 68 2e 63 6f 6d 2c 65 63 64 73 61 2d 73 |nssh.com,ecdsa-s|
00000270 68 61 32 2d 6e 69 73 74 70 35 32 31 2d 63 65 72 |ha2-nistp521-cer|
00000280 74 2d 76 30 31 40 6f 70 65 6e 73 73 68 2e 63 6f |t-v01@openssh.co|
00000290 6d 2c 73 6b 2d 73 73 68 2d 65 64 32 35 35 31 39 |m,sk-ssh-ed25519|
000002a0 2d 63 65 72 74 2d 76 30 31 40 6f 70 65 6e 73 73 |-cert-v01@openss|
000002b0 68 2e 63 6f 6d 2c 73 6b 2d 65 63 64 73 61 2d 73 |h.com,sk-ecdsa-s|
000002c0 68 61 32 2d 6e 69 73 74 70 32 35 36 2d 63 65 72 |ha2-nistp256-cer|
000002d0 74 2d 76 30 31 40 6f 70 65 6e 73 73 68 2e 63 6f |t-v01@openssh.co|
000002e0 6d 2c 73 73 68 2d 65 64 32 35 35 31 39 2c 65 63 |m,ssh-ed25519,ec|
000002f0 64 73 61 2d 73 68 61 32 2d 6e 69 73 74 70 32 35 |dsa-sha2-nistp25|
00000300 36 2c 65 63 64 73 61 2d 73 68 61 32 2d 6e 69 73 |6,ecdsa-sha2-nis|
00000310 74 70 33 38 34 2c 65 63 64 73 61 2d 73 68 61 32 |tp384,ecdsa-sha2|
00000320 2d 6e 69 73 74 70 35 32 31 2c 73 6b 2d 73 73 68 |-nistp521,sk-ssh|
00000330 2d 65 64 32 35 35 31 39 40 6f 70 65 6e 73 73 68 |-ed25519@openssh|
00000340 2e 63 6f 6d 2c 73 6b 2d 65 63 64 73 61 2d 73 68 |.com,sk-ecdsa-sh|
00000350 61 32 2d 6e 69 73 74 70 32 35 36 40 6f 70 65 6e |a2-nistp256@open|
00000360 73 73 68 2e 63 6f 6d 00 00 00 6c 63 68 61 63 68 |ssh.com...lchach|
00000190 2e 63 6f 6d 00 00 01 cf 73 73 68 2d 65 64 32 35 |.com....ssh-ed25|
000001a0 35 31 39 2d 63 65 72 74 2d 76 30 31 40 6f 70 65 |519-cert-v01@ope|
000001b0 6e 73 73 68 2e 63 6f 6d 2c 65 63 64 73 61 2d 73 |nssh.com,ecdsa-s|
000001c0 68 61 32 2d 6e 69 73 74 70 32 35 36 2d 63 65 72 |ha2-nistp256-cer|
000001d0 74 2d 76 30 31 40 6f 70 65 6e 73 73 68 2e 63 6f |t-v01@openssh.co|
000001e0 6d 2c 65 63 64 73 61 2d 73 68 61 32 2d 6e 69 73 |m,ecdsa-sha2-nis|
000001f0 74 70 33 38 34 2d 63 65 72 74 2d 76 30 31 40 6f |tp384-cert-v01@o|
00000200 70 65 6e 73 73 68 2e 63 6f 6d 2c 65 63 64 73 61 |penssh.com,ecdsa|
00000210 2d 73 68 61 32 2d 6e 69 73 74 70 35 32 31 2d 63 |-sha2-nistp521-c|
00000220 65 72 74 2d 76 30 31 40 6f 70 65 6e 73 73 68 2e |ert-v01@openssh.|
00000230 63 6f 6d 2c 73 6b 2d 73 73 68 2d 65 64 32 35 35 |com,sk-ssh-ed255|
00000240 31 39 2d 63 65 72 74 2d 76 30 31 40 6f 70 65 6e |19-cert-v01@open|
00000250 73 73 68 2e 63 6f 6d 2c 73 6b 2d 65 63 64 73 61 |ssh.com,sk-ecdsa|
00000260 2d 73 68 61 32 2d 6e 69 73 74 70 32 35 36 2d 63 |-sha2-nistp256-c|
00000270 65 72 74 2d 76 30 31 40 6f 70 65 6e 73 73 68 2e |ert-v01@openssh.|
00000280 63 6f 6d 2c 72 73 61 2d 73 68 61 32 2d 35 31 32 |com,rsa-sha2-512|
00000290 2d 63 65 72 74 2d 76 30 31 40 6f 70 65 6e 73 73 |-cert-v01@openss|
000002a0 68 2e 63 6f 6d 2c 72 73 61 2d 73 68 61 32 2d 32 |h.com,rsa-sha2-2|
000002b0 35 36 2d 63 65 72 74 2d 76 30 31 40 6f 70 65 6e |56-cert-v01@open|
000002c0 73 73 68 2e 63 6f 6d 2c 73 73 68 2d 65 64 32 35 |ssh.com,ssh-ed25|
000002d0 35 31 39 2c 65 63 64 73 61 2d 73 68 61 32 2d 6e |519,ecdsa-sha2-n|
000002e0 69 73 74 70 32 35 36 2c 65 63 64 73 61 2d 73 68 |istp256,ecdsa-sh|
000002f0 61 32 2d 6e 69 73 74 70 33 38 34 2c 65 63 64 73 |a2-nistp384,ecds|
00000300 61 2d 73 68 61 32 2d 6e 69 73 74 70 35 32 31 2c |a-sha2-nistp521,|
00000310 73 6b 2d 73 73 68 2d 65 64 32 35 35 31 39 40 6f |sk-ssh-ed25519@o|
00000320 70 65 6e 73 73 68 2e 63 6f 6d 2c 73 6b 2d 65 63 |penssh.com,sk-ec|
00000330 64 73 61 2d 73 68 61 32 2d 6e 69 73 74 70 32 35 |dsa-sha2-nistp25|
00000340 36 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 72 73 |6@openssh.com,rs|
00000350 61 2d 73 68 61 32 2d 35 31 32 2c 72 73 61 2d 73 |a-sha2-512,rsa-s|
00000360 68 61 32 2d 32 35 36 00 00 00 6c 63 68 61 63 68 |ha2-256...lchach|
00000370 61 32 30 2d 70 6f 6c 79 31 33 30 35 40 6f 70 65 |a20-poly1305@ope|
00000380 6e 73 73 68 2e 63 6f 6d 2c 61 65 73 31 32 38 2d |nssh.com,aes128-|
00000390 63 74 72 2c 61 65 73 31 39 32 2d 63 74 72 2c 61 |ctr,aes192-ctr,a|
000003a0 65 73 32 35 36 2d 63 74 72 2c 61 65 73 31 32 38 |es256-ctr,aes128|
000003b0 2d 67 63 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d |-gcm@openssh.com|
000003c0 2c 61 65 73 32 35 36 2d 67 63 6d 40 6f 70 65 6e |,aes256-gcm@open|
000003d0 73 73 68 2e 63 6f 6d 00 00 00 6c 63 68 61 63 68 |ssh.com...lchach|
00000390 67 63 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c |gcm@openssh.com,|
000003a0 61 65 73 32 35 36 2d 67 63 6d 40 6f 70 65 6e 73 |aes256-gcm@opens|
000003b0 73 68 2e 63 6f 6d 2c 61 65 73 31 32 38 2d 63 74 |sh.com,aes128-ct|
000003c0 72 2c 61 65 73 31 39 32 2d 63 74 72 2c 61 65 73 |r,aes192-ctr,aes|
000003d0 32 35 36 2d 63 74 72 00 00 00 6c 63 68 61 63 68 |256-ctr...lchach|
000003e0 61 32 30 2d 70 6f 6c 79 31 33 30 35 40 6f 70 65 |a20-poly1305@ope|
000003f0 6e 73 73 68 2e 63 6f 6d 2c 61 65 73 31 32 38 2d |nssh.com,aes128-|
00000400 63 74 72 2c 61 65 73 31 39 32 2d 63 74 72 2c 61 |ctr,aes192-ctr,a|
00000410 65 73 32 35 36 2d 63 74 72 2c 61 65 73 31 32 38 |es256-ctr,aes128|
00000420 2d 67 63 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d |-gcm@openssh.com|
00000430 2c 61 65 73 32 35 36 2d 67 63 6d 40 6f 70 65 6e |,aes256-gcm@open|
00000440 73 73 68 2e 63 6f 6d 00 00 00 d5 75 6d 61 63 2d |ssh.com....umac-|
00000400 67 63 6d 40 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c |gcm@openssh.com,|
00000410 61 65 73 32 35 36 2d 67 63 6d 40 6f 70 65 6e 73 |aes256-gcm@opens|
00000420 73 68 2e 63 6f 6d 2c 61 65 73 31 32 38 2d 63 74 |sh.com,aes128-ct|
00000430 72 2c 61 65 73 31 39 32 2d 63 74 72 2c 61 65 73 |r,aes192-ctr,aes|
00000440 32 35 36 2d 63 74 72 00 00 00 d5 75 6d 61 63 2d |256-ctr....umac-|
00000450 36 34 2d 65 74 6d 40 6f 70 65 6e 73 73 68 2e 63 |64-etm@openssh.c|
00000460 6f 6d 2c 75 6d 61 63 2d 31 32 38 2d 65 74 6d 40 |om,umac-128-etm@|
00000470 6f 70 65 6e 73 73 68 2e 63 6f 6d 2c 68 6d 61 63 |openssh.com,hmac|
@@ -143,9 +144,9 @@
00000610 6f 6d 00 00 00 15 6e 6f 6e 65 2c 7a 6c 69 62 40 |om....none,zlib@|
00000620 6f 70 65 6e 73 73 68 2e 63 6f 6d 00 00 00 00 00 |openssh.com.....|
00000630 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000640 00 00 00 2c 06 1e 00 00 00 20 f9 b4 36 4f 33 07 |...,..... ..6O3.|
00000650 53 32 b2 ba a5 a4 e1 aa 34 8b ff d7 44 18 42 7c |S2......4...D.B||
00000660 70 05 cf 55 41 9f aa 46 44 3e 00 00 00 00 00 00 |p..UA..FD>......|
00000640 00 00 00 2c 06 1e 00 00 00 20 07 c6 6a cc 26 c1 |...,..... ..j.&.|
00000650 98 86 8f 53 1c 84 6b 74 ca a1 20 cc 6e 95 46 85 |...S..kt.. .n.F.|
00000660 4a 31 4a e7 06 31 df f7 8b 49 00 00 00 00 00 00 |J1J..1...I......|
>>> Flow 5 (server to client)
00000000 00 00 02 6c 13 1f 00 00 01 17 00 00 00 07 73 73 |...l..........ss|
00000010 68 2d 72 73 61 00 00 00 03 01 00 01 00 00 01 01 |h-rsa...........|
@@ -165,185 +166,185 @@
000000f0 e0 27 88 e0 a2 05 8a a1 35 56 b9 9f 70 c8 0f f1 |.'......5V..p...|
00000100 fb 62 d1 2d 1b e0 9b 66 bd eb d8 a0 f7 7e ff 00 |.b.-...f.....~..|
00000110 7d 22 d1 6a be 17 3a 9f 2b b1 17 43 df 58 7f 92 |}".j..:.+..C.X..|
00000120 bf 00 00 00 20 ba d1 83 03 7f f7 60 46 13 f5 6f |.... ......`F..o|
00000130 4a 2c ee b3 5c 07 13 87 44 f6 a5 ed 9a 96 1a 29 |J,..\...D......)|
00000140 f5 1e 71 79 71 00 00 01 14 00 00 00 0c 72 73 61 |..qyq........rsa|
00000150 2d 73 68 61 32 2d 35 31 32 00 00 01 00 5a 61 32 |-sha2-512....Za2|
00000160 73 4c 41 20 e8 8b f9 ab f8 8b da 4a 7f d2 76 7f |sLA .......J..v.|
00000170 83 b5 4a 20 7b 0a 45 2c 60 dd f6 27 63 c4 8a d1 |..J {.E,`..'c...|
00000180 d2 0f 93 c7 e4 16 1a dc c9 c5 25 a1 04 cc e9 0e |..........%.....|
00000190 0c f0 a7 1b a5 e7 ed ac 5f 78 f2 73 d2 4a fe d1 |........_x.s.J..|
000001a0 5f b2 e2 fe fa 51 30 2a 99 57 7d c8 3f 5f 83 73 |_....Q0*.W}.?_.s|
000001b0 fa 31 da e6 86 cf e0 0f 77 97 3d 9f 48 08 32 b6 |.1......w.=.H.2.|
000001c0 12 68 d9 74 cb b7 6f 21 ff cd d9 53 de b3 3a b2 |.h.t..o!...S..:.|
000001d0 f8 8a b1 fe 47 ea 93 7e c2 01 8f 09 b0 f1 d2 ca |....G..~........|
000001e0 de b8 c3 89 4a 97 84 7a 07 38 06 ca 41 ac b1 cb |....J..z.8..A...|
000001f0 b5 70 8a c9 d9 58 11 04 95 17 40 97 4b 46 03 54 |.p...X....@.KF.T|
00000200 6f 9e 38 e0 be 5c bc d9 16 c0 11 70 79 3a c2 9f |o.8..\.....py:..|
00000210 ea e7 47 f3 c7 38 84 26 e8 ed b4 e1 a3 3b d2 64 |..G..8.&.....;.d|
00000220 22 b4 60 9d d4 cb e7 f0 b7 f5 0f c7 44 c3 10 2d |".`.........D..-|
00000230 32 ec e2 e6 a3 00 4b ed f6 5f 82 fe 2e 19 ed c3 |2.....K.._......|
00000240 e2 92 09 0e cb a9 6f ba 1c 98 8a 6e d0 de ad 4a |......o....n...J|
00000250 17 a1 cf 69 a8 6d 6e bb 52 37 2a a6 2a cc 87 3c |...i.mn.R7*.*..<|
00000260 23 dc 62 b8 d2 60 16 9a fa 2f 75 ab 91 6a 58 d9 |#.b..`.../u..jX.|
00000270 00 00 00 0c 0a 15 74 91 88 35 d2 5e 6a 43 50 85 |......t..5.^jCP.|
00000280 f7 ed ca f3 01 aa f4 da b9 50 da 63 fa 6c 03 10 |.........P.c.l..|
00000290 47 76 f7 f6 36 a7 10 42 d3 38 45 66 2f df bc a9 |Gv..6..B.8Ef/...|
000002a0 0c f6 4d f7 38 f8 bf de 81 55 1b 6f 80 cf f5 70 |..M.8....U.o...p|
000002b0 d1 4d 2d bf 26 7a a4 f2 fd ec 48 92 1a 9a 1d 02 |.M-.&z....H.....|
000002c0 80 30 74 21 a9 47 b1 ae 84 95 c5 ce e0 fa 42 2c |.0t!.G........B,|
000002d0 a0 48 27 f0 fa 18 a7 75 ce 13 3f b0 8f fb ad 9c |.H'....u..?.....|
000002e0 c6 54 01 40 3c d2 64 5b 67 04 d0 d0 fc 4e 08 74 |.T.@<.d[g....N.t|
000002f0 3a 4e 3f 91 e1 05 26 9a b7 f6 88 b0 a7 d1 be 41 |:N?...&........A|
00000300 ea 32 d3 2b 50 bb 82 30 ca 96 e1 c3 c0 5d 6e be |.2.+P..0.....]n.|
00000310 be 11 57 e8 39 d6 52 d2 ee 1b 15 ba d0 5d d2 75 |..W.9.R......].u|
00000320 6d 30 fa 1c 8b c5 37 9c 9f a4 da f3 c9 b2 d4 68 |m0....7........h|
00000330 66 b6 6d 27 ff 5d 22 ad fa 49 99 a1 99 28 c2 bd |f.m'.]"..I...(..|
00000340 7c a6 11 d2 77 38 ae ab 67 ed 98 88 b4 d9 63 c5 ||...w8..g.....c.|
00000350 59 c1 63 4d 03 b4 76 9c c6 60 09 46 25 d4 0a 4a |Y.cM..v..`.F%..J|
00000360 a8 54 fe a5 55 43 45 a2 b5 4d db 58 38 ab 2b 3f |.T..UCE..M.X8.+?|
00000370 c5 e2 2d 47 c9 91 7b 11 43 fb ab c2 47 e7 21 a5 |..-G..{.C...G.!.|
00000380 1c 0f 16 a2 |....|
00000120 bf 00 00 00 20 13 cf b6 0f c2 c9 08 d9 7b f6 60 |.... ........{.`|
00000130 d4 53 7f 4b b1 29 37 59 98 3c dd ab b1 51 12 94 |.S.K.)7Y.<...Q..|
00000140 92 eb 56 4c 6f 00 00 01 14 00 00 00 0c 72 73 61 |..VLo........rsa|
00000150 2d 73 68 61 32 2d 35 31 32 00 00 01 00 64 a3 e2 |-sha2-512....d..|
00000160 1b 13 ca 48 cf 1b ce 82 1f 24 15 74 dd 53 c4 20 |...H.....$.t.S. |
00000170 f3 bb f5 48 45 2c 11 db 63 e2 b2 f6 30 c8 72 b1 |...HE,..c...0.r.|
00000180 42 8a f3 67 3b 1d 9f dc 60 f8 57 d8 9e bf 24 7b |B..g;...`.W...${|
00000190 97 96 cc dd 52 e6 1e f7 d6 ab 83 98 a2 5f 05 fd |....R........_..|
000001a0 15 ae b3 b8 a8 7e d0 b8 3e 49 0a 03 89 69 14 d7 |.....~..>I...i..|
000001b0 58 9f d2 a5 48 3d 65 25 91 e2 13 1b fa 09 e5 f9 |X...H=e%........|
000001c0 c2 6d 2e 6e b7 21 55 90 cd c9 97 29 94 03 c4 d2 |.m.n.!U....)....|
000001d0 b5 0a 36 54 ad 7e de d5 33 e3 04 1e bb 63 c3 ed |..6T.~..3....c..|
000001e0 ce b1 b6 36 f9 8a 75 53 c3 6e 55 4c 78 99 66 55 |...6..uS.nULx.fU|
000001f0 0e eb 1f 41 f6 d9 ca a6 67 3b c9 2a a2 81 26 d6 |...A....g;.*..&.|
00000200 7c 7b fb bb 06 b4 64 1a 44 72 e6 01 3b 51 e4 76 ||{....d.Dr..;Q.v|
00000210 39 66 8e cb a1 5a c8 5c df 94 27 e2 9c 1a fd 97 |9f...Z.\..'.....|
00000220 ef fb c5 c8 15 c1 76 f9 70 ad 33 64 e4 8e df c0 |......v.p.3d....|
00000230 39 b2 12 93 8d d7 59 f3 4e 6e ae bc 4a 9a 7e 8e |9.....Y.Nn..J.~.|
00000240 d6 e5 ba 85 b4 32 2f f6 95 dd c2 79 ca f7 77 74 |.....2/....y..wt|
00000250 ef 12 a7 4e c9 f8 5e b5 4d f2 b4 0c ef e8 a3 63 |...N..^.M......c|
00000260 9c a8 a1 e3 f9 ae 57 e2 35 b8 cc 87 3c 23 dc 62 |......W.5...<#.b|
00000270 00 00 00 0c 0a 15 b8 d2 60 16 9a fa 2f 75 ab 91 |........`.../u..|
00000280 46 fe cb a3 45 3f 82 e6 fe 2c d4 e1 e3 0c 8b 21 |F...E?...,.....!|
00000290 72 62 8b bd 5a de b1 65 d3 68 43 56 c3 65 ef d9 |rb..Z..e.hCV.e..|
000002a0 b0 ea 9b 6b 54 1c 04 93 d8 71 5d 4c ff 55 08 84 |...kT....q]L.U..|
000002b0 0f ee 03 cb f7 c1 fd 8f e6 75 26 13 98 b6 c9 63 |.........u&....c|
000002c0 1f 7d 0b 09 d7 14 9d b3 ab a1 32 6f 88 8f 53 2d |.}........2o..S-|
000002d0 fb 0f f8 cf 58 c3 23 78 9f cd a5 81 34 08 24 2f |....X.#x....4.$/|
000002e0 9e 5b 41 f0 81 c5 8d 2f 80 2a d8 6b a5 5b 81 c2 |.[A..../.*.k.[..|
000002f0 4e 2a 71 c6 69 39 94 ba 8c ef 3f ec 13 ba 73 81 |N*q.i9....?...s.|
00000300 9e 23 ca 7c 97 66 4d 06 9c f5 1b d9 30 e4 26 af |.#.|.fM.....0.&.|
00000310 e0 bf 67 25 7a b3 57 02 cd 5d 31 a2 5f 80 22 37 |..g%z.W..]1._."7|
00000320 79 75 87 f7 50 3b 8f 43 cb ec 14 3c 0b d7 6f 0c |yu..P;.C...<..o.|
00000330 3e 95 60 03 f3 fc 36 23 5a 92 63 0d 66 a3 98 61 |>.`...6#Z.c.f..a|
00000340 e5 b2 db b4 bb 1f d6 e7 e7 bd 39 92 c9 66 12 eb |..........9..f..|
00000350 9b 54 3e ba f1 96 bf 39 c6 cd 7a 27 95 c1 b6 7c |.T>....9..z'...||
00000360 9b e3 f1 a6 ea 93 aa a1 db 3a 3f 32 2f 6d 9e 14 |.........:?2/m..|
00000370 ee 93 3b 89 85 54 87 c7 12 07 66 e4 42 65 ba 28 |..;..T....f.Be.(|
00000380 0b ad 7c d4 |..|.|
>>> Flow 6 (client to server)
00000000 00 00 00 0c 0a 15 00 00 00 00 00 00 00 00 00 00 |................|
00000010 a8 13 62 11 40 33 41 a5 63 99 c5 71 e8 12 5d 6e |..b.@3A.c..q..]n|
00000020 f2 a0 ab 7c ca 08 84 b2 c5 92 f7 1a 33 93 2c 97 |...|........3.,.|
00000030 97 2a ed 6f cf b3 60 cd 06 0f ce cb |.*.o..`.....|
00000010 e8 62 17 70 dc 16 ff ad b2 d7 c5 23 45 62 93 83 |.b.p.......#Eb..|
00000020 f8 01 1b 79 df 30 da 0e a9 fd 8b 3c 51 4d b6 cc |...y.0.....<QM..|
00000030 05 ce 2e 21 93 08 ae 1e 0e ac 80 80 |...!........|
>>> Flow 7 (server to client)
00000000 ca 84 08 4b 1d 34 d2 7e d8 ff 49 51 d9 62 3d 46 |...K.4.~..IQ.b=F|
00000010 2c b5 29 4e 75 2f a3 e3 ef c5 a4 46 7e fc d3 16 |,.)Nu/.....F~...|
00000020 d8 51 39 df bd e8 f6 bd 0f cb 41 2f |.Q9.......A/|
00000000 10 45 f8 2f ae bb 2c e7 03 ee d8 7e 5a b0 d8 a4 |.E./..,....~Z...|
00000010 91 17 95 56 a1 e3 d3 85 e9 d2 84 84 09 a2 3b d3 |...V..........;.|
00000020 d0 95 a3 1c 05 01 9c e5 b0 91 74 09 |..........t.|
>>> Flow 8 (client to server)
00000000 a5 bd d6 66 38 58 c2 81 c2 5c c8 51 9f dd 92 73 |...f8X...\.Q...s|
00000010 bf 4b bf e6 62 11 27 4c f2 0e ca 0e 98 8b 79 1b |.K..b.'L......y.|
00000020 e7 61 6c f8 69 d2 9d 9b 24 92 7b 7e d8 c2 e7 9e |.al.i...$.{~....|
00000030 d1 98 3f a6 f5 db 7f 49 da 57 f7 2d 83 c4 92 7f |..?....I.W.-....|
00000040 6d b2 77 83 |m.w.|
00000000 c3 4f 1f 70 2e b7 7c b2 cf 8a 41 71 87 5a fa ff |.O.p..|...Aq.Z..|
00000010 f1 80 e4 e2 28 05 b8 ff cf 93 ef 9b d3 c8 fe f4 |....(...........|
00000020 19 a7 aa f3 43 82 20 ab a5 f4 84 00 c9 99 33 32 |....C. .......32|
00000030 7e b2 0b 20 b9 75 2b d8 79 ec 48 b0 9e 8d c0 b5 |~.. .u+.y.H.....|
00000040 ff 06 1c b3 |....|
>>> Flow 9 (server to client)
00000000 47 50 20 fd 43 ef ef 7a 52 7b 41 0a 1e 15 37 bd |GP .C..zR{A...7.|
00000010 b9 22 2b 3e a8 51 89 fe f2 49 6e d0 93 58 98 52 |."+>.Q...In..X.R|
00000020 f0 7b 81 01 8d f4 ce 59 21 24 d9 31 |.{.....Y!$.1|
00000000 b7 1a 6c 15 3c 76 f9 e2 f1 4e 22 39 9e af 8c b4 |..l.<v...N"9....|
00000010 42 a6 39 88 3f c7 d4 50 ae e3 5b 11 57 4d ab e1 |B.9.?..P..[.WM..|
00000020 39 77 75 63 43 e3 8b 81 21 8c b2 0a |9wucC...!...|
>>> Flow 10 (client to server)
00000000 00 fc 64 a5 9b 15 da a4 22 c6 cb 48 5a 57 ce f9 |..d....."..HZW..|
00000010 54 e9 47 6b ec d6 b1 4c 3c 39 c8 45 55 0e 27 58 |T.Gk...L<9.EU.'X|
00000020 af 26 70 f4 59 d3 1a c8 6f c5 30 b4 79 96 4f f4 |.&p.Y...o.0.y.O.|
00000030 d6 d0 55 e4 c5 c2 eb 94 1c ee 68 6f 3a 92 a1 49 |..U.......ho:..I|
00000040 ce 55 cc 92 ba 26 5c 79 67 5a ee 14 01 ee df 29 |.U...&\ygZ.....)|
00000050 0b 67 5e dc fb 13 ba 1c 36 86 e2 65 93 ff 78 a1 |.g^.....6..e..x.|
00000060 56 f0 82 ac 11 c2 49 ed a9 79 1c 68 d9 20 17 3e |V.....I..y.h. .>|
00000070 2b 76 d2 38 a4 4c 77 91 27 bc f9 2a 20 53 97 fe |+v.8.Lw.'..* S..|
00000080 3f 15 e6 77 cd 34 c6 30 96 f0 71 5f 79 ea d1 7b |?..w.4.0..q_y..{|
00000090 53 96 94 65 10 de 72 06 b5 47 98 5a a6 47 d7 c2 |S..e..r..G.Z.G..|
000000a0 bf 4e 1b 76 58 3a ac f1 27 dd ef af a4 4b 4b d1 |.N.vX:..'....KK.|
000000b0 7d d1 76 b8 57 0e ca 50 c3 88 94 50 96 42 10 be |}.v.W..P...P.B..|
000000c0 58 8e a7 e8 12 bd 03 b7 e8 58 45 3e 74 2b b8 59 |X........XE>t+.Y|
000000d0 29 e9 0e 0d 20 4f b8 b2 84 9e c7 86 c8 d9 30 ba |)... O........0.|
000000e0 ff 47 37 50 49 b4 00 d7 aa 39 54 54 57 b6 c2 b0 |.G7PI....9TTW...|
000000f0 f5 be 83 6d 3b 3d 75 cb a2 13 36 8b b1 76 3c c8 |...m;=u...6..v<.|
00000100 71 f7 b1 30 ff 73 c1 3b 0c ff 8f df 24 74 26 50 |q..0.s.;....$t&P|
00000110 61 8d 81 e7 48 85 2e df 99 1e ad c9 e0 70 09 d9 |a...H........p..|
00000120 b7 00 5e 1e 01 07 02 89 2f 83 1f 44 f9 96 2f 64 |..^...../..D../d|
00000130 01 7e b7 38 8c df ab 7a fc e3 68 3e b1 7d 95 e7 |.~.8...z..h>.}..|
00000140 d8 98 44 49 ed ff dd e9 9a 9d 80 06 67 73 a1 db |..DI........gs..|
00000150 c3 0b 85 18 26 bf 35 1b 6b b4 af 08 67 32 c5 bf |....&.5.k...g2..|
00000160 43 16 cc 1f fc 35 f4 50 32 1f 4e 89 11 16 cf 85 |C....5.P2.N.....|
00000170 7c 19 ef 87 ||...|
00000000 46 ee 3c 00 23 17 9c ef 31 90 23 82 d7 4e f0 da |F.<.#...1.#..N..|
00000010 ad 91 8f 32 08 a3 db eb 7d 0c 9f f1 00 37 8f 68 |...2....}....7.h|
00000020 1a 0d 8d 04 b0 ab e5 15 bc e9 3c 31 ff 4d 63 52 |..........<1.McR|
00000030 a6 3b e4 f7 2f ed 6c f8 df cd f8 6c 1b 2e 55 37 |.;../.l....l..U7|
00000040 d6 7a 22 20 45 bb cb c3 ba cf fb 45 06 1a 6d 86 |.z" E......E..m.|
00000050 66 f6 d3 b6 60 ab 80 e5 c6 1d 12 2b 10 55 2b cc |f...`......+.U+.|
00000060 59 54 4b b2 f5 71 bd 02 93 0a ed 4a 2f 54 cb 27 |YTK..q.....J/T.'|
00000070 04 e0 b6 6d 2e 9f d1 79 48 e4 75 fb a2 7b 5b 7b |...m...yH.u..{[{|
00000080 59 6a d4 a0 f5 f6 7b b9 60 e8 2e c0 b2 2a bf 2e |Yj....{.`....*..|
00000090 fe 89 c8 71 fa 24 9f 48 be 94 7e 93 cf 1f c0 e5 |...q.$.H..~.....|
000000a0 4c 2d c2 0a f6 95 75 96 a6 54 e5 7a 9b 52 95 4d |L-....u..T.z.R.M|
000000b0 8e 30 a9 1b 0f 07 2d 6f 54 30 3c db 79 47 91 08 |.0....-oT0<.yG..|
000000c0 8e 6b cd fa b2 d8 cc c4 69 07 69 82 48 d9 05 01 |.k......i.i.H...|
000000d0 d8 4d 14 7b 16 6d aa cf 4a da 5e 94 93 5f 81 dd |.M.{.m..J.^.._..|
000000e0 d0 8e 10 e4 fe 94 ed 2e 70 42 2f 6c e5 50 2a 2f |........pB/l.P*/|
000000f0 b6 6d 1f 48 38 93 44 d9 55 46 b5 2b 18 1f 78 89 |.m.H8.D.UF.+..x.|
00000100 32 f1 21 03 cb a8 37 9c e4 dc 7f 34 cd 42 f1 90 |2.!...7....4.B..|
00000110 72 14 d3 c3 25 37 5e b8 a1 bd 39 60 f2 e7 da 39 |r...%7^...9`...9|
00000120 87 5c 37 99 87 64 05 bd c8 45 22 be 79 7c 1e 47 |.\7..d...E".y|.G|
00000130 99 a7 b9 5b ab b0 81 d6 24 3e 85 fe 30 3e b0 3d |...[....$>..0>.=|
00000140 4d 92 eb 95 86 0d 72 2c eb 2c 7d 2a a1 f0 9e ed |M.....r,.,}*....|
00000150 d1 9c 2f 71 3d c4 e0 92 2c 62 ae 4e 32 cd 20 ae |../q=...,b.N2. .|
00000160 e2 6b 91 c2 0f d5 90 6e 80 91 4c eb 40 74 36 1f |.k.....n..L.@t6.|
00000170 50 36 4d f7 |P6M.|
>>> Flow 11 (server to client)
00000000 5f 10 56 52 67 5a d9 3e 33 8d dc 87 a5 e9 65 30 |_.VRgZ.>3.....e0|
00000010 b2 38 f2 bb 02 86 ae a4 d3 9d 1a f0 ff a7 f6 88 |.8..............|
00000020 1f bd 6f 7f cc 61 67 aa e8 08 39 b1 3d 44 be 39 |..o..ag...9.=D.9|
00000030 d7 5c f9 11 41 02 9d 01 fb e5 22 16 3f 70 8c 0c |.\..A.....".?p..|
00000040 7e db a5 f5 88 87 fa 13 61 f0 c7 9b 06 90 65 a5 |~.......a.....e.|
00000050 b4 55 32 e7 4d fd 87 40 31 c0 56 28 61 91 e6 34 |.U2.M..@1.V(a..4|
00000060 dd 46 05 75 ff 8b e4 7c a9 fe 05 06 42 41 01 97 |.F.u...|....BA..|
00000070 a2 43 fd c3 7c cc 60 93 41 8f ec f9 43 ae 72 ff |.C..|.`.A...C.r.|
00000080 fd a4 d1 13 07 a4 0d cd c5 e8 1e f8 71 83 9b d3 |............q...|
00000090 40 75 e5 8b 41 45 f1 3b 10 28 fd fa 7b a0 e4 55 |@u..AE.;.(..{..U|
000000a0 a3 8f 1d 17 4a 7a bb 00 5a 05 ae 96 e0 8e fe b2 |....Jz..Z.......|
000000b0 4d fc 4d ad 60 1b 6e 3c 29 66 40 16 47 93 8c 50 |M.M.`.n<)f@.G..P|
000000c0 98 41 9b 74 1f 3a 8f 61 60 8e cf 64 d1 1b 90 a3 |.A.t.:.a`..d....|
000000d0 2f 5e ef 63 32 11 75 91 fd 8a ff 80 d1 c7 89 55 |/^.c2.u........U|
000000e0 e1 4b d7 de c0 29 8c 31 6f ef 58 09 1c f4 3c 6b |.K...).1o.X...<k|
000000f0 46 f6 65 9b ad 76 31 e6 7d a1 96 29 b5 00 aa c2 |F.e..v1.}..)....|
00000100 eb ad cf da e2 ef f9 2a 82 b0 5b e8 39 a6 2d 0e |.......*..[.9.-.|
00000110 76 c0 aa 77 d5 5d 9d f5 8a 3f 3a e4 21 e2 88 12 |v..w.]...?:.!...|
00000120 9d 16 2f 25 74 c5 5f 4b 5f d1 b3 d1 ad 8b 56 86 |../%t._K_.....V.|
00000130 75 64 c7 82 8c e2 66 10 d0 38 21 64 4e af d5 f1 |ud....f..8!dN...|
00000140 61 34 e4 77 81 85 6e ed f7 46 9a 43 |a4.w..n..F.C|
00000000 d5 4c 72 d9 7a c9 22 77 d6 4c 63 74 37 fa 30 ad |.Lr.z."w.Lct7.0.|
00000010 7e 13 45 59 61 84 82 f7 d2 13 3f 44 ac 2d de 41 |~.EYa.....?D.-.A|
00000020 db 9c dd 1b fe 06 aa bc 38 30 1d a4 bb ed 7c 9d |........80....|.|
00000030 4b 6e 79 78 e4 0a 35 5f 68 e9 4f a5 e9 65 99 f3 |Knyx..5_h.O..e..|
00000040 e9 72 59 1d cd 0b ed 44 52 5d f9 dc 76 c9 23 ce |.rY....DR]..v.#.|
00000050 9f 27 21 ff 94 0c dd 60 95 89 ce 71 8d 37 c1 ec |.'!....`...q.7..|
00000060 12 90 99 b3 62 83 18 3a ea d9 c8 73 67 67 c4 64 |....b..:...sgg.d|
00000070 55 f8 3b b9 5b ea 67 5b de 8a e6 00 49 15 9a 64 |U.;.[.g[....I..d|
00000080 2f b6 7c ec 76 71 d0 c2 cc e0 3f 44 46 ed 27 85 |/.|.vq....?DF.'.|
00000090 f1 71 5e 44 94 d0 c0 b4 da 4b ab e5 28 be 62 44 |.q^D.....K..(.bD|
000000a0 3a 36 ef 1d d3 5a a2 ea 9e 5e e0 19 18 5d be 85 |:6...Z...^...]..|
000000b0 26 7c 92 b9 14 38 18 27 35 32 bf f9 18 ce d2 ec |&|...8.'52......|
000000c0 20 1d ac de ea 78 2a 2b 5a 3d a8 e8 95 43 24 ea | ....x*+Z=...C$.|
000000d0 ee eb 24 f1 c1 c3 d0 e4 d9 21 f9 57 17 f9 4d 4c |..$......!.W..ML|
000000e0 03 37 ae dd b8 80 f0 52 77 43 80 e0 6e 79 07 7c |.7.....RwC..ny.||
000000f0 a9 c6 38 43 a1 89 fd 0d f4 07 84 33 e2 f6 94 61 |..8C.......3...a|
00000100 ff 33 66 05 21 0f 6d 88 4b 41 88 12 9a cc b8 a0 |.3f.!.m.KA......|
00000110 81 4b 7b ed 61 0d a2 6f 05 91 dd ff f8 56 95 73 |.K{.a..o.....V.s|
00000120 6d ec 15 6a ee df cc 7c 65 f0 7d 02 23 06 0f c1 |m..j...|e.}.#...|
00000130 90 6c 76 81 15 9a 22 99 09 78 9f 2c b6 9d 67 9f |.lv..."..x.,..g.|
00000140 45 85 3c a2 dd af c7 f9 f3 0d 05 d3 |E.<.........|
>>> Flow 12 (client to server)
00000000 3d 66 0f 09 25 f8 c6 94 96 76 8f bb 42 e1 39 a5 |=f..%....v..B.9.|
00000010 a4 68 37 80 00 dc c3 5f d4 e7 da de 1f d0 6c 13 |.h7...._......l.|
00000020 3c 8c 55 96 d4 4e 6c 9c 46 02 d9 5b ba 25 a2 42 |<.U..Nl.F..[.%.B|
00000030 ed 92 b2 4a 78 e0 90 c5 ab 03 68 30 a3 e8 b6 c2 |...Jx.....h0....|
00000040 74 50 9b 60 c6 27 a2 d4 6f fe b6 30 15 7e b7 74 |tP.`.'..o..0.~.t|
00000050 89 a1 05 37 4c ae 9e dd 93 32 ad d3 1e fe 47 05 |...7L....2....G.|
00000060 cb 07 f0 46 2b 11 96 57 19 c0 ec 4b 19 2c c7 2b |...F+..W...K.,.+|
00000070 5a 7a 31 e7 0a 6d 9e 2e bf d2 15 e6 eb 2b f6 9a |Zz1..m.......+..|
00000080 80 01 99 69 1d e6 0b 5b 02 b2 22 19 b8 f8 6c b8 |...i...[.."...l.|
00000090 f0 95 54 e5 67 1a e9 71 8d 05 7c d7 6f 39 eb 15 |..T.g..q..|.o9..|
000000a0 dc be 20 e6 c1 dd fa 22 13 af fb 08 8b 67 2a eb |.. ....".....g*.|
000000b0 12 fd 55 c4 c7 e0 8a 7c 28 14 cf 27 58 fd 2d 87 |..U....|(..'X.-.|
000000c0 3d 5b 3b 42 94 8c da 72 b4 97 d6 e3 a5 3c 73 05 |=[;B...r.....<s.|
000000d0 c9 e9 d3 bb 89 8c 8d 84 db 9d e6 4e 25 a0 a4 eb |...........N%...|
000000e0 47 4b c2 09 ed 0e 00 71 3e 27 09 7c 37 f5 e3 1c |GK.....q>'.|7...|
000000f0 b3 d7 f3 bc bc bf 48 f3 12 64 89 df 5d a9 4a da |......H..d..].J.|
00000100 dd 76 1d 9d a0 0a fb 15 81 b0 78 0d 75 29 43 1f |.v........x.u)C.|
00000110 5b 0c 47 dc a0 dc d1 3f e4 2f 90 e8 59 6e 80 47 |[.G....?./..Yn.G|
00000120 11 1a 42 db 86 72 ec 84 41 ca 19 8d 48 8f b3 e8 |..B..r..A...H...|
00000130 97 3e dc f8 36 1d 44 1b 54 68 fa 61 17 c2 c8 6e |.>..6.D.Th.a...n|
00000140 89 7d 25 f7 1e 55 fa 26 f1 0b a5 9d f9 af 66 32 |.}%..U.&......f2|
00000150 65 6c 9b 16 f5 41 8c 5c d6 29 44 5e 97 9e 73 35 |el...A.\.)D^..s5|
00000160 f3 78 54 1d 7e fe 41 92 a2 85 49 9f 4b 8e f9 87 |.xT.~.A...I.K...|
00000170 05 9b 93 6e 62 ce 1c ae ed 14 b4 22 89 df e7 97 |...nb......"....|
00000180 fc e9 67 eb bd a9 ae 4a 14 bf cf 37 cb 46 b9 55 |..g....J...7.F.U|
00000190 0d 72 f5 be 7f 5b d7 39 15 23 30 00 fb 1d 2c 04 |.r...[.9.#0...,.|
000001a0 54 49 14 c5 ac d6 1b 56 20 f9 5f 4c 0a ca 02 1e |TI.....V ._L....|
000001b0 82 78 be e5 db a1 cd b5 85 1b 54 18 51 c2 3c 2c |.x........T.Q.<,|
000001c0 1c bb 3c 85 1d 93 51 61 a4 2d ec f6 76 62 d7 1e |..<...Qa.-..vb..|
000001d0 12 f6 24 e7 93 3b c3 63 9e ca 2d 0d 04 b1 b4 f8 |..$..;.c..-.....|
000001e0 41 03 b9 53 43 e3 7c b4 ca 65 35 f7 b8 af 71 91 |A..SC.|..e5...q.|
000001f0 2f f2 4c 04 e1 d3 a7 1a 35 78 55 44 d6 2b 7f 52 |/.L.....5xUD.+.R|
00000200 c5 60 26 68 30 7a ff e0 d5 77 56 b1 02 84 0a 09 |.`&h0z...wV.....|
00000210 5a b3 22 ad 8b f7 be 50 00 21 69 fd 2f c2 c7 0c |Z."....P.!i./...|
00000220 8d 15 76 9d 49 c9 48 2a 4d e9 d6 da e7 18 0a 73 |..v.I.H*M......s|
00000230 47 1d ab 9b f6 41 22 e9 18 e6 e3 cd a4 b7 51 ee |G....A".......Q.|
00000240 d2 68 fb 35 3c cb 1d 86 e7 1b 89 36 5a a6 37 8f |.h.5<......6Z.7.|
00000250 60 79 d5 e5 42 82 f8 9f 0a 04 03 27 ac a3 31 d3 |`y..B......'..1.|
00000260 dd c9 5e fe 8d 69 cd 06 19 93 69 07 12 e7 fd c8 |..^..i....i.....|
00000270 8a d4 65 df 14 36 f0 0b bf f3 8f 1a dc 7c c7 33 |..e..6.......|.3|
00000280 11 d3 4b 25 4b 68 d1 8b 38 70 05 22 |..K%Kh..8p."|
00000000 30 4c 00 77 a3 9a a9 d7 56 a7 37 b1 b5 ed 6d 26 |0L.w....V.7...m&|
00000010 c1 6c e2 cf 5e c8 76 3c f8 84 3f 8b 2b 9d bc 1d |.l..^.v<..?.+...|
00000020 3b 7a 6c 44 e2 64 9b f2 b2 04 09 96 47 27 58 4b |;zlD.d......G'XK|
00000030 18 7b c4 e7 b5 71 07 29 94 34 9b a3 30 4a 4e de |.{...q.).4..0JN.|
00000040 48 83 10 65 25 41 6b 71 2a b8 a1 77 e5 26 9c dd |H..e%Akq*..w.&..|
00000050 d2 5b b7 89 a3 54 1f e2 1e f0 78 19 fe a3 92 db |.[...T....x.....|
00000060 b6 fe 47 3e dd 7c 3a 3d 76 ec 6e 1b 20 9c 02 b1 |..G>.|:=v.n. ...|
00000070 58 85 d0 77 c9 3f 04 68 f3 b5 a1 6c dd 9f 5c 39 |X..w.?.h...l..\9|
00000080 8e 3f 06 9e bd c5 a2 e8 5a 85 b7 de 75 d6 82 81 |.?......Z...u...|
00000090 f2 6f 35 dd b8 e3 de bd a0 b7 2e d6 12 5b b1 0b |.o5..........[..|
000000a0 bb 68 36 ac 1f ad 08 bd f6 0f 27 08 ed 70 5b 5e |.h6.......'..p[^|
000000b0 34 45 8b 00 ef 4f b3 52 53 b0 8d c5 9d 1b c0 fb |4E...O.RS.......|
000000c0 f3 40 70 bb 07 8b e0 d8 11 de 5f 98 b9 36 18 aa |.@p......._..6..|
000000d0 8d 5b 29 45 02 ce 32 04 35 2e f4 21 6a 2d 24 cf |.[)E..2.5..!j-$.|
000000e0 29 a0 3e 1e 14 91 17 09 64 77 23 34 23 3f 5b 37 |).>.....dw#4#?[7|
000000f0 b3 c7 e7 50 23 8d be 32 3d 98 8e 6c d6 96 8b a9 |...P#..2=..l....|
00000100 82 5d 89 e2 56 9a ad 39 04 af 4a 78 20 3e 46 60 |.]..V..9..Jx >F`|
00000110 38 e0 b9 3f 8f 5c 2d b6 82 cf ad 20 89 fd c7 c5 |8..?.\-.... ....|
00000120 e7 54 9a d8 a0 70 31 18 7c 8c 32 99 73 a4 6c ff |.T...p1.|.2.s.l.|
00000130 c0 36 5f 22 1b 01 d3 a6 b0 03 9a 04 0c 0a e3 8d |.6_"............|
00000140 b2 23 28 16 6c 2a c6 8d 53 fa 4c 25 db 0b d9 ab |.#(.l*..S.L%....|
00000150 d1 e6 e1 d3 38 f7 2b 3d 36 03 e2 65 37 04 14 d5 |....8.+=6..e7...|
00000160 66 9f 3b 4f bb e6 8e 64 22 60 f2 ba 8f 15 98 2b |f.;O...d"`.....+|
00000170 09 d6 c0 af aa 26 e5 64 14 0e 61 dd 51 9d 0d ba |.....&.d..a.Q...|
00000180 50 74 51 2d c6 45 5e 25 a3 94 5a b3 68 ce 73 aa |PtQ-.E^%..Z.h.s.|
00000190 e3 11 d1 a4 c2 49 79 1a 1d 24 7c 03 32 ac 91 43 |.....Iy..$|.2..C|
000001a0 1f 40 74 2c 95 c3 90 6d 86 ea d2 ef b5 9e 92 6b |.@t,...m.......k|
000001b0 1e af 4c b4 a7 4f d8 ea 59 27 cb 8c b4 f5 7e 8e |..L..O..Y'....~.|
000001c0 62 7c ae e0 57 20 bc 4b 2c b7 7c 1b 50 a4 3a 14 |b|..W .K,.|.P.:.|
000001d0 3b 9b b3 cd 89 52 3b 72 bb 22 7d 74 00 a9 3f d9 |;....R;r."}t..?.|
000001e0 7c 70 04 f2 24 db 49 e3 80 95 98 85 50 9d 90 8c ||p..$.I.....P...|
000001f0 8c 67 d8 0c 9f 78 32 5b b0 af ab 06 0d 04 1f 19 |.g...x2[........|
00000200 d7 b7 62 3a 2b 49 4f 8e 1b 08 3e f1 0e 6f dd 62 |..b:+IO...>..o.b|
00000210 57 2b c1 fd e1 dc d2 8c 4e 95 d6 0f ab 00 94 a3 |W+......N.......|
00000220 ba 85 08 bf 4c a0 a6 0c 26 dd b0 b2 e5 2c 82 5d |....L...&....,.]|
00000230 53 e0 3b 2e 8e 03 98 c2 d7 f2 67 bc 48 56 bf bb |S.;.......g.HV..|
00000240 9b 62 11 33 38 a1 bb 8d 36 e0 63 5c b8 11 c7 4d |.b.38...6.c\...M|
00000250 00 19 33 73 e8 e2 96 46 e2 ff 44 95 84 05 7f 58 |..3s...F..D....X|
00000260 d1 fb 92 86 a5 65 43 c4 f5 90 a0 f4 00 5b fe 73 |.....eC......[.s|
00000270 86 ec 69 b1 c7 a8 51 d3 3a 6e 4e fa 9c 2c a7 09 |..i...Q.:nN..,..|
00000280 f3 8b 74 39 6d a6 5d d0 43 a2 e6 30 |..t9m.].C..0|
>>> Flow 13 (server to client)
00000000 d9 20 09 e1 7e 5e 59 b2 ae 81 50 fa 2b e7 82 42 |. ..~^Y...P.+..B|
00000010 6b 14 29 2a 1e 44 96 52 30 6a b5 ed |k.)*.D.R0j..|
00000000 33 a5 4d 90 84 2c 28 47 86 8c fc 3b 1e 21 e4 54 |3.M..,(G...;.!.T|
00000010 a9 2a 37 82 53 41 fe f2 40 ef b3 c3 |.*7.SA..@...|
>>> Flow 14 (client to server)
00000000 9f 1b 62 56 0a 21 c5 b5 5d 76 ae 1b f9 c1 70 3f |..bV.!..]v....p?|
00000010 b6 09 a5 be 89 12 04 b1 9a f8 3f bf 90 a8 e2 c0 |..........?.....|
00000020 58 68 0a 8b 17 63 3c 43 2d 33 7d 89 8c 32 90 3f |Xh...c<C-3}..2.?|
00000030 21 88 63 86 |!.c.|
00000000 9b c2 b4 83 be 85 e9 65 ef 00 3d 7e d4 4c 8f 1a |.......e..=~.L..|
00000010 d7 3a 45 d7 30 b2 4c c2 fe 3e 44 bd 25 e7 96 fd |.:E.0.L..>D.%...|
00000020 d5 22 6a 21 12 d0 89 b7 8a 97 b3 48 c6 27 04 69 |."j!.......H.'.i|
00000030 0d eb 54 0a |..T.|
>>> Flow 15 (server to client)
00000000 23 d0 5d 42 65 ba 45 89 75 8c 88 76 69 e3 80 b6 |#.]Be.E.u..vi...|
00000010 8d 3b d5 b5 84 27 05 03 43 19 67 20 c1 c8 08 d8 |.;...'..C.g ....|
00000020 d0 60 b3 e3 c4 1e 32 25 a0 1f 44 6e |.`....2%..Dn|
00000000 9c 34 68 72 6f 87 4e 6f 08 a5 1e ac 43 31 d3 74 |.4hro.No....C1.t|
00000010 9f 0a 0b e2 c0 3c aa ae 82 35 1f c7 3c 0d 04 c9 |.....<...5..<...|
00000020 67 c9 ec fd 39 e5 15 16 fa ee ca 89 |g...9.......|
>>> Flow 16 (client to server)
00000000 42 00 21 ac 15 92 63 b5 bd e6 7a 1d f7 06 b9 24 |B.!...c...z....$|
00000010 cf 18 31 ac a4 49 fd a1 d2 9f 46 06 04 f7 7b 76 |..1..I....F...{v|
00000020 9a 8f 07 c0 55 a8 01 e3 bb e6 b9 02 4b c4 86 d3 |....U.......K...|
00000030 b1 aa 9f 21 d1 cc 03 ea c7 61 72 df be c7 d2 38 |...!.....ar....8|
00000040 93 b4 ad 5f 4b ec e8 8d 85 70 31 14 11 e0 d6 3a |..._K....p1....:|
00000050 62 62 bb e7 c6 a5 3c 9e |bb....<.|
00000000 14 7e 97 99 c7 2b 99 94 40 0c 0d 0a a3 2c 69 8a |.~...+..@....,i.|
00000010 59 bd ab ab 60 dd 35 c9 f7 59 02 fc 08 05 0e a7 |Y...`.5..Y......|
00000020 3b de d1 9d 37 29 09 64 1b ed 10 f7 82 7b d6 8c |;...7).d.....{..|
00000030 fc 89 54 ca ca 67 86 c0 6a 60 f8 cf b3 c6 08 fa |..T..g..j`......|
00000040 3d f6 68 d1 c1 1b 5b ea e5 cf d4 24 8f b4 9b e7 |=.h...[....$....|
00000050 b6 95 a9 2e ea 50 38 75 |.....P8u|
>>> Flow 17 (server to client)
00000000 b1 ec 5a ea 52 a1 03 42 b3 1a b1 42 ea 71 fd 16 |..Z.R..B...B.q..|
00000010 06 2c 3d 74 f5 4f 9e 76 2f cd 3e 57 75 97 eb e6 |.,=t.O.v/.>Wu...|
00000020 21 ba aa 47 ae 3c 11 92 47 55 90 44 9d 08 01 68 |!..G.<..GU.D...h|
00000030 f6 e2 71 7d 2f 0c 64 83 4d 4d 58 96 14 cd 1e 70 |..q}/.d.MMX....p|
00000040 3f 7d ad e3 4f f2 65 45 4d 4d 0f 14 c3 99 a8 ca |?}..O.eEMM......|
00000050 7a 65 f2 75 59 02 e5 1c 19 13 96 f5 05 57 ac 0b |ze.uY........W..|
00000060 d7 39 86 d1 76 55 0a 99 93 5a f0 fc 39 41 19 38 |.9..vU...Z..9A.8|
00000070 af 6f 24 90 5e fe e1 e2 47 6c d7 24 |.o$.^...Gl.$|
00000000 fa cf 4c 38 01 c5 20 b6 3c cd ec b8 9e ae 50 8f |..L8.. .<.....P.|
00000010 bd 5e 77 6a e1 46 ac 25 c8 a3 7c e7 f7 f5 a4 3f |.^wj.F.%..|....?|
00000020 1f f8 4a 68 0e cb e6 a8 3f 9e e7 64 61 3f 42 7e |..Jh....?..da?B~|
00000030 d0 e0 f1 82 15 ab 65 e9 c4 3f 8e 36 7a 84 0c c3 |......e..?.6z...|
00000040 36 90 6d 28 06 89 fa 6a 53 f3 ca 45 8d 52 5c a2 |6.m(...jS..E.R\.|
00000050 50 97 0f 7d e1 61 70 d1 ce bd d8 6f 1c 63 39 13 |P..}.ap....o.c9.|
00000060 10 66 38 a4 8f 0d 90 ff dc e0 57 f5 79 94 3a c8 |.f8.......W.y.:.|
00000070 39 d7 ff 31 8e 62 7f ca 69 8e 37 da |9..1.b..i.7.|
>>> Flow 18 (client to server)
00000000 98 d1 51 36 8b d9 89 f9 8b 1b bf 5a e6 aa ab 28 |..Q6.......Z...(|
00000010 8a 68 c3 a6 7d d9 f1 01 a1 93 fe 62 c1 49 4f 46 |.h..}......b.IOF|
00000020 42 52 76 45 59 46 02 a0 19 5d 6e 7f 5c ce ab 85 |BRvEYF...]n.\...|
00000030 c5 0d 89 6e f2 c4 aa 99 71 f8 35 5d a9 3c 6a 42 |...n....q.5].<jB|
00000040 a0 4e f0 48 0b 9c 72 68 0d b9 a7 97 24 b4 50 7d |.N.H..rh....$.P}|
00000050 49 f8 c1 b1 a9 24 a2 b5 99 33 f4 72 74 8c 35 9e |I....$...3.rt.5.|
00000000 f8 55 f0 f7 44 40 b9 e2 bb aa e3 09 00 11 f8 19 |.U..D@..........|
00000010 d3 06 9e 95 e5 84 d5 d5 3f 6a d0 89 fe 18 86 3d |........?j.....=|
00000020 a6 02 6e 7f af 0d 37 e4 c1 3e 3c 64 85 13 ea e3 |..n...7..><d....|
00000030 34 5e 5a d7 d2 c6 be d3 85 a1 a7 d6 38 22 17 21 |4^Z.........8".!|
00000040 91 70 06 b5 7a 2f 16 fe 0f 39 c5 47 68 12 df 03 |.p..z/...9.Gh...|
00000050 8f b6 29 a6 17 7c 2f c6 3a 3f b2 7a 83 7f 17 14 |..)..|/.:?.z....|
+4 -11
View File
@@ -16,13 +16,6 @@ import (
// wire. No message decoding is done, to minimize the impact on timing.
const debugTransport = false
const (
gcm128CipherID = "aes128-gcm@openssh.com"
gcm256CipherID = "aes256-gcm@openssh.com"
aes128cbcID = "aes128-cbc"
tripledescbcID = "3des-cbc"
)
// packetConn represents a transport that implements packet based
// operations.
type packetConn interface {
@@ -92,14 +85,14 @@ func (t *transport) setInitialKEXDone() {
// prepareKeyChange sets up key material for a keychange. The key changes in
// both directions are triggered by reading and writing a msgNewKey packet
// respectively.
func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error {
ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult)
func (t *transport) prepareKeyChange(algs *NegotiatedAlgorithms, kexResult *kexResult) error {
ciph, err := newPacketCipher(t.reader.dir, algs.Read, kexResult)
if err != nil {
return err
}
t.reader.pendingKeyChange <- ciph
ciph, err = newPacketCipher(t.writer.dir, algs.w, kexResult)
ciph, err = newPacketCipher(t.writer.dir, algs.Write, kexResult)
if err != nil {
return err
}
@@ -259,7 +252,7 @@ var (
// setupKeys sets the cipher and MAC keys from kex.K, kex.H and sessionId, as
// described in RFC 4253, section 6.4. direction should either be serverKeys
// (to setup server->client keys) or clientKeys (for client->server keys).
func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (packetCipher, error) {
func newPacketCipher(d direction, algs DirectionAlgorithms, kex *kexResult) (packetCipher, error) {
cipherMode := cipherModes[algs.Cipher]
iv := make([]byte, cipherMode.ivSize)
Binary file not shown.
+753 -4360
View File
File diff suppressed because it is too large Load Diff
+32
View File
@@ -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)
}
}
}
+58 -3
View File
@@ -18,11 +18,23 @@
// available.
package fallback
import "crypto/x509"
import (
"crypto/x509"
_ "embed"
"fmt"
"time"
)
//go:embed bundle.der
var rawCerts []byte
func init() {
x509.SetFallbackRoots(newFallbackCertPool())
}
func newFallbackCertPool() *x509.CertPool {
p := x509.NewCertPool()
for _, c := range parsedCertificates {
for _, c := range mustParse(unparsedCertificates) {
if len(c.constraints) == 0 {
p.AddCert(c.cert)
} else {
@@ -36,5 +48,48 @@ func init() {
})
}
}
x509.SetFallbackRoots(p)
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
}
+11
View File
@@ -0,0 +1,11 @@
package fallback
import "testing"
// BenchmarkInitTime benchmarks the time it takes to parse all certificates
// in this bundle, it corresponds to the init time of this package.
func BenchmarkInitTime(b *testing.B) {
for range b.N {
newFallbackCertPool()
}
}
+19 -70
View File
@@ -11,7 +11,6 @@ package main
import (
"bytes"
"crypto/sha256"
"encoding/pem"
"flag"
"fmt"
"go/format"
@@ -30,67 +29,6 @@ const tmpl = `// Code generated by gen_fallback_bundle.go; DO NOT EDIT.
package fallback
import (
"crypto/x509"
"encoding/pem"
"fmt"
"time"
)
type unparsedCertificate struct {
cn string
sha256Hash string
pem string
// possible constraints
distrustAfter string
}
type parsedCertificate struct {
cert *x509.Certificate
constraints []func([]*x509.Certificate) error
}
func mustParse(unparsedCerts []unparsedCertificate) []parsedCertificate {
var b []parsedCertificate
for _, unparsed := range unparsedCerts {
block, rest := pem.Decode([]byte(unparsed.pem))
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 {
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
}
var parsedCertificates = mustParse(unparsedCertificates)
var unparsedCertificates = []unparsedCertificate{
`
@@ -98,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)")
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)")
)
func main() {
@@ -153,8 +92,9 @@ func main() {
return subjI < subjJ
})
b := new(bytes.Buffer)
b.WriteString(tmpl)
rawCertsData := new(bytes.Buffer)
goSrcOut := new(bytes.Buffer)
goSrcOut.WriteString(tmpl)
for _, c := range certs {
var constraints []string
var skip bool
@@ -171,19 +111,24 @@ func main() {
if skip {
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(),
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 {
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 {
log.Fatalf("failed to format source: %s", err)
}
@@ -191,4 +136,8 @@ func main() {
if err := os.WriteFile(*output, formatted, 0644); err != nil {
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)
}
}