Compare commits

...
Author SHA1 Message Date
Gopher Robot 183a9b70cc 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: Ic7c0afcece0f3d2065c7a7e08f092c4344d90655
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/448755
Run-TryBot: Gopher Robot <gobot@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Auto-Submit: Gopher Robot <gobot@golang.org>
Reviewed-by: Jenny Rakoczy <jenny@golang.org>
2022-11-08 20:34:43 +00:00
Park Zhou 334521b960 ssh: fix typo
Change-Id: I560d7f5a62161cd88361a9fe9982d36f8e25e5af
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/447475
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-11-04 14:06:20 +00:00
Alexander Yastrebov a3485e1740 all: use math/bits.RotateLeft
Updates golang/go#31456

Change-Id: Idf043a25632526baa190bf42ed360cb79f85e493
GitHub-Last-Rev: 59461578926a85a87cc68dac96c0b7559766b7cf
GitHub-Pull-Request: golang/crypto#195
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/356518
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: David Chase <drchase@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
2022-10-24 17:35:37 +00:00
Gopher Robot 642fcc37f5 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: If840eea1cadc749ce55efd88eb7d9fc38472839e
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/443996
Auto-Submit: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Gopher Robot <gobot@golang.org>
2022-10-19 16:43:05 +00:00
Axel Wagner 56aed06173 all: use automatic RFC linking
pkgsite automatically links /RFC \d+/ to the mentioned RFC. Insert a
bunch of spaces into doc-comments for that to match.

Change-Id: I01834d7573428563f21c37e43316442e148dd8c4
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/442055
Reviewed-by: Joedian Reid <joedian@golang.org>
Run-TryBot: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
2022-10-12 13:47:37 +00:00
cui fliter 9be5aaad42 all: fix a few function names on comments
Change-Id: Iac9c8f06b874e62b56f634dede8757b87514f421
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/442135
Run-TryBot: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Joedian Reid <joedian@golang.org>
2022-10-12 13:45:58 +00:00
Brad Fitzpatrick d6f0a8c073 ssh: add ServerConfig.NoClientAuthCallback
It was possible to accept auth type "none" before, but not dynamically
at runtime as a function of the ConnMetadata like the other auth types'
callback hooks.

Fixes golang/go#51994

Change-Id: I83ea80901d4977d8f78523e3d1e16e0a7df5b172
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/395314
Reviewed-by: Roland Shoemaker <roland@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Julie Qiu <julieqiu@google.com>
2022-10-10 15:29:10 +00:00
cui fliter 4161e89ecf all: replace bytes.Compare with bytes.Equal
Change-Id: I911366b91ff2a1d02d7de202a166d876fb873142
GitHub-Last-Rev: f50e00376856fb9da36bb98ed0cdfd96c2f3b304
GitHub-Pull-Request: golang/crypto#233
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/438536
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
2022-10-05 02:52:14 +00:00
Roland Shoemaker eccd6366d1 acme/autocert: remove TestRenewFromCache skips
Removes the skips from TestRenewFromCache and
TestRenewFromCacheAlreadyRenewed, which were added due to flakes which
may have been fixed by the renewal timer change.

Updates golang/go#51080

Change-Id: Ib953a24e610e89dfbbea450a4c257c105055ce7e
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/433815
Run-TryBot: Roland Shoemaker <roland@golang.org>
Auto-Submit: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Bryan Mills <bcmills@google.com>
2022-09-26 16:16:30 +00:00
Roland Shoemaker 4ba4fb4dd9 acme/autocert: fix renewal timer issue
Block when creating the renewal timer, rather than doing it in a
goroutine. This fixes an issue where startRenew and stopRenew are called
very closely together, and due to lock ordering, stopRenew may be called
before startRenew, resulting in the appearance that the renewal timer
has been stopped before it has actually been created.

This is only an issue in tests, as that is the only place stopRenew is
actually used. In particular this issue manifests in TestGetCertiifcate
sub-tests, where a httptest server reuses a port across two of the
sub-tests. In this case, the renewal calls end up creating dirty state
for the subsequent test, which can cause confusing behavior (such as
attempting to register an account twice.)

Another solution to this problem would be introducing a bool, protected
by renewalMu, which indicates if renewal has been halted, and to check
it in startRenew to check if stopRenew has already been called, which
would allow us to continue calling startRenew in a goroutine and relying
on renewalMu locking for ordering. That said I don't see a particularly
strong reason to call startRenew concurrently, so this seems like the
simplest solution for now.

Fixes golang/go#52494

Change-Id: I95420d3fd877572a0b9e408d2f8cd353f6a4e80e
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/433016
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
Reviewed-by: Bryan Mills <bcmills@google.com>
2022-09-24 01:33:50 +00:00
cui fliter 35f4265a4b all: replace io/ioutil with io and os package
For golang/go#45557

Change-Id: I447530cc66896aef7a8d528ccb8d095b80e3cf47
GitHub-Last-Rev: 5f385ff46487ac318bd1147cdbbd26bb0ffd0426
GitHub-Pull-Request: golang/crypto#230
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/430797
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Meng Zhuo <mzh@golangcn.org>
Run-TryBot: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
2022-09-19 17:36:07 +00:00
Roland Shoemaker c86fa9a7ed internal/wycheproof: add crypto/ecdh tests
Alongside the existing ECDH tests, add tests that use the new
crypto/ecdh package. The test vectors include a number of private
that use non-standard sizes, which we reject, but aren't flagged,
so we need to skip them.

Change-Id: Iaaef225b0149a86833095f51748d230385d43bfe
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/424274
Reviewed-by: Russ Cox <rsc@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Roland Shoemaker <roland@golang.org>
2022-08-29 22:05:03 +00:00
edef bd7e27e617 ssh/agent: match OpenSSH extensionAgentMsg, not IETF draft
The OpenSSH wire format just suffixes the raw extension body,
without a nested string.

Fixes golang/go#51689

Change-Id: Ic224cedb934ba0563abca9a45a6be1c67769ed6d
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/412154
Reviewed-by: Roland Shoemaker <roland@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Run-TryBot: hopehook <hopehook@qq.com>
Reviewed-by: Daniel Lublin <daniel@lublin.se>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2022-08-26 18:10:53 +00:00
hopehook 5757bc0c55 cryptobyte: add ReadUint64 and AddUint64
Fixes golang/go#53481.

Change-Id: Ic00eef498d1d3b5b0ca5c9c526fac7c26de30cf2
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/421014
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Run-TryBot: hopehook <hopehook@qq.com>
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2022-08-24 17:17:10 +00:00
Russ Cox bc19a97f63 acme: gofmt code with Go 1.19 gofmt
Change-Id: Ib0fd6fcfa358df2bdb820a512b73e7cdb34120f8
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/424174
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Russ Cox <rsc@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2022-08-17 20:11:39 +00:00
Russ Cox 04dced17f7 internal/subtle: rename to internal/alias
This avoids an import conflict in code that needs to import
crypto/subtle as well.

CL 424194 does the same for the main repo.

Change-Id: Ic54cb62bbfdcf5c2cb6f15ac47075ee1c41981ad
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/424175
Reviewed-by: Cherry Mui <cherryyz@google.com>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Russ Cox <rsc@golang.org>
2022-08-17 18:37:53 +00:00
Russ Cox 630584e8d5 A+C: delete AUTHORS and CONTRIBUTORS
In 2009, Google's open-source lawyers asked us to create the AUTHORS
file to define "The Go Authors", and the CONTRIBUTORS file was in
keeping with open source best practices of the time.

Re-reviewing our repos now in 2022, the open-source lawyers are
comfortable with source control history taking the place of the
AUTHORS file, and most open source projects no longer maintain
CONTRIBUTORS files.

To ease maintenance, remove AUTHORS and CONTRIBUTORS from all repos.

For golang/go#53961.

Change-Id: Ieb32933de4f234c77f0131490d4081b6c336820c
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/419094
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: David Chase <drchase@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-07-22 15:52:17 +00:00
Aaqa Ishtyaq 05595931fe curve25519: remove dependency on fmt
For golang/go#48154

Change-Id: If7e99bd1159edc2e3deeb3a4e3d8fb048bc591ab
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/348069
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
2022-06-22 21:31:12 +00:00
Roland Shoemaker 793ad666bf acme/autocert: properly clean DirCache paths
Don't assume the path passed into the DirCache methods is absolute, and
clean it before further operating on it. Put and Delete are not attacker
controlled, but clean them anyway.

Fixes #53082
Fixes CVE-2022-30636

Change-Id: I755f525a737da60ccba07ebce4d41cc8faebfcca
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/408694
Run-TryBot: Roland Shoemaker <roland@golang.org>
Reviewed-by: Damien Neil <dneil@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-05-25 23:09:36 +00:00
Jason Baker 6f7dac9698 acme: DeactivateReg fix panic
Currently discover is not called which results in a panic if just a key
is added to an ACME client and then deactivation is attempted.
This patch adds a discover call as well as missing unit tests for the
API.

Change-Id: I0719e5376eb2fccf62182e5f91e5b5eaa7bdd518
GitHub-Last-Rev: 501d7c6c1b75a3069dcad4254b4d4a0d2ccb02c8
GitHub-Pull-Request: golang/crypto#217
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/406734
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2022-05-18 03:45:28 +00:00
Tobias Klauser 85d78b3ac1 curve25519/internal/field: update generator to avo v0.4.0
This version generates //go:build lines.

For golang/go#46155

Change-Id: I23e4617aa96bc5c15c10f3cd0882028ca08e09e8
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/388874
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Tobias Klauser <tobias.klauser@gmail.com>
Auto-Submit: Tobias Klauser <tobias.klauser@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
2022-05-17 00:50:47 +00:00
Jason Baker 403b01795a acme: add AccountKeyRollover
Add support for AccountKeyRollover. API only returns an error since acme.Error
will contain appropriate KID lookup information. Due to the requirements
of double JWS encoding jwsEncodeJSON is also modified to support a
missing Nonce header and raw string embedding in the payload.

Fixes golang/go#42516

Change-Id: I959660a1a39b2c469b959accd48fda519daf4eb3
GitHub-Last-Rev: 8e8cc5b094743262939c145f56d3a3b57a057d64
GitHub-Pull-Request: golang/crypto#215
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/400274
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
2022-05-16 16:29:34 +00:00
Filippo Valsorda 46612604a0 ssh/agent: fix non-RSA certificates
The type of ssh.PublicKey.Type can be a certificate type, while the
algorithm passed to SignWithAlgorithm is going to be an underlying
algorithm.

Fixes golang/go#52185

Change-Id: I0f7c46defa83d1fd64a3c1e861734650b20cca21
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/404614
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
2022-05-13 21:02:58 +00:00
Roland Shoemaker c6db032c6c acme/autocert/internal/acmetest: don't validate in goroutine
In the test server, rather than spawning a goroutine to validate
challenges, block on the validation before responding to the client.
This prevents a test race, where testing.T.Logf is called after the
test is completed.

While this has a slight behavioral difference to some production
ACME server implementations (although is behavior allowed in the spec),
the change has little material impact on what we are testing, since
previously the validation would happen so quickly that it would be
indistinguishable from the new blocking behavior (i.e. we would not be
sending multiple requests during polling previously.)

Fixes golang/go#52170

Change-Id: I75e3b2da69ddc2302be25a99f1b1151ed0f4af9b
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/405548
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Bryan Mills <bcmills@google.com>
2022-05-11 20:02:25 +00:00
Roland Shoemaker 2cf3adece1 internal/wycheproof: skip truncated SHA-512 RSAPSS tests for boring
On the boringcrypto builder, skip the RSAPSS tests that use the
truncated SHA-512 hashes, since boringcrypto does not support them.

Fixes #52670

Change-Id: I8caecd0f34eb6d2740372db2b641563e3965ac7c
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/404654
Run-TryBot: Roland Shoemaker <roland@golang.org>
Auto-Submit: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Bryan Mills <bcmills@google.com>
2022-05-07 01:19:49 +00:00
Filippo Valsorda eb4f295cb3 internal/wycheproof: add ECDH tests, including point decompression
Fixes golang/go#38936

Change-Id: I231d30fcc683abd9efb36b6fd9cc05f599078ade
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/396174
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <valsorda@google.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2022-04-27 17:25:11 +00:00
Russ Cox 7b82a4e95d all: gofmt
Gofmt to update doc comments to the new formatting.

For golang/go#51082.

Change-Id: I076031b6613691eefbb0f21739366e3fd2011ec9
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/399356
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Russ Cox <rsc@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
2022-04-11 22:02:26 +00:00
Ben Burkert 5352b09029 acme/autocert: support External Account Binding (EAB) tokens
Support External Account Binding (EAB) tokens to the Manager as defined
in RFC 8555, Section 7.3.4. If the ExternalAccountBinding field is set
on Manager, pass it into the acme Account during registration.

Fixes golang/go#48809

Change-Id: I64c38b05ab577acbde9f526638cc8104d15ff055
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/354189
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Trust: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-04-08 19:05:44 +00:00
Roland Shoemaker ae2d96664a ocsp: add Response.Raw
Fixes golang/go#38340

Change-Id: I77afc901584ac3361eafa13c9ee9f8cf9ec2ee28
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/389256
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
Auto-Submit: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Damien Neil <dneil@google.com>
2022-03-31 22:09:35 +00:00
Peter Verraedt 2c7772ba30 ssh: send ext-info-c only once
In accordance to RFC8308, send ext-info-c only during the first key
exchange. Some server implementations such as OpenSSH 7 will send an
extInfoMsg message each time when ext-info-c is received. This results
in a closed connection, as our client does not expect this message while
handling the mux.

See https://bugzilla.mindrot.org/show_bug.cgi?id=2929 regarding the
behaviour of OpenSSH if it sees ext-info-c in later key exchanges.

Fixes golang/go#51808

Change-Id: Id94f1ef73cec6147136246b0b6048b57db92660d
GitHub-Last-Rev: fcfe5ed37306136219854031abc809e0dc9b3124
GitHub-Pull-Request: golang/crypto#208
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/394134
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Trust: Roland Shoemaker <roland@golang.org>
2022-03-21 15:39:16 +00:00
Filippo Valsorda 3147a52a75 ssh: support rsa-sha2-256/512 for client certificates
The server-sig-algs logic was not working for certificate algorithms.
Follow-up on CL 392394.

Tested with OpenSSH 8.8 configured with

    PubkeyAcceptedKeyTypes -ssh-rsa-cert-v01@openssh.com

Updates golang/go#39885
For golang/go#49952

Change-Id: Ic230dd6f98e96b7938acbd0128ab37d33b70abe5
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/392974
Trust: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-03-15 16:07:06 +00:00
Filippo Valsorda 5d542ad81a ssh: support rsa-sha2-256/512 for client authentication
CL 220037 had implemented support for host authentication using
rsa-sha2-256/512, but not client public key authentication. OpenSSH
disabled the SHA-1 based ssh-rsa by default in version 8.8 (after
pre-announcing it in versions 8.2, 8.3, 8.4, 8.5, 8.6, and 8.7) although
some distributions re-enable it. GitHub will start rejecting ssh-rsa for
keys uploaded before November 2, 2021 on March 15, 2022.

https://github.blog/2021-09-01-improving-git-protocol-security-github/

The server side already worked, as long as the client selected one of
the SHA-2 algorithms, because the signature flowed freely to Verify.
There was however nothing verifying that the signature algorithm matched
the advertised one. The comment suggested the check was being performed,
but it got lost back in CL 86190043. Not a security issue because the
signature had to pass the callback's Verify method regardless, and both
values were checked to be acceptable.

Tested with OpenSSH 8.8 configured with "PubkeyAcceptedKeyTypes -ssh-rsa"
and no application-side changes.

The Signers returned by ssh/agent (when backed by an agent client)
didn't actually implement AlgorithmSigner but ParameterizedSigner, an
interface defined in an earlier version of CL 123955.

Updates golang/go#49269
Fixes golang/go#39885
For golang/go#49952

Change-Id: I13b41db8041f1112a70f106c55f077b904b12cb8
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/392394
Trust: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2022-03-14 23:47:24 +00:00
Filippo ValsordaandKevin Wallace a5774263c1 ssh: send (and rename) keyboard-interactive name field to the client
The server side implementation was not actually populating the
SSH_MSG_USERAUTH_INFO_REQUEST field with the
KeyboardInteractiveChallenge argument, although the client side was
deserializing it and passing it to the KeyboardInteractiveChallenge
callback.

Anyway, the first field of SSH_MSG_USERAUTH_INFO_REQUEST is "name", not
"user". Maybe the confusion was due to the first field of
SSH_MSG_USERAUTH_REQUEST being the user.

RFC 4256, Section 3.3, says this about it

   One possibility is to use the name field (possibly
   prefixed with the application's name) as the title of a dialog window
   in which the prompt(s) are presented.

and examples include "CRYPTOCard Authentication", "Password
Authentication", and "Password Expired".

Co-authored-by: Kevin Wallace <kevin@pentabarf.net>
Change-Id: Ic6ec0dfea2122704603c44f42898a980689a15c9
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/372234
Trust: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-03-14 23:47:16 +00:00
Filippo Valsorda 1baeb1ce4c ssh: don't advertise rsa-sha2 algorithms if we can't use them
The server implementation looks at the HostKeys to advertise and
negotiate host key signature algorithms. A fundamental issue of the
Signer and AlgorithmSigner interfaces is that they don't expose the
supported signature algorithms, so really the server has to guess.

Currently, it would guess exclusively based on the PublicKey.Type,
regardless of whether the host key implemented AlgorithmSigner. This
means that a legacy Signer that only supports ssh-rsa still led the
server to negotiate rsa-sha2 algorithms. The server would then fail to
find a suitable host key to make the signature and crash.

This won't happen if only Signers from this package are used, but if a
custom Signer that doesn't support SignWithAlgorithm() but returns
"ssh-rsa" from PublicKey().Type() is used as a HostKey, the server is
vulnerable to DoS.

The only workable rules to determine what to advertise seems to be:

   1. a pure Signer will always Sign with the PublicKey.Type

   2. an AlgorithmSigner supports all algorithms associated with the
      PublicKey.Type

Rule number two means that we can't add new supported algorithms in the
future, which is not great, but it's too late to fix that.

rsaSigner was breaking rule number one, and although it would have been
fine where it's used, I didn't want to break our own interface contract.

It's unclear why we had separate test key entries for rsa-sha2
algorithms, since we can use the ssh-rsa key for those. The only test
that used them, TestCertTypes, seemed broken: the init was actually
failing at making the corresponding signers rsaSigners, and indeed the
test for the SHA-256 signer expected and checked a SHA-512 signature.

Pending CVE
For golang/go#49952

Change-Id: Ie658eefcadd87906e63fc7faae8249376aa96c79
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/392355
Trust: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-03-14 23:46:59 +00:00
Filippo Valsorda fcc990c556 ssh: deprecate and replace SigAlgo constants
RFC 8332, Section 2 sets up two overlapping namespaces: public key
formats and public key algorithms.

* The formats are what we currently have KeyAlgo constants for, and they
  appear in PublicKey.Type.

* The algorithms are the set of both KeyAlgo and SigAlgo constants, and
  they appear in Signature.Format (amongst other places).

This is incoherent, because that means Signature.Format can be both a
KeyAlgo (like KeyAlgoECDSA256) or a SigAlgo (like SigAlgoRSASHA2256).

One solution would be to duplicate all the KeyAlgo constants into the
SigAlgo namespace, but that would be confusing because applications are
currently using KeyAlgos where they'd be supposed to use the new
SigAlgos (while we can't deprecate the KeyAlgos because they are still
necessary for the PublicKey.Type namespace).

Instead, drop the separate namespaces, and use KeyAlgos throughout.
There are simply some KeyAlgos that can't be a PublicKey.Type.

Take the opportunity to fix the stuttering SHA22565/SHA2512 names. It's
totally ok to call those hashes SHA-256 and SHA-512 without the family
infix.

For golang/go#49952

Change-Id: Ia1fce3912a7e60aa70a88f75ed311be331fd19d5
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/392354
Trust: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-03-14 23:46:46 +00:00
Filippo Valsorda 4d399097f0 ssh: fix diffie-hellman-group-exchange g and K bounds checks
The previous code

	if gex.g.Cmp(one) != 1 && gex.g.Cmp(pMinusOne) != -1 {

deobfuscates to the classic mistake

    if g <= 1 && g >= p - 1 {

which is never true.

What the code actually intended to do is

	if gex.g.Cmp(one) != 1 || gex.g.Cmp(pMinusOne) != -1 {

or more readably and consistently with the diffieHellman method

	if gex.g.Cmp(one) <= 0 || gex.g.Cmp(pMinusOne) >= 0 {

Now, is this a security issue? The incorrect checks apply to g and k,
but not what we call Y and the spec calls f. RFC 4419 says:

   Either side MUST NOT send or accept e or f values that are not in the
   range [1, p-1].  If this condition is violated, the key exchange
   fails.  To prevent confinement attacks, they MUST accept the shared
   secret K only if 1 < K < p - 1.

Note that RFC 8268, Section 4 updates the equivalent RFC 4253 statement
(although not the RFC 4419 one) about e and f, but we are already doing
the correct full check there.

      DH Public Key values MUST be checked and both conditions:

         1 < e < p-1

         1 < f < p-1

      MUST be true.  Values not within these bounds MUST NOT be sent or
      accepted by either side.  If either one of these conditions is
      violated, then the key exchange fails.

   This simple check ensures that:

   o  The remote peer behaves properly.

   o  The local system is not forced into the two-element subgroup.

The check on K seems like a proxy for checking a number of ways to fix
the DH output (for example by manipulating g) to one of 0, 1, or -1.
This should not be meaningful to security for two reasons:

   - all parameters end up in the "transcript" hash that will get signed
     by the server's host key, and if the attacker controls the host
     key's signature, they have the ability to MitM without resorting to
     confinement attacks

   - the client secret is ephemeral, so leaking bits of it by forcing it
     into small sub-groups does not gain the attacker anything, as the
     secret does not get reused

Indeed, this is the same explanation of why it's ok not to check that p
is indeed a (safe) prime, which even OpenSSH omits. Building an
equivalent attack by manipulating p instead of g is left as an exercise
to the reader.

For the future, this is a case study in why we should not add complexity
even when it looks easy enough to do. CL 174257 added the
diffie-hellman-group-exchange kex. That introduced a data race (arguably
a security issue), which was fixed in CL 222078. Then it was too slow,
which led to CL 252337 that removed the primalty check, which required a
full analysis of whether it's safe to skip it, and checking against
other implementations. Now we find there's a bug and we have to do
another security analysis that not even the RFC bothered to do in order
to decide if it's a security issue. My decision in
https://github.com/golang/go/issues/17230#issuecomment-489163656
does not look like the right one in hindsight.

While at it, clean up the code some

   - drop useless bit size bounds logic in the server stub that get
     ignored by the rest of the function

   - make p and g local variables instead of method fields, since they
     are not persistent state (this was originally a data race which was
     fixed in CL 222078 by making Client not a pointer receiver)

Updates golang/go#17230

Change-Id: I4b1c68537109f627ccd75ec381dcfab57ce1768c
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/392015
Trust: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-03-14 23:46:39 +00:00
Filippo ValsordaandNicola Murino e4b3678e5f ssh: add diffie-hellman-group14-sha256 kex
RFC 9142 made diffie-hellman-group14-sha256 from RFC 8268 a MUST, and
it's strictly better than diffie-hellman-group14-sha1, which we already
have, and trivial to add.

> The method of key exchange used for the name "diffie-hellman-
> group14-sha256" is the same as that for "diffie-hellman-group14-sha1"
> except that the SHA256 hash algorithm is used.

Ignore the bigger groups which have a meaningful performance cost, and
don't share the same interoperability benefit.

Adapted from CL 387994.

Fixes golang/go#31731

Co-authored-by: Nicola Murino <nicola.murino@gmail.com>
Change-Id: Id4ce345a2065840f193986739ea890f105a1e929
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/392014
Trust: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2022-03-14 23:46:33 +00:00
Михаил ПатинandFilippo Valsorda 9b076918e3 ssh: support new curve25519-sha256 kex name
RFC 8731 standardized curve25519-sha256@libssh.org as curve25519-sha256,
and some systems only advertise support for the new name.

Fixes golang/go#48756

Change-Id: Ice35874cd8c07ad48752686ac368bf11ab793f77
Co-authored-by: Filippo Valsorda <filippo@golang.org>
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/385394
Trust: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Trust: Matt Layher <mdlayher@gmail.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2022-03-14 23:46:23 +00:00
Brad Fitzpatrick b769efc7c0 ssh: add IUTF8 constant from RFC 8160
Change-Id: I65ac40d86c4f0ee5552cd2e167ac91a735f6c56e
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/392134
Trust: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Maisem Ali <maisem@tailscale.com>
2022-03-13 00:37:12 +00:00
Roland Shoemaker 6068a2e6cf ssh: ignore MAC if AEAD ciphers negotiated
If the server/client cipher chosen is one of the two AEAD ciphers that
we support (aes128-gcm@openssh.com and chacha20-poly1305@openssh.com),
don't attempt to find a common MAC algorithm in findAgreedAlgorithms.
Similarly in newPacketCipher, don't attempt to generate a MAC key if we
are using a AEAD cipher.

Fixes golang/go#51406

Change-Id: Id48ae72f052cb0a0c597b32e9901a0f218e4161f
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/389214
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
2022-03-12 13:11:42 +00:00
cuishuang efcb8507fb ssh: fix typo
Change-Id: I5832916eb921d2d18e3082cd04520e22e2b04127
GitHub-Last-Rev: c0999b83439a5f0ff57b740c18f47d81028e552a
GitHub-Pull-Request: golang/crypto#207
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/390414
Reviewed-by: Roland Shoemaker <roland@golang.org>
Trust: Carlos Amedee <carlos@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-03-07 21:11:46 +00:00
Bryan C. Mills 86341886e2 acme/autocert: skip renewal tests broken on windows/arm64
For golang/go#51080

Change-Id: Icf4414ab58bdea44b793a66770b4c05f9faf5387
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/385675
Trust: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-02-14 20:07:02 +00:00
Roland Shoemaker 1e6e3497d5 crypto/ssh: don't send the last auth failure message if disconnecting
If we are going to send the disconnect message and close the connection,
because we have exhausted the number of authentication attempts, don't
send the final authentication failure message. This behavior matches
that of OpenSSH.

Fixes golang/go#51149

Change-Id: I43b2de2e854f789161cd7fd6c05876661adfb2c1
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/385236
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
Auto-Submit: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2022-02-13 19:09:39 +00:00
Bryan C. Mills f4118a5b28 acme/autocert: fix races in renewal tests
TestRenewFromCache and TestRenewFromCacheAlreadyRenewed had several
races and API misuses:

1. They called t.Fatalf from a goroutine other than the one invoking
   the Test function, which is explicitly disallowed (see
   https://pkg.go.dev/testing#T).

2. The test did not stop the renewal timers prior to restoring
   test-hook functions, and the process of stopping the renewal timers
   itself did not wait for in-flight calls to complete. That could
   cause data races if one of the renewals failed and triggered a
   retry with a short-enough randomized backoff.
   (One such race was observed in
   https://build.golang.org/log/1a19e22ad826bedeb5a939c6130f368f9979208a.)

3. The testDidRenewLoop hooks accessed the Manager.renewal field
   without locking the Mutex guarding that field.

4. TestGetCertificate_failedAttempt set a testDidRemoveState hook, but
   didn't wait for the timers referring to that hook to complete
   before restoring it, causing races with other timers. I tried
   pulling on that thread a bit, but couldn't untangle the numerous
   untracked goroutines in the package. Instead, I have made a smaller
   and more local change to copy the value of testDidRemoveState into
   a local variable in the timer's closure.

Given the number of untracked goroutines in this package, it is likely
that races and/or deadlocks remain. Notably, so far I have been unable
to spot the actual cause of golang/go#51080.

For golang/go#51080

Change-Id: I7797f6ac34ef3c272f16ca805251dac3aa7f0009
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/384594
Trust: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-02-10 15:16:21 +00:00
Ian Lance Taylor db638375bc ssh: accept WSAECONNABORTED in TestClientAuthMaxAuthTriesPublicKey
Fixes golang/go#50805

Change-Id: Icdd2835b1626240faf61936288f279570c873158
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/381614
Trust: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2022-02-09 19:56:52 +00:00
Jacob Hoffman-Andrews dad33157f4 acme/autocert: include email in example
At Let's Encrypt, we've found that most autocert users do not provide an email address, which makes it hard to get in touch when things go wrong with their client. Demonstrating how to provide an email will probably encourage more people to provide one.

Change-Id: I0aa1ecea7cfbe55ae155804194fb57a8e695c658
GitHub-Last-Rev: 902b5fe2682a6be5af91e886272fd680fd67a94e
GitHub-Pull-Request: golang/crypto#186
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/323313
Reviewed-by: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Katie Hockman <katie@golang.org>
Trust: Katie Hockman <katie@golang.org>
2022-02-09 15:55:44 +00:00
HowJmay 2f5cac09c9 acme, sha3, ssh: fix the typos
Change-Id: I2287ab3492c105791b03f40d45b5dff5a56aa32a
GitHub-Last-Rev: 02cdf82d8b1604fc47b656172b102a48a70e6f05
GitHub-Pull-Request: golang/crypto#183
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/319269
Trust: Alberto Donizetti <alb.donizetti@gmail.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2022-02-09 15:53:12 +00:00
Ben BurkertandBrad Fitzpatrick 65fa2f7aa8 acme: remove support for pre-RFC 8555 ACME spec
LetsEncrypt removed it anyway.

No API changes. Just a lot of deleted code.

Fixes golang/go#46654

Co-authored-by: Brad Fitzpatrick <bradfitz@golang.org>
Change-Id: I65cd0d33236033682b767403ad92aa572bee4fdd
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/380314
Trust: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Trust: Brad Fitzpatrick <bradfitz@golang.org>
2022-02-09 15:49:31 +00:00
Dan Kortschak bba287dce9 ocsp: fix typo
Change-Id: Icd770cee9ce183f75c49c301f96a4aab73e80303
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/381674
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Cherry Mui <cherryyz@google.com>
2022-02-08 23:39:18 +00:00
Filippo Valsorda 20e1d8d225 ed25519: drop Go 1.12 compatibility
x/crypto/ed25519 moved to the standard library in Go 1.13. This change
drops the compatibility wrapper we were keeping around to make Go 1.12
and earlier work.

Change-Id: I860ed963a0660753d01c89014c21360b239a38ac
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/317169
Trust: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Katie Hockman <katie@golang.org>
Trust: Katie Hockman <katie@golang.org>
2022-02-08 05:03:32 +00:00
Roland Shoemaker 30dcbda588 acme: expose Client KID field
Expose the previously private KID field of the Client type. This allows
callers which have locally cached their key identity to avoid needing
to make a call to the ACME service every time they construct a new
client.

Fixes golang/go#46303

Change-Id: I219167c5b941f56a2028c4bc253ff56386845549
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/354697
Trust: Katie Hockman <katie@golang.org>
Reviewed-by: Katie Hockman <katie@golang.org>
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-01-31 19:55:33 +00:00
Filippo Valsorda 198e4374d7 acme/autocert: replace all ACME server stubs with acmetest
Change-Id: Ie5520f33674471b4a018feb9d0efaf6696ea38a2
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/381715
Run-TryBot: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Trust: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-01-28 20:06:15 +00:00
Filippo Valsorda aa10faf2a1 acme/autocert: renew Let's Encrypt certificates likely to get revoked
Let's Encrypt is revoking all certificates verified with TLS-ALPN-01
beofre January 26th due to a compliance issue. Detect them and force a
renewal.

Also, fix the tests which were not testing if expired certificates were
renewed anymore, as the test certificates were always invalid due to not
having SANs.

Change-Id: If9d0632b2edfe0b7fb70f6cfd7e65e46e2d047dc
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/381114
Trust: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2022-01-26 23:43:51 +00:00
Roland Shoemaker e04a8579fe ssh/agent: fix TestMalformedRequests
Use a net.Pipe, rather than setting up a local TCP connection,
for testing malformed SSH requests, since we don't need the
complex intricacies of a real connection to test this protocol
behavior.

Fixes golang/go#50161

Change-Id: I5e46c42041ddd03b06725469216b82b801990e64
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/371874
Reviewed-by: Bryan Mills <bcmills@google.com>
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
2022-01-26 17:37:29 +00:00
Bryan C. Mills 5e0467b6c7 acme: in TestFetchCertCancel, do not reply before the cancellation propagates
Fixes golang/go#44611

Change-Id: I81b3c3b7be25fe4e492695fa5935e70aa7b96c07
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/378074
Trust: Bryan Mills <bcmills@google.com>
Run-TryBot: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
2022-01-12 18:07:41 +00:00
127 changed files with 3026 additions and 6954 deletions
-3
View File
@@ -1,3 +0,0 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at https://tip.golang.org/AUTHORS.
-3
View File
@@ -1,3 +0,0 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at https://tip.golang.org/CONTRIBUTORS.
+91 -375
View File
@@ -3,17 +3,20 @@
// license that can be found in the LICENSE file.
// Package acme provides an implementation of the
// Automatic Certificate Management Environment (ACME) spec.
// The initial implementation was based on ACME draft-02 and
// is now being extended to comply with RFC 8555.
// See https://tools.ietf.org/html/draft-ietf-acme-acme-02
// and https://tools.ietf.org/html/rfc8555 for details.
// Automatic Certificate Management Environment (ACME) spec,
// most famously used by Let's Encrypt.
//
// The initial implementation of this package was based on an early version
// of the spec. The current implementation supports only the modern
// RFC 8555 but some of the old API surface remains for compatibility.
// While code using the old API will still compile, it will return an error.
// Note the deprecation comments to update your code.
//
// See https://tools.ietf.org/html/rfc8555 for the spec.
//
// Most common scenarios will want to use autocert subdirectory instead,
// which provides automatic access to certificates from Let's Encrypt
// and any other ACME-based CA.
//
// This package is a work in progress and makes no API stability promises.
package acme
import (
@@ -33,8 +36,6 @@ import (
"encoding/pem"
"errors"
"fmt"
"io"
"io/ioutil"
"math/big"
"net/http"
"strings"
@@ -72,22 +73,22 @@ const (
)
// Client is an ACME client.
//
// The only required field is Key. An example of creating a client with a new key
// is as follows:
//
// key, err := rsa.GenerateKey(rand.Reader, 2048)
// if err != nil {
// log.Fatal(err)
// }
// client := &Client{Key: key}
//
// key, err := rsa.GenerateKey(rand.Reader, 2048)
// if err != nil {
// log.Fatal(err)
// }
// client := &Client{Key: key}
type Client struct {
// Key is the account key used to register with a CA and sign requests.
// Key.Public() must return a *rsa.PublicKey or *ecdsa.PublicKey.
//
// The following algorithms are supported:
// RS256, ES256, ES384 and ES512.
// See RFC7518 for more details about the algorithms.
// See RFC 7518 for more details about the algorithms.
Key crypto.Signer
// HTTPClient optionally specifies an HTTP client to use
@@ -125,7 +126,9 @@ type Client struct {
cacheMu sync.Mutex
dir *Directory // cached result of Client's Discover method
kid keyID // cached Account.URI obtained from registerRFC or getAccountRFC
// KID is the key identifier provided by the CA. If not provided it will be
// retrieved from the CA by making a call to the registration endpoint.
KID KeyID
noncesMu sync.Mutex
nonces map[string]struct{} // nonces collected from previous responses
@@ -140,23 +143,22 @@ type Client struct {
//
// When in pre-RFC mode or when c.getRegRFC responds with an error, accountKID
// returns noKeyID.
func (c *Client) accountKID(ctx context.Context) keyID {
func (c *Client) accountKID(ctx context.Context) KeyID {
c.cacheMu.Lock()
defer c.cacheMu.Unlock()
if !c.dir.rfcCompliant() {
return noKeyID
}
if c.kid != noKeyID {
return c.kid
if c.KID != noKeyID {
return c.KID
}
a, err := c.getRegRFC(ctx)
if err != nil {
return noKeyID
}
c.kid = keyID(a.URI)
return c.kid
c.KID = KeyID(a.URI)
return c.KID
}
var errPreRFC = errors.New("acme: server does not support the RFC 8555 version of ACME")
// Discover performs ACME server discovery using c.DirectoryURL.
//
// It caches successful result. So, subsequent calls will not result in
@@ -177,53 +179,36 @@ func (c *Client) Discover(ctx context.Context) (Directory, error) {
c.addNonce(res.Header)
var v struct {
Reg string `json:"new-reg"`
RegRFC string `json:"newAccount"`
Authz string `json:"new-authz"`
AuthzRFC string `json:"newAuthz"`
OrderRFC string `json:"newOrder"`
Cert string `json:"new-cert"`
Revoke string `json:"revoke-cert"`
RevokeRFC string `json:"revokeCert"`
NonceRFC string `json:"newNonce"`
KeyChangeRFC string `json:"keyChange"`
Meta struct {
Terms string `json:"terms-of-service"`
TermsRFC string `json:"termsOfService"`
WebsiteRFC string `json:"website"`
CAA []string `json:"caa-identities"`
CAARFC []string `json:"caaIdentities"`
ExternalAcctRFC bool `json:"externalAccountRequired"`
Reg string `json:"newAccount"`
Authz string `json:"newAuthz"`
Order string `json:"newOrder"`
Revoke string `json:"revokeCert"`
Nonce string `json:"newNonce"`
KeyChange string `json:"keyChange"`
Meta struct {
Terms string `json:"termsOfService"`
Website string `json:"website"`
CAA []string `json:"caaIdentities"`
ExternalAcct bool `json:"externalAccountRequired"`
}
}
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
return Directory{}, err
}
if v.OrderRFC == "" {
// Non-RFC compliant ACME CA.
c.dir = &Directory{
RegURL: v.Reg,
AuthzURL: v.Authz,
CertURL: v.Cert,
RevokeURL: v.Revoke,
Terms: v.Meta.Terms,
Website: v.Meta.WebsiteRFC,
CAA: v.Meta.CAA,
}
return *c.dir, nil
if v.Order == "" {
return Directory{}, errPreRFC
}
// RFC compliant ACME CA.
c.dir = &Directory{
RegURL: v.RegRFC,
AuthzURL: v.AuthzRFC,
OrderURL: v.OrderRFC,
RevokeURL: v.RevokeRFC,
NonceURL: v.NonceRFC,
KeyChangeURL: v.KeyChangeRFC,
Terms: v.Meta.TermsRFC,
Website: v.Meta.WebsiteRFC,
CAA: v.Meta.CAARFC,
ExternalAccountRequired: v.Meta.ExternalAcctRFC,
RegURL: v.Reg,
AuthzURL: v.Authz,
OrderURL: v.Order,
RevokeURL: v.Revoke,
NonceURL: v.Nonce,
KeyChangeURL: v.KeyChange,
Terms: v.Meta.Terms,
Website: v.Meta.Website,
CAA: v.Meta.CAA,
ExternalAccountRequired: v.Meta.ExternalAcct,
}
return *c.dir, nil
}
@@ -235,55 +220,11 @@ func (c *Client) directoryURL() string {
return LetsEncryptURL
}
// CreateCert requests a new certificate using the Certificate Signing Request csr encoded in DER format.
// It is incompatible with RFC 8555. Callers should use CreateOrderCert when interfacing
// with an RFC-compliant CA.
// CreateCert was part of the old version of ACME. It is incompatible with RFC 8555.
//
// The exp argument indicates the desired certificate validity duration. CA may issue a certificate
// with a different duration.
// If the bundle argument is true, the returned value will also contain the CA (issuer) certificate chain.
//
// In the case where CA server does not provide the issued certificate in the response,
// CreateCert will poll certURL using c.FetchCert, which will result in additional round-trips.
// In such a scenario, the caller can cancel the polling with ctx.
//
// CreateCert returns an error if the CA's response or chain was unreasonably large.
// Callers are encouraged to parse the returned value to ensure the certificate is valid and has the expected features.
// Deprecated: this was for the pre-RFC 8555 version of ACME. Callers should use CreateOrderCert.
func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration, bundle bool) (der [][]byte, certURL string, err error) {
if _, err := c.Discover(ctx); err != nil {
return nil, "", err
}
req := struct {
Resource string `json:"resource"`
CSR string `json:"csr"`
NotBefore string `json:"notBefore,omitempty"`
NotAfter string `json:"notAfter,omitempty"`
}{
Resource: "new-cert",
CSR: base64.RawURLEncoding.EncodeToString(csr),
}
now := timeNow()
req.NotBefore = now.Format(time.RFC3339)
if exp > 0 {
req.NotAfter = now.Add(exp).Format(time.RFC3339)
}
res, err := c.post(ctx, nil, c.dir.CertURL, req, wantStatus(http.StatusCreated))
if err != nil {
return nil, "", err
}
defer res.Body.Close()
curl := res.Header.Get("Location") // cert permanent URL
if res.ContentLength == 0 {
// no cert in the body; poll until we get it
cert, err := c.FetchCert(ctx, curl, bundle)
return cert, curl, err
}
// slurp issued cert and CA chain, if requested
cert, err := c.responseCert(ctx, res, bundle)
return cert, curl, err
return nil, "", errPreRFC
}
// FetchCert retrieves already issued certificate from the given url, in DER format.
@@ -297,20 +238,10 @@ func (c *Client) CreateCert(ctx context.Context, csr []byte, exp time.Duration,
// Callers are encouraged to parse the returned value to ensure the certificate is valid
// and has expected features.
func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]byte, error) {
dir, err := c.Discover(ctx)
if err != nil {
if _, err := c.Discover(ctx); err != nil {
return nil, err
}
if dir.rfcCompliant() {
return c.fetchCertRFC(ctx, url, bundle)
}
// Legacy non-authenticated GET request.
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
if err != nil {
return nil, err
}
return c.responseCert(ctx, res, bundle)
return c.fetchCertRFC(ctx, url, bundle)
}
// RevokeCert revokes a previously issued certificate cert, provided in DER format.
@@ -320,30 +251,10 @@ func (c *Client) FetchCert(ctx context.Context, url string, bundle bool) ([][]by
// For instance, the key pair of the certificate may be authorized.
// If the key is nil, c.Key is used instead.
func (c *Client) RevokeCert(ctx context.Context, key crypto.Signer, cert []byte, reason CRLReasonCode) error {
dir, err := c.Discover(ctx)
if err != nil {
if _, err := c.Discover(ctx); err != nil {
return err
}
if dir.rfcCompliant() {
return c.revokeCertRFC(ctx, key, cert, reason)
}
// Legacy CA.
body := &struct {
Resource string `json:"resource"`
Cert string `json:"certificate"`
Reason int `json:"reason"`
}{
Resource: "revoke-cert",
Cert: base64.RawURLEncoding.EncodeToString(cert),
Reason: int(reason),
}
res, err := c.post(ctx, key, dir.RevokeURL, body, wantStatus(http.StatusOK))
if err != nil {
return err
}
defer res.Body.Close()
return nil
return c.revokeCertRFC(ctx, key, cert, reason)
}
// AcceptTOS always returns true to indicate the acceptance of a CA's Terms of Service
@@ -366,75 +277,47 @@ func (c *Client) Register(ctx context.Context, acct *Account, prompt func(tosURL
if c.Key == nil {
return nil, errors.New("acme: client.Key must be set to Register")
}
dir, err := c.Discover(ctx)
if err != nil {
if _, err := c.Discover(ctx); err != nil {
return nil, err
}
if dir.rfcCompliant() {
return c.registerRFC(ctx, acct, prompt)
}
// Legacy ACME draft registration flow.
a, err := c.doReg(ctx, dir.RegURL, "new-reg", acct)
if err != nil {
return nil, err
}
var accept bool
if a.CurrentTerms != "" && a.CurrentTerms != a.AgreedTerms {
accept = prompt(a.CurrentTerms)
}
if accept {
a.AgreedTerms = a.CurrentTerms
a, err = c.UpdateReg(ctx, a)
}
return a, err
return c.registerRFC(ctx, acct, prompt)
}
// GetReg retrieves an existing account associated with c.Key.
//
// The url argument is an Account URI used with pre-RFC 8555 CAs.
// It is ignored when interfacing with an RFC-compliant CA.
// The url argument is a legacy artifact of the pre-RFC 8555 API
// and is ignored.
func (c *Client) GetReg(ctx context.Context, url string) (*Account, error) {
dir, err := c.Discover(ctx)
if err != nil {
if _, err := c.Discover(ctx); err != nil {
return nil, err
}
if dir.rfcCompliant() {
return c.getRegRFC(ctx)
}
// Legacy CA.
a, err := c.doReg(ctx, url, "reg", nil)
if err != nil {
return nil, err
}
a.URI = url
return a, nil
return c.getRegRFC(ctx)
}
// UpdateReg updates an existing registration.
// It returns an updated account copy. The provided account is not modified.
//
// When interfacing with RFC-compliant CAs, a.URI is ignored and the account URL
// associated with c.Key is used instead.
// The account's URI is ignored and the account URL associated with
// c.Key is used instead.
func (c *Client) UpdateReg(ctx context.Context, acct *Account) (*Account, error) {
dir, err := c.Discover(ctx)
if err != nil {
if _, err := c.Discover(ctx); err != nil {
return nil, err
}
if dir.rfcCompliant() {
return c.updateRegRFC(ctx, acct)
}
return c.updateRegRFC(ctx, acct)
}
// Legacy CA.
uri := acct.URI
a, err := c.doReg(ctx, uri, "reg", acct)
if err != nil {
return nil, err
}
a.URI = uri
return a, nil
// AccountKeyRollover attempts to transition a client's account key to a new key.
// On success client's Key is updated which is not concurrency safe.
// On failure an error will be returned.
// The new key is already registered with the ACME provider if the following is true:
// - error is of type acme.Error
// - StatusCode should be 409 (Conflict)
// - Location header will have the KID of the associated account
//
// More about account key rollover can be found at
// https://tools.ietf.org/html/rfc8555#section-7.3.5.
func (c *Client) AccountKeyRollover(ctx context.Context, newKey crypto.Signer) error {
return c.accountKeyRollover(ctx, newKey)
}
// Authorize performs the initial step in the pre-authorization flow,
@@ -503,17 +386,11 @@ func (c *Client) authorize(ctx context.Context, typ, val string) (*Authorization
// If a caller needs to poll an authorization until its status is final,
// see the WaitAuthorization method.
func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorization, error) {
dir, err := c.Discover(ctx)
if err != nil {
if _, err := c.Discover(ctx); err != nil {
return nil, err
}
var res *http.Response
if dir.rfcCompliant() {
res, err = c.postAsGet(ctx, url, wantStatus(http.StatusOK))
} else {
res, err = c.get(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
}
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK))
if err != nil {
return nil, err
}
@@ -535,7 +412,6 @@ func (c *Client) GetAuthorization(ctx context.Context, url string) (*Authorizati
//
// It does not revoke existing certificates.
func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
// Required for c.accountKID() when in RFC mode.
if _, err := c.Discover(ctx); err != nil {
return err
}
@@ -565,18 +441,11 @@ func (c *Client) RevokeAuthorization(ctx context.Context, url string) error {
// In all other cases WaitAuthorization returns an error.
// If the Status is StatusInvalid, the returned error is of type *AuthorizationError.
func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorization, error) {
// Required for c.accountKID() when in RFC mode.
dir, err := c.Discover(ctx)
if err != nil {
if _, err := c.Discover(ctx); err != nil {
return nil, err
}
getfn := c.postAsGet
if !dir.rfcCompliant() {
getfn = c.get
}
for {
res, err := getfn(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
if err != nil {
return nil, err
}
@@ -619,17 +488,11 @@ func (c *Client) WaitAuthorization(ctx context.Context, url string) (*Authorizat
//
// A client typically polls a challenge status using this method.
func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, error) {
// Required for c.accountKID() when in RFC mode.
dir, err := c.Discover(ctx)
if err != nil {
if _, err := c.Discover(ctx); err != nil {
return nil, err
}
getfn := c.postAsGet
if !dir.rfcCompliant() {
getfn = c.get
}
res, err := getfn(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
res, err := c.postAsGet(ctx, url, wantStatus(http.StatusOK, http.StatusAccepted))
if err != nil {
return nil, err
}
@@ -647,29 +510,11 @@ func (c *Client) GetChallenge(ctx context.Context, url string) (*Challenge, erro
//
// The server will then perform the validation asynchronously.
func (c *Client) Accept(ctx context.Context, chal *Challenge) (*Challenge, error) {
// Required for c.accountKID() when in RFC mode.
dir, err := c.Discover(ctx)
if err != nil {
if _, err := c.Discover(ctx); err != nil {
return nil, err
}
var req interface{} = json.RawMessage("{}") // RFC-compliant CA
if !dir.rfcCompliant() {
auth, err := keyAuth(c.Key.Public(), chal.Token)
if err != nil {
return nil, err
}
req = struct {
Resource string `json:"resource"`
Type string `json:"type"`
Auth string `json:"keyAuthorization"`
}{
Resource: "challenge",
Type: chal.Type,
Auth: auth,
}
}
res, err := c.post(ctx, nil, chal.URI, req, wantStatus(
res, err := c.post(ctx, nil, chal.URI, json.RawMessage("{}"), wantStatus(
http.StatusOK, // according to the spec
http.StatusAccepted, // Let's Encrypt: see https://goo.gl/WsJ7VT (acme-divergences.md)
))
@@ -720,7 +565,7 @@ func (c *Client) HTTP01ChallengePath(token string) string {
// TLSSNI01ChallengeCert creates a certificate for TLS-SNI-01 challenge response.
//
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of ACME spec.
// 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 {
@@ -738,7 +583,7 @@ func (c *Client) TLSSNI01ChallengeCert(token string, opt ...CertOption) (cert tl
// TLSSNI02ChallengeCert creates a certificate for TLS-SNI-02 challenge response.
//
// Deprecated: This challenge type is unused in both draft-02 and RFC versions of ACME spec.
// 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[:])
@@ -805,63 +650,6 @@ func (c *Client) TLSALPN01ChallengeCert(token, domain string, opt ...CertOption)
return tlsChallengeCert([]string{domain}, newOpt)
}
// doReg sends all types of registration requests the old way (pre-RFC world).
// The type of request is identified by typ argument, which is a "resource"
// in the ACME spec terms.
//
// A non-nil acct argument indicates whether the intention is to mutate data
// of the Account. Only Contact and Agreement of its fields are used
// in such cases.
func (c *Client) doReg(ctx context.Context, url string, typ string, acct *Account) (*Account, error) {
req := struct {
Resource string `json:"resource"`
Contact []string `json:"contact,omitempty"`
Agreement string `json:"agreement,omitempty"`
}{
Resource: typ,
}
if acct != nil {
req.Contact = acct.Contact
req.Agreement = acct.AgreedTerms
}
res, err := c.post(ctx, nil, url, req, wantStatus(
http.StatusOK, // updates and deletes
http.StatusCreated, // new account creation
http.StatusAccepted, // Let's Encrypt divergent implementation
))
if err != nil {
return nil, err
}
defer res.Body.Close()
var v struct {
Contact []string
Agreement string
Authorizations string
Certificates string
}
if err := json.NewDecoder(res.Body).Decode(&v); err != nil {
return nil, fmt.Errorf("acme: invalid response: %v", err)
}
var tos string
if v := linkHeader(res.Header, "terms-of-service"); len(v) > 0 {
tos = v[0]
}
var authz string
if v := linkHeader(res.Header, "next"); len(v) > 0 {
authz = v[0]
}
return &Account{
URI: res.Header.Get("Location"),
Contact: v.Contact,
AgreedTerms: v.Agreement,
CurrentTerms: tos,
Authz: authz,
Authorizations: v.Authorizations,
Certificates: v.Certificates,
}, nil
}
// popNonce returns a nonce value previously stored with c.addNonce
// or fetches a fresh one from c.dir.NonceURL.
// If NonceURL is empty, it first tries c.directoryURL() and, failing that,
@@ -936,78 +724,6 @@ func nonceFromHeader(h http.Header) string {
return h.Get("Replay-Nonce")
}
func (c *Client) responseCert(ctx context.Context, res *http.Response, bundle bool) ([][]byte, error) {
b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
if err != nil {
return nil, fmt.Errorf("acme: response stream: %v", err)
}
if len(b) > maxCertSize {
return nil, errors.New("acme: certificate is too big")
}
cert := [][]byte{b}
if !bundle {
return cert, nil
}
// Append CA chain cert(s).
// At least one is required according to the spec:
// https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-6.3.1
up := linkHeader(res.Header, "up")
if len(up) == 0 {
return nil, errors.New("acme: rel=up link not found")
}
if len(up) > maxChainLen {
return nil, errors.New("acme: rel=up link is too large")
}
for _, url := range up {
cc, err := c.chainCert(ctx, url, 0)
if err != nil {
return nil, err
}
cert = append(cert, cc...)
}
return cert, nil
}
// chainCert fetches CA certificate chain recursively by following "up" links.
// Each recursive call increments the depth by 1, resulting in an error
// if the recursion level reaches maxChainLen.
//
// First chainCert call starts with depth of 0.
func (c *Client) chainCert(ctx context.Context, url string, depth int) ([][]byte, error) {
if depth >= maxChainLen {
return nil, errors.New("acme: certificate chain is too deep")
}
res, err := c.get(ctx, url, wantStatus(http.StatusOK))
if err != nil {
return nil, err
}
defer res.Body.Close()
b, err := ioutil.ReadAll(io.LimitReader(res.Body, maxCertSize+1))
if err != nil {
return nil, err
}
if len(b) > maxCertSize {
return nil, errors.New("acme: certificate is too big")
}
chain := [][]byte{b}
uplink := linkHeader(res.Header, "up")
if len(uplink) > maxChainLen {
return nil, errors.New("acme: certificate chain is too large")
}
for _, up := range uplink {
cc, err := c.chainCert(ctx, up, depth+1)
if err != nil {
return nil, err
}
chain = append(chain, cc...)
}
return chain, nil
}
// linkHeader returns URI-Reference values of all Link headers
// with relation-type rel.
// See https://tools.ietf.org/html/rfc5988#section-5 for details.
@@ -1098,5 +814,5 @@ func encodePEM(typ string, b []byte) []byte {
return pem.EncodeToMemory(pb)
}
// timeNow is useful for testing for fixed current time.
// timeNow is time.Now, except in tests which can mess with it.
var timeNow = time.Now
+12 -640
View File
@@ -79,115 +79,6 @@ func decodeJWSHead(r io.Reader) (*jwsHead, error) {
return &head, nil
}
func TestDiscover(t *testing.T) {
const (
reg = "https://example.com/acme/new-reg"
authz = "https://example.com/acme/new-authz"
cert = "https://example.com/acme/new-cert"
revoke = "https://example.com/acme/revoke-cert"
)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Replay-Nonce", "testnonce")
fmt.Fprintf(w, `{
"new-reg": %q,
"new-authz": %q,
"new-cert": %q,
"revoke-cert": %q
}`, reg, authz, cert, revoke)
}))
defer ts.Close()
c := Client{DirectoryURL: ts.URL}
dir, err := c.Discover(context.Background())
if err != nil {
t.Fatal(err)
}
if dir.RegURL != reg {
t.Errorf("dir.RegURL = %q; want %q", dir.RegURL, reg)
}
if dir.AuthzURL != authz {
t.Errorf("dir.AuthzURL = %q; want %q", dir.AuthzURL, authz)
}
if dir.CertURL != cert {
t.Errorf("dir.CertURL = %q; want %q", dir.CertURL, cert)
}
if dir.RevokeURL != revoke {
t.Errorf("dir.RevokeURL = %q; want %q", dir.RevokeURL, revoke)
}
if _, exist := c.nonces["testnonce"]; !exist {
t.Errorf("c.nonces = %q; want 'testnonce' in the map", c.nonces)
}
}
func TestRegister(t *testing.T) {
contacts := []string{"mailto:admin@example.com"}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
w.Header().Set("Replay-Nonce", "test-nonce")
return
}
if r.Method != "POST" {
t.Errorf("r.Method = %q; want POST", r.Method)
}
var j struct {
Resource string
Contact []string
Agreement string
}
decodeJWSRequest(t, &j, r.Body)
// Test request
if j.Resource != "new-reg" {
t.Errorf("j.Resource = %q; want new-reg", j.Resource)
}
if !reflect.DeepEqual(j.Contact, contacts) {
t.Errorf("j.Contact = %v; want %v", j.Contact, contacts)
}
w.Header().Set("Location", "https://ca.tld/acme/reg/1")
w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`)
w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`)
w.Header().Add("Link", `<https://ca.tld/acme/terms>;rel="terms-of-service"`)
w.WriteHeader(http.StatusCreated)
b, _ := json.Marshal(contacts)
fmt.Fprintf(w, `{"contact": %s}`, b)
}))
defer ts.Close()
prompt := func(url string) bool {
const terms = "https://ca.tld/acme/terms"
if url != terms {
t.Errorf("prompt url = %q; want %q", url, terms)
}
return false
}
c := Client{
Key: testKeyEC,
DirectoryURL: ts.URL,
dir: &Directory{RegURL: ts.URL},
}
a := &Account{Contact: contacts}
var err error
if a, err = c.Register(context.Background(), a, prompt); err != nil {
t.Fatal(err)
}
if a.URI != "https://ca.tld/acme/reg/1" {
t.Errorf("a.URI = %q; want https://ca.tld/acme/reg/1", a.URI)
}
if a.Authz != "https://ca.tld/acme/new-authz" {
t.Errorf("a.Authz = %q; want https://ca.tld/acme/new-authz", a.Authz)
}
if a.CurrentTerms != "https://ca.tld/acme/terms" {
t.Errorf("a.CurrentTerms = %q; want https://ca.tld/acme/terms", a.CurrentTerms)
}
if !reflect.DeepEqual(a.Contact, contacts) {
t.Errorf("a.Contact = %v; want %v", a.Contact, contacts)
}
}
func TestRegisterWithoutKey(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
@@ -213,134 +104,6 @@ func TestRegisterWithoutKey(t *testing.T) {
}
}
func TestUpdateReg(t *testing.T) {
const terms = "https://ca.tld/acme/terms"
contacts := []string{"mailto:admin@example.com"}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
w.Header().Set("Replay-Nonce", "test-nonce")
return
}
if r.Method != "POST" {
t.Errorf("r.Method = %q; want POST", r.Method)
}
var j struct {
Resource string
Contact []string
Agreement string
}
decodeJWSRequest(t, &j, r.Body)
// Test request
if j.Resource != "reg" {
t.Errorf("j.Resource = %q; want reg", j.Resource)
}
if j.Agreement != terms {
t.Errorf("j.Agreement = %q; want %q", j.Agreement, terms)
}
if !reflect.DeepEqual(j.Contact, contacts) {
t.Errorf("j.Contact = %v; want %v", j.Contact, contacts)
}
w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`)
w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`)
w.Header().Add("Link", fmt.Sprintf(`<%s>;rel="terms-of-service"`, terms))
w.WriteHeader(http.StatusOK)
b, _ := json.Marshal(contacts)
fmt.Fprintf(w, `{"contact":%s, "agreement":%q}`, b, terms)
}))
defer ts.Close()
c := Client{
Key: testKeyEC,
DirectoryURL: ts.URL, // don't dial outside of localhost
dir: &Directory{}, // don't do discovery
}
a := &Account{URI: ts.URL, Contact: contacts, AgreedTerms: terms}
var err error
if a, err = c.UpdateReg(context.Background(), a); err != nil {
t.Fatal(err)
}
if a.Authz != "https://ca.tld/acme/new-authz" {
t.Errorf("a.Authz = %q; want https://ca.tld/acme/new-authz", a.Authz)
}
if a.AgreedTerms != terms {
t.Errorf("a.AgreedTerms = %q; want %q", a.AgreedTerms, terms)
}
if a.CurrentTerms != terms {
t.Errorf("a.CurrentTerms = %q; want %q", a.CurrentTerms, terms)
}
if a.URI != ts.URL {
t.Errorf("a.URI = %q; want %q", a.URI, ts.URL)
}
}
func TestGetReg(t *testing.T) {
const terms = "https://ca.tld/acme/terms"
const newTerms = "https://ca.tld/acme/new-terms"
contacts := []string{"mailto:admin@example.com"}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
w.Header().Set("Replay-Nonce", "test-nonce")
return
}
if r.Method != "POST" {
t.Errorf("r.Method = %q; want POST", r.Method)
}
var j struct {
Resource string
Contact []string
Agreement string
}
decodeJWSRequest(t, &j, r.Body)
// Test request
if j.Resource != "reg" {
t.Errorf("j.Resource = %q; want reg", j.Resource)
}
if len(j.Contact) != 0 {
t.Errorf("j.Contact = %v", j.Contact)
}
if j.Agreement != "" {
t.Errorf("j.Agreement = %q", j.Agreement)
}
w.Header().Set("Link", `<https://ca.tld/acme/new-authz>;rel="next"`)
w.Header().Add("Link", `<https://ca.tld/acme/recover-reg>;rel="recover"`)
w.Header().Add("Link", fmt.Sprintf(`<%s>;rel="terms-of-service"`, newTerms))
w.WriteHeader(http.StatusOK)
b, _ := json.Marshal(contacts)
fmt.Fprintf(w, `{"contact":%s, "agreement":%q}`, b, terms)
}))
defer ts.Close()
c := Client{
Key: testKeyEC,
DirectoryURL: ts.URL, // don't dial outside of localhost
dir: &Directory{}, // don't do discovery
}
a, err := c.GetReg(context.Background(), ts.URL)
if err != nil {
t.Fatal(err)
}
if a.Authz != "https://ca.tld/acme/new-authz" {
t.Errorf("a.AuthzURL = %q; want https://ca.tld/acme/new-authz", a.Authz)
}
if a.AgreedTerms != terms {
t.Errorf("a.AgreedTerms = %q; want %q", a.AgreedTerms, terms)
}
if a.CurrentTerms != newTerms {
t.Errorf("a.CurrentTerms = %q; want %q", a.CurrentTerms, newTerms)
}
if a.URI != ts.URL {
t.Errorf("a.URI = %q; want %q", a.URI, ts.URL)
}
}
func TestAuthorize(t *testing.T) {
tt := []struct{ typ, value string }{
{"dns", "example.com"},
@@ -491,82 +254,6 @@ func TestAuthorizeValid(t *testing.T) {
}
}
func TestGetAuthorization(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
t.Errorf("r.Method = %q; want GET", r.Method)
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{
"identifier": {"type":"dns","value":"example.com"},
"status":"pending",
"challenges":[
{
"type":"http-01",
"status":"pending",
"uri":"https://ca.tld/acme/challenge/publickey/id1",
"token":"token1"
},
{
"type":"tls-sni-01",
"status":"pending",
"uri":"https://ca.tld/acme/challenge/publickey/id2",
"token":"token2"
}
],
"combinations":[[0],[1]]}`)
}))
defer ts.Close()
cl := Client{Key: testKeyEC, DirectoryURL: ts.URL}
auth, err := cl.GetAuthorization(context.Background(), ts.URL)
if err != nil {
t.Fatal(err)
}
if auth.Status != "pending" {
t.Errorf("Status = %q; want pending", auth.Status)
}
if auth.Identifier.Type != "dns" {
t.Errorf("Identifier.Type = %q; want dns", auth.Identifier.Type)
}
if auth.Identifier.Value != "example.com" {
t.Errorf("Identifier.Value = %q; want example.com", auth.Identifier.Value)
}
if n := len(auth.Challenges); n != 2 {
t.Fatalf("len(set.Challenges) = %d; want 2", n)
}
c := auth.Challenges[0]
if c.Type != "http-01" {
t.Errorf("c.Type = %q; want http-01", c.Type)
}
if c.URI != "https://ca.tld/acme/challenge/publickey/id1" {
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI)
}
if c.Token != "token1" {
t.Errorf("c.Token = %q; want token1", c.Token)
}
c = auth.Challenges[1]
if c.Type != "tls-sni-01" {
t.Errorf("c.Type = %q; want tls-sni-01", c.Type)
}
if c.URI != "https://ca.tld/acme/challenge/publickey/id2" {
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id2", c.URI)
}
if c.Token != "token2" {
t.Errorf("c.Token = %q; want token2", c.Token)
}
combs := [][]int{{0}, {1}}
if !reflect.DeepEqual(auth.Combinations, combs) {
t.Errorf("auth.Combinations: %+v\nwant: %+v\n", auth.Combinations, combs)
}
}
func TestWaitAuthorization(t *testing.T) {
t.Run("wait loop", func(t *testing.T) {
var count int
@@ -678,9 +365,13 @@ func TestWaitAuthorization(t *testing.T) {
}
})
}
func runWaitAuthorization(ctx context.Context, t *testing.T, h http.HandlerFunc) (*Authorization, error) {
t.Helper()
ts := httptest.NewServer(h)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Replay-Nonce", fmt.Sprintf("bad-test-nonce-%v", time.Now().UnixNano()))
h(w, r)
}))
defer ts.Close()
type res struct {
authz *Authorization
@@ -688,7 +379,12 @@ func runWaitAuthorization(ctx context.Context, t *testing.T, h http.HandlerFunc)
}
ch := make(chan res, 1)
go func() {
var client = Client{DirectoryURL: ts.URL}
client := &Client{
Key: testKey,
DirectoryURL: ts.URL,
dir: &Directory{},
KID: "some-key-id", // set to avoid lookup attempt
}
a, err := client.WaitAuthorization(ctx, ts.URL)
ch <- res{a, err}
}()
@@ -743,238 +439,9 @@ func TestRevokeAuthorization(t *testing.T) {
}
}
func TestPollChallenge(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
t.Errorf("r.Method = %q; want GET", r.Method)
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, `{
"type":"http-01",
"status":"pending",
"uri":"https://ca.tld/acme/challenge/publickey/id1",
"token":"token1"}`)
}))
defer ts.Close()
cl := Client{Key: testKeyEC, DirectoryURL: ts.URL}
chall, err := cl.GetChallenge(context.Background(), ts.URL)
if err != nil {
t.Fatal(err)
}
if chall.Status != "pending" {
t.Errorf("Status = %q; want pending", chall.Status)
}
if chall.Type != "http-01" {
t.Errorf("c.Type = %q; want http-01", chall.Type)
}
if chall.URI != "https://ca.tld/acme/challenge/publickey/id1" {
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", chall.URI)
}
if chall.Token != "token1" {
t.Errorf("c.Token = %q; want token1", chall.Token)
}
}
func TestAcceptChallenge(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
w.Header().Set("Replay-Nonce", "test-nonce")
return
}
if r.Method != "POST" {
t.Errorf("r.Method = %q; want POST", r.Method)
}
var j struct {
Resource string
Type string
Auth string `json:"keyAuthorization"`
}
decodeJWSRequest(t, &j, r.Body)
// Test request
if j.Resource != "challenge" {
t.Errorf(`resource = %q; want "challenge"`, j.Resource)
}
if j.Type != "http-01" {
t.Errorf(`type = %q; want "http-01"`, j.Type)
}
keyAuth := "token1." + testKeyECThumbprint
if j.Auth != keyAuth {
t.Errorf(`keyAuthorization = %q; want %q`, j.Auth, keyAuth)
}
// Respond to request
w.WriteHeader(http.StatusAccepted)
fmt.Fprintf(w, `{
"type":"http-01",
"status":"pending",
"uri":"https://ca.tld/acme/challenge/publickey/id1",
"token":"token1",
"keyAuthorization":%q
}`, keyAuth)
}))
defer ts.Close()
cl := Client{
Key: testKeyEC,
DirectoryURL: ts.URL, // don't dial outside of localhost
dir: &Directory{}, // don't do discovery
}
c, err := cl.Accept(context.Background(), &Challenge{
URI: ts.URL,
Token: "token1",
Type: "http-01",
})
if err != nil {
t.Fatal(err)
}
if c.Type != "http-01" {
t.Errorf("c.Type = %q; want http-01", c.Type)
}
if c.URI != "https://ca.tld/acme/challenge/publickey/id1" {
t.Errorf("c.URI = %q; want https://ca.tld/acme/challenge/publickey/id1", c.URI)
}
if c.Token != "token1" {
t.Errorf("c.Token = %q; want token1", c.Token)
}
}
func TestNewCert(t *testing.T) {
notBefore := time.Now()
notAfter := notBefore.AddDate(0, 2, 0)
timeNow = func() time.Time { return notBefore }
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
w.Header().Set("Replay-Nonce", "test-nonce")
return
}
if r.Method != "POST" {
t.Errorf("r.Method = %q; want POST", r.Method)
}
var j struct {
Resource string `json:"resource"`
CSR string `json:"csr"`
NotBefore string `json:"notBefore,omitempty"`
NotAfter string `json:"notAfter,omitempty"`
}
decodeJWSRequest(t, &j, r.Body)
// Test request
if j.Resource != "new-cert" {
t.Errorf(`resource = %q; want "new-cert"`, j.Resource)
}
if j.NotBefore != notBefore.Format(time.RFC3339) {
t.Errorf(`notBefore = %q; wanted %q`, j.NotBefore, notBefore.Format(time.RFC3339))
}
if j.NotAfter != notAfter.Format(time.RFC3339) {
t.Errorf(`notAfter = %q; wanted %q`, j.NotAfter, notAfter.Format(time.RFC3339))
}
// Respond to request
template := x509.Certificate{
SerialNumber: big.NewInt(int64(1)),
Subject: pkix.Name{
Organization: []string{"goacme"},
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
sampleCert, err := x509.CreateCertificate(rand.Reader, &template, &template, &testKeyEC.PublicKey, testKeyEC)
if err != nil {
t.Fatalf("Error creating certificate: %v", err)
}
w.Header().Set("Location", "https://ca.tld/acme/cert/1")
w.WriteHeader(http.StatusCreated)
w.Write(sampleCert)
}))
defer ts.Close()
csr := x509.CertificateRequest{
Version: 0,
Subject: pkix.Name{
CommonName: "example.com",
Organization: []string{"goacme"},
},
}
csrb, err := x509.CreateCertificateRequest(rand.Reader, &csr, testKeyEC)
if err != nil {
t.Fatal(err)
}
c := Client{Key: testKeyEC, dir: &Directory{CertURL: ts.URL}}
cert, certURL, err := c.CreateCert(context.Background(), csrb, notAfter.Sub(notBefore), false)
if err != nil {
t.Fatal(err)
}
if cert == nil {
t.Errorf("cert is nil")
}
if certURL != "https://ca.tld/acme/cert/1" {
t.Errorf("certURL = %q; want https://ca.tld/acme/cert/1", certURL)
}
}
func TestFetchCert(t *testing.T) {
var count byte
var ts *httptest.Server
ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count++
if count < 3 {
up := fmt.Sprintf("<%s>;rel=up", ts.URL)
w.Header().Set("Link", up)
}
w.Write([]byte{count})
}))
defer ts.Close()
cl := newTestClient()
res, err := cl.FetchCert(context.Background(), ts.URL, true)
if err != nil {
t.Fatalf("FetchCert: %v", err)
}
cert := [][]byte{{1}, {2}, {3}}
if !reflect.DeepEqual(res, cert) {
t.Errorf("res = %v; want %v", res, cert)
}
}
func TestFetchCertRetry(t *testing.T) {
var count int
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if count < 1 {
w.Header().Set("Retry-After", "0")
w.WriteHeader(http.StatusTooManyRequests)
count++
return
}
w.Write([]byte{1})
}))
defer ts.Close()
cl := newTestClient()
res, err := cl.FetchCert(context.Background(), ts.URL, false)
if err != nil {
t.Fatalf("FetchCert: %v", err)
}
cert := [][]byte{{1}}
if !reflect.DeepEqual(res, cert) {
t.Errorf("res = %v; want %v", res, cert)
}
}
func TestFetchCertCancel(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
<-r.Context().Done()
w.Header().Set("Retry-After", "0")
w.WriteHeader(http.StatusBadRequest)
}))
@@ -1043,42 +510,6 @@ func TestFetchCertSize(t *testing.T) {
}
}
func TestRevokeCert(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "HEAD" {
w.Header().Set("Replay-Nonce", "nonce")
return
}
var req struct {
Resource string
Certificate string
Reason int
}
decodeJWSRequest(t, &req, r.Body)
if req.Resource != "revoke-cert" {
t.Errorf("req.Resource = %q; want revoke-cert", req.Resource)
}
if req.Reason != 1 {
t.Errorf("req.Reason = %d; want 1", req.Reason)
}
// echo -n cert | base64 | tr -d '=' | tr '/+' '_-'
cert := "Y2VydA"
if req.Certificate != cert {
t.Errorf("req.Certificate = %q; want %q", req.Certificate, cert)
}
}))
defer ts.Close()
client := &Client{
Key: testKeyEC,
dir: &Directory{RevokeURL: ts.URL},
}
ctx := context.Background()
if err := client.RevokeCert(ctx, nil, []byte("cert"), CRLReasonKeyCompromise); err != nil {
t.Fatal(err)
}
}
func TestNonce_add(t *testing.T) {
var c Client
c.addNonce(http.Header{"Replay-Nonce": {"nonce"}})
@@ -1199,65 +630,6 @@ func TestNonce_popWhenEmpty(t *testing.T) {
}
}
func TestNonce_postJWS(t *testing.T) {
var count int
seen := make(map[string]bool)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count++
w.Header().Set("Replay-Nonce", fmt.Sprintf("nonce%d", count))
if r.Method == "HEAD" {
// We expect the client do a HEAD request
// but only to fetch the first nonce.
return
}
// Make client.Authorize happy; we're not testing its result.
defer func() {
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{"status":"valid"}`))
}()
head, err := decodeJWSHead(r.Body)
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
return
}
if head.Nonce == "" {
t.Error("head.Nonce is empty")
return
}
if seen[head.Nonce] {
t.Errorf("nonce is already used: %q", head.Nonce)
}
seen[head.Nonce] = true
}))
defer ts.Close()
client := Client{
Key: testKey,
DirectoryURL: ts.URL, // nonces are fetched from here first
dir: &Directory{AuthzURL: ts.URL},
}
if _, err := client.Authorize(context.Background(), "example.com"); err != nil {
t.Errorf("client.Authorize 1: %v", err)
}
// The second call should not generate another extra HEAD request.
if _, err := client.Authorize(context.Background(), "example.com"); err != nil {
t.Errorf("client.Authorize 2: %v", err)
}
if count != 3 {
t.Errorf("total requests count: %d; want 3", count)
}
if n := len(client.nonces); n != 1 {
t.Errorf("len(client.nonces) = %d; want 1", n)
}
for k := range seen {
if _, exist := client.nonces[k]; exist {
t.Errorf("used nonce %q in client.nonces", k)
}
}
}
func TestLinkHeader(t *testing.T) {
h := http.Header{"Link": {
`<https://example.com/acme/new-authz>;rel="next"`,
+42 -93
View File
@@ -47,6 +47,8 @@ var createCertRetryAfter = time.Minute
// pseudoRand is safe for concurrent use.
var pseudoRand *lockedMathRand
var errPreRFC = errors.New("autocert: ACME server doesn't support RFC 8555")
func init() {
src := mathrand.NewSource(time.Now().UnixNano())
pseudoRand = &lockedMathRand{rnd: mathrand.New(src)}
@@ -168,6 +170,11 @@ type Manager struct {
// in the template's ExtraExtensions field as is.
ExtraExtensions []pkix.Extension
// ExternalAccountBinding optionally represents an arbitrary binding to an
// account of the CA to which the ACME server is tied.
// See RFC 8555, Section 7.3.4 for more details.
ExternalAccountBinding *acme.ExternalAccountBinding
clientMu sync.Mutex
client *acme.Client // initialized by acmeClient method
@@ -456,7 +463,7 @@ func (m *Manager) cert(ctx context.Context, ck certKey) (*tls.Certificate, error
leaf: cert.Leaf,
}
m.state[ck] = s
go m.renew(ck, s.key, s.leaf.NotAfter)
m.startRenew(ck, s.key, s.leaf.NotAfter)
return cert, nil
}
@@ -582,8 +589,9 @@ func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate,
if err != nil {
// Remove the failed state after some time,
// making the manager call createCert again on the following TLS hello.
didRemove := testDidRemoveState // The lifetime of this timer is untracked, so copy mutable local state to avoid races.
time.AfterFunc(createCertRetryAfter, func() {
defer testDidRemoveState(ck)
defer didRemove(ck)
m.stateMu.Lock()
defer m.stateMu.Unlock()
// Verify the state hasn't changed and it's still invalid
@@ -601,7 +609,7 @@ func (m *Manager) createCert(ctx context.Context, ck certKey) (*tls.Certificate,
}
state.cert = der
state.leaf = leaf
go m.renew(ck, state.key, state.leaf.NotAfter)
m.startRenew(ck, state.key, state.leaf.NotAfter)
return state.tlscert()
}
@@ -658,31 +666,19 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck cert
if err != nil {
return nil, nil, err
}
var chain [][]byte
switch {
// Pre-RFC legacy CA.
case dir.OrderURL == "":
if err := m.verify(ctx, client, ck.domain); err != nil {
return nil, nil, err
}
der, _, err := client.CreateCert(ctx, csr, 0, true)
if err != nil {
return nil, nil, err
}
chain = der
// RFC 8555 compliant CA.
default:
o, err := m.verifyRFC(ctx, client, ck.domain)
if err != nil {
return nil, nil, err
}
der, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
if err != nil {
return nil, nil, err
}
chain = der
if dir.OrderURL == "" {
return nil, nil, errPreRFC
}
o, err := m.verifyRFC(ctx, client, ck.domain)
if err != nil {
return nil, nil, err
}
chain, _, err := client.CreateOrderCert(ctx, o.FinalizeURL, csr, true)
if err != nil {
return nil, nil, err
}
leaf, err = validCert(ck, chain, key, m.now())
if err != nil {
return nil, nil, err
@@ -690,69 +686,6 @@ func (m *Manager) authorizedCert(ctx context.Context, key crypto.Signer, ck cert
return chain, leaf, nil
}
// verify runs the identifier (domain) pre-authorization flow for legacy CAs
// using each applicable ACME challenge type.
func (m *Manager) verify(ctx context.Context, client *acme.Client, domain string) error {
// Remove all hanging authorizations to reduce rate limit quotas
// after we're done.
var authzURLs []string
defer func() {
go m.deactivatePendingAuthz(authzURLs)
}()
// errs accumulates challenge failure errors, printed if all fail
errs := make(map[*acme.Challenge]error)
challengeTypes := m.supportedChallengeTypes()
var nextTyp int // challengeType index of the next challenge type to try
for {
// Start domain authorization and get the challenge.
authz, err := client.Authorize(ctx, domain)
if err != nil {
return err
}
authzURLs = append(authzURLs, authz.URI)
// No point in accepting challenges if the authorization status
// is in a final state.
switch authz.Status {
case acme.StatusValid:
return nil // already authorized
case acme.StatusInvalid:
return fmt.Errorf("acme/autocert: invalid authorization %q", authz.URI)
}
// Pick the next preferred challenge.
var chal *acme.Challenge
for chal == nil && nextTyp < len(challengeTypes) {
chal = pickChallenge(challengeTypes[nextTyp], authz.Challenges)
nextTyp++
}
if chal == nil {
errorMsg := fmt.Sprintf("acme/autocert: unable to authorize %q", domain)
for chal, err := range errs {
errorMsg += fmt.Sprintf("; challenge %q failed with error: %v", chal.Type, err)
}
return errors.New(errorMsg)
}
cleanup, err := m.fulfill(ctx, client, chal, domain)
if err != nil {
errs[chal] = err
continue
}
defer cleanup()
if _, err := client.Accept(ctx, chal); err != nil {
errs[chal] = err
continue
}
// A challenge is fulfilled and accepted: wait for the CA to validate.
if _, err := client.WaitAuthorization(ctx, authz.URI); err != nil {
errs[chal] = err
continue
}
return nil
}
}
// verifyRFC runs the identifier (domain) order-based authorization flow for RFC compliant CAs
// using each applicable ACME challenge type.
func (m *Manager) verifyRFC(ctx context.Context, client *acme.Client, domain string) (*acme.Order, error) {
@@ -966,7 +899,7 @@ func httpTokenCacheKey(tokenPath string) string {
return path.Base(tokenPath) + "+http-01"
}
// renew starts a cert renewal timer loop, one per domain.
// startRenew starts a cert renewal timer loop, one per domain.
//
// The loop is scheduled in two cases:
// - a cert was fetched from cache for the first time (wasn't in m.state)
@@ -974,7 +907,7 @@ func httpTokenCacheKey(tokenPath string) string {
//
// The key argument is a certificate private key.
// The exp argument is the cert expiration time (NotAfter).
func (m *Manager) renew(ck certKey, key crypto.Signer, exp time.Time) {
func (m *Manager) startRenew(ck certKey, key crypto.Signer, exp time.Time) {
m.renewalMu.Lock()
defer m.renewalMu.Unlock()
if m.renewal[ck] != nil {
@@ -1068,7 +1001,7 @@ func (m *Manager) acmeClient(ctx context.Context) (*acme.Client, error) {
if m.Email != "" {
contact = []string{"mailto:" + m.Email}
}
a := &acme.Account{Contact: contact}
a := &acme.Account{Contact: contact, ExternalAccountBinding: m.ExternalAccountBinding}
_, err := client.Register(ctx, a, m.Prompt)
if err == nil || isAccountAlreadyExist(err) {
m.client = client
@@ -1200,6 +1133,10 @@ func validCert(ck certKey, der [][]byte, key crypto.Signer, now time.Time) (leaf
if err := leaf.VerifyHostname(ck.domain); err != nil {
return nil, err
}
// renew certificates revoked by Let's Encrypt in January 2022
if isRevokedLetsEncrypt(leaf) {
return nil, errors.New("acme/autocert: certificate was probably revoked by Let's Encrypt")
}
// ensure the leaf corresponds to the private key and matches the certKey type
switch pub := leaf.PublicKey.(type) {
case *rsa.PublicKey:
@@ -1230,6 +1167,18 @@ func validCert(ck certKey, der [][]byte, key crypto.Signer, now time.Time) (leaf
return leaf, nil
}
// https://community.letsencrypt.org/t/2022-01-25-issue-with-tls-alpn-01-validation-method/170450
var letsEncryptFixDeployTime = time.Date(2022, time.January, 26, 00, 48, 0, 0, time.UTC)
// isRevokedLetsEncrypt returns whether the certificate is likely to be part of
// a batch of certificates revoked by Let's Encrypt in January 2022. This check
// can be safely removed from May 2022.
func isRevokedLetsEncrypt(cert *x509.Certificate) bool {
O := cert.Issuer.Organization
return len(O) == 1 && O[0] == "Let's Encrypt" &&
cert.NotBefore.Before(letsEncryptFixDeployTime)
}
type lockedMathRand struct {
sync.Mutex
rnd *mathrand.Rand
File diff suppressed because it is too large Load Diff
+5 -6
View File
@@ -7,7 +7,6 @@ package autocert
import (
"context"
"errors"
"io/ioutil"
"os"
"path/filepath"
)
@@ -41,14 +40,14 @@ type DirCache string
// Get reads a certificate data from the specified file name.
func (d DirCache) Get(ctx context.Context, name string) ([]byte, error) {
name = filepath.Join(string(d), name)
name = filepath.Join(string(d), filepath.Clean("/"+name))
var (
data []byte
err error
done = make(chan struct{})
)
go func() {
data, err = ioutil.ReadFile(name)
data, err = os.ReadFile(name)
close(done)
}()
select {
@@ -82,7 +81,7 @@ func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
case <-ctx.Done():
// Don't overwrite the file if the context was canceled.
default:
newName := filepath.Join(string(d), name)
newName := filepath.Join(string(d), filepath.Clean("/"+name))
err = os.Rename(tmp, newName)
}
}()
@@ -96,7 +95,7 @@ func (d DirCache) Put(ctx context.Context, name string, data []byte) error {
// Delete removes the specified file name.
func (d DirCache) Delete(ctx context.Context, name string) error {
name = filepath.Join(string(d), name)
name = filepath.Join(string(d), filepath.Clean("/"+name))
var (
err error
done = make(chan struct{})
@@ -119,7 +118,7 @@ func (d DirCache) Delete(ctx context.Context, name string) error {
// writeTempFile writes b to a temporary file, closes the file and returns its path.
func (d DirCache) writeTempFile(prefix string, b []byte) (name string, reterr error) {
// TempFile uses 0600 permissions
f, err := ioutil.TempFile(string(d), prefix)
f, err := os.CreateTemp(string(d), prefix)
if err != nil {
return "", err
}
+1 -2
View File
@@ -6,7 +6,6 @@ package autocert
import (
"context"
"io/ioutil"
"os"
"path/filepath"
"reflect"
@@ -17,7 +16,7 @@ import (
var _ Cache = DirCache("/")
func TestDirCache(t *testing.T) {
dir, err := ioutil.TempDir("", "autocert")
dir, err := os.MkdirTemp("", "autocert")
if err != nil {
t.Fatal(err)
}
+1
View File
@@ -24,6 +24,7 @@ func ExampleManager() {
m := &autocert.Manager{
Cache: autocert.DirCache("secret-dir"),
Prompt: autocert.AcceptTOS,
Email: "example@example.org",
HostPolicy: autocert.HostWhitelist("example.org", "www.example.org"),
}
s := &http.Server{
+424 -180
View File
@@ -8,27 +8,30 @@
package acmetest
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"log"
"math/big"
"net"
"net/http"
"net/http/httptest"
"path"
"sort"
"strconv"
"strings"
"sync"
"testing"
"time"
"golang.org/x/crypto/acme"
@@ -36,56 +39,65 @@ import (
// CAServer is a simple test server which implements ACME spec bits needed for testing.
type CAServer struct {
URL string // server URL after it has been started
Roots *x509.CertPool // CA root certificates; initialized in NewCAServer
rootKey crypto.Signer
rootCert []byte // DER encoding
rootTemplate *x509.Certificate
server *httptest.Server
challengeTypes []string // supported challenge types
domainsWhitelist []string // only these domains are valid for issuing, unless empty
t *testing.T
server *httptest.Server
issuer pkix.Name
challengeTypes []string
url string
roots *x509.CertPool
eabRequired bool
mu sync.Mutex
certCount int // number of issued certs
domainAddr map[string]string // domain name to addr:port resolution
authorizations map[string]*authorization // keyed by domain name
orders []*order // index is used as order ID
errors []error // encountered client errors
certCount int // number of issued certs
acctRegistered bool // set once an account has been registered
domainAddr map[string]string // domain name to addr:port resolution
domainGetCert map[string]getCertificateFunc // domain name to GetCertificate function
domainHandler map[string]http.Handler // domain name to Handle function
validAuthz map[string]*authorization // valid authz, keyed by domain name
authorizations []*authorization // all authz, index is used as ID
orders []*order // index is used as order ID
errors []error // encountered client errors
}
// NewCAServer creates a new ACME test server and starts serving requests.
// The returned CAServer issues certs signed with the CA roots
// available in the Roots field.
//
// The challengeTypes argument defines the supported ACME challenge types
// sent to a client in a response for a domain authorization.
// If domainsWhitelist is non-empty, the certs will be issued only for the specified
// list of domains. Otherwise, any domain name is allowed.
func NewCAServer(challengeTypes []string, domainsWhitelist []string) *CAServer {
var whitelist []string
for _, name := range domainsWhitelist {
whitelist = append(whitelist, name)
}
sort.Strings(whitelist)
ca := &CAServer{
challengeTypes: challengeTypes,
domainsWhitelist: whitelist,
domainAddr: make(map[string]string),
authorizations: make(map[string]*authorization),
type getCertificateFunc func(hello *tls.ClientHelloInfo) (*tls.Certificate, error)
// NewCAServer creates a new ACME test server. The returned CAServer issues
// certs signed with the CA roots available in the Roots field.
func NewCAServer(t *testing.T) *CAServer {
ca := &CAServer{t: t,
challengeTypes: []string{"fake-01", "tls-alpn-01", "http-01"},
domainAddr: make(map[string]string),
domainGetCert: make(map[string]getCertificateFunc),
domainHandler: make(map[string]http.Handler),
validAuthz: make(map[string]*authorization),
}
ca.server = httptest.NewUnstartedServer(http.HandlerFunc(ca.handle))
r, err := rand.Int(rand.Reader, big.NewInt(1000000))
if err != nil {
panic(fmt.Sprintf("rand.Int: %v", err))
}
ca.issuer = pkix.Name{
Organization: []string{"Test Acme Co"},
CommonName: "Root CA " + r.String(),
}
return ca
}
func (ca *CAServer) generateRoot() {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(fmt.Sprintf("ecdsa.GenerateKey: %v", err))
}
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Organization: []string{"Test Acme Co"},
CommonName: "Root CA",
},
SerialNumber: big.NewInt(1),
Subject: ca.issuer,
NotBefore: time.Now(),
NotAfter: time.Now().Add(365 * 24 * time.Hour),
KeyUsage: x509.KeyUsageCertSign,
@@ -100,40 +112,96 @@ func NewCAServer(challengeTypes []string, domainsWhitelist []string) *CAServer {
if err != nil {
panic(fmt.Sprintf("x509.ParseCertificate: %v", err))
}
ca.Roots = x509.NewCertPool()
ca.Roots.AddCert(cert)
ca.roots = x509.NewCertPool()
ca.roots.AddCert(cert)
ca.rootKey = key
ca.rootCert = der
ca.rootTemplate = tmpl
}
ca.server = httptest.NewServer(http.HandlerFunc(ca.handle))
ca.URL = ca.server.URL
// IssuerName sets the name of the issuing CA.
func (ca *CAServer) IssuerName(name pkix.Name) *CAServer {
if ca.url != "" {
panic("IssuerName must be called before Start")
}
ca.issuer = name
return ca
}
// Close shuts down the server and blocks until all outstanding
// requests on this server have completed.
func (ca *CAServer) Close() {
ca.server.Close()
// ChallengeTypes sets the supported challenge types.
func (ca *CAServer) ChallengeTypes(types ...string) *CAServer {
if ca.url != "" {
panic("ChallengeTypes must be called before Start")
}
ca.challengeTypes = types
return ca
}
// URL returns the server address, after Start has been called.
func (ca *CAServer) URL() string {
if ca.url == "" {
panic("URL called before Start")
}
return ca.url
}
// Roots returns a pool cointaining the CA root.
func (ca *CAServer) Roots() *x509.CertPool {
if ca.url == "" {
panic("Roots called before Start")
}
return ca.roots
}
// ExternalAccountRequired makes an EAB JWS required for account registration.
func (ca *CAServer) ExternalAccountRequired() *CAServer {
if ca.url != "" {
panic("ExternalAccountRequired must be called before Start")
}
ca.eabRequired = true
return ca
}
// Start starts serving requests. The server address becomes available in the
// URL field.
func (ca *CAServer) Start() *CAServer {
if ca.url == "" {
ca.generateRoot()
ca.server.Start()
ca.t.Cleanup(ca.server.Close)
ca.url = ca.server.URL
}
return ca
}
func (ca *CAServer) serverURL(format string, arg ...interface{}) string {
return ca.server.URL + fmt.Sprintf(format, arg...)
}
func (ca *CAServer) addr(domain string) (string, error) {
func (ca *CAServer) addr(domain string) (string, bool) {
ca.mu.Lock()
defer ca.mu.Unlock()
addr, ok := ca.domainAddr[domain]
if !ok {
return "", fmt.Errorf("CAServer: no addr resolution for %q", domain)
}
return addr, nil
return addr, ok
}
func (ca *CAServer) getCert(domain string) (getCertificateFunc, bool) {
ca.mu.Lock()
defer ca.mu.Unlock()
f, ok := ca.domainGetCert[domain]
return f, ok
}
func (ca *CAServer) getHandler(domain string) (http.Handler, bool) {
ca.mu.Lock()
defer ca.mu.Unlock()
h, ok := ca.domainHandler[domain]
return h, ok
}
func (ca *CAServer) httpErrorf(w http.ResponseWriter, code int, format string, a ...interface{}) {
s := fmt.Sprintf(format, a...)
log.Println(s)
ca.t.Errorf(format, a...)
http.Error(w, s, code)
}
@@ -145,11 +213,33 @@ func (ca *CAServer) Resolve(domain, addr string) {
ca.domainAddr[domain] = addr
}
// ResolveGetCertificate redirects TLS connections for domain to f when
// validating challenges for the domain authorization.
func (ca *CAServer) ResolveGetCertificate(domain string, f getCertificateFunc) {
ca.mu.Lock()
defer ca.mu.Unlock()
ca.domainGetCert[domain] = f
}
// ResolveHandler redirects HTTP requests for domain to f when
// validating challenges for the domain authorization.
func (ca *CAServer) ResolveHandler(domain string, h http.Handler) {
ca.mu.Lock()
defer ca.mu.Unlock()
ca.domainHandler[domain] = h
}
type discovery struct {
NewNonce string `json:"newNonce"`
NewReg string `json:"newAccount"`
NewOrder string `json:"newOrder"`
NewAuthz string `json:"newAuthz"`
NewNonce string `json:"newNonce"`
NewAccount string `json:"newAccount"`
NewOrder string `json:"newOrder"`
NewAuthz string `json:"newAuthz"`
Meta discoveryMeta `json:"meta,omitempty"`
}
type discoveryMeta struct {
ExternalAccountRequired bool `json:"externalAccountRequired,omitempty"`
}
type challenge struct {
@@ -163,6 +253,7 @@ type authorization struct {
Challenges []challenge `json:"challenges"`
domain string
id int
}
type order struct {
@@ -175,7 +266,7 @@ type order struct {
}
func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %s", r.Method, r.URL)
ca.t.Logf("%s %s", r.Method, r.URL)
w.Header().Set("Replay-Nonce", "nonce")
// TODO: Verify nonce header for all POST requests.
@@ -186,10 +277,12 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
// Discovery request.
case r.URL.Path == "/":
resp := &discovery{
NewNonce: ca.serverURL("/new-nonce"),
NewReg: ca.serverURL("/new-reg"),
NewOrder: ca.serverURL("/new-order"),
NewAuthz: ca.serverURL("/new-authz"),
NewNonce: ca.serverURL("/new-nonce"),
NewAccount: ca.serverURL("/new-account"),
NewOrder: ca.serverURL("/new-order"),
Meta: discoveryMeta{
ExternalAccountRequired: ca.eabRequired,
},
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
panic(fmt.Sprintf("discovery response: %v", err))
@@ -201,7 +294,29 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
return
// Client key registration request.
case r.URL.Path == "/new-reg":
case r.URL.Path == "/new-account":
ca.mu.Lock()
defer ca.mu.Unlock()
if ca.acctRegistered {
ca.httpErrorf(w, http.StatusServiceUnavailable, "multiple accounts are not implemented")
return
}
ca.acctRegistered = true
var req struct {
ExternalAccountBinding json.RawMessage
}
if err := decodePayload(&req, r.Body); err != nil {
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
return
}
if ca.eabRequired && len(req.ExternalAccountBinding) == 0 {
ca.httpErrorf(w, http.StatusBadRequest, "registration failed: no JWS for EAB")
return
}
// TODO: Check the user account key against a ca.accountKeys?
w.Header().Set("Location", ca.serverURL("/accounts/1"))
w.WriteHeader(http.StatusCreated)
@@ -221,7 +336,7 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
o := &order{Status: acme.StatusPending}
for _, id := range req.Identifiers {
z := ca.authz(id.Value)
o.AuthzURLs = append(o.AuthzURLs, ca.serverURL("/authz/%s", z.domain))
o.AuthzURLs = append(o.AuthzURLs, ca.serverURL("/authz/%d", z.id))
}
orderID := len(ca.orders)
ca.orders = append(ca.orders, o)
@@ -244,49 +359,50 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
panic(err)
}
// Identifier authorization request.
case r.URL.Path == "/new-authz":
var req struct {
Identifier struct{ Value string }
}
if err := decodePayload(&req, r.Body); err != nil {
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
return
}
// Accept challenge requests.
case strings.HasPrefix(r.URL.Path, "/challenge/"):
parts := strings.Split(r.URL.Path, "/")
typ, id := parts[len(parts)-2], parts[len(parts)-1]
ca.mu.Lock()
defer ca.mu.Unlock()
z := ca.authz(req.Identifier.Value)
w.Header().Set("Location", ca.serverURL("/authz/%s", z.domain))
w.WriteHeader(http.StatusCreated)
if err := json.NewEncoder(w).Encode(z); err != nil {
panic(fmt.Sprintf("new authz response: %v", err))
supported := false
for _, suppTyp := range ca.challengeTypes {
if suppTyp == typ {
supported = true
}
}
// Accept tls-alpn-01 challenge type requests.
case strings.HasPrefix(r.URL.Path, "/challenge/tls-alpn-01/"):
domain := strings.TrimPrefix(r.URL.Path, "/challenge/tls-alpn-01/")
ca.mu.Lock()
_, exist := ca.authorizations[domain]
a, err := ca.storedAuthz(id)
ca.mu.Unlock()
if !exist {
ca.httpErrorf(w, http.StatusBadRequest, "challenge accept: no authz for %q", domain)
if !supported {
ca.httpErrorf(w, http.StatusBadRequest, "unsupported challenge: %v", typ)
return
}
go ca.validateChallenge("tls-alpn-01", domain)
if err != nil {
ca.httpErrorf(w, http.StatusBadRequest, "challenge accept: %v", err)
return
}
ca.validateChallenge(a, typ)
w.Write([]byte("{}"))
// Get authorization status requests.
case strings.HasPrefix(r.URL.Path, "/authz/"):
domain := strings.TrimPrefix(r.URL.Path, "/authz/")
var req struct{ Status string }
decodePayload(&req, r.Body)
deactivate := req.Status == "deactivated"
ca.mu.Lock()
defer ca.mu.Unlock()
authz, ok := ca.authorizations[domain]
if !ok {
ca.httpErrorf(w, http.StatusNotFound, "no authz for %q", domain)
authz, err := ca.storedAuthz(strings.TrimPrefix(r.URL.Path, "/authz/"))
if err != nil {
ca.httpErrorf(w, http.StatusNotFound, "%v", err)
return
}
if deactivate {
// Note we don't invalidate authorized orders as we should.
authz.Status = "deactivated"
ca.t.Logf("authz %d is now %s", authz.id, authz.Status)
ca.updatePendingOrders()
}
if err := json.NewEncoder(w).Encode(authz); err != nil {
panic(fmt.Sprintf("get authz for %q response: %v", domain, err))
panic(fmt.Sprintf("encoding authz %d: %v", authz.id, err))
}
// Certificate issuance request.
@@ -314,15 +430,6 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
ca.httpErrorf(w, http.StatusBadRequest, err.Error())
return
}
names := unique(append(csr.DNSNames, csr.Subject.CommonName))
if err := ca.matchWhitelist(names); err != nil {
ca.httpErrorf(w, http.StatusUnauthorized, err.Error())
return
}
if err := ca.authorized(names); err != nil {
ca.httpErrorf(w, http.StatusUnauthorized, err.Error())
return
}
// Issue the certificate.
der, err := ca.leafCert(csr)
if err != nil {
@@ -355,25 +462,6 @@ func (ca *CAServer) handle(w http.ResponseWriter, r *http.Request) {
}
}
// matchWhitelist reports whether all dnsNames are whitelisted.
// The whitelist is provided in NewCAServer.
func (ca *CAServer) matchWhitelist(dnsNames []string) error {
if len(ca.domainsWhitelist) == 0 {
return nil
}
var nomatch []string
for _, name := range dnsNames {
i := sort.SearchStrings(ca.domainsWhitelist, name)
if i == len(ca.domainsWhitelist) || ca.domainsWhitelist[i] != name {
nomatch = append(nomatch, name)
}
}
if len(nomatch) > 0 {
return fmt.Errorf("matchWhitelist: some domains don't match: %q", nomatch)
}
return nil
}
// storedOrder retrieves a previously created order at index i.
// It requires ca.mu to be locked.
func (ca *CAServer) storedOrder(i string) (*order, error) {
@@ -387,46 +475,50 @@ func (ca *CAServer) storedOrder(i string) (*order, error) {
if idx > len(ca.orders)-1 {
return nil, fmt.Errorf("storedOrder: no such order %d", idx)
}
ca.updatePendingOrders()
return ca.orders[idx], nil
}
// authz returns an existing authorization for the identifier or creates a new one.
// storedAuthz retrieves a previously created authz at index i.
// It requires ca.mu to be locked.
func (ca *CAServer) storedAuthz(i string) (*authorization, error) {
idx, err := strconv.Atoi(i)
if err != nil {
return nil, fmt.Errorf("storedAuthz: %v", err)
}
if idx < 0 {
return nil, fmt.Errorf("storedAuthz: invalid authz index %d", idx)
}
if idx > len(ca.authorizations)-1 {
return nil, fmt.Errorf("storedAuthz: no such authz %d", idx)
}
return ca.authorizations[idx], nil
}
// authz returns an existing valid authorization for the identifier or creates a
// new one. It requires ca.mu to be locked.
func (ca *CAServer) authz(identifier string) *authorization {
authz, ok := ca.authorizations[identifier]
authz, ok := ca.validAuthz[identifier]
if !ok {
authzId := len(ca.authorizations)
authz = &authorization{
id: authzId,
domain: identifier,
Status: acme.StatusPending,
}
for _, typ := range ca.challengeTypes {
authz.Challenges = append(authz.Challenges, challenge{
Type: typ,
URI: ca.serverURL("/challenge/%s/%s", typ, authz.domain),
Token: challengeToken(authz.domain, typ),
URI: ca.serverURL("/challenge/%s/%d", typ, authzId),
Token: challengeToken(authz.domain, typ, authzId),
})
}
ca.authorizations[authz.domain] = authz
ca.authorizations = append(ca.authorizations, authz)
}
return authz
}
// authorized reports whether all authorizations for dnsNames have been satisfied.
// It requires ca.mu to be locked.
func (ca *CAServer) authorized(dnsNames []string) error {
var noauthz []string
for _, name := range dnsNames {
authz, ok := ca.authorizations[name]
if !ok || authz.Status != acme.StatusValid {
noauthz = append(noauthz, name)
}
}
if len(noauthz) > 0 {
return fmt.Errorf("CAServer: no authz for %q", noauthz)
}
return nil
}
// leafCert issues a new certificate.
// It requires ca.mu to be locked.
func (ca *CAServer) leafCert(csr *x509.CertificateRequest) (der []byte, err error) {
@@ -447,79 +539,231 @@ func (ca *CAServer) leafCert(csr *x509.CertificateRequest) (der []byte, err erro
return x509.CreateCertificate(rand.Reader, leaf, ca.rootTemplate, csr.PublicKey, ca.rootKey)
}
// TODO: Only tls-alpn-01 is currently supported: implement http-01 and dns-01.
func (ca *CAServer) validateChallenge(typ, identifier string) {
// LeafCert issues a leaf certificate.
func (ca *CAServer) LeafCert(name, keyType string, notBefore, notAfter time.Time) *tls.Certificate {
if ca.url == "" {
panic("LeafCert called before Start")
}
ca.mu.Lock()
defer ca.mu.Unlock()
var pk crypto.Signer
switch keyType {
case "RSA":
var err error
pk, err = rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
ca.t.Fatal(err)
}
case "ECDSA":
var err error
pk, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
ca.t.Fatal(err)
}
default:
panic("LeafCert: unknown key type")
}
ca.certCount++ // next leaf cert serial number
leaf := &x509.Certificate{
SerialNumber: big.NewInt(int64(ca.certCount)),
Subject: pkix.Name{Organization: []string{"Test Acme Co"}},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
DNSNames: []string{name},
BasicConstraintsValid: true,
}
der, err := x509.CreateCertificate(rand.Reader, leaf, ca.rootTemplate, pk.Public(), ca.rootKey)
if err != nil {
ca.t.Fatal(err)
}
return &tls.Certificate{
Certificate: [][]byte{der},
PrivateKey: pk,
}
}
func (ca *CAServer) validateChallenge(authz *authorization, typ string) {
var err error
switch typ {
case "tls-alpn-01":
err = ca.verifyALPNChallenge(identifier)
err = ca.verifyALPNChallenge(authz)
case "http-01":
err = ca.verifyHTTPChallenge(authz)
default:
panic(fmt.Sprintf("validation of %q is not implemented", typ))
}
ca.mu.Lock()
defer ca.mu.Unlock()
authz := ca.authorizations[identifier]
if err != nil {
authz.Status = "invalid"
} else {
authz.Status = "valid"
ca.validAuthz[authz.domain] = authz
}
log.Printf("validated %q for %q; authz status is now: %s", typ, identifier, authz.Status)
ca.t.Logf("validated %q for %q, err: %v", typ, authz.domain, err)
ca.t.Logf("authz %d is now %s", authz.id, authz.Status)
ca.updatePendingOrders()
}
func (ca *CAServer) updatePendingOrders() {
// Update all pending orders.
// An order becomes "ready" if all authorizations are "valid".
// An order becomes "invalid" if any authorization is "invalid".
// Status changes: https://tools.ietf.org/html/rfc8555#section-7.1.6
OrdersLoop:
for i, o := range ca.orders {
if o.Status != acme.StatusPending {
continue
}
var countValid int
for _, zurl := range o.AuthzURLs {
z, ok := ca.authorizations[path.Base(zurl)]
if !ok {
log.Printf("no authz %q for order %d", zurl, i)
continue OrdersLoop
}
if z.Status == acme.StatusInvalid {
o.Status = acme.StatusInvalid
log.Printf("order %d is now invalid", i)
continue OrdersLoop
}
if z.Status == acme.StatusValid {
countValid++
}
countValid, countInvalid := ca.validateAuthzURLs(o.AuthzURLs, i)
if countInvalid > 0 {
o.Status = acme.StatusInvalid
ca.t.Logf("order %d is now invalid", i)
continue
}
if countValid == len(o.AuthzURLs) {
o.Status = acme.StatusReady
o.FinalizeURL = ca.serverURL("/new-cert/%d", i)
log.Printf("order %d is now ready", i)
ca.t.Logf("order %d is now ready", i)
}
}
}
func (ca *CAServer) verifyALPNChallenge(domain string) error {
func (ca *CAServer) validateAuthzURLs(urls []string, orderNum int) (countValid, countInvalid int) {
for _, zurl := range urls {
z, err := ca.storedAuthz(path.Base(zurl))
if err != nil {
ca.t.Logf("no authz %q for order %d", zurl, orderNum)
continue
}
if z.Status == acme.StatusInvalid {
countInvalid++
}
if z.Status == acme.StatusValid {
countValid++
}
}
return countValid, countInvalid
}
func (ca *CAServer) verifyALPNChallenge(a *authorization) error {
const acmeALPNProto = "acme-tls/1"
addr, err := ca.addr(domain)
if err != nil {
return err
addr, haveAddr := ca.addr(a.domain)
getCert, haveGetCert := ca.getCert(a.domain)
if !haveAddr && !haveGetCert {
return fmt.Errorf("no resolution information for %q", a.domain)
}
conn, err := tls.Dial("tcp", addr, &tls.Config{
ServerName: domain,
InsecureSkipVerify: true,
NextProtos: []string{acmeALPNProto},
})
if err != nil {
return err
if haveAddr && haveGetCert {
return fmt.Errorf("overlapping resolution information for %q", a.domain)
}
if v := conn.ConnectionState().NegotiatedProtocol; v != acmeALPNProto {
return fmt.Errorf("CAServer: verifyALPNChallenge: negotiated proto is %q; want %q", v, acmeALPNProto)
var crt *x509.Certificate
switch {
case haveAddr:
conn, err := tls.Dial("tcp", addr, &tls.Config{
ServerName: a.domain,
InsecureSkipVerify: true,
NextProtos: []string{acmeALPNProto},
MinVersion: tls.VersionTLS12,
})
if err != nil {
return err
}
if v := conn.ConnectionState().NegotiatedProtocol; v != acmeALPNProto {
return fmt.Errorf("CAServer: verifyALPNChallenge: negotiated proto is %q; want %q", v, acmeALPNProto)
}
if n := len(conn.ConnectionState().PeerCertificates); n != 1 {
return fmt.Errorf("len(PeerCertificates) = %d; want 1", n)
}
crt = conn.ConnectionState().PeerCertificates[0]
case haveGetCert:
hello := &tls.ClientHelloInfo{
ServerName: a.domain,
// TODO: support selecting ECDSA.
CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305},
SupportedProtos: []string{acme.ALPNProto},
SupportedVersions: []uint16{tls.VersionTLS12},
}
c, err := getCert(hello)
if err != nil {
return err
}
crt, err = x509.ParseCertificate(c.Certificate[0])
if err != nil {
return err
}
}
if n := len(conn.ConnectionState().PeerCertificates); n != 1 {
return fmt.Errorf("len(PeerCertificates) = %d; want 1", n)
if err := crt.VerifyHostname(a.domain); err != nil {
return fmt.Errorf("verifyALPNChallenge: VerifyHostname: %v", err)
}
// See RFC 8737, Section 6.1.
oid := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31}
for _, x := range crt.Extensions {
if x.Id.Equal(oid) {
// TODO: check the token.
return nil
}
}
return fmt.Errorf("verifyTokenCert: no id-pe-acmeIdentifier extension found")
}
func (ca *CAServer) verifyHTTPChallenge(a *authorization) error {
addr, haveAddr := ca.addr(a.domain)
handler, haveHandler := ca.getHandler(a.domain)
if !haveAddr && !haveHandler {
return fmt.Errorf("no resolution information for %q", a.domain)
}
if haveAddr && haveHandler {
return fmt.Errorf("overlapping resolution information for %q", a.domain)
}
token := challengeToken(a.domain, "http-01", a.id)
path := "/.well-known/acme-challenge/" + token
var body string
switch {
case haveAddr:
t := &http.Transport{
DialContext: func(ctx context.Context, network, _ string) (net.Conn, error) {
return (&net.Dialer{}).DialContext(ctx, network, addr)
},
}
req, err := http.NewRequest("GET", "http://"+a.domain+path, nil)
if err != nil {
return err
}
res, err := t.RoundTrip(req)
if err != nil {
return err
}
if res.StatusCode != http.StatusOK {
return fmt.Errorf("http token: w.Code = %d; want %d", res.StatusCode, http.StatusOK)
}
b, err := io.ReadAll(res.Body)
if err != nil {
return err
}
body = string(b)
case haveHandler:
r := httptest.NewRequest("GET", path, nil)
r.Host = a.domain
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
if w.Code != http.StatusOK {
return fmt.Errorf("http token: w.Code = %d; want %d", w.Code, http.StatusOK)
}
body = w.Body.String()
}
if !strings.HasPrefix(body, token) {
return fmt.Errorf("http token value = %q; want 'token-http-01.' prefix", body)
}
// TODO: verify conn.ConnectionState().PeerCertificates[0]
return nil
}
@@ -535,8 +779,8 @@ func decodePayload(v interface{}, r io.Reader) error {
return json.Unmarshal(payload, v)
}
func challengeToken(domain, challType string) string {
return fmt.Sprintf("token-%s-%s", domain, challType)
func challengeToken(domain, challType string, authzID int) string {
return fmt.Sprintf("token-%s-%s-%d", domain, challType, authzID)
}
func unique(a []string) []string {
+1 -1
View File
@@ -20,7 +20,7 @@ import (
//
// It enables one-line HTTPS servers:
//
// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))
// log.Fatal(http.Serve(autocert.NewListener("example.com"), handler))
//
// NewListener is a convenience function for a common configuration.
// More complex or custom configurations can use the autocert.Manager
+25 -10
View File
@@ -21,8 +21,9 @@ type domainRenewal struct {
ck certKey
key crypto.Signer
timerMu sync.Mutex
timer *time.Timer
timerMu sync.Mutex
timer *time.Timer
timerClose chan struct{} // if non-nil, renew closes this channel (and nils out the timer fields) instead of running
}
// start starts a cert renewal timer at the time
@@ -38,16 +39,28 @@ func (dr *domainRenewal) start(exp time.Time) {
dr.timer = time.AfterFunc(dr.next(exp), dr.renew)
}
// stop stops the cert renewal timer.
// If the timer is already stopped, calling stop is a noop.
// stop stops the cert renewal timer and waits for any in-flight calls to renew
// to complete. If the timer is already stopped, calling stop is a noop.
func (dr *domainRenewal) stop() {
dr.timerMu.Lock()
defer dr.timerMu.Unlock()
if dr.timer == nil {
return
for {
if dr.timer == nil {
return
}
if dr.timer.Stop() {
dr.timer = nil
return
} else {
// dr.timer fired, and we acquired dr.timerMu before the renew callback did.
// (We know this because otherwise the renew callback would have reset dr.timer!)
timerClose := make(chan struct{})
dr.timerClose = timerClose
dr.timerMu.Unlock()
<-timerClose
dr.timerMu.Lock()
}
}
dr.timer.Stop()
dr.timer = nil
}
// renew is called periodically by a timer.
@@ -55,7 +68,9 @@ func (dr *domainRenewal) stop() {
func (dr *domainRenewal) renew() {
dr.timerMu.Lock()
defer dr.timerMu.Unlock()
if dr.timer == nil {
if dr.timerClose != nil {
close(dr.timerClose)
dr.timer, dr.timerClose = nil, nil
return
}
@@ -67,8 +82,8 @@ func (dr *domainRenewal) renew() {
next = renewJitter / 2
next += time.Duration(pseudoRand.int63n(int64(next)))
}
dr.timer = time.AfterFunc(next, dr.renew)
testDidRenewLoop(next, err)
dr.timer = time.AfterFunc(next, dr.renew)
}
// updateState locks and replaces the relevant Manager.state item with the given
+92 -155
View File
@@ -6,19 +6,13 @@ package autocert
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"golang.org/x/crypto/acme"
"golang.org/x/crypto/acme/autocert/internal/acmetest"
)
func TestRenewalNext(t *testing.T) {
@@ -48,101 +42,47 @@ func TestRenewalNext(t *testing.T) {
}
func TestRenewFromCache(t *testing.T) {
// ACME CA server stub
var ca *httptest.Server
ca = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Replay-Nonce", "nonce")
if r.Method == "HEAD" {
// a nonce request
return
}
man := testManager(t)
man.RenewBefore = 24 * time.Hour
switch r.URL.Path {
// discovery
case "/":
if err := discoTmpl.Execute(w, ca.URL); err != nil {
t.Fatalf("discoTmpl: %v", err)
}
// client key registration
case "/new-reg":
w.Write([]byte("{}"))
// domain authorization
case "/new-authz":
w.Header().Set("Location", ca.URL+"/authz/1")
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{"status": "valid"}`))
// authorization status request done by Manager's revokePendingAuthz.
case "/authz/1":
w.Write([]byte(`{"status": "valid"}`))
// cert request
case "/new-cert":
var req struct {
CSR string `json:"csr"`
}
decodePayload(&req, r.Body)
b, _ := base64.RawURLEncoding.DecodeString(req.CSR)
csr, err := x509.ParseCertificateRequest(b)
if err != nil {
t.Fatalf("new-cert: CSR: %v", err)
}
der, err := dummyCert(csr.PublicKey, exampleDomain)
if err != nil {
t.Fatalf("new-cert: dummyCert: %v", err)
}
chainUp := fmt.Sprintf("<%s/ca-cert>; rel=up", ca.URL)
w.Header().Set("Link", chainUp)
w.WriteHeader(http.StatusCreated)
w.Write(der)
// CA chain cert
case "/ca-cert":
der, err := dummyCert(nil, "ca")
if err != nil {
t.Fatalf("ca-cert: dummyCert: %v", err)
}
w.Write(der)
default:
t.Errorf("unrecognized r.URL.Path: %s", r.URL.Path)
}
}))
defer ca.Close()
ca := acmetest.NewCAServer(t).Start()
ca.ResolveGetCertificate(exampleDomain, man.GetCertificate)
man := &Manager{
Prompt: AcceptTOS,
Cache: newMemCache(t),
RenewBefore: 24 * time.Hour,
Client: &acme.Client{
DirectoryURL: ca.URL,
},
man.Client = &acme.Client{
DirectoryURL: ca.URL(),
}
defer man.stopRenew()
// cache an almost expired cert
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
now := time.Now()
cert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), exampleDomain)
if err != nil {
t.Fatal(err)
}
tlscert := &tls.Certificate{PrivateKey: key, Certificate: [][]byte{cert}}
if err := man.cachePut(context.Background(), exampleCertKey, tlscert); err != nil {
c := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Minute))
if err := man.cachePut(context.Background(), exampleCertKey, c); err != nil {
t.Fatal(err)
}
// veriy the renewal happened
// verify the renewal happened
defer func() {
// Stop the timers that read and execute testDidRenewLoop before restoring it.
// Otherwise the timer callback may race with the deferred write.
man.stopRenew()
testDidRenewLoop = func(next time.Duration, err error) {}
}()
done := make(chan struct{})
renewed := make(chan bool, 1)
testDidRenewLoop = func(next time.Duration, err error) {
defer close(done)
defer func() {
select {
case renewed <- true:
default:
// The renewal timer uses a random backoff. If the first renewal fails for
// some reason, we could end up with multiple calls here before the test
// stops the timer.
}
}()
if err != nil {
t.Errorf("testDidRenewLoop: %v", err)
}
// Next should be about 90 days:
// dummyCert creates 90days expiry + account for man.RenewBefore.
// CaServer creates 90days expiry + account for man.RenewBefore.
// Previous expiration was within 1 min.
future := 88 * 24 * time.Hour
if next < future {
@@ -153,7 +93,8 @@ func TestRenewFromCache(t *testing.T) {
after := time.Now().Add(future)
tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
if err != nil {
t.Fatalf("man.cacheGet: %v", err)
t.Errorf("man.cacheGet: %v", err)
return
}
if !tlscert.Leaf.NotAfter.After(after) {
t.Errorf("cache leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
@@ -164,11 +105,13 @@ func TestRenewFromCache(t *testing.T) {
defer man.stateMu.Unlock()
s := man.state[exampleCertKey]
if s == nil {
t.Fatalf("m.state[%q] is nil", exampleCertKey)
t.Errorf("m.state[%q] is nil", exampleCertKey)
return
}
tlscert, err = s.tlscert()
if err != nil {
t.Fatalf("s.tlscert: %v", err)
t.Errorf("s.tlscert: %v", err)
return
}
if !tlscert.Leaf.NotAfter.After(after) {
t.Errorf("state leaf.NotAfter = %v; want > %v", tlscert.Leaf.NotAfter, after)
@@ -180,55 +123,34 @@ func TestRenewFromCache(t *testing.T) {
if _, err := man.GetCertificate(hello); err != nil {
t.Fatal(err)
}
// wait for renew loop
select {
case <-time.After(10 * time.Second):
t.Fatal("renew took too long to occur")
case <-done:
}
<-renewed
}
func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
man := &Manager{
Prompt: AcceptTOS,
Cache: newMemCache(t),
RenewBefore: 24 * time.Hour,
Client: &acme.Client{
DirectoryURL: "invalid",
},
ca := acmetest.NewCAServer(t).Start()
man := testManager(t)
man.RenewBefore = 24 * time.Hour
man.Client = &acme.Client{
DirectoryURL: "invalid",
}
defer man.stopRenew()
// cache a recently renewed cert with a different private key
newKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
now := time.Now()
newCert, err := dateDummyCert(newKey.Public(), now.Add(-2*time.Hour), now.Add(time.Hour*24*90), exampleDomain)
if err != nil {
newCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Hour*24*90))
if err := man.cachePut(context.Background(), exampleCertKey, newCert); err != nil {
t.Fatal(err)
}
newLeaf, err := validCert(exampleCertKey, [][]byte{newCert}, newKey, now)
newLeaf, err := validCert(exampleCertKey, newCert.Certificate, newCert.PrivateKey.(crypto.Signer), now)
if err != nil {
t.Fatal(err)
}
newTLSCert := &tls.Certificate{PrivateKey: newKey, Certificate: [][]byte{newCert}, Leaf: newLeaf}
if err := man.cachePut(context.Background(), exampleCertKey, newTLSCert); err != nil {
t.Fatal(err)
}
// set internal state to an almost expired cert
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
oldCert := ca.LeafCert(exampleDomain, "ECDSA", now.Add(-2*time.Hour), now.Add(time.Minute))
if err != nil {
t.Fatal(err)
}
oldCert, err := dateDummyCert(key.Public(), now.Add(-2*time.Hour), now.Add(time.Minute), exampleDomain)
if err != nil {
t.Fatal(err)
}
oldLeaf, err := validCert(exampleCertKey, [][]byte{oldCert}, key, now)
oldLeaf, err := validCert(exampleCertKey, oldCert.Certificate, oldCert.PrivateKey.(crypto.Signer), now)
if err != nil {
t.Fatal(err)
}
@@ -237,20 +159,32 @@ func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
man.state = make(map[certKey]*certState)
}
s := &certState{
key: key,
cert: [][]byte{oldCert},
key: oldCert.PrivateKey.(crypto.Signer),
cert: oldCert.Certificate,
leaf: oldLeaf,
}
man.state[exampleCertKey] = s
man.stateMu.Unlock()
// veriy the renewal accepted the newer cached cert
// verify the renewal accepted the newer cached cert
defer func() {
// Stop the timers that read and execute testDidRenewLoop before restoring it.
// Otherwise the timer callback may race with the deferred write.
man.stopRenew()
testDidRenewLoop = func(next time.Duration, err error) {}
}()
done := make(chan struct{})
renewed := make(chan bool, 1)
testDidRenewLoop = func(next time.Duration, err error) {
defer close(done)
defer func() {
select {
case renewed <- true:
default:
// The renewal timer uses a random backoff. If the first renewal fails for
// some reason, we could end up with multiple calls here before the test
// stops the timer.
}
}()
if err != nil {
t.Errorf("testDidRenewLoop: %v", err)
}
@@ -264,7 +198,8 @@ func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
// ensure the cached cert was not modified
tlscert, err := man.cacheGet(context.Background(), exampleCertKey)
if err != nil {
t.Fatalf("man.cacheGet: %v", err)
t.Errorf("man.cacheGet: %v", err)
return
}
if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
t.Errorf("cache leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
@@ -275,30 +210,22 @@ func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
defer man.stateMu.Unlock()
s := man.state[exampleCertKey]
if s == nil {
t.Fatalf("m.state[%q] is nil", exampleCertKey)
t.Errorf("m.state[%q] is nil", exampleCertKey)
return
}
stateKey := s.key.Public().(*ecdsa.PublicKey)
if stateKey.X.Cmp(newKey.X) != 0 || stateKey.Y.Cmp(newKey.Y) != 0 {
t.Fatalf("state key was not updated from cache x: %v y: %v; want x: %v y: %v", stateKey.X, stateKey.Y, newKey.X, newKey.Y)
if !stateKey.Equal(newLeaf.PublicKey) {
t.Error("state key was not updated from cache")
return
}
tlscert, err = s.tlscert()
if err != nil {
t.Fatalf("s.tlscert: %v", err)
t.Errorf("s.tlscert: %v", err)
return
}
if !tlscert.Leaf.NotAfter.Equal(newLeaf.NotAfter) {
t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
}
// verify the private key is replaced in the renewal state
r := man.renewal[exampleCertKey]
if r == nil {
t.Fatalf("m.renewal[%q] is nil", exampleCertKey)
}
renewalKey := r.key.Public().(*ecdsa.PublicKey)
if renewalKey.X.Cmp(newKey.X) != 0 || renewalKey.Y.Cmp(newKey.Y) != 0 {
t.Fatalf("renewal private key was not updated from cache x: %v y: %v; want x: %v y: %v", renewalKey.X, renewalKey.Y, newKey.X, newKey.Y)
}
}
// assert the expiring cert is returned from state
@@ -312,21 +239,31 @@ func TestRenewFromCacheAlreadyRenewed(t *testing.T) {
}
// trigger renew
go man.renew(exampleCertKey, s.key, s.leaf.NotAfter)
man.startRenew(exampleCertKey, s.key, s.leaf.NotAfter)
<-renewed
func() {
man.renewalMu.Lock()
defer man.renewalMu.Unlock()
// wait for renew loop
select {
case <-time.After(10 * time.Second):
t.Fatal("renew took too long to occur")
case <-done:
// assert the new cert is returned from state after renew
hello := clientHelloInfo(exampleDomain, algECDSA)
tlscert, err := man.GetCertificate(hello)
if err != nil {
t.Fatal(err)
// verify the private key is replaced in the renewal state
r := man.renewal[exampleCertKey]
if r == nil {
t.Errorf("m.renewal[%q] is nil", exampleCertKey)
return
}
if !newTLSCert.Leaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newTLSCert.Leaf.NotAfter)
renewalKey := r.key.Public().(*ecdsa.PublicKey)
if !renewalKey.Equal(newLeaf.PublicKey) {
t.Error("renewal private key was not updated from cache")
}
}()
// assert the new cert is returned from state after renew
hello = clientHelloInfo(exampleDomain, algECDSA)
tlscert, err = man.GetCertificate(hello)
if err != nil {
t.Fatal(err)
}
if !newLeaf.NotAfter.Equal(tlscert.Leaf.NotAfter) {
t.Errorf("state leaf.NotAfter = %v; want == %v", tlscert.Leaf.NotAfter, newLeaf.NotAfter)
}
}
+3 -3
View File
@@ -12,7 +12,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"math/big"
"net/http"
"strconv"
@@ -156,7 +156,7 @@ func (c *Client) get(ctx context.Context, url string, ok resOkay) (*http.Respons
}
}
// postAsGet is POST-as-GET, a replacement for GET in RFC8555
// postAsGet is POST-as-GET, a replacement for GET in RFC 8555
// as described in https://tools.ietf.org/html/rfc8555#section-6.3.
// It makes a POST request in KID form with zero JWS payload.
// See nopayload doc comments in jws.go.
@@ -310,7 +310,7 @@ func isRetriable(code int) bool {
func responseError(resp *http.Response) error {
// don't care if ReadAll returns an error:
// json.Unmarshal will fail in that case anyway
b, _ := ioutil.ReadAll(resp.Body)
b, _ := io.ReadAll(resp.Body)
e := &wireError{Status: resp.StatusCode}
if err := json.Unmarshal(b, e); err != nil {
// this is not a regular error response:
+5 -5
View File
@@ -7,7 +7,7 @@ package acme
import (
"context"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/http/httptest"
"reflect"
@@ -54,7 +54,7 @@ func TestErrorResponse(t *testing.T) {
res := &http.Response{
StatusCode: 400,
Status: "400 Bad Request",
Body: ioutil.NopCloser(strings.NewReader(s)),
Body: io.NopCloser(strings.NewReader(s)),
Header: http.Header{"X-Foo": {"bar"}},
}
err := responseError(res)
@@ -115,8 +115,8 @@ func TestPostWithRetries(t *testing.T) {
if _, err := client.Authorize(context.Background(), "example.com"); err != nil {
t.Errorf("client.Authorize 1: %v", err)
}
if count != 4 {
t.Errorf("total requests count: %d; want 4", count)
if count != 3 {
t.Errorf("total requests count: %d; want 3", count)
}
}
@@ -224,7 +224,7 @@ func TestUserAgent(t *testing.T) {
}
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{}`))
w.Write([]byte(`{"newOrder": "sure"}`))
}))
defer ts.Close()
+7 -54
View File
@@ -11,12 +11,12 @@
//
// A usage example:
//
// go run prober.go \
// -d https://acme-staging-v02.api.letsencrypt.org/directory \
// -f order \
// -t http-01 \
// -a :8080 \
// -domain some.example.org
// go run prober.go \
// -d https://acme-staging-v02.api.letsencrypt.org/directory \
// -f order \
// -t http-01 \
// -a :8080 \
// -domain some.example.org
//
// The above assumes a TCP tunnel from some.example.org:80 to 0.0.0.0:8080
// in order for the test to be able to fulfill http-01 challenge.
@@ -50,14 +50,13 @@ import (
var (
// ACME CA directory URL.
// Let's Encrypt v1 prod: https://acme-v01.api.letsencrypt.org/directory
// Let's Encrypt v2 prod: https://acme-v02.api.letsencrypt.org/directory
// Let's Encrypt v2 staging: https://acme-staging-v02.api.letsencrypt.org/directory
// See the following for more CAs implementing ACME protocol:
// https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment#CAs_&_PKIs_that_offer_ACME_certificates
directory = flag.String("d", "", "ACME directory URL.")
reginfo = flag.String("r", "", "ACME account registration info.")
flow = flag.String("f", "", "Flow to run: order, preauthz (RFC8555) or preauthz02 (draft-02).")
flow = flag.String("f", "", `Flow to run: "order" or "preauthz" (RFC8555).`)
chaltyp = flag.String("t", "", "Challenge type: tls-alpn-01, http-01 or dns-01.")
addr = flag.String("a", "", "Local server address for tls-alpn-01 and http-01.")
dnsscript = flag.String("s", "", "Script to run for provisioning dns-01 challenges.")
@@ -127,8 +126,6 @@ When running with dns-01 challenge type, use -s argument instead of -a.
p.runOrder(ctx, identifiers)
case "preauthz":
p.runPreauthz(ctx, identifiers)
case "preauthz02":
p.runPreauthzLegacy(ctx, identifiers)
default:
log.Fatalf("unknown flow: %q", *flow)
}
@@ -276,50 +273,6 @@ func (p *prober) runPreauthz(ctx context.Context, identifiers []acme.AuthzID) {
}
}
func (p *prober) runPreauthzLegacy(ctx context.Context, identifiers []acme.AuthzID) {
var zurls []string
for _, id := range identifiers {
z, err := authorize(ctx, p.client, id)
if err != nil {
log.Fatalf("AuthorizeID(%+v): %v", id, err)
}
if z.Status == acme.StatusValid {
log.Printf("authz %s is valid; skipping", z.URI)
continue
}
if err := p.fulfill(ctx, z); err != nil {
log.Fatalf("fulfill(%s): %v", z.URI, err)
}
zurls = append(zurls, z.URI)
log.Printf("authorized for %+v", id)
}
// We should be all set now.
log.Print("all authorizations are done")
csr, certkey := newCSR(identifiers)
der, curl, err := p.client.CreateCert(ctx, csr, 48*time.Hour, true)
if err != nil {
log.Fatalf("CreateCert: %v", err)
}
log.Printf("cert URL: %s", curl)
if err := checkCert(der, identifiers); err != nil {
p.errorf("invalid cert: %v", err)
}
// Deactivate all authorizations we satisfied earlier.
for _, v := range zurls {
if err := p.client.RevokeAuthorization(ctx, v); err != nil {
p.errorf("RevokAuthorization(%q): %v", v, err)
continue
}
}
// Try revoking the issued cert using its private key.
if err := p.client.RevokeCert(ctx, certkey, der[0], acme.CRLReasonCessationOfOperation); err != nil {
p.errorf("RevokeCert: %v", err)
}
}
func (p *prober) fulfill(ctx context.Context, z *acme.Authorization) error {
var chal *acme.Challenge
for i, c := range z.Challenges {
+39 -11
View File
@@ -20,12 +20,12 @@ import (
"math/big"
)
// keyID is the account identity provided by a CA during registration.
type keyID string
// KeyID is the account key identity provided by a CA during registration.
type KeyID string
// noKeyID indicates that jwsEncodeJSON should compute and use JWK instead of a KID.
// See jwsEncodeJSON for details.
const noKeyID = keyID("")
const noKeyID = KeyID("")
// noPayload indicates jwsEncodeJSON will encode zero-length octet string
// in a JWS request. This is called POST-as-GET in RFC 8555 and is used to make
@@ -33,6 +33,10 @@ const noKeyID = keyID("")
// See https://tools.ietf.org/html/rfc8555#section-6.3 for more details.
const noPayload = ""
// noNonce indicates that the nonce should be omitted from the protected header.
// See jwsEncodeJSON for details.
const noNonce = ""
// jsonWebSignature can be easily serialized into a JWS following
// https://tools.ietf.org/html/rfc7515#section-3.2.
type jsonWebSignature struct {
@@ -43,32 +47,56 @@ type jsonWebSignature struct {
// jwsEncodeJSON signs claimset using provided key and a nonce.
// The result is serialized in JSON format containing either kid or jwk
// fields based on the provided keyID value.
// fields based on the provided KeyID value.
//
// If kid is non-empty, its quoted value is inserted in the protected head
// The claimset is marshalled using json.Marshal unless it is a string.
// In which case it is inserted directly into the message.
//
// If kid is non-empty, its quoted value is inserted in the protected header
// as "kid" field value. Otherwise, JWK is computed using jwkEncode and inserted
// as "jwk" field value. The "jwk" and "kid" fields are mutually exclusive.
//
// If nonce is non-empty, its quoted value is inserted in the protected header.
//
// See https://tools.ietf.org/html/rfc7515#section-7.
func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid keyID, nonce, url string) ([]byte, error) {
func jwsEncodeJSON(claimset interface{}, key crypto.Signer, kid KeyID, nonce, url string) ([]byte, error) {
if key == nil {
return nil, errors.New("nil key")
}
alg, sha := jwsHasher(key.Public())
if alg == "" || !sha.Available() {
return nil, ErrUnsupportedKey
}
var phead string
headers := struct {
Alg string `json:"alg"`
KID string `json:"kid,omitempty"`
JWK json.RawMessage `json:"jwk,omitempty"`
Nonce string `json:"nonce,omitempty"`
URL string `json:"url"`
}{
Alg: alg,
Nonce: nonce,
URL: url,
}
switch kid {
case noKeyID:
jwk, err := jwkEncode(key.Public())
if err != nil {
return nil, err
}
phead = fmt.Sprintf(`{"alg":%q,"jwk":%s,"nonce":%q,"url":%q}`, alg, jwk, nonce, url)
headers.JWK = json.RawMessage(jwk)
default:
phead = fmt.Sprintf(`{"alg":%q,"kid":%q,"nonce":%q,"url":%q}`, alg, kid, nonce, url)
headers.KID = string(kid)
}
phead = base64.RawURLEncoding.EncodeToString([]byte(phead))
phJSON, err := json.Marshal(headers)
if err != nil {
return nil, err
}
phead := base64.RawURLEncoding.EncodeToString([]byte(phJSON))
var payload string
if claimset != noPayload {
if val, ok := claimset.(string); ok {
payload = val
} else {
cs, err := json.Marshal(claimset)
if err != nil {
return nil, err
+39 -1
View File
@@ -195,8 +195,46 @@ func TestJWSEncodeJSON(t *testing.T) {
}
}
func TestJWSEncodeNoNonce(t *testing.T) {
kid := KeyID("https://example.org/account/1")
claims := "RawString"
const (
// {"alg":"ES256","kid":"https://example.org/account/1","nonce":"nonce","url":"url"}
protected = "eyJhbGciOiJFUzI1NiIsImtpZCI6Imh0dHBzOi8vZXhhbXBsZS5vcmcvYWNjb3VudC8xIiwidXJsIjoidXJsIn0"
// "Raw String"
payload = "RawString"
)
b, err := jwsEncodeJSON(claims, testKeyEC, kid, "", "url")
if err != nil {
t.Fatal(err)
}
var jws struct{ Protected, Payload, Signature string }
if err := json.Unmarshal(b, &jws); err != nil {
t.Fatal(err)
}
if jws.Protected != protected {
t.Errorf("protected:\n%s\nwant:\n%s", jws.Protected, protected)
}
if jws.Payload != payload {
t.Errorf("payload:\n%s\nwant:\n%s", jws.Payload, payload)
}
sig, err := base64.RawURLEncoding.DecodeString(jws.Signature)
if err != nil {
t.Fatalf("jws.Signature: %v", err)
}
r, s := big.NewInt(0), big.NewInt(0)
r.SetBytes(sig[:len(sig)/2])
s.SetBytes(sig[len(sig)/2:])
h := sha256.Sum256([]byte(protected + "." + payload))
if !ecdsa.Verify(testKeyEC.Public().(*ecdsa.PublicKey), h[:], r, s) {
t.Error("invalid signature")
}
}
func TestJWSEncodeKID(t *testing.T) {
kid := keyID("https://example.org/account/1")
kid := KeyID("https://example.org/account/1")
claims := struct{ Msg string }{"Hello JWS"}
// JWS signed with testKeyEC
const (
+42 -4
View File
@@ -13,7 +13,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"time"
)
@@ -24,6 +23,9 @@ import (
//
// It only works with CAs implementing RFC 8555.
func (c *Client) DeactivateReg(ctx context.Context) error {
if _, err := c.Discover(ctx); err != nil { // required by c.accountKID
return err
}
url := string(c.accountKID(ctx))
if url == "" {
return ErrNoAccount
@@ -78,7 +80,7 @@ func (c *Client) registerRFC(ctx context.Context, acct *Account, prompt func(tos
}
// Cache Account URL even if we return an error to the caller.
// It is by all means a valid and usable "kid" value for future requests.
c.kid = keyID(a.URI)
c.KID = KeyID(a.URI)
if res.StatusCode == http.StatusOK {
return nil, ErrAccountAlreadyExists
}
@@ -148,6 +150,42 @@ func responseAccount(res *http.Response) (*Account, error) {
}, nil
}
// accountKeyRollover attempts to perform account key rollover.
// On success it will change client.Key to the new key.
func (c *Client) accountKeyRollover(ctx context.Context, newKey crypto.Signer) error {
dir, err := c.Discover(ctx) // Also required by c.accountKID
if err != nil {
return err
}
kid := c.accountKID(ctx)
if kid == noKeyID {
return ErrNoAccount
}
oldKey, err := jwkEncode(c.Key.Public())
if err != nil {
return err
}
payload := struct {
Account string `json:"account"`
OldKey json.RawMessage `json:"oldKey"`
}{
Account: string(kid),
OldKey: json.RawMessage(oldKey),
}
inner, err := jwsEncodeJSON(payload, newKey, noKeyID, noNonce, dir.KeyChangeURL)
if err != nil {
return err
}
res, err := c.post(ctx, nil, dir.KeyChangeURL, base64.RawURLEncoding.EncodeToString(inner), wantStatus(http.StatusOK))
if err != nil {
return err
}
defer res.Body.Close()
c.Key = newKey
return nil
}
// AuthorizeOrder initiates the order-based application for certificate issuance,
// as opposed to pre-authorization in Authorize.
// It is only supported by CAs implementing RFC 8555.
@@ -351,7 +389,7 @@ func (c *Client) fetchCertRFC(ctx context.Context, url string, bundle bool) ([][
// Get all the bytes up to a sane maximum.
// Account very roughly for base64 overhead.
const max = maxCertChainSize + maxCertChainSize/33
b, err := ioutil.ReadAll(io.LimitReader(res.Body, max+1))
b, err := io.ReadAll(io.LimitReader(res.Body, max+1))
if err != nil {
return nil, fmt.Errorf("acme: fetch cert response stream: %v", err)
}
@@ -430,7 +468,7 @@ func (c *Client) ListCertAlternates(ctx context.Context, url string) ([]string,
// We don't need the body but we need to discard it so we don't end up
// preventing keep-alive
if _, err := io.Copy(ioutil.Discard, res.Body); err != nil {
if _, err := io.Copy(io.Discard, res.Body); err != nil {
return nil, fmt.Errorf("acme: cert alternates response stream: %v", err)
}
alts := linkHeader(res.Header, "alternate")
+136 -10
View File
@@ -15,12 +15,14 @@ import (
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"io"
"math/big"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"sync"
"testing"
"time"
@@ -62,7 +64,7 @@ func TestRFC_Discover(t *testing.T) {
}`, nonce, reg, order, authz, revoke, keychange, metaTerms, metaWebsite, metaCAA)
}))
defer ts.Close()
c := Client{DirectoryURL: ts.URL}
c := &Client{DirectoryURL: ts.URL}
dir, err := c.Discover(context.Background())
if err != nil {
t.Fatal(err)
@@ -146,7 +148,7 @@ func TestRFC_postKID(t *testing.T) {
w.Header().Set("Location", "/account-1")
w.Write([]byte(`{"status":"valid"}`))
case "/post":
b, _ := ioutil.ReadAll(r.Body) // check err later in decodeJWSxxx
b, _ := io.ReadAll(r.Body) // check err later in decodeJWSxxx
head, err := decodeJWSHead(bytes.NewReader(b))
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
@@ -192,13 +194,13 @@ func TestRFC_postKID(t *testing.T) {
t.Fatal(err)
}
defer res.Body.Close()
b, _ := ioutil.ReadAll(res.Body) // don't care about err - just checking b
b, _ := io.ReadAll(res.Body) // don't care about err - just checking b
if string(b) != "pong" {
t.Errorf("res.Body = %q; want pong", b)
}
}
// acmeServer simulates a subset of RFC8555 compliant CA.
// acmeServer simulates a subset of RFC 8555 compliant CA.
//
// TODO: We also have x/crypto/acme/autocert/acmetest and startACMEServerStub in autocert_test.go.
// It feels like this acmeServer is a sweet spot between usefulness and added complexity.
@@ -232,6 +234,7 @@ func (s *acmeServer) start() {
"newOrder": %q,
"newAuthz": %q,
"revokeCert": %q,
"keyChange": %q,
"meta": {"termsOfService": %q}
}`,
s.url("/acme/new-nonce"),
@@ -239,6 +242,7 @@ func (s *acmeServer) start() {
s.url("/acme/new-order"),
s.url("/acme/new-authz"),
s.url("/acme/revoke-cert"),
s.url("/acme/key-change"),
s.url("/terms"),
)
return
@@ -293,7 +297,7 @@ func TestRFC_Register(t *testing.T) {
"orders": %q
}`, email, s.url("/accounts/1/orders"))
b, _ := ioutil.ReadAll(r.Body) // check err later in decodeJWSxxx
b, _ := io.ReadAll(r.Body) // check err later in decodeJWSxxx
head, err := decodeJWSHead(bytes.NewReader(b))
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
@@ -344,7 +348,7 @@ func TestRFC_Register(t *testing.T) {
if !didPrompt {
t.Error("tos prompt wasn't called")
}
if v := cl.accountKID(ctx); v != keyID(okAccount.URI) {
if v := cl.accountKID(ctx); v != KeyID(okAccount.URI) {
t.Errorf("account kid = %q; want %q", v, okAccount.URI)
}
}
@@ -482,7 +486,7 @@ func TestRFC_RegisterExternalAccountBinding(t *testing.T) {
if !didPrompt {
t.Error("tos prompt wasn't called")
}
if v := cl.accountKID(ctx); v != keyID(okAccount.URI) {
if v := cl.accountKID(ctx); v != KeyID(okAccount.URI) {
t.Errorf("account kid = %q; want %q", v, okAccount.URI)
}
}
@@ -502,7 +506,7 @@ func TestRFC_RegisterExisting(t *testing.T) {
if err != ErrAccountAlreadyExists {
t.Errorf("err = %v; want %v", err, ErrAccountAlreadyExists)
}
kid := keyID(s.url("/accounts/1"))
kid := KeyID(s.url("/accounts/1"))
if v := cl.accountKID(context.Background()); v != kid {
t.Errorf("account kid = %q; want %q", v, kid)
}
@@ -524,7 +528,7 @@ func TestRFC_UpdateReg(t *testing.T) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status": "valid"}`))
b, _ := ioutil.ReadAll(r.Body) // check err later in decodeJWSxxx
b, _ := io.ReadAll(r.Body) // check err later in decodeJWSxxx
head, err := decodeJWSHead(bytes.NewReader(b))
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
@@ -621,6 +625,128 @@ func TestRFC_GetRegOtherError(t *testing.T) {
}
}
func TestRFC_AccountKeyRollover(t *testing.T) {
s := newACMEServer()
s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Location", s.url("/accounts/1"))
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status": "valid"}`))
})
s.handle("/acme/key-change", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
s.start()
defer s.close()
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
if err := cl.AccountKeyRollover(context.Background(), testKeyEC384); err != nil {
t.Errorf("AccountKeyRollover: %v, wanted no error", err)
} else if cl.Key != testKeyEC384 {
t.Error("AccountKeyRollover did not rotate the client key")
}
}
func TestRFC_DeactivateReg(t *testing.T) {
const email = "mailto:user@example.org"
curStatus := StatusValid
type account struct {
Status string `json:"status"`
Contact []string `json:"contact"`
AcceptTOS bool `json:"termsOfServiceAgreed"`
Orders string `json:"orders"`
}
s := newACMEServer()
s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Location", s.url("/accounts/1"))
w.WriteHeader(http.StatusOK) // 200 means existing account
json.NewEncoder(w).Encode(account{
Status: curStatus,
Contact: []string{email},
AcceptTOS: true,
Orders: s.url("/accounts/1/orders"),
})
b, _ := io.ReadAll(r.Body) // check err later in decodeJWSxxx
head, err := decodeJWSHead(bytes.NewReader(b))
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
return
}
if len(head.JWK) == 0 {
t.Error("head.JWK is empty")
}
var req struct {
Status string `json:"status"`
Contact []string `json:"contact"`
AcceptTOS bool `json:"termsOfServiceAgreed"`
OnlyExisting bool `json:"onlyReturnExisting"`
}
decodeJWSRequest(t, &req, bytes.NewReader(b))
if !req.OnlyExisting {
t.Errorf("req.OnlyReturnExisting = %t; want = %t", req.OnlyExisting, true)
}
})
s.handle("/accounts/1", func(w http.ResponseWriter, r *http.Request) {
if curStatus == StatusValid {
curStatus = StatusDeactivated
w.WriteHeader(http.StatusOK)
} else {
s.error(w, &wireError{
Status: http.StatusUnauthorized,
Type: "urn:ietf:params:acme:error:unauthorized",
})
}
var req account
b, _ := io.ReadAll(r.Body) // check err later in decodeJWSxxx
head, err := decodeJWSHead(bytes.NewReader(b))
if err != nil {
t.Errorf("decodeJWSHead: %v", err)
return
}
if len(head.JWK) != 0 {
t.Error("head.JWK is not empty")
}
if !strings.HasSuffix(head.KID, "/accounts/1") {
t.Errorf("head.KID = %q; want suffix /accounts/1", head.KID)
}
decodeJWSRequest(t, &req, bytes.NewReader(b))
if req.Status != StatusDeactivated {
t.Errorf("req.Status = %q; want = %q", req.Status, StatusDeactivated)
}
})
s.start()
defer s.close()
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
if err := cl.DeactivateReg(context.Background()); err != nil {
t.Errorf("DeactivateReg: %v, wanted no error", err)
}
if err := cl.DeactivateReg(context.Background()); err == nil {
t.Errorf("DeactivateReg: %v, wanted error for unauthorized", err)
}
}
func TestRF_DeactivateRegNoAccount(t *testing.T) {
s := newACMEServer()
s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
s.error(w, &wireError{
Status: http.StatusBadRequest,
Type: "urn:ietf:params:acme:error:accountDoesNotExist",
})
})
s.start()
defer s.close()
cl := &Client{Key: testKeyEC, DirectoryURL: s.url("/")}
if err := cl.DeactivateReg(context.Background()); !errors.Is(err, ErrNoAccount) {
t.Errorf("DeactivateReg: %v, wanted ErrNoAccount", err)
}
}
func TestRFC_AuthorizeOrder(t *testing.T) {
s := newACMEServer()
s.handle("/acme/new-account", func(w http.ResponseWriter, r *http.Request) {
+2 -10
View File
@@ -297,7 +297,7 @@ type Directory struct {
// CAA consists of lowercase hostname elements, which the ACME server
// recognises as referring to itself for the purposes of CAA record validation
// as defined in RFC6844.
// as defined in RFC 6844.
CAA []string
// ExternalAccountRequired indicates that the CA requires for all account-related
@@ -305,14 +305,6 @@ type Directory struct {
ExternalAccountRequired bool
}
// rfcCompliant reports whether the ACME server implements RFC 8555.
// Note that some servers may have incomplete RFC implementation
// even if the returned value is true.
// If rfcCompliant reports false, the server most likely implements draft-02.
func (d *Directory) rfcCompliant() bool {
return d.OrderURL != ""
}
// Order represents a client's request for a certificate.
// It tracks the request flow progress through to issuance.
type Order struct {
@@ -448,7 +440,7 @@ func DomainIDs(names ...string) []AuthzID {
// IPIDs creates a slice of AuthzID with "ip" identifier type.
// Each element of addr is textual form of an address as defined
// in RFC1123 Section 2.1 for IPv4 and in RFC5952 Section 4 for IPv6.
// in RFC 1123 Section 2.1 for IPv4 and in RFC 5952 Section 4 for IPv6.
func IPIDs(addr ...string) []AuthzID {
a := make([]AuthzID, len(addr))
for i, v := range addr {
+4 -6
View File
@@ -11,8 +11,7 @@
// If you aren't sure which function you need, use Argon2id (IDKey) and
// the parameter recommendations for your scenario.
//
//
// Argon2i
// # Argon2i
//
// Argon2i (implemented by Key) is the side-channel resistant version of Argon2.
// It uses data-independent memory access, which is preferred for password
@@ -21,8 +20,7 @@
// parameters (taken from [2]) for non-interactive operations are time=3 and to
// use the maximum available memory.
//
//
// Argon2id
// # Argon2id
//
// Argon2id (implemented by IDKey) is a hybrid version of Argon2 combining
// Argon2i and Argon2d. It uses data-independent memory access for the first
@@ -59,7 +57,7 @@ const (
// For example, you can get a derived key for e.g. AES-256 (which needs a
// 32-byte key) by doing:
//
// key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
// key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
//
// The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number.
// If using that amount of memory (32 MB) is not possible in some contexts then
@@ -83,7 +81,7 @@ func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint3
// For example, you can get a derived key for e.g. AES-256 (which needs a
// 32-byte key) by doing:
//
// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
//
// The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
// If using that amount of memory (64 MB) is not possible in some contexts then
+1 -1
View File
@@ -162,7 +162,7 @@ type G2 struct {
p *twistPoint
}
// RandomG1 returns x and g₂ˣ where x is a random, non-zero number read from r.
// RandomG2 returns x and g₂ˣ where x is a random, non-zero number read from r.
func RandomG2(r io.Reader) (*big.Int, *G2, error) {
var k *big.Int
var err error
+7 -4
View File
@@ -13,7 +13,10 @@
// golang.org/x/crypto/chacha20poly1305).
package cast5 // import "golang.org/x/crypto/cast5"
import "errors"
import (
"errors"
"math/bits"
)
const BlockSize = 8
const KeySize = 16
@@ -241,19 +244,19 @@ func (c *Cipher) keySchedule(in []byte) {
// These are the three 'f' functions. See RFC 2144, section 2.2.
func f1(d, m uint32, r uint8) uint32 {
t := m + d
I := (t << r) | (t >> (32 - r))
I := bits.RotateLeft32(t, int(r))
return ((sBox[0][I>>24] ^ sBox[1][(I>>16)&0xff]) - sBox[2][(I>>8)&0xff]) + sBox[3][I&0xff]
}
func f2(d, m uint32, r uint8) uint32 {
t := m ^ d
I := (t << r) | (t >> (32 - r))
I := bits.RotateLeft32(t, int(r))
return ((sBox[0][I>>24] - sBox[1][(I>>16)&0xff]) + sBox[2][(I>>8)&0xff]) ^ sBox[3][I&0xff]
}
func f3(d, m uint32, r uint8) uint32 {
t := m - d
I := (t << r) | (t >> (32 - r))
I := bits.RotateLeft32(t, int(r))
return ((sBox[0][I>>24] + sBox[1][(I>>16)&0xff]) ^ sBox[2][(I>>8)&0xff]) - sBox[3][I&0xff]
}
+2 -2
View File
@@ -12,7 +12,7 @@ import (
"errors"
"math/bits"
"golang.org/x/crypto/internal/subtle"
"golang.org/x/crypto/internal/alias"
)
const (
@@ -189,7 +189,7 @@ func (s *Cipher) XORKeyStream(dst, src []byte) {
panic("chacha20: output smaller than input")
}
dst = dst[:len(src)]
if subtle.InexactOverlap(dst, src) {
if alias.InexactOverlap(dst, src) {
panic("chacha20: invalid buffer overlap")
}
+1
View File
@@ -15,6 +15,7 @@ const bufSize = 256
// xorKeyStreamVX is an assembly implementation of XORKeyStream. It must only
// be called when the vector facility is available. Implementation in asm_s390x.s.
//
//go:noescape
func xorKeyStreamVX(dst, src []byte, key *[8]uint32, nonce *[3]uint32, counter *uint32)
+3 -3
View File
@@ -10,7 +10,7 @@ package chacha20poly1305
import (
"encoding/binary"
"golang.org/x/crypto/internal/subtle"
"golang.org/x/crypto/internal/alias"
"golang.org/x/sys/cpu"
)
@@ -56,7 +56,7 @@ func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []
setupState(&state, &c.key, nonce)
ret, out := sliceForAppend(dst, len(plaintext)+16)
if subtle.InexactOverlap(out, plaintext) {
if alias.InexactOverlap(out, plaintext) {
panic("chacha20poly1305: invalid buffer overlap")
}
chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
@@ -73,7 +73,7 @@ func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) (
ciphertext = ciphertext[:len(ciphertext)-16]
ret, out := sliceForAppend(dst, len(ciphertext))
if subtle.InexactOverlap(out, ciphertext) {
if alias.InexactOverlap(out, ciphertext) {
panic("chacha20poly1305: invalid buffer overlap")
}
if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
+3 -3
View File
@@ -8,8 +8,8 @@ import (
"encoding/binary"
"golang.org/x/crypto/chacha20"
"golang.org/x/crypto/internal/alias"
"golang.org/x/crypto/internal/poly1305"
"golang.org/x/crypto/internal/subtle"
)
func writeWithPadding(p *poly1305.MAC, b []byte) {
@@ -30,7 +30,7 @@ func writeUint64(p *poly1305.MAC, n int) {
func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []byte) []byte {
ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize)
ciphertext, tag := out[:len(plaintext)], out[len(plaintext):]
if subtle.InexactOverlap(out, plaintext) {
if alias.InexactOverlap(out, plaintext) {
panic("chacha20poly1305: invalid buffer overlap")
}
@@ -66,7 +66,7 @@ func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []
writeUint64(p, len(ciphertext))
ret, out := sliceForAppend(dst, len(ciphertext))
if subtle.InexactOverlap(out, ciphertext) {
if alias.InexactOverlap(out, ciphertext) {
panic("chacha20poly1305: invalid buffer overlap")
}
if !p.Verify(tag) {
+12 -7
View File
@@ -95,6 +95,11 @@ func (b *Builder) AddUint32(v uint32) {
b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}
// AddUint64 appends a big-endian, 64-bit value to the byte string.
func (b *Builder) AddUint64(v uint64) {
b.add(byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
}
// AddBytes appends a sequence of bytes to the byte string.
func (b *Builder) AddBytes(v []byte) {
b.add(v...)
@@ -106,13 +111,13 @@ func (b *Builder) AddBytes(v []byte) {
// supplied to them. The child builder passed to the continuation can be used
// to build the content of the length-prefixed sequence. For example:
//
// parent := cryptobyte.NewBuilder()
// parent.AddUint8LengthPrefixed(func (child *Builder) {
// child.AddUint8(42)
// child.AddUint8LengthPrefixed(func (grandchild *Builder) {
// grandchild.AddUint8(5)
// })
// })
// parent := cryptobyte.NewBuilder()
// parent.AddUint8LengthPrefixed(func (child *Builder) {
// child.AddUint8(42)
// child.AddUint8LengthPrefixed(func (grandchild *Builder) {
// grandchild.AddUint8(5)
// })
// })
//
// It is an error to write more bytes to the child than allowed by the reserved
// length prefix. After the continuation returns, the child must be considered
+22 -2
View File
@@ -141,7 +141,7 @@ func TestUint24(t *testing.T) {
var s String = b.BytesOrPanic()
var v uint32
if !s.ReadUint24(&v) {
t.Error("ReadUint8() = false, want true")
t.Error("ReadUint24() = false, want true")
}
if v != 0xfffefd {
t.Errorf("v = %d, want fffefd", v)
@@ -169,7 +169,7 @@ func TestUint32(t *testing.T) {
var s String = b.BytesOrPanic()
var v uint32
if !s.ReadUint32(&v) {
t.Error("ReadUint8() = false, want true")
t.Error("ReadUint32() = false, want true")
}
if v != 0xfffefdfc {
t.Errorf("v = %x, want fffefdfc", v)
@@ -179,6 +179,26 @@ func TestUint32(t *testing.T) {
}
}
func TestUint64(t *testing.T) {
var b Builder
b.AddUint64(0xf2fefefcff3cfdfc)
if err := builderBytesEq(&b, 242, 254, 254, 252, 255, 60, 253, 252); err != nil {
t.Error(err)
}
var s String = b.BytesOrPanic()
var v uint64
if !s.ReadUint64(&v) {
t.Error("ReadUint64() = false, want true")
}
if v != 0xf2fefefcff3cfdfc {
t.Errorf("v = %x, want f2fefefcff3cfdfc", v)
}
if len(s) != 0 {
t.Errorf("len(s) = %d, want 0", len(s))
}
}
func TestUMultiple(t *testing.T) {
var b Builder
b.AddUint8(23)
+11
View File
@@ -81,6 +81,17 @@ func (s *String) ReadUint32(out *uint32) bool {
return true
}
// ReadUint64 decodes a big-endian, 64-bit value into out and advances over it.
// It reports whether the read was successful.
func (s *String) ReadUint64(out *uint64) bool {
v := s.read(8)
if v == nil {
return false
}
*out = uint64(v[0])<<56 | uint64(v[1])<<48 | uint64(v[2])<<40 | uint64(v[3])<<32 | uint64(v[4])<<24 | uint64(v[5])<<16 | uint64(v[6])<<8 | uint64(v[7])
return true
}
func (s *String) readUnsigned(out *uint32, length int) bool {
v := s.read(length)
if v == nil {
+5 -4
View File
@@ -9,7 +9,8 @@ package curve25519 // import "golang.org/x/crypto/curve25519"
import (
"crypto/subtle"
"fmt"
"errors"
"strconv"
"golang.org/x/crypto/curve25519/internal/field"
)
@@ -124,10 +125,10 @@ func X25519(scalar, point []byte) ([]byte, error) {
func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) {
var in [32]byte
if l := len(scalar); l != 32 {
return nil, fmt.Errorf("bad scalar length: %d, expected %d", l, 32)
return nil, errors.New("bad scalar length: " + strconv.Itoa(l) + ", expected 32")
}
if l := len(point); l != 32 {
return nil, fmt.Errorf("bad point length: %d, expected %d", l, 32)
return nil, errors.New("bad point length: " + strconv.Itoa(l) + ", expected 32")
}
copy(in[:], scalar)
if &point[0] == &Basepoint[0] {
@@ -138,7 +139,7 @@ func x25519(dst *[32]byte, scalar, point []byte) ([]byte, error) {
copy(base[:], point)
ScalarMult(dst, &in, &base)
if subtle.ConstantTimeCompare(dst[:], zero[:]) == 1 {
return nil, fmt.Errorf("bad input point: low order point")
return nil, errors.New("bad input point: low order point")
}
}
return dst[:], nil
+2 -2
View File
@@ -7,7 +7,7 @@ package curve25519
import (
"bytes"
"crypto/rand"
"fmt"
"encoding/hex"
"testing"
)
@@ -25,7 +25,7 @@ func TestX25519Basepoint(t *testing.T) {
}
}
result := fmt.Sprintf("%x", x)
result := hex.EncodeToString(x)
if result != expectedHex {
t.Errorf("incorrect result: got %s, want %s", result, expectedHex)
}
+1 -1
View File
@@ -3,7 +3,7 @@ module asm
go 1.16
require (
github.com/mmcloughlin/avo v0.2.0
github.com/mmcloughlin/avo v0.4.0
golang.org/x/crypto v0.0.0
)
+17 -17
View File
@@ -1,32 +1,32 @@
github.com/mmcloughlin/avo v0.2.0 h1:6vhoSaKtxb6f4RiH+LK2qL6GSMpFzhEwJYTTSZNy09w=
github.com/mmcloughlin/avo v0.2.0/go.mod h1:5tidO2Z9Z7N6X7UMcGg+1KTj51O8OxYDCMHxCZTVpEA=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/arch v0.0.0-20210405154355-08b684f594a5/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
github.com/mmcloughlin/avo v0.4.0 h1:jeHDRktVD+578ULxWpQHkilor6pkdLF7u7EiTzDbfcU=
github.com/mmcloughlin/avo v0.4.0/go.mod h1:RW9BfYA3TgO9uCdNrKU2h6J8cPD8ZLznvfgHAeszb1s=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021 h1:giLT+HuUP/gXYrG2Plg9WTjj4qhfgaW424ZIFog3rlk=
golang.org/x/sys v0.0.0-20211030160813-b3129d9d1021/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
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.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+2 -2
View File
@@ -77,11 +77,11 @@ func checkAliasingTwoArgs(f func(v, x, y *Element) *Element) func(v, x, y Elemen
// TestAliasing checks that receivers and arguments can alias each other without
// leading to incorrect results. That is, it ensures that it's safe to write
//
// v.Invert(v)
// v.Invert(v)
//
// or
//
// v.Add(v, v)
// v.Add(v, v)
//
// without any of the inputs getting clobbered by the output being written.
func TestAliasing(t *testing.T) {
+3
View File
@@ -1,13 +1,16 @@
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
//go:build amd64 && gc && !purego
// +build amd64,gc,!purego
package field
// feMul sets out = a * b. It works like feMulGeneric.
//
//go:noescape
func feMul(out *Element, a *Element, b *Element)
// feSquare sets out = a * a. It works like feSquareGeneric.
//
//go:noescape
func feSquare(out *Element, a *Element)
+18 -170
View File
@@ -1,13 +1,7 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Copyright 2019 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.
// In Go 1.13, the ed25519 package was promoted to the standard library as
// crypto/ed25519, and this package became a wrapper for the standard library one.
//
//go:build !go1.13
// +build !go1.13
// Package ed25519 implements the Ed25519 signature algorithm. See
// https://ed25519.cr.yp.to/.
//
@@ -16,21 +10,15 @@
// representation includes a public key suffix to make multiple signing
// operations with the same key more efficient. This package refers to the RFC
// 8032 private key as the “seed”.
//
// Beginning with Go 1.13, the functionality of this package was moved to the
// standard library as crypto/ed25519. This package only acts as a compatibility
// wrapper.
package ed25519
// This code is a port of the public domain, “ref10” implementation of ed25519
// from SUPERCOP.
import (
"bytes"
"crypto"
cryptorand "crypto/rand"
"crypto/sha512"
"errors"
"crypto/ed25519"
"io"
"strconv"
"golang.org/x/crypto/ed25519/internal/edwards25519"
)
const (
@@ -45,57 +33,21 @@ const (
)
// PublicKey is the type of Ed25519 public keys.
type PublicKey []byte
//
// This type is an alias for crypto/ed25519's PublicKey type.
// See the crypto/ed25519 package for the methods on this type.
type PublicKey = ed25519.PublicKey
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
type PrivateKey []byte
// Public returns the PublicKey corresponding to priv.
func (priv PrivateKey) Public() crypto.PublicKey {
publicKey := make([]byte, PublicKeySize)
copy(publicKey, priv[32:])
return PublicKey(publicKey)
}
// Seed returns the private key seed corresponding to priv. It is provided for
// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
// in this package.
func (priv PrivateKey) Seed() []byte {
seed := make([]byte, SeedSize)
copy(seed, priv[:32])
return seed
}
// Sign signs the given message with priv.
// Ed25519 performs two passes over messages to be signed and therefore cannot
// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
// indicate the message hasn't been hashed. This can be achieved by passing
// crypto.Hash(0) as the value for opts.
func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
if opts.HashFunc() != crypto.Hash(0) {
return nil, errors.New("ed25519: cannot sign hashed message")
}
return Sign(priv, message), nil
}
//
// This type is an alias for crypto/ed25519's PrivateKey type.
// See the crypto/ed25519 package for the methods on this type.
type PrivateKey = ed25519.PrivateKey
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
if rand == nil {
rand = cryptorand.Reader
}
seed := make([]byte, SeedSize)
if _, err := io.ReadFull(rand, seed); err != nil {
return nil, nil, err
}
privateKey := NewKeyFromSeed(seed)
publicKey := make([]byte, PublicKeySize)
copy(publicKey, privateKey[32:])
return publicKey, privateKey, nil
return ed25519.GenerateKey(rand)
}
// NewKeyFromSeed calculates a private key from a seed. It will panic if
@@ -103,121 +55,17 @@ func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
// package.
func NewKeyFromSeed(seed []byte) PrivateKey {
if l := len(seed); l != SeedSize {
panic("ed25519: bad seed length: " + strconv.Itoa(l))
}
digest := sha512.Sum512(seed)
digest[0] &= 248
digest[31] &= 127
digest[31] |= 64
var A edwards25519.ExtendedGroupElement
var hBytes [32]byte
copy(hBytes[:], digest[:])
edwards25519.GeScalarMultBase(&A, &hBytes)
var publicKeyBytes [32]byte
A.ToBytes(&publicKeyBytes)
privateKey := make([]byte, PrivateKeySize)
copy(privateKey, seed)
copy(privateKey[32:], publicKeyBytes[:])
return privateKey
return ed25519.NewKeyFromSeed(seed)
}
// Sign signs the message with privateKey and returns a signature. It will
// panic if len(privateKey) is not PrivateKeySize.
func Sign(privateKey PrivateKey, message []byte) []byte {
if l := len(privateKey); l != PrivateKeySize {
panic("ed25519: bad private key length: " + strconv.Itoa(l))
}
h := sha512.New()
h.Write(privateKey[:32])
var digest1, messageDigest, hramDigest [64]byte
var expandedSecretKey [32]byte
h.Sum(digest1[:0])
copy(expandedSecretKey[:], digest1[:])
expandedSecretKey[0] &= 248
expandedSecretKey[31] &= 63
expandedSecretKey[31] |= 64
h.Reset()
h.Write(digest1[32:])
h.Write(message)
h.Sum(messageDigest[:0])
var messageDigestReduced [32]byte
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
var R edwards25519.ExtendedGroupElement
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
var encodedR [32]byte
R.ToBytes(&encodedR)
h.Reset()
h.Write(encodedR[:])
h.Write(privateKey[32:])
h.Write(message)
h.Sum(hramDigest[:0])
var hramDigestReduced [32]byte
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
var s [32]byte
edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
signature := make([]byte, SignatureSize)
copy(signature[:], encodedR[:])
copy(signature[32:], s[:])
return signature
return ed25519.Sign(privateKey, message)
}
// Verify reports whether sig is a valid signature of message by publicKey. It
// will panic if len(publicKey) is not PublicKeySize.
func Verify(publicKey PublicKey, message, sig []byte) bool {
if l := len(publicKey); l != PublicKeySize {
panic("ed25519: bad public key length: " + strconv.Itoa(l))
}
if len(sig) != SignatureSize || sig[63]&224 != 0 {
return false
}
var A edwards25519.ExtendedGroupElement
var publicKeyBytes [32]byte
copy(publicKeyBytes[:], publicKey)
if !A.FromBytes(&publicKeyBytes) {
return false
}
edwards25519.FeNeg(&A.X, &A.X)
edwards25519.FeNeg(&A.T, &A.T)
h := sha512.New()
h.Write(sig[:32])
h.Write(publicKey[:])
h.Write(message)
var digest [64]byte
h.Sum(digest[:0])
var hReduced [32]byte
edwards25519.ScReduce(&hReduced, &digest)
var R edwards25519.ProjectiveGroupElement
var s [32]byte
copy(s[:], sig[32:])
// https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in
// the range [0, order) in order to prevent signature malleability.
if !edwards25519.ScMinimal(&s) {
return false
}
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &s)
var checkR [32]byte
R.ToBytes(&checkR)
return bytes.Equal(sig[:32], checkR[:])
return ed25519.Verify(publicKey, message, sig)
}
-74
View File
@@ -1,74 +0,0 @@
// Copyright 2019 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.13
// +build go1.13
// Package ed25519 implements the Ed25519 signature algorithm. See
// https://ed25519.cr.yp.to/.
//
// These functions are also compatible with the “Ed25519” function defined in
// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
// representation includes a public key suffix to make multiple signing
// operations with the same key more efficient. This package refers to the RFC
// 8032 private key as the “seed”.
//
// Beginning with Go 1.13, the functionality of this package was moved to the
// standard library as crypto/ed25519. This package only acts as a compatibility
// wrapper.
package ed25519
import (
"crypto/ed25519"
"io"
)
const (
// PublicKeySize is the size, in bytes, of public keys as used in this package.
PublicKeySize = 32
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
PrivateKeySize = 64
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
SignatureSize = 64
// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
SeedSize = 32
)
// PublicKey is the type of Ed25519 public keys.
//
// This type is an alias for crypto/ed25519's PublicKey type.
// See the crypto/ed25519 package for the methods on this type.
type PublicKey = ed25519.PublicKey
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
//
// This type is an alias for crypto/ed25519's PrivateKey type.
// See the crypto/ed25519 package for the methods on this type.
type PrivateKey = ed25519.PrivateKey
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
return ed25519.GenerateKey(rand)
}
// NewKeyFromSeed calculates a private key from a seed. It will panic if
// len(seed) is not SeedSize. This function is provided for interoperability
// with RFC 8032. RFC 8032's private keys correspond to seeds in this
// package.
func NewKeyFromSeed(seed []byte) PrivateKey {
return ed25519.NewKeyFromSeed(seed)
}
// Sign signs the message with privateKey and returns a signature. It will
// panic if len(privateKey) is not PrivateKeySize.
func Sign(privateKey PrivateKey, message []byte) []byte {
return ed25519.Sign(privateKey, message)
}
// Verify reports whether sig is a valid signature of message by publicKey. It
// will panic if len(publicKey) is not PublicKeySize.
func Verify(publicKey PublicKey, message, sig []byte) bool {
return ed25519.Verify(publicKey, message, sig)
}
+4 -203
View File
@@ -1,221 +1,22 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Copyright 2019 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 ed25519_test
import (
"bufio"
"bytes"
"compress/gzip"
"crypto"
"crypto/rand"
"encoding/hex"
"os"
"strings"
ed25519std "crypto/ed25519"
"testing"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/ed25519/internal/edwards25519"
)
type zeroReader struct{}
func (zeroReader) Read(buf []byte) (int, error) {
for i := range buf {
buf[i] = 0
}
return len(buf), nil
}
func TestUnmarshalMarshal(t *testing.T) {
pub, _, _ := ed25519.GenerateKey(rand.Reader)
var A edwards25519.ExtendedGroupElement
var pubBytes [32]byte
copy(pubBytes[:], pub)
if !A.FromBytes(&pubBytes) {
t.Fatalf("ExtendedGroupElement.FromBytes failed")
}
var pub2 [32]byte
A.ToBytes(&pub2)
if pubBytes != pub2 {
t.Errorf("FromBytes(%v)->ToBytes does not round-trip, got %x\n", pubBytes, pub2)
}
}
func TestSignVerify(t *testing.T) {
var zero zeroReader
public, private, _ := ed25519.GenerateKey(zero)
func TestTypeAlias(t *testing.T) {
public, private, _ := ed25519std.GenerateKey(nil)
message := []byte("test message")
sig := ed25519.Sign(private, message)
if !ed25519.Verify(public, message, sig) {
t.Errorf("valid signature rejected")
}
wrongMessage := []byte("wrong message")
if ed25519.Verify(public, wrongMessage, sig) {
t.Errorf("signature of different message accepted")
}
}
func TestCryptoSigner(t *testing.T) {
var zero zeroReader
public, private, _ := ed25519.GenerateKey(zero)
signer := crypto.Signer(private)
publicInterface := signer.Public()
public2, ok := publicInterface.(ed25519.PublicKey)
if !ok {
t.Fatalf("expected PublicKey from Public() but got %T", publicInterface)
}
if !bytes.Equal(public, public2) {
t.Errorf("public keys do not match: original:%x vs Public():%x", public, public2)
}
message := []byte("message")
var noHash crypto.Hash
signature, err := signer.Sign(zero, message, noHash)
if err != nil {
t.Fatalf("error from Sign(): %s", err)
}
if !ed25519.Verify(public, message, signature) {
t.Errorf("Verify failed on signature from Sign()")
}
}
func TestGolden(t *testing.T) {
// sign.input.gz is a selection of test cases from
// https://ed25519.cr.yp.to/python/sign.input
testDataZ, err := os.Open("testdata/sign.input.gz")
if err != nil {
t.Fatal(err)
}
defer testDataZ.Close()
testData, err := gzip.NewReader(testDataZ)
if err != nil {
t.Fatal(err)
}
defer testData.Close()
scanner := bufio.NewScanner(testData)
lineNo := 0
for scanner.Scan() {
lineNo++
line := scanner.Text()
parts := strings.Split(line, ":")
if len(parts) != 5 {
t.Fatalf("bad number of parts on line %d", lineNo)
}
privBytes, _ := hex.DecodeString(parts[0])
pubKey, _ := hex.DecodeString(parts[1])
msg, _ := hex.DecodeString(parts[2])
sig, _ := hex.DecodeString(parts[3])
// The signatures in the test vectors also include the message
// at the end, but we just want R and S.
sig = sig[:ed25519.SignatureSize]
if l := len(pubKey); l != ed25519.PublicKeySize {
t.Fatalf("bad public key length on line %d: got %d bytes", lineNo, l)
}
var priv [ed25519.PrivateKeySize]byte
copy(priv[:], privBytes)
copy(priv[32:], pubKey)
sig2 := ed25519.Sign(priv[:], msg)
if !bytes.Equal(sig, sig2[:]) {
t.Errorf("different signature result on line %d: %x vs %x", lineNo, sig, sig2)
}
if !ed25519.Verify(pubKey, msg, sig2) {
t.Errorf("signature failed to verify on line %d", lineNo)
}
priv2 := ed25519.NewKeyFromSeed(priv[:32])
if !bytes.Equal(priv[:], priv2) {
t.Errorf("recreating key pair gave different private key on line %d: %x vs %x", lineNo, priv[:], priv2)
}
if pubKey2 := priv2.Public().(ed25519.PublicKey); !bytes.Equal(pubKey, pubKey2) {
t.Errorf("recreating key pair gave different public key on line %d: %x vs %x", lineNo, pubKey, pubKey2)
}
if seed := priv2.Seed(); !bytes.Equal(priv[:32], seed) {
t.Errorf("recreating key pair gave different seed on line %d: %x vs %x", lineNo, priv[:32], seed)
}
}
if err := scanner.Err(); err != nil {
t.Fatalf("error reading test data: %s", err)
}
}
func TestMalleability(t *testing.T) {
// https://tools.ietf.org/html/rfc8032#section-5.1.7 adds an additional test
// that s be in [0, order). This prevents someone from adding a multiple of
// order to s and obtaining a second valid signature for the same message.
msg := []byte{0x54, 0x65, 0x73, 0x74}
sig := []byte{
0x7c, 0x38, 0xe0, 0x26, 0xf2, 0x9e, 0x14, 0xaa, 0xbd, 0x05, 0x9a,
0x0f, 0x2d, 0xb8, 0xb0, 0xcd, 0x78, 0x30, 0x40, 0x60, 0x9a, 0x8b,
0xe6, 0x84, 0xdb, 0x12, 0xf8, 0x2a, 0x27, 0x77, 0x4a, 0xb0, 0x67,
0x65, 0x4b, 0xce, 0x38, 0x32, 0xc2, 0xd7, 0x6f, 0x8f, 0x6f, 0x5d,
0xaf, 0xc0, 0x8d, 0x93, 0x39, 0xd4, 0xee, 0xf6, 0x76, 0x57, 0x33,
0x36, 0xa5, 0xc5, 0x1e, 0xb6, 0xf9, 0x46, 0xb3, 0x1d,
}
publicKey := []byte{
0x7d, 0x4d, 0x0e, 0x7f, 0x61, 0x53, 0xa6, 0x9b, 0x62, 0x42, 0xb5,
0x22, 0xab, 0xbe, 0xe6, 0x85, 0xfd, 0xa4, 0x42, 0x0f, 0x88, 0x34,
0xb1, 0x08, 0xc3, 0xbd, 0xae, 0x36, 0x9e, 0xf5, 0x49, 0xfa,
}
if ed25519.Verify(publicKey, msg, sig) {
t.Fatal("non-canonical signature accepted")
}
}
func BenchmarkKeyGeneration(b *testing.B) {
var zero zeroReader
for i := 0; i < b.N; i++ {
if _, _, err := ed25519.GenerateKey(zero); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkSigning(b *testing.B) {
var zero zeroReader
_, priv, err := ed25519.GenerateKey(zero)
if err != nil {
b.Fatal(err)
}
message := []byte("Hello, world!")
b.ResetTimer()
for i := 0; i < b.N; i++ {
ed25519.Sign(priv, message)
}
}
func BenchmarkVerification(b *testing.B) {
var zero zeroReader
pub, priv, err := ed25519.GenerateKey(zero)
if err != nil {
b.Fatal(err)
}
message := []byte("Hello, world!")
signature := ed25519.Sign(priv, message)
b.ResetTimer()
for i := 0; i < b.N; i++ {
ed25519.Verify(pub, message, signature)
}
}
-25
View File
@@ -1,25 +0,0 @@
// Copyright 2019 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.13
// +build go1.13
package ed25519_test
import (
ed25519std "crypto/ed25519"
"golang.org/x/crypto/ed25519"
"testing"
)
func TestTypeAlias(t *testing.T) {
var zero zeroReader
public, private, _ := ed25519std.GenerateKey(zero)
message := []byte("test message")
sig := ed25519.Sign(private, message)
if !ed25519.Verify(public, message, sig) {
t.Errorf("valid signature rejected")
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+4 -4
View File
@@ -3,9 +3,9 @@ module golang.org/x/crypto
go 1.17
require (
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
golang.org/x/net v0.2.0
golang.org/x/sys v0.2.0
golang.org/x/term v0.2.0
)
require golang.org/x/text v0.3.6 // indirect
require golang.org/x/text v0.4.0 // indirect
+27 -7
View File
@@ -1,11 +1,31 @@
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
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.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
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.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
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=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -5,9 +5,8 @@
//go:build !purego
// +build !purego
// Package subtle implements functions that are often useful in cryptographic
// code but require careful thought to use correctly.
package subtle // import "golang.org/x/crypto/internal/subtle"
// Package alias implements memory aliasing tests.
package alias
import "unsafe"
@@ -5,9 +5,8 @@
//go:build purego
// +build purego
// Package subtle implements functions that are often useful in cryptographic
// code but require careful thought to use correctly.
package subtle // import "golang.org/x/crypto/internal/subtle"
// Package alias implements memory aliasing tests.
package alias
// This is the Google App Engine standard variant based on reflect
// because the unsafe package and cgo are disallowed.
@@ -2,13 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package subtle_test
package alias
import (
"testing"
"golang.org/x/crypto/internal/subtle"
)
import "testing"
var a, b [100]byte
@@ -32,11 +28,11 @@ var aliasingTests = []struct {
}
func testAliasing(t *testing.T, i int, x, y []byte, anyOverlap, inexactOverlap bool) {
any := subtle.AnyOverlap(x, y)
any := AnyOverlap(x, y)
if any != anyOverlap {
t.Errorf("%d: wrong AnyOverlap result, expected %v, got %v", i, anyOverlap, any)
}
inexact := subtle.InexactOverlap(x, y)
inexact := InexactOverlap(x, y)
if inexact != inexactOverlap {
t.Errorf("%d: wrong InexactOverlap result, expected %v, got %v", i, inexactOverlap, any)
}
+2 -3
View File
@@ -136,7 +136,7 @@ func shiftRightBy2(a uint128) uint128 {
// updateGeneric absorbs msg into the state.h accumulator. For each chunk m of
// 128 bits of message, it computes
//
// h₊ = (h + m) * r mod 2¹³⁰ - 5
// h₊ = (h + m) * r mod 2¹³⁰ - 5
//
// If the msg length is not a multiple of TagSize, it assumes the last
// incomplete chunk is the final one.
@@ -278,8 +278,7 @@ const (
// finalize completes the modular reduction of h and computes
//
// out = h + s mod 2¹²⁸
//
// out = h + s mod 2¹²⁸
func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) {
h0, h1, h2 := h[0], h[1], h[2]
+1
View File
@@ -14,6 +14,7 @@ import (
// updateVX is an assembly implementation of Poly1305 that uses vector
// instructions. It must only be called if the vector facility (vx) is
// available.
//
//go:noescape
func updateVX(state *macState, msg []byte)
+9
View File
@@ -0,0 +1,9 @@
// Copyright 2022 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 boringcrypto
package wycheproof
const boringcryptoEnabled = true
+142
View File
@@ -0,0 +1,142 @@
// Copyright 2022 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 wycheproof
import (
"bytes"
"crypto/ecdh"
"fmt"
"testing"
)
func TestECDHStdLib(t *testing.T) {
type ECDHTestVector struct {
// A brief description of the test case
Comment string `json:"comment,omitempty"`
// A list of flags
Flags []string `json:"flags,omitempty"`
// the private key
Private string `json:"private,omitempty"`
// Encoded public key
Public string `json:"public,omitempty"`
// Test result
Result string `json:"result,omitempty"`
// The shared secret key
Shared string `json:"shared,omitempty"`
// Identifier of the test case
TcID int `json:"tcId,omitempty"`
}
type ECDHTestGroup struct {
Curve string `json:"curve,omitempty"`
Tests []*ECDHTestVector `json:"tests,omitempty"`
}
type Root struct {
TestGroups []*ECDHTestGroup `json:"testGroups,omitempty"`
}
flagsShouldPass := map[string]bool{
// We don't support compressed points.
"CompressedPoint": false,
// We don't support decoding custom curves.
"UnnamedCurve": false,
// WrongOrder and UnusedParam are only found with UnnamedCurve.
"WrongOrder": false,
"UnusedParam": false,
// X25519 specific flags
"Twist": true,
"SmallPublicKey": false,
"LowOrderPublic": false,
"ZeroSharedSecret": false,
"NonCanonicalPublic": true,
}
// curveToCurve is a map of all elliptic curves supported
// by crypto/elliptic, which can subsequently be parsed and tested.
curveToCurve := map[string]ecdh.Curve{
"secp256r1": ecdh.P256(),
"secp384r1": ecdh.P384(),
"secp521r1": ecdh.P521(),
"curve25519": ecdh.X25519(),
}
curveToKeySize := map[string]int{
"secp256r1": 32,
"secp384r1": 48,
"secp521r1": 66,
"curve25519": 32,
}
for _, f := range []string{
"ecdh_secp256r1_ecpoint_test.json",
"ecdh_secp384r1_ecpoint_test.json",
"ecdh_secp521r1_ecpoint_test.json",
"x25519_test.json",
} {
var root Root
readTestVector(t, f, &root)
for _, tg := range root.TestGroups {
if _, ok := curveToCurve[tg.Curve]; !ok {
continue
}
for _, tt := range tg.Tests {
tg, tt := tg, tt
t.Run(fmt.Sprintf("%s/%d", tg.Curve, tt.TcID), func(t *testing.T) {
t.Logf("Type: %v", tt.Result)
t.Logf("Flags: %q", tt.Flags)
t.Log(tt.Comment)
shouldPass := shouldPass(tt.Result, tt.Flags, flagsShouldPass)
curve := curveToCurve[tg.Curve]
p := decodeHex(tt.Public)
pub, err := curve.NewPublicKey(p)
if err != nil {
if shouldPass {
t.Errorf("NewPublicKey: %v", err)
}
return
}
privBytes := decodeHex(tt.Private)
if len(privBytes) != curveToKeySize[tg.Curve] {
t.Skipf("non-standard key size %d", len(privBytes))
}
priv, err := curve.NewPrivateKey(privBytes)
if err != nil {
if shouldPass {
t.Errorf("NewPrivateKey: %v", err)
}
return
}
shared := decodeHex(tt.Shared)
x, err := curve.ECDH(priv, pub)
if err != nil {
if tg.Curve == "curve25519" && !shouldPass {
// ECDH is expected to only return an error when using X25519,
// in all other cases an error is unexpected.
return
}
t.Fatalf("ECDH: %v", err)
}
if bytes.Equal(shared, x) != shouldPass {
if shouldPass {
t.Errorf("ECDH = %x, want %x", shared, x)
} else {
t.Errorf("ECDH = %x, want anything else", shared)
}
}
})
}
}
}
}
+163
View File
@@ -0,0 +1,163 @@
// Copyright 2019 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 wycheproof
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/x509"
"encoding/asn1"
"errors"
"fmt"
"testing"
"golang.org/x/crypto/cryptobyte"
casn1 "golang.org/x/crypto/cryptobyte/asn1"
)
func TestECDH(t *testing.T) {
type ECDHTestVector struct {
// A brief description of the test case
Comment string `json:"comment,omitempty"`
// A list of flags
Flags []string `json:"flags,omitempty"`
// the private key
Private string `json:"private,omitempty"`
// Encoded public key
Public string `json:"public,omitempty"`
// Test result
Result string `json:"result,omitempty"`
// The shared secret key
Shared string `json:"shared,omitempty"`
// Identifier of the test case
TcID int `json:"tcId,omitempty"`
}
type ECDHTestGroup struct {
Curve string `json:"curve,omitempty"`
Tests []*ECDHTestVector `json:"tests,omitempty"`
}
type Root struct {
TestGroups []*ECDHTestGroup `json:"testGroups,omitempty"`
}
flagsShouldPass := map[string]bool{
// ParsePKIXPublicKey doesn't support compressed points, but we test
// them against UnmarshalCompressed anyway.
"CompressedPoint": true,
// We don't support decoding custom curves.
"UnnamedCurve": false,
// WrongOrder and UnusedParam are only found with UnnamedCurve.
"WrongOrder": false,
"UnusedParam": false,
}
// supportedCurves is a map of all elliptic curves supported
// by crypto/elliptic, which can subsequently be parsed and tested.
supportedCurves := map[string]bool{
"secp224r1": true,
"secp256r1": true,
"secp384r1": true,
"secp521r1": true,
}
var root Root
readTestVector(t, "ecdh_test.json", &root)
for _, tg := range root.TestGroups {
if !supportedCurves[tg.Curve] {
continue
}
for _, tt := range tg.Tests {
tg, tt := tg, tt
t.Run(fmt.Sprintf("%s/%d", tg.Curve, tt.TcID), func(t *testing.T) {
t.Logf("Type: %v", tt.Result)
t.Logf("Flags: %q", tt.Flags)
t.Log(tt.Comment)
shouldPass := shouldPass(tt.Result, tt.Flags, flagsShouldPass)
p := decodeHex(tt.Public)
pp, err := x509.ParsePKIXPublicKey(p)
if err != nil {
pp, err = decodeCompressedPKIX(p)
}
if err != nil {
if shouldPass {
t.Errorf("unexpected parsing error: %s", err)
}
return
}
pub := pp.(*ecdsa.PublicKey)
priv := decodeHex(tt.Private)
shared := decodeHex(tt.Shared)
x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, priv)
xBytes := make([]byte, (pub.Curve.Params().BitSize+7)/8)
got := bytes.Equal(shared, x.FillBytes(xBytes))
if want := shouldPass; got != want {
t.Errorf("wanted success %v, got %v", want, got)
}
})
}
}
}
func decodeCompressedPKIX(der []byte) (interface{}, error) {
s := cryptobyte.String(der)
var s1, s2 cryptobyte.String
var algoOID, namedCurveOID asn1.ObjectIdentifier
var pointDER []byte
if !s.ReadASN1(&s1, casn1.SEQUENCE) || !s.Empty() ||
!s1.ReadASN1(&s2, casn1.SEQUENCE) ||
!s2.ReadASN1ObjectIdentifier(&algoOID) ||
!s2.ReadASN1ObjectIdentifier(&namedCurveOID) || !s2.Empty() ||
!s1.ReadASN1BitStringAsBytes(&pointDER) || !s1.Empty() {
return nil, errors.New("failed to parse PKIX structure")
}
if !algoOID.Equal(oidPublicKeyECDSA) {
return nil, errors.New("wrong algorithm OID")
}
namedCurve := namedCurveFromOID(namedCurveOID)
if namedCurve == nil {
return nil, errors.New("unsupported elliptic curve")
}
x, y := elliptic.UnmarshalCompressed(namedCurve, pointDER)
if x == nil {
return nil, errors.New("failed to unmarshal elliptic curve point")
}
pub := &ecdsa.PublicKey{
Curve: namedCurve,
X: x,
Y: y,
}
return pub, nil
}
var (
oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33}
oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
)
func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
switch {
case oid.Equal(oidNamedCurveP224):
return elliptic.P224()
case oid.Equal(oidNamedCurveP256):
return elliptic.P256()
case oid.Equal(oidNamedCurveP384):
return elliptic.P384()
case oid.Equal(oidNamedCurveP521):
return elliptic.P521()
}
return nil
}
-34
View File
@@ -1,34 +0,0 @@
// Copyright 2020 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.15
// +build !go1.15
// ecdsa.VerifyASN1 was added in Go 1.15.
package wycheproof
import (
"crypto/ecdsa"
"math/big"
"golang.org/x/crypto/cryptobyte"
"golang.org/x/crypto/cryptobyte/asn1"
)
func verifyASN1(pub *ecdsa.PublicKey, hash, sig []byte) bool {
var (
r, s = &big.Int{}, &big.Int{}
inner cryptobyte.String
)
input := cryptobyte.String(sig)
if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
!input.Empty() ||
!inner.ReadASN1Integer(r) ||
!inner.ReadASN1Integer(s) ||
!inner.Empty() {
return false
}
return ecdsa.Verify(pub, hash, r, s)
}
-16
View File
@@ -1,16 +0,0 @@
// Copyright 2020 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.15
// +build go1.15
package wycheproof
import (
"crypto/ecdsa"
)
func verifyASN1(pub *ecdsa.PublicKey, hash, sig []byte) bool {
return ecdsa.VerifyASN1(pub, hash, sig)
}
+27 -106
View File
@@ -9,126 +9,47 @@ import (
"testing"
)
func TestEcdsa(t *testing.T) {
// AsnSignatureTestVector
type AsnSignatureTestVector struct {
func TestECDSA(t *testing.T) {
type ASNSignatureTestVector struct {
// A brief description of the test case
Comment string `json:"comment,omitempty"`
Comment string `json:"comment"`
// A list of flags
Flags []string `json:"flags,omitempty"`
Flags []string `json:"flags"`
// The message to sign
Msg string `json:"msg,omitempty"`
Msg string `json:"msg"`
// Test result
Result string `json:"result,omitempty"`
// An ASN encoded signature for msg
Sig string `json:"sig,omitempty"`
Result string `json:"result"`
// An ASN.1 encoded signature for msg
Sig string `json:"sig"`
// Identifier of the test case
TcId int `json:"tcId,omitempty"`
TcID int `json:"tcId"`
}
// EcPublicKey
type EcPublicKey struct {
// the EC group used by this public key
Curve interface{} `json:"curve,omitempty"`
// the key size in bits
KeySize int `json:"keySize,omitempty"`
// the key type
Type string `json:"type,omitempty"`
// encoded public key point
Uncompressed string `json:"uncompressed,omitempty"`
// the x-coordinate of the public key point
Wx string `json:"wx,omitempty"`
// the y-coordinate of the public key point
Wy string `json:"wy,omitempty"`
type ECPublicKey struct {
// The EC group used by this public key
Curve interface{} `json:"curve"`
}
// EcUnnamedGroup
type EcUnnamedGroup struct {
// coefficient a of the elliptic curve equation
A string `json:"a,omitempty"`
// coefficient b of the elliptic curve equation
B string `json:"b,omitempty"`
// the x-coordinate of the generator
Gx string `json:"gx,omitempty"`
// the y-coordinate of the generator
Gy string `json:"gy,omitempty"`
// the cofactor
H int `json:"h,omitempty"`
// the order of the generator
N string `json:"n,omitempty"`
// the order of the underlying field
P string `json:"p,omitempty"`
// an unnamed EC group over a prime field in Weierstrass form
Type string `json:"type,omitempty"`
}
// EcdsaTestGroup
type EcdsaTestGroup struct {
// unenocded EC public key
Key *EcPublicKey `json:"key,omitempty"`
type ECDSATestGroup struct {
// Unencoded EC public key
Key *ECPublicKey `json:"key"`
// DER encoded public key
KeyDer string `json:"keyDer,omitempty"`
// Pem encoded public key
KeyPem string `json:"keyPem,omitempty"`
KeyDER string `json:"keyDer"`
// the hash function used for ECDSA
Sha string `json:"sha,omitempty"`
Tests []*AsnSignatureTestVector `json:"tests,omitempty"`
Type interface{} `json:"type,omitempty"`
SHA string `json:"sha"`
Tests []*ASNSignatureTestVector `json:"tests"`
}
// Notes a description of the labels used in the test vectors
type Notes struct {
}
// Root
type Root struct {
// the primitive tested in the test file
Algorithm string `json:"algorithm,omitempty"`
// the version of the test vectors.
GeneratorVersion string `json:"generatorVersion,omitempty"`
// additional documentation
Header []string `json:"header,omitempty"`
// a description of the labels used in the test vectors
Notes *Notes `json:"notes,omitempty"`
// the number of test vectors in this test
NumberOfTests int `json:"numberOfTests,omitempty"`
Schema interface{} `json:"schema,omitempty"`
TestGroups []*EcdsaTestGroup `json:"testGroups,omitempty"`
TestGroups []*ECDSATestGroup `json:"testGroups"`
}
flagsShouldPass := map[string]bool{
// An encoded ASN.1 integer missing a leading zero is invalid, but accepted by some implementations.
// An encoded ASN.1 integer missing a leading zero is invalid, but
// accepted by some implementations.
"MissingZero": false,
// A signature using a weaker hash than the EC params is not a security risk, as long as the hash is secure.
// A signature using a weaker hash than the EC params is not a security
// risk, as long as the hash is secure.
// https://www.imperialviolet.org/2014/05/25/strengthmatching.html
"WeakHash": true,
}
@@ -149,15 +70,15 @@ func TestEcdsa(t *testing.T) {
if !supportedCurves[curve] {
continue
}
pub := decodePublicKey(tg.KeyDer).(*ecdsa.PublicKey)
h := parseHash(tg.Sha).New()
pub := decodePublicKey(tg.KeyDER).(*ecdsa.PublicKey)
h := parseHash(tg.SHA).New()
for _, sig := range tg.Tests {
h.Reset()
h.Write(decodeHex(sig.Msg))
hashed := h.Sum(nil)
got := verifyASN1(pub, hashed, decodeHex(sig.Sig))
got := ecdsa.VerifyASN1(pub, hashed, decodeHex(sig.Sig))
if want := shouldPass(sig.Result, sig.Flags, flagsShouldPass); got != want {
t.Errorf("tcid: %d, type: %s, comment: %q, wanted success: %t", sig.TcId, sig.Result, sig.Comment, want)
t.Errorf("tcid: %d, type: %s, comment: %q, wanted success: %t", sig.TcID, sig.Result, sig.Comment, want)
}
}
}
+9
View File
@@ -0,0 +1,9 @@
// Copyright 2022 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 !boringcrypto
package wycheproof
const boringcryptoEnabled = false
+13 -8
View File
@@ -112,17 +112,22 @@ func TestRsaPss(t *testing.T) {
// works deterministically to auto-detect the length when
// verifying, so these tests actually pass as they should.
filesOverrideToPassZeroSLen := map[string][]int{
"rsa_pss_2048_sha1_mgf1_20_test.json": []int{46, 47},
"rsa_pss_2048_sha256_mgf1_0_test.json": []int{67, 68},
"rsa_pss_2048_sha256_mgf1_32_test.json": []int{67, 68},
"rsa_pss_2048_sha512_256_mgf1_28_test.json": []int{13, 14, 15},
"rsa_pss_2048_sha512_256_mgf1_32_test.json": []int{13, 14},
"rsa_pss_3072_sha256_mgf1_32_test.json": []int{67, 68},
"rsa_pss_4096_sha256_mgf1_32_test.json": []int{67, 68},
"rsa_pss_4096_sha512_mgf1_32_test.json": []int{136, 137},
"rsa_pss_2048_sha1_mgf1_20_test.json": []int{46, 47},
"rsa_pss_2048_sha256_mgf1_0_test.json": []int{67, 68},
"rsa_pss_2048_sha256_mgf1_32_test.json": []int{67, 68},
"rsa_pss_3072_sha256_mgf1_32_test.json": []int{67, 68},
"rsa_pss_4096_sha256_mgf1_32_test.json": []int{67, 68},
"rsa_pss_4096_sha512_mgf1_32_test.json": []int{136, 137},
// "rsa_pss_misc_test.json": nil, // TODO: This ones seems to be broken right now, but can enable later on.
}
if !boringcryptoEnabled {
// boringcrypto doesn't support the truncated SHA-512 hashes, so only
// test them if boringcrypto isn't enabled.
filesOverrideToPassZeroSLen["rsa_pss_2048_sha512_256_mgf1_28_test.json"] = []int{13, 14, 15}
filesOverrideToPassZeroSLen["rsa_pss_2048_sha512_256_mgf1_32_test.json"] = []int{13, 14}
}
for f := range filesOverrideToPassZeroSLen {
var root Root
readTestVector(t, f, &root)
+1 -2
View File
@@ -12,7 +12,6 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
@@ -62,7 +61,7 @@ func TestMain(m *testing.M) {
}
func readTestVector(t *testing.T, f string, dest interface{}) {
b, err := ioutil.ReadFile(filepath.Join(wycheproofTestVectorsDir, f))
b, err := os.ReadFile(filepath.Join(wycheproofTestVectorsDir, f))
if err != nil {
t.Fatalf("failed to read json file: %v", err)
}
+8 -6
View File
@@ -8,9 +8,11 @@
package md4
var shift1 = []uint{3, 7, 11, 19}
var shift2 = []uint{3, 5, 9, 13}
var shift3 = []uint{3, 9, 11, 15}
import "math/bits"
var shift1 = []int{3, 7, 11, 19}
var shift2 = []int{3, 5, 9, 13}
var shift3 = []int{3, 9, 11, 15}
var xIndex2 = []uint{0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15}
var xIndex3 = []uint{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}
@@ -48,7 +50,7 @@ func _Block(dig *digest, p []byte) int {
s := shift1[i%4]
f := ((c ^ d) & b) ^ d
a += f + X[x]
a = a<<s | a>>(32-s)
a = bits.RotateLeft32(a, s)
a, b, c, d = d, a, b, c
}
@@ -58,7 +60,7 @@ func _Block(dig *digest, p []byte) int {
s := shift2[i%4]
g := (b & c) | (b & d) | (c & d)
a += g + X[x] + 0x5a827999
a = a<<s | a>>(32-s)
a = bits.RotateLeft32(a, s)
a, b, c, d = d, a, b, c
}
@@ -68,7 +70,7 @@ func _Block(dig *digest, p []byte) int {
s := shift3[i%4]
h := b ^ c ^ d
a += h + X[x] + 0x6ed9eba1
a = a<<s | a>>(32-s)
a = bits.RotateLeft32(a, s)
a, b, c, d = d, a, b, c
}
+3 -3
View File
@@ -35,8 +35,8 @@ This package is interoperable with NaCl: https://nacl.cr.yp.to/secretbox.html.
package secretbox // import "golang.org/x/crypto/nacl/secretbox"
import (
"golang.org/x/crypto/internal/alias"
"golang.org/x/crypto/internal/poly1305"
"golang.org/x/crypto/internal/subtle"
"golang.org/x/crypto/salsa20/salsa"
)
@@ -88,7 +88,7 @@ func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte {
copy(poly1305Key[:], firstBlock[:])
ret, out := sliceForAppend(out, len(message)+poly1305.TagSize)
if subtle.AnyOverlap(out, message) {
if alias.AnyOverlap(out, message) {
panic("nacl: invalid buffer overlap")
}
@@ -147,7 +147,7 @@ func Open(out, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) {
}
ret, out := sliceForAppend(out, len(box)-Overhead)
if subtle.AnyOverlap(out, box) {
if alias.AnyOverlap(out, box) {
panic("nacl: invalid buffer overlap")
}
+3 -3
View File
@@ -24,7 +24,7 @@ import (
"io"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/internal/subtle"
"golang.org/x/crypto/internal/alias"
)
// Overhead is the number of bytes of overhead when signing a message.
@@ -48,7 +48,7 @@ func GenerateKey(rand io.Reader) (publicKey *[32]byte, privateKey *[64]byte, err
func Sign(out, message []byte, privateKey *[64]byte) []byte {
sig := ed25519.Sign(ed25519.PrivateKey((*privateKey)[:]), message)
ret, out := sliceForAppend(out, Overhead+len(message))
if subtle.AnyOverlap(out, message) {
if alias.AnyOverlap(out, message) {
panic("nacl: invalid buffer overlap")
}
copy(out, sig)
@@ -67,7 +67,7 @@ func Open(out, signedMessage []byte, publicKey *[32]byte) ([]byte, bool) {
return nil, false
}
ret, out := sliceForAppend(out, len(signedMessage)-Overhead)
if subtle.AnyOverlap(out, signedMessage) {
if alias.AnyOverlap(out, signedMessage) {
panic("nacl: invalid buffer overlap")
}
copy(out, signedMessage[Overhead:])
+4 -1
View File
@@ -345,6 +345,8 @@ func (req *Request) Marshal() ([]byte, error) {
// Response represents an OCSP response containing a single SingleResponse. See
// RFC 6960.
type Response struct {
Raw []byte
// Status is one of {Good, Revoked, Unknown}
Status int
SerialNumber *big.Int
@@ -518,6 +520,7 @@ func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Respon
}
ret := &Response{
Raw: bytes,
TBSResponseData: basicResp.TBSResponseData.Raw,
Signature: basicResp.Signature.RightAlign(),
SignatureAlgorithm: getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm),
@@ -668,7 +671,7 @@ func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte
// The responder cert is used to populate the responder's name field, and the
// certificate itself is provided alongside the OCSP response signature.
//
// The issuer cert is used to puplate the IssuerNameHash and IssuerKeyHash fields.
// The issuer cert is used to populate the IssuerNameHash and IssuerKeyHash fields.
//
// The template is used to populate the SerialNumber, Status, RevokedAt,
// RevocationReason, ThisUpdate, and NextUpdate fields.
+7 -5
View File
@@ -23,12 +23,14 @@ import (
// A Block represents an OpenPGP armored structure.
//
// The encoded form is:
// -----BEGIN Type-----
// Headers
//
// base64-encoded Bytes
// '=' base64 encoded checksum
// -----END Type-----
// -----BEGIN Type-----
// Headers
//
// base64-encoded Bytes
// '=' base64 encoded checksum
// -----END Type-----
//
// where Headers is a possibly empty sequence of Key: Value lines.
//
// Since the armored data can be very large, this package presents a streaming
+2 -2
View File
@@ -7,7 +7,7 @@ package armor
import (
"bytes"
"hash/adler32"
"io/ioutil"
"io"
"testing"
)
@@ -29,7 +29,7 @@ func TestDecodeEncode(t *testing.T) {
t.Errorf("result.Header: got:%#v", result.Header)
}
contents, err := ioutil.ReadAll(result.Body)
contents, err := io.ReadAll(result.Body)
if err != nil {
t.Error(err)
}
+2 -1
View File
@@ -96,7 +96,8 @@ func (l *lineBreaker) Close() (err error) {
// trailer.
//
// It's built into a stack of io.Writers:
// encoding -> base64 encoder -> lineBreaker -> out
//
// encoding -> base64 encoder -> lineBreaker -> out
type encoding struct {
out io.Writer
breaker *lineBreaker
+2 -2
View File
@@ -77,8 +77,8 @@ func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err
// returns the plaintext of the message. An error can result only if the
// ciphertext is invalid. Users should keep in mind that this is a padding
// oracle and thus, if exposed to an adaptive chosen ciphertext attack, can
// be used to break the cryptosystem. See ``Chosen Ciphertext Attacks
// Against Protocols Based on the RSA Encryption Standard PKCS #1'', Daniel
// be used to break the cryptosystem. See “Chosen Ciphertext Attacks
// Against Protocols Based on the RSA Encryption Standard PKCS #1”, Daniel
// Bleichenbacher, Advances in Cryptology (Crypto '98),
func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) {
s := new(big.Int).Exp(c1, priv.X, priv.P)
+1 -2
View File
@@ -8,7 +8,6 @@ import (
"bytes"
"encoding/hex"
"io"
"io/ioutil"
"testing"
)
@@ -25,7 +24,7 @@ func TestCompressed(t *testing.T) {
return
}
contents, err := ioutil.ReadAll(c.Body)
contents, err := io.ReadAll(c.Body)
if err != nil && err != io.EOF {
t.Error(err)
return
+1 -2
View File
@@ -7,7 +7,6 @@ package packet
import (
"bytes"
"io"
"io/ioutil"
"golang.org/x/crypto/openpgp/errors"
)
@@ -26,7 +25,7 @@ type OpaquePacket struct {
}
func (op *OpaquePacket) parse(r io.Reader) (err error) {
op.Contents, err = ioutil.ReadAll(r)
op.Contents, err = io.ReadAll(r)
return
}
+2 -3
View File
@@ -10,7 +10,6 @@ import (
"fmt"
"golang.org/x/crypto/openpgp/errors"
"io"
"io/ioutil"
"testing"
)
@@ -100,7 +99,7 @@ var partialLengthReaderTests = []struct {
func TestPartialLengthReader(t *testing.T) {
for i, test := range partialLengthReaderTests {
r := &partialLengthReader{readerFromHex(test.hexInput), 0, true}
out, err := ioutil.ReadAll(r)
out, err := io.ReadAll(r)
if test.err != nil {
if err != test.err {
t.Errorf("%d: expected different error got:%s want:%s", i, err, test.err)
@@ -172,7 +171,7 @@ func TestReadHeader(t *testing.T) {
continue
}
body, err := ioutil.ReadAll(contents)
body, err := io.ReadAll(contents)
if err != nil {
if !test.unexpectedEOF || err != io.ErrUnexpectedEOF {
t.Errorf("%d: unexpected error from contents: %s", i, err)
+1 -2
View File
@@ -13,7 +13,6 @@ import (
"crypto/rsa"
"crypto/sha1"
"io"
"io/ioutil"
"math/big"
"strconv"
"time"
@@ -133,7 +132,7 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
}
}
pk.encryptedData, err = ioutil.ReadAll(r)
pk.encryptedData, err = io.ReadAll(r)
if err != nil {
return
}
+16 -12
View File
@@ -9,7 +9,6 @@ import (
"crypto"
"encoding/hex"
"io"
"io/ioutil"
"testing"
"golang.org/x/crypto/openpgp/armor"
@@ -45,7 +44,7 @@ func TestSignatureV3Reserialize(t *testing.T) {
t.Errorf("error reserializing: %s", err)
return
}
expected, err := ioutil.ReadAll(v3KeyReader(t))
expected, err := io.ReadAll(v3KeyReader(t))
if err != nil {
t.Error(err)
return
@@ -66,18 +65,23 @@ func v3KeyReader(t *testing.T) io.Reader {
// keySigV3Armor is some V3 public key I found in an SKS dump.
// Old: Public Key Packet(tag 6)(141 bytes)
// Ver 4 - new
// Public key creation time - Fri Sep 16 17:13:54 CDT 1994
// Pub alg - unknown(pub 0)
// Unknown public key(pub 0)
//
// Ver 4 - new
// Public key creation time - Fri Sep 16 17:13:54 CDT 1994
// Pub alg - unknown(pub 0)
// Unknown public key(pub 0)
//
// Old: User ID Packet(tag 13)(39 bytes)
// User ID - Armin M. Warda <warda@nephilim.ruhr.de>
//
// User ID - Armin M. Warda <warda@nephilim.ruhr.de>
//
// Old: Signature Packet(tag 2)(149 bytes)
// Ver 4 - new
// Sig type - unknown(05)
// Pub alg - ElGamal Encrypt-Only(pub 16)
// Hash alg - unknown(hash 46)
// Hashed Sub: unknown(sub 81, critical)(1988 bytes)
//
// Ver 4 - new
// Sig type - unknown(05)
// Pub alg - ElGamal Encrypt-Only(pub 16)
// Hash alg - unknown(hash 46)
// Hashed Sub: unknown(sub 81, critical)(1988 bytes)
const keySigV3Armor = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: SKS 1.0.10
@@ -8,7 +8,6 @@ import (
"bytes"
"encoding/hex"
"io"
"io/ioutil"
"testing"
)
@@ -46,7 +45,7 @@ func TestSymmetricKeyEncrypted(t *testing.T) {
return
}
contents, err := ioutil.ReadAll(r)
contents, err := io.ReadAll(r)
if err != nil && err != io.EOF {
t.Error(err)
return
+1 -1
View File
@@ -236,7 +236,7 @@ func (w *seMDCWriter) Close() (err error) {
return w.w.Close()
}
// noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
// noOpCloser is like an io.NopCloser, but for an io.Writer.
type noOpCloser struct {
w io.Writer
}
@@ -10,7 +10,6 @@ import (
"encoding/hex"
"golang.org/x/crypto/openpgp/errors"
"io"
"io/ioutil"
"testing"
)
@@ -42,7 +41,7 @@ func testMDCReader(t *testing.T) {
for stride := 1; stride < len(mdcPlaintext)/2; stride++ {
r := &testReader{data: mdcPlaintext, stride: stride}
mdcReader := &seMDCReader{in: r, h: sha1.New()}
body, err := ioutil.ReadAll(mdcReader)
body, err := io.ReadAll(mdcReader)
if err != nil {
t.Errorf("stride: %d, error: %s", stride, err)
continue
@@ -62,7 +61,7 @@ func testMDCReader(t *testing.T) {
r := &testReader{data: mdcPlaintext, stride: 2}
mdcReader := &seMDCReader{in: r, h: sha1.New()}
_, err := ioutil.ReadAll(mdcReader)
_, err := io.ReadAll(mdcReader)
if err != nil {
t.Errorf("corruption test, error: %s", err)
return
+1 -2
View File
@@ -9,7 +9,6 @@ import (
"image"
"image/jpeg"
"io"
"io/ioutil"
)
const UserAttrImageSubpacket = 1
@@ -56,7 +55,7 @@ func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute {
func (uat *UserAttribute) parse(r io.Reader) (err error) {
// RFC 4880, section 5.13
b, err := ioutil.ReadAll(r)
b, err := io.ReadAll(r)
if err != nil {
return
}
+1 -2
View File
@@ -6,7 +6,6 @@ package packet
import (
"io"
"io/ioutil"
"strings"
)
@@ -66,7 +65,7 @@ func NewUserId(name, comment, email string) *UserId {
func (uid *UserId) parse(r io.Reader) (err error) {
// RFC 4880, section 5.11
b, err := ioutil.ReadAll(r)
b, err := io.ReadAll(r)
if err != nil {
return
}
+5 -6
View File
@@ -9,7 +9,6 @@ import (
_ "crypto/sha512"
"encoding/hex"
"io"
"io/ioutil"
"strings"
"testing"
@@ -128,7 +127,7 @@ func checkSignedMessage(t *testing.T, signedHex, expected string) {
t.Errorf("bad MessageDetails: %#v", md)
}
contents, err := ioutil.ReadAll(md.UnverifiedBody)
contents, err := io.ReadAll(md.UnverifiedBody)
if err != nil {
t.Errorf("error reading UnverifiedBody: %s", err)
}
@@ -221,7 +220,7 @@ func TestSignedEncryptedMessage(t *testing.T) {
t.Errorf("#%d: bad MessageDetails: %#v", i, md)
}
contents, err := ioutil.ReadAll(md.UnverifiedBody)
contents, err := io.ReadAll(md.UnverifiedBody)
if err != nil {
t.Errorf("#%d: error reading UnverifiedBody: %s", i, err)
}
@@ -245,7 +244,7 @@ func TestUnspecifiedRecipient(t *testing.T) {
return
}
contents, err := ioutil.ReadAll(md.UnverifiedBody)
contents, err := io.ReadAll(md.UnverifiedBody)
if err != nil {
t.Errorf("error reading UnverifiedBody: %s", err)
}
@@ -280,7 +279,7 @@ func TestSymmetricallyEncrypted(t *testing.T) {
return
}
contents, err := ioutil.ReadAll(md.UnverifiedBody)
contents, err := io.ReadAll(md.UnverifiedBody)
if err != nil {
t.Errorf("ReadAll: %s", err)
}
@@ -454,7 +453,7 @@ func TestSignatureV3Message(t *testing.T) {
return
}
_, err = ioutil.ReadAll(md.UnverifiedBody)
_, err = io.ReadAll(md.UnverifiedBody)
if err != nil {
t.Error(err)
return
+1 -1
View File
@@ -268,7 +268,7 @@ func HashIdToString(id byte) (name string, ok bool) {
return "", false
}
// HashIdToHash returns an OpenPGP hash id which corresponds the given Hash.
// HashToHashId returns an OpenPGP hash id which corresponds the given Hash.
func HashToHashId(h crypto.Hash) (id byte, ok bool) {
for _, m := range hashToHashIdMapping {
if m.hash == h {
+1 -1
View File
@@ -402,7 +402,7 @@ func (s signatureWriter) Close() error {
return s.encryptedData.Close()
}
// noOpCloser is like an ioutil.NopCloser, but for an io.Writer.
// noOpCloser is like an io.NopCloser, but for an io.Writer.
// TODO: we have two of these in OpenPGP packages alone. This probably needs
// to be promoted somewhere more common.
type noOpCloser struct {
+2 -3
View File
@@ -7,7 +7,6 @@ package openpgp
import (
"bytes"
"io"
"io/ioutil"
"testing"
"time"
@@ -245,7 +244,7 @@ func TestEncryption(t *testing.T) {
}
}
plaintext, err := ioutil.ReadAll(md.UnverifiedBody)
plaintext, err := io.ReadAll(md.UnverifiedBody)
if err != nil {
t.Errorf("#%d: error reading encrypted contents: %s", i, err)
continue
@@ -342,7 +341,7 @@ func TestSigning(t *testing.T) {
t.Errorf("#%d: failed to find the signing Entity", i)
}
plaintext, err := ioutil.ReadAll(md.UnverifiedBody)
plaintext, err := io.ReadAll(md.UnverifiedBody)
if err != nil {
t.Errorf("#%d: error reading contents: %v", i, err)
continue
+1 -1
View File
@@ -32,7 +32,7 @@ import (
// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
// doing:
//
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
//
// Remember to get a good random salt. At least 8 bytes is recommended by the
// RFC.
+1 -1
View File
@@ -117,7 +117,7 @@ func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error)
}
ps := decrypted[len(decrypted)-psLen:]
decrypted = decrypted[:len(decrypted)-psLen]
if bytes.Compare(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) != 0 {
if !bytes.Equal(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) {
return nil, ErrDecryption
}
+25 -28
View File
@@ -14,6 +14,7 @@ package rc2
import (
"crypto/cipher"
"encoding/binary"
"math/bits"
)
// The rc2 block size in bytes
@@ -80,10 +81,6 @@ func expandKey(key []byte, t1 int) [64]uint16 {
return k
}
func rotl16(x uint16, b uint) uint16 {
return (x >> (16 - b)) | (x << b)
}
func (c *rc2Cipher) Encrypt(dst, src []byte) {
r0 := binary.LittleEndian.Uint16(src[0:])
@@ -96,22 +93,22 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
for j <= 16 {
// mix r0
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
r0 = rotl16(r0, 1)
r0 = bits.RotateLeft16(r0, 1)
j++
// mix r1
r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
r1 = rotl16(r1, 2)
r1 = bits.RotateLeft16(r1, 2)
j++
// mix r2
r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
r2 = rotl16(r2, 3)
r2 = bits.RotateLeft16(r2, 3)
j++
// mix r3
r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
r3 = rotl16(r3, 5)
r3 = bits.RotateLeft16(r3, 5)
j++
}
@@ -124,22 +121,22 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
for j <= 40 {
// mix r0
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
r0 = rotl16(r0, 1)
r0 = bits.RotateLeft16(r0, 1)
j++
// mix r1
r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
r1 = rotl16(r1, 2)
r1 = bits.RotateLeft16(r1, 2)
j++
// mix r2
r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
r2 = rotl16(r2, 3)
r2 = bits.RotateLeft16(r2, 3)
j++
// mix r3
r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
r3 = rotl16(r3, 5)
r3 = bits.RotateLeft16(r3, 5)
j++
}
@@ -152,22 +149,22 @@ func (c *rc2Cipher) Encrypt(dst, src []byte) {
for j <= 60 {
// mix r0
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
r0 = rotl16(r0, 1)
r0 = bits.RotateLeft16(r0, 1)
j++
// mix r1
r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
r1 = rotl16(r1, 2)
r1 = bits.RotateLeft16(r1, 2)
j++
// mix r2
r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
r2 = rotl16(r2, 3)
r2 = bits.RotateLeft16(r2, 3)
j++
// mix r3
r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
r3 = rotl16(r3, 5)
r3 = bits.RotateLeft16(r3, 5)
j++
}
@@ -188,22 +185,22 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) {
for j >= 44 {
// unmix r3
r3 = rotl16(r3, 16-5)
r3 = bits.RotateLeft16(r3, 16-5)
r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
j--
// unmix r2
r2 = rotl16(r2, 16-3)
r2 = bits.RotateLeft16(r2, 16-3)
r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
j--
// unmix r1
r1 = rotl16(r1, 16-2)
r1 = bits.RotateLeft16(r1, 16-2)
r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
j--
// unmix r0
r0 = rotl16(r0, 16-1)
r0 = bits.RotateLeft16(r0, 16-1)
r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
j--
}
@@ -215,22 +212,22 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) {
for j >= 20 {
// unmix r3
r3 = rotl16(r3, 16-5)
r3 = bits.RotateLeft16(r3, 16-5)
r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
j--
// unmix r2
r2 = rotl16(r2, 16-3)
r2 = bits.RotateLeft16(r2, 16-3)
r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
j--
// unmix r1
r1 = rotl16(r1, 16-2)
r1 = bits.RotateLeft16(r1, 16-2)
r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
j--
// unmix r0
r0 = rotl16(r0, 16-1)
r0 = bits.RotateLeft16(r0, 16-1)
r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
j--
@@ -243,22 +240,22 @@ func (c *rc2Cipher) Decrypt(dst, src []byte) {
for j >= 0 {
// unmix r3
r3 = rotl16(r3, 16-5)
r3 = bits.RotateLeft16(r3, 16-5)
r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
j--
// unmix r2
r2 = rotl16(r2, 16-3)
r2 = bits.RotateLeft16(r2, 16-3)
r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
j--
// unmix r1
r1 = rotl16(r1, 16-2)
r1 = bits.RotateLeft16(r1, 16-2)
r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
j--
// unmix r0
r0 = rotl16(r0, 16-1)
r0 = bits.RotateLeft16(r0, 16-1)
r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
j--
+34 -32
View File
@@ -5,6 +5,8 @@
// Package salsa provides low-level access to functions in the Salsa family.
package salsa // import "golang.org/x/crypto/salsa20/salsa"
import "math/bits"
// Sigma is the Salsa20 constant for 256-bit keys.
var Sigma = [16]byte{'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k'}
@@ -31,76 +33,76 @@ func HSalsa20(out *[32]byte, in *[16]byte, k *[32]byte, c *[16]byte) {
for i := 0; i < 20; i += 2 {
u := x0 + x12
x4 ^= u<<7 | u>>(32-7)
x4 ^= bits.RotateLeft32(u, 7)
u = x4 + x0
x8 ^= u<<9 | u>>(32-9)
x8 ^= bits.RotateLeft32(u, 9)
u = x8 + x4
x12 ^= u<<13 | u>>(32-13)
x12 ^= bits.RotateLeft32(u, 13)
u = x12 + x8
x0 ^= u<<18 | u>>(32-18)
x0 ^= bits.RotateLeft32(u, 18)
u = x5 + x1
x9 ^= u<<7 | u>>(32-7)
x9 ^= bits.RotateLeft32(u, 7)
u = x9 + x5
x13 ^= u<<9 | u>>(32-9)
x13 ^= bits.RotateLeft32(u, 9)
u = x13 + x9
x1 ^= u<<13 | u>>(32-13)
x1 ^= bits.RotateLeft32(u, 13)
u = x1 + x13
x5 ^= u<<18 | u>>(32-18)
x5 ^= bits.RotateLeft32(u, 18)
u = x10 + x6
x14 ^= u<<7 | u>>(32-7)
x14 ^= bits.RotateLeft32(u, 7)
u = x14 + x10
x2 ^= u<<9 | u>>(32-9)
x2 ^= bits.RotateLeft32(u, 9)
u = x2 + x14
x6 ^= u<<13 | u>>(32-13)
x6 ^= bits.RotateLeft32(u, 13)
u = x6 + x2
x10 ^= u<<18 | u>>(32-18)
x10 ^= bits.RotateLeft32(u, 18)
u = x15 + x11
x3 ^= u<<7 | u>>(32-7)
x3 ^= bits.RotateLeft32(u, 7)
u = x3 + x15
x7 ^= u<<9 | u>>(32-9)
x7 ^= bits.RotateLeft32(u, 9)
u = x7 + x3
x11 ^= u<<13 | u>>(32-13)
x11 ^= bits.RotateLeft32(u, 13)
u = x11 + x7
x15 ^= u<<18 | u>>(32-18)
x15 ^= bits.RotateLeft32(u, 18)
u = x0 + x3
x1 ^= u<<7 | u>>(32-7)
x1 ^= bits.RotateLeft32(u, 7)
u = x1 + x0
x2 ^= u<<9 | u>>(32-9)
x2 ^= bits.RotateLeft32(u, 9)
u = x2 + x1
x3 ^= u<<13 | u>>(32-13)
x3 ^= bits.RotateLeft32(u, 13)
u = x3 + x2
x0 ^= u<<18 | u>>(32-18)
x0 ^= bits.RotateLeft32(u, 18)
u = x5 + x4
x6 ^= u<<7 | u>>(32-7)
x6 ^= bits.RotateLeft32(u, 7)
u = x6 + x5
x7 ^= u<<9 | u>>(32-9)
x7 ^= bits.RotateLeft32(u, 9)
u = x7 + x6
x4 ^= u<<13 | u>>(32-13)
x4 ^= bits.RotateLeft32(u, 13)
u = x4 + x7
x5 ^= u<<18 | u>>(32-18)
x5 ^= bits.RotateLeft32(u, 18)
u = x10 + x9
x11 ^= u<<7 | u>>(32-7)
x11 ^= bits.RotateLeft32(u, 7)
u = x11 + x10
x8 ^= u<<9 | u>>(32-9)
x8 ^= bits.RotateLeft32(u, 9)
u = x8 + x11
x9 ^= u<<13 | u>>(32-13)
x9 ^= bits.RotateLeft32(u, 13)
u = x9 + x8
x10 ^= u<<18 | u>>(32-18)
x10 ^= bits.RotateLeft32(u, 18)
u = x15 + x14
x12 ^= u<<7 | u>>(32-7)
x12 ^= bits.RotateLeft32(u, 7)
u = x12 + x15
x13 ^= u<<9 | u>>(32-9)
x13 ^= bits.RotateLeft32(u, 9)
u = x13 + x12
x14 ^= u<<13 | u>>(32-13)
x14 ^= bits.RotateLeft32(u, 13)
u = x14 + x13
x15 ^= u<<18 | u>>(32-18)
x15 ^= bits.RotateLeft32(u, 18)
}
out[0] = byte(x0)
out[1] = byte(x0 >> 8)
+34 -32
View File
@@ -4,6 +4,8 @@
package salsa
import "math/bits"
// Core208 applies the Salsa20/8 core function to the 64-byte array in and puts
// the result into the 64-byte array out. The input and output may be the same array.
func Core208(out *[64]byte, in *[64]byte) {
@@ -29,76 +31,76 @@ func Core208(out *[64]byte, in *[64]byte) {
for i := 0; i < 8; i += 2 {
u := x0 + x12
x4 ^= u<<7 | u>>(32-7)
x4 ^= bits.RotateLeft32(u, 7)
u = x4 + x0
x8 ^= u<<9 | u>>(32-9)
x8 ^= bits.RotateLeft32(u, 9)
u = x8 + x4
x12 ^= u<<13 | u>>(32-13)
x12 ^= bits.RotateLeft32(u, 13)
u = x12 + x8
x0 ^= u<<18 | u>>(32-18)
x0 ^= bits.RotateLeft32(u, 18)
u = x5 + x1
x9 ^= u<<7 | u>>(32-7)
x9 ^= bits.RotateLeft32(u, 7)
u = x9 + x5
x13 ^= u<<9 | u>>(32-9)
x13 ^= bits.RotateLeft32(u, 9)
u = x13 + x9
x1 ^= u<<13 | u>>(32-13)
x1 ^= bits.RotateLeft32(u, 13)
u = x1 + x13
x5 ^= u<<18 | u>>(32-18)
x5 ^= bits.RotateLeft32(u, 18)
u = x10 + x6
x14 ^= u<<7 | u>>(32-7)
x14 ^= bits.RotateLeft32(u, 7)
u = x14 + x10
x2 ^= u<<9 | u>>(32-9)
x2 ^= bits.RotateLeft32(u, 9)
u = x2 + x14
x6 ^= u<<13 | u>>(32-13)
x6 ^= bits.RotateLeft32(u, 13)
u = x6 + x2
x10 ^= u<<18 | u>>(32-18)
x10 ^= bits.RotateLeft32(u, 18)
u = x15 + x11
x3 ^= u<<7 | u>>(32-7)
x3 ^= bits.RotateLeft32(u, 7)
u = x3 + x15
x7 ^= u<<9 | u>>(32-9)
x7 ^= bits.RotateLeft32(u, 9)
u = x7 + x3
x11 ^= u<<13 | u>>(32-13)
x11 ^= bits.RotateLeft32(u, 13)
u = x11 + x7
x15 ^= u<<18 | u>>(32-18)
x15 ^= bits.RotateLeft32(u, 18)
u = x0 + x3
x1 ^= u<<7 | u>>(32-7)
x1 ^= bits.RotateLeft32(u, 7)
u = x1 + x0
x2 ^= u<<9 | u>>(32-9)
x2 ^= bits.RotateLeft32(u, 9)
u = x2 + x1
x3 ^= u<<13 | u>>(32-13)
x3 ^= bits.RotateLeft32(u, 13)
u = x3 + x2
x0 ^= u<<18 | u>>(32-18)
x0 ^= bits.RotateLeft32(u, 18)
u = x5 + x4
x6 ^= u<<7 | u>>(32-7)
x6 ^= bits.RotateLeft32(u, 7)
u = x6 + x5
x7 ^= u<<9 | u>>(32-9)
x7 ^= bits.RotateLeft32(u, 9)
u = x7 + x6
x4 ^= u<<13 | u>>(32-13)
x4 ^= bits.RotateLeft32(u, 13)
u = x4 + x7
x5 ^= u<<18 | u>>(32-18)
x5 ^= bits.RotateLeft32(u, 18)
u = x10 + x9
x11 ^= u<<7 | u>>(32-7)
x11 ^= bits.RotateLeft32(u, 7)
u = x11 + x10
x8 ^= u<<9 | u>>(32-9)
x8 ^= bits.RotateLeft32(u, 9)
u = x8 + x11
x9 ^= u<<13 | u>>(32-13)
x9 ^= bits.RotateLeft32(u, 13)
u = x9 + x8
x10 ^= u<<18 | u>>(32-18)
x10 ^= bits.RotateLeft32(u, 18)
u = x15 + x14
x12 ^= u<<7 | u>>(32-7)
x12 ^= bits.RotateLeft32(u, 7)
u = x12 + x15
x13 ^= u<<9 | u>>(32-9)
x13 ^= bits.RotateLeft32(u, 9)
u = x13 + x12
x14 ^= u<<13 | u>>(32-13)
x14 ^= bits.RotateLeft32(u, 13)
u = x14 + x13
x15 ^= u<<18 | u>>(32-18)
x15 ^= bits.RotateLeft32(u, 18)
}
x0 += j0
x1 += j1
+34 -32
View File
@@ -4,6 +4,8 @@
package salsa
import "math/bits"
const rounds = 20
// core applies the Salsa20 core function to 16-byte input in, 32-byte key k,
@@ -31,76 +33,76 @@ func core(out *[64]byte, in *[16]byte, k *[32]byte, c *[16]byte) {
for i := 0; i < rounds; i += 2 {
u := x0 + x12
x4 ^= u<<7 | u>>(32-7)
x4 ^= bits.RotateLeft32(u, 7)
u = x4 + x0
x8 ^= u<<9 | u>>(32-9)
x8 ^= bits.RotateLeft32(u, 9)
u = x8 + x4
x12 ^= u<<13 | u>>(32-13)
x12 ^= bits.RotateLeft32(u, 13)
u = x12 + x8
x0 ^= u<<18 | u>>(32-18)
x0 ^= bits.RotateLeft32(u, 18)
u = x5 + x1
x9 ^= u<<7 | u>>(32-7)
x9 ^= bits.RotateLeft32(u, 7)
u = x9 + x5
x13 ^= u<<9 | u>>(32-9)
x13 ^= bits.RotateLeft32(u, 9)
u = x13 + x9
x1 ^= u<<13 | u>>(32-13)
x1 ^= bits.RotateLeft32(u, 13)
u = x1 + x13
x5 ^= u<<18 | u>>(32-18)
x5 ^= bits.RotateLeft32(u, 18)
u = x10 + x6
x14 ^= u<<7 | u>>(32-7)
x14 ^= bits.RotateLeft32(u, 7)
u = x14 + x10
x2 ^= u<<9 | u>>(32-9)
x2 ^= bits.RotateLeft32(u, 9)
u = x2 + x14
x6 ^= u<<13 | u>>(32-13)
x6 ^= bits.RotateLeft32(u, 13)
u = x6 + x2
x10 ^= u<<18 | u>>(32-18)
x10 ^= bits.RotateLeft32(u, 18)
u = x15 + x11
x3 ^= u<<7 | u>>(32-7)
x3 ^= bits.RotateLeft32(u, 7)
u = x3 + x15
x7 ^= u<<9 | u>>(32-9)
x7 ^= bits.RotateLeft32(u, 9)
u = x7 + x3
x11 ^= u<<13 | u>>(32-13)
x11 ^= bits.RotateLeft32(u, 13)
u = x11 + x7
x15 ^= u<<18 | u>>(32-18)
x15 ^= bits.RotateLeft32(u, 18)
u = x0 + x3
x1 ^= u<<7 | u>>(32-7)
x1 ^= bits.RotateLeft32(u, 7)
u = x1 + x0
x2 ^= u<<9 | u>>(32-9)
x2 ^= bits.RotateLeft32(u, 9)
u = x2 + x1
x3 ^= u<<13 | u>>(32-13)
x3 ^= bits.RotateLeft32(u, 13)
u = x3 + x2
x0 ^= u<<18 | u>>(32-18)
x0 ^= bits.RotateLeft32(u, 18)
u = x5 + x4
x6 ^= u<<7 | u>>(32-7)
x6 ^= bits.RotateLeft32(u, 7)
u = x6 + x5
x7 ^= u<<9 | u>>(32-9)
x7 ^= bits.RotateLeft32(u, 9)
u = x7 + x6
x4 ^= u<<13 | u>>(32-13)
x4 ^= bits.RotateLeft32(u, 13)
u = x4 + x7
x5 ^= u<<18 | u>>(32-18)
x5 ^= bits.RotateLeft32(u, 18)
u = x10 + x9
x11 ^= u<<7 | u>>(32-7)
x11 ^= bits.RotateLeft32(u, 7)
u = x11 + x10
x8 ^= u<<9 | u>>(32-9)
x8 ^= bits.RotateLeft32(u, 9)
u = x8 + x11
x9 ^= u<<13 | u>>(32-13)
x9 ^= bits.RotateLeft32(u, 13)
u = x9 + x8
x10 ^= u<<18 | u>>(32-18)
x10 ^= bits.RotateLeft32(u, 18)
u = x15 + x14
x12 ^= u<<7 | u>>(32-7)
x12 ^= bits.RotateLeft32(u, 7)
u = x12 + x15
x13 ^= u<<9 | u>>(32-9)
x13 ^= bits.RotateLeft32(u, 9)
u = x13 + x12
x14 ^= u<<13 | u>>(32-13)
x14 ^= bits.RotateLeft32(u, 13)
u = x14 + x13
x15 ^= u<<18 | u>>(32-18)
x15 ^= bits.RotateLeft32(u, 18)
}
x0 += j0
x1 += j1
+2 -2
View File
@@ -24,7 +24,7 @@ package salsa20 // import "golang.org/x/crypto/salsa20"
// TODO(agl): implement XORKeyStream12 and XORKeyStream8 - the reduced round variants of Salsa20.
import (
"golang.org/x/crypto/internal/subtle"
"golang.org/x/crypto/internal/alias"
"golang.org/x/crypto/salsa20/salsa"
)
@@ -35,7 +35,7 @@ func XORKeyStream(out, in []byte, nonce []byte, key *[32]byte) {
if len(out) < len(in) {
panic("salsa20: output smaller than input")
}
if subtle.InexactOverlap(out[:len(in)], in) {
if alias.InexactOverlap(out[:len(in)], in) {
panic("salsa20: invalid buffer overlap")
}
+1 -1
View File
@@ -186,7 +186,7 @@ func smix(b []byte, r, N int, v, xy []uint32) {
// For example, you can get a derived key for e.g. AES-256 (which needs a
// 32-byte key) by doing:
//
// dk, err := scrypt.Key([]byte("some password"), salt, 32768, 8, 1, 32)
// dk, err := scrypt.Key([]byte("some password"), salt, 32768, 8, 1, 32)
//
// The recommended parameters for interactive logins as of 2017 are N=32768, r=8
// and p=1. The parameters N, r, and p should be increased as memory latency and
+4 -8
View File
@@ -8,8 +8,7 @@
// Both types of hash function use the "sponge" construction and the Keccak
// permutation. For a detailed specification see http://keccak.noekeon.org/
//
//
// Guidance
// # Guidance
//
// If you aren't sure what function you need, use SHAKE256 with at least 64
// bytes of output. The SHAKE instances are faster than the SHA3 instances;
@@ -19,8 +18,7 @@
// secret key to the input, hash with SHAKE256 and read at least 32 bytes of
// output.
//
//
// Security strengths
// # Security strengths
//
// The SHA3-x (x equals 224, 256, 384, or 512) functions have a security
// strength against preimage attacks of x bits. Since they only produce "x"
@@ -31,8 +29,7 @@
// is used. Requesting more than 64 or 32 bytes of output, respectively, does
// not increase the collision-resistance of the SHAKE functions.
//
//
// The sponge construction
// # The sponge construction
//
// A sponge builds a pseudo-random function from a public pseudo-random
// permutation, by applying the permutation to a state of "rate + capacity"
@@ -50,8 +47,7 @@
// Since the KeccakF-1600 permutation is 1600 bits (200 bytes) wide, this means
// that the security strength of a sponge instance is equal to (1600 - bitrate) / 2.
//
//
// Recommendations
// # Recommendations
//
// The SHAKE functions are recommended for most new uses. They can produce
// output of arbitrary length. SHAKE256, with an output length of at least
+98 -96
View File
@@ -7,6 +7,8 @@
package sha3
import "math/bits"
// rc stores the round constants for use in the ι step.
var rc = [24]uint64{
0x0000000000000001,
@@ -60,13 +62,13 @@ func keccakF1600(a *[25]uint64) {
bc0 = a[0] ^ d0
t = a[6] ^ d1
bc1 = t<<44 | t>>(64-44)
bc1 = bits.RotateLeft64(t, 44)
t = a[12] ^ d2
bc2 = t<<43 | t>>(64-43)
bc2 = bits.RotateLeft64(t, 43)
t = a[18] ^ d3
bc3 = t<<21 | t>>(64-21)
bc3 = bits.RotateLeft64(t, 21)
t = a[24] ^ d4
bc4 = t<<14 | t>>(64-14)
bc4 = bits.RotateLeft64(t, 14)
a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i]
a[6] = bc1 ^ (bc3 &^ bc2)
a[12] = bc2 ^ (bc4 &^ bc3)
@@ -74,15 +76,15 @@ func keccakF1600(a *[25]uint64) {
a[24] = bc4 ^ (bc1 &^ bc0)
t = a[10] ^ d0
bc2 = t<<3 | t>>(64-3)
bc2 = bits.RotateLeft64(t, 3)
t = a[16] ^ d1
bc3 = t<<45 | t>>(64-45)
bc3 = bits.RotateLeft64(t, 45)
t = a[22] ^ d2
bc4 = t<<61 | t>>(64-61)
bc4 = bits.RotateLeft64(t, 61)
t = a[3] ^ d3
bc0 = t<<28 | t>>(64-28)
bc0 = bits.RotateLeft64(t, 28)
t = a[9] ^ d4
bc1 = t<<20 | t>>(64-20)
bc1 = bits.RotateLeft64(t, 20)
a[10] = bc0 ^ (bc2 &^ bc1)
a[16] = bc1 ^ (bc3 &^ bc2)
a[22] = bc2 ^ (bc4 &^ bc3)
@@ -90,15 +92,15 @@ func keccakF1600(a *[25]uint64) {
a[9] = bc4 ^ (bc1 &^ bc0)
t = a[20] ^ d0
bc4 = t<<18 | t>>(64-18)
bc4 = bits.RotateLeft64(t, 18)
t = a[1] ^ d1
bc0 = t<<1 | t>>(64-1)
bc0 = bits.RotateLeft64(t, 1)
t = a[7] ^ d2
bc1 = t<<6 | t>>(64-6)
bc1 = bits.RotateLeft64(t, 6)
t = a[13] ^ d3
bc2 = t<<25 | t>>(64-25)
bc2 = bits.RotateLeft64(t, 25)
t = a[19] ^ d4
bc3 = t<<8 | t>>(64-8)
bc3 = bits.RotateLeft64(t, 8)
a[20] = bc0 ^ (bc2 &^ bc1)
a[1] = bc1 ^ (bc3 &^ bc2)
a[7] = bc2 ^ (bc4 &^ bc3)
@@ -106,15 +108,15 @@ func keccakF1600(a *[25]uint64) {
a[19] = bc4 ^ (bc1 &^ bc0)
t = a[5] ^ d0
bc1 = t<<36 | t>>(64-36)
bc1 = bits.RotateLeft64(t, 36)
t = a[11] ^ d1
bc2 = t<<10 | t>>(64-10)
bc2 = bits.RotateLeft64(t, 10)
t = a[17] ^ d2
bc3 = t<<15 | t>>(64-15)
bc3 = bits.RotateLeft64(t, 15)
t = a[23] ^ d3
bc4 = t<<56 | t>>(64-56)
bc4 = bits.RotateLeft64(t, 56)
t = a[4] ^ d4
bc0 = t<<27 | t>>(64-27)
bc0 = bits.RotateLeft64(t, 27)
a[5] = bc0 ^ (bc2 &^ bc1)
a[11] = bc1 ^ (bc3 &^ bc2)
a[17] = bc2 ^ (bc4 &^ bc3)
@@ -122,15 +124,15 @@ func keccakF1600(a *[25]uint64) {
a[4] = bc4 ^ (bc1 &^ bc0)
t = a[15] ^ d0
bc3 = t<<41 | t>>(64-41)
bc3 = bits.RotateLeft64(t, 41)
t = a[21] ^ d1
bc4 = t<<2 | t>>(64-2)
bc4 = bits.RotateLeft64(t, 2)
t = a[2] ^ d2
bc0 = t<<62 | t>>(64-62)
bc0 = bits.RotateLeft64(t, 62)
t = a[8] ^ d3
bc1 = t<<55 | t>>(64-55)
bc1 = bits.RotateLeft64(t, 55)
t = a[14] ^ d4
bc2 = t<<39 | t>>(64-39)
bc2 = bits.RotateLeft64(t, 39)
a[15] = bc0 ^ (bc2 &^ bc1)
a[21] = bc1 ^ (bc3 &^ bc2)
a[2] = bc2 ^ (bc4 &^ bc3)
@@ -151,13 +153,13 @@ func keccakF1600(a *[25]uint64) {
bc0 = a[0] ^ d0
t = a[16] ^ d1
bc1 = t<<44 | t>>(64-44)
bc1 = bits.RotateLeft64(t, 44)
t = a[7] ^ d2
bc2 = t<<43 | t>>(64-43)
bc2 = bits.RotateLeft64(t, 43)
t = a[23] ^ d3
bc3 = t<<21 | t>>(64-21)
bc3 = bits.RotateLeft64(t, 21)
t = a[14] ^ d4
bc4 = t<<14 | t>>(64-14)
bc4 = bits.RotateLeft64(t, 14)
a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+1]
a[16] = bc1 ^ (bc3 &^ bc2)
a[7] = bc2 ^ (bc4 &^ bc3)
@@ -165,15 +167,15 @@ func keccakF1600(a *[25]uint64) {
a[14] = bc4 ^ (bc1 &^ bc0)
t = a[20] ^ d0
bc2 = t<<3 | t>>(64-3)
bc2 = bits.RotateLeft64(t, 3)
t = a[11] ^ d1
bc3 = t<<45 | t>>(64-45)
bc3 = bits.RotateLeft64(t, 45)
t = a[2] ^ d2
bc4 = t<<61 | t>>(64-61)
bc4 = bits.RotateLeft64(t, 61)
t = a[18] ^ d3
bc0 = t<<28 | t>>(64-28)
bc0 = bits.RotateLeft64(t, 28)
t = a[9] ^ d4
bc1 = t<<20 | t>>(64-20)
bc1 = bits.RotateLeft64(t, 20)
a[20] = bc0 ^ (bc2 &^ bc1)
a[11] = bc1 ^ (bc3 &^ bc2)
a[2] = bc2 ^ (bc4 &^ bc3)
@@ -181,15 +183,15 @@ func keccakF1600(a *[25]uint64) {
a[9] = bc4 ^ (bc1 &^ bc0)
t = a[15] ^ d0
bc4 = t<<18 | t>>(64-18)
bc4 = bits.RotateLeft64(t, 18)
t = a[6] ^ d1
bc0 = t<<1 | t>>(64-1)
bc0 = bits.RotateLeft64(t, 1)
t = a[22] ^ d2
bc1 = t<<6 | t>>(64-6)
bc1 = bits.RotateLeft64(t, 6)
t = a[13] ^ d3
bc2 = t<<25 | t>>(64-25)
bc2 = bits.RotateLeft64(t, 25)
t = a[4] ^ d4
bc3 = t<<8 | t>>(64-8)
bc3 = bits.RotateLeft64(t, 8)
a[15] = bc0 ^ (bc2 &^ bc1)
a[6] = bc1 ^ (bc3 &^ bc2)
a[22] = bc2 ^ (bc4 &^ bc3)
@@ -197,15 +199,15 @@ func keccakF1600(a *[25]uint64) {
a[4] = bc4 ^ (bc1 &^ bc0)
t = a[10] ^ d0
bc1 = t<<36 | t>>(64-36)
bc1 = bits.RotateLeft64(t, 36)
t = a[1] ^ d1
bc2 = t<<10 | t>>(64-10)
bc2 = bits.RotateLeft64(t, 10)
t = a[17] ^ d2
bc3 = t<<15 | t>>(64-15)
bc3 = bits.RotateLeft64(t, 15)
t = a[8] ^ d3
bc4 = t<<56 | t>>(64-56)
bc4 = bits.RotateLeft64(t, 56)
t = a[24] ^ d4
bc0 = t<<27 | t>>(64-27)
bc0 = bits.RotateLeft64(t, 27)
a[10] = bc0 ^ (bc2 &^ bc1)
a[1] = bc1 ^ (bc3 &^ bc2)
a[17] = bc2 ^ (bc4 &^ bc3)
@@ -213,15 +215,15 @@ func keccakF1600(a *[25]uint64) {
a[24] = bc4 ^ (bc1 &^ bc0)
t = a[5] ^ d0
bc3 = t<<41 | t>>(64-41)
bc3 = bits.RotateLeft64(t, 41)
t = a[21] ^ d1
bc4 = t<<2 | t>>(64-2)
bc4 = bits.RotateLeft64(t, 2)
t = a[12] ^ d2
bc0 = t<<62 | t>>(64-62)
bc0 = bits.RotateLeft64(t, 62)
t = a[3] ^ d3
bc1 = t<<55 | t>>(64-55)
bc1 = bits.RotateLeft64(t, 55)
t = a[19] ^ d4
bc2 = t<<39 | t>>(64-39)
bc2 = bits.RotateLeft64(t, 39)
a[5] = bc0 ^ (bc2 &^ bc1)
a[21] = bc1 ^ (bc3 &^ bc2)
a[12] = bc2 ^ (bc4 &^ bc3)
@@ -242,13 +244,13 @@ func keccakF1600(a *[25]uint64) {
bc0 = a[0] ^ d0
t = a[11] ^ d1
bc1 = t<<44 | t>>(64-44)
bc1 = bits.RotateLeft64(t, 44)
t = a[22] ^ d2
bc2 = t<<43 | t>>(64-43)
bc2 = bits.RotateLeft64(t, 43)
t = a[8] ^ d3
bc3 = t<<21 | t>>(64-21)
bc3 = bits.RotateLeft64(t, 21)
t = a[19] ^ d4
bc4 = t<<14 | t>>(64-14)
bc4 = bits.RotateLeft64(t, 14)
a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+2]
a[11] = bc1 ^ (bc3 &^ bc2)
a[22] = bc2 ^ (bc4 &^ bc3)
@@ -256,15 +258,15 @@ func keccakF1600(a *[25]uint64) {
a[19] = bc4 ^ (bc1 &^ bc0)
t = a[15] ^ d0
bc2 = t<<3 | t>>(64-3)
bc2 = bits.RotateLeft64(t, 3)
t = a[1] ^ d1
bc3 = t<<45 | t>>(64-45)
bc3 = bits.RotateLeft64(t, 45)
t = a[12] ^ d2
bc4 = t<<61 | t>>(64-61)
bc4 = bits.RotateLeft64(t, 61)
t = a[23] ^ d3
bc0 = t<<28 | t>>(64-28)
bc0 = bits.RotateLeft64(t, 28)
t = a[9] ^ d4
bc1 = t<<20 | t>>(64-20)
bc1 = bits.RotateLeft64(t, 20)
a[15] = bc0 ^ (bc2 &^ bc1)
a[1] = bc1 ^ (bc3 &^ bc2)
a[12] = bc2 ^ (bc4 &^ bc3)
@@ -272,15 +274,15 @@ func keccakF1600(a *[25]uint64) {
a[9] = bc4 ^ (bc1 &^ bc0)
t = a[5] ^ d0
bc4 = t<<18 | t>>(64-18)
bc4 = bits.RotateLeft64(t, 18)
t = a[16] ^ d1
bc0 = t<<1 | t>>(64-1)
bc0 = bits.RotateLeft64(t, 1)
t = a[2] ^ d2
bc1 = t<<6 | t>>(64-6)
bc1 = bits.RotateLeft64(t, 6)
t = a[13] ^ d3
bc2 = t<<25 | t>>(64-25)
bc2 = bits.RotateLeft64(t, 25)
t = a[24] ^ d4
bc3 = t<<8 | t>>(64-8)
bc3 = bits.RotateLeft64(t, 8)
a[5] = bc0 ^ (bc2 &^ bc1)
a[16] = bc1 ^ (bc3 &^ bc2)
a[2] = bc2 ^ (bc4 &^ bc3)
@@ -288,15 +290,15 @@ func keccakF1600(a *[25]uint64) {
a[24] = bc4 ^ (bc1 &^ bc0)
t = a[20] ^ d0
bc1 = t<<36 | t>>(64-36)
bc1 = bits.RotateLeft64(t, 36)
t = a[6] ^ d1
bc2 = t<<10 | t>>(64-10)
bc2 = bits.RotateLeft64(t, 10)
t = a[17] ^ d2
bc3 = t<<15 | t>>(64-15)
bc3 = bits.RotateLeft64(t, 15)
t = a[3] ^ d3
bc4 = t<<56 | t>>(64-56)
bc4 = bits.RotateLeft64(t, 56)
t = a[14] ^ d4
bc0 = t<<27 | t>>(64-27)
bc0 = bits.RotateLeft64(t, 27)
a[20] = bc0 ^ (bc2 &^ bc1)
a[6] = bc1 ^ (bc3 &^ bc2)
a[17] = bc2 ^ (bc4 &^ bc3)
@@ -304,15 +306,15 @@ func keccakF1600(a *[25]uint64) {
a[14] = bc4 ^ (bc1 &^ bc0)
t = a[10] ^ d0
bc3 = t<<41 | t>>(64-41)
bc3 = bits.RotateLeft64(t, 41)
t = a[21] ^ d1
bc4 = t<<2 | t>>(64-2)
bc4 = bits.RotateLeft64(t, 2)
t = a[7] ^ d2
bc0 = t<<62 | t>>(64-62)
bc0 = bits.RotateLeft64(t, 62)
t = a[18] ^ d3
bc1 = t<<55 | t>>(64-55)
bc1 = bits.RotateLeft64(t, 55)
t = a[4] ^ d4
bc2 = t<<39 | t>>(64-39)
bc2 = bits.RotateLeft64(t, 39)
a[10] = bc0 ^ (bc2 &^ bc1)
a[21] = bc1 ^ (bc3 &^ bc2)
a[7] = bc2 ^ (bc4 &^ bc3)
@@ -333,13 +335,13 @@ func keccakF1600(a *[25]uint64) {
bc0 = a[0] ^ d0
t = a[1] ^ d1
bc1 = t<<44 | t>>(64-44)
bc1 = bits.RotateLeft64(t, 44)
t = a[2] ^ d2
bc2 = t<<43 | t>>(64-43)
bc2 = bits.RotateLeft64(t, 43)
t = a[3] ^ d3
bc3 = t<<21 | t>>(64-21)
bc3 = bits.RotateLeft64(t, 21)
t = a[4] ^ d4
bc4 = t<<14 | t>>(64-14)
bc4 = bits.RotateLeft64(t, 14)
a[0] = bc0 ^ (bc2 &^ bc1) ^ rc[i+3]
a[1] = bc1 ^ (bc3 &^ bc2)
a[2] = bc2 ^ (bc4 &^ bc3)
@@ -347,15 +349,15 @@ func keccakF1600(a *[25]uint64) {
a[4] = bc4 ^ (bc1 &^ bc0)
t = a[5] ^ d0
bc2 = t<<3 | t>>(64-3)
bc2 = bits.RotateLeft64(t, 3)
t = a[6] ^ d1
bc3 = t<<45 | t>>(64-45)
bc3 = bits.RotateLeft64(t, 45)
t = a[7] ^ d2
bc4 = t<<61 | t>>(64-61)
bc4 = bits.RotateLeft64(t, 61)
t = a[8] ^ d3
bc0 = t<<28 | t>>(64-28)
bc0 = bits.RotateLeft64(t, 28)
t = a[9] ^ d4
bc1 = t<<20 | t>>(64-20)
bc1 = bits.RotateLeft64(t, 20)
a[5] = bc0 ^ (bc2 &^ bc1)
a[6] = bc1 ^ (bc3 &^ bc2)
a[7] = bc2 ^ (bc4 &^ bc3)
@@ -363,15 +365,15 @@ func keccakF1600(a *[25]uint64) {
a[9] = bc4 ^ (bc1 &^ bc0)
t = a[10] ^ d0
bc4 = t<<18 | t>>(64-18)
bc4 = bits.RotateLeft64(t, 18)
t = a[11] ^ d1
bc0 = t<<1 | t>>(64-1)
bc0 = bits.RotateLeft64(t, 1)
t = a[12] ^ d2
bc1 = t<<6 | t>>(64-6)
bc1 = bits.RotateLeft64(t, 6)
t = a[13] ^ d3
bc2 = t<<25 | t>>(64-25)
bc2 = bits.RotateLeft64(t, 25)
t = a[14] ^ d4
bc3 = t<<8 | t>>(64-8)
bc3 = bits.RotateLeft64(t, 8)
a[10] = bc0 ^ (bc2 &^ bc1)
a[11] = bc1 ^ (bc3 &^ bc2)
a[12] = bc2 ^ (bc4 &^ bc3)
@@ -379,15 +381,15 @@ func keccakF1600(a *[25]uint64) {
a[14] = bc4 ^ (bc1 &^ bc0)
t = a[15] ^ d0
bc1 = t<<36 | t>>(64-36)
bc1 = bits.RotateLeft64(t, 36)
t = a[16] ^ d1
bc2 = t<<10 | t>>(64-10)
bc2 = bits.RotateLeft64(t, 10)
t = a[17] ^ d2
bc3 = t<<15 | t>>(64-15)
bc3 = bits.RotateLeft64(t, 15)
t = a[18] ^ d3
bc4 = t<<56 | t>>(64-56)
bc4 = bits.RotateLeft64(t, 56)
t = a[19] ^ d4
bc0 = t<<27 | t>>(64-27)
bc0 = bits.RotateLeft64(t, 27)
a[15] = bc0 ^ (bc2 &^ bc1)
a[16] = bc1 ^ (bc3 &^ bc2)
a[17] = bc2 ^ (bc4 &^ bc3)
@@ -395,15 +397,15 @@ func keccakF1600(a *[25]uint64) {
a[19] = bc4 ^ (bc1 &^ bc0)
t = a[20] ^ d0
bc3 = t<<41 | t>>(64-41)
bc3 = bits.RotateLeft64(t, 41)
t = a[21] ^ d1
bc4 = t<<2 | t>>(64-2)
bc4 = bits.RotateLeft64(t, 2)
t = a[22] ^ d2
bc0 = t<<62 | t>>(64-62)
bc0 = bits.RotateLeft64(t, 62)
t = a[23] ^ d3
bc1 = t<<55 | t>>(64-55)
bc1 = bits.RotateLeft64(t, 55)
t = a[24] ^ d4
bc2 = t<<39 | t>>(64-39)
bc2 = bits.RotateLeft64(t, 39)
a[20] = bc0 ^ (bc2 &^ bc1)
a[21] = bc1 ^ (bc3 &^ bc2)
a[22] = bc2 ^ (bc4 &^ bc3)
+1 -1
View File
@@ -86,7 +86,7 @@ func (d *state) permute() {
d.buf = d.storage.asBytes()[:0]
keccakF1600(&d.a)
case spongeSqueezing:
// If we're squeezing, we need to apply the permutatin before
// If we're squeezing, we need to apply the permutation before
// copying more output.
keccakF1600(&d.a)
d.buf = d.storage.asBytes()[:d.rate]
+2
View File
@@ -34,11 +34,13 @@ const (
// kimd is a wrapper for the 'compute intermediate message digest' instruction.
// src must be a multiple of the rate for the given function code.
//
//go:noescape
func kimd(function code, chain *[200]byte, src []byte)
// klmd is a wrapper for the 'compute last message digest' instruction.
// src padding is handled by the instruction.
//
//go:noescape
func klmd(function code, chain *[200]byte, dst, src []byte)
+50 -14
View File
@@ -8,7 +8,8 @@
// ssh-agent process using the sample server.
//
// References:
// [PROTOCOL.agent]: https://tools.ietf.org/html/draft-miller-ssh-agent-00
//
// [PROTOCOL.agent]: https://tools.ietf.org/html/draft-miller-ssh-agent-00
package agent // import "golang.org/x/crypto/ssh/agent"
import (
@@ -25,7 +26,6 @@ import (
"math/big"
"sync"
"crypto"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/ssh"
)
@@ -93,7 +93,7 @@ type ExtendedAgent interface {
type ConstraintExtension struct {
// ExtensionName consist of a UTF-8 string suffixed by the
// implementation domain following the naming scheme defined
// in Section 4.2 of [RFC4251], e.g. "foo@example.com".
// in Section 4.2 of RFC 4251, e.g. "foo@example.com".
ExtensionName string
// ExtensionDetails contains the actual content of the extended
// constraint.
@@ -226,7 +226,9 @@ var ErrExtensionUnsupported = errors.New("agent: extension unsupported")
type extensionAgentMsg struct {
ExtensionType string `sshtype:"27"`
Contents []byte
// NOTE: this matches OpenSSH's PROTOCOL.agent, not the IETF draft [PROTOCOL.agent],
// so that it matches what OpenSSH actually implements in the wild.
Contents []byte `ssh:"rest"`
}
// Key represents a protocol 2 public key as defined in
@@ -729,7 +731,7 @@ func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string
if err != nil {
return err
}
if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 {
if !bytes.Equal(cert.Key.Marshal(), signer.PublicKey().Marshal()) {
return errors.New("agent: signer and cert have different public key")
}
@@ -771,19 +773,53 @@ func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature,
return s.agent.Sign(s.pub, data)
}
func (s *agentKeyringSigner) SignWithOpts(rand io.Reader, data []byte, opts crypto.SignerOpts) (*ssh.Signature, error) {
var flags SignatureFlags
if opts != nil {
switch opts.HashFunc() {
case crypto.SHA256:
flags = SignatureFlagRsaSha256
case crypto.SHA512:
flags = SignatureFlagRsaSha512
}
func (s *agentKeyringSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*ssh.Signature, error) {
if algorithm == "" || algorithm == underlyingAlgo(s.pub.Type()) {
return s.Sign(rand, data)
}
var flags SignatureFlags
switch algorithm {
case ssh.KeyAlgoRSASHA256:
flags = SignatureFlagRsaSha256
case ssh.KeyAlgoRSASHA512:
flags = SignatureFlagRsaSha512
default:
return nil, fmt.Errorf("agent: unsupported algorithm %q", algorithm)
}
return s.agent.SignWithFlags(s.pub, data, flags)
}
var _ ssh.AlgorithmSigner = &agentKeyringSigner{}
// certKeyAlgoNames is a mapping from known certificate algorithm names to the
// corresponding public key signature algorithm.
//
// 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,
}
// underlyingAlgo returns the signature algorithm associated with algo (which is
// an advertised or negotiated public key or host key algorithm). These are
// usually the same, except for certificate algorithms.
func underlyingAlgo(algo string) string {
if a, ok := certKeyAlgoNames[algo]; ok {
return a
}
return algo
}
// Calls an extension method. It is up to the agent implementation as to whether or not
// any particular extension is supported and may always return an error. Because the
// type of the response is up to the implementation, this returns the bytes of the
+16 -35
View File
@@ -16,7 +16,6 @@ import (
"runtime"
"strconv"
"strings"
"sync"
"testing"
"time"
@@ -183,9 +182,9 @@ func testAgentInterface(t *testing.T, agent ExtendedAgent, key interface{}, cert
t.Fatalf("Verify(%s): %v", pubKey.Type(), err)
}
}
sshFlagTest(0, ssh.SigAlgoRSA)
sshFlagTest(SignatureFlagRsaSha256, ssh.SigAlgoRSASHA2256)
sshFlagTest(SignatureFlagRsaSha512, ssh.SigAlgoRSASHA2512)
sshFlagTest(0, ssh.KeyAlgoRSA)
sshFlagTest(SignatureFlagRsaSha256, ssh.KeyAlgoRSASHA256)
sshFlagTest(SignatureFlagRsaSha512, ssh.KeyAlgoRSASHA512)
}
// If the key has a lifetime, is it removed when it should be?
@@ -204,44 +203,26 @@ func testAgentInterface(t *testing.T, agent ExtendedAgent, key interface{}, cert
func TestMalformedRequests(t *testing.T) {
keyringAgent := NewKeyring()
listener, err := netListener()
if err != nil {
t.Fatalf("netListener: %v", err)
}
defer listener.Close()
testCase := func(t *testing.T, requestBytes []byte, wantServerErr bool) {
var wg sync.WaitGroup
wg.Add(1)
c, s := net.Pipe()
defer c.Close()
defer s.Close()
go func() {
defer wg.Done()
c, err := listener.Accept()
_, err := c.Write(requestBytes)
if err != nil {
t.Errorf("listener.Accept: %v", err)
return
}
defer c.Close()
err = ServeAgent(keyringAgent, c)
if err == nil {
t.Error("ServeAgent should have returned an error to malformed input")
} else {
if (err != io.EOF) != wantServerErr {
t.Errorf("ServeAgent returned expected error: %v", err)
}
t.Errorf("Unexpected error writing raw bytes on connection: %v", err)
}
c.Close()
}()
c, err := net.Dial("tcp", listener.Addr().String())
if err != nil {
t.Fatalf("net.Dial: %v", err)
err := ServeAgent(keyringAgent, s)
if err == nil {
t.Error("ServeAgent should have returned an error to malformed input")
} else {
if (err != io.EOF) != wantServerErr {
t.Errorf("ServeAgent returned expected error: %v", err)
}
}
_, err = c.Write(requestBytes)
if err != nil {
t.Errorf("Unexpected error writing raw bytes on connection: %v", err)
}
c.Close()
wg.Wait()
}
var testCases = []struct {
+3 -3
View File
@@ -113,7 +113,7 @@ func (r *keyring) Unlock(passphrase []byte) error {
// expireKeysLocked removes expired keys from the keyring. If a key was added
// with a lifetimesecs contraint and seconds >= lifetimesecs seconds have
// ellapsed, it is removed. The caller *must* be holding the keyring mutex.
// elapsed, it is removed. The caller *must* be holding the keyring mutex.
func (r *keyring) expireKeysLocked() {
for _, k := range r.keys {
if k.expire != nil && time.Now().After(*k.expire) {
@@ -205,9 +205,9 @@ func (r *keyring) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureF
var algorithm string
switch flags {
case SignatureFlagRsaSha256:
algorithm = ssh.SigAlgoRSASHA2256
algorithm = ssh.KeyAlgoRSASHA256
case SignatureFlagRsaSha512:
algorithm = ssh.SigAlgoRSASHA2512
algorithm = ssh.KeyAlgoRSASHA512
default:
return nil, fmt.Errorf("agent: unsupported signature flags: %d", flags)
}
+60 -37
View File
@@ -14,8 +14,10 @@ import (
"time"
)
// These constants from [PROTOCOL.certkeys] represent the key algorithm names
// for certificate types supported by this package.
// Certificate algorithm names from [PROTOCOL.certkeys]. These values can appear
// in Certificate.Type, PublicKey.Type, and ClientConfig.HostKeyAlgorithms.
// Unlike key algorithm names, these are not passed to AlgorithmSigner and don't
// appear in the Signature.Format field.
const (
CertAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
CertAlgoDSAv01 = "ssh-dss-cert-v01@openssh.com"
@@ -25,14 +27,21 @@ const (
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
// ClientConfig.HostKeyAlgorithms.
CertAlgoRSASHA256v01 = "rsa-sha2-256-cert-v01@openssh.com"
CertAlgoRSASHA512v01 = "rsa-sha2-512-cert-v01@openssh.com"
)
// These constants from [PROTOCOL.certkeys] represent additional signature
// algorithm names for certificate types supported by this package.
const (
CertSigAlgoRSAv01 = "ssh-rsa-cert-v01@openssh.com"
CertSigAlgoRSASHA2256v01 = "rsa-sha2-256-cert-v01@openssh.com"
CertSigAlgoRSASHA2512v01 = "rsa-sha2-512-cert-v01@openssh.com"
// Deprecated: use CertAlgoRSAv01.
CertSigAlgoRSAv01 = CertAlgoRSAv01
// Deprecated: use CertAlgoRSASHA256v01.
CertSigAlgoRSASHA2256v01 = CertAlgoRSASHA256v01
// Deprecated: use CertAlgoRSASHA512v01.
CertSigAlgoRSASHA2512v01 = CertAlgoRSASHA512v01
)
// Certificate types distinguish between host and user
@@ -242,7 +251,7 @@ type algorithmOpenSSHCertSigner struct {
// private key is held by signer. It returns an error if the public key in cert
// doesn't match the key used by signer.
func NewCertSigner(cert *Certificate, signer Signer) (Signer, error) {
if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 {
if !bytes.Equal(cert.Key.Marshal(), signer.PublicKey().Marshal()) {
return nil, errors.New("ssh: signer and cert have different public key")
}
@@ -431,10 +440,14 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
}
c.SignatureKey = authority.PublicKey()
if v, ok := authority.(AlgorithmSigner); ok {
if v.PublicKey().Type() == KeyAlgoRSA {
authority = &rsaSigner{v, SigAlgoRSASHA2512}
// Default to KeyAlgoRSASHA512 for ssh-rsa signers.
if v, ok := authority.(AlgorithmSigner); ok && v.PublicKey().Type() == KeyAlgoRSA {
sig, err := v.SignWithAlgorithm(rand, c.bytesForSigning(), KeyAlgoRSASHA512)
if err != nil {
return err
}
c.Signature = sig
return nil
}
sig, err := authority.Sign(rand, c.bytesForSigning())
@@ -445,32 +458,42 @@ func (c *Certificate) SignCert(rand io.Reader, authority Signer) error {
return nil
}
// certAlgoNames includes a mapping from signature algorithms to the
// corresponding certificate signature algorithm. When a key type (such
// as ED25516) is associated with only one algorithm, the KeyAlgo
// constant is used instead of the SigAlgo.
var certAlgoNames = map[string]string{
SigAlgoRSA: CertSigAlgoRSAv01,
SigAlgoRSASHA2256: CertSigAlgoRSASHA2256v01,
SigAlgoRSASHA2512: CertSigAlgoRSASHA2512v01,
KeyAlgoDSA: CertAlgoDSAv01,
KeyAlgoECDSA256: CertAlgoECDSA256v01,
KeyAlgoECDSA384: CertAlgoECDSA384v01,
KeyAlgoECDSA521: CertAlgoECDSA521v01,
KeyAlgoSKECDSA256: CertAlgoSKECDSA256v01,
KeyAlgoED25519: CertAlgoED25519v01,
KeyAlgoSKED25519: CertAlgoSKED25519v01,
// certKeyAlgoNames is a mapping from known certificate algorithm names to the
// corresponding public key signature algorithm.
//
// 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,
}
// certToPrivAlgo returns the underlying algorithm for a certificate algorithm.
// Panics if a non-certificate algorithm is passed.
func certToPrivAlgo(algo string) string {
for privAlgo, pubAlgo := range certAlgoNames {
if pubAlgo == algo {
return privAlgo
// underlyingAlgo returns the signature algorithm associated with algo (which is
// an advertised or negotiated public key or host key algorithm). These are
// usually the same, except for certificate algorithms.
func underlyingAlgo(algo string) string {
if a, ok := certKeyAlgoNames[algo]; ok {
return a
}
return algo
}
// certificateAlgo returns the certificate algorithms that uses the provided
// underlying signature algorithm.
func certificateAlgo(algo string) (certAlgo string, ok bool) {
for certName, algoName := range certKeyAlgoNames {
if algoName == algo {
return certName, true
}
}
panic("unknown cert algorithm")
return "", false
}
func (cert *Certificate) bytesForSigning() []byte {
@@ -514,13 +537,13 @@ func (c *Certificate) Marshal() []byte {
return result
}
// Type returns the key name. It is part of the PublicKey interface.
// Type returns the certificate algorithm name. It is part of the PublicKey interface.
func (c *Certificate) Type() string {
algo, ok := certAlgoNames[c.Key.Type()]
certName, ok := certificateAlgo(c.Key.Type())
if !ok {
panic("unknown cert key type " + c.Key.Type())
panic("unknown certificate type for key type " + c.Key.Type())
}
return algo
return certName
}
// Verify verifies a signature against the certificate's public
+15 -14
View File
@@ -49,14 +49,17 @@ func TestParseCert(t *testing.T) {
// % ssh-keygen -s ca -I testcert -O source-address=192.168.1.0/24 -O force-command=/bin/sleep user.pub
// user.pub key: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDACh1rt2DXfV3hk6fszSQcQ/rueMId0kVD9U7nl8cfEnFxqOCrNT92g4laQIGl2mn8lsGZfTLg8ksHq3gkvgO3oo/0wHy4v32JeBOHTsN5AL4gfHNEhWeWb50ev47hnTsRIt9P4dxogeUo/hTu7j9+s9lLpEQXCvq6xocXQt0j8MV9qZBBXFLXVT3cWIkSqOdwt/5ZBg+1GSrc7WfCXVWgTk4a20uPMuJPxU4RQwZW6X3+O8Pqo8C3cW0OzZRFP6gUYUKUsTI5WntlS+LAxgw1mZNsozFGdbiOPRnEryE3SRldh9vjDR3tin1fGpA5P7+CEB/bqaXtG3V+F2OkqaMN
// Critical Options:
// force-command /bin/sleep
// source-address 192.168.1.0/24
//
// force-command /bin/sleep
// source-address 192.168.1.0/24
//
// Extensions:
// permit-X11-forwarding
// permit-agent-forwarding
// permit-port-forwarding
// permit-pty
// permit-user-rc
//
// permit-X11-forwarding
// permit-agent-forwarding
// permit-port-forwarding
// permit-pty
// permit-user-rc
const exampleSSHCertWithOptions = `ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgDyysCJY0XrO1n03EeRRoITnTPdjENFmWDs9X58PP3VUAAAADAQABAAABAQDACh1rt2DXfV3hk6fszSQcQ/rueMId0kVD9U7nl8cfEnFxqOCrNT92g4laQIGl2mn8lsGZfTLg8ksHq3gkvgO3oo/0wHy4v32JeBOHTsN5AL4gfHNEhWeWb50ev47hnTsRIt9P4dxogeUo/hTu7j9+s9lLpEQXCvq6xocXQt0j8MV9qZBBXFLXVT3cWIkSqOdwt/5ZBg+1GSrc7WfCXVWgTk4a20uPMuJPxU4RQwZW6X3+O8Pqo8C3cW0OzZRFP6gUYUKUsTI5WntlS+LAxgw1mZNsozFGdbiOPRnEryE3SRldh9vjDR3tin1fGpA5P7+CEB/bqaXtG3V+F2OkqaMNAAAAAAAAAAAAAAABAAAACHRlc3RjZXJ0AAAAAAAAAAAAAAAA//////////8AAABLAAAADWZvcmNlLWNvbW1hbmQAAAAOAAAACi9iaW4vc2xlZXAAAAAOc291cmNlLWFkZHJlc3MAAAASAAAADjE5Mi4xNjguMS4wLzI0AAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEAwU+c5ui5A8+J/CFpjW8wCa52bEODA808WWQDCSuTG/eMXNf59v9Y8Pk0F1E9dGCosSNyVcB/hacUrc6He+i97+HJCyKavBsE6GDxrjRyxYqAlfcOXi/IVmaUGiO8OQ39d4GHrjToInKvExSUeleQyH4Y4/e27T/pILAqPFL3fyrvMLT5qU9QyIt6zIpa7GBP5+urouNavMprV3zsfIqNBbWypinOQAw823a5wN+zwXnhZrgQiHZ/USG09Y6k98y1dTVz8YHlQVR4D3lpTAsKDKJ5hCH9WU4fdf+lU8OyNGaJ/vz0XNqxcToe1l4numLTnaoSuH89pHryjqurB7lJKwAAAQ8AAAAHc3NoLXJzYQAAAQCaHvUIoPL1zWUHIXLvu96/HU1s/i4CAW2IIEuGgxCUCiFj6vyTyYtgxQxcmbfZf6eaITlS6XJZa7Qq4iaFZh75C1DXTX8labXhRSD4E2t//AIP9MC1rtQC5xo6FmbQ+BoKcDskr+mNACcbRSxs3IL3bwCfWDnIw2WbVox9ZdcthJKk4UoCW4ix4QwdHw7zlddlz++fGEEVhmTbll1SUkycGApPFBsAYRTMupUJcYPIeReBI/m8XfkoMk99bV8ZJQTAd7OekHY2/48Ff53jLmyDjP7kNw1F8OaPtkFs6dGJXta4krmaekPy87j+35In5hFj7yoOqvSbmYUkeX70/GGQ`
func TestParseCertWithOptions(t *testing.T) {
@@ -216,12 +219,12 @@ func TestHostKeyCert(t *testing.T) {
_, _, _, err = NewClientConn(c2, test.addr, config)
if (err == nil) != test.succeed {
t.Fatalf("NewClientConn(%q): %v", test.addr, err)
t.Errorf("NewClientConn(%q): %v", test.addr, err)
}
err = <-errc
if (err == nil) != test.succeed {
t.Fatalf("NewServerConn(%q): %v", test.addr, err)
t.Errorf("NewServerConn(%q): %v", test.addr, err)
}
}
}
@@ -235,7 +238,7 @@ func (s *legacyRSASigner) Sign(rand io.Reader, data []byte) (*Signature, error)
if !ok {
return nil, fmt.Errorf("invalid signer")
}
return v.SignWithAlgorithm(rand, data, SigAlgoRSA)
return v.SignWithAlgorithm(rand, data, KeyAlgoRSA)
}
func TestCertTypes(t *testing.T) {
@@ -248,10 +251,8 @@ func TestCertTypes(t *testing.T) {
{CertAlgoECDSA384v01, testSigners["ecdsap384"], ""},
{CertAlgoECDSA521v01, testSigners["ecdsap521"], ""},
{CertAlgoED25519v01, testSigners["ed25519"], ""},
{CertAlgoRSAv01, testSigners["rsa"], SigAlgoRSASHA2512},
{CertAlgoRSAv01, &legacyRSASigner{testSigners["rsa"]}, SigAlgoRSA},
{CertAlgoRSAv01, testSigners["rsa-sha2-256"], SigAlgoRSASHA2512},
{CertAlgoRSAv01, testSigners["rsa-sha2-512"], SigAlgoRSASHA2512},
{CertAlgoRSAv01, testSigners["rsa"], KeyAlgoRSASHA512},
{"legacyRSASigner", &legacyRSASigner{testSigners["rsa"]}, KeyAlgoRSA},
{CertAlgoDSAv01, testSigners["dsa"], ""},
}
+6 -7
View File
@@ -15,7 +15,6 @@ import (
"fmt"
"hash"
"io"
"io/ioutil"
"golang.org/x/crypto/chacha20"
"golang.org/x/crypto/internal/poly1305"
@@ -97,13 +96,13 @@ func streamCipherMode(skip int, createFunc func(key, iv []byte) (cipher.Stream,
// are not supported and will not be negotiated, even if explicitly requested in
// ClientConfig.Crypto.Ciphers.
var cipherModes = map[string]*cipherMode{
// Ciphers from RFC4344, which introduced many CTR-based ciphers. Algorithms
// 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)},
// Ciphers from RFC4345, which introduces security-improved arcfour ciphers.
// 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)},
@@ -111,7 +110,7 @@ var cipherModes = map[string]*cipherMode{
// 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."
// RFC4345 introduces improved versions of Arcfour.
// RFC 4345 introduces improved versions of Arcfour.
"arcfour": {16, 0, streamCipherMode(0, newRC4)},
// AEAD ciphers
@@ -497,7 +496,7 @@ func (c *cbcCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error)
// data, to make distinguishing between
// failing MAC and failing length check more
// difficult.
io.CopyN(ioutil.Discard, r, int64(c.oracleCamouflage))
io.CopyN(io.Discard, r, int64(c.oracleCamouflage))
}
}
return p, err
@@ -640,9 +639,9 @@ const chacha20Poly1305ID = "chacha20-poly1305@openssh.com"
// chacha20Poly1305Cipher implements the chacha20-poly1305@openssh.com
// AEAD, which is described here:
//
// https://tools.ietf.org/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00
// https://tools.ietf.org/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00
//
// the methods here also implement padding, which RFC4253 Section 6
// the methods here also implement padding, which RFC 4253 Section 6
// also requires of stream ciphers.
type chacha20Poly1305Cipher struct {
lengthKey [32]byte

Some files were not shown because too many files have changed in this diff Show More