Compare commits

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

Change-Id: Ib391e4f2f09056cb025de97d5d8f2640859d9163
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/525335
Run-TryBot: 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>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Gopher Robot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
2023-09-05 14:51:56 +00:00
Gopher Robot b4ddeeda5b go.mod: update golang.org/x dependencies
Update golang.org/x dependencies to their latest tagged versions.
Once this CL is submitted, and post-submit testing succeeds on all
first-class ports across all supported Go versions, this repository
will be tagged with its next minor version.

Change-Id: Id40feba36dfc31c7033c91b952ec824a38e048ee
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/515976
Auto-Submit: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Gopher Robot <gobot@golang.org>
2023-08-04 16:57:07 +00:00
Nicola Murino edc325d13a ssh: fix call to Fatalf from a non-test goroutine
Also fix some redundant type declarations.

Change-Id: Iad2950b67b1ec2e2590c59393b8ad15421ed3add
GitHub-Last-Rev: 41cf552f11387208491dee7b867050475043b25e
GitHub-Pull-Request: golang/crypto#263
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/505798
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: David Chase <drchase@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
2023-07-31 18:14:41 +00:00
Nicola Murino eab9315960 ssh: add diffie-hellman-group16-sha512 kex
This group is disabled by default because it is a bit slower than
the others.
The group18-sha512 variant is too slow to include.

Benchstat results including diffie-hellman-group18-sha512:

name                                           time/op
Kexes/diffie-hellman-group-exchange-sha256-12  22.6ms ± 9%
Kexes/diffie-hellman-group18-sha512-12          1.15s ±11%
Kexes/ecdh-sha2-nistp384-12                    3.91ms ± 6%
Kexes/ecdh-sha2-nistp256-12                     304µs ± 5%
Kexes/curve25519-sha256@libssh.org-12           413µs ± 7%
Kexes/ecdh-sha2-nistp521-12                    11.6ms ±13%
Kexes/curve25519-sha256-12                      361µs ± 5%
Kexes/diffie-hellman-group-exchange-sha1-12    22.9ms ± 9%
Kexes/diffie-hellman-group1-sha1-12            3.59ms ± 6%
Kexes/diffie-hellman-group14-sha1-12           22.1ms ±11%
Kexes/diffie-hellman-group14-sha256-12         21.6ms ± 8%
Kexes/diffie-hellman-group16-sha512-12          138ms ± 9%

name                                           alloc/op
Kexes/diffie-hellman-group-exchange-sha256-12  67.8kB ± 1%
Kexes/diffie-hellman-group18-sha512-12          243kB ± 9%
Kexes/ecdh-sha2-nistp384-12                    13.9kB ± 0%
Kexes/ecdh-sha2-nistp256-12                    12.1kB ± 0%
Kexes/curve25519-sha256@libssh.org-12          8.22kB ± 0%
Kexes/ecdh-sha2-nistp521-12                    16.5kB ± 0%
Kexes/curve25519-sha256-12                     8.22kB ± 0%
Kexes/diffie-hellman-group-exchange-sha1-12    67.5kB ± 0%
Kexes/diffie-hellman-group1-sha1-12            34.9kB ± 0%
Kexes/diffie-hellman-group14-sha1-12           61.9kB ± 0%
Kexes/diffie-hellman-group14-sha256-12         62.0kB ± 0%
Kexes/diffie-hellman-group16-sha512-12          117kB ± 0%

name                                           allocs/op
Kexes/diffie-hellman-group-exchange-sha256-12     314 ± 0%
Kexes/diffie-hellman-group18-sha512-12            271 ± 4%
Kexes/ecdh-sha2-nistp384-12                       243 ± 0%
Kexes/ecdh-sha2-nistp256-12                       213 ± 0%
Kexes/curve25519-sha256@libssh.org-12             168 ± 0%
Kexes/ecdh-sha2-nistp521-12                       245 ± 0%
Kexes/curve25519-sha256-12                        168 ± 0%
Kexes/diffie-hellman-group-exchange-sha1-12       314 ± 0%
Kexes/diffie-hellman-group1-sha1-12               255 ± 0%
Kexes/diffie-hellman-group14-sha1-12              255 ± 0%
Kexes/diffie-hellman-group14-sha256-12            255 ± 0%
Kexes/diffie-hellman-group16-sha512-12            256 ± 0%

Change-Id: Id119401fda7e417675325f37e3d442e70585206c
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/506839
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
2023-07-31 17:46:52 +00:00
Nicola Murino ddfa82138e ssh: ignore invalid MACs and KEXs just like we do for ciphers
Tighter validation could cause backwards incompatibility issues, eg
configurations with valid and invalid MACs, KEXs, ciphers currently work
if a supported algorithm is negotiated and that's also the scenario of
removing support for an existing algorithm.

Fixes golang/go#39397

Change-Id: If90253ba89e1d8f732cc1e1c3d24fe0a1e2dac71
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/512175
Run-TryBot: Han-Wen Nienhuys <hanwen@google.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Han-Wen Nienhuys <hanwen@google.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: David Chase <drchase@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-07-31 15:35:32 +00:00
Gopher Robot d08e19beac x509roots/fallback: update bundle
This is an automated CL which updates the NSS root bundle.

Change-Id: Ic70152e674c60e48e85d96eab244add9b4fa5eb8
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/512595
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Gopher Robot <gobot@golang.org>
Auto-Submit: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-07-25 17:50:45 +00:00
Dmitri Shuralyov 12e1fcd8c0 internal/wycheproof: skip all tests in short test mode
The testdata for this package is around 8 MB and downloaded dynamically
via 'go mod download' from its canonical source rather than being copied
to this repository. We're moving towards disallowing all network use in
short test mode, including proxy.golang.org, so add a corresponding test
skip.

Needing to lookup a go test flag is unfortunate, but I don't know of a
less bad available option while the test does the download in TestMain.

On balance, it becomes viable to no longer disable the checksum database
since the test will only run on builders that permit internet use and so
sum.golang.org should just work.

Change-Id: Iaffe3899351da375928aaba114c4875f5438336b
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/510695
Run-TryBot: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-07-18 15:17:25 +00:00
Nicola Murino 3f8f0645c2 ssh: prefer sha256 based RSA key algorithms
sha256 is more optimized than sha512 in Go and is secure enough
so prefer sha256 over sha512.

Change-Id: I3fcf7457791e3ef4539e97049aa905dcd293499d
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/507556
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2023-07-14 16:27:34 +00:00
Nicola Murino 5df3b59941 ssh: disable client agent tests on Windows
ssh-agent is implemented as a Windows service and exposed on a
named pipe. We don't currently support it.

See golang/go#60981

Change-Id: Iebdc42db30b37a87ac0766231b16aff3f17b3f56
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/509035
Run-TryBot: Heschi Kreinick <heschi@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Auto-Submit: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-07-13 13:29:36 +00:00
Boshi Lian 2e82bdd171 fix TestValidTerminalMode: missing output from echo SHELL $SHELL
add leading `echo` to have better compatibility

before

```
 go test -run ^TestValidTerminalMode -v
=== RUN   TestValidTerminalMode
    session_test.go:261: echo SHELL $SHELL && stty -a && exit:
        Last login: Thu Jul  6 12:24:38 2023 from 192.168.200.1
SHELL /bin/bashubuntu:~$
        speed 38400 baud; rows 80; columns 40;
        line = 0;
        intr = ^C; quit = ^\; erase = ^?;
        kill = ^U; eof = ^D; eol = <undef>;
        eol2 = <undef>; swtch = <undef>;
        start = ^Q; stop = ^S; susp = ^Z;
        rprnt = ^R; werase = ^W; lnext = ^V;
        discard = ^O; min = 1; time = 0;
        -parenb -parodd -cmspar cs8 -hupcl
        -cstopb cread -clocal -crtscts
        -ignbrk -brkint -ignpar -parmrk -inpck
        -istrip -inlcr -igncr icrnl ixon -ixoff
        -iuclc -ixany -imaxbel -iutf8
        opost -olcuc -ocrnl onlcr -onocr -onlret
        -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
        isig icanon iexten -echo echoe echok
        -echonl -noflsh -xcase -tostop -echoprt
        echoctl echoke -flusho -extproc
        logout
    session_test.go:266: missing output from echo SHELL $SHELL
```

after

```
 go test -run ^TestValidTerminalMode -v
=== RUN   TestValidTerminalMode
    session_test.go:261: echo SHELL $SHELL && stty -a && exit:
        Last login: Thu Jul  6 12:24:38 2023 from 192.168.200.1
        bolian@ubuntu:~$
        SHELL /bin/bash
        speed 38400 baud; rows 80; columns 40;
        line = 0;
        intr = ^C; quit = ^\; erase = ^?;
        kill = ^U; eof = ^D; eol = <undef>;
        eol2 = <undef>; swtch = <undef>;
        start = ^Q; stop = ^S; susp = ^Z;
        rprnt = ^R; werase = ^W; lnext = ^V;
        discard = ^O; min = 1; time = 0;
        -parenb -parodd -cmspar cs8 -hupcl
        -cstopb cread -clocal -crtscts
        -ignbrk -brkint -ignpar -parmrk -inpck
        -istrip -inlcr -igncr icrnl ixon -ixoff
        -iuclc -ixany -imaxbel -iutf8
        opost -olcuc -ocrnl onlcr -onocr -onlret
        -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
        isig icanon iexten -echo echoe echok
        -echonl -noflsh -xcase -tostop -echoprt
        echoctl echoke -flusho -extproc
        logout
--- PASS: TestValidTerminalMode (0.06s)
```

Change-Id: If60c040edb8c78a7d86bf58a6be47636d9e8f173
GitHub-Last-Rev: a2cc1b1af09e47df82fcb8685d829dfed945e8b0
GitHub-Pull-Request: golang/crypto#264
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/508115
Reviewed-by: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Heschi Kreinick <heschi@google.com>
Auto-Submit: Heschi Kreinick <heschi@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
2023-07-11 16:17:43 +00:00
Nicola Murino 64e0e99383 ssh: fix RSA certificate and public key authentication with older clients
After adding support for rsa-sha2-256/512 on the server side some edge
cases started to arise with old clients:

1) public key authentication with gpg-agent < 2.2.6 fails because we
   receive ssh-rsa as signature format and rsa-sha2-256 or rsa-sha2-512
   as algorithm.
   This is a bug in gpg-agent fixed in this commit:

   https://github.com/gpg/gnupg/commit/80b775bdbb852aa4a80292c9357e5b1876110c00

2) certificate authentication fails with OpenSSH 7.2-7.7 because we
   receive ssh-rsa-cert-v01@openssh.com as algorithm and rsa-sha2-256
   or rsa-sha2-512 as signature format.

This patch is based on CL 412854 and has been tested with every version
of OpenSSH from 7.1 to 7.9 and OpenSSH 9.3.

Fixes golang/go#53391

Change-Id: Id71f596f73d84efb5c76d6d5388432cccad3e3b1
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/506835
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2023-07-10 19:57:54 +00:00
Nicola Murino 23b1b90df2 ssh: prefer sha256 based MAC algorithms
sha256 is more optimized than sha512 in Go and is secure enough
so prefer sha256 over sha512.

Fixes golang/go#61138

Change-Id: I7658808655367f1ab5f4ac8b52e6b20bd30ebf87
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/507555
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Joedian Reid <joedian@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
2023-07-05 20:33:07 +00:00
Gopher Robot e98487292d go.mod: update golang.org/x dependencies
Update golang.org/x dependencies to their latest tagged versions.
Once this CL is submitted, and post-submit testing succeeds on all
first-class ports across all supported Go versions, this repository
will be tagged with its next minor version.

Change-Id: Icede82501a3703fcaad524f6b91ff6e5452b4547
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/507837
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Gopher Robot <gobot@golang.org>
Auto-Submit: Gopher Robot <gobot@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-07-05 13:50:10 +00:00
Roland Shoemaker 183630ada7 x509roots: generate a stable sort, for real this time
Sort based on the stringified subject, then break ties based on the raw
DER (which will, actually, be unique this time).

Change-Id: I3dd912fb19b103e92fabfb4562e31c6dcec40614
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/505695
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
2023-06-23 17:05:55 +00:00
Dmitri Shuralyov a9e447dde7 x509roots/fallback: add //go:build go1.20 to bundle.go
Package fallback has no API; its only purpose is to automatically call
x509.SetFallbackRoots with a set of fallback roots. That API was added
in Go 1.20, hence the go1.20 build constraint in fallback.go.

Add that constraint to bundle.go too, so that it fails to build rather
than quietly being a no-op in Go 1.19.

Also simplify Write(fmt.Sprintf()) into fmt.Fprintf while here.

Add a temporary workaround for go.dev/issue/52287.
It has no effect on the public API in this module.

For golang/go#57792.
For golang/go#52287.

Change-Id: I1fe13f7d54b07b0b031e8bae685cffd7a8160165
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/505578
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
2023-06-23 16:46:51 +00:00
Stan Hu 64c3993f5c ssh: add hmac-sha2-512
This adds support for hmac-sha2-512 to ensure compatibility with SSH clients that request this MAC algorithm.

This rebases https://github.com/golang/crypto/pull/18.

Change-Id: Ia103c10a8b7e2e8dde556d5c36550eb5fa6bc1f6
GitHub-Last-Rev: 987ccae2bc7ae5e90a482d8797351c39dcb9bf33
GitHub-Pull-Request: golang/crypto#257
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/501455
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Commit-Queue: Han-Wen Nienhuys <hanwen@google.com>
Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Han-Wen Nienhuys <hanwen@google.com>
Run-TryBot: Han-Wen Nienhuys <hanwen@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-22 14:36:59 +00:00
Roland Shoemaker 5fe8145aca x509roots: remove list hash and generation date, change ordering
This makes the automated update workflow simpler.

Also switch the ordering from human readable subject (which is not
necessarily unique), to the raw SPKI (which should always be unique).
This makes it somewhat harder to read to a human (since it'll appear a
little jumbled) but results in a stable sort.

Note this results in adding two new roots, which were added since we
last generated the bundle.

Change-Id: Id4d34bf9e98164e7b2fc4f06f9b46b63c0013d23
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/504597
Run-TryBot: Roland Shoemaker <roland@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-20 21:21:47 +00:00
Roland Shoemaker 043e94c17a x509roots: fix generate script argument checking
Check for supply of both arguments forgot that the URL is set by
default. Instead just let the local path supersede the URL.

Change-Id: I0499137c99c735e8e453ff1c2a925435f3cd8039
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/504596
Run-TryBot: Roland Shoemaker <roland@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
2023-06-20 18:34:45 +00:00
Roland Shoemaker 0d502d7cd6 x509roots: use "generate" build tag
Since go generate sets it automatically.

Change-Id: I4623e523392140c0472b250ac99c8c3fa31e5b15
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/504595
Auto-Submit: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
2023-06-20 18:28:47 +00:00
Bryan C. Mills 0ff60057bb ssh/test: set a timeout and WaitDelay on sshd subcommands
This uses a copy of testenv.Command copied from the main repo, with
light edits to allow the testenv helpers to build with Go 1.19.

The testenv helper revealed an exec.Command leak in TestCertLogin, so
we also fix that leak and simplify server cleanup using
testing.T.Cleanup.

For golang/go#60099.
Fixes golang/go#60343.

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

Change-Id: If19e251a79af033583e6968766b7a831741cebb7
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/502518
Reviewed-by: Heschi Kreinick <heschi@google.com>
Run-TryBot: Gopher Robot <gobot@golang.org>
Reviewed-by: Carlos Amedee <carlos@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Gopher Robot <gobot@golang.org>
2023-06-12 19:51:08 +00:00
Heschi Kreinick 22c9997952 go.mod: tell x repo tagging to ignore dep on net
CL 475438 introduced a cycle between net and crypto. This direction is
less important, so have the tagging process ignore it.

Change-Id: Ie424fef0238702a5a16aba79bb60f86f39dc66eb
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/502595
Auto-Submit: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Damien Neil <dneil@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
2023-06-12 17:20:30 +00:00
Roland Shoemaker d0b316056b x509roots: add new module
Adds the nss parser, under x509roots/nss, and the fallback
module/package, with the initial generated bundle.

Fixes golang/go#57792

Change-Id: Iebb1052e49126fa5baba1236f4ebc8dd8a823179
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/462036
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
2023-06-09 17:23:14 +00:00
Stefan Klein 16222386f4 Add support for "hmac-sha2-512-etm@openssh.com"
Change-Id: I0203881afd7ad72e68f76650817451d7e292c91b
GitHub-Last-Rev: 42b4119e1987e7a46aa06a2b142d5fd3ef6f216a
GitHub-Pull-Request: golang/crypto#129
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/226982
Run-TryBot: Han-Wen Nienhuys <hanwen@google.com>
Reviewed-by: Han-Wen Nienhuys <hanwen@google.com>
Auto-Submit: Han-Wen Nienhuys <hanwen@google.com>
Reviewed-by: David Chase <drchase@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2023-06-05 16:42:04 +00:00
Gopher Robot a4e984136a go.mod: update golang.org/x dependencies
Update golang.org/x dependencies to their latest tagged versions.
Once this CL is submitted, and post-submit testing succeeds on all
first-class ports across all supported Go versions, this repository
will be tagged with its next minor version.

Change-Id: I1eb2365549b72cbad23fa7c355f427c6ed75e450
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/493575
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2023-05-08 17:07:49 +00:00
Tobias Klauser 3ef8056490 ssh/test: enable on solaris
Change-Id: Icf9c867e64ef68f6f46dd7d4cec07cf7c315c2ad
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/490155
Reviewed-by: Bryan Mills <bcmills@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Tobias Klauser <tobias.klauser@gmail.com>
2023-05-04 16:27:40 +00:00
Tobias Klauser a8cc953517 ssh: skip unsupported tests on wasip1
Updates golang/go#32840
Updates golang/go#58141

Change-Id: Ib4425c1743d417920745205586af250dbf80c7e4
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/485695
Auto-Submit: Tobias Klauser <tobias.klauser@gmail.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
2023-04-18 17:07:48 +00:00
Bryan C. Mills 7d6d3f5d4a ssh/test: skip TestValidTerminalMode on non-Bourne shells
Fixes golang/go#38037.

Change-Id: Ide77dddc9f57b3f0318a419a1474e11215623b64
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/485175
Run-TryBot: Bryan Mills <bcmills@google.com>
Commit-Queue: Bryan Mills <bcmills@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2023-04-17 17:20:41 +00:00
Mateusz Poliwczak 1faeef9713 cryptobyte: reject Object Identifiers with leading 0x80
Change-Id: Ie3a1b53e801077cd86963799e644b9783943933c
GitHub-Last-Rev: 6629bd74f1874eb9fde8e72bfb444ebf9073a1ab
GitHub-Pull-Request: golang/crypto#255
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/483955
Run-TryBot: Mateusz Poliwczak <mpoliwczak34@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Run-TryBot: Ian Lance Taylor <iant@google.com>
2023-04-12 18:30:42 +00:00
37 changed files with 6204 additions and 190 deletions
+8
View File
@@ -431,6 +431,14 @@ func (s *String) readBase128Int(out *int) bool {
}
ret <<= 7
b := s.read(1)[0]
// ITU-T X.690, section 8.19.2:
// The subidentifier shall be encoded in the fewest possible octets,
// that is, the leading octet of the subidentifier shall not have the value 0x80.
if i == 0 && b == 0x80 {
return false
}
ret |= int(b & 0x7f)
if b&0x80 == 0 {
*out = ret
+1
View File
@@ -276,6 +276,7 @@ func TestASN1ObjectIdentifier(t *testing.T) {
{[]byte{6, 7, 85, 0x02, 0x85, 0xc7, 0xcc, 0xfb, 0x01}, true, []int{2, 5, 2, 1492336001}},
{[]byte{6, 7, 0x55, 0x02, 0x87, 0xff, 0xff, 0xff, 0x7f}, true, []int{2, 5, 2, 2147483647}}, // 2**31-1
{[]byte{6, 7, 0x55, 0x02, 0x88, 0x80, 0x80, 0x80, 0x00}, false, []int{}}, // 2**31
{[]byte{6, 3, 85, 0x80, 0x02}, false, []int{}}, // leading 0x80 octet
}
for i, test := range testData {
+4 -4
View File
@@ -3,9 +3,9 @@ module golang.org/x/crypto
go 1.17
require (
golang.org/x/net v0.9.0
golang.org/x/sys v0.7.0
golang.org/x/term v0.7.0
golang.org/x/net v0.10.0 // tagx:ignore
golang.org/x/sys v0.12.0
golang.org/x/term v0.12.0
)
require golang.org/x/text v0.9.0 // indirect
require golang.org/x/text v0.13.0 // indirect
+10 -7
View File
@@ -7,8 +7,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -18,19 +18,22 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+120
View File
@@ -0,0 +1,120 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package testenv
import (
"context"
"os"
"os/exec"
"reflect"
"strconv"
"testing"
"time"
)
// CommandContext is like exec.CommandContext, but:
// - skips t if the platform does not support os/exec,
// - sends SIGQUIT (if supported by the platform) instead of SIGKILL
// in its Cancel function
// - if the test has a deadline, adds a Context timeout and WaitDelay
// for an arbitrary grace period before the test's deadline expires,
// - fails the test if the command does not complete before the test's deadline, and
// - sets a Cleanup function that verifies that the test did not leak a subprocess.
func CommandContext(t testing.TB, ctx context.Context, name string, args ...string) *exec.Cmd {
t.Helper()
var (
cancelCtx context.CancelFunc
gracePeriod time.Duration // unlimited unless the test has a deadline (to allow for interactive debugging)
)
if t, ok := t.(interface {
testing.TB
Deadline() (time.Time, bool)
}); ok {
if td, ok := t.Deadline(); ok {
// Start with a minimum grace period, just long enough to consume the
// output of a reasonable program after it terminates.
gracePeriod = 100 * time.Millisecond
if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
scale, err := strconv.Atoi(s)
if err != nil {
t.Fatalf("invalid GO_TEST_TIMEOUT_SCALE: %v", err)
}
gracePeriod *= time.Duration(scale)
}
// If time allows, increase the termination grace period to 5% of the
// test's remaining time.
testTimeout := time.Until(td)
if gp := testTimeout / 20; gp > gracePeriod {
gracePeriod = gp
}
// When we run commands that execute subprocesses, we want to reserve two
// grace periods to clean up: one for the delay between the first
// termination signal being sent (via the Cancel callback when the Context
// expires) and the process being forcibly terminated (via the WaitDelay
// field), and a second one for the delay becween the process being
// terminated and and the test logging its output for debugging.
//
// (We want to ensure that the test process itself has enough time to
// log the output before it is also terminated.)
cmdTimeout := testTimeout - 2*gracePeriod
if cd, ok := ctx.Deadline(); !ok || time.Until(cd) > cmdTimeout {
// Either ctx doesn't have a deadline, or its deadline would expire
// after (or too close before) the test has already timed out.
// Add a shorter timeout so that the test will produce useful output.
ctx, cancelCtx = context.WithTimeout(ctx, cmdTimeout)
}
}
}
cmd := exec.CommandContext(ctx, name, args...)
// Set the Cancel and WaitDelay fields only if present (go 1.20 and later).
// TODO: When Go 1.19 is no longer supported, remove this use of reflection
// and instead set the fields directly.
if cmdCancel := reflect.ValueOf(cmd).Elem().FieldByName("Cancel"); cmdCancel.IsValid() {
cmdCancel.Set(reflect.ValueOf(func() error {
if cancelCtx != nil && ctx.Err() == context.DeadlineExceeded {
// The command timed out due to running too close to the test's deadline.
// There is no way the test did that intentionally — it's too close to the
// wire! — so mark it as a test failure. That way, if the test expects the
// command to fail for some other reason, it doesn't have to distinguish
// between that reason and a timeout.
t.Errorf("test timed out while running command: %v", cmd)
} else {
// The command is being terminated due to ctx being canceled, but
// apparently not due to an explicit test deadline that we added.
// Log that information in case it is useful for diagnosing a failure,
// but don't actually fail the test because of it.
t.Logf("%v: terminating command: %v", ctx.Err(), cmd)
}
return cmd.Process.Signal(Sigquit)
}))
}
if cmdWaitDelay := reflect.ValueOf(cmd).Elem().FieldByName("WaitDelay"); cmdWaitDelay.IsValid() {
cmdWaitDelay.Set(reflect.ValueOf(gracePeriod))
}
t.Cleanup(func() {
if cancelCtx != nil {
cancelCtx()
}
if cmd.Process != nil && cmd.ProcessState == nil {
t.Errorf("command was started, but test did not wait for it to complete: %v", cmd)
}
})
return cmd
}
// Command is like exec.Command, but applies the same changes as
// testenv.CommandContext (with a default Context).
func Command(t testing.TB, name string, args ...string) *exec.Cmd {
t.Helper()
return CommandContext(t, context.Background(), name, args...)
}
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2021 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 windows || plan9 || (js && wasm) || wasip1
package testenv
import (
"os"
)
// Sigquit is the signal to send to kill a hanging subprocess.
// On Unix we send SIGQUIT, but on non-Unix we only have os.Kill.
var Sigquit = os.Kill
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2021 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 unix
package testenv
import (
"syscall"
)
// Sigquit is the signal to send to kill a hanging subprocess.
// Send SIGQUIT to get a stack trace.
var Sigquit = syscall.SIGQUIT
+6 -2
View File
@@ -11,6 +11,7 @@ import (
"crypto/x509"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"log"
"os"
@@ -28,6 +29,11 @@ const wycheproofModVer = "v0.0.0-20191219022705-2196000605e4"
var wycheproofTestVectorsDir string
func TestMain(m *testing.M) {
flag.Parse()
if flag.Lookup("test.short").Value.(flag.Getter).Get().(bool) {
log.Println("skipping test that downloads testdata via 'go mod download' in short mode")
os.Exit(0)
}
if _, err := exec.LookPath("go"); err != nil {
log.Printf("skipping test because 'go' command is unavailable: %v", err)
os.Exit(0)
@@ -42,8 +48,6 @@ func TestMain(m *testing.M) {
// can be used in the following tests.
path := "github.com/google/wycheproof@" + wycheproofModVer
cmd := exec.Command("go", "mod", "download", "-json", path)
// TODO: enable the sumdb once the Trybots proxy supports it.
cmd.Env = append(os.Environ(), "GONOSUMDB=*")
output, err := cmd.Output()
if err != nil {
log.Fatalf("failed to run `go mod download -json %s`, output: %s", path, output)
+5 -1
View File
@@ -29,6 +29,9 @@ func startOpenSSHAgent(t *testing.T) (client ExtendedAgent, socket string, clean
// types supported vary by platform.
t.Skip("skipping test due to -short")
}
if runtime.GOOS == "windows" {
t.Skip("skipping on windows, we don't support connecting to the ssh-agent via a named pipe")
}
bin, err := exec.LookPath("ssh-agent")
if err != nil {
@@ -366,7 +369,8 @@ func TestAuth(t *testing.T) {
go func() {
conn, _, _, err := ssh.NewServerConn(a, &serverConf)
if err != nil {
t.Fatalf("Server: %v", err)
t.Errorf("NewServerConn error: %v", err)
return
}
conn.Close()
}()
+7 -4
View File
@@ -53,10 +53,11 @@ func TestSetupForwardAgent(t *testing.T) {
incoming := make(chan *ssh.ServerConn, 1)
go func() {
conn, _, _, err := ssh.NewServerConn(a, &serverConf)
if err != nil {
t.Fatalf("Server: %v", err)
}
incoming <- conn
if err != nil {
t.Errorf("NewServerConn error: %v", err)
return
}
}()
conf := ssh.ClientConfig{
@@ -71,8 +72,10 @@ func TestSetupForwardAgent(t *testing.T) {
if err := ForwardToRemote(client, socket); err != nil {
t.Fatalf("SetupForwardAgent: %v", err)
}
server := <-incoming
if server == nil {
t.Fatal("Unable to get server")
}
ch, reqs, err := server.OpenChannel(channelType, nil)
if err != nil {
t.Fatalf("OpenChannel(%q): %v", channelType, err)
+4 -3
View File
@@ -6,6 +6,7 @@ package ssh
import (
"errors"
"fmt"
"io"
"net"
"testing"
@@ -90,16 +91,16 @@ func BenchmarkEndToEnd(b *testing.B) {
go func() {
newCh, err := server.Accept()
if err != nil {
b.Fatalf("Client: %v", err)
panic(fmt.Sprintf("Client: %v", err))
}
ch, incoming, err := newCh.Accept()
if err != nil {
b.Fatalf("Accept: %v", err)
panic(fmt.Sprintf("Accept: %v", err))
}
go DiscardRequests(incoming)
for i := 0; i < b.N; i++ {
if _, err := io.ReadFull(ch, output); err != nil {
b.Fatalf("ReadFull: %v", err)
panic(fmt.Sprintf("ReadFull: %v", err))
}
}
ch.Close()
+121
View File
@@ -955,3 +955,124 @@ func TestAuthMethodGSSAPIWithMIC(t *testing.T) {
}
}
}
func TestCompatibleAlgoAndSignatures(t *testing.T) {
type testcase struct {
algo string
sigFormat string
compatible bool
}
testcases := []*testcase{
{
KeyAlgoRSA,
KeyAlgoRSA,
true,
},
{
KeyAlgoRSA,
KeyAlgoRSASHA256,
true,
},
{
KeyAlgoRSA,
KeyAlgoRSASHA512,
true,
},
{
KeyAlgoRSASHA256,
KeyAlgoRSA,
true,
},
{
KeyAlgoRSASHA512,
KeyAlgoRSA,
true,
},
{
KeyAlgoRSASHA512,
KeyAlgoRSASHA256,
true,
},
{
KeyAlgoRSASHA256,
KeyAlgoRSASHA512,
true,
},
{
KeyAlgoRSASHA512,
KeyAlgoRSASHA512,
true,
},
{
CertAlgoRSAv01,
KeyAlgoRSA,
true,
},
{
CertAlgoRSAv01,
KeyAlgoRSASHA256,
true,
},
{
CertAlgoRSAv01,
KeyAlgoRSASHA512,
true,
},
{
CertAlgoRSASHA256v01,
KeyAlgoRSASHA512,
true,
},
{
CertAlgoRSASHA512v01,
KeyAlgoRSASHA512,
true,
},
{
CertAlgoRSASHA512v01,
KeyAlgoRSASHA256,
true,
},
{
CertAlgoRSASHA256v01,
CertAlgoRSAv01,
true,
},
{
CertAlgoRSAv01,
CertAlgoRSASHA512v01,
true,
},
{
KeyAlgoECDSA256,
KeyAlgoRSA,
false,
},
{
KeyAlgoECDSA256,
KeyAlgoECDSA521,
false,
},
{
KeyAlgoECDSA256,
KeyAlgoECDSA256,
true,
},
{
KeyAlgoECDSA256,
KeyAlgoED25519,
false,
},
{
KeyAlgoED25519,
KeyAlgoED25519,
true,
},
}
for _, c := range testcases {
if isAlgoCompatible(c.algo, c.sigFormat) != c.compatible {
t.Errorf("algorithm %q, signature format %q, expected compatible to be %t", c.algo, c.sigFormat, c.compatible)
}
}
}
+90
View File
@@ -254,3 +254,93 @@ func TestNewClientConn(t *testing.T) {
})
}
}
func TestUnsupportedAlgorithm(t *testing.T) {
for _, tt := range []struct {
name string
config Config
wantError string
}{
{
"unsupported KEX",
Config{
KeyExchanges: []string{"unsupported"},
},
"no common algorithm",
},
{
"unsupported and supported KEXs",
Config{
KeyExchanges: []string{"unsupported", kexAlgoCurve25519SHA256},
},
"",
},
{
"unsupported cipher",
Config{
Ciphers: []string{"unsupported"},
},
"no common algorithm",
},
{
"unsupported and supported ciphers",
Config{
Ciphers: []string{"unsupported", chacha20Poly1305ID},
},
"",
},
{
"unsupported MAC",
Config{
MACs: []string{"unsupported"},
// MAC is used for non AAED ciphers.
Ciphers: []string{"aes256-ctr"},
},
"no common algorithm",
},
{
"unsupported and supported MACs",
Config{
MACs: []string{"unsupported", "hmac-sha2-256-etm@openssh.com"},
// MAC is used for non AAED ciphers.
Ciphers: []string{"aes256-ctr"},
},
"",
},
} {
t.Run(tt.name, func(t *testing.T) {
c1, c2, err := netPipe()
if err != nil {
t.Fatalf("netPipe: %v", err)
}
defer c1.Close()
defer c2.Close()
serverConf := &ServerConfig{
Config: tt.config,
PasswordCallback: func(conn ConnMetadata, password []byte) (*Permissions, error) {
return &Permissions{}, nil
},
}
serverConf.AddHostKey(testSigners["rsa"])
go NewServerConn(c1, serverConf)
clientConf := &ClientConfig{
User: "testuser",
Config: tt.config,
Auth: []AuthMethod{
Password("testpw"),
},
HostKeyCallback: InsecureIgnoreHostKey(),
}
_, _, _, err = NewClientConn(c2, "", clientConf)
if err != nil {
if tt.wantError == "" || !strings.Contains(err.Error(), tt.wantError) {
t.Errorf("%s: got error %q, missing %q", tt.name, err.Error(), tt.wantError)
}
} else if tt.wantError != "" {
t.Errorf("%s: succeeded, but want error string %q", tt.name, tt.wantError)
}
})
}
}
+38 -13
View File
@@ -49,7 +49,8 @@ var supportedKexAlgos = []string{
// 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, kexAlgoDH14SHA1, kexAlgoDH1SHA1,
kexAlgoDH14SHA256, kexAlgoDH16SHA512, kexAlgoDH14SHA1,
kexAlgoDH1SHA1,
}
// serverForbiddenKexAlgos contains key exchange algorithms, that are forbidden
@@ -59,8 +60,9 @@ var serverForbiddenKexAlgos = map[string]struct{}{
kexAlgoDHGEXSHA256: {}, // server half implementation is only minimal to satisfy the automated tests
}
// preferredKexAlgos specifies the default preference for key-exchange algorithms
// in preference order.
// 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,
@@ -70,12 +72,12 @@ var preferredKexAlgos = []string{
// supportedHostKeyAlgos specifies the supported host-key algorithms (i.e. methods
// of authenticating servers) in preference order.
var supportedHostKeyAlgos = []string{
CertAlgoRSASHA512v01, CertAlgoRSASHA256v01,
CertAlgoRSASHA256v01, CertAlgoRSASHA512v01,
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
KeyAlgoRSASHA512, KeyAlgoRSASHA256,
KeyAlgoRSASHA256, KeyAlgoRSASHA512,
KeyAlgoRSA, KeyAlgoDSA,
KeyAlgoED25519,
@@ -85,7 +87,7 @@ var supportedHostKeyAlgos = []string{
// 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-256", "hmac-sha1", "hmac-sha1-96",
"hmac-sha2-256-etm@openssh.com", "hmac-sha2-512-etm@openssh.com", "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", "hmac-sha1-96",
}
var supportedCompressions = []string{compressionNone}
@@ -119,6 +121,13 @@ func algorithmsForKeyFormat(keyFormat string) []string {
}
}
// isRSA returns whether algo is a supported RSA algorithm, including certificate
// algorithms.
func isRSA(algo string) bool {
algos := algorithmsForKeyFormat(KeyAlgoRSA)
return contains(algos, underlyingAlgo(algo))
}
// 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
@@ -262,16 +271,16 @@ type Config struct {
// unspecified, a size suitable for the chosen cipher is used.
RekeyThreshold uint64
// The allowed key exchanges algorithms. If unspecified then a
// default set of algorithms is used.
// The allowed key exchanges algorithms. If unspecified then a default set
// of algorithms is used. Unsupported values are silently ignored.
KeyExchanges []string
// The allowed cipher algorithms. If unspecified then a sensible
// default is used.
// The allowed cipher algorithms. If unspecified then a sensible default is
// used. Unsupported values are silently ignored.
Ciphers []string
// The allowed MAC algorithms. If unspecified then a sensible default
// is used.
// The allowed MAC algorithms. If unspecified then a sensible default is
// used. Unsupported values are silently ignored.
MACs []string
}
@@ -288,7 +297,7 @@ func (c *Config) SetDefaults() {
var ciphers []string
for _, c := range c.Ciphers {
if cipherModes[c] != nil {
// reject the cipher if we have no cipherModes definition
// Ignore the cipher if we have no cipherModes definition.
ciphers = append(ciphers, c)
}
}
@@ -297,10 +306,26 @@ func (c *Config) SetDefaults() {
if c.KeyExchanges == nil {
c.KeyExchanges = preferredKexAlgos
}
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)
}
}
c.KeyExchanges = kexs
if c.MACs == nil {
c.MACs = supportedMACs
}
var macs []string
for _, m := range c.MACs {
if macModes[m] != nil {
// Ignore the MAC if we have no macModes definition.
macs = append(macs, m)
}
}
c.MACs = macs
if c.RekeyThreshold == 0 {
// cipher specific default
+5 -5
View File
@@ -82,11 +82,11 @@ func TestFindAgreedAlgorithms(t *testing.T) {
}
cases := []testcase{
testcase{
{
name: "standard",
},
testcase{
{
name: "no common hostkey",
serverIn: kexInitMsg{
ServerHostKeyAlgos: []string{"hostkey2"},
@@ -94,7 +94,7 @@ func TestFindAgreedAlgorithms(t *testing.T) {
wantErr: true,
},
testcase{
{
name: "no common kex",
serverIn: kexInitMsg{
KexAlgos: []string{"kex2"},
@@ -102,7 +102,7 @@ func TestFindAgreedAlgorithms(t *testing.T) {
wantErr: true,
},
testcase{
{
name: "no common cipher",
serverIn: kexInitMsg{
CiphersClientServer: []string{"cipher2"},
@@ -110,7 +110,7 @@ func TestFindAgreedAlgorithms(t *testing.T) {
wantErr: true,
},
testcase{
{
name: "client decides cipher",
serverIn: kexInitMsg{
CiphersClientServer: []string{"cipher1", "cipher2"},
+19 -17
View File
@@ -148,6 +148,7 @@ func TestHandshakeBasic(t *testing.T) {
clientDone := make(chan int, 0)
gotHalf := make(chan int, 0)
const N = 20
errorCh := make(chan error, 1)
go func() {
defer close(clientDone)
@@ -158,7 +159,9 @@ func TestHandshakeBasic(t *testing.T) {
for i := 0; i < N; i++ {
p := []byte{msgRequestSuccess, byte(i)}
if err := trC.writePacket(p); err != nil {
t.Fatalf("sendPacket: %v", err)
errorCh <- err
trC.Close()
return
}
if (i % 10) == 5 {
<-gotHalf
@@ -177,16 +180,15 @@ func TestHandshakeBasic(t *testing.T) {
checker.waitCall <- 1
}
}
errorCh <- nil
}()
// Server checks that client messages come in cleanly
i := 0
err = nil
for ; i < N; i++ {
var p []byte
p, err = trS.readPacket()
if err != nil {
break
p, err := trS.readPacket()
if err != nil && err != io.EOF {
t.Fatalf("server error: %v", err)
}
if (i % 10) == 5 {
gotHalf <- 1
@@ -198,8 +200,8 @@ func TestHandshakeBasic(t *testing.T) {
}
}
<-clientDone
if err != nil && err != io.EOF {
t.Fatalf("server error: %v", err)
if err := <-errorCh; err != nil {
t.Fatalf("sendPacket: %v", err)
}
if i != N {
t.Errorf("received %d messages, want 10.", i)
@@ -345,16 +347,16 @@ func TestHandshakeAutoRekeyRead(t *testing.T) {
// While we read out the packet, a key change will be
// initiated.
done := make(chan int, 1)
errorCh := make(chan error, 1)
go func() {
defer close(done)
if _, err := trC.readPacket(); err != nil {
t.Fatalf("readPacket(client): %v", err)
}
_, err := trC.readPacket()
errorCh <- err
}()
<-done
if err := <-errorCh; err != nil {
t.Fatalf("readPacket(client): %v", err)
}
<-sync.called
}
@@ -421,8 +423,8 @@ func TestHandshakeErrorHandlingWriteCoupled(t *testing.T) {
// handshakeTransport deadlocks, the go runtime will detect it and
// panic.
func testHandshakeErrorHandlingN(t *testing.T, readLimit, writeLimit int, coupled bool) {
if runtime.GOOS == "js" && runtime.GOARCH == "wasm" {
t.Skip("skipping on js/wasm; see golang.org/issue/32840")
if (runtime.GOOS == "js" || runtime.GOOS == "wasip1") && runtime.GOARCH == "wasm" {
t.Skipf("skipping on %s/wasm; see golang.org/issue/32840", runtime.GOOS)
}
msg := Marshal(&serviceRequestMsg{strings.Repeat("x", int(minRekeyThreshold)/4)})
+12
View File
@@ -23,6 +23,7 @@ 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"
@@ -430,6 +431,17 @@ func init() {
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)
kexAlgoMap[kexAlgoDH16SHA512] = &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()}
+41
View File
@@ -8,6 +8,7 @@ package ssh
import (
"crypto/rand"
"fmt"
"reflect"
"sync"
"testing"
@@ -63,3 +64,43 @@ func TestKexes(t *testing.T) {
})
}
}
func BenchmarkKexes(b *testing.B) {
type kexResultErr struct {
result *kexResult
err error
}
for name, kex := range kexAlgoMap {
b.Run(name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
t1, t2 := memPipe()
s := make(chan kexResultErr, 1)
c := make(chan kexResultErr, 1)
var magics handshakeMagics
go func() {
r, e := kex.Client(t1, rand.Reader, &magics)
t1.Close()
c <- kexResultErr{r, e}
}()
go func() {
r, e := kex.Server(t2, rand.Reader, &magics, testSigners["ecdsa"].(AlgorithmSigner), testSigners["ecdsa"].PublicKey().Type())
t2.Close()
s <- kexResultErr{r, e}
}()
clientRes := <-c
serverRes := <-s
if clientRes.err != nil {
panic(fmt.Sprintf("client: %v", clientRes.err))
}
if serverRes.err != nil {
panic(fmt.Sprintf("server: %v", serverRes.err))
}
}
})
}
}
+7
View File
@@ -10,6 +10,7 @@ import (
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"hash"
)
@@ -46,9 +47,15 @@ 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 {
return hmac.New(sha512.New, key)
}},
"hmac-sha2-256-etm@openssh.com": {32, true, func(key []byte) hash.Hash {
return hmac.New(sha256.New, key)
}},
"hmac-sha2-512": {64, false, func(key []byte) hash.Hash {
return hmac.New(sha512.New, key)
}},
"hmac-sha2-256": {32, false, func(key []byte) hash.Hash {
return hmac.New(sha256.New, key)
}},
+85 -38
View File
@@ -5,6 +5,8 @@
package ssh
import (
"errors"
"fmt"
"io"
"sync"
"testing"
@@ -29,14 +31,21 @@ func channelPair(t *testing.T) (*channel, *channel, *mux) {
go func() {
newCh, ok := <-s.incomingChannels
if !ok {
t.Fatalf("No incoming channel")
t.Error("no incoming channel")
close(res)
return
}
if newCh.ChannelType() != "chan" {
t.Fatalf("got type %q want chan", newCh.ChannelType())
t.Errorf("got type %q want chan", newCh.ChannelType())
newCh.Reject(Prohibited, fmt.Sprintf("got type %q want chan", newCh.ChannelType()))
close(res)
return
}
ch, _, err := newCh.Accept()
if err != nil {
t.Fatalf("Accept %v", err)
t.Errorf("accept: %v", err)
close(res)
return
}
res <- ch.(*channel)
}()
@@ -45,8 +54,12 @@ func channelPair(t *testing.T) (*channel, *channel, *mux) {
if err != nil {
t.Fatalf("OpenChannel: %v", err)
}
w := <-res
if w == nil {
t.Fatal("unable to get write channel")
}
return <-res, ch, c
return w, ch, c
}
// Test that stderr and stdout can be addressed from different
@@ -74,14 +87,14 @@ func TestMuxChannelExtendedThreadSafety(t *testing.T) {
go func() {
c, err := io.ReadAll(reader)
if string(c) != magic {
t.Fatalf("stdout read got %q, want %q (error %s)", c, magic, err)
t.Errorf("stdout read got %q, want %q (error %s)", c, magic, err)
}
rd.Done()
}()
go func() {
c, err := io.ReadAll(reader.Stderr())
if string(c) != magic {
t.Fatalf("stderr read got %q, want %q (error %s)", c, magic, err)
t.Errorf("stderr read got %q, want %q (error %s)", c, magic, err)
}
rd.Done()
}()
@@ -102,11 +115,13 @@ func TestMuxReadWrite(t *testing.T) {
go func() {
_, err := s.Write([]byte(magic))
if err != nil {
t.Fatalf("Write: %v", err)
t.Errorf("Write: %v", err)
return
}
_, err = s.Extended(1).Write([]byte(magicExt))
if err != nil {
t.Fatalf("Write: %v", err)
t.Errorf("Write: %v", err)
return
}
}()
@@ -215,10 +230,13 @@ func TestMuxReject(t *testing.T) {
go func() {
ch, ok := <-server.incomingChannels
if !ok {
t.Fatalf("Accept")
t.Error("cannot accept channel")
return
}
if ch.ChannelType() != "ch" || string(ch.ExtraData()) != "extra" {
t.Fatalf("unexpected channel: %q, %q", ch.ChannelType(), ch.ExtraData())
t.Errorf("unexpected channel: %q, %q", ch.ChannelType(), ch.ExtraData())
ch.Reject(RejectionReason(UnknownChannelType), UnknownChannelType.String())
return
}
ch.Reject(RejectionReason(42), "message")
}()
@@ -294,7 +312,7 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
defer serverPipe.Close()
defer client.Close()
kDone := make(chan struct{})
kDone := make(chan error, 1)
go func() {
// Ignore unknown channel messages that don't want a reply.
err := serverPipe.writePacket(Marshal(channelRequestMsg{
@@ -304,7 +322,8 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
RequestSpecificData: []byte{},
}))
if err != nil {
t.Fatalf("send: %v", err)
kDone <- fmt.Errorf("send: %w", err)
return
}
// Send a keepalive, which should get a channel failure message
@@ -316,44 +335,53 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
RequestSpecificData: []byte{},
}))
if err != nil {
t.Fatalf("send: %v", err)
kDone <- fmt.Errorf("send: %w", err)
return
}
packet, err := serverPipe.readPacket()
if err != nil {
t.Fatalf("read packet: %v", err)
kDone <- fmt.Errorf("read packet: %w", err)
return
}
decoded, err := decode(packet)
if err != nil {
t.Fatalf("decode failed: %v", err)
kDone <- fmt.Errorf("decode failed: %w", err)
return
}
switch msg := decoded.(type) {
case *channelRequestFailureMsg:
if msg.PeersID != 2 {
t.Fatalf("received response to wrong message: %v", msg)
kDone <- fmt.Errorf("received response to wrong message: %v", msg)
return
}
default:
t.Fatalf("unexpected channel message: %v", msg)
kDone <- fmt.Errorf("unexpected channel message: %v", msg)
return
}
kDone <- struct{}{}
kDone <- nil
// Receive and respond to the keepalive to confirm the mux is
// still processing requests.
packet, err = serverPipe.readPacket()
if err != nil {
t.Fatalf("read packet: %v", err)
kDone <- fmt.Errorf("read packet: %w", err)
return
}
if packet[0] != msgGlobalRequest {
t.Fatalf("expected global request")
kDone <- errors.New("expected global request")
return
}
err = serverPipe.writePacket(Marshal(globalRequestFailureMsg{
Data: []byte{},
}))
if err != nil {
t.Fatalf("failed to send failure msg: %v", err)
kDone <- fmt.Errorf("failed to send failure msg: %w", err)
return
}
close(kDone)
@@ -362,7 +390,10 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
// Wait for the server to send the keepalive message and receive back a
// response.
select {
case <-kDone:
case err := <-kDone:
if err != nil {
t.Fatal(err)
}
case <-time.After(10 * time.Second):
t.Fatalf("server never received ack")
}
@@ -373,7 +404,10 @@ func TestMuxUnknownChannelRequests(t *testing.T) {
}
select {
case <-kDone:
case err := <-kDone:
if err != nil {
t.Fatal(err)
}
case <-time.After(10 * time.Second):
t.Fatalf("server never shut down")
}
@@ -385,20 +419,23 @@ func TestMuxClosedChannel(t *testing.T) {
defer serverPipe.Close()
defer client.Close()
kDone := make(chan struct{})
kDone := make(chan error, 1)
go func() {
// Open the channel.
packet, err := serverPipe.readPacket()
if err != nil {
t.Fatalf("read packet: %v", err)
kDone <- fmt.Errorf("read packet: %w", err)
return
}
if packet[0] != msgChannelOpen {
t.Fatalf("expected chan open")
kDone <- errors.New("expected chan open")
return
}
var openMsg channelOpenMsg
if err := Unmarshal(packet, &openMsg); err != nil {
t.Fatalf("unmarshal: %v", err)
kDone <- fmt.Errorf("unmarshal: %w", err)
return
}
// Send back the opened channel confirmation.
@@ -409,7 +446,8 @@ func TestMuxClosedChannel(t *testing.T) {
MaxPacketSize: channelMaxPacket,
}))
if err != nil {
t.Fatalf("send: %v", err)
kDone <- fmt.Errorf("send: %w", err)
return
}
// Close the channel.
@@ -417,7 +455,8 @@ func TestMuxClosedChannel(t *testing.T) {
PeersID: openMsg.PeersID,
}))
if err != nil {
t.Fatalf("send: %v", err)
kDone <- fmt.Errorf("send: %w", err)
return
}
// Send a keepalive message on the channel we just closed.
@@ -428,43 +467,51 @@ func TestMuxClosedChannel(t *testing.T) {
RequestSpecificData: []byte{},
}))
if err != nil {
t.Fatalf("send: %v", err)
kDone <- fmt.Errorf("send: %w", err)
return
}
// Receive the channel closed response.
packet, err = serverPipe.readPacket()
if err != nil {
t.Fatalf("read packet: %v", err)
kDone <- fmt.Errorf("read packet: %w", err)
return
}
if packet[0] != msgChannelClose {
t.Fatalf("expected channel close")
kDone <- errors.New("expected channel close")
return
}
// Receive the keepalive response failure.
packet, err = serverPipe.readPacket()
if err != nil {
t.Fatalf("read packet: %v", err)
kDone <- fmt.Errorf("read packet: %w", err)
return
}
if packet[0] != msgChannelFailure {
t.Fatalf("expected channel close")
kDone <- errors.New("expected channel failure")
return
}
kDone <- struct{}{}
kDone <- nil
// Receive and respond to the keepalive to confirm the mux is
// still processing requests.
packet, err = serverPipe.readPacket()
if err != nil {
t.Fatalf("read packet: %v", err)
kDone <- fmt.Errorf("read packet: %w", err)
return
}
if packet[0] != msgGlobalRequest {
t.Fatalf("expected global request")
kDone <- errors.New("expected global request")
return
}
err = serverPipe.writePacket(Marshal(globalRequestFailureMsg{
Data: []byte{},
}))
if err != nil {
t.Fatalf("failed to send failure msg: %v", err)
kDone <- fmt.Errorf("failed to send failure msg: %w", err)
return
}
close(kDone)
+20 -1
View File
@@ -370,6 +370,25 @@ func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, firstToken []byte, s *c
return authErr, perms, nil
}
// isAlgoCompatible checks if the signature format is compatible with the
// selected algorithm taking into account edge cases that occur with old
// clients.
func isAlgoCompatible(algo, sigFormat string) bool {
// Compatibility for old clients.
//
// For certificate authentication with OpenSSH 7.2-7.7 signature format can
// be rsa-sha2-256 or rsa-sha2-512 for the algorithm
// ssh-rsa-cert-v01@openssh.com.
//
// With gpg-agent < 2.2.6 the algorithm can be rsa-sha2-256 or rsa-sha2-512
// for signature format ssh-rsa.
if isRSA(algo) && isRSA(sigFormat) {
return true
}
// Standard case: the underlying algorithm must match the signature format.
return underlyingAlgo(algo) == sigFormat
}
// ServerAuthError represents server authentication errors and is
// sometimes returned by NewServerConn. It appends any authentication
// errors that may occur, and is returned if all of the authentication
@@ -567,7 +586,7 @@ userAuthLoop:
authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
break
}
if underlyingAlgo(algo) != sig.Format {
if !isAlgoCompatible(algo, sig.Format) {
authErr = fmt.Errorf("ssh: signature %q not compatible with selected algorithm %q", sig.Format, algo)
break
}
+16 -3
View File
@@ -36,7 +36,8 @@ func dial(handler serverType, t *testing.T) *Client {
conn, chans, reqs, err := NewServerConn(c1, &conf)
if err != nil {
t.Fatalf("Unable to handshake: %v", err)
t.Errorf("Unable to handshake: %v", err)
return
}
go DiscardRequests(reqs)
@@ -647,10 +648,12 @@ func TestSessionID(t *testing.T) {
User: "user",
}
srvErrCh := make(chan error, 1)
go func() {
conn, chans, reqs, err := NewServerConn(c1, serverConf)
srvErrCh <- err
if err != nil {
t.Fatalf("server handshake: %v", err)
return
}
serverID <- conn.SessionID()
go DiscardRequests(reqs)
@@ -659,10 +662,12 @@ func TestSessionID(t *testing.T) {
}
}()
cliErrCh := make(chan error, 1)
go func() {
conn, chans, reqs, err := NewClientConn(c2, "", clientConf)
cliErrCh <- err
if err != nil {
t.Fatalf("client handshake: %v", err)
return
}
clientID <- conn.SessionID()
go DiscardRequests(reqs)
@@ -671,6 +676,14 @@ func TestSessionID(t *testing.T) {
}
}()
if err := <-srvErrCh; err != nil {
t.Fatalf("server handshake: %v", err)
}
if err := <-cliErrCh; err != nil {
t.Fatalf("client handshake: %v", err)
}
s := <-serverID
c := <-clientID
if bytes.Compare(s, c) != 0 {
+2 -3
View File
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd
// +build aix darwin dragonfly freebsd linux netbsd openbsd
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package test
@@ -17,7 +17,6 @@ import (
func TestAgentForward(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
defer conn.Close()
+2 -3
View File
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd
// +build aix darwin dragonfly freebsd linux netbsd openbsd
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package test
@@ -13,7 +13,6 @@ import (
func TestBannerCallbackAgainstOpenSSH(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
clientConf := clientConfig()
+2 -3
View File
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd
// +build aix darwin dragonfly freebsd linux netbsd openbsd
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package test
@@ -18,7 +18,6 @@ import (
// Test both logging in with a cert, and also that the certificate presented by an OpenSSH host can be validated correctly
func TestCertLogin(t *testing.T) {
s := newServer(t)
defer s.Shutdown()
// Use a key different from the default.
clientKey := testSigners["dsa"]
+2 -3
View File
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !windows && !solaris && !js
// +build !windows,!solaris,!js
//go:build !windows && !js && !wasip1
// +build !windows,!js,!wasip1
package test
@@ -24,7 +24,6 @@ type dialTester interface {
func testDial(t *testing.T, n, listenAddr string, x dialTester) {
server := newServer(t)
defer server.Shutdown()
sshConn := server.Dial(clientConfig())
defer sshConn.Close()
+7 -14
View File
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd
// +build aix darwin dragonfly freebsd linux netbsd openbsd
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package test
@@ -23,7 +23,6 @@ type closeWriter interface {
func testPortForward(t *testing.T, n, listenAddr string) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
defer conn.Close()
@@ -120,7 +119,6 @@ func TestPortForwardUnix(t *testing.T) {
func testAcceptClose(t *testing.T, n, listenAddr string) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
sshListener, err := conn.Listen(n, listenAddr)
@@ -162,10 +160,9 @@ func TestAcceptCloseUnix(t *testing.T) {
// Check that listeners exit if the underlying client transport dies.
func testPortForwardConnectionClose(t *testing.T, n, listenAddr string) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
client := server.Dial(clientConfig())
sshListener, err := conn.Listen(n, listenAddr)
sshListener, err := client.Listen(n, listenAddr)
if err != nil {
t.Fatal(err)
}
@@ -184,14 +181,10 @@ func testPortForwardConnectionClose(t *testing.T, n, listenAddr string) {
// It would be even nicer if we closed the server side, but it
// is more involved as the fd for that side is dup()ed.
server.clientConn.Close()
server.lastDialConn.Close()
select {
case <-time.After(1 * time.Second):
t.Errorf("timeout: listener did not close.")
case err := <-quit:
t.Logf("quit as expected (error %v)", err)
}
err = <-quit
t.Logf("quit as expected (error %v)", err)
}
func TestPortForwardConnectionCloseTCP(t *testing.T) {
+5 -6
View File
@@ -77,27 +77,27 @@ func (ctx *multiAuthTestCtx) kbdIntCb(user, instruction string, questions []stri
func TestMultiAuth(t *testing.T) {
testCases := []multiAuthTestCase{
// Test password,publickey authentication, assert that password callback is called 1 time
multiAuthTestCase{
{
authMethods: []string{"password", "publickey"},
expectedPasswordCbs: 1,
},
// Test keyboard-interactive,publickey authentication, assert that keyboard-interactive callback is called 1 time
multiAuthTestCase{
{
authMethods: []string{"keyboard-interactive", "publickey"},
expectedKbdIntCbs: 1,
},
// Test publickey,password authentication, assert that password callback is called 1 time
multiAuthTestCase{
{
authMethods: []string{"publickey", "password"},
expectedPasswordCbs: 1,
},
// Test publickey,keyboard-interactive authentication, assert that keyboard-interactive callback is called 1 time
multiAuthTestCase{
{
authMethods: []string{"publickey", "keyboard-interactive"},
expectedKbdIntCbs: 1,
},
// Test password,password authentication, assert that password callback is called 2 times
multiAuthTestCase{
{
authMethods: []string{"password", "password"},
expectedPasswordCbs: 2,
},
@@ -108,7 +108,6 @@ func TestMultiAuth(t *testing.T) {
ctx := newMultiAuthTestCtx(t)
server := newServerForConfig(t, "MultiAuth", map[string]string{"AuthMethods": strings.Join(testCase.authMethods, ",")})
defer server.Shutdown()
clientConfig := clientConfig()
server.setTestPassword(clientConfig.User, ctx.password)
+27 -21
View File
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !windows && !solaris && !js
// +build !windows,!solaris,!js
//go:build !windows && !js && !wasip1
// +build !windows,!js,!wasip1
package test
@@ -14,6 +14,8 @@ import (
"errors"
"fmt"
"io"
"path/filepath"
"regexp"
"runtime"
"strings"
"testing"
@@ -23,7 +25,6 @@ import (
func TestRunCommandSuccess(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
defer conn.Close()
@@ -40,7 +41,6 @@ func TestRunCommandSuccess(t *testing.T) {
func TestHostKeyCheck(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conf := clientConfig()
hostDB := hostKeyDB()
@@ -62,7 +62,6 @@ func TestHostKeyCheck(t *testing.T) {
func TestRunCommandStdin(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
defer conn.Close()
@@ -85,7 +84,6 @@ func TestRunCommandStdin(t *testing.T) {
func TestRunCommandStdinError(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
defer conn.Close()
@@ -109,7 +107,6 @@ func TestRunCommandStdinError(t *testing.T) {
func TestRunCommandFailed(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
defer conn.Close()
@@ -126,7 +123,6 @@ func TestRunCommandFailed(t *testing.T) {
func TestRunCommandWeClosed(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
defer conn.Close()
@@ -146,7 +142,6 @@ func TestRunCommandWeClosed(t *testing.T) {
func TestFuncLargeRead(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
defer conn.Close()
@@ -178,7 +173,6 @@ func TestFuncLargeRead(t *testing.T) {
func TestKeyChange(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conf := clientConfig()
hostDB := hostKeyDB()
conf.HostKeyCallback = hostDB.Check
@@ -225,7 +219,6 @@ func TestValidTerminalMode(t *testing.T) {
t.Skipf("skipping on %s", runtime.GOOS)
}
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
defer conn.Close()
@@ -255,15 +248,31 @@ func TestValidTerminalMode(t *testing.T) {
t.Fatalf("session failed: %s", err)
}
stdin.Write([]byte("stty -a && exit\n"))
if _, err := io.WriteString(stdin, "echo && echo SHELL $SHELL && stty -a && exit\n"); err != nil {
t.Fatal(err)
}
var buf bytes.Buffer
if _, err := io.Copy(&buf, stdout); err != nil {
buf := new(strings.Builder)
if _, err := io.Copy(buf, stdout); err != nil {
t.Fatalf("reading failed: %s", err)
}
if testing.Verbose() {
t.Logf("echo && echo SHELL $SHELL && stty -a && exit:\n%s", buf)
}
shellLine := regexp.MustCompile("(?m)^SHELL (.*)$").FindStringSubmatch(buf.String())
if len(shellLine) != 2 {
t.Fatalf("missing output from echo SHELL $SHELL")
}
switch shell := filepath.Base(strings.TrimSpace(shellLine[1])); shell {
case "sh", "bash":
default:
t.Skipf("skipping test on non-Bourne shell %q", shell)
}
if sttyOutput := buf.String(); !strings.Contains(sttyOutput, "-echo ") {
t.Fatalf("terminal mode failure: expected -echo in stty output, got %s", sttyOutput)
t.Fatal("terminal mode failure: expected -echo in stty output")
}
}
@@ -274,7 +283,6 @@ func TestWindowChange(t *testing.T) {
t.Skipf("skipping on %s", runtime.GOOS)
}
server := newServer(t)
defer server.Shutdown()
conn := server.Dial(clientConfig())
defer conn.Close()
@@ -322,7 +330,6 @@ func TestWindowChange(t *testing.T) {
func testOneCipher(t *testing.T, cipher string, cipherOrder []string) {
server := newServer(t)
defer server.Shutdown()
conf := clientConfig()
conf.Ciphers = []string{cipher}
// Don't fail if sshd doesn't have the cipher.
@@ -381,7 +388,6 @@ func TestMACs(t *testing.T) {
for _, mac := range macOrder {
t.Run(mac, func(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conf := clientConfig()
conf.MACs = []string{mac}
// Don't fail if sshd doesn't have the MAC.
@@ -404,10 +410,12 @@ func TestKeyExchanges(t *testing.T) {
// are not included in the default list of supported kex so we have to add them
// here manually.
kexOrder = append(kexOrder, "diffie-hellman-group-exchange-sha1", "diffie-hellman-group-exchange-sha256")
// The key exchange algorithms diffie-hellman-group16-sha512 is disabled by
// default so we add it here manually.
kexOrder = append(kexOrder, "diffie-hellman-group16-sha512")
for _, kex := range kexOrder {
t.Run(kex, func(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conf := clientConfig()
// Don't fail if sshd doesn't have the kex.
conf.KeyExchanges = append([]string{kex}, kexOrder...)
@@ -442,8 +450,6 @@ func TestClientAuthAlgorithms(t *testing.T) {
} else {
t.Errorf("failed for key %q", key)
}
server.Shutdown()
})
}
}
+29 -39
View File
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || plan9
// +build aix darwin dragonfly freebsd linux netbsd openbsd plan9
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || plan9 || solaris
// +build aix darwin dragonfly freebsd linux netbsd openbsd plan9 solaris
package test
@@ -23,6 +23,7 @@ import (
"testing"
"text/template"
"golang.org/x/crypto/internal/testenv"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/testdata"
)
@@ -67,17 +68,13 @@ var configTmpl = map[string]*template.Template{
type server struct {
t *testing.T
cleanup func() // executed during Shutdown
configfile string
cmd *exec.Cmd
output bytes.Buffer // holds stderr from sshd process
testUser string // test username for sshd
testPasswd string // test password for sshd
sshdTestPwSo string // dynamic library to inject a custom password into sshd
// Client half of the network connection.
clientConn net.Conn
lastDialConn net.Conn
}
func username() string {
@@ -193,15 +190,15 @@ func (s *server) TryDialWithAddr(config *ssh.ClientConfig, addr string) (*ssh.Cl
s.t.Fatalf("unixConnection: %v", err)
}
s.cmd = exec.Command(sshd, "-f", s.configfile, "-i", "-e")
cmd := testenv.Command(s.t, sshd, "-f", s.configfile, "-i", "-e")
f, err := c2.File()
if err != nil {
s.t.Fatalf("UnixConn.File: %v", err)
}
defer f.Close()
s.cmd.Stdin = f
s.cmd.Stdout = f
s.cmd.Stderr = &s.output
cmd.Stdin = f
cmd.Stdout = f
cmd.Stderr = new(bytes.Buffer)
if s.sshdTestPwSo != "" {
if s.testUser == "" {
@@ -210,18 +207,29 @@ func (s *server) TryDialWithAddr(config *ssh.ClientConfig, addr string) (*ssh.Cl
if s.testPasswd == "" {
s.t.Fatal("password missing from sshd_test_pw.so config")
}
s.cmd.Env = append(os.Environ(),
cmd.Env = append(os.Environ(),
fmt.Sprintf("LD_PRELOAD=%s", s.sshdTestPwSo),
fmt.Sprintf("TEST_USER=%s", s.testUser),
fmt.Sprintf("TEST_PASSWD=%s", s.testPasswd))
}
if err := s.cmd.Start(); err != nil {
s.t.Fail()
s.Shutdown()
if err := cmd.Start(); err != nil {
s.t.Fatalf("s.cmd.Start: %v", err)
}
s.clientConn = c1
s.lastDialConn = c1
s.t.Cleanup(func() {
// Don't check for errors; if it fails it's most
// likely "os: process already finished", and we don't
// care about that. Use os.Interrupt, so child
// processes are killed too.
cmd.Process.Signal(os.Interrupt)
cmd.Wait()
if s.t.Failed() {
// log any output from sshd process
s.t.Logf("sshd:\n%s", cmd.Stderr)
}
})
conn, chans, reqs, err := ssh.NewClientConn(c1, addr, config)
if err != nil {
return nil, err
@@ -232,29 +240,11 @@ func (s *server) TryDialWithAddr(config *ssh.ClientConfig, addr string) (*ssh.Cl
func (s *server) Dial(config *ssh.ClientConfig) *ssh.Client {
conn, err := s.TryDial(config)
if err != nil {
s.t.Fail()
s.Shutdown()
s.t.Fatalf("ssh.Client: %v", err)
}
return conn
}
func (s *server) Shutdown() {
if s.cmd != nil && s.cmd.Process != nil {
// Don't check for errors; if it fails it's most
// likely "os: process already finished", and we don't
// care about that. Use os.Interrupt, so child
// processes are killed too.
s.cmd.Process.Signal(os.Interrupt)
s.cmd.Wait()
}
if s.t.Failed() {
// log any output from sshd process
s.t.Logf("sshd: %s", s.output.String())
}
s.cleanup()
}
func writeFile(path string, contents []byte) {
f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600)
if err != nil {
@@ -351,15 +341,15 @@ func newServerForConfig(t *testing.T, config string, configVars map[string]strin
authkeys.Write(ssh.MarshalAuthorizedKey(testPublicKeys[k]))
}
writeFile(filepath.Join(dir, "authorized_keys"), authkeys.Bytes())
t.Cleanup(func() {
if err := os.RemoveAll(dir); err != nil {
t.Error(err)
}
})
return &server{
t: t,
configfile: f.Name(),
cleanup: func() {
if err := os.RemoveAll(dir); err != nil {
t.Error(err)
}
},
}
}
File diff suppressed because it is too large Load Diff
+31
View File
@@ -0,0 +1,31 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.20
// Package fallback embeds a set of fallback X.509 trusted roots in the
// application by automatically invoking [x509.SetFallbackRoots]. This allows
// the application to work correctly even if the operating system does not
// provide a verifier or system roots pool.
//
// To use it, import the package like
//
// import _ "golang.org/x/crypto/x509roots/fallback"
//
// It's recommended that only binaries, and not libraries, import this package.
//
// This package must be kept up to date for security and compatibility reasons.
// Use govulncheck to be notified of when new versions of the package are
// available.
package fallback
import "crypto/x509"
func init() {
p := x509.NewCertPool()
for _, c := range bundle {
p.AddCert(c)
}
x509.SetFallbackRoots(p)
}
+3
View File
@@ -0,0 +1,3 @@
module golang.org/x/crypto/x509roots/fallback
go 1.20
@@ -0,0 +1,8 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package goissue52287 is an empty internal package.
// It exists only to work around go.dev/issue/52287 and
// can be removed after Go 1.19 stops being supported.
package goissue52287
+135
View File
@@ -0,0 +1,135 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build generate
// +build generate
//go:generate go run gen_fallback_bundle.go
package main
import (
"bytes"
"crypto/sha256"
"encoding/pem"
"flag"
"fmt"
"go/format"
"io"
"log"
"net/http"
"os"
"sort"
"golang.org/x/crypto/x509roots/nss"
)
const tmpl = `// Code generated by gen_fallback_bundle.go; DO NOT EDIT.
//go:build go1.20
package fallback
import "crypto/x509"
import "encoding/pem"
func mustParse(b []byte) []*x509.Certificate {
var roots []*x509.Certificate
for len(b) > 0 {
var block *pem.Block
block, b = pem.Decode(b)
if block == nil {
break
}
if block.Type != "CERTIFICATE" {
panic("unexpected PEM block type: " + block.Type)
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
panic(err)
}
roots = append(roots, cert)
}
return roots
}
var bundle = mustParse([]byte(pemRoots))
// Format of the PEM list is:
// * Subject common name
// * SHA256 hash
// * PEM block
`
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")
)
func main() {
flag.Parse()
var certdata io.Reader
if *certDataPath != "" {
f, err := os.Open(*certDataPath)
if err != nil {
log.Fatalf("unable to open %q: %s", *certDataPath, err)
}
defer f.Close()
certdata = f
} else {
resp, err := http.Get(*certDataURL)
if err != nil {
log.Fatalf("failed to request %q: %s", *certDataURL, err)
}
defer resp.Body.Close()
certdata = resp.Body
}
certs, err := nss.Parse(certdata)
if err != nil {
log.Fatalf("failed to parse %q: %s", *certDataPath, err)
}
sort.Slice(certs, func(i, j int) bool {
// Sort based on the stringified subject (which may not be unique), and
// break any ties by just sorting on the raw DER (which will be unique,
// but is expensive). This should produce a stable sorting, which should
// be mostly readable by a human looking for a specific root or set of
// roots.
subjI, subjJ := certs[i].X509.Subject.String(), certs[j].X509.Subject.String()
if subjI == subjJ {
return string(certs[i].X509.Raw) < string(certs[j].X509.Raw)
}
return subjI < subjJ
})
b := new(bytes.Buffer)
b.WriteString(tmpl)
fmt.Fprintln(b, "const pemRoots = `")
for _, c := range certs {
if len(c.Constraints) > 0 {
// Until the constrained roots API lands, skip anything that has any
// additional constraints. Once that API is available, we can add
// build constraints that support both the current version and the
// new version.
continue
}
fmt.Fprintf(b, "# %s\n# %x\n", c.X509.Subject.String(), sha256.Sum256(c.X509.Raw))
pem.Encode(b, &pem.Block{Type: "CERTIFICATE", Bytes: c.X509.Raw})
}
fmt.Fprintln(b, "`")
formatted, err := format.Source(b.Bytes())
if err != nil {
log.Fatalf("failed to format source: %s", err)
}
if err := os.WriteFile(*output, formatted, 0644); err != nil {
log.Fatalf("failed to write to %q: %s", *output, err)
}
}
+259
View File
@@ -0,0 +1,259 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package nss provides functionality for parsing NSS certdata.txt
// formatted certificate lists and extracting serverAuth roots. Most
// users should not use this package themselves, and should instead
// rely on the golang.org/x/crypto/x509roots/fallback package which
// calls x509.SetFallbackRoots on a pre-parsed set of roots.
package nss
import (
"bufio"
"bytes"
"crypto/sha1"
"crypto/x509"
"errors"
"fmt"
"io"
"strconv"
"strings"
"time"
)
// Constraint is a constraint to be applied to a certificate or
// certificate chain.
type Constraint interface {
Kind() Kind
}
// Kind is the constraint kind, using the NSS enumeration.
type Kind int
const (
CKA_NSS_SERVER_DISTRUST_AFTER Kind = iota
)
// DistrustAfter is a Constraint that indicates a certificate has a
// CKA_NSS_SERVER_DISTRUST_AFTER constraint. This constraint defines a date
// after which any certificate issued which is rooted by the constrained
// certificate should be distrusted.
type DistrustAfter time.Time
func (DistrustAfter) Kind() Kind {
return CKA_NSS_SERVER_DISTRUST_AFTER
}
// A Certificate represents a single trusted serverAuth certificate in the NSS
// certdata.txt list and any constraints that should be applied to chains
// rooted by it.
type Certificate struct {
// Certificate is the parsed certificate
X509 *x509.Certificate
// Constraints contains a list of additional constraints that should be
// applied to any certificates that chain to Certificate. If there are
// any unknown constraints in the slice, Certificate should not be
// trusted.
Constraints []Constraint
}
func parseMulitLineOctal(s *bufio.Scanner) ([]byte, error) {
buf := bytes.NewBuffer(nil)
for s.Scan() {
if s.Text() == "END" {
break
}
b, err := strconv.Unquote(fmt.Sprintf("\"%s\"", s.Text()))
if err != nil {
return nil, err
}
buf.Write([]byte(b))
}
return buf.Bytes(), nil
}
type certObj struct {
c *x509.Certificate
DistrustAfter *time.Time
}
func parseCertClass(s *bufio.Scanner) ([sha1.Size]byte, *certObj, error) {
var h [sha1.Size]byte
co := &certObj{}
for s.Scan() {
l := s.Text()
if l == "" {
// assume an empty newline indicates the end of a block
break
}
if strings.HasPrefix(l, "CKA_VALUE") {
b, err := parseMulitLineOctal(s)
if err != nil {
return h, nil, err
}
co.c, err = x509.ParseCertificate(b)
if err != nil {
return h, nil, err
}
h = sha1.Sum(b)
} else if strings.HasPrefix(l, "CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_FALSE") {
// we don't want it
return h, nil, nil
} else if l == "CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL" {
dateStr, err := parseMulitLineOctal(s)
if err != nil {
return h, nil, err
}
t, err := time.Parse("060102150405Z0700", string(dateStr))
if err != nil {
return h, nil, err
}
co.DistrustAfter = &t
}
}
if co.c == nil {
return h, nil, errors.New("malformed CKO_CERTIFICATE object")
}
return h, co, nil
}
type trustObj struct {
trusted bool
}
func parseTrustClass(s *bufio.Scanner) ([sha1.Size]byte, *trustObj, error) {
var h [sha1.Size]byte
to := &trustObj{trusted: false} // default to untrusted
for s.Scan() {
l := s.Text()
if l == "" {
// assume an empty newline indicates the end of a block
break
}
if l == "CKA_CERT_SHA1_HASH MULTILINE_OCTAL" {
hash, err := parseMulitLineOctal(s)
if err != nil {
return h, nil, err
}
copy(h[:], hash)
} else if l == "CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR" {
// we only care about server auth
to.trusted = true
}
}
return h, to, nil
}
// Parse parses a NSS certdata.txt formatted file, returning only
// trusted serverAuth roots, as well as any additional constraints. This parser
// is very opinionated, only returning roots that are currently trusted for
// serverAuth. As such roots returned by this package should only be used for
// making trust decisions about serverAuth certificates, as the trust status for
// other uses is not considered. Using the roots returned by this package for
// trust decisions should be done carefully.
//
// Some roots returned by the parser may include additional constraints
// (currently only DistrustAfter) which need to be considered when verifying
// certificates which chain to them.
//
// Parse is not intended to be a general purpose parser for certdata.txt.
func Parse(r io.Reader) ([]*Certificate, error) {
// certdata.txt is a rather strange format. It is essentially a list of
// textual PKCS#11 objects, delimited by empty lines. There are two main
// types of objects, certificates (CKO_CERTIFICATE) and trust definitions
// (CKO_NSS_TRUST). These objects appear to alternate, but this ordering is
// not defined anywhere, and should probably not be relied on. A single root
// certificate requires both the certificate object and the trust definition
// object in order to be properly understood.
//
// The list contains not just serverAuth certificates, so we need to be
// careful to only extract certificates which have the serverAuth trust bit
// set. Similarly there are a number of trust related bool fields that
// appear to _always_ be CKA_TRUE, but it seems unsafe to assume this is the
// case, so we should always double check.
//
// Since we only really care about a couple of fields, this parser throws
// away a lot of information, essentially just consuming CKA_CLASS objects
// and looking for the individual fields we care about. We could write a
// siginificantly more complex parser, which handles the entire format, but
// it feels like that would be over engineered for the little information
// that we really care about.
scanner := bufio.NewScanner(r)
type nssEntry struct {
cert *certObj
trust *trustObj
}
entries := map[[sha1.Size]byte]*nssEntry{}
for scanner.Scan() {
// scan until we hit CKA_CLASS
if !strings.HasPrefix(scanner.Text(), "CKA_CLASS") {
continue
}
f := strings.Fields(scanner.Text())
if len(f) != 3 {
return nil, errors.New("malformed CKA_CLASS")
}
switch f[2] {
case "CKO_CERTIFICATE":
h, co, err := parseCertClass(scanner)
if err != nil {
return nil, err
}
if co != nil {
e, ok := entries[h]
if !ok {
e = &nssEntry{}
entries[h] = e
}
e.cert = co
}
case "CKO_NSS_TRUST":
h, to, err := parseTrustClass(scanner)
if err != nil {
return nil, err
}
if to != nil {
e, ok := entries[h]
if !ok {
e = &nssEntry{}
entries[h] = e
}
e.trust = to
}
}
}
if err := scanner.Err(); err != nil {
return nil, err
}
var certs []*Certificate
for h, e := range entries {
if e.cert == nil && e.trust != nil {
// We may skip some certificates which are distrusted due to mozilla
// policy (CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_FALSE), which means
// we might get entries that appear to have a trust object, but no
// certificate. We can just continue on here.
continue
} else if e.cert != nil && e.trust == nil {
return nil, fmt.Errorf("missing trust object for certificate with SHA1 hash: %x", h)
}
if !e.trust.trusted {
continue
}
nssCert := &Certificate{X509: e.cert.c}
if e.cert.DistrustAfter != nil {
nssCert.Constraints = append(nssCert.Constraints, DistrustAfter(*e.cert.DistrustAfter))
}
certs = append(certs, nssCert)
}
return certs, nil
}
File diff suppressed because it is too large Load Diff