pax_global_header 0000666 0000000 0000000 00000000064 14761622347 0014526 g ustar 00root root 0000000 0000000 52 comment=c5128231e831c2424a6fc40889e61e310ae81f66
golang-github-go-jose-go-jose-4.0.5/ 0000775 0000000 0000000 00000000000 14761622347 0017165 5 ustar 00root root 0000000 0000000 golang-github-go-jose-go-jose-4.0.5/.github/ 0000775 0000000 0000000 00000000000 14761622347 0020525 5 ustar 00root root 0000000 0000000 golang-github-go-jose-go-jose-4.0.5/.github/dependabot.yml 0000664 0000000 0000000 00000000317 14761622347 0023356 0 ustar 00root root 0000000 0000000 version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
golang-github-go-jose-go-jose-4.0.5/.github/workflows/ 0000775 0000000 0000000 00000000000 14761622347 0022562 5 ustar 00root root 0000000 0000000 golang-github-go-jose-go-jose-4.0.5/.github/workflows/cram.yml 0000664 0000000 0000000 00000000737 14761622347 0024236 0 ustar 00root root 0000000 0000000 name: Cram testing jose-util
on:
push:
branches: [ v3, main ]
pull_request: {}
jobs:
cram:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
- run: pip install cram
- uses: actions/setup-go@v5
- run: go build .
working-directory: jose-util
- run: PATH=$PWD:$PATH cram -v jose-util.t
working-directory: jose-util
golang-github-go-jose-go-jose-4.0.5/.github/workflows/go.yml 0000664 0000000 0000000 00000002332 14761622347 0023712 0 ustar 00root root 0000000 0000000 name: Go
on:
push:
branches: [ v3, main ]
pull_request:
branches: [ v3, main ]
jobs:
build:
strategy:
fail-fast: false
matrix:
GO_VERSION:
- "1.22.x"
- "1.21.x"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Go ${{ matrix.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: "${{ matrix.GO_VERSION }}"
- name: Build
run: go build -v ./...
- name: Test
run: go test -v ./...
govulncheck:
strategy:
matrix:
GO_VERSION:
# We only need to test vulns on one version
- "1.22.x"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Go ${{ matrix.GO_VERSION }}
uses: actions/setup-go@v5
with:
# When Go produces a security release, we want govulncheck to run
# against the most recently released Go version.
check-latest: true
go-version: "${{ matrix.GO_VERSION }}"
- name: Run govulncheck
run: go run golang.org/x/vuln/cmd/govulncheck@latest ./...
golang-github-go-jose-go-jose-4.0.5/.gitignore 0000664 0000000 0000000 00000000043 14761622347 0021152 0 ustar 00root root 0000000 0000000 jose-util/jose-util
jose-util.t.err golang-github-go-jose-go-jose-4.0.5/.golangci.yml 0000664 0000000 0000000 00000001737 14761622347 0021561 0 ustar 00root root 0000000 0000000 # https://github.com/golangci/golangci-lint
run:
skip-files:
- doc_test.go
modules-download-mode: readonly
linters:
enable-all: true
disable:
- gochecknoglobals
- goconst
- lll
- maligned
- nakedret
- scopelint
- unparam
- funlen # added in 1.18 (requires go-jose changes before it can be enabled)
linters-settings:
gocyclo:
min-complexity: 35
issues:
exclude-rules:
- text: "don't use ALL_CAPS in Go names"
linters:
- golint
- text: "hardcoded credentials"
linters:
- gosec
- text: "weak cryptographic primitive"
linters:
- gosec
- path: json/
linters:
- dupl
- errcheck
- gocritic
- gocyclo
- golint
- govet
- ineffassign
- staticcheck
- structcheck
- stylecheck
- unused
- path: _test\.go
linters:
- scopelint
- path: jwk.go
linters:
- gocyclo
golang-github-go-jose-go-jose-4.0.5/.travis.yml 0000664 0000000 0000000 00000002007 14761622347 0021275 0 ustar 00root root 0000000 0000000 language: go
matrix:
fast_finish: true
allow_failures:
- go: tip
go:
- "1.13.x"
- "1.14.x"
- tip
before_script:
- export PATH=$HOME/.local/bin:$PATH
before_install:
- go get -u github.com/mattn/goveralls github.com/wadey/gocovmerge
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.18.0
- pip install cram --user
script:
- go test -v -covermode=count -coverprofile=profile.cov .
- go test -v -covermode=count -coverprofile=cryptosigner/profile.cov ./cryptosigner
- go test -v -covermode=count -coverprofile=cipher/profile.cov ./cipher
- go test -v -covermode=count -coverprofile=jwt/profile.cov ./jwt
- go test -v ./json # no coverage for forked encoding/json package
- golangci-lint run
- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t # cram tests jose-util
- cd ..
after_success:
- gocovmerge *.cov */*.cov > merged.coverprofile
- goveralls -coverprofile merged.coverprofile -service=travis-ci
golang-github-go-jose-go-jose-4.0.5/CHANGELOG.md 0000664 0000000 0000000 00000006641 14761622347 0021005 0 ustar 00root root 0000000 0000000 # v4.0.4
## Fixed
- Reverted "Allow unmarshalling JSONWebKeySets with unsupported key types" as a
breaking change. See #136 / #137.
# v4.0.3
## Changed
- Allow unmarshalling JSONWebKeySets with unsupported key types (#130)
- Document that OpaqueKeyEncrypter can't be implemented (for now) (#129)
- Dependency updates
# v4.0.2
## Changed
- Improved documentation of Verify() to note that JSONWebKeySet is a supported
argument type (#104)
- Defined exported error values for missing x5c header and unsupported elliptic
curves error cases (#117)
# v4.0.1
## Fixed
- An attacker could send a JWE containing compressed data that used large
amounts of memory and CPU when decompressed by `Decrypt` or `DecryptMulti`.
Those functions now return an error if the decompressed data would exceed
250kB or 10x the compressed size (whichever is larger). Thanks to
Enze Wang@Alioth and Jianjun Chen@Zhongguancun Lab (@zer0yu and @chenjj)
for reporting.
# v4.0.0
This release makes some breaking changes in order to more thoroughly
address the vulnerabilities discussed in [Three New Attacks Against JSON Web
Tokens][1], "Sign/encrypt confusion", "Billion hash attack", and "Polyglot
token".
## Changed
- Limit JWT encryption types (exclude password or public key types) (#78)
- Enforce minimum length for HMAC keys (#85)
- jwt: match any audience in a list, rather than requiring all audiences (#81)
- jwt: accept only Compact Serialization (#75)
- jws: Add expected algorithms for signatures (#74)
- Require specifying expected algorithms for ParseEncrypted,
ParseSigned, ParseDetached, jwt.ParseEncrypted, jwt.ParseSigned,
jwt.ParseSignedAndEncrypted (#69, #74)
- Usually there is a small, known set of appropriate algorithms for a program
to use and it's a mistake to allow unexpected algorithms. For instance the
"billion hash attack" relies in part on programs accepting the PBES2
encryption algorithm and doing the necessary work even if they weren't
specifically configured to allow PBES2.
- Revert "Strip padding off base64 strings" (#82)
- The specs require base64url encoding without padding.
- Minimum supported Go version is now 1.21
## Added
- ParseSignedCompact, ParseSignedJSON, ParseEncryptedCompact, ParseEncryptedJSON.
- These allow parsing a specific serialization, as opposed to ParseSigned and
ParseEncrypted, which try to automatically detect which serialization was
provided. It's common to require a specific serialization for a specific
protocol - for instance JWT requires Compact serialization.
[1]: https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
# v3.0.2
## Fixed
- DecryptMulti: handle decompression error (#19)
## Changed
- jwe/CompactSerialize: improve performance (#67)
- Increase the default number of PBKDF2 iterations to 600k (#48)
- Return the proper algorithm for ECDSA keys (#45)
## Added
- Add Thumbprint support for opaque signers (#38)
# v3.0.1
## Fixed
- Security issue: an attacker specifying a large "p2c" value can cause
JSONWebEncryption.Decrypt and JSONWebEncryption.DecryptMulti to consume large
amounts of CPU, causing a DoS. Thanks to Matt Schwager (@mschwager) for the
disclosure and to Tom Tervoort for originally publishing the category of attack.
https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
golang-github-go-jose-go-jose-4.0.5/CONTRIBUTING.md 0000664 0000000 0000000 00000000657 14761622347 0021426 0 ustar 00root root 0000000 0000000 # Contributing
If you would like to contribute code to go-jose you can do so through GitHub by
forking the repository and sending a pull request.
When submitting code, please make every effort to follow existing conventions
and style in order to keep the code as readable as possible. Please also make
sure all tests pass by running `go test`, and format your code with `go fmt`.
We also recommend using `golint` and `errcheck`.
golang-github-go-jose-go-jose-4.0.5/LICENSE 0000664 0000000 0000000 00000026136 14761622347 0020202 0 ustar 00root root 0000000 0000000
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
golang-github-go-jose-go-jose-4.0.5/README.md 0000664 0000000 0000000 00000012377 14761622347 0020456 0 ustar 00root root 0000000 0000000 # Go JOSE
[](https://pkg.go.dev/github.com/go-jose/go-jose/v4)
[](https://pkg.go.dev/github.com/go-jose/go-jose/v4/jwt)
[](https://raw.githubusercontent.com/go-jose/go-jose/master/LICENSE)
[](https://github.com/go-jose/go-jose/actions)
Package jose aims to provide an implementation of the Javascript Object Signing
and Encryption set of standards. This includes support for JSON Web Encryption,
JSON Web Signature, and JSON Web Token standards.
## Overview
The implementation follows the
[JSON Web Encryption](https://dx.doi.org/10.17487/RFC7516) (RFC 7516),
[JSON Web Signature](https://dx.doi.org/10.17487/RFC7515) (RFC 7515), and
[JSON Web Token](https://dx.doi.org/10.17487/RFC7519) (RFC 7519) specifications.
Tables of supported algorithms are shown below. The library supports both
the compact and JWS/JWE JSON Serialization formats, and has optional support for
multiple recipients. It also comes with a small command-line utility
([`jose-util`](https://pkg.go.dev/github.com/go-jose/go-jose/jose-util))
for dealing with JOSE messages in a shell.
**Note**: We use a forked version of the `encoding/json` package from the Go
standard library which uses case-sensitive matching for member names (instead
of [case-insensitive matching](https://www.ietf.org/mail-archive/web/json/current/msg03763.html)).
This is to avoid differences in interpretation of messages between go-jose and
libraries in other languages.
### Versions
[Version 4](https://github.com/go-jose/go-jose)
([branch](https://github.com/go-jose/go-jose/tree/main),
[doc](https://pkg.go.dev/github.com/go-jose/go-jose/v4), [releases](https://github.com/go-jose/go-jose/releases)) is the current stable version:
import "github.com/go-jose/go-jose/v4"
The old [square/go-jose](https://github.com/square/go-jose) repo contains the prior v1 and v2 versions, which
are still useable but not actively developed anymore.
Version 3, in this repo, is still receiving security fixes but not functionality
updates.
### Supported algorithms
See below for a table of supported algorithms. Algorithm identifiers match
the names in the [JSON Web Algorithms](https://dx.doi.org/10.17487/RFC7518)
standard where possible. The Godoc reference has a list of constants.
Key encryption | Algorithm identifier(s)
:------------------------- | :------------------------------
RSA-PKCS#1v1.5 | RSA1_5
RSA-OAEP | RSA-OAEP, RSA-OAEP-256
AES key wrap | A128KW, A192KW, A256KW
AES-GCM key wrap | A128GCMKW, A192GCMKW, A256GCMKW
ECDH-ES + AES key wrap | ECDH-ES+A128KW, ECDH-ES+A192KW, ECDH-ES+A256KW
ECDH-ES (direct) | ECDH-ES1
Direct encryption | dir1
1. Not supported in multi-recipient mode
Signing / MAC | Algorithm identifier(s)
:------------------------- | :------------------------------
RSASSA-PKCS#1v1.5 | RS256, RS384, RS512
RSASSA-PSS | PS256, PS384, PS512
HMAC | HS256, HS384, HS512
ECDSA | ES256, ES384, ES512
Ed25519 | EdDSA2
2. Only available in version 2 of the package
Content encryption | Algorithm identifier(s)
:------------------------- | :------------------------------
AES-CBC+HMAC | A128CBC-HS256, A192CBC-HS384, A256CBC-HS512
AES-GCM | A128GCM, A192GCM, A256GCM
Compression | Algorithm identifiers(s)
:------------------------- | -------------------------------
DEFLATE (RFC 1951) | DEF
### Supported key types
See below for a table of supported key types. These are understood by the
library, and can be passed to corresponding functions such as `NewEncrypter` or
`NewSigner`. Each of these keys can also be wrapped in a JWK if desired, which
allows attaching a key id.
Algorithm(s) | Corresponding types
:------------------------- | -------------------------------
RSA | *[rsa.PublicKey](https://pkg.go.dev/crypto/rsa/#PublicKey), *[rsa.PrivateKey](https://pkg.go.dev/crypto/rsa/#PrivateKey)
ECDH, ECDSA | *[ecdsa.PublicKey](https://pkg.go.dev/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](https://pkg.go.dev/crypto/ecdsa/#PrivateKey)
EdDSA1 | [ed25519.PublicKey](https://pkg.go.dev/crypto/ed25519#PublicKey), [ed25519.PrivateKey](https://pkg.go.dev/crypto/ed25519#PrivateKey)
AES, HMAC | []byte
1. Only available in version 2 or later of the package
## Examples
[](https://pkg.go.dev/github.com/go-jose/go-jose/v4)
[](https://pkg.go.dev/github.com/go-jose/go-jose/v4/jwt)
Examples can be found in the Godoc
reference for this package. The
[`jose-util`](https://github.com/go-jose/go-jose/tree/main/jose-util)
subdirectory also contains a small command-line utility which might be useful
as an example as well.
golang-github-go-jose-go-jose-4.0.5/SECURITY.md 0000664 0000000 0000000 00000001252 14761622347 0020756 0 ustar 00root root 0000000 0000000 # Security Policy
This document explains how to contact the Let's Encrypt security team to report security vulnerabilities.
## Supported Versions
| Version | Supported |
| ------- | ----------|
| >= v3 | ✓ |
| v2 | ✗ |
| v1 | ✗ |
## Reporting a vulnerability
Please see [https://letsencrypt.org/contact/#security](https://letsencrypt.org/contact/#security) for the email address to report a vulnerability. Ensure that the subject line for your report contains the word `vulnerability` and is descriptive. Your email should be acknowledged within 24 hours. If you do not receive a response within 24 hours, please follow-up again with another email.
golang-github-go-jose-go-jose-4.0.5/asymmetric.go 0000664 0000000 0000000 00000037354 14761622347 0021705 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"crypto"
"crypto/aes"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"errors"
"fmt"
"math/big"
josecipher "github.com/go-jose/go-jose/v4/cipher"
"github.com/go-jose/go-jose/v4/json"
)
// A generic RSA-based encrypter/verifier
type rsaEncrypterVerifier struct {
publicKey *rsa.PublicKey
}
// A generic RSA-based decrypter/signer
type rsaDecrypterSigner struct {
privateKey *rsa.PrivateKey
}
// A generic EC-based encrypter/verifier
type ecEncrypterVerifier struct {
publicKey *ecdsa.PublicKey
}
type edEncrypterVerifier struct {
publicKey ed25519.PublicKey
}
// A key generator for ECDH-ES
type ecKeyGenerator struct {
size int
algID string
publicKey *ecdsa.PublicKey
}
// A generic EC-based decrypter/signer
type ecDecrypterSigner struct {
privateKey *ecdsa.PrivateKey
}
type edDecrypterSigner struct {
privateKey ed25519.PrivateKey
}
// newRSARecipient creates recipientKeyInfo based on the given key.
func newRSARecipient(keyAlg KeyAlgorithm, publicKey *rsa.PublicKey) (recipientKeyInfo, error) {
// Verify that key management algorithm is supported by this encrypter
switch keyAlg {
case RSA1_5, RSA_OAEP, RSA_OAEP_256:
default:
return recipientKeyInfo{}, ErrUnsupportedAlgorithm
}
if publicKey == nil {
return recipientKeyInfo{}, errors.New("invalid public key")
}
return recipientKeyInfo{
keyAlg: keyAlg,
keyEncrypter: &rsaEncrypterVerifier{
publicKey: publicKey,
},
}, nil
}
// newRSASigner creates a recipientSigInfo based on the given key.
func newRSASigner(sigAlg SignatureAlgorithm, privateKey *rsa.PrivateKey) (recipientSigInfo, error) {
// Verify that key management algorithm is supported by this encrypter
switch sigAlg {
case RS256, RS384, RS512, PS256, PS384, PS512:
default:
return recipientSigInfo{}, ErrUnsupportedAlgorithm
}
if privateKey == nil {
return recipientSigInfo{}, errors.New("invalid private key")
}
return recipientSigInfo{
sigAlg: sigAlg,
publicKey: staticPublicKey(&JSONWebKey{
Key: privateKey.Public(),
}),
signer: &rsaDecrypterSigner{
privateKey: privateKey,
},
}, nil
}
func newEd25519Signer(sigAlg SignatureAlgorithm, privateKey ed25519.PrivateKey) (recipientSigInfo, error) {
if sigAlg != EdDSA {
return recipientSigInfo{}, ErrUnsupportedAlgorithm
}
if privateKey == nil {
return recipientSigInfo{}, errors.New("invalid private key")
}
return recipientSigInfo{
sigAlg: sigAlg,
publicKey: staticPublicKey(&JSONWebKey{
Key: privateKey.Public(),
}),
signer: &edDecrypterSigner{
privateKey: privateKey,
},
}, nil
}
// newECDHRecipient creates recipientKeyInfo based on the given key.
func newECDHRecipient(keyAlg KeyAlgorithm, publicKey *ecdsa.PublicKey) (recipientKeyInfo, error) {
// Verify that key management algorithm is supported by this encrypter
switch keyAlg {
case ECDH_ES, ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW:
default:
return recipientKeyInfo{}, ErrUnsupportedAlgorithm
}
if publicKey == nil || !publicKey.Curve.IsOnCurve(publicKey.X, publicKey.Y) {
return recipientKeyInfo{}, errors.New("invalid public key")
}
return recipientKeyInfo{
keyAlg: keyAlg,
keyEncrypter: &ecEncrypterVerifier{
publicKey: publicKey,
},
}, nil
}
// newECDSASigner creates a recipientSigInfo based on the given key.
func newECDSASigner(sigAlg SignatureAlgorithm, privateKey *ecdsa.PrivateKey) (recipientSigInfo, error) {
// Verify that key management algorithm is supported by this encrypter
switch sigAlg {
case ES256, ES384, ES512:
default:
return recipientSigInfo{}, ErrUnsupportedAlgorithm
}
if privateKey == nil {
return recipientSigInfo{}, errors.New("invalid private key")
}
return recipientSigInfo{
sigAlg: sigAlg,
publicKey: staticPublicKey(&JSONWebKey{
Key: privateKey.Public(),
}),
signer: &ecDecrypterSigner{
privateKey: privateKey,
},
}, nil
}
// Encrypt the given payload and update the object.
func (ctx rsaEncrypterVerifier) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) {
encryptedKey, err := ctx.encrypt(cek, alg)
if err != nil {
return recipientInfo{}, err
}
return recipientInfo{
encryptedKey: encryptedKey,
header: &rawHeader{},
}, nil
}
// Encrypt the given payload. Based on the key encryption algorithm,
// this will either use RSA-PKCS1v1.5 or RSA-OAEP (with SHA-1 or SHA-256).
func (ctx rsaEncrypterVerifier) encrypt(cek []byte, alg KeyAlgorithm) ([]byte, error) {
switch alg {
case RSA1_5:
return rsa.EncryptPKCS1v15(RandReader, ctx.publicKey, cek)
case RSA_OAEP:
return rsa.EncryptOAEP(sha1.New(), RandReader, ctx.publicKey, cek, []byte{})
case RSA_OAEP_256:
return rsa.EncryptOAEP(sha256.New(), RandReader, ctx.publicKey, cek, []byte{})
}
return nil, ErrUnsupportedAlgorithm
}
// Decrypt the given payload and return the content encryption key.
func (ctx rsaDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
return ctx.decrypt(recipient.encryptedKey, headers.getAlgorithm(), generator)
}
// Decrypt the given payload. Based on the key encryption algorithm,
// this will either use RSA-PKCS1v1.5 or RSA-OAEP (with SHA-1 or SHA-256).
func (ctx rsaDecrypterSigner) decrypt(jek []byte, alg KeyAlgorithm, generator keyGenerator) ([]byte, error) {
// Note: The random reader on decrypt operations is only used for blinding,
// so stubbing is meanlingless (hence the direct use of rand.Reader).
switch alg {
case RSA1_5:
defer func() {
// DecryptPKCS1v15SessionKey sometimes panics on an invalid payload
// because of an index out of bounds error, which we want to ignore.
// This has been fixed in Go 1.3.1 (released 2014/08/13), the recover()
// only exists for preventing crashes with unpatched versions.
// See: https://groups.google.com/forum/#!topic/golang-dev/7ihX6Y6kx9k
// See: https://code.google.com/p/go/source/detail?r=58ee390ff31602edb66af41ed10901ec95904d33
_ = recover()
}()
// Perform some input validation.
keyBytes := ctx.privateKey.PublicKey.N.BitLen() / 8
if keyBytes != len(jek) {
// Input size is incorrect, the encrypted payload should always match
// the size of the public modulus (e.g. using a 2048 bit key will
// produce 256 bytes of output). Reject this since it's invalid input.
return nil, ErrCryptoFailure
}
cek, _, err := generator.genKey()
if err != nil {
return nil, ErrCryptoFailure
}
// When decrypting an RSA-PKCS1v1.5 payload, we must take precautions to
// prevent chosen-ciphertext attacks as described in RFC 3218, "Preventing
// the Million Message Attack on Cryptographic Message Syntax". We are
// therefore deliberately ignoring errors here.
_ = rsa.DecryptPKCS1v15SessionKey(rand.Reader, ctx.privateKey, jek, cek)
return cek, nil
case RSA_OAEP:
// Use rand.Reader for RSA blinding
return rsa.DecryptOAEP(sha1.New(), rand.Reader, ctx.privateKey, jek, []byte{})
case RSA_OAEP_256:
// Use rand.Reader for RSA blinding
return rsa.DecryptOAEP(sha256.New(), rand.Reader, ctx.privateKey, jek, []byte{})
}
return nil, ErrUnsupportedAlgorithm
}
// Sign the given payload
func (ctx rsaDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
var hash crypto.Hash
switch alg {
case RS256, PS256:
hash = crypto.SHA256
case RS384, PS384:
hash = crypto.SHA384
case RS512, PS512:
hash = crypto.SHA512
default:
return Signature{}, ErrUnsupportedAlgorithm
}
hasher := hash.New()
// According to documentation, Write() on hash never fails
_, _ = hasher.Write(payload)
hashed := hasher.Sum(nil)
var out []byte
var err error
switch alg {
case RS256, RS384, RS512:
// TODO(https://github.com/go-jose/go-jose/issues/40): As of go1.20, the
// random parameter is legacy and ignored, and it can be nil.
// https://cs.opensource.google/go/go/+/refs/tags/go1.20:src/crypto/rsa/pkcs1v15.go;l=263;bpv=0;bpt=1
out, err = rsa.SignPKCS1v15(RandReader, ctx.privateKey, hash, hashed)
case PS256, PS384, PS512:
out, err = rsa.SignPSS(RandReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash,
})
}
if err != nil {
return Signature{}, err
}
return Signature{
Signature: out,
protected: &rawHeader{},
}, nil
}
// Verify the given payload
func (ctx rsaEncrypterVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error {
var hash crypto.Hash
switch alg {
case RS256, PS256:
hash = crypto.SHA256
case RS384, PS384:
hash = crypto.SHA384
case RS512, PS512:
hash = crypto.SHA512
default:
return ErrUnsupportedAlgorithm
}
hasher := hash.New()
// According to documentation, Write() on hash never fails
_, _ = hasher.Write(payload)
hashed := hasher.Sum(nil)
switch alg {
case RS256, RS384, RS512:
return rsa.VerifyPKCS1v15(ctx.publicKey, hash, hashed, signature)
case PS256, PS384, PS512:
return rsa.VerifyPSS(ctx.publicKey, hash, hashed, signature, nil)
}
return ErrUnsupportedAlgorithm
}
// Encrypt the given payload and update the object.
func (ctx ecEncrypterVerifier) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) {
switch alg {
case ECDH_ES:
// ECDH-ES mode doesn't wrap a key, the shared secret is used directly as the key.
return recipientInfo{
header: &rawHeader{},
}, nil
case ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW:
default:
return recipientInfo{}, ErrUnsupportedAlgorithm
}
generator := ecKeyGenerator{
algID: string(alg),
publicKey: ctx.publicKey,
}
switch alg {
case ECDH_ES_A128KW:
generator.size = 16
case ECDH_ES_A192KW:
generator.size = 24
case ECDH_ES_A256KW:
generator.size = 32
}
kek, header, err := generator.genKey()
if err != nil {
return recipientInfo{}, err
}
block, err := aes.NewCipher(kek)
if err != nil {
return recipientInfo{}, err
}
jek, err := josecipher.KeyWrap(block, cek)
if err != nil {
return recipientInfo{}, err
}
return recipientInfo{
encryptedKey: jek,
header: &header,
}, nil
}
// Get key size for EC key generator
func (ctx ecKeyGenerator) keySize() int {
return ctx.size
}
// Get a content encryption key for ECDH-ES
func (ctx ecKeyGenerator) genKey() ([]byte, rawHeader, error) {
priv, err := ecdsa.GenerateKey(ctx.publicKey.Curve, RandReader)
if err != nil {
return nil, rawHeader{}, err
}
out := josecipher.DeriveECDHES(ctx.algID, []byte{}, []byte{}, priv, ctx.publicKey, ctx.size)
b, err := json.Marshal(&JSONWebKey{
Key: &priv.PublicKey,
})
if err != nil {
return nil, nil, err
}
headers := rawHeader{
headerEPK: makeRawMessage(b),
}
return out, headers, nil
}
// Decrypt the given payload and return the content encryption key.
func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
epk, err := headers.getEPK()
if err != nil {
return nil, errors.New("go-jose/go-jose: invalid epk header")
}
if epk == nil {
return nil, errors.New("go-jose/go-jose: missing epk header")
}
publicKey, ok := epk.Key.(*ecdsa.PublicKey)
if publicKey == nil || !ok {
return nil, errors.New("go-jose/go-jose: invalid epk header")
}
if !ctx.privateKey.Curve.IsOnCurve(publicKey.X, publicKey.Y) {
return nil, errors.New("go-jose/go-jose: invalid public key in epk header")
}
apuData, err := headers.getAPU()
if err != nil {
return nil, errors.New("go-jose/go-jose: invalid apu header")
}
apvData, err := headers.getAPV()
if err != nil {
return nil, errors.New("go-jose/go-jose: invalid apv header")
}
deriveKey := func(algID string, size int) []byte {
return josecipher.DeriveECDHES(algID, apuData.bytes(), apvData.bytes(), ctx.privateKey, publicKey, size)
}
var keySize int
algorithm := headers.getAlgorithm()
switch algorithm {
case ECDH_ES:
// ECDH-ES uses direct key agreement, no key unwrapping necessary.
return deriveKey(string(headers.getEncryption()), generator.keySize()), nil
case ECDH_ES_A128KW:
keySize = 16
case ECDH_ES_A192KW:
keySize = 24
case ECDH_ES_A256KW:
keySize = 32
default:
return nil, ErrUnsupportedAlgorithm
}
key := deriveKey(string(algorithm), keySize)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return josecipher.KeyUnwrap(block, recipient.encryptedKey)
}
func (ctx edDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
if alg != EdDSA {
return Signature{}, ErrUnsupportedAlgorithm
}
sig, err := ctx.privateKey.Sign(RandReader, payload, crypto.Hash(0))
if err != nil {
return Signature{}, err
}
return Signature{
Signature: sig,
protected: &rawHeader{},
}, nil
}
func (ctx edEncrypterVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error {
if alg != EdDSA {
return ErrUnsupportedAlgorithm
}
ok := ed25519.Verify(ctx.publicKey, payload, signature)
if !ok {
return errors.New("go-jose/go-jose: ed25519 signature failed to verify")
}
return nil
}
// Sign the given payload
func (ctx ecDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
var expectedBitSize int
var hash crypto.Hash
switch alg {
case ES256:
expectedBitSize = 256
hash = crypto.SHA256
case ES384:
expectedBitSize = 384
hash = crypto.SHA384
case ES512:
expectedBitSize = 521
hash = crypto.SHA512
}
curveBits := ctx.privateKey.Curve.Params().BitSize
if expectedBitSize != curveBits {
return Signature{}, fmt.Errorf("go-jose/go-jose: expected %d bit key, got %d bits instead", expectedBitSize, curveBits)
}
hasher := hash.New()
// According to documentation, Write() on hash never fails
_, _ = hasher.Write(payload)
hashed := hasher.Sum(nil)
r, s, err := ecdsa.Sign(RandReader, ctx.privateKey, hashed)
if err != nil {
return Signature{}, err
}
keyBytes := curveBits / 8
if curveBits%8 > 0 {
keyBytes++
}
// We serialize the outputs (r and s) into big-endian byte arrays and pad
// them with zeros on the left to make sure the sizes work out. Both arrays
// must be keyBytes long, and the output must be 2*keyBytes long.
rBytes := r.Bytes()
rBytesPadded := make([]byte, keyBytes)
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
sBytes := s.Bytes()
sBytesPadded := make([]byte, keyBytes)
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
out := append(rBytesPadded, sBytesPadded...)
return Signature{
Signature: out,
protected: &rawHeader{},
}, nil
}
// Verify the given payload
func (ctx ecEncrypterVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error {
var keySize int
var hash crypto.Hash
switch alg {
case ES256:
keySize = 32
hash = crypto.SHA256
case ES384:
keySize = 48
hash = crypto.SHA384
case ES512:
keySize = 66
hash = crypto.SHA512
default:
return ErrUnsupportedAlgorithm
}
if len(signature) != 2*keySize {
return fmt.Errorf("go-jose/go-jose: invalid signature size, have %d bytes, wanted %d", len(signature), 2*keySize)
}
hasher := hash.New()
// According to documentation, Write() on hash never fails
_, _ = hasher.Write(payload)
hashed := hasher.Sum(nil)
r := big.NewInt(0).SetBytes(signature[:keySize])
s := big.NewInt(0).SetBytes(signature[keySize:])
match := ecdsa.Verify(ctx.publicKey, hashed, r, s)
if !match {
return errors.New("go-jose/go-jose: ecdsa signature failed to verify")
}
return nil
}
golang-github-go-jose-go-jose-4.0.5/asymmetric_test.go 0000664 0000000 0000000 00000023104 14761622347 0022730 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"errors"
"io"
"testing"
)
func TestEd25519(t *testing.T) {
_, err := newEd25519Signer("XYZ", nil)
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
enc := new(edEncrypterVerifier)
enc.publicKey = ed25519PublicKey
err = enc.verifyPayload([]byte{}, []byte{}, "XYZ")
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
dec := new(edDecrypterSigner)
dec.privateKey = ed25519PrivateKey
_, err = dec.signPayload([]byte{}, "XYZ")
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
sig, err := dec.signPayload([]byte("This is a test"), "EdDSA")
if err != nil {
t.Error("should not error trying to sign payload")
}
if sig.Signature == nil {
t.Error("Check the signature")
}
err = enc.verifyPayload([]byte("This is a test"), sig.Signature, "EdDSA")
if err != nil {
t.Error("should not error trying to verify payload")
}
err = enc.verifyPayload([]byte("This is test number 2"), sig.Signature, "EdDSA")
if err == nil {
t.Error("should not error trying to verify payload")
}
}
func TestInvalidAlgorithmsRSA(t *testing.T) {
_, err := newRSARecipient("XYZ", nil)
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
_, err = newRSASigner("XYZ", nil)
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
enc := new(rsaEncrypterVerifier)
enc.publicKey = &rsaTestKey.PublicKey
_, err = enc.encryptKey([]byte{}, "XYZ")
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
err = enc.verifyPayload([]byte{}, []byte{}, "XYZ")
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
dec := new(rsaDecrypterSigner)
dec.privateKey = rsaTestKey
_, err = dec.decrypt(make([]byte, 256), "XYZ", randomKeyGenerator{size: 16})
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
_, err = dec.signPayload([]byte{}, "XYZ")
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
}
type failingKeyGenerator struct{}
func (ctx failingKeyGenerator) keySize() int {
return 0
}
func (ctx failingKeyGenerator) genKey() ([]byte, rawHeader, error) {
return nil, rawHeader{}, errors.New("failed to generate key")
}
func TestPKCSKeyGeneratorFailure(t *testing.T) {
dec := new(rsaDecrypterSigner)
dec.privateKey = rsaTestKey
generator := failingKeyGenerator{}
_, err := dec.decrypt(make([]byte, 256), RSA1_5, generator)
if err != ErrCryptoFailure {
t.Error("should return error on invalid algorithm")
}
}
func TestInvalidAlgorithmsEC(t *testing.T) {
_, err := newECDHRecipient("XYZ", nil)
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
_, err = newECDSASigner("XYZ", nil)
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
enc := new(ecEncrypterVerifier)
enc.publicKey = &ecTestKey256.PublicKey
_, err = enc.encryptKey([]byte{}, "XYZ")
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
}
func TestInvalidECKeyGen(t *testing.T) {
gen := ecKeyGenerator{
size: 16,
algID: "A128GCM",
publicKey: &ecTestKey256.PublicKey,
}
if gen.keySize() != 16 {
t.Error("ec key generator reported incorrect key size")
}
_, _, err := gen.genKey()
if err != nil {
t.Error("ec key generator failed to generate key", err)
}
}
func TestInvalidECDecrypt(t *testing.T) {
dec := ecDecrypterSigner{
privateKey: ecTestKey256,
}
generator := randomKeyGenerator{size: 16}
// Missing epk header
headers := rawHeader{}
if err := headers.set(headerAlgorithm, ECDH_ES); err != nil {
t.Fatal(err)
}
if _, err := dec.decryptKey(headers, nil, generator); err == nil {
t.Error("ec decrypter accepted object with missing epk header")
}
// Invalid epk header
if err := headers.set(headerEPK, &JSONWebKey{}); err == nil {
t.Fatal("epk header should be invalid")
}
if _, err := dec.decryptKey(headers, nil, generator); err == nil {
t.Error("ec decrypter accepted object with invalid epk header")
}
}
func TestDecryptWithIncorrectSize(t *testing.T) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Error(err)
return
}
dec := new(rsaDecrypterSigner)
dec.privateKey = priv
aes := newAESGCM(16)
keygen := randomKeyGenerator{
size: aes.keySize(),
}
payload := make([]byte, 254)
_, err = dec.decrypt(payload, RSA1_5, keygen)
if err == nil {
t.Error("Invalid payload size should return error")
}
payload = make([]byte, 257)
_, err = dec.decrypt(payload, RSA1_5, keygen)
if err == nil {
t.Error("Invalid payload size should return error")
}
}
func TestPKCSDecryptNeverFails(t *testing.T) {
// We don't want RSA-PKCS1 v1.5 decryption to ever fail, in order to prevent
// side-channel timing attacks (Bleichenbacher attack in particular).
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Error(err)
return
}
dec := new(rsaDecrypterSigner)
dec.privateKey = priv
aes := newAESGCM(16)
keygen := randomKeyGenerator{
size: aes.keySize(),
}
for i := 1; i < 50; i++ {
payload := make([]byte, 256)
_, err := io.ReadFull(rand.Reader, payload)
if err != nil {
t.Error("Unable to get random data:", err)
return
}
_, err = dec.decrypt(payload, RSA1_5, keygen)
if err != nil {
t.Error("PKCS1v1.5 decrypt should never fail:", err)
return
}
}
}
func BenchmarkPKCSDecryptWithValidPayloads(b *testing.B) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
enc := new(rsaEncrypterVerifier)
enc.publicKey = &priv.PublicKey
dec := new(rsaDecrypterSigner)
dec.privateKey = priv
aes := newAESGCM(32)
b.StopTimer()
b.ResetTimer()
for i := 0; i < b.N; i++ {
plaintext := make([]byte, 32)
_, err = io.ReadFull(rand.Reader, plaintext)
if err != nil {
panic(err)
}
ciphertext, err := enc.encrypt(plaintext, RSA1_5)
if err != nil {
panic(err)
}
keygen := randomKeyGenerator{
size: aes.keySize(),
}
b.StartTimer()
_, err = dec.decrypt(ciphertext, RSA1_5, keygen)
b.StopTimer()
if err != nil {
panic(err)
}
}
}
func BenchmarkPKCSDecryptWithInvalidPayloads(b *testing.B) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
enc := new(rsaEncrypterVerifier)
enc.publicKey = &priv.PublicKey
dec := new(rsaDecrypterSigner)
dec.privateKey = priv
aes := newAESGCM(16)
keygen := randomKeyGenerator{
size: aes.keySize(),
}
b.StopTimer()
b.ResetTimer()
for i := 0; i < b.N; i++ {
plaintext := make([]byte, 16)
_, err = io.ReadFull(rand.Reader, plaintext)
if err != nil {
panic(err)
}
ciphertext, err := enc.encrypt(plaintext, RSA1_5)
if err != nil {
panic(err)
}
// Do some simple scrambling
ciphertext[128] ^= 0xFF
b.StartTimer()
_, err = dec.decrypt(ciphertext, RSA1_5, keygen)
b.StopTimer()
if err != nil {
panic(err)
}
}
}
func TestInvalidEllipticCurve(t *testing.T) {
signer256 := ecDecrypterSigner{privateKey: ecTestKey256}
signer384 := ecDecrypterSigner{privateKey: ecTestKey384}
signer521 := ecDecrypterSigner{privateKey: ecTestKey521}
_, err := signer256.signPayload([]byte{}, ES384)
if err == nil {
t.Error("should not generate ES384 signature with P-256 key")
}
_, err = signer256.signPayload([]byte{}, ES512)
if err == nil {
t.Error("should not generate ES512 signature with P-256 key")
}
_, err = signer384.signPayload([]byte{}, ES256)
if err == nil {
t.Error("should not generate ES256 signature with P-384 key")
}
_, err = signer384.signPayload([]byte{}, ES512)
if err == nil {
t.Error("should not generate ES512 signature with P-384 key")
}
_, err = signer521.signPayload([]byte{}, ES256)
if err == nil {
t.Error("should not generate ES256 signature with P-521 key")
}
_, err = signer521.signPayload([]byte{}, ES384)
if err == nil {
t.Error("should not generate ES384 signature with P-521 key")
}
}
func TestInvalidECPublicKey(t *testing.T) {
// Invalid key
invalid := &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
Curve: elliptic.P256(),
X: fromBase64Int("MTEx"),
Y: fromBase64Int("MTEx"),
},
D: fromBase64Int("0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo"),
}
headers := rawHeader{}
if err := headers.set(headerAlgorithm, ECDH_ES); err != nil {
t.Fatal(err)
}
if err := headers.set(headerEPK, &JSONWebKey{Key: &invalid.PublicKey}); err != nil {
t.Fatal(err)
}
dec := ecDecrypterSigner{
privateKey: ecTestKey256,
}
if _, err := dec.decryptKey(headers, nil, randomKeyGenerator{size: 16}); err == nil {
t.Fatal("decrypter accepted JWS with invalid ECDH public key")
}
}
func TestInvalidAlgorithmEC(t *testing.T) {
err := ecEncrypterVerifier{publicKey: &ecTestKey256.PublicKey}.verifyPayload([]byte{}, []byte{}, "XYZ")
if err != ErrUnsupportedAlgorithm {
t.Fatal("should not accept invalid/unsupported algorithm")
}
}
golang-github-go-jose-go-jose-4.0.5/cipher/ 0000775 0000000 0000000 00000000000 14761622347 0020437 5 ustar 00root root 0000000 0000000 golang-github-go-jose-go-jose-4.0.5/cipher/cbc_hmac.go 0000664 0000000 0000000 00000012435 14761622347 0022512 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package josecipher
import (
"bytes"
"crypto/cipher"
"crypto/hmac"
"crypto/sha256"
"crypto/sha512"
"crypto/subtle"
"encoding/binary"
"errors"
"hash"
)
const (
nonceBytes = 16
)
// NewCBCHMAC instantiates a new AEAD based on CBC+HMAC.
func NewCBCHMAC(key []byte, newBlockCipher func([]byte) (cipher.Block, error)) (cipher.AEAD, error) {
keySize := len(key) / 2
integrityKey := key[:keySize]
encryptionKey := key[keySize:]
blockCipher, err := newBlockCipher(encryptionKey)
if err != nil {
return nil, err
}
var hash func() hash.Hash
switch keySize {
case 16:
hash = sha256.New
case 24:
hash = sha512.New384
case 32:
hash = sha512.New
}
return &cbcAEAD{
hash: hash,
blockCipher: blockCipher,
authtagBytes: keySize,
integrityKey: integrityKey,
}, nil
}
// An AEAD based on CBC+HMAC
type cbcAEAD struct {
hash func() hash.Hash
authtagBytes int
integrityKey []byte
blockCipher cipher.Block
}
func (ctx *cbcAEAD) NonceSize() int {
return nonceBytes
}
func (ctx *cbcAEAD) Overhead() int {
// Maximum overhead is block size (for padding) plus auth tag length, where
// the length of the auth tag is equivalent to the key size.
return ctx.blockCipher.BlockSize() + ctx.authtagBytes
}
// Seal encrypts and authenticates the plaintext.
func (ctx *cbcAEAD) Seal(dst, nonce, plaintext, data []byte) []byte {
// Output buffer -- must take care not to mangle plaintext input.
ciphertext := make([]byte, uint64(len(plaintext))+uint64(ctx.Overhead()))[:len(plaintext)]
copy(ciphertext, plaintext)
ciphertext = padBuffer(ciphertext, ctx.blockCipher.BlockSize())
cbc := cipher.NewCBCEncrypter(ctx.blockCipher, nonce)
cbc.CryptBlocks(ciphertext, ciphertext)
authtag := ctx.computeAuthTag(data, nonce, ciphertext)
ret, out := resize(dst, uint64(len(dst))+uint64(len(ciphertext))+uint64(len(authtag)))
copy(out, ciphertext)
copy(out[len(ciphertext):], authtag)
return ret
}
// Open decrypts and authenticates the ciphertext.
func (ctx *cbcAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
if len(ciphertext) < ctx.authtagBytes {
return nil, errors.New("go-jose/go-jose: invalid ciphertext (too short)")
}
offset := len(ciphertext) - ctx.authtagBytes
expectedTag := ctx.computeAuthTag(data, nonce, ciphertext[:offset])
match := subtle.ConstantTimeCompare(expectedTag, ciphertext[offset:])
if match != 1 {
return nil, errors.New("go-jose/go-jose: invalid ciphertext (auth tag mismatch)")
}
cbc := cipher.NewCBCDecrypter(ctx.blockCipher, nonce)
// Make copy of ciphertext buffer, don't want to modify in place
buffer := append([]byte{}, ciphertext[:offset]...)
if len(buffer)%ctx.blockCipher.BlockSize() > 0 {
return nil, errors.New("go-jose/go-jose: invalid ciphertext (invalid length)")
}
cbc.CryptBlocks(buffer, buffer)
// Remove padding
plaintext, err := unpadBuffer(buffer, ctx.blockCipher.BlockSize())
if err != nil {
return nil, err
}
ret, out := resize(dst, uint64(len(dst))+uint64(len(plaintext)))
copy(out, plaintext)
return ret, nil
}
// Compute an authentication tag
func (ctx *cbcAEAD) computeAuthTag(aad, nonce, ciphertext []byte) []byte {
buffer := make([]byte, uint64(len(aad))+uint64(len(nonce))+uint64(len(ciphertext))+8)
n := 0
n += copy(buffer, aad)
n += copy(buffer[n:], nonce)
n += copy(buffer[n:], ciphertext)
binary.BigEndian.PutUint64(buffer[n:], uint64(len(aad))*8)
// According to documentation, Write() on hash.Hash never fails.
hmac := hmac.New(ctx.hash, ctx.integrityKey)
_, _ = hmac.Write(buffer)
return hmac.Sum(nil)[:ctx.authtagBytes]
}
// resize ensures that the given slice has a capacity of at least n bytes.
// If the capacity of the slice is less than n, a new slice is allocated
// and the existing data will be copied.
func resize(in []byte, n uint64) (head, tail []byte) {
if uint64(cap(in)) >= n {
head = in[:n]
} else {
head = make([]byte, n)
copy(head, in)
}
tail = head[len(in):]
return
}
// Apply padding
func padBuffer(buffer []byte, blockSize int) []byte {
missing := blockSize - (len(buffer) % blockSize)
ret, out := resize(buffer, uint64(len(buffer))+uint64(missing))
padding := bytes.Repeat([]byte{byte(missing)}, missing)
copy(out, padding)
return ret
}
// Remove padding
func unpadBuffer(buffer []byte, blockSize int) ([]byte, error) {
if len(buffer)%blockSize != 0 {
return nil, errors.New("go-jose/go-jose: invalid padding")
}
last := buffer[len(buffer)-1]
count := int(last)
if count == 0 || count > blockSize || count > len(buffer) {
return nil, errors.New("go-jose/go-jose: invalid padding")
}
padding := bytes.Repeat([]byte{last}, count)
if !bytes.HasSuffix(buffer, padding) {
return nil, errors.New("go-jose/go-jose: invalid padding")
}
return buffer[:len(buffer)-count], nil
}
golang-github-go-jose-go-jose-4.0.5/cipher/cbc_hmac_test.go 0000664 0000000 0000000 00000035305 14761622347 0023552 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package josecipher
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"io"
"strings"
"testing"
)
func TestInvalidInputs(t *testing.T) {
key := []byte{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
}
nonce := []byte{
92, 80, 104, 49, 133, 25, 161, 215, 173, 101, 219, 211, 136, 91, 210, 145}
aead, _ := NewCBCHMAC(key, aes.NewCipher)
ciphertext := aead.Seal(nil, nonce, []byte("plaintext"), []byte("aad"))
// Changed AAD, must fail
_, err := aead.Open(nil, nonce, ciphertext, []byte("INVALID"))
if err == nil {
t.Error("must detect invalid aad")
}
// Empty ciphertext, must fail
_, err = aead.Open(nil, nonce, []byte{}, []byte("aad"))
if err == nil {
t.Error("must detect invalid/empty ciphertext")
}
// Corrupt ciphertext, must fail
corrupt := make([]byte, len(ciphertext))
copy(corrupt, ciphertext)
corrupt[0] ^= 0xFF
_, err = aead.Open(nil, nonce, corrupt, []byte("aad"))
if err == nil {
t.Error("must detect corrupt ciphertext")
}
// Corrupt authtag, must fail
copy(corrupt, ciphertext)
corrupt[len(ciphertext)-1] ^= 0xFF
_, err = aead.Open(nil, nonce, corrupt, []byte("aad"))
if err == nil {
t.Error("must detect corrupt authtag")
}
// Truncated data, must fail
_, err = aead.Open(nil, nonce, ciphertext[:10], []byte("aad"))
if err == nil {
t.Error("must detect corrupt authtag")
}
}
func TestVectorsAESCBC128(t *testing.T) {
// Source: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-29#appendix-A.2
plaintext := []byte{
76, 105, 118, 101, 32, 108, 111, 110, 103, 32, 97, 110, 100, 32,
112, 114, 111, 115, 112, 101, 114, 46}
aad := []byte{
101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 83, 85, 48, 69,
120, 88, 122, 85, 105, 76, 67, 74, 108, 98, 109, 77, 105, 79, 105,
74, 66, 77, 84, 73, 52, 81, 48, 74, 68, 76, 85, 104, 84, 77, 106, 85,
50, 73, 110, 48}
expectedCiphertext := []byte{
40, 57, 83, 181, 119, 33, 133, 148, 198, 185, 243, 24, 152, 230, 6,
75, 129, 223, 127, 19, 210, 82, 183, 230, 168, 33, 215, 104, 143,
112, 56, 102}
expectedAuthtag := []byte{
246, 17, 244, 190, 4, 95, 98, 3, 231, 0, 115, 157, 242, 203, 100,
191}
key := []byte{
4, 211, 31, 197, 84, 157, 252, 254, 11, 100, 157, 250, 63, 170, 106, 206,
107, 124, 212, 45, 111, 107, 9, 219, 200, 177, 0, 240, 143, 156, 44, 207}
nonce := []byte{
3, 22, 60, 12, 43, 67, 104, 105, 108, 108, 105, 99, 111, 116, 104, 101}
enc, err := NewCBCHMAC(key, aes.NewCipher)
out := enc.Seal(nil, nonce, plaintext, aad)
if err != nil {
t.Error("Unable to encrypt:", err)
return
}
if !bytes.Equal(out[:len(out)-16], expectedCiphertext) {
t.Error("Ciphertext did not match")
}
if !bytes.Equal(out[len(out)-16:], expectedAuthtag) {
t.Error("Auth tag did not match")
}
}
func TestVectorsAESCBC256(t *testing.T) {
// Source: https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05#section-5.4
plaintext := []byte{
0x41, 0x20, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20,
0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75,
0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65,
0x74, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62,
0x65, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x20, 0x69,
0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x20, 0x6f, 0x66,
0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x65, 0x6d, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f,
0x75, 0x74, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65}
aad := []byte{
0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63,
0x69, 0x70, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x41, 0x75, 0x67, 0x75, 0x73, 0x74, 0x65, 0x20,
0x4b, 0x65, 0x72, 0x63, 0x6b, 0x68, 0x6f, 0x66, 0x66, 0x73}
expectedCiphertext := []byte{
0x4a, 0xff, 0xaa, 0xad, 0xb7, 0x8c, 0x31, 0xc5, 0xda, 0x4b, 0x1b, 0x59, 0x0d, 0x10, 0xff, 0xbd,
0x3d, 0xd8, 0xd5, 0xd3, 0x02, 0x42, 0x35, 0x26, 0x91, 0x2d, 0xa0, 0x37, 0xec, 0xbc, 0xc7, 0xbd,
0x82, 0x2c, 0x30, 0x1d, 0xd6, 0x7c, 0x37, 0x3b, 0xcc, 0xb5, 0x84, 0xad, 0x3e, 0x92, 0x79, 0xc2,
0xe6, 0xd1, 0x2a, 0x13, 0x74, 0xb7, 0x7f, 0x07, 0x75, 0x53, 0xdf, 0x82, 0x94, 0x10, 0x44, 0x6b,
0x36, 0xeb, 0xd9, 0x70, 0x66, 0x29, 0x6a, 0xe6, 0x42, 0x7e, 0xa7, 0x5c, 0x2e, 0x08, 0x46, 0xa1,
0x1a, 0x09, 0xcc, 0xf5, 0x37, 0x0d, 0xc8, 0x0b, 0xfe, 0xcb, 0xad, 0x28, 0xc7, 0x3f, 0x09, 0xb3,
0xa3, 0xb7, 0x5e, 0x66, 0x2a, 0x25, 0x94, 0x41, 0x0a, 0xe4, 0x96, 0xb2, 0xe2, 0xe6, 0x60, 0x9e,
0x31, 0xe6, 0xe0, 0x2c, 0xc8, 0x37, 0xf0, 0x53, 0xd2, 0x1f, 0x37, 0xff, 0x4f, 0x51, 0x95, 0x0b,
0xbe, 0x26, 0x38, 0xd0, 0x9d, 0xd7, 0xa4, 0x93, 0x09, 0x30, 0x80, 0x6d, 0x07, 0x03, 0xb1, 0xf6}
expectedAuthtag := []byte{
0x4d, 0xd3, 0xb4, 0xc0, 0x88, 0xa7, 0xf4, 0x5c, 0x21, 0x68, 0x39, 0x64, 0x5b, 0x20, 0x12, 0xbf,
0x2e, 0x62, 0x69, 0xa8, 0xc5, 0x6a, 0x81, 0x6d, 0xbc, 0x1b, 0x26, 0x77, 0x61, 0x95, 0x5b, 0xc5}
key := []byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f}
nonce := []byte{
0x1a, 0xf3, 0x8c, 0x2d, 0xc2, 0xb9, 0x6f, 0xfd, 0xd8, 0x66, 0x94, 0x09, 0x23, 0x41, 0xbc, 0x04}
enc, err := NewCBCHMAC(key, aes.NewCipher)
out := enc.Seal(nil, nonce, plaintext, aad)
if err != nil {
t.Error("Unable to encrypt:", err)
return
}
if !bytes.Equal(out[:len(out)-32], expectedCiphertext) {
t.Error("Ciphertext did not match, got", out[:len(out)-32], "wanted", expectedCiphertext)
}
if !bytes.Equal(out[len(out)-32:], expectedAuthtag) {
t.Error("Auth tag did not match, got", out[len(out)-32:], "wanted", expectedAuthtag)
}
}
func TestAESCBCRoundtrip(t *testing.T) {
key128 := []byte{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
key192 := []byte{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7}
key256 := []byte{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
nonce := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
RunRoundtrip(t, key128, nonce)
RunRoundtrip(t, key192, nonce)
RunRoundtrip(t, key256, nonce)
}
func RunRoundtrip(t *testing.T, key, nonce []byte) {
aead, err := NewCBCHMAC(key, aes.NewCipher)
if err != nil {
panic(err)
}
if aead.NonceSize() != len(nonce) {
panic("invalid nonce")
}
// Test pre-existing data in dst buffer
dst := []byte{15, 15, 15, 15}
plaintext := []byte{0, 0, 0, 0}
aad := []byte{4, 3, 2, 1}
result := aead.Seal(dst, nonce, plaintext, aad)
if !bytes.Equal(dst, result[:4]) {
t.Error("Existing data in dst not preserved")
}
// Test pre-existing (empty) dst buffer with sufficient capacity
dst = make([]byte, 256)[:0]
result, err = aead.Open(dst, nonce, result[4:], aad)
if err != nil {
panic(err)
}
if !bytes.Equal(result, plaintext) {
t.Error("Plaintext does not match output")
}
}
func TestAESCBCOverhead(t *testing.T) {
aead, err := NewCBCHMAC(make([]byte, 32), aes.NewCipher)
if err != nil {
panic(err)
}
if aead.Overhead() != 32 {
t.Error("CBC-HMAC reports incorrect overhead value")
}
}
func TestPadding(t *testing.T) {
for i := 0; i < 256; i++ {
slice := make([]byte, i)
padded := padBuffer(slice, 16)
if len(padded)%16 != 0 {
t.Error("failed to pad slice properly", i)
return
}
unpadded, err := unpadBuffer(padded, 16)
if err != nil || len(unpadded) != i {
t.Error("failed to unpad slice properly", i)
return
}
}
}
func TestInvalidKey(t *testing.T) {
key := make([]byte, 30)
_, err := NewCBCHMAC(key, aes.NewCipher)
if err == nil {
t.Error("should not be able to instantiate CBC-HMAC with invalid key")
}
}
func TestTruncatedCiphertext(t *testing.T) {
key := make([]byte, 32)
nonce := make([]byte, 16)
data := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
t.Fatal(err)
}
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
t.Fatal(err)
}
aead, err := NewCBCHMAC(key, aes.NewCipher)
if err != nil {
t.Fatal(err)
}
ctx := aead.(*cbcAEAD)
ct := aead.Seal(nil, nonce, data, nil)
// Truncated ciphertext, but with correct auth tag
truncated, tail := resize(ct[:len(ct)-ctx.authtagBytes-2], uint64(len(ct))-2)
copy(tail, ctx.computeAuthTag(nil, nonce, truncated[:len(truncated)-ctx.authtagBytes]))
// Open should fail
_, err = aead.Open(nil, nonce, truncated, nil)
if err == nil {
t.Error("open on truncated ciphertext should fail")
}
}
func TestInvalidPaddingOpen(t *testing.T) {
key := make([]byte, 32)
nonce := make([]byte, 16)
// Plaintext with invalid padding
plaintext := padBuffer(make([]byte, 28), aes.BlockSize)
plaintext[len(plaintext)-1] = 0xFF
if _, err := io.ReadFull(rand.Reader, key); err != nil {
t.Fatal(err)
}
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
t.Fatal(err)
}
block, _ := aes.NewCipher(key)
cbc := cipher.NewCBCEncrypter(block, nonce)
buffer := append([]byte{}, plaintext...)
cbc.CryptBlocks(buffer, buffer)
aead, _ := NewCBCHMAC(key, aes.NewCipher)
ctx := aead.(*cbcAEAD)
// Mutated ciphertext, but with correct auth tag
size := uint64(len(buffer))
ciphertext, tail := resize(buffer, size+(uint64(len(key))/2))
copy(tail, ctx.computeAuthTag(nil, nonce, ciphertext[:size]))
// Open should fail (b/c of invalid padding, even though tag matches)
_, err := aead.Open(nil, nonce, ciphertext, nil)
if err == nil || !strings.Contains(err.Error(), "invalid padding") {
t.Error("no or unexpected error on open with invalid padding:", err)
}
}
func TestInvalidPadding(t *testing.T) {
for i := 0; i < 256; i++ {
slice := make([]byte, i)
padded := padBuffer(slice, 16)
if len(padded)%16 != 0 {
t.Error("failed to pad slice properly", i)
return
}
paddingBytes := 16 - (i % 16)
// Mutate padding for testing
for j := 1; j <= paddingBytes; j++ {
mutated := make([]byte, len(padded))
copy(mutated, padded)
mutated[len(mutated)-j] ^= 0xFF
_, err := unpadBuffer(mutated, 16)
if err == nil {
t.Error("unpad on invalid padding should fail", i)
return
}
}
// Test truncated padding
_, err := unpadBuffer(padded[:len(padded)-1], 16)
if err == nil {
t.Error("unpad on truncated padding should fail", i)
return
}
}
}
func TestZeroLengthPadding(t *testing.T) {
data := make([]byte, 16)
_, err := unpadBuffer(data, 16)
if err == nil {
t.Error("padding with 0x00 should never be valid")
}
}
func benchEncryptCBCHMAC(b *testing.B, keySize, chunkSize int) {
key := make([]byte, keySize*2)
nonce := make([]byte, 16)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
b.Fatal(err)
}
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
b.Fatal(err)
}
chunk := make([]byte, chunkSize)
aead, err := NewCBCHMAC(key, aes.NewCipher)
if err != nil {
b.Fatal(err)
}
b.SetBytes(int64(chunkSize))
b.ResetTimer()
for i := 0; i < b.N; i++ {
aead.Seal(nil, nonce, chunk, nil)
}
}
func benchDecryptCBCHMAC(b *testing.B, keySize, chunkSize int) {
key := make([]byte, keySize*2)
nonce := make([]byte, 16)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
b.Fatal(err)
}
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
b.Fatal(err)
}
chunk := make([]byte, chunkSize)
aead, err := NewCBCHMAC(key, aes.NewCipher)
if err != nil {
b.Fatal(err)
}
out := aead.Seal(nil, nonce, chunk, nil)
b.SetBytes(int64(chunkSize))
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, err = aead.Open(nil, nonce, out, nil); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkEncryptAES128_CBCHMAC_1k(b *testing.B) {
benchEncryptCBCHMAC(b, 16, 1024)
}
func BenchmarkEncryptAES128_CBCHMAC_64k(b *testing.B) {
benchEncryptCBCHMAC(b, 16, 65536)
}
func BenchmarkEncryptAES128_CBCHMAC_1MB(b *testing.B) {
benchEncryptCBCHMAC(b, 16, 1048576)
}
func BenchmarkEncryptAES128_CBCHMAC_64MB(b *testing.B) {
benchEncryptCBCHMAC(b, 16, 67108864)
}
func BenchmarkDecryptAES128_CBCHMAC_1k(b *testing.B) {
benchDecryptCBCHMAC(b, 16, 1024)
}
func BenchmarkDecryptAES128_CBCHMAC_64k(b *testing.B) {
benchDecryptCBCHMAC(b, 16, 65536)
}
func BenchmarkDecryptAES128_CBCHMAC_1MB(b *testing.B) {
benchDecryptCBCHMAC(b, 16, 1048576)
}
func BenchmarkDecryptAES128_CBCHMAC_64MB(b *testing.B) {
benchDecryptCBCHMAC(b, 16, 67108864)
}
func BenchmarkEncryptAES192_CBCHMAC_64k(b *testing.B) {
benchEncryptCBCHMAC(b, 24, 65536)
}
func BenchmarkEncryptAES192_CBCHMAC_1MB(b *testing.B) {
benchEncryptCBCHMAC(b, 24, 1048576)
}
func BenchmarkEncryptAES192_CBCHMAC_64MB(b *testing.B) {
benchEncryptCBCHMAC(b, 24, 67108864)
}
func BenchmarkDecryptAES192_CBCHMAC_1k(b *testing.B) {
benchDecryptCBCHMAC(b, 24, 1024)
}
func BenchmarkDecryptAES192_CBCHMAC_64k(b *testing.B) {
benchDecryptCBCHMAC(b, 24, 65536)
}
func BenchmarkDecryptAES192_CBCHMAC_1MB(b *testing.B) {
benchDecryptCBCHMAC(b, 24, 1048576)
}
func BenchmarkDecryptAES192_CBCHMAC_64MB(b *testing.B) {
benchDecryptCBCHMAC(b, 24, 67108864)
}
func BenchmarkEncryptAES256_CBCHMAC_64k(b *testing.B) {
benchEncryptCBCHMAC(b, 32, 65536)
}
func BenchmarkEncryptAES256_CBCHMAC_1MB(b *testing.B) {
benchEncryptCBCHMAC(b, 32, 1048576)
}
func BenchmarkEncryptAES256_CBCHMAC_64MB(b *testing.B) {
benchEncryptCBCHMAC(b, 32, 67108864)
}
func BenchmarkDecryptAES256_CBCHMAC_1k(b *testing.B) {
benchDecryptCBCHMAC(b, 32, 1032)
}
func BenchmarkDecryptAES256_CBCHMAC_64k(b *testing.B) {
benchDecryptCBCHMAC(b, 32, 65536)
}
func BenchmarkDecryptAES256_CBCHMAC_1MB(b *testing.B) {
benchDecryptCBCHMAC(b, 32, 1048576)
}
func BenchmarkDecryptAES256_CBCHMAC_64MB(b *testing.B) {
benchDecryptCBCHMAC(b, 32, 67108864)
}
golang-github-go-jose-go-jose-4.0.5/cipher/concat_kdf.go 0000664 0000000 0000000 00000003516 14761622347 0023066 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package josecipher
import (
"crypto"
"encoding/binary"
"hash"
"io"
)
type concatKDF struct {
z, info []byte
i uint32
cache []byte
hasher hash.Hash
}
// NewConcatKDF builds a KDF reader based on the given inputs.
func NewConcatKDF(hash crypto.Hash, z, algID, ptyUInfo, ptyVInfo, supPubInfo, supPrivInfo []byte) io.Reader {
buffer := make([]byte, uint64(len(algID))+uint64(len(ptyUInfo))+uint64(len(ptyVInfo))+uint64(len(supPubInfo))+uint64(len(supPrivInfo)))
n := 0
n += copy(buffer, algID)
n += copy(buffer[n:], ptyUInfo)
n += copy(buffer[n:], ptyVInfo)
n += copy(buffer[n:], supPubInfo)
copy(buffer[n:], supPrivInfo)
hasher := hash.New()
return &concatKDF{
z: z,
info: buffer,
hasher: hasher,
cache: []byte{},
i: 1,
}
}
func (ctx *concatKDF) Read(out []byte) (int, error) {
copied := copy(out, ctx.cache)
ctx.cache = ctx.cache[copied:]
for copied < len(out) {
ctx.hasher.Reset()
// Write on a hash.Hash never fails
_ = binary.Write(ctx.hasher, binary.BigEndian, ctx.i)
_, _ = ctx.hasher.Write(ctx.z)
_, _ = ctx.hasher.Write(ctx.info)
hash := ctx.hasher.Sum(nil)
chunkCopied := copy(out[copied:], hash)
copied += chunkCopied
ctx.cache = hash[chunkCopied:]
ctx.i++
}
return copied, nil
}
golang-github-go-jose-go-jose-4.0.5/cipher/concat_kdf_test.go 0000664 0000000 0000000 00000007357 14761622347 0024134 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package josecipher
import (
"bytes"
"crypto"
"testing"
)
// Taken from: https://tools.ietf.org/id/draft-ietf-jose-json-web-algorithms-38.txt
func TestVectorConcatKDF(t *testing.T) {
z := []byte{
158, 86, 217, 29, 129, 113, 53, 211, 114, 131, 66, 131, 191, 132,
38, 156, 251, 49, 110, 163, 218, 128, 106, 72, 246, 218, 167, 121,
140, 254, 144, 196}
algID := []byte{0, 0, 0, 7, 65, 49, 50, 56, 71, 67, 77}
ptyUInfo := []byte{0, 0, 0, 5, 65, 108, 105, 99, 101}
ptyVInfo := []byte{0, 0, 0, 3, 66, 111, 98}
supPubInfo := []byte{0, 0, 0, 128}
supPrivInfo := []byte{}
expected := []byte{
86, 170, 141, 234, 248, 35, 109, 32, 92, 34, 40, 205, 113, 167, 16, 26}
ckdf := NewConcatKDF(crypto.SHA256, z, algID, ptyUInfo, ptyVInfo, supPubInfo, supPrivInfo)
out0 := make([]byte, 9)
out1 := make([]byte, 7)
read0, err := ckdf.Read(out0)
if err != nil {
t.Error("error when reading from concat kdf reader", err)
return
}
read1, err := ckdf.Read(out1)
if err != nil {
t.Error("error when reading from concat kdf reader", err)
return
}
if read0+read1 != len(out0)+len(out1) {
t.Error("did not receive enough bytes from concat kdf reader")
return
}
out := []byte{}
out = append(out, out0...)
out = append(out, out1...)
if !bytes.Equal(out, expected) {
t.Error("did not receive expected output from concat kdf reader")
return
}
}
func TestCache(t *testing.T) {
z := []byte{
158, 86, 217, 29, 129, 113, 53, 211, 114, 131, 66, 131, 191, 132,
38, 156, 251, 49, 110, 163, 218, 128, 106, 72, 246, 218, 167, 121,
140, 254, 144, 196}
algID := []byte{1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4}
ptyUInfo := []byte{1, 2, 3, 4}
ptyVInfo := []byte{4, 3, 2, 1}
supPubInfo := []byte{}
supPrivInfo := []byte{}
outputs := [][]byte{}
// Read the same amount of data in different chunk sizes
chunkSizes := []int{1, 2, 4, 8, 16, 32, 64, 128, 256, 512}
for _, c := range chunkSizes {
out := make([]byte, 1024)
reader := NewConcatKDF(crypto.SHA256, z, algID, ptyUInfo, ptyVInfo, supPubInfo, supPrivInfo)
for i := 0; i < 1024; i += c {
_, _ = reader.Read(out[i : i+c])
}
outputs = append(outputs, out)
}
for i := range outputs {
if !bytes.Equal(outputs[i], outputs[(i+1)%len(outputs)]) {
t.Error("not all outputs from KDF matched")
}
}
}
func benchmarkKDF(b *testing.B, total int) {
z := []byte{
158, 86, 217, 29, 129, 113, 53, 211, 114, 131, 66, 131, 191, 132,
38, 156, 251, 49, 110, 163, 218, 128, 106, 72, 246, 218, 167, 121,
140, 254, 144, 196}
algID := []byte{1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4}
ptyUInfo := []byte{1, 2, 3, 4}
ptyVInfo := []byte{4, 3, 2, 1}
supPubInfo := []byte{}
supPrivInfo := []byte{}
out := make([]byte, total)
reader := NewConcatKDF(crypto.SHA256, z, algID, ptyUInfo, ptyVInfo, supPubInfo, supPrivInfo)
b.ResetTimer()
b.SetBytes(int64(total))
for i := 0; i < b.N; i++ {
_, _ = reader.Read(out)
}
}
func BenchmarkConcatKDF_1k(b *testing.B) {
benchmarkKDF(b, 1024)
}
func BenchmarkConcatKDF_64k(b *testing.B) {
benchmarkKDF(b, 65536)
}
func BenchmarkConcatKDF_1MB(b *testing.B) {
benchmarkKDF(b, 1048576)
}
func BenchmarkConcatKDF_64MB(b *testing.B) {
benchmarkKDF(b, 67108864)
}
golang-github-go-jose-go-jose-4.0.5/cipher/ecdh_es.go 0000664 0000000 0000000 00000005416 14761622347 0022366 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package josecipher
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"encoding/binary"
)
// DeriveECDHES derives a shared encryption key using ECDH/ConcatKDF as described in JWE/JWA.
// It is an error to call this function with a private/public key that are not on the same
// curve. Callers must ensure that the keys are valid before calling this function. Output
// size may be at most 1<<16 bytes (64 KiB).
func DeriveECDHES(alg string, apuData, apvData []byte, priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, size int) []byte {
if size > 1<<16 {
panic("ECDH-ES output size too large, must be less than or equal to 1<<16")
}
// algId, partyUInfo, partyVInfo inputs must be prefixed with the length
algID := lengthPrefixed([]byte(alg))
ptyUInfo := lengthPrefixed(apuData)
ptyVInfo := lengthPrefixed(apvData)
// suppPubInfo is the encoded length of the output size in bits
supPubInfo := make([]byte, 4)
binary.BigEndian.PutUint32(supPubInfo, uint32(size)*8)
if !priv.PublicKey.Curve.IsOnCurve(pub.X, pub.Y) {
panic("public key not on same curve as private key")
}
z, _ := priv.Curve.ScalarMult(pub.X, pub.Y, priv.D.Bytes())
zBytes := z.Bytes()
// Note that calling z.Bytes() on a big.Int may strip leading zero bytes from
// the returned byte array. This can lead to a problem where zBytes will be
// shorter than expected which breaks the key derivation. Therefore we must pad
// to the full length of the expected coordinate here before calling the KDF.
octSize := dSize(priv.Curve)
if len(zBytes) != octSize {
zBytes = append(bytes.Repeat([]byte{0}, octSize-len(zBytes)), zBytes...)
}
reader := NewConcatKDF(crypto.SHA256, zBytes, algID, ptyUInfo, ptyVInfo, supPubInfo, []byte{})
key := make([]byte, size)
// Read on the KDF will never fail
_, _ = reader.Read(key)
return key
}
// dSize returns the size in octets for a coordinate on a elliptic curve.
func dSize(curve elliptic.Curve) int {
order := curve.Params().P
bitLen := order.BitLen()
size := bitLen / 8
if bitLen%8 != 0 {
size++
}
return size
}
func lengthPrefixed(data []byte) []byte {
out := make([]byte, len(data)+4)
binary.BigEndian.PutUint32(out, uint32(len(data)))
copy(out[4:], data)
return out
}
golang-github-go-jose-go-jose-4.0.5/cipher/ecdh_es_test.go 0000664 0000000 0000000 00000006172 14761622347 0023425 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package josecipher
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"encoding/base64"
"math/big"
"testing"
)
// Example keys from JWA, Appendix C
var aliceKey = &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
Curve: elliptic.P256(),
X: fromBase64Int("gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0="),
Y: fromBase64Int("SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps="),
},
D: fromBase64Int("0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo="),
}
var bobKey = &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
Curve: elliptic.P256(),
X: fromBase64Int("weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ="),
Y: fromBase64Int("e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck="),
},
D: fromBase64Int("VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw="),
}
// Build big int from base64-encoded string. Strips whitespace (for testing).
func fromBase64Int(data string) *big.Int {
val, err := base64.URLEncoding.DecodeString(data)
if err != nil {
panic("Invalid test data: " + err.Error())
}
return new(big.Int).SetBytes(val)
}
func TestVectorECDHES(t *testing.T) {
apuData := []byte("Alice")
apvData := []byte("Bob")
expected := []byte{
86, 170, 141, 234, 248, 35, 109, 32, 92, 34, 40, 205, 113, 167, 16, 26}
output := DeriveECDHES("A128GCM", apuData, apvData, bobKey, &aliceKey.PublicKey, 16)
if !bytes.Equal(output, expected) {
t.Error("output did not match what we expect, got", output, "wanted", expected)
}
}
func TestInvalidECPublicKey(t *testing.T) {
defer func() {
if r := recover(); r == nil {
panic("panic expected")
}
}()
// Invalid key
invalid := &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
Curve: elliptic.P256(),
X: fromBase64Int("MTEx"),
Y: fromBase64Int("MTEx"),
},
D: fromBase64Int("0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo="),
}
DeriveECDHES("A128GCM", []byte{}, []byte{}, bobKey, &invalid.PublicKey, 16)
t.Fatal("should panic if public key was invalid")
}
func BenchmarkECDHES_128(b *testing.B) {
apuData := []byte("APU")
apvData := []byte("APV")
b.ResetTimer()
for i := 0; i < b.N; i++ {
DeriveECDHES("ID", apuData, apvData, bobKey, &aliceKey.PublicKey, 16)
}
}
func BenchmarkECDHES_192(b *testing.B) {
apuData := []byte("APU")
apvData := []byte("APV")
b.ResetTimer()
for i := 0; i < b.N; i++ {
DeriveECDHES("ID", apuData, apvData, bobKey, &aliceKey.PublicKey, 24)
}
}
func BenchmarkECDHES_256(b *testing.B) {
apuData := []byte("APU")
apvData := []byte("APV")
b.ResetTimer()
for i := 0; i < b.N; i++ {
DeriveECDHES("ID", apuData, apvData, bobKey, &aliceKey.PublicKey, 32)
}
}
golang-github-go-jose-go-jose-4.0.5/cipher/key_wrap.go 0000664 0000000 0000000 00000005033 14761622347 0022610 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package josecipher
import (
"crypto/cipher"
"crypto/subtle"
"encoding/binary"
"errors"
)
var defaultIV = []byte{0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6}
// KeyWrap implements NIST key wrapping; it wraps a content encryption key (cek) with the given block cipher.
func KeyWrap(block cipher.Block, cek []byte) ([]byte, error) {
if len(cek)%8 != 0 {
return nil, errors.New("go-jose/go-jose: key wrap input must be 8 byte blocks")
}
n := len(cek) / 8
r := make([][]byte, n)
for i := range r {
r[i] = make([]byte, 8)
copy(r[i], cek[i*8:])
}
buffer := make([]byte, 16)
tBytes := make([]byte, 8)
copy(buffer, defaultIV)
for t := 0; t < 6*n; t++ {
copy(buffer[8:], r[t%n])
block.Encrypt(buffer, buffer)
binary.BigEndian.PutUint64(tBytes, uint64(t+1))
for i := 0; i < 8; i++ {
buffer[i] ^= tBytes[i]
}
copy(r[t%n], buffer[8:])
}
out := make([]byte, (n+1)*8)
copy(out, buffer[:8])
for i := range r {
copy(out[(i+1)*8:], r[i])
}
return out, nil
}
// KeyUnwrap implements NIST key unwrapping; it unwraps a content encryption key (cek) with the given block cipher.
func KeyUnwrap(block cipher.Block, ciphertext []byte) ([]byte, error) {
if len(ciphertext)%8 != 0 {
return nil, errors.New("go-jose/go-jose: key wrap input must be 8 byte blocks")
}
n := (len(ciphertext) / 8) - 1
r := make([][]byte, n)
for i := range r {
r[i] = make([]byte, 8)
copy(r[i], ciphertext[(i+1)*8:])
}
buffer := make([]byte, 16)
tBytes := make([]byte, 8)
copy(buffer[:8], ciphertext[:8])
for t := 6*n - 1; t >= 0; t-- {
binary.BigEndian.PutUint64(tBytes, uint64(t+1))
for i := 0; i < 8; i++ {
buffer[i] ^= tBytes[i]
}
copy(buffer[8:], r[t%n])
block.Decrypt(buffer, buffer)
copy(r[t%n], buffer[8:])
}
if subtle.ConstantTimeCompare(buffer[:8], defaultIV) == 0 {
return nil, errors.New("go-jose/go-jose: failed to unwrap key")
}
out := make([]byte, n*8)
for i := range r {
copy(out[i*8:], r[i])
}
return out, nil
}
golang-github-go-jose-go-jose-4.0.5/cipher/key_wrap_test.go 0000664 0000000 0000000 00000007676 14761622347 0023666 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package josecipher
import (
"bytes"
"crypto/aes"
"encoding/hex"
"testing"
)
func TestAesKeyWrap(t *testing.T) {
// Test vectors from: http://csrc.nist.gov/groups/ST/toolkit/documents/kms/key-wrap.pdf
kek0, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0F")
cek0, _ := hex.DecodeString("00112233445566778899AABBCCDDEEFF")
expected0, _ := hex.DecodeString("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5")
kek1, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0F1011121314151617")
cek1, _ := hex.DecodeString("00112233445566778899AABBCCDDEEFF")
expected1, _ := hex.DecodeString("96778B25AE6CA435F92B5B97C050AED2468AB8A17AD84E5D")
kek2, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F")
cek2, _ := hex.DecodeString("00112233445566778899AABBCCDDEEFF0001020304050607")
expected2, _ := hex.DecodeString("A8F9BC1612C68B3FF6E6F4FBE30E71E4769C8B80A32CB8958CD5D17D6B254DA1")
block0, _ := aes.NewCipher(kek0)
block1, _ := aes.NewCipher(kek1)
block2, _ := aes.NewCipher(kek2)
out0, _ := KeyWrap(block0, cek0)
out1, _ := KeyWrap(block1, cek1)
out2, _ := KeyWrap(block2, cek2)
if !bytes.Equal(out0, expected0) {
t.Error("output 0 not as expected, got", out0, "wanted", expected0)
}
if !bytes.Equal(out1, expected1) {
t.Error("output 1 not as expected, got", out1, "wanted", expected1)
}
if !bytes.Equal(out2, expected2) {
t.Error("output 2 not as expected, got", out2, "wanted", expected2)
}
unwrap0, _ := KeyUnwrap(block0, out0)
unwrap1, _ := KeyUnwrap(block1, out1)
unwrap2, _ := KeyUnwrap(block2, out2)
if !bytes.Equal(unwrap0, cek0) {
t.Error("key unwrap did not return original input, got", unwrap0, "wanted", cek0)
}
if !bytes.Equal(unwrap1, cek1) {
t.Error("key unwrap did not return original input, got", unwrap1, "wanted", cek1)
}
if !bytes.Equal(unwrap2, cek2) {
t.Error("key unwrap did not return original input, got", unwrap2, "wanted", cek2)
}
}
func TestAesKeyWrapInvalid(t *testing.T) {
kek, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0F")
// Invalid unwrap input (bit flipped)
input0, _ := hex.DecodeString("1EA68C1A8112B447AEF34BD8FB5A7B828D3E862371D2CFE5")
block, _ := aes.NewCipher(kek)
_, err := KeyUnwrap(block, input0)
if err == nil {
t.Error("key unwrap failed to detect invalid input")
}
// Invalid unwrap input (truncated)
input1, _ := hex.DecodeString("1EA68C1A8112B447AEF34BD8FB5A7B828D3E862371D2CF")
_, err = KeyUnwrap(block, input1)
if err == nil {
t.Error("key unwrap failed to detect truncated input")
}
// Invalid wrap input (not multiple of 8)
input2, _ := hex.DecodeString("0123456789ABCD")
_, err = KeyWrap(block, input2)
if err == nil {
t.Error("key wrap accepted invalid input")
}
}
func BenchmarkAesKeyWrap(b *testing.B) {
kek, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0F")
key, _ := hex.DecodeString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
block, _ := aes.NewCipher(kek)
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, err := KeyWrap(block, key); err != nil {
b.Fatal(err)
}
}
}
func BenchmarkAesKeyUnwrap(b *testing.B) {
kek, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0F")
input, _ := hex.DecodeString("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5")
block, _ := aes.NewCipher(kek)
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, err := KeyUnwrap(block, input); err != nil {
b.Fatal(err)
}
}
}
golang-github-go-jose-go-jose-4.0.5/crypter.go 0000664 0000000 0000000 00000041054 14761622347 0021210 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"crypto/ecdsa"
"crypto/rsa"
"errors"
"fmt"
"github.com/go-jose/go-jose/v4/json"
)
// Encrypter represents an encrypter which produces an encrypted JWE object.
type Encrypter interface {
Encrypt(plaintext []byte) (*JSONWebEncryption, error)
EncryptWithAuthData(plaintext []byte, aad []byte) (*JSONWebEncryption, error)
Options() EncrypterOptions
}
// A generic content cipher
type contentCipher interface {
keySize() int
encrypt(cek []byte, aad, plaintext []byte) (*aeadParts, error)
decrypt(cek []byte, aad []byte, parts *aeadParts) ([]byte, error)
}
// A key generator (for generating/getting a CEK)
type keyGenerator interface {
keySize() int
genKey() ([]byte, rawHeader, error)
}
// A generic key encrypter
type keyEncrypter interface {
encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) // Encrypt a key
}
// A generic key decrypter
type keyDecrypter interface {
decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) // Decrypt a key
}
// A generic encrypter based on the given key encrypter and content cipher.
type genericEncrypter struct {
contentAlg ContentEncryption
compressionAlg CompressionAlgorithm
cipher contentCipher
recipients []recipientKeyInfo
keyGenerator keyGenerator
extraHeaders map[HeaderKey]interface{}
}
type recipientKeyInfo struct {
keyID string
keyAlg KeyAlgorithm
keyEncrypter keyEncrypter
}
// EncrypterOptions represents options that can be set on new encrypters.
type EncrypterOptions struct {
Compression CompressionAlgorithm
// Optional map of name/value pairs to be inserted into the protected
// header of a JWS object. Some specifications which make use of
// JWS require additional values here.
//
// Values will be serialized by [json.Marshal] and must be valid inputs to
// that function.
//
// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
ExtraHeaders map[HeaderKey]interface{}
}
// WithHeader adds an arbitrary value to the ExtraHeaders map, initializing it
// if necessary, and returns the updated EncrypterOptions.
//
// The v parameter will be serialized by [json.Marshal] and must be a valid
// input to that function.
//
// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
func (eo *EncrypterOptions) WithHeader(k HeaderKey, v interface{}) *EncrypterOptions {
if eo.ExtraHeaders == nil {
eo.ExtraHeaders = map[HeaderKey]interface{}{}
}
eo.ExtraHeaders[k] = v
return eo
}
// WithContentType adds a content type ("cty") header and returns the updated
// EncrypterOptions.
func (eo *EncrypterOptions) WithContentType(contentType ContentType) *EncrypterOptions {
return eo.WithHeader(HeaderContentType, contentType)
}
// WithType adds a type ("typ") header and returns the updated EncrypterOptions.
func (eo *EncrypterOptions) WithType(typ ContentType) *EncrypterOptions {
return eo.WithHeader(HeaderType, typ)
}
// Recipient represents an algorithm/key to encrypt messages to.
//
// PBES2Count and PBES2Salt correspond with the "p2c" and "p2s" headers used
// on the password-based encryption algorithms PBES2-HS256+A128KW,
// PBES2-HS384+A192KW, and PBES2-HS512+A256KW. If they are not provided a safe
// default of 100000 will be used for the count and a 128-bit random salt will
// be generated.
type Recipient struct {
Algorithm KeyAlgorithm
// Key must have one of these types:
// - ed25519.PublicKey
// - *ecdsa.PublicKey
// - *rsa.PublicKey
// - *JSONWebKey
// - JSONWebKey
// - []byte (a symmetric key)
// - Any type that satisfies the OpaqueKeyEncrypter interface
//
// The type of Key must match the value of Algorithm.
Key interface{}
KeyID string
PBES2Count int
PBES2Salt []byte
}
// NewEncrypter creates an appropriate encrypter based on the key type
func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions) (Encrypter, error) {
encrypter := &genericEncrypter{
contentAlg: enc,
recipients: []recipientKeyInfo{},
cipher: getContentCipher(enc),
}
if opts != nil {
encrypter.compressionAlg = opts.Compression
encrypter.extraHeaders = opts.ExtraHeaders
}
if encrypter.cipher == nil {
return nil, ErrUnsupportedAlgorithm
}
var keyID string
var rawKey interface{}
switch encryptionKey := rcpt.Key.(type) {
case JSONWebKey:
keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key
case *JSONWebKey:
keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key
case OpaqueKeyEncrypter:
keyID, rawKey = encryptionKey.KeyID(), encryptionKey
default:
rawKey = encryptionKey
}
switch rcpt.Algorithm {
case DIRECT:
// Direct encryption mode must be treated differently
keyBytes, ok := rawKey.([]byte)
if !ok {
return nil, ErrUnsupportedKeyType
}
if encrypter.cipher.keySize() != len(keyBytes) {
return nil, ErrInvalidKeySize
}
encrypter.keyGenerator = staticKeyGenerator{
key: keyBytes,
}
recipientInfo, _ := newSymmetricRecipient(rcpt.Algorithm, keyBytes)
recipientInfo.keyID = keyID
if rcpt.KeyID != "" {
recipientInfo.keyID = rcpt.KeyID
}
encrypter.recipients = []recipientKeyInfo{recipientInfo}
return encrypter, nil
case ECDH_ES:
// ECDH-ES (w/o key wrapping) is similar to DIRECT mode
keyDSA, ok := rawKey.(*ecdsa.PublicKey)
if !ok {
return nil, ErrUnsupportedKeyType
}
encrypter.keyGenerator = ecKeyGenerator{
size: encrypter.cipher.keySize(),
algID: string(enc),
publicKey: keyDSA,
}
recipientInfo, _ := newECDHRecipient(rcpt.Algorithm, keyDSA)
recipientInfo.keyID = keyID
if rcpt.KeyID != "" {
recipientInfo.keyID = rcpt.KeyID
}
encrypter.recipients = []recipientKeyInfo{recipientInfo}
return encrypter, nil
default:
// Can just add a standard recipient
encrypter.keyGenerator = randomKeyGenerator{
size: encrypter.cipher.keySize(),
}
err := encrypter.addRecipient(rcpt)
return encrypter, err
}
}
// NewMultiEncrypter creates a multi-encrypter based on the given parameters
func NewMultiEncrypter(enc ContentEncryption, rcpts []Recipient, opts *EncrypterOptions) (Encrypter, error) {
cipher := getContentCipher(enc)
if cipher == nil {
return nil, ErrUnsupportedAlgorithm
}
if len(rcpts) == 0 {
return nil, fmt.Errorf("go-jose/go-jose: recipients is nil or empty")
}
encrypter := &genericEncrypter{
contentAlg: enc,
recipients: []recipientKeyInfo{},
cipher: cipher,
keyGenerator: randomKeyGenerator{
size: cipher.keySize(),
},
}
if opts != nil {
encrypter.compressionAlg = opts.Compression
encrypter.extraHeaders = opts.ExtraHeaders
}
for _, recipient := range rcpts {
err := encrypter.addRecipient(recipient)
if err != nil {
return nil, err
}
}
return encrypter, nil
}
func (ctx *genericEncrypter) addRecipient(recipient Recipient) (err error) {
var recipientInfo recipientKeyInfo
switch recipient.Algorithm {
case DIRECT, ECDH_ES:
return fmt.Errorf("go-jose/go-jose: key algorithm '%s' not supported in multi-recipient mode", recipient.Algorithm)
}
recipientInfo, err = makeJWERecipient(recipient.Algorithm, recipient.Key)
if recipient.KeyID != "" {
recipientInfo.keyID = recipient.KeyID
}
switch recipient.Algorithm {
case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
if sr, ok := recipientInfo.keyEncrypter.(*symmetricKeyCipher); ok {
sr.p2c = recipient.PBES2Count
sr.p2s = recipient.PBES2Salt
}
}
if err == nil {
ctx.recipients = append(ctx.recipients, recipientInfo)
}
return err
}
func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKeyInfo, error) {
switch encryptionKey := encryptionKey.(type) {
case *rsa.PublicKey:
return newRSARecipient(alg, encryptionKey)
case *ecdsa.PublicKey:
return newECDHRecipient(alg, encryptionKey)
case []byte:
return newSymmetricRecipient(alg, encryptionKey)
case string:
return newSymmetricRecipient(alg, []byte(encryptionKey))
case *JSONWebKey:
recipient, err := makeJWERecipient(alg, encryptionKey.Key)
recipient.keyID = encryptionKey.KeyID
return recipient, err
case OpaqueKeyEncrypter:
return newOpaqueKeyEncrypter(alg, encryptionKey)
}
return recipientKeyInfo{}, ErrUnsupportedKeyType
}
// newDecrypter creates an appropriate decrypter based on the key type
func newDecrypter(decryptionKey interface{}) (keyDecrypter, error) {
switch decryptionKey := decryptionKey.(type) {
case *rsa.PrivateKey:
return &rsaDecrypterSigner{
privateKey: decryptionKey,
}, nil
case *ecdsa.PrivateKey:
return &ecDecrypterSigner{
privateKey: decryptionKey,
}, nil
case []byte:
return &symmetricKeyCipher{
key: decryptionKey,
}, nil
case string:
return &symmetricKeyCipher{
key: []byte(decryptionKey),
}, nil
case JSONWebKey:
return newDecrypter(decryptionKey.Key)
case *JSONWebKey:
return newDecrypter(decryptionKey.Key)
case OpaqueKeyDecrypter:
return &opaqueKeyDecrypter{decrypter: decryptionKey}, nil
default:
return nil, ErrUnsupportedKeyType
}
}
// Implementation of encrypt method producing a JWE object.
func (ctx *genericEncrypter) Encrypt(plaintext []byte) (*JSONWebEncryption, error) {
return ctx.EncryptWithAuthData(plaintext, nil)
}
// Implementation of encrypt method producing a JWE object.
func (ctx *genericEncrypter) EncryptWithAuthData(plaintext, aad []byte) (*JSONWebEncryption, error) {
obj := &JSONWebEncryption{}
obj.aad = aad
obj.protected = &rawHeader{}
err := obj.protected.set(headerEncryption, ctx.contentAlg)
if err != nil {
return nil, err
}
obj.recipients = make([]recipientInfo, len(ctx.recipients))
if len(ctx.recipients) == 0 {
return nil, fmt.Errorf("go-jose/go-jose: no recipients to encrypt to")
}
cek, headers, err := ctx.keyGenerator.genKey()
if err != nil {
return nil, err
}
obj.protected.merge(&headers)
for i, info := range ctx.recipients {
recipient, err := info.keyEncrypter.encryptKey(cek, info.keyAlg)
if err != nil {
return nil, err
}
err = recipient.header.set(headerAlgorithm, info.keyAlg)
if err != nil {
return nil, err
}
if info.keyID != "" {
err = recipient.header.set(headerKeyID, info.keyID)
if err != nil {
return nil, err
}
}
obj.recipients[i] = recipient
}
if len(ctx.recipients) == 1 {
// Move per-recipient headers into main protected header if there's
// only a single recipient.
obj.protected.merge(obj.recipients[0].header)
obj.recipients[0].header = nil
}
if ctx.compressionAlg != NONE {
plaintext, err = compress(ctx.compressionAlg, plaintext)
if err != nil {
return nil, err
}
err = obj.protected.set(headerCompression, ctx.compressionAlg)
if err != nil {
return nil, err
}
}
for k, v := range ctx.extraHeaders {
b, err := json.Marshal(v)
if err != nil {
return nil, err
}
(*obj.protected)[k] = makeRawMessage(b)
}
authData := obj.computeAuthData()
parts, err := ctx.cipher.encrypt(cek, authData, plaintext)
if err != nil {
return nil, err
}
obj.iv = parts.iv
obj.ciphertext = parts.ciphertext
obj.tag = parts.tag
return obj, nil
}
func (ctx *genericEncrypter) Options() EncrypterOptions {
return EncrypterOptions{
Compression: ctx.compressionAlg,
ExtraHeaders: ctx.extraHeaders,
}
}
// Decrypt and validate the object and return the plaintext. This
// function does not support multi-recipient. If you desire multi-recipient
// decryption use DecryptMulti instead.
//
// The decryptionKey argument must contain a private or symmetric key
// and must have one of these types:
// - *ecdsa.PrivateKey
// - *rsa.PrivateKey
// - *JSONWebKey
// - JSONWebKey
// - *JSONWebKeySet
// - JSONWebKeySet
// - []byte (a symmetric key)
// - string (a symmetric key)
// - Any type that satisfies the OpaqueKeyDecrypter interface.
//
// Note that ed25519 is only available for signatures, not encryption, so is
// not an option here.
//
// Automatically decompresses plaintext, but returns an error if the decompressed
// data would be >250kB or >10x the size of the compressed data, whichever is larger.
func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) {
headers := obj.mergedHeaders(nil)
if len(obj.recipients) > 1 {
return nil, errors.New("go-jose/go-jose: too many recipients in payload; expecting only one")
}
critical, err := headers.getCritical()
if err != nil {
return nil, fmt.Errorf("go-jose/go-jose: invalid crit header")
}
if len(critical) > 0 {
return nil, fmt.Errorf("go-jose/go-jose: unsupported crit header")
}
key, err := tryJWKS(decryptionKey, obj.Header)
if err != nil {
return nil, err
}
decrypter, err := newDecrypter(key)
if err != nil {
return nil, err
}
cipher := getContentCipher(headers.getEncryption())
if cipher == nil {
return nil, fmt.Errorf("go-jose/go-jose: unsupported enc value '%s'", string(headers.getEncryption()))
}
generator := randomKeyGenerator{
size: cipher.keySize(),
}
parts := &aeadParts{
iv: obj.iv,
ciphertext: obj.ciphertext,
tag: obj.tag,
}
authData := obj.computeAuthData()
var plaintext []byte
recipient := obj.recipients[0]
recipientHeaders := obj.mergedHeaders(&recipient)
cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator)
if err == nil {
// Found a valid CEK -- let's try to decrypt.
plaintext, err = cipher.decrypt(cek, authData, parts)
}
if plaintext == nil {
return nil, ErrCryptoFailure
}
// The "zip" header parameter may only be present in the protected header.
if comp := obj.protected.getCompression(); comp != "" {
plaintext, err = decompress(comp, plaintext)
if err != nil {
return nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err)
}
}
return plaintext, nil
}
// DecryptMulti decrypts and validates the object and returns the plaintexts,
// with support for multiple recipients. It returns the index of the recipient
// for which the decryption was successful, the merged headers for that recipient,
// and the plaintext.
//
// The decryptionKey argument must have one of the types allowed for the
// decryptionKey argument of Decrypt().
//
// Automatically decompresses plaintext, but returns an error if the decompressed
// data would be >250kB or >3x the size of the compressed data, whichever is larger.
func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) {
globalHeaders := obj.mergedHeaders(nil)
critical, err := globalHeaders.getCritical()
if err != nil {
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: invalid crit header")
}
if len(critical) > 0 {
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: unsupported crit header")
}
key, err := tryJWKS(decryptionKey, obj.Header)
if err != nil {
return -1, Header{}, nil, err
}
decrypter, err := newDecrypter(key)
if err != nil {
return -1, Header{}, nil, err
}
encryption := globalHeaders.getEncryption()
cipher := getContentCipher(encryption)
if cipher == nil {
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: unsupported enc value '%s'", string(encryption))
}
generator := randomKeyGenerator{
size: cipher.keySize(),
}
parts := &aeadParts{
iv: obj.iv,
ciphertext: obj.ciphertext,
tag: obj.tag,
}
authData := obj.computeAuthData()
index := -1
var plaintext []byte
var headers rawHeader
for i, recipient := range obj.recipients {
recipientHeaders := obj.mergedHeaders(&recipient)
cek, err := decrypter.decryptKey(recipientHeaders, &recipient, generator)
if err == nil {
// Found a valid CEK -- let's try to decrypt.
plaintext, err = cipher.decrypt(cek, authData, parts)
if err == nil {
index = i
headers = recipientHeaders
break
}
}
}
if plaintext == nil {
return -1, Header{}, nil, ErrCryptoFailure
}
// The "zip" header parameter may only be present in the protected header.
if comp := obj.protected.getCompression(); comp != "" {
plaintext, err = decompress(comp, plaintext)
if err != nil {
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err)
}
}
sanitized, err := headers.sanitized()
if err != nil {
return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: failed to sanitize header: %v", err)
}
return index, sanitized, plaintext, err
}
golang-github-go-jose-go-jose-4.0.5/crypter_test.go 0000664 0000000 0000000 00000126124 14761622347 0022251 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"bytes"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"encoding/hex"
"fmt"
"io"
"math/big"
"reflect"
"regexp"
"strings"
"testing"
)
// We generate only a single RSA and EC key for testing, speeds up tests.
var rsaTestKey, _ = rsa.GenerateKey(rand.Reader, 2048)
var ecTestKey256, _ = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
var ecTestKey384, _ = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
var ecTestKey521, _ = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
var ed25519PublicKey, ed25519PrivateKey, _ = ed25519.GenerateKey(rand.Reader)
func TestCompressionError(t *testing.T) {
testKey, _ := hex.DecodeString("0c5433f844097b2e910db9b3638ff824")
// // This code was used to provide the test vector `jweBytes` below,
// // by running with a local modification of crypter.go to set "zip" to "DEF"
// // in the protected header, but not actually compress the payload.
// keyAlg := A128KW
// encAlg := A128GCM
// rcpt := Recipient{Algorithm: keyAlg, Key: testKey}
// enc, err := NewEncrypter(encAlg, rcpt, &EncrypterOptions{Compression: DEFLATE})
// if err != nil {
// t.Fatal(err)
// }
// jwe, err := enc.Encrypt([]byte("Lorem ipsum dolor sit amet"))
// if err != nil {
// t.Fatal(err)
// }
// t.Logf(jwe.FullSerialize())
jweBytes := `{
"protected":"eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0",
"encrypted_key":"0ZJRSSVctBhIpWvT9Ke2Z-KvSby-kjmi",
"iv":"4nuUbYF0BR0KPz1v",
"ciphertext":"luLq8QTsJEXbZdRvEzIiHWEitTZTORZqXIk",
"tag":"S1j6wvSGtTUCXhED91lUGQ"
}`
jwe, err := ParseEncrypted(jweBytes, []KeyAlgorithm{A128KW}, []ContentEncryption{A128GCM})
if err != nil {
t.Fatal(err)
}
_, err = jwe.Decrypt(testKey)
if err == nil {
t.Fatal("expected error on decompression failure")
}
if !strings.Contains(err.Error(), "failed to decompress plaintext: flate: corrupt input") {
t.Errorf("expected flate error, got %s", err)
}
_, _, _, err = jwe.DecryptMulti(testKey)
if err == nil {
t.Fatal("expected error on decompression failure")
}
if !strings.Contains(err.Error(), "failed to decompress plaintext: flate: corrupt input") {
t.Errorf("expected flate error, got %s", err)
}
}
func RoundtripJWE(keyAlg KeyAlgorithm, encAlg ContentEncryption, compressionAlg CompressionAlgorithm, serializer func(*JSONWebEncryption) (string, error), corrupter func(*JSONWebEncryption) bool, aad []byte, encryptionKey interface{}, decryptionKey interface{}) error {
var rcpt Recipient
switch keyAlg {
case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
// use 1k iterations instead of 100k to reduce computational cost
rcpt = Recipient{Algorithm: keyAlg, Key: encryptionKey, PBES2Count: 1000}
default:
rcpt = Recipient{Algorithm: keyAlg, Key: encryptionKey}
}
enc, err := NewEncrypter(encAlg, rcpt, &EncrypterOptions{Compression: compressionAlg})
if err != nil {
return fmt.Errorf("error on new encrypter: %s", err)
}
input := []byte("Lorem ipsum dolor sit amet")
obj, err := enc.EncryptWithAuthData(input, aad)
if err != nil {
return fmt.Errorf("error in encrypt: %s", err)
}
msg, err := serializer(obj)
if err != nil {
return fmt.Errorf("error in serializer: %s", err)
}
parsed, err := ParseEncrypted(msg, []KeyAlgorithm{keyAlg}, []ContentEncryption{encAlg})
if err != nil {
return fmt.Errorf("error in parse: %s, on msg '%s'", err, msg)
}
// (Maybe) mangle object
skip := corrupter(parsed)
if skip {
return fmt.Errorf("corrupter indicated message should be skipped")
}
if !bytes.Equal(parsed.GetAuthData(), aad) {
return fmt.Errorf("auth data in parsed object does not match")
}
output, err := parsed.Decrypt(decryptionKey)
if err != nil {
return fmt.Errorf("error on decrypt: %s", err)
}
if !bytes.Equal(input, output) {
return fmt.Errorf("Decrypted output does not match input, got '%s' but wanted '%s'", output, input)
}
return nil
}
func TestRoundtripsJWE(t *testing.T) {
// Test matrix
keyAlgs := []KeyAlgorithm{
DIRECT, ECDH_ES, ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW, A128KW, A192KW, A256KW,
RSA1_5, RSA_OAEP, RSA_OAEP_256, A128GCMKW, A192GCMKW, A256GCMKW,
PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW,
}
encAlgs := []ContentEncryption{A128GCM, A192GCM, A256GCM, A128CBC_HS256, A192CBC_HS384, A256CBC_HS512}
zipAlgs := []CompressionAlgorithm{NONE, DEFLATE}
serializers := []func(*JSONWebEncryption) (string, error){
func(obj *JSONWebEncryption) (string, error) { return obj.CompactSerialize() },
func(obj *JSONWebEncryption) (string, error) { return obj.FullSerialize(), nil },
}
corrupter := func(obj *JSONWebEncryption) bool { return false }
// Note: can't use AAD with compact serialization
aads := [][]byte{
nil,
[]byte("Ut enim ad minim veniam"),
}
// Test all different configurations
for _, alg := range keyAlgs {
for _, enc := range encAlgs {
for _, key := range generateTestKeys(alg, enc) {
for _, zip := range zipAlgs {
for i, serializer := range serializers {
err := RoundtripJWE(alg, enc, zip, serializer, corrupter, aads[i], key.enc, key.dec)
if err != nil {
t.Error(err, alg, enc, zip, i)
}
}
}
}
}
}
}
func TestRoundtripsJWECorrupted(t *testing.T) {
// Test matrix
keyAlgs := []KeyAlgorithm{DIRECT, ECDH_ES, ECDH_ES_A128KW, A128KW, RSA1_5, RSA_OAEP, RSA_OAEP_256, A128GCMKW, PBES2_HS256_A128KW}
encAlgs := []ContentEncryption{A128GCM, A192GCM, A256GCM, A128CBC_HS256, A192CBC_HS384, A256CBC_HS512}
zipAlgs := []CompressionAlgorithm{NONE, DEFLATE}
serializers := []func(*JSONWebEncryption) (string, error){
func(obj *JSONWebEncryption) (string, error) { return obj.CompactSerialize() },
func(obj *JSONWebEncryption) (string, error) { return obj.FullSerialize(), nil },
}
bitflip := func(slice []byte) bool {
if len(slice) > 0 {
slice[0] ^= 0xFF
return false
}
return true
}
corrupters := []func(*JSONWebEncryption) bool{
func(obj *JSONWebEncryption) bool {
// Set invalid ciphertext
return bitflip(obj.ciphertext)
},
func(obj *JSONWebEncryption) bool {
// Set invalid auth tag
return bitflip(obj.tag)
},
func(obj *JSONWebEncryption) bool {
// Set invalid AAD
return bitflip(obj.aad)
},
func(obj *JSONWebEncryption) bool {
// Mess with encrypted key
return bitflip(obj.recipients[0].encryptedKey)
},
func(obj *JSONWebEncryption) bool {
// Mess with GCM-KW auth tag
tag, _ := obj.protected.getTag()
skip := bitflip(tag.bytes())
if skip {
return true
}
if err := obj.protected.set(headerTag, tag); err != nil {
t.Fatal(err)
}
return false
},
}
// Note: can't use AAD with compact serialization
aads := [][]byte{
nil,
[]byte("Ut enim ad minim veniam"),
}
// Test all different configurations
for _, alg := range keyAlgs {
for _, enc := range encAlgs {
for _, key := range generateTestKeys(alg, enc) {
for _, zip := range zipAlgs {
for i, serializer := range serializers {
for j, corrupter := range corrupters {
err := RoundtripJWE(alg, enc, zip, serializer, corrupter, aads[i], key.enc, key.dec)
if err == nil {
t.Error("failed to detect corrupt data", err, alg, enc, zip, i, j)
}
}
}
}
}
}
}
}
func TestEncrypterWithJWKAndKeyID(t *testing.T) {
enc, err := NewEncrypter(A128GCM, Recipient{Algorithm: A128KW, Key: &JSONWebKey{
KeyID: "test-id",
Key: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
}}, nil)
if err != nil {
t.Error(err)
}
ciphertext, _ := enc.Encrypt([]byte("Lorem ipsum dolor sit amet"))
serialized1, _ := ciphertext.CompactSerialize()
serialized2 := ciphertext.FullSerialize()
parsed1, _ := ParseEncrypted(serialized1, []KeyAlgorithm{A128KW}, []ContentEncryption{A128GCM})
parsed2, _ := ParseEncrypted(serialized2, []KeyAlgorithm{A128KW}, []ContentEncryption{A128GCM})
if parsed1.Header.KeyID != "test-id" {
t.Errorf("expected message to have key id from JWK, but found '%s' instead", parsed1.Header.KeyID)
}
if parsed2.Header.KeyID != "test-id" {
t.Errorf("expected message to have key id from JWK, but found '%s' instead", parsed2.Header.KeyID)
}
}
func TestEncrypterWithBrokenRand(t *testing.T) {
keyAlgs := []KeyAlgorithm{ECDH_ES_A128KW, A128KW, RSA1_5, RSA_OAEP, RSA_OAEP_256, A128GCMKW, PBES2_HS256_A128KW}
encAlgs := []ContentEncryption{A128GCM, A192GCM, A256GCM, A128CBC_HS256, A192CBC_HS384, A256CBC_HS512}
serializer := func(obj *JSONWebEncryption) (string, error) { return obj.CompactSerialize() }
corrupter := func(obj *JSONWebEncryption) bool { return false }
// Break rand reader
readers := []func() io.Reader{
// Totally broken
func() io.Reader { return bytes.NewReader([]byte{}) },
// Not enough bytes
func() io.Reader { return io.LimitReader(rand.Reader, 20) },
}
defer resetRandReader()
for _, alg := range keyAlgs {
for _, enc := range encAlgs {
for _, key := range generateTestKeys(alg, enc) {
for i, getReader := range readers {
RandReader = getReader()
err := RoundtripJWE(alg, enc, NONE, serializer, corrupter, nil, key.enc, key.dec)
if err == nil {
t.Error("encrypter should fail if rand is broken", i)
}
}
}
}
}
}
func TestNewEncrypterErrors(t *testing.T) {
_, err := NewEncrypter("XYZ", Recipient{}, nil)
if err == nil {
t.Error("was able to instantiate encrypter with invalid cipher")
}
_, err = NewMultiEncrypter("XYZ", []Recipient{}, nil)
if err == nil {
t.Error("was able to instantiate multi-encrypter with invalid cipher")
}
_, err = NewEncrypter(A128GCM, Recipient{Algorithm: DIRECT, Key: nil}, nil)
if err == nil {
t.Error("was able to instantiate encrypter with invalid direct key")
}
_, err = NewEncrypter(A128GCM, Recipient{Algorithm: ECDH_ES, Key: nil}, nil)
if err == nil {
t.Error("was able to instantiate encrypter with invalid EC key")
}
}
func TestMultiRecipientJWE(t *testing.T) {
sharedKey := []byte{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
}
enc, err := NewMultiEncrypter(A128GCM, []Recipient{
{Algorithm: RSA_OAEP, Key: &rsaTestKey.PublicKey},
{Algorithm: A256GCMKW, Key: sharedKey},
}, nil)
if err != nil {
panic(err)
}
input := []byte("Lorem ipsum dolor sit amet")
obj, err := enc.Encrypt(input)
if err != nil {
t.Fatal("error in encrypt: ", err)
}
msg := obj.FullSerialize()
parsed, err := ParseEncrypted(msg, []KeyAlgorithm{RSA_OAEP, A256GCMKW}, []ContentEncryption{A128GCM})
if err != nil {
t.Fatal("error in parse: ", err)
}
i, _, output, err := parsed.DecryptMulti(rsaTestKey)
if err != nil {
t.Fatal("error on decrypt with RSA: ", err)
}
if i != 0 {
t.Fatal("recipient index should be 0 for RSA key")
}
if !bytes.Equal(input, output) {
t.Fatal("Decrypted output does not match input: ", output, input)
}
i, _, output, err = parsed.DecryptMulti(sharedKey)
if err != nil {
t.Fatal("error on decrypt with AES: ", err)
}
if i != 1 {
t.Fatal("recipient index should be 1 for shared key")
}
if !bytes.Equal(input, output) {
t.Fatal("Decrypted output does not match input", output, input)
}
}
func TestMultiRecipientErrors(t *testing.T) {
_, err := NewMultiEncrypter(A128GCM, []Recipient{}, nil)
if err == nil {
t.Error("should fail to instantiate with zero recipients")
}
}
func TestEncrypterOptions(t *testing.T) {
sharedKey := []byte{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
}
opts := &EncrypterOptions{
Compression: DEFLATE,
}
opts.WithType("JWT")
opts.WithContentType("JWT")
enc, err := NewEncrypter(A256GCM, Recipient{Algorithm: A256GCMKW, Key: sharedKey}, opts)
if err != nil {
fmt.Println(err)
t.Error("Failed to create encrypter")
}
if !reflect.DeepEqual(*opts, enc.Options()) {
t.Error("Encrypter options do not match")
}
}
// Test that extra headers are generated and parsed in a round trip.
func TestEncrypterExtraHeaderInclusion(t *testing.T) {
sharedKey := []byte{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
}
opts := &EncrypterOptions{
Compression: DEFLATE,
}
opts.WithType("JWT")
opts.WithContentType("JWT")
opts.WithHeader(HeaderKey("myCustomHeader"), "xyz")
enc, err := NewEncrypter(A256GCM, Recipient{Algorithm: A256GCMKW, Key: sharedKey}, opts)
if err != nil {
fmt.Println(err)
t.Error("Failed to create encrypter")
}
if !reflect.DeepEqual(*opts, enc.Options()) {
t.Error("Encrypter options do not match")
}
input := []byte("Lorem ipsum dolor sit amet")
obj, err := enc.Encrypt(input)
if err != nil {
t.Fatal("error in encrypt: ", err)
}
parsed, err := ParseEncrypted(obj.FullSerialize(), []KeyAlgorithm{A256GCMKW}, []ContentEncryption{A256GCM})
if err != nil {
t.Fatal("error in parse: ", err)
}
output, err := parsed.Decrypt(sharedKey)
if err != nil {
t.Fatal("error on decrypt: ", err)
}
if !bytes.Equal(input, output) {
t.Fatal("Decrypted output does not match input: ", output, input)
}
if parsed.Header.ExtraHeaders[HeaderType] != "JWT" ||
parsed.Header.ExtraHeaders[HeaderContentType] != "JWT" ||
parsed.Header.ExtraHeaders[HeaderKey("myCustomHeader")] != "xyz" {
t.Fatalf("Mismatch in extra headers: %#v", parsed.Header.ExtraHeaders)
}
}
// TestPBES2JWKEncryption uses the plaintext and serialization reference of
// JWK RFC https://tools.ietf.org/html/rfc7517#appendix-C.4
func TestPBES2JWKEncryption(t *testing.T) {
passphrase := []byte("Thus from my lips, by yours, my sin is purged.")
plaintext := []byte(`{
"kty":"RSA",
"kid":"juliet@capulet.lit",
"use":"enc",
"n":"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRy
O125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP
8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0
Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0X
OC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1
_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q",
"e":"AQAB",
"d":"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfS
NkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9U
vqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnu
ToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsu
rY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2a
hecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ",
"p":"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHf
QP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8
UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws",
"q":"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6I
edis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYK
rYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s",
"dp":"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3
tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1w
Y52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c",
"dq":"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9
GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBy
mXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots",
"qi":"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqq
abu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0o
Yu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8"
}`)
serializationReference := `
eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJwMnMiOiIyV0NUY0paMVJ2ZF9DSn
VKcmlwUTF3IiwicDJjIjo0MDk2LCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiY3R5Ijoi
andrK2pzb24ifQ.
TrqXOwuNUfDV9VPTNbyGvEJ9JMjefAVn-TR1uIxR9p6hsRQh9Tk7BA.
Ye9j1qs22DmRSAddIh-VnA.
AwhB8lxrlKjFn02LGWEqg27H4Tg9fyZAbFv3p5ZicHpj64QyHC44qqlZ3JEmnZTgQo
wIqZJ13jbyHB8LgePiqUJ1hf6M2HPLgzw8L-mEeQ0jvDUTrE07NtOerBk8bwBQyZ6g
0kQ3DEOIglfYxV8-FJvNBYwbqN1Bck6d_i7OtjSHV-8DIrp-3JcRIe05YKy3Oi34Z_
GOiAc1EK21B11c_AE11PII_wvvtRiUiG8YofQXakWd1_O98Kap-UgmyWPfreUJ3lJP
nbD4Ve95owEfMGLOPflo2MnjaTDCwQokoJ_xplQ2vNPz8iguLcHBoKllyQFJL2mOWB
wqhBo9Oj-O800as5mmLsvQMTflIrIEbbTMzHMBZ8EFW9fWwwFu0DWQJGkMNhmBZQ-3
lvqTc-M6-gWA6D8PDhONfP2Oib2HGizwG1iEaX8GRyUpfLuljCLIe1DkGOewhKuKkZ
h04DKNM5Nbugf2atmU9OP0Ldx5peCUtRG1gMVl7Qup5ZXHTjgPDr5b2N731UooCGAU
qHdgGhg0JVJ_ObCTdjsH4CF1SJsdUhrXvYx3HJh2Xd7CwJRzU_3Y1GxYU6-s3GFPbi
rfqqEipJDBTHpcoCmyrwYjYHFgnlqBZRotRrS95g8F95bRXqsaDY7UgQGwBQBwy665
d0zpvTasvfXf_c0MWAl-neFaKOW_Px6g4EUDjG1GWSXV9cLStLw_0ovdApDIFLHYHe
PyagyHjouQUuGiq7BsYwYrwaF06tgB8hV8omLNfMEmDPJaZUzMuHw6tBDwGkzD-tS_
ub9hxrpJ4UsOWnt5rGUyoN2N_c1-TQlXxm5oto14MxnoAyBQBpwIEgSH3Y4ZhwKBhH
PjSo0cdwuNdYbGPpb-YUvF-2NZzODiQ1OvWQBRHSbPWYz_xbGkgD504LRtqRwCO7CC
_CyyURi1sEssPVsMJRX_U4LFEOc82TiDdqjKOjRUfKK5rqLi8nBE9soQ0DSaOoFQZi
GrBrqxDsNYiAYAmxxkos-i3nX4qtByVx85sCE5U_0MqG7COxZWMOPEFrDaepUV-cOy
rvoUIng8i8ljKBKxETY2BgPegKBYCxsAUcAkKamSCC9AiBxA0UOHyhTqtlvMksO7AE
hNC2-YzPyx1FkhMoS4LLe6E_pFsMlmjA6P1NSge9C5G5tETYXGAn6b1xZbHtmwrPSc
ro9LWhVmAaA7_bxYObnFUxgWtK4vzzQBjZJ36UTk4OTB-JvKWgfVWCFsaw5WCHj6Oo
4jpO7d2yN7WMfAj2hTEabz9wumQ0TMhBduZ-QON3pYObSy7TSC1vVme0NJrwF_cJRe
hKTFmdlXGVldPxZCplr7ZQqRQhF8JP-l4mEQVnCaWGn9ONHlemczGOS-A-wwtnmwjI
B1V_vgJRf4FdpV-4hUk4-QLpu3-1lWFxrtZKcggq3tWTduRo5_QebQbUUT_VSCgsFc
OmyWKoj56lbxthN19hq1XGWbLGfrrR6MWh23vk01zn8FVwi7uFwEnRYSafsnWLa1Z5
TpBj9GvAdl2H9NHwzpB5NqHpZNkQ3NMDj13Fn8fzO0JB83Etbm_tnFQfcb13X3bJ15
Cz-Ww1MGhvIpGGnMBT_ADp9xSIyAM9dQ1yeVXk-AIgWBUlN5uyWSGyCxp0cJwx7HxM
38z0UIeBu-MytL-eqndM7LxytsVzCbjOTSVRmhYEMIzUAnS1gs7uMQAGRdgRIElTJE
SGMjb_4bZq9s6Ve1LKkSi0_QDsrABaLe55UY0zF4ZSfOV5PMyPtocwV_dcNPlxLgNA
D1BFX_Z9kAdMZQW6fAmsfFle0zAoMe4l9pMESH0JB4sJGdCKtQXj1cXNydDYozF7l8
H00BV_Er7zd6VtIw0MxwkFCTatsv_R-GsBCH218RgVPsfYhwVuT8R4HarpzsDBufC4
r8_c8fc9Z278sQ081jFjOja6L2x0N_ImzFNXU6xwO-Ska-QeuvYZ3X_L31ZOX4Llp-
7QSfgDoHnOxFv1Xws-D5mDHD3zxOup2b2TppdKTZb9eW2vxUVviM8OI9atBfPKMGAO
v9omA-6vv5IxUH0-lWMiHLQ_g8vnswp-Jav0c4t6URVUzujNOoNd_CBGGVnHiJTCHl
88LQxsqLHHIu4Fz-U2SGnlxGTj0-ihit2ELGRv4vO8E1BosTmf0cx3qgG0Pq0eOLBD
IHsrdZ_CCAiTc0HVkMbyq1M6qEhM-q5P6y1QCIrwg.
0HFmhOzsQ98nNWJjIHkR7A`
// remove white spaces and line breaks
r := regexp.MustCompile(`\s`)
plaintext = r.ReplaceAll(plaintext, []byte(""))
serializationReference = r.ReplaceAllString(serializationReference, "")
rcpt := Recipient{
Algorithm: PBES2_HS256_A128KW,
Key: passphrase,
PBES2Count: 4096,
PBES2Salt: []byte{
217, 96, 147, 112, 150, 117, 70,
247, 127, 8, 155, 137, 174, 42, 80, 215,
},
}
enc, err := NewEncrypter(A128CBC_HS256, rcpt, nil)
if err != nil {
t.Fatal("error on NewEncrypter:", err)
}
obj, err := enc.Encrypt(plaintext)
if err != nil {
t.Fatal("error on new Encrypt:", err)
}
serialized, err := obj.CompactSerialize()
if err != nil {
t.Fatal("error on CompactSerialize")
}
jwe1, err := ParseEncrypted(serialized, []KeyAlgorithm{PBES2_HS256_A128KW}, []ContentEncryption{A128CBC_HS256})
if err != nil {
t.Fatal("error in ParseEncrypted")
}
jwe2, err := ParseEncrypted(serializationReference, []KeyAlgorithm{PBES2_HS256_A128KW}, []ContentEncryption{A128CBC_HS256})
if err != nil {
t.Fatal("error in ParseEncrypted")
}
original1, err := jwe1.Decrypt(passphrase)
if err != nil {
t.Fatal("error in Decrypt:", err)
}
original2, err := jwe2.Decrypt(passphrase)
if err != nil {
t.Fatal("error in Decrypt reference:", err)
}
if !bytes.Equal(original1, original2) {
t.Error("decryption does not match reference decryption")
}
if !bytes.Equal(plaintext, original1) {
t.Error("decryption does not match plaintext")
}
if !bytes.Equal(plaintext, original2) {
t.Error("reference decryption does not match plaintext")
}
}
func TestEncrypterWithPBES2(t *testing.T) {
expected := []byte("Lorem ipsum dolor sit amet")
algs := []KeyAlgorithm{
PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW,
}
// Check with both strings and []byte
recipientKeys := []interface{}{"password", []byte("password")}
for _, key := range recipientKeys {
for _, alg := range algs {
enc, err := NewEncrypter(A128GCM, Recipient{Algorithm: alg, Key: &JSONWebKey{
KeyID: "test-id",
Key: key,
}}, nil)
if err != nil {
t.Error(err)
}
ciphertext, _ := enc.Encrypt(expected)
serialized1, _ := ciphertext.CompactSerialize()
serialized2 := ciphertext.FullSerialize()
parsed1, _ := ParseEncrypted(serialized1, []KeyAlgorithm{alg}, []ContentEncryption{A128GCM})
parsed2, _ := ParseEncrypted(serialized2, []KeyAlgorithm{alg}, []ContentEncryption{A128GCM})
actual1, err := parsed1.Decrypt("password")
if err != nil {
t.Fatal("error on Decrypt:", err)
}
actual2, err := parsed2.Decrypt([]byte("password"))
if err != nil {
t.Fatal("error on Decrypt:", err)
}
if !bytes.Equal(actual1, expected) {
t.Errorf("error comparing decrypted message (%s) and expected (%s)", actual1, expected)
}
if !bytes.Equal(actual2, expected) {
t.Errorf("error comparing decrypted message (%s) and expected (%s)", actual2, expected)
}
}
}
}
func TestRejectTooHighP2C(t *testing.T) {
expected := []byte("Lorem ipsum dolor sit amet")
algs := []KeyAlgorithm{
PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW,
}
// Check with both strings and []byte
recipientKeys := []interface{}{"password", []byte("password")}
for _, key := range recipientKeys {
for _, alg := range algs {
enc, err := NewEncrypter(A128GCM, Recipient{Algorithm: alg, PBES2Count: 1000001, Key: &JSONWebKey{
KeyID: "test-id",
Key: key,
}}, nil)
if err != nil {
t.Error(err)
}
ciphertext, _ := enc.Encrypt(expected)
serialized1, _ := ciphertext.CompactSerialize()
serialized2 := ciphertext.FullSerialize()
parsed1, _ := ParseEncrypted(serialized1, []KeyAlgorithm{alg}, []ContentEncryption{A128GCM})
parsed2, _ := ParseEncrypted(serialized2, []KeyAlgorithm{alg}, []ContentEncryption{A128GCM})
_, err = parsed1.Decrypt("password")
if err == nil {
t.Fatal("expected error decrypting expensive PBES2 key, got none")
}
_, err = parsed2.Decrypt([]byte("password"))
if err == nil {
t.Fatal("expected error decrypting expensive PBES2 key, got none")
}
}
}
}
type testKey struct {
enc, dec interface{}
}
func symmetricTestKey(size int) []testKey {
key, _, _ := randomKeyGenerator{size: size}.genKey()
return []testKey{
{
enc: key,
dec: key,
},
{
enc: &JSONWebKey{KeyID: "test", Key: key},
dec: &JSONWebKey{KeyID: "test", Key: key},
},
}
}
func TestDirectEncryptionKeySizeCheck(t *testing.T) {
// 16-byte key
key16 := []byte("0123456789ABCDEF")
// 32-byte key
key32 := []byte("0123456789ABCDEF0123456789ABCDEF")
// AES-128 with 32-byte key should reject
_, err := NewEncrypter(A128GCM, Recipient{Algorithm: DIRECT, Key: key32}, nil)
if err != ErrInvalidKeySize {
t.Error("Should reject AES-128 with 32-byte key")
}
// AES-256 with 16-byte key should reject
_, err = NewEncrypter(A256GCM, Recipient{Algorithm: DIRECT, Key: key16}, nil)
if err != ErrInvalidKeySize {
t.Error("Should reject AES-256 with 16-byte key")
}
}
func generateTestKeys(keyAlg KeyAlgorithm, encAlg ContentEncryption) []testKey {
switch keyAlg {
case DIRECT:
return symmetricTestKey(getContentCipher(encAlg).keySize())
case ECDH_ES, ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW:
return []testKey{
{
dec: ecTestKey256,
enc: &ecTestKey256.PublicKey,
},
{
dec: ecTestKey384,
enc: &ecTestKey384.PublicKey,
},
{
dec: ecTestKey521,
enc: &ecTestKey521.PublicKey,
},
{
dec: &JSONWebKey{KeyID: "test", Key: ecTestKey256},
enc: &JSONWebKey{KeyID: "test", Key: &ecTestKey256.PublicKey},
},
}
case A128GCMKW, A128KW:
return symmetricTestKey(16)
case A192GCMKW, A192KW:
return symmetricTestKey(24)
case A256GCMKW, A256KW:
return symmetricTestKey(32)
case RSA1_5, RSA_OAEP, RSA_OAEP_256:
return []testKey{{
dec: rsaTestKey,
enc: &rsaTestKey.PublicKey,
}}
case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
// size does not matter, use random integer
i, err := rand.Int(rand.Reader, big.NewInt(64))
if err != nil {
panic(err)
}
return symmetricTestKey(int(i.Int64()))
}
panic("Must update test case")
}
var (
chunks = map[string][]byte{
"1B": make([]byte, 1),
"64B": make([]byte, 64),
"1KB": make([]byte, 1024),
"64KB": make([]byte, 65536),
"1MB": make([]byte, 1048576),
"64MB": make([]byte, 67108864),
}
symKey16, _, _ = randomKeyGenerator{size: 16}.genKey()
symKey32, _, _ = randomKeyGenerator{size: 32}.genKey()
symKey64, _, _ = randomKeyGenerator{size: 64}.genKey()
encrypters = map[string]Encrypter{
"OAEPAndGCM": mustEncrypter(RSA_OAEP, A128GCM, &rsaTestKey.PublicKey),
"PKCSAndGCM": mustEncrypter(RSA1_5, A128GCM, &rsaTestKey.PublicKey),
"OAEPAndCBC": mustEncrypter(RSA_OAEP, A128CBC_HS256, &rsaTestKey.PublicKey),
"PKCSAndCBC": mustEncrypter(RSA1_5, A128CBC_HS256, &rsaTestKey.PublicKey),
"DirectGCM128": mustEncrypter(DIRECT, A128GCM, symKey16),
"DirectCBC128": mustEncrypter(DIRECT, A128CBC_HS256, symKey32),
"DirectGCM256": mustEncrypter(DIRECT, A256GCM, symKey32),
"DirectCBC256": mustEncrypter(DIRECT, A256CBC_HS512, symKey64),
"AESKWAndGCM128": mustEncrypter(A128KW, A128GCM, symKey16),
"AESKWAndCBC256": mustEncrypter(A256KW, A256GCM, symKey32),
"ECDHOnP256AndGCM128": mustEncrypter(ECDH_ES, A128GCM, &ecTestKey256.PublicKey),
"ECDHOnP384AndGCM128": mustEncrypter(ECDH_ES, A128GCM, &ecTestKey384.PublicKey),
"ECDHOnP521AndGCM128": mustEncrypter(ECDH_ES, A128GCM, &ecTestKey521.PublicKey),
}
)
func BenchmarkEncrypt1BWithOAEPAndGCM(b *testing.B) { benchEncrypt("1B", "OAEPAndGCM", b) }
func BenchmarkEncrypt64BWithOAEPAndGCM(b *testing.B) { benchEncrypt("64B", "OAEPAndGCM", b) }
func BenchmarkEncrypt1KBWithOAEPAndGCM(b *testing.B) { benchEncrypt("1KB", "OAEPAndGCM", b) }
func BenchmarkEncrypt64KBWithOAEPAndGCM(b *testing.B) { benchEncrypt("64KB", "OAEPAndGCM", b) }
func BenchmarkEncrypt1MBWithOAEPAndGCM(b *testing.B) { benchEncrypt("1MB", "OAEPAndGCM", b) }
func BenchmarkEncrypt64MBWithOAEPAndGCM(b *testing.B) { benchEncrypt("64MB", "OAEPAndGCM", b) }
func BenchmarkEncrypt1BWithPKCSAndGCM(b *testing.B) { benchEncrypt("1B", "PKCSAndGCM", b) }
func BenchmarkEncrypt64BWithPKCSAndGCM(b *testing.B) { benchEncrypt("64B", "PKCSAndGCM", b) }
func BenchmarkEncrypt1KBWithPKCSAndGCM(b *testing.B) { benchEncrypt("1KB", "PKCSAndGCM", b) }
func BenchmarkEncrypt64KBWithPKCSAndGCM(b *testing.B) { benchEncrypt("64KB", "PKCSAndGCM", b) }
func BenchmarkEncrypt1MBWithPKCSAndGCM(b *testing.B) { benchEncrypt("1MB", "PKCSAndGCM", b) }
func BenchmarkEncrypt64MBWithPKCSAndGCM(b *testing.B) { benchEncrypt("64MB", "PKCSAndGCM", b) }
func BenchmarkEncrypt1BWithOAEPAndCBC(b *testing.B) { benchEncrypt("1B", "OAEPAndCBC", b) }
func BenchmarkEncrypt64BWithOAEPAndCBC(b *testing.B) { benchEncrypt("64B", "OAEPAndCBC", b) }
func BenchmarkEncrypt1KBWithOAEPAndCBC(b *testing.B) { benchEncrypt("1KB", "OAEPAndCBC", b) }
func BenchmarkEncrypt64KBWithOAEPAndCBC(b *testing.B) { benchEncrypt("64KB", "OAEPAndCBC", b) }
func BenchmarkEncrypt1MBWithOAEPAndCBC(b *testing.B) { benchEncrypt("1MB", "OAEPAndCBC", b) }
func BenchmarkEncrypt64MBWithOAEPAndCBC(b *testing.B) { benchEncrypt("64MB", "OAEPAndCBC", b) }
func BenchmarkEncrypt1BWithPKCSAndCBC(b *testing.B) { benchEncrypt("1B", "PKCSAndCBC", b) }
func BenchmarkEncrypt64BWithPKCSAndCBC(b *testing.B) { benchEncrypt("64B", "PKCSAndCBC", b) }
func BenchmarkEncrypt1KBWithPKCSAndCBC(b *testing.B) { benchEncrypt("1KB", "PKCSAndCBC", b) }
func BenchmarkEncrypt64KBWithPKCSAndCBC(b *testing.B) { benchEncrypt("64KB", "PKCSAndCBC", b) }
func BenchmarkEncrypt1MBWithPKCSAndCBC(b *testing.B) { benchEncrypt("1MB", "PKCSAndCBC", b) }
func BenchmarkEncrypt64MBWithPKCSAndCBC(b *testing.B) { benchEncrypt("64MB", "PKCSAndCBC", b) }
func BenchmarkEncrypt1BWithDirectGCM128(b *testing.B) { benchEncrypt("1B", "DirectGCM128", b) }
func BenchmarkEncrypt64BWithDirectGCM128(b *testing.B) { benchEncrypt("64B", "DirectGCM128", b) }
func BenchmarkEncrypt1KBWithDirectGCM128(b *testing.B) { benchEncrypt("1KB", "DirectGCM128", b) }
func BenchmarkEncrypt64KBWithDirectGCM128(b *testing.B) { benchEncrypt("64KB", "DirectGCM128", b) }
func BenchmarkEncrypt1MBWithDirectGCM128(b *testing.B) { benchEncrypt("1MB", "DirectGCM128", b) }
func BenchmarkEncrypt64MBWithDirectGCM128(b *testing.B) { benchEncrypt("64MB", "DirectGCM128", b) }
func BenchmarkEncrypt1BWithDirectCBC128(b *testing.B) { benchEncrypt("1B", "DirectCBC128", b) }
func BenchmarkEncrypt64BWithDirectCBC128(b *testing.B) { benchEncrypt("64B", "DirectCBC128", b) }
func BenchmarkEncrypt1KBWithDirectCBC128(b *testing.B) { benchEncrypt("1KB", "DirectCBC128", b) }
func BenchmarkEncrypt64KBWithDirectCBC128(b *testing.B) { benchEncrypt("64KB", "DirectCBC128", b) }
func BenchmarkEncrypt1MBWithDirectCBC128(b *testing.B) { benchEncrypt("1MB", "DirectCBC128", b) }
func BenchmarkEncrypt64MBWithDirectCBC128(b *testing.B) { benchEncrypt("64MB", "DirectCBC128", b) }
func BenchmarkEncrypt1BWithDirectGCM256(b *testing.B) { benchEncrypt("1B", "DirectGCM256", b) }
func BenchmarkEncrypt64BWithDirectGCM256(b *testing.B) { benchEncrypt("64B", "DirectGCM256", b) }
func BenchmarkEncrypt1KBWithDirectGCM256(b *testing.B) { benchEncrypt("1KB", "DirectGCM256", b) }
func BenchmarkEncrypt64KBWithDirectGCM256(b *testing.B) { benchEncrypt("64KB", "DirectGCM256", b) }
func BenchmarkEncrypt1MBWithDirectGCM256(b *testing.B) { benchEncrypt("1MB", "DirectGCM256", b) }
func BenchmarkEncrypt64MBWithDirectGCM256(b *testing.B) { benchEncrypt("64MB", "DirectGCM256", b) }
func BenchmarkEncrypt1BWithDirectCBC256(b *testing.B) { benchEncrypt("1B", "DirectCBC256", b) }
func BenchmarkEncrypt64BWithDirectCBC256(b *testing.B) { benchEncrypt("64B", "DirectCBC256", b) }
func BenchmarkEncrypt1KBWithDirectCBC256(b *testing.B) { benchEncrypt("1KB", "DirectCBC256", b) }
func BenchmarkEncrypt64KBWithDirectCBC256(b *testing.B) { benchEncrypt("64KB", "DirectCBC256", b) }
func BenchmarkEncrypt1MBWithDirectCBC256(b *testing.B) { benchEncrypt("1MB", "DirectCBC256", b) }
func BenchmarkEncrypt64MBWithDirectCBC256(b *testing.B) { benchEncrypt("64MB", "DirectCBC256", b) }
func BenchmarkEncrypt1BWithAESKWAndGCM128(b *testing.B) { benchEncrypt("1B", "AESKWAndGCM128", b) }
func BenchmarkEncrypt64BWithAESKWAndGCM128(b *testing.B) { benchEncrypt("64B", "AESKWAndGCM128", b) }
func BenchmarkEncrypt1KBWithAESKWAndGCM128(b *testing.B) { benchEncrypt("1KB", "AESKWAndGCM128", b) }
func BenchmarkEncrypt64KBWithAESKWAndGCM128(b *testing.B) { benchEncrypt("64KB", "AESKWAndGCM128", b) }
func BenchmarkEncrypt1MBWithAESKWAndGCM128(b *testing.B) { benchEncrypt("1MB", "AESKWAndGCM128", b) }
func BenchmarkEncrypt64MBWithAESKWAndGCM128(b *testing.B) { benchEncrypt("64MB", "AESKWAndGCM128", b) }
func BenchmarkEncrypt1BWithAESKWAndCBC256(b *testing.B) { benchEncrypt("1B", "AESKWAndCBC256", b) }
func BenchmarkEncrypt64BWithAESKWAndCBC256(b *testing.B) { benchEncrypt("64B", "AESKWAndCBC256", b) }
func BenchmarkEncrypt1KBWithAESKWAndCBC256(b *testing.B) { benchEncrypt("1KB", "AESKWAndCBC256", b) }
func BenchmarkEncrypt64KBWithAESKWAndCBC256(b *testing.B) { benchEncrypt("64KB", "AESKWAndCBC256", b) }
func BenchmarkEncrypt1MBWithAESKWAndCBC256(b *testing.B) { benchEncrypt("1MB", "AESKWAndCBC256", b) }
func BenchmarkEncrypt64MBWithAESKWAndCBC256(b *testing.B) { benchEncrypt("64MB", "AESKWAndCBC256", b) }
func BenchmarkEncrypt1BWithECDHOnP256AndGCM128(b *testing.B) {
benchEncrypt("1B", "ECDHOnP256AndGCM128", b)
}
func BenchmarkEncrypt64BWithECDHOnP256AndGCM128(b *testing.B) {
benchEncrypt("64B", "ECDHOnP256AndGCM128", b)
}
func BenchmarkEncrypt1KBWithECDHOnP256AndGCM128(b *testing.B) {
benchEncrypt("1KB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkEncrypt64KBWithECDHOnP256AndGCM128(b *testing.B) {
benchEncrypt("64KB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkEncrypt1MBWithECDHOnP256AndGCM128(b *testing.B) {
benchEncrypt("1MB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkEncrypt64MBWithECDHOnP256AndGCM128(b *testing.B) {
benchEncrypt("64MB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkEncrypt1BWithECDHOnP384AndGCM128(b *testing.B) {
benchEncrypt("1B", "ECDHOnP384AndGCM128", b)
}
func BenchmarkEncrypt64BWithECDHOnP384AndGCM128(b *testing.B) {
benchEncrypt("64B", "ECDHOnP384AndGCM128", b)
}
func BenchmarkEncrypt1KBWithECDHOnP384AndGCM128(b *testing.B) {
benchEncrypt("1KB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkEncrypt64KBWithECDHOnP384AndGCM128(b *testing.B) {
benchEncrypt("64KB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkEncrypt1MBWithECDHOnP384AndGCM128(b *testing.B) {
benchEncrypt("1MB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkEncrypt64MBWithECDHOnP384AndGCM128(b *testing.B) {
benchEncrypt("64MB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkEncrypt1BWithECDHOnP521AndGCM128(b *testing.B) {
benchEncrypt("1B", "ECDHOnP521AndGCM128", b)
}
func BenchmarkEncrypt64BWithECDHOnP521AndGCM128(b *testing.B) {
benchEncrypt("64B", "ECDHOnP521AndGCM128", b)
}
func BenchmarkEncrypt1KBWithECDHOnP521AndGCM128(b *testing.B) {
benchEncrypt("1KB", "ECDHOnP521AndGCM128", b)
}
func BenchmarkEncrypt64KBWithECDHOnP521AndGCM128(b *testing.B) {
benchEncrypt("64KB", "ECDHOnP521AndGCM128", b)
}
func BenchmarkEncrypt1MBWithECDHOnP521AndGCM128(b *testing.B) {
benchEncrypt("1MB", "ECDHOnP521AndGCM128", b)
}
func BenchmarkEncrypt64MBWithECDHOnP521AndGCM128(b *testing.B) {
benchEncrypt("64MB", "ECDHOnP521AndGCM128", b)
}
func benchEncrypt(chunkKey, primKey string, b *testing.B) {
data, ok := chunks[chunkKey]
if !ok {
b.Fatalf("unknown chunk size %s", chunkKey)
}
enc, ok := encrypters[primKey]
if !ok {
b.Fatalf("unknown encrypter %s", primKey)
}
b.SetBytes(int64(len(data)))
for i := 0; i < b.N; i++ {
if _, err := enc.Encrypt(data); err != nil {
b.Fatal(err)
}
}
}
var (
decryptionKeys = map[string]interface{}{
"OAEPAndGCM": rsaTestKey,
"PKCSAndGCM": rsaTestKey,
"OAEPAndCBC": rsaTestKey,
"PKCSAndCBC": rsaTestKey,
"DirectGCM128": symKey16,
"DirectCBC128": symKey32,
"DirectGCM256": symKey32,
"DirectCBC256": symKey64,
"AESKWAndGCM128": symKey16,
"AESKWAndCBC256": symKey32,
"ECDHOnP256AndGCM128": ecTestKey256,
"ECDHOnP384AndGCM128": ecTestKey384,
"ECDHOnP521AndGCM128": ecTestKey521,
}
)
func BenchmarkDecrypt1BWithOAEPAndGCM(b *testing.B) { benchDecrypt("1B", "OAEPAndGCM", b) }
func BenchmarkDecrypt64BWithOAEPAndGCM(b *testing.B) { benchDecrypt("64B", "OAEPAndGCM", b) }
func BenchmarkDecrypt1KBWithOAEPAndGCM(b *testing.B) { benchDecrypt("1KB", "OAEPAndGCM", b) }
func BenchmarkDecrypt64KBWithOAEPAndGCM(b *testing.B) { benchDecrypt("64KB", "OAEPAndGCM", b) }
func BenchmarkDecrypt1MBWithOAEPAndGCM(b *testing.B) { benchDecrypt("1MB", "OAEPAndGCM", b) }
func BenchmarkDecrypt64MBWithOAEPAndGCM(b *testing.B) { benchDecrypt("64MB", "OAEPAndGCM", b) }
func BenchmarkDecrypt1BWithPKCSAndGCM(b *testing.B) { benchDecrypt("1B", "PKCSAndGCM", b) }
func BenchmarkDecrypt64BWithPKCSAndGCM(b *testing.B) { benchDecrypt("64B", "PKCSAndGCM", b) }
func BenchmarkDecrypt1KBWithPKCSAndGCM(b *testing.B) { benchDecrypt("1KB", "PKCSAndGCM", b) }
func BenchmarkDecrypt64KBWithPKCSAndGCM(b *testing.B) { benchDecrypt("64KB", "PKCSAndGCM", b) }
func BenchmarkDecrypt1MBWithPKCSAndGCM(b *testing.B) { benchDecrypt("1MB", "PKCSAndGCM", b) }
func BenchmarkDecrypt64MBWithPKCSAndGCM(b *testing.B) { benchDecrypt("64MB", "PKCSAndGCM", b) }
func BenchmarkDecrypt1BWithOAEPAndCBC(b *testing.B) { benchDecrypt("1B", "OAEPAndCBC", b) }
func BenchmarkDecrypt64BWithOAEPAndCBC(b *testing.B) { benchDecrypt("64B", "OAEPAndCBC", b) }
func BenchmarkDecrypt1KBWithOAEPAndCBC(b *testing.B) { benchDecrypt("1KB", "OAEPAndCBC", b) }
func BenchmarkDecrypt64KBWithOAEPAndCBC(b *testing.B) { benchDecrypt("64KB", "OAEPAndCBC", b) }
func BenchmarkDecrypt1MBWithOAEPAndCBC(b *testing.B) { benchDecrypt("1MB", "OAEPAndCBC", b) }
func BenchmarkDecrypt64MBWithOAEPAndCBC(b *testing.B) { benchDecrypt("64MB", "OAEPAndCBC", b) }
func BenchmarkDecrypt1BWithPKCSAndCBC(b *testing.B) { benchDecrypt("1B", "PKCSAndCBC", b) }
func BenchmarkDecrypt64BWithPKCSAndCBC(b *testing.B) { benchDecrypt("64B", "PKCSAndCBC", b) }
func BenchmarkDecrypt1KBWithPKCSAndCBC(b *testing.B) { benchDecrypt("1KB", "PKCSAndCBC", b) }
func BenchmarkDecrypt64KBWithPKCSAndCBC(b *testing.B) { benchDecrypt("64KB", "PKCSAndCBC", b) }
func BenchmarkDecrypt1MBWithPKCSAndCBC(b *testing.B) { benchDecrypt("1MB", "PKCSAndCBC", b) }
func BenchmarkDecrypt64MBWithPKCSAndCBC(b *testing.B) { benchDecrypt("64MB", "PKCSAndCBC", b) }
func BenchmarkDecrypt1BWithDirectGCM128(b *testing.B) { benchDecrypt("1B", "DirectGCM128", b) }
func BenchmarkDecrypt64BWithDirectGCM128(b *testing.B) { benchDecrypt("64B", "DirectGCM128", b) }
func BenchmarkDecrypt1KBWithDirectGCM128(b *testing.B) { benchDecrypt("1KB", "DirectGCM128", b) }
func BenchmarkDecrypt64KBWithDirectGCM128(b *testing.B) { benchDecrypt("64KB", "DirectGCM128", b) }
func BenchmarkDecrypt1MBWithDirectGCM128(b *testing.B) { benchDecrypt("1MB", "DirectGCM128", b) }
func BenchmarkDecrypt64MBWithDirectGCM128(b *testing.B) { benchDecrypt("64MB", "DirectGCM128", b) }
func BenchmarkDecrypt1BWithDirectCBC128(b *testing.B) { benchDecrypt("1B", "DirectCBC128", b) }
func BenchmarkDecrypt64BWithDirectCBC128(b *testing.B) { benchDecrypt("64B", "DirectCBC128", b) }
func BenchmarkDecrypt1KBWithDirectCBC128(b *testing.B) { benchDecrypt("1KB", "DirectCBC128", b) }
func BenchmarkDecrypt64KBWithDirectCBC128(b *testing.B) { benchDecrypt("64KB", "DirectCBC128", b) }
func BenchmarkDecrypt1MBWithDirectCBC128(b *testing.B) { benchDecrypt("1MB", "DirectCBC128", b) }
func BenchmarkDecrypt64MBWithDirectCBC128(b *testing.B) { benchDecrypt("64MB", "DirectCBC128", b) }
func BenchmarkDecrypt1BWithDirectGCM256(b *testing.B) { benchDecrypt("1B", "DirectGCM256", b) }
func BenchmarkDecrypt64BWithDirectGCM256(b *testing.B) { benchDecrypt("64B", "DirectGCM256", b) }
func BenchmarkDecrypt1KBWithDirectGCM256(b *testing.B) { benchDecrypt("1KB", "DirectGCM256", b) }
func BenchmarkDecrypt64KBWithDirectGCM256(b *testing.B) { benchDecrypt("64KB", "DirectGCM256", b) }
func BenchmarkDecrypt1MBWithDirectGCM256(b *testing.B) { benchDecrypt("1MB", "DirectGCM256", b) }
func BenchmarkDecrypt64MBWithDirectGCM256(b *testing.B) { benchDecrypt("64MB", "DirectGCM256", b) }
func BenchmarkDecrypt1BWithDirectCBC256(b *testing.B) { benchDecrypt("1B", "DirectCBC256", b) }
func BenchmarkDecrypt64BWithDirectCBC256(b *testing.B) { benchDecrypt("64B", "DirectCBC256", b) }
func BenchmarkDecrypt1KBWithDirectCBC256(b *testing.B) { benchDecrypt("1KB", "DirectCBC256", b) }
func BenchmarkDecrypt64KBWithDirectCBC256(b *testing.B) { benchDecrypt("64KB", "DirectCBC256", b) }
func BenchmarkDecrypt1MBWithDirectCBC256(b *testing.B) { benchDecrypt("1MB", "DirectCBC256", b) }
func BenchmarkDecrypt64MBWithDirectCBC256(b *testing.B) { benchDecrypt("64MB", "DirectCBC256", b) }
func BenchmarkDecrypt1BWithAESKWAndGCM128(b *testing.B) { benchDecrypt("1B", "AESKWAndGCM128", b) }
func BenchmarkDecrypt64BWithAESKWAndGCM128(b *testing.B) { benchDecrypt("64B", "AESKWAndGCM128", b) }
func BenchmarkDecrypt1KBWithAESKWAndGCM128(b *testing.B) { benchDecrypt("1KB", "AESKWAndGCM128", b) }
func BenchmarkDecrypt64KBWithAESKWAndGCM128(b *testing.B) { benchDecrypt("64KB", "AESKWAndGCM128", b) }
func BenchmarkDecrypt1MBWithAESKWAndGCM128(b *testing.B) { benchDecrypt("1MB", "AESKWAndGCM128", b) }
func BenchmarkDecrypt64MBWithAESKWAndGCM128(b *testing.B) { benchDecrypt("64MB", "AESKWAndGCM128", b) }
func BenchmarkDecrypt1BWithAESKWAndCBC256(b *testing.B) { benchDecrypt("1B", "AESKWAndCBC256", b) }
func BenchmarkDecrypt64BWithAESKWAndCBC256(b *testing.B) { benchDecrypt("64B", "AESKWAndCBC256", b) }
func BenchmarkDecrypt1KBWithAESKWAndCBC256(b *testing.B) { benchDecrypt("1KB", "AESKWAndCBC256", b) }
func BenchmarkDecrypt64KBWithAESKWAndCBC256(b *testing.B) { benchDecrypt("64KB", "AESKWAndCBC256", b) }
func BenchmarkDecrypt1MBWithAESKWAndCBC256(b *testing.B) { benchDecrypt("1MB", "AESKWAndCBC256", b) }
func BenchmarkDecrypt64MBWithAESKWAndCBC256(b *testing.B) { benchDecrypt("64MB", "AESKWAndCBC256", b) }
func BenchmarkDecrypt1BWithECDHOnP256AndGCM128(b *testing.B) {
benchDecrypt("1B", "ECDHOnP256AndGCM128", b)
}
func BenchmarkDecrypt64BWithECDHOnP256AndGCM128(b *testing.B) {
benchDecrypt("64B", "ECDHOnP256AndGCM128", b)
}
func BenchmarkDecrypt1KBWithECDHOnP256AndGCM128(b *testing.B) {
benchDecrypt("1KB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkDecrypt64KBWithECDHOnP256AndGCM128(b *testing.B) {
benchDecrypt("64KB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkDecrypt1MBWithECDHOnP256AndGCM128(b *testing.B) {
benchDecrypt("1MB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkDecrypt64MBWithECDHOnP256AndGCM128(b *testing.B) {
benchDecrypt("64MB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkDecrypt1BWithECDHOnP384AndGCM128(b *testing.B) {
benchDecrypt("1B", "ECDHOnP384AndGCM128", b)
}
func BenchmarkDecrypt64BWithECDHOnP384AndGCM128(b *testing.B) {
benchDecrypt("64B", "ECDHOnP384AndGCM128", b)
}
func BenchmarkDecrypt1KBWithECDHOnP384AndGCM128(b *testing.B) {
benchDecrypt("1KB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkDecrypt64KBWithECDHOnP384AndGCM128(b *testing.B) {
benchDecrypt("64KB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkDecrypt1MBWithECDHOnP384AndGCM128(b *testing.B) {
benchDecrypt("1MB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkDecrypt64MBWithECDHOnP384AndGCM128(b *testing.B) {
benchDecrypt("64MB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkDecrypt1BWithECDHOnP521AndGCM128(b *testing.B) {
benchDecrypt("1B", "ECDHOnP521AndGCM128", b)
}
func BenchmarkDecrypt64BWithECDHOnP521AndGCM128(b *testing.B) {
benchDecrypt("64B", "ECDHOnP521AndGCM128", b)
}
func BenchmarkDecrypt1KBWithECDHOnP521AndGCM128(b *testing.B) {
benchDecrypt("1KB", "ECDHOnP521AndGCM128", b)
}
func BenchmarkDecrypt64KBWithECDHOnP521AndGCM128(b *testing.B) {
benchDecrypt("64KB", "ECDHOnP521AndGCM128", b)
}
func BenchmarkDecrypt1MBWithECDHOnP521AndGCM128(b *testing.B) {
benchDecrypt("1MB", "ECDHOnP521AndGCM128", b)
}
func BenchmarkDecrypt64MBWithECDHOnP521AndGCM128(b *testing.B) {
benchDecrypt("64MB", "ECDHOnP521AndGCM128", b)
}
func benchDecrypt(chunkKey, primKey string, b *testing.B) {
chunk, ok := chunks[chunkKey]
if !ok {
b.Fatalf("unknown chunk size %s", chunkKey)
}
enc, ok := encrypters[primKey]
if !ok {
b.Fatalf("unknown encrypter %s", primKey)
}
dec, ok := decryptionKeys[primKey]
if !ok {
b.Fatalf("unknown decryption key %s", primKey)
}
data, err := enc.Encrypt(chunk)
if err != nil {
b.Fatal(err)
}
b.SetBytes(int64(len(chunk)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, err := data.Decrypt(dec); err != nil {
b.Fatal(err)
}
}
}
func mustEncrypter(keyAlg KeyAlgorithm, encAlg ContentEncryption, encryptionKey interface{}) Encrypter {
enc, err := NewEncrypter(encAlg, Recipient{Algorithm: keyAlg, Key: encryptionKey}, nil)
if err != nil {
panic(err)
}
return enc
}
golang-github-go-jose-go-jose-4.0.5/cryptosigner/ 0000775 0000000 0000000 00000000000 14761622347 0021715 5 ustar 00root root 0000000 0000000 golang-github-go-jose-go-jose-4.0.5/cryptosigner/cryptosigner.go 0000664 0000000 0000000 00000006741 14761622347 0025004 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2018 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Package cryptosigner implements an OpaqueSigner that wraps a "crypto".Signer
//
// https://godoc.org/crypto#Signer
package cryptosigner
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"encoding/asn1"
"io"
"math/big"
"github.com/go-jose/go-jose/v4"
)
// Opaque creates an OpaqueSigner from a "crypto".Signer
func Opaque(s crypto.Signer) jose.OpaqueSigner {
pk := &jose.JSONWebKey{
Key: s.Public(),
}
return &cryptoSigner{signer: s, rand: rand.Reader, pk: pk}
}
type cryptoSigner struct {
pk *jose.JSONWebKey
signer crypto.Signer
rand io.Reader
}
func (s *cryptoSigner) Public() *jose.JSONWebKey {
return s.pk
}
func (s *cryptoSigner) Algs() []jose.SignatureAlgorithm {
switch key := s.signer.Public().(type) {
case ed25519.PublicKey:
return []jose.SignatureAlgorithm{jose.EdDSA}
case *ecdsa.PublicKey:
switch key.Curve {
case elliptic.P256():
return []jose.SignatureAlgorithm{jose.ES256}
case elliptic.P384():
return []jose.SignatureAlgorithm{jose.ES384}
case elliptic.P521():
return []jose.SignatureAlgorithm{jose.ES512}
default:
return nil
}
case *rsa.PublicKey:
return []jose.SignatureAlgorithm{jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512}
default:
return nil
}
}
func (s *cryptoSigner) SignPayload(payload []byte, alg jose.SignatureAlgorithm) ([]byte, error) {
var hash crypto.Hash
switch alg {
case jose.EdDSA:
case jose.RS256, jose.PS256, jose.ES256:
hash = crypto.SHA256
case jose.RS384, jose.PS384, jose.ES384:
hash = crypto.SHA384
case jose.RS512, jose.PS512, jose.ES512:
hash = crypto.SHA512
default:
return nil, jose.ErrUnsupportedAlgorithm
}
var hashed []byte
if hash != crypto.Hash(0) {
hasher := hash.New()
if _, err := hasher.Write(payload); err != nil {
return nil, err
}
hashed = hasher.Sum(nil)
}
var (
out []byte
err error
)
switch alg {
case jose.EdDSA:
out, err = s.signer.Sign(s.rand, payload, crypto.Hash(0))
case jose.ES256, jose.ES384, jose.ES512:
var byteLen int
switch alg {
case jose.ES256:
byteLen = 32
case jose.ES384:
byteLen = 48
case jose.ES512:
byteLen = 66
}
var b []byte
b, err = s.signer.Sign(s.rand, hashed, hash)
if err != nil {
return nil, err
}
sig := struct {
R, S *big.Int
}{}
if _, err = asn1.Unmarshal(b, &sig); err != nil {
return nil, err
}
rBytes := sig.R.Bytes()
out = make([]byte, byteLen)
copy(out[byteLen-len(rBytes):], rBytes)
sBytes := sig.S.Bytes()
sBytesPadded := make([]byte, byteLen)
copy(sBytesPadded[byteLen-len(sBytes):], sBytes)
out = append(out, sBytesPadded...)
case jose.RS256, jose.RS384, jose.RS512:
out, err = s.signer.Sign(s.rand, hashed, hash)
case jose.PS256, jose.PS384, jose.PS512:
out, err = s.signer.Sign(s.rand, hashed, &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthAuto,
Hash: hash,
})
}
return out, err
}
golang-github-go-jose-go-jose-4.0.5/cryptosigner/cryptosigner_test.go 0000664 0000000 0000000 00000013053 14761622347 0026035 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2018 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cryptosigner
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"errors"
"fmt"
"io"
"reflect"
"testing"
"github.com/go-jose/go-jose/v4"
)
func TestRoundtripsJWSCryptoSigner(t *testing.T) {
sigAlgs := []jose.SignatureAlgorithm{jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512, jose.ES256, jose.ES384, jose.ES512, jose.EdDSA}
serializers := []func(*jose.JSONWebSignature) (string, error){
func(obj *jose.JSONWebSignature) (string, error) { return obj.CompactSerialize() },
func(obj *jose.JSONWebSignature) (string, error) { return obj.FullSerialize(), nil },
}
for _, alg := range sigAlgs {
signingKey, verificationKey := generateSigningTestKey(alg)
for i, serializer := range serializers {
err := roundtripJWS(alg, serializer, Opaque(signingKey.(crypto.Signer)), verificationKey)
if err != nil {
t.Error(err, alg, i)
}
}
}
}
type staticNonceSource string
func (sns staticNonceSource) Nonce() (string, error) {
return string(sns), nil
}
func roundtripJWS(sigAlg jose.SignatureAlgorithm, serializer func(*jose.JSONWebSignature) (string, error), signingKey interface{}, verificationKey interface{}) error {
nonce := "test_nonce"
opts := &jose.SignerOptions{
NonceSource: staticNonceSource(nonce),
}
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: sigAlg, Key: signingKey}, opts)
if err != nil {
return fmt.Errorf("error on new signer: %s", err)
}
input := []byte("Lorem ipsum dolor sit amet")
obj, err := signer.Sign(input)
if err != nil {
return fmt.Errorf("error on sign: %s", err)
}
msg, err := serializer(obj)
if err != nil {
return fmt.Errorf("error on serialize: %s", err)
}
obj, err = jose.ParseSigned(msg, []jose.SignatureAlgorithm{sigAlg})
if err != nil {
return fmt.Errorf("error on parse: %s", err)
}
output, err := obj.Verify(verificationKey)
if err != nil {
return fmt.Errorf("error on verify: %s", err)
}
// Check that verify works with embedded keys (if present)
for i, sig := range obj.Signatures {
if sig.Header.JSONWebKey != nil {
_, err = obj.Verify(sig.Header.JSONWebKey)
if err != nil {
return fmt.Errorf("error on verify with embedded key %d: %s", i, err)
}
}
// Check that the nonce correctly round-tripped (if present)
if sig.Header.Nonce != nonce {
return fmt.Errorf("Incorrect nonce returned: [%s]", sig.Header.Nonce)
}
}
if !bytes.Equal(output, input) {
return fmt.Errorf("input/output do not match, got '%s', expected '%s'", output, input)
}
return nil
}
func generateSigningTestKey(sigAlg jose.SignatureAlgorithm) (sig, ver interface{}) {
switch sigAlg {
case jose.EdDSA:
ver, sig, _ = ed25519.GenerateKey(rand.Reader)
case jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512:
rsaTestKey, _ := rsa.GenerateKey(rand.Reader, 2048)
sig = rsaTestKey
ver = &rsaTestKey.PublicKey
case jose.ES256:
key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
sig = key
ver = &key.PublicKey
case jose.ES384:
key, _ := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
sig = key
ver = &key.PublicKey
case jose.ES512:
key, _ := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
sig = key
ver = &key.PublicKey
default:
panic("Must update test case")
}
return
}
type fakeSigner struct{}
func (fakeSigner) Public() crypto.PublicKey {
return []byte("fake-key")
}
func (fakeSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
return nil, errors.New("not a signer")
}
func Test_cryptoSigner_Algs(t *testing.T) {
_, edKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatal(err)
}
p224, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
if err != nil {
t.Fatal(err)
}
p256, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
p384, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
t.Fatal(err)
}
p521, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
t.Fatal(err)
}
type fields struct {
signer crypto.Signer
}
tests := []struct {
name string
fields fields
want []jose.SignatureAlgorithm
}{
{"EdDSA", fields{edKey}, []jose.SignatureAlgorithm{jose.EdDSA}},
{"ES256", fields{p256}, []jose.SignatureAlgorithm{jose.ES256}},
{"ES384", fields{p384}, []jose.SignatureAlgorithm{jose.ES384}},
{"ES512", fields{p521}, []jose.SignatureAlgorithm{jose.ES512}},
{"RSA", fields{rsaKey}, []jose.SignatureAlgorithm{jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512}},
{"fail P-224", fields{p224}, nil},
{"fail other", fields{fakeSigner{}}, nil},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cs := &cryptoSigner{
signer: tt.fields.signer,
}
if got := cs.Algs(); !reflect.DeepEqual(tt.want, got) {
t.Errorf("cryptoSigner.Algs() got = %v, want %v", got, tt.want)
}
})
}
}
golang-github-go-jose-go-jose-4.0.5/doc.go 0000664 0000000 0000000 00000002002 14761622347 0020253 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
Package jose aims to provide an implementation of the Javascript Object Signing
and Encryption set of standards. It implements encryption and signing based on
the JSON Web Encryption and JSON Web Signature standards, with optional JSON Web
Token support available in a sub-package. The library supports both the compact
and JWS/JWE JSON Serialization formats, and has optional support for multiple
recipients.
*/
package jose
golang-github-go-jose-go-jose-4.0.5/doc_test.go 0000664 0000000 0000000 00000014450 14761622347 0021324 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"fmt"
)
// Dummy encrypter for use in examples
var encrypter Encrypter
func Example_jWE() {
// Generate a public/private key pair to use for this example.
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
// Instantiate an encrypter using RSA-OAEP with AES128-GCM. An error would
// indicate that the selected algorithm(s) are not currently supported.
publicKey := &privateKey.PublicKey
encrypter, err := NewEncrypter(A128GCM, Recipient{Algorithm: RSA_OAEP, Key: publicKey}, nil)
if err != nil {
panic(err)
}
// Encrypt a sample plaintext. Calling the encrypter returns an encrypted
// JWE object, which can then be serialized for output afterwards. An error
// would indicate a problem in an underlying cryptographic primitive.
var plaintext = []byte("Lorem ipsum dolor sit amet")
object, err := encrypter.Encrypt(plaintext)
if err != nil {
panic(err)
}
// Serialize the encrypted object using the JWE JSON Serialization format.
// Alternatively you can also use the compact format here by calling
// object.CompactSerialize() instead.
serialized := object.FullSerialize()
// Parse the serialized, encrypted JWE object. An error would indicate that
// the given input did not represent a valid message.
object, err = ParseEncrypted(serialized, []KeyAlgorithm{RSA_OAEP}, []ContentEncryption{A128GCM})
if err != nil {
panic(err)
}
// Now we can decrypt and get back our original plaintext. An error here
// would indicate that the message failed to decrypt, e.g. because the auth
// tag was broken or the message was tampered with.
decrypted, err := object.Decrypt(privateKey)
if err != nil {
panic(err)
}
fmt.Print(string(decrypted))
// output: Lorem ipsum dolor sit amet
}
func Example_jWS() {
// Generate a public/private key pair to use for this example.
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
// Instantiate a signer using RSASSA-PSS (SHA512) with the given private key.
signer, err := NewSigner(SigningKey{Algorithm: PS512, Key: privateKey}, nil)
if err != nil {
panic(err)
}
// Sign a sample payload. Calling the signer returns a protected JWS object,
// which can then be serialized for output afterwards. An error would
// indicate a problem in an underlying cryptographic primitive.
var payload = []byte("Lorem ipsum dolor sit amet")
object, err := signer.Sign(payload)
if err != nil {
panic(err)
}
// Serialize the signed object using the JWS JSON Serialization format.
// Alternatively you can also use the compact format here by calling
// object.CompactSerialize() instead.
serialized := object.FullSerialize()
// Parse the serialized, protected JWS object. An error would indicate that
// the given input did not represent a valid message.
object, err = ParseSigned(serialized, []SignatureAlgorithm{PS512})
if err != nil {
panic(err)
}
// Now we can verify the signature on the payload. An error here would
// indicate that the message failed to verify, e.g. because the signature was
// broken or the message was tampered with.
output, err := object.Verify(&privateKey.PublicKey)
if err != nil {
panic(err)
}
fmt.Print(string(output))
// output: Lorem ipsum dolor sit amet
}
func ExampleNewEncrypter_publicKey() {
var publicKey *rsa.PublicKey
// Instantiate an encrypter using RSA-OAEP with AES128-GCM.
NewEncrypter(A128GCM, Recipient{Algorithm: RSA_OAEP, Key: publicKey}, nil)
// Instantiate an encrypter using RSA-PKCS1v1.5 with AES128-CBC+HMAC.
NewEncrypter(A128CBC_HS256, Recipient{Algorithm: RSA1_5, Key: publicKey}, nil)
}
func ExampleNewEncrypter_symmetric() {
var sharedKey []byte
// Instantiate an encrypter using AES128-GCM with AES-GCM key wrap.
NewEncrypter(A128GCM, Recipient{Algorithm: A128GCMKW, Key: sharedKey}, nil)
// Instantiate an encrypter using AES128-GCM directly, w/o key wrapping.
NewEncrypter(A128GCM, Recipient{Algorithm: DIRECT, Key: sharedKey}, nil)
}
func ExampleNewSigner_publicKey() {
var rsaPrivateKey *rsa.PrivateKey
var ecdsaPrivateKey *ecdsa.PrivateKey
// Instantiate a signer using RSA-PKCS#1v1.5 with SHA-256.
NewSigner(SigningKey{Algorithm: RS256, Key: rsaPrivateKey}, nil)
// Instantiate a signer using ECDSA with SHA-384.
NewSigner(SigningKey{Algorithm: ES384, Key: ecdsaPrivateKey}, nil)
}
func ExampleNewSigner_symmetric() {
var sharedKey []byte
// Instantiate an signer using HMAC-SHA256.
NewSigner(SigningKey{Algorithm: HS256, Key: sharedKey}, nil)
// Instantiate an signer using HMAC-SHA512.
NewSigner(SigningKey{Algorithm: HS512, Key: sharedKey}, nil)
}
func ExampleNewMultiEncrypter() {
var publicKey *rsa.PublicKey
var sharedKey []byte
// Instantiate an encrypter using AES-GCM.
NewMultiEncrypter(A128GCM, []Recipient{
{Algorithm: A128GCMKW, Key: sharedKey},
{Algorithm: RSA_OAEP, Key: publicKey},
}, nil)
}
func ExampleNewMultiSigner() {
var privateKey *rsa.PrivateKey
var sharedKey []byte
// Instantiate a signer for multiple recipients.
NewMultiSigner([]SigningKey{
{Algorithm: HS256, Key: sharedKey},
{Algorithm: PS384, Key: privateKey},
}, nil)
}
func ExampleEncrypter_encrypt() {
// Encrypt a plaintext in order to get an encrypted JWE object.
var plaintext = []byte("This is a secret message")
encrypter.Encrypt(plaintext)
}
func ExampleEncrypter_encryptWithAuthData() {
// Encrypt a plaintext in order to get an encrypted JWE object. Also attach
// some additional authenticated data (AAD) to the object. Note that objects
// with attached AAD can only be represented using JWE JSON Serialization.
var plaintext = []byte("This is a secret message")
var aad = []byte("This is authenticated, but public data")
encrypter.EncryptWithAuthData(plaintext, aad)
}
golang-github-go-jose-go-jose-4.0.5/encoding.go 0000664 0000000 0000000 00000012754 14761622347 0021313 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"bytes"
"compress/flate"
"encoding/base64"
"encoding/binary"
"fmt"
"io"
"math/big"
"strings"
"unicode"
"github.com/go-jose/go-jose/v4/json"
)
// Helper function to serialize known-good objects.
// Precondition: value is not a nil pointer.
func mustSerializeJSON(value interface{}) []byte {
out, err := json.Marshal(value)
if err != nil {
panic(err)
}
// We never want to serialize the top-level value "null," since it's not a
// valid JOSE message. But if a caller passes in a nil pointer to this method,
// MarshalJSON will happily serialize it as the top-level value "null". If
// that value is then embedded in another operation, for instance by being
// base64-encoded and fed as input to a signing algorithm
// (https://github.com/go-jose/go-jose/issues/22), the result will be
// incorrect. Because this method is intended for known-good objects, and a nil
// pointer is not a known-good object, we are free to panic in this case.
// Note: It's not possible to directly check whether the data pointed at by an
// interface is a nil pointer, so we do this hacky workaround.
// https://groups.google.com/forum/#!topic/golang-nuts/wnH302gBa4I
if string(out) == "null" {
panic("Tried to serialize a nil pointer.")
}
return out
}
// Strip all newlines and whitespace
func stripWhitespace(data string) string {
buf := strings.Builder{}
buf.Grow(len(data))
for _, r := range data {
if !unicode.IsSpace(r) {
buf.WriteRune(r)
}
}
return buf.String()
}
// Perform compression based on algorithm
func compress(algorithm CompressionAlgorithm, input []byte) ([]byte, error) {
switch algorithm {
case DEFLATE:
return deflate(input)
default:
return nil, ErrUnsupportedAlgorithm
}
}
// Perform decompression based on algorithm
func decompress(algorithm CompressionAlgorithm, input []byte) ([]byte, error) {
switch algorithm {
case DEFLATE:
return inflate(input)
default:
return nil, ErrUnsupportedAlgorithm
}
}
// deflate compresses the input.
func deflate(input []byte) ([]byte, error) {
output := new(bytes.Buffer)
// Writing to byte buffer, err is always nil
writer, _ := flate.NewWriter(output, 1)
_, _ = io.Copy(writer, bytes.NewBuffer(input))
err := writer.Close()
return output.Bytes(), err
}
// inflate decompresses the input.
//
// Errors if the decompressed data would be >250kB or >10x the size of the
// compressed data, whichever is larger.
func inflate(input []byte) ([]byte, error) {
output := new(bytes.Buffer)
reader := flate.NewReader(bytes.NewBuffer(input))
maxCompressedSize := max(250_000, 10*int64(len(input)))
limit := maxCompressedSize + 1
n, err := io.CopyN(output, reader, limit)
if err != nil && err != io.EOF {
return nil, err
}
if n == limit {
return nil, fmt.Errorf("uncompressed data would be too large (>%d bytes)", maxCompressedSize)
}
err = reader.Close()
return output.Bytes(), err
}
// byteBuffer represents a slice of bytes that can be serialized to url-safe base64.
type byteBuffer struct {
data []byte
}
func newBuffer(data []byte) *byteBuffer {
if data == nil {
return nil
}
return &byteBuffer{
data: data,
}
}
func newFixedSizeBuffer(data []byte, length int) *byteBuffer {
if len(data) > length {
panic("go-jose/go-jose: invalid call to newFixedSizeBuffer (len(data) > length)")
}
pad := make([]byte, length-len(data))
return newBuffer(append(pad, data...))
}
func newBufferFromInt(num uint64) *byteBuffer {
data := make([]byte, 8)
binary.BigEndian.PutUint64(data, num)
return newBuffer(bytes.TrimLeft(data, "\x00"))
}
func (b *byteBuffer) MarshalJSON() ([]byte, error) {
return json.Marshal(b.base64())
}
func (b *byteBuffer) UnmarshalJSON(data []byte) error {
var encoded string
err := json.Unmarshal(data, &encoded)
if err != nil {
return err
}
if encoded == "" {
return nil
}
decoded, err := base64.RawURLEncoding.DecodeString(encoded)
if err != nil {
return err
}
*b = *newBuffer(decoded)
return nil
}
func (b *byteBuffer) base64() string {
return base64.RawURLEncoding.EncodeToString(b.data)
}
func (b *byteBuffer) bytes() []byte {
// Handling nil here allows us to transparently handle nil slices when serializing.
if b == nil {
return nil
}
return b.data
}
func (b byteBuffer) bigInt() *big.Int {
return new(big.Int).SetBytes(b.data)
}
func (b byteBuffer) toInt() int {
return int(b.bigInt().Int64())
}
func base64EncodeLen(sl []byte) int {
return base64.RawURLEncoding.EncodedLen(len(sl))
}
func base64JoinWithDots(inputs ...[]byte) string {
if len(inputs) == 0 {
return ""
}
// Count of dots.
totalCount := len(inputs) - 1
for _, input := range inputs {
totalCount += base64EncodeLen(input)
}
out := make([]byte, totalCount)
startEncode := 0
for i, input := range inputs {
base64.RawURLEncoding.Encode(out[startEncode:], input)
if i == len(inputs)-1 {
continue
}
startEncode += base64EncodeLen(input)
out[startEncode] = '.'
startEncode++
}
return string(out)
}
golang-github-go-jose-go-jose-4.0.5/encoding_test.go 0000664 0000000 0000000 00000007755 14761622347 0022357 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"bytes"
"crypto/rand"
"io"
"strings"
"testing"
)
func TestDeflateRoundtrip(t *testing.T) {
original := []byte("Lorem ipsum dolor sit amet")
compressed, err := deflate(original)
if err != nil {
panic(err)
}
output, err := inflate(compressed)
if err != nil {
panic(err)
}
if !bytes.Equal(output, original) {
t.Error("Input and output do not match")
}
}
func TestInvalidCompression(t *testing.T) {
_, err := compress("XYZ", []byte{})
if err == nil {
t.Error("should not accept invalid algorithm")
}
_, err = decompress("XYZ", []byte{})
if err == nil {
t.Error("should not accept invalid algorithm")
}
_, err = decompress(DEFLATE, []byte{1, 2, 3, 4})
if err == nil {
t.Error("should not accept invalid data")
}
}
// TestLargeZip tests that we can decompress a large input, so long as its
// compression ratio is reasonable.
func TestLargeZip(t *testing.T) {
input := new(bytes.Buffer)
_, err := io.CopyN(input, rand.Reader, 251_000)
if err != nil {
t.Fatalf("generating input: %s", err)
}
compressed, err := compress(DEFLATE, input.Bytes())
if err != nil {
t.Errorf("compressing: %s", err)
}
t.Logf("compression ratio: %g", float64(len(input.Bytes()))/float64(len(compressed)))
_, err = decompress(DEFLATE, compressed)
if err != nil {
t.Errorf("decompressing large input with low compression ratio: %s", err)
}
}
func TestZipBomb(t *testing.T) {
input := strings.Repeat("a", 251_000)
compressed, err := compress(DEFLATE, []byte(input))
if err != nil {
t.Errorf("compressing: %s", err)
}
t.Logf("compression ratio: %d %g", len(compressed), float64(len(input))/float64(len(compressed)))
out, err := decompress(DEFLATE, compressed)
if err == nil {
t.Errorf("expected error decompressing zip bomb, got none. output size %d", len(out))
}
}
func TestByteBufferTrim(t *testing.T) {
buf := newBufferFromInt(1)
if !bytes.Equal(buf.data, []byte{1}) {
t.Error("Byte buffer for integer '1' should contain [0x01]")
}
buf = newBufferFromInt(65537)
if !bytes.Equal(buf.data, []byte{1, 0, 1}) {
t.Error("Byte buffer for integer '65537' should contain [0x01, 0x00, 0x01]")
}
}
func TestFixedSizeBuffer(t *testing.T) {
data0 := []byte{}
data1 := []byte{1}
data2 := []byte{1, 2}
data3 := []byte{1, 2, 3}
data4 := []byte{1, 2, 3, 4}
buf0 := newFixedSizeBuffer(data0, 4)
buf1 := newFixedSizeBuffer(data1, 4)
buf2 := newFixedSizeBuffer(data2, 4)
buf3 := newFixedSizeBuffer(data3, 4)
buf4 := newFixedSizeBuffer(data4, 4)
if !bytes.Equal(buf0.data, []byte{0, 0, 0, 0}) {
t.Error("Invalid padded buffer for buf0")
}
if !bytes.Equal(buf1.data, []byte{0, 0, 0, 1}) {
t.Error("Invalid padded buffer for buf1")
}
if !bytes.Equal(buf2.data, []byte{0, 0, 1, 2}) {
t.Error("Invalid padded buffer for buf2")
}
if !bytes.Equal(buf3.data, []byte{0, 1, 2, 3}) {
t.Error("Invalid padded buffer for buf3")
}
if !bytes.Equal(buf4.data, []byte{1, 2, 3, 4}) {
t.Error("Invalid padded buffer for buf4")
}
}
func TestSerializeJSONRejectsNil(t *testing.T) {
defer func() {
r := recover()
if r == nil || !strings.Contains(r.(string), "nil pointer") {
t.Error("serialize function should not accept nil pointer")
}
}()
mustSerializeJSON(nil)
}
func TestFixedSizeBufferTooLarge(t *testing.T) {
defer func() {
r := recover()
if r == nil {
t.Error("should not be able to create fixed size buffer with oversized data")
}
}()
newFixedSizeBuffer(make([]byte, 2), 1)
}
golang-github-go-jose-go-jose-4.0.5/go.mod 0000664 0000000 0000000 00000000461 14761622347 0020274 0 ustar 00root root 0000000 0000000 module github.com/go-jose/go-jose/v4
go 1.21
require (
github.com/google/go-cmp v0.6.0
github.com/stretchr/testify v1.10.0
golang.org/x/crypto v0.32.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
golang-github-go-jose-go-jose-4.0.5/go.sum 0000664 0000000 0000000 00000002271 14761622347 0020322 0 ustar 00root root 0000000 0000000 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
golang-github-go-jose-go-jose-4.0.5/jose-util/ 0000775 0000000 0000000 00000000000 14761622347 0021100 5 ustar 00root root 0000000 0000000 golang-github-go-jose-go-jose-4.0.5/jose-util/README.md 0000664 0000000 0000000 00000007211 14761622347 0022360 0 ustar 00root root 0000000 0000000 # JOSE CLI
The `jose-util` command line utility allows for encryption, decryption, signing
and verification of JOSE messages. Its main purpose is to facilitate dealing
with JOSE messages when testing or debugging.
## Installation
```
$ go install github.com/go-jose/go-jose/v4/jose-util@latest
```
## Usage
The utility includes the subcommands `encrypt`, `decrypt`, `sign`, `verify` and
`expand`. Examples for each command can be found below.
Algorithms are selected via the `--alg` and `--enc` flags, which influence the
`alg` and `enc` headers in respectively. For JWE, `--alg` specifies the key
management algorithm (e.g. `RSA-OAEP`) and `--enc` specifies the content
encryption algorithm (e.g. `A128GCM`). For JWS, `--alg` specifies the
signature algorithm (e.g. `PS256`).
Input and output files can be specified via the `--in` and `--out` flags.
Either flag can be omitted, in which case `jose-util` uses stdin/stdout for
input/output respectively. By default each command will output a compact
message, but it's possible to get the JSON Serialization by supplying the
`--full` flag.
Keys are specified via the `--key` flag. Supported key types are naked RSA/EC
keys and X.509 certificates with embedded RSA/EC keys. Keys must be in PEM,
DER or JWK formats.
## Testing
`cram` is used for testing. This can be installed with pip or `sudo apt install
python-cram` See the travis file for how this is used in testing. For example,
`go build && PATH=$PWD:$PATH cram -v jose-util.t`
## Testing
`cram` is used for testing. This can be installed with pip or `sudo apt install
python-cram` See the travis file for how this is used in testing. For example,
`go build && PATH=$PWD:$PATH cram -v jose-util.t`
## Examples
### Generate key pair
Generates a key pair, either for signing/verification or encryption/decryption. Generated keys will be written to the current directory.
# Generate keys for signing (for RSA-PSS)
jose-util generate-key --use sig --alg RS256
# Generate keys for signing (for EdDSA)
jose-util generate-key --use sig --alg EdDSA
# Generate keys for encryption (for RSA-OAEP)
jose-util generate-key --use enc --alg RSA-OAEP
# Generate keys for encryption (for ECDH-ES)
jose-util generate-key --use enc --alg ECDH-ES+A128KW
### Encrypt
Takes a plaintext as input, encrypts, and prints the encrypted message.
# From stdin, to stdout
jose-util encrypt --key public-key.pem --alg RSA-OAEP --enc A128GCM
# Operating on files
jose-util encrypt --key public-key.pem --alg RSA-OAEP --enc A128GCM --in plaintext.txt --out ciphertext.txt
### Decrypt
Takes an encrypted message (JWE) as input, decrypts, and prints the plaintext.
# From stdin, to stdout
jose-util decrypt --key private-key.pem
# Operating on files
jose-util decrypt --key private-key.pem --in ciphertext.txt --out plaintext.txt
### Sign
Takes a payload as input, signs it, and prints the signed message with the embedded payload.
# From stdin, to stdout
jose-util sign --key private-key.pem --alg PS256
# Operating on files
jose-util sign --key private-key.pem --alg PS256 --in message.txt --out signed-message.txt
### Verify
Reads a signed message (JWS), verifies it, and extracts the payload.
# From stdin, to stdout
jose-util verify --key public-key.pem
# Operating on files
jose-util verify --key public-key.pem --in signed-message.txt --out message.txt
### Expand
Expands a compact message to the JWE/JWS JSON Serialization format.
jose-util expand --format JWE # Expands a compact JWE to JWE JSON Serialization
jose-util expand --format JWS # Expands a compact JWS to JWS JSON Serialization
golang-github-go-jose-go-jose-4.0.5/jose-util/crypto.go 0000664 0000000 0000000 00000012234 14761622347 0022751 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2019 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"flag"
"fmt"
"github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jose-util/generator"
)
var allKeyAlgorithms = []jose.KeyAlgorithm{
jose.ED25519,
jose.RSA1_5,
jose.RSA_OAEP,
jose.RSA_OAEP_256,
jose.A128KW,
jose.A192KW,
jose.A256KW,
jose.DIRECT,
jose.ECDH_ES,
jose.ECDH_ES_A128KW,
jose.ECDH_ES_A192KW,
jose.ECDH_ES_A256KW,
jose.A128GCMKW,
jose.A192GCMKW,
jose.A256GCMKW,
jose.PBES2_HS256_A128KW,
jose.PBES2_HS384_A192KW,
jose.PBES2_HS512_A256KW,
}
var allSignatureAlgorithms = []jose.SignatureAlgorithm{
jose.EdDSA,
jose.HS256,
jose.HS384,
jose.HS512,
jose.RS256,
jose.RS384,
jose.RS512,
jose.ES256,
jose.ES384,
jose.ES512,
jose.PS256,
jose.PS384,
jose.PS512,
}
var allContentEncryption = []jose.ContentEncryption{
jose.A128CBC_HS256,
jose.A192CBC_HS384,
jose.A256CBC_HS512,
jose.A128GCM,
jose.A192GCM,
jose.A256GCM,
}
func encrypt(args []string) error {
fs := flag.NewFlagSet("encrypt", flag.ExitOnError)
encryptAlgFlag := fs.String("alg", "", "Key management algorithm (e.g. RSA-OAEP)")
encryptEncFlag := fs.String("enc", "", "Content encryption algorithm (e.g. A128GCM)")
encryptFullFlag := fs.Bool("full", false, "Use JSON Serialization format (instead of compact)")
registerCommon(fs)
err := fs.Parse(args)
bytes, err := keyBytes()
if err != nil {
return err
}
pub, err := generator.LoadPublicKey(bytes)
if err != nil {
return fmt.Errorf("unable to read public key: %w", err)
}
alg := jose.KeyAlgorithm(*encryptAlgFlag)
enc := jose.ContentEncryption(*encryptEncFlag)
crypter, err := jose.NewEncrypter(enc, jose.Recipient{Algorithm: alg, Key: pub}, nil)
if err != nil {
return fmt.Errorf("unable to instantiate encrypter: %w", err)
}
input, err := readInput(*inFile)
if err != nil {
return err
}
obj, err := crypter.Encrypt(input)
if err != nil {
return fmt.Errorf("unable to encrypt: %w", err)
}
var msg string
if *encryptFullFlag {
msg = obj.FullSerialize()
} else {
msg, err = obj.CompactSerialize()
if err != nil {
return fmt.Errorf("unable to serialzie message: %w", err)
}
}
return writeOutput(*outFile, []byte(msg))
}
func decrypt(args []string) error {
fs := flag.NewFlagSet("decrypt", flag.ExitOnError)
registerCommon(fs)
fs.Parse(args)
bytes, err := keyBytes()
if err != nil {
return err
}
priv, err := generator.LoadPrivateKey(bytes)
if err != nil {
return fmt.Errorf("unable to read private key %s: %w", priv, err)
}
input, err := readInput(*inFile)
if err != nil {
return err
}
obj, err := jose.ParseEncrypted(string(input), allKeyAlgorithms, allContentEncryption)
if err != nil {
return fmt.Errorf("unable to parse message: %w", err)
}
plaintext, err := obj.Decrypt(priv)
if err != nil {
return fmt.Errorf("unable to decrypt message: %w", err)
}
return writeOutput(*outFile, plaintext)
}
func sign(args []string) error {
fs := flag.NewFlagSet("encrypt", flag.ExitOnError)
signAlgFlag := fs.String("alg", "", "Key management algorithm (e.g. RSA-OAEP)")
signFullFlag := fs.Bool("full", false, "Use JSON Serialization format (instead of compact)")
registerCommon(fs)
fs.Parse(args)
bytes, err := keyBytes()
if err != nil {
return err
}
signingKey, err := generator.LoadPrivateKey(bytes)
if err != nil {
return fmt.Errorf("unable to read private key: %w", err)
}
alg := jose.SignatureAlgorithm(*signAlgFlag)
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: signingKey}, nil)
if err != nil {
return fmt.Errorf("unable to make signer: %w", err)
}
input, err := readInput(*inFile)
if err != nil {
return err
}
obj, err := signer.Sign(input)
if err != nil {
return fmt.Errorf("unable to sign: %w", err)
}
var msg string
if *signFullFlag {
msg = obj.FullSerialize()
} else {
msg, err = obj.CompactSerialize()
if err != nil {
return fmt.Errorf("unable to serialize message: %w", err)
}
}
return writeOutput(*outFile, []byte(msg))
}
func verify(args []string) error {
fs := flag.NewFlagSet("verify", flag.ExitOnError)
registerCommon(fs)
fs.Parse(args)
bytes, err := keyBytes()
if err != nil {
return err
}
verificationKey, err := generator.LoadPublicKey(bytes)
if err != nil {
return fmt.Errorf("unable to read public key: %w", err)
}
input, err := readInput(*inFile)
if err != nil {
return err
}
obj, err := jose.ParseSigned(string(input), allSignatureAlgorithms)
if err != nil {
return fmt.Errorf("unable to parse message: %w", err)
}
plaintext, err := obj.Verify(verificationKey)
if err != nil {
return fmt.Errorf("invalid signature: %w", err)
}
return writeOutput(*outFile, plaintext)
}
golang-github-go-jose-go-jose-4.0.5/jose-util/format.go 0000664 0000000 0000000 00000003106 14761622347 0022717 0 ustar 00root root 0000000 0000000 /*
* Copyright 2019 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"flag"
"fmt"
"github.com/go-jose/go-jose/v4"
)
func expand(args []string) error {
fs := flag.NewFlagSet("expand", flag.ExitOnError)
expandFormatFlag := fs.String("format", "", "Type of message to expand (JWS or JWE, defaults to JWE)")
registerCommon(fs)
fs.Parse(args)
bytes, err := readInput(*inFile)
if err != nil {
return err
}
input := string(bytes)
var serialized string
switch *expandFormatFlag {
case "", "JWE":
var jwe *jose.JSONWebEncryption
jwe, err = jose.ParseEncrypted(input, allKeyAlgorithms, allContentEncryption)
if err == nil {
serialized = jwe.FullSerialize()
}
case "JWS":
var jws *jose.JSONWebSignature
jws, err = jose.ParseSigned(input, allSignatureAlgorithms)
if err == nil {
serialized = jws.FullSerialize()
}
}
if err != nil {
return fmt.Errorf("unable to expand message: %w", err)
}
err = writeOutput(*outFile, []byte(serialized))
if err != nil {
return err
}
return writeOutput(*outFile, []byte("\n"))
}
golang-github-go-jose-go-jose-4.0.5/jose-util/generate.go 0000664 0000000 0000000 00000006734 14761622347 0023233 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2019 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"crypto"
"encoding/base64"
"flag"
"fmt"
"github.com/go-jose/go-jose/v4"
"github.com/go-jose/go-jose/v4/jose-util/generator"
)
func generate(args []string) error {
fs := flag.NewFlagSet("generate", flag.ExitOnError)
generateUseFlag := fs.String("use", "", "Desired public key usage (use header), one of [enc sig]")
generateAlgFlag := fs.String("alg", "", "Desired key pair algorithm (alg header)")
generateKeySizeFlag := fs.Int("size", 0, "Key size in bits (e.g. 2048 if generating an RSA key)")
generateKeyIdentFlag := fs.String("kid", "", "Optional Key ID (kid header, generate random kid if not set)")
fs.Parse(args)
var privKey crypto.PrivateKey
var pubKey crypto.PublicKey
var err error
switch *generateUseFlag {
case "sig":
pubKey, privKey, err = generator.NewSigningKey(jose.SignatureAlgorithm(*generateAlgFlag), *generateKeySizeFlag)
case "enc":
pubKey, privKey, err = generator.NewEncryptionKey(jose.KeyAlgorithm(*generateAlgFlag), *generateKeySizeFlag)
default:
// According to RFC 7517 section-8.2. This is unlikely to change in the
// near future. If it were, new values could be found in the registry under
// "JSON Web Key Use": https://www.iana.org/assignments/jose/jose.xhtml
return fmt.Errorf("invalid key use '%s'. Must be \"sig\" or \"enc\"", *generateUseFlag)
}
if err != nil {
return fmt.Errorf("unable to generate key: %w", err)
}
kid := *generateKeyIdentFlag
priv := jose.JSONWebKey{Key: privKey, KeyID: kid, Algorithm: *generateAlgFlag, Use: *generateUseFlag}
// Generate a canonical kid based on RFC 7638
if kid == "" {
thumb, err := priv.Thumbprint(crypto.SHA256)
if err != nil {
return fmt.Errorf("unable to compute thumbprint: %w", err)
}
kid = base64.URLEncoding.EncodeToString(thumb)
priv.KeyID = kid
}
// I'm not sure why we couldn't use `pub := priv.Public()` here as the private
// key should contain the public key. In case for some reason it doesn't,
// this builds a public JWK from scratch.
pub := jose.JSONWebKey{Key: pubKey, KeyID: kid, Algorithm: *generateAlgFlag, Use: *generateUseFlag}
if priv.IsPublic() || !pub.IsPublic() || !priv.Valid() || !pub.Valid() {
// This should never happen
panic("invalid keys were generated")
}
privJSON, err := priv.MarshalJSON()
if err != nil {
return fmt.Errorf("failed to marshal private key to JSON: %w", err)
}
pubJSON, err := pub.MarshalJSON()
if err != nil {
return fmt.Errorf("failed to marshal public key to JSON: %w", err)
}
name := fmt.Sprintf("jwk-%s-%s", *generateUseFlag, kid)
pubFile := fmt.Sprintf("%s-pub.json", name)
privFile := fmt.Sprintf("%s-priv.json", name)
err = writeNewFile(pubFile, pubJSON, 0444)
if err != nil {
return fmt.Errorf("error on write to file %s: %w", pubFile, err)
}
err = writeNewFile(privFile, privJSON, 0400)
if err != nil {
return fmt.Errorf("error on write to file %s: %w", privFile, err)
}
return nil
}
golang-github-go-jose-go-jose-4.0.5/jose-util/generator/ 0000775 0000000 0000000 00000000000 14761622347 0023066 5 ustar 00root root 0000000 0000000 golang-github-go-jose-go-jose-4.0.5/jose-util/generator/encoding.go 0000664 0000000 0000000 00000002342 14761622347 0025204 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2019 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generator
import "io"
// Base64Reader wraps an input stream consisting of either standard or url-safe
// base64 data, and maps it to a raw (unpadded) standard encoding. This can be used
// to read any base64-encoded data as input, whether padded, unpadded, standard or
// url-safe.
type Base64Reader struct {
In io.Reader
}
func (r Base64Reader) Read(p []byte) (n int, err error) {
n, err = r.In.Read(p)
for i := 0; i < n; i++ {
switch p[i] {
// Map - to +
case 0x2D:
p[i] = 0x2B
// Map _ to /
case 0x5F:
p[i] = 0x2F
// Strip =
case 0x3D:
n = i
break
default:
}
}
if n == 0 {
err = io.EOF
}
return
}
golang-github-go-jose-go-jose-4.0.5/jose-util/generator/generate.go 0000664 0000000 0000000 00000006661 14761622347 0025220 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2019 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generator
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"errors"
"fmt"
"github.com/go-jose/go-jose/v4"
)
// NewSigningKey generates a keypair for corresponding SignatureAlgorithm.
func NewSigningKey(alg jose.SignatureAlgorithm, bits int) (crypto.PublicKey, crypto.PrivateKey, error) {
switch alg {
case jose.ES256, jose.ES384, jose.ES512, jose.EdDSA:
keylen := map[jose.SignatureAlgorithm]int{
jose.ES256: 256,
jose.ES384: 384,
jose.ES512: 521, // sic!
jose.EdDSA: 256,
}
if bits != 0 && bits != keylen[alg] {
return nil, nil, errors.New("invalid elliptic curve key size, this algorithm does not support arbitrary size")
}
case jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512:
if bits == 0 {
bits = 2048
}
if bits < 2048 {
return nil, nil, errors.New("invalid key size for RSA key, 2048 or more is required")
}
}
switch alg {
case jose.ES256:
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, nil, err
}
return key.Public(), key, err
case jose.ES384:
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
return nil, nil, err
}
return key.Public(), key, err
case jose.ES512:
key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
return nil, nil, err
}
return key.Public(), key, err
case jose.EdDSA:
pub, key, err := ed25519.GenerateKey(rand.Reader)
return pub, key, err
case jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512:
key, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, nil, err
}
return key.Public(), key, err
default:
return nil, nil, fmt.Errorf("unknown algorithm %s for signing key", alg)
}
}
// NewEncryptionKey generates a keypair for corresponding KeyAlgorithm.
func NewEncryptionKey(alg jose.KeyAlgorithm, bits int) (crypto.PublicKey, crypto.PrivateKey, error) {
switch alg {
case jose.RSA1_5, jose.RSA_OAEP, jose.RSA_OAEP_256:
if bits == 0 {
bits = 2048
}
if bits < 2048 {
return nil, nil, errors.New("invalid key size for RSA key, 2048 or more is required")
}
key, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, nil, err
}
return key.Public(), key, err
case jose.ECDH_ES, jose.ECDH_ES_A128KW, jose.ECDH_ES_A192KW, jose.ECDH_ES_A256KW:
var crv elliptic.Curve
switch bits {
case 0, 256:
crv = elliptic.P256()
case 384:
crv = elliptic.P384()
case 521:
crv = elliptic.P521()
default:
return nil, nil, errors.New("invalid elliptic curve key size, use one of 256, 384, or 521")
}
key, err := ecdsa.GenerateKey(crv, rand.Reader)
if err != nil {
return nil, nil, err
}
return key.Public(), key, err
default:
return nil, nil, fmt.Errorf("unknown algorithm %s for encryption key", alg)
}
}
golang-github-go-jose-go-jose-4.0.5/jose-util/generator/utils.go 0000664 0000000 0000000 00000004325 14761622347 0024561 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2019 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package generator
import (
"crypto/x509"
"encoding/pem"
"errors"
"github.com/go-jose/go-jose/v4"
)
func LoadJSONWebKey(json []byte, pub bool) (*jose.JSONWebKey, error) {
var jwk jose.JSONWebKey
err := jwk.UnmarshalJSON(json)
if err != nil {
return nil, err
}
if !jwk.Valid() {
return nil, errors.New("invalid JWK key")
}
if jwk.IsPublic() != pub {
return nil, errors.New("priv/pub JWK key mismatch")
}
return &jwk, nil
}
// LoadPublicKey loads a public key from PEM/DER/JWK-encoded data.
func LoadPublicKey(data []byte) (interface{}, error) {
input := data
block, _ := pem.Decode(data)
if block != nil {
input = block.Bytes
}
// Try to load SubjectPublicKeyInfo
pub, err0 := x509.ParsePKIXPublicKey(input)
if err0 == nil {
return pub, nil
}
cert, err1 := x509.ParseCertificate(input)
if err1 == nil {
return cert.PublicKey, nil
}
jwk, err2 := LoadJSONWebKey(data, true)
if err2 == nil {
return jwk, nil
}
return nil, errors.New("parse error, invalid public key")
}
// LoadPrivateKey loads a private key from PEM/DER/JWK-encoded data.
func LoadPrivateKey(data []byte) (interface{}, error) {
input := data
block, _ := pem.Decode(data)
if block != nil {
input = block.Bytes
}
var priv interface{}
priv, err0 := x509.ParsePKCS1PrivateKey(input)
if err0 == nil {
return priv, nil
}
priv, err1 := x509.ParsePKCS8PrivateKey(input)
if err1 == nil {
return priv, nil
}
priv, err2 := x509.ParseECPrivateKey(input)
if err2 == nil {
return priv, nil
}
jwk, err3 := LoadJSONWebKey(input, false)
if err3 == nil {
return jwk, nil
}
return nil, errors.New("parse error, invalid private key")
}
golang-github-go-jose-go-jose-4.0.5/jose-util/io.go 0000664 0000000 0000000 00000003573 14761622347 0022046 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2019 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"fmt"
"io"
"os"
)
// Read input from file or stdin
func readInput(path string) ([]byte, error) {
var bytes []byte
var err error
if path != "" {
bytes, err = os.ReadFile(path)
} else {
bytes, err = io.ReadAll(os.Stdin)
}
if err != nil {
return nil, fmt.Errorf("unable to read input: %w", err)
}
return bytes, nil
}
// Write output to file or stdin
func writeOutput(path string, data []byte) error {
var err error
if path != "" {
err = os.WriteFile(path, data, 0644)
} else {
_, err = os.Stdout.Write(data)
}
if err != nil {
return fmt.Errorf("unable to write output: %w", err)
}
return nil
}
// Byte contents of key file
func keyBytes() ([]byte, error) {
if *keyFile == "" {
return nil, fmt.Errorf("no key file provided. See -h for usage")
}
keyBytes, err := os.ReadFile(*keyFile)
if err != nil {
return nil, fmt.Errorf("unable to read key file: %w", err)
}
return keyBytes, nil
}
// Write new file to current dir
func writeNewFile(filename string, data []byte, perm os.FileMode) error {
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
if err != nil {
return err
}
n, err := f.Write(data)
if err == nil && n < len(data) {
err = io.ErrShortWrite
}
if err1 := f.Close(); err == nil {
err = err1
}
return err
}
golang-github-go-jose-go-jose-4.0.5/jose-util/jose-util.t 0000664 0000000 0000000 00000011232 14761622347 0023177 0 ustar 00root root 0000000 0000000 This is a cram test file. See the travis file for how this is used in testing.
For example, `go build && PATH=$PWD:$PATH cram -v jose-util.t`
Set up static test keys.
$ cat > rsa.pub < -----BEGIN PUBLIC KEY-----
> MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAslWybuiNYR7uOgKuvaBw
> qVk8saEutKhOAaW+3hWF65gJei+ZV8QFfYDxs9ZaRZlWAUMtncQPnw7ZQlXO9ogN
> 5cMcN50C6qMOOZzghK7danalhF5lUETC4Hk3Eisbi/PR3IfVyXaRmqL6X66MKj/J
> AKyD9NFIDVy52K8A198Jojnrw2+XXQW72U68fZtvlyl/BTBWQ9Re5JSTpEcVmpCR
> 8FrFc0RPMBm+G5dRs08vvhZNiTT2JACO5V+J5ZrgP3s5hnGFcQFZgDnXLInDUdoi
> 1MuCjaAU0ta8/08pHMijNix5kFofdPEB954MiZ9k4kQ5/utt02I9x2ssHqw71ojj
> vwIDAQAB
> -----END PUBLIC KEY-----
> EOF
$ cat > rsa.key < -----BEGIN RSA PRIVATE KEY-----
> MIIEogIBAAKCAQEAslWybuiNYR7uOgKuvaBwqVk8saEutKhOAaW+3hWF65gJei+Z
> V8QFfYDxs9ZaRZlWAUMtncQPnw7ZQlXO9ogN5cMcN50C6qMOOZzghK7danalhF5l
> UETC4Hk3Eisbi/PR3IfVyXaRmqL6X66MKj/JAKyD9NFIDVy52K8A198Jojnrw2+X
> XQW72U68fZtvlyl/BTBWQ9Re5JSTpEcVmpCR8FrFc0RPMBm+G5dRs08vvhZNiTT2
> JACO5V+J5ZrgP3s5hnGFcQFZgDnXLInDUdoi1MuCjaAU0ta8/08pHMijNix5kFof
> dPEB954MiZ9k4kQ5/utt02I9x2ssHqw71ojjvwIDAQABAoIBABrYDYDmXom1BzUS
> PE1s/ihvt1QhqA8nmn5i/aUeZkc9XofW7GUqq4zlwPxKEtKRL0IHY7Fw1s0hhhCX
> LA0uE7F3OiMg7lR1cOm5NI6kZ83jyCxxrRx1DUSO2nxQotfhPsDMbaDiyS4WxEts
> 0cp2SYJhdYd/jTH9uDfmt+DGwQN7Jixio1Dj3vwB7krDY+mdre4SFY7Gbk9VxkDg
> LgCLMoq52m+wYufP8CTgpKFpMb2/yJrbLhuJxYZrJ3qd/oYo/91k6v7xlBKEOkwD
> 2veGk9Dqi8YPNxaRktTEjnZb6ybhezat93+VVxq4Oem3wMwou1SfXrSUKtgM/p2H
> vfw/76ECgYEA2fNL9tC8u9M0wjA+kvvtDG96qO6O66Hksssy6RWInD+Iqk3MtHQt
> LeoCjvX+zERqwOb6SI6empk5pZ9E3/9vJ0dBqkxx3nqn4M/nRWnExGgngJsL959t
> f50cdxva8y1RjNhT4kCwTrupX/TP8lAG8SfG1Alo2VFR8iWd8hDQcTECgYEA0Xfj
> EgqAsVh4U0s3lFxKjOepEyp0G1Imty5J16SvcOEAD1Mrmz94aSSp0bYhXNVdbf7n
> Rk77htWC7SE29fGjOzZRS76wxj/SJHF+rktHB2Zt23k1jBeZ4uLMPMnGLY/BJ099
> 5DTGo0yU0rrPbyXosx+ukfQLAHFuggX4RNeM5+8CgYB7M1J/hGMLcUpjcs4MXCgV
> XXbiw2c6v1r9zmtK4odEe42PZ0cNwpY/XAZyNZAAe7Q0stxL44K4NWEmxC80x7lX
> ZKozz96WOpNnO16qGC3IMHAT/JD5Or+04WTT14Ue7UEp8qcIQDTpbJ9DxKk/eglS
> jH+SIHeKULOXw7fSu7p4IQKBgBnyVchIUMSnBtCagpn4DKwDjif3nEY+GNmb/D2g
> ArNiy5UaYk5qwEmV5ws5GkzbiSU07AUDh5ieHgetk5dHhUayZcOSLWeBRFCLVnvU
> i0nZYEZNb1qZGdDG8zGcdNXz9qMd76Qy/WAA/nZT+Zn1AiweAovFxQ8a/etRPf2Z
> DbU1AoGAHpCgP7B/4GTBe49H0AQueQHBn4RIkgqMy9xiMeR+U+U0vaY0TlfLhnX+
> 5PkNfkPXohXlfL7pxwZNYa6FZhCAubzvhKCdUASivkoGaIEk6g1VTVYS/eDVQ4CA
> slfl+elXtLq/l1kQ8C14jlHrQzSXx4PQvjDEnAmaHSJNz4mP9Fg=
> -----END RSA PRIVATE KEY-----
> EOF
$ cat > ec.pub < -----BEGIN PUBLIC KEY-----
> MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE9yoUEAgxTd9svwe9oPqjhcP+f2jcdTL2
> Wq8Aw2v9ht1dBy00tFRPNrCxFCkvMcJFhSPoDUV5NL7zfh3/psiSNYziGPrWEJYf
> gmYihjSeoOf0ru1erpBrTflImPrMftCy
> -----END PUBLIC KEY-----
> EOF
$ cat > ec.key < -----BEGIN EC PRIVATE KEY-----
> MIGkAgEBBDDvoj/bM1HokUjYWO/IDFs26Jo0GIFtU3tMQQu7ZabKscDMK3dZA0mK
> v97ij7BBFbCgBwYFK4EEACKhZANiAAT3KhQQCDFN32y/B72g+qOFw/5/aNx1MvZa
> rwDDa/2G3V0HLTS0VE82sLEUKS8xwkWFI+gNRXk0vvN+Hf+myJI1jOIY+tYQlh+C
> ZiKGNJ6g5/Su7V6ukGtN+UiY+sx+0LI=
> -----END EC PRIVATE KEY-----
> EOF
Encrypt and then decrypt a test message (RSA).
$ echo "Lorem ipsum dolor sit amet" |
> jose-util encrypt --alg RSA-OAEP --enc A128GCM --key rsa.pub |
> jose-util decrypt --key rsa.key
Lorem ipsum dolor sit amet
Encrypt and then decrypt a test message (EC).
$ echo "Lorem ipsum dolor sit amet" |
> jose-util encrypt --alg ECDH-ES+A128KW --enc A128GCM --key ec.pub |
> jose-util decrypt --key ec.key
Lorem ipsum dolor sit amet
Sign and verify a test message (RSA).
$ echo "Lorem ipsum dolor sit amet" |
> jose-util sign --alg PS256 --key rsa.key |
> jose-util verify --key rsa.pub
Lorem ipsum dolor sit amet
Sign and verify a test message (EC).
$ echo "Lorem ipsum dolor sit amet" |
> jose-util sign --alg ES384 --key ec.key |
> jose-util verify --key ec.pub
Lorem ipsum dolor sit amet
Expand a compact message to JSON format.
$ echo "eyJhbGciOiJFUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQK.QPU35XY913Im7ZEaN2yHykfbtPqjHZvYp-lV8OcTAJZs67bJFSdTSkQhQWE9ch6tvYrj_7py6HKaWVFLll_s_Rm6bmwq3JszsHrIvFFm1NydruYHhvAnx7rjYiqwOu0W" |
> jose-util expand --format JWS
{"payload":"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQK","protected":"eyJhbGciOiJFUzM4NCJ9","signature":"QPU35XY913Im7ZEaN2yHykfbtPqjHZvYp-lV8OcTAJZs67bJFSdTSkQhQWE9ch6tvYrj_7py6HKaWVFLll_s_Rm6bmwq3JszsHrIvFFm1NydruYHhvAnx7rjYiqwOu0W"}
Generate signing keys in JWK format.
$ jose-util generate-key --use enc --alg RSA-OAEP --kid test && ls jwk-enc-test-*.json
jwk-enc-test-priv.json
jwk-enc-test-pub.json
$ jose-util generate-key --use sig --alg RS256 --kid test && ls jwk-sig-test-*.json
jwk-sig-test-priv.json
jwk-sig-test-pub.json
golang-github-go-jose-go-jose-4.0.5/jose-util/main.go 0000664 0000000 0000000 00000004315 14761622347 0022356 0 ustar 00root root 0000000 0000000 /*-
* Copyright 2019 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"flag"
"fmt"
"os"
)
var (
// Util-wide flags
keyFile *string
inFile *string
outFile *string
)
func registerCommon(fs *flag.FlagSet) {
keyFile = fs.String("key", "", "Path to key file (if applicable, PEM, DER or JWK format)")
inFile = fs.String("in", "", "Path to input file (if applicable, stdin if missing)")
outFile = fs.String("out", "", "Path to output file (if applicable, stdout if missing)")
}
func main() {
subCommands := map[string]struct {
desc string
run func(args []string) error
}{
"encrypt": {
desc: "Encrypt a plaintext, output ciphertext",
run: encrypt,
},
"decrypt": {
desc: "Decrypt a ciphertext, output plaintext",
run: decrypt,
},
"sign": {
desc: "Sign a payload, output signed message",
run: sign,
},
"verify": {
desc: "Verify a signed message, output payload",
run: verify,
},
"expand": {
desc: "Expand JOSE object to JSON Serialization format",
run: expand,
},
"generate-key": {
desc: "Generate a public/private key pair in JWK format",
run: generate,
},
}
usage := func() {
fmt.Printf("Usage: jose-utils [subcommand]\nSubcommands:\n")
for name, command := range subCommands {
fmt.Printf(" %s\n", name)
fmt.Printf("\t%s\n", command.desc)
}
fmt.Printf("Pass -h to each subcommand for more information")
os.Exit(1)
}
if len(os.Args) < 2 {
usage()
}
cmd, ok := subCommands[os.Args[1]]
if !ok {
fmt.Fprintf(os.Stderr, "invalid subcommand %s\n", os.Args[1])
usage()
}
err := cmd.run(os.Args[2:])
if err != nil {
fmt.Fprintf(os.Stderr, "error running command: %s\n", err)
os.Exit(1)
}
}
golang-github-go-jose-go-jose-4.0.5/json/ 0000775 0000000 0000000 00000000000 14761622347 0020136 5 ustar 00root root 0000000 0000000 golang-github-go-jose-go-jose-4.0.5/json/LICENSE 0000664 0000000 0000000 00000002707 14761622347 0021151 0 ustar 00root root 0000000 0000000 Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
golang-github-go-jose-go-jose-4.0.5/json/README.md 0000664 0000000 0000000 00000001170 14761622347 0021414 0 ustar 00root root 0000000 0000000 # Safe JSON
This repository contains a fork of the `encoding/json` package from Go 1.6.
The following changes were made:
* Object deserialization uses case-sensitive member name matching instead of
[case-insensitive matching](https://www.ietf.org/mail-archive/web/json/current/msg03763.html).
This is to avoid differences in the interpretation of JOSE messages between
go-jose and libraries written in other languages.
* When deserializing a JSON object, we check for duplicate keys and reject the
input whenever we detect a duplicate. Rather than trying to work with malformed
data, we prefer to reject it right away.
golang-github-go-jose-go-jose-4.0.5/json/bench_test.go 0000664 0000000 0000000 00000011007 14761622347 0022602 0 ustar 00root root 0000000 0000000 // Copyright 2011 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.
// Large data benchmark.
// The JSON data is a summary of agl's changes in the
// go, webkit, and chromium open source projects.
// We benchmark converting between the JSON form
// and in-memory data structures.
package json
import (
"bytes"
"compress/gzip"
"io"
"os"
"strings"
"testing"
)
type codeResponse struct {
Tree *codeNode `json:"tree"`
Username string `json:"username"`
}
type codeNode struct {
Name string `json:"name"`
Kids []*codeNode `json:"kids"`
CLWeight float64 `json:"cl_weight"`
Touches int `json:"touches"`
MinT int64 `json:"min_t"`
MaxT int64 `json:"max_t"`
MeanT int64 `json:"mean_t"`
}
var codeJSON []byte
var codeStruct codeResponse
func codeInit() {
f, err := os.Open("testdata/code.json.gz")
if err != nil {
panic(err)
}
defer f.Close()
gz, err := gzip.NewReader(f)
if err != nil {
panic(err)
}
data, err := io.ReadAll(gz)
if err != nil {
panic(err)
}
codeJSON = data
if err := Unmarshal(codeJSON, &codeStruct); err != nil {
panic("unmarshal code.json: " + err.Error())
}
if data, err = Marshal(&codeStruct); err != nil {
panic("marshal code.json: " + err.Error())
}
if !bytes.Equal(data, codeJSON) {
println("different lengths", len(data), len(codeJSON))
for i := 0; i < len(data) && i < len(codeJSON); i++ {
if data[i] != codeJSON[i] {
println("re-marshal: changed at byte", i)
println("orig: ", string(codeJSON[i-10:i+10]))
println("new: ", string(data[i-10:i+10]))
break
}
}
panic("re-marshal code.json: different result")
}
}
func BenchmarkCodeEncoder(b *testing.B) {
if codeJSON == nil {
b.StopTimer()
codeInit()
b.StartTimer()
}
enc := NewEncoder(io.Discard)
for i := 0; i < b.N; i++ {
if err := enc.Encode(&codeStruct); err != nil {
b.Fatal("Encode:", err)
}
}
b.SetBytes(int64(len(codeJSON)))
}
func BenchmarkCodeMarshal(b *testing.B) {
if codeJSON == nil {
b.StopTimer()
codeInit()
b.StartTimer()
}
for i := 0; i < b.N; i++ {
if _, err := Marshal(&codeStruct); err != nil {
b.Fatal("Marshal:", err)
}
}
b.SetBytes(int64(len(codeJSON)))
}
func BenchmarkCodeDecoder(b *testing.B) {
if codeJSON == nil {
b.StopTimer()
codeInit()
b.StartTimer()
}
var buf bytes.Buffer
dec := NewDecoder(&buf)
var r codeResponse
for i := 0; i < b.N; i++ {
buf.Write(codeJSON)
// hide EOF
buf.WriteByte('\n')
buf.WriteByte('\n')
buf.WriteByte('\n')
if err := dec.Decode(&r); err != nil {
b.Fatal("Decode:", err)
}
}
b.SetBytes(int64(len(codeJSON)))
}
func BenchmarkDecoderStream(b *testing.B) {
b.StopTimer()
var buf bytes.Buffer
dec := NewDecoder(&buf)
buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n")
var x interface{}
if err := dec.Decode(&x); err != nil {
b.Fatal("Decode:", err)
}
ones := strings.Repeat(" 1\n", 300000) + "\n\n\n"
b.StartTimer()
for i := 0; i < b.N; i++ {
if i%300000 == 0 {
buf.WriteString(ones)
}
x = nil
if err := dec.Decode(&x); err != nil || x != 1.0 {
b.Fatalf("Decode: %v after %d", err, i)
}
}
}
func BenchmarkCodeUnmarshal(b *testing.B) {
if codeJSON == nil {
b.StopTimer()
codeInit()
b.StartTimer()
}
for i := 0; i < b.N; i++ {
var r codeResponse
if err := Unmarshal(codeJSON, &r); err != nil {
b.Fatal("Unmmarshal:", err)
}
}
b.SetBytes(int64(len(codeJSON)))
}
func BenchmarkCodeUnmarshalReuse(b *testing.B) {
if codeJSON == nil {
b.StopTimer()
codeInit()
b.StartTimer()
}
var r codeResponse
for i := 0; i < b.N; i++ {
if err := Unmarshal(codeJSON, &r); err != nil {
b.Fatal("Unmmarshal:", err)
}
}
}
func BenchmarkUnmarshalString(b *testing.B) {
data := []byte(`"hello, world"`)
var s string
for i := 0; i < b.N; i++ {
if err := Unmarshal(data, &s); err != nil {
b.Fatal("Unmarshal:", err)
}
}
}
func BenchmarkUnmarshalFloat64(b *testing.B) {
var f float64
data := []byte(`3.14`)
for i := 0; i < b.N; i++ {
if err := Unmarshal(data, &f); err != nil {
b.Fatal("Unmarshal:", err)
}
}
}
func BenchmarkUnmarshalInt64(b *testing.B) {
var x int64
data := []byte(`3`)
for i := 0; i < b.N; i++ {
if err := Unmarshal(data, &x); err != nil {
b.Fatal("Unmarshal:", err)
}
}
}
func BenchmarkIssue10335(b *testing.B) {
b.ReportAllocs()
var s struct{}
j := []byte(`{"a":{ }}`)
for n := 0; n < b.N; n++ {
if err := Unmarshal(j, &s); err != nil {
b.Fatal(err)
}
}
}
golang-github-go-jose-go-jose-4.0.5/json/decode.go 0000664 0000000 0000000 00000072757 14761622347 0021732 0 ustar 00root root 0000000 0000000 // Copyright 2010 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.
// Represents JSON data structure using native Go types: booleans, floats,
// strings, arrays, and maps.
package json
import (
"bytes"
"encoding"
"encoding/base64"
"errors"
"fmt"
"math"
"reflect"
"runtime"
"strconv"
"unicode"
"unicode/utf16"
"unicode/utf8"
)
// Unmarshal parses the JSON-encoded data and stores the result
// in the value pointed to by v.
//
// Unmarshal uses the inverse of the encodings that
// Marshal uses, allocating maps, slices, and pointers as necessary,
// with the following additional rules:
//
// To unmarshal JSON into a pointer, Unmarshal first handles the case of
// the JSON being the JSON literal null. In that case, Unmarshal sets
// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into
// the value pointed at by the pointer. If the pointer is nil, Unmarshal
// allocates a new value for it to point to.
//
// To unmarshal JSON into a struct, Unmarshal matches incoming object
// keys to the keys used by Marshal (either the struct field name or its tag),
// preferring an exact match but also accepting a case-insensitive match.
// Unmarshal will only set exported fields of the struct.
//
// To unmarshal JSON into an interface value,
// Unmarshal stores one of these in the interface value:
//
// bool, for JSON booleans
// float64, for JSON numbers
// string, for JSON strings
// []interface{}, for JSON arrays
// map[string]interface{}, for JSON objects
// nil for JSON null
//
// To unmarshal a JSON array into a slice, Unmarshal resets the slice length
// to zero and then appends each element to the slice.
// As a special case, to unmarshal an empty JSON array into a slice,
// Unmarshal replaces the slice with a new empty slice.
//
// To unmarshal a JSON array into a Go array, Unmarshal decodes
// JSON array elements into corresponding Go array elements.
// If the Go array is smaller than the JSON array,
// the additional JSON array elements are discarded.
// If the JSON array is smaller than the Go array,
// the additional Go array elements are set to zero values.
//
// To unmarshal a JSON object into a string-keyed map, Unmarshal first
// establishes a map to use, If the map is nil, Unmarshal allocates a new map.
// Otherwise Unmarshal reuses the existing map, keeping existing entries.
// Unmarshal then stores key-value pairs from the JSON object into the map.
//
// If a JSON value is not appropriate for a given target type,
// or if a JSON number overflows the target type, Unmarshal
// skips that field and completes the unmarshaling as best it can.
// If no more serious errors are encountered, Unmarshal returns
// an UnmarshalTypeError describing the earliest such error.
//
// The JSON null value unmarshals into an interface, map, pointer, or slice
// by setting that Go value to nil. Because null is often used in JSON to mean
// “not present,†unmarshaling a JSON null into any other Go type has no effect
// on the value and produces no error.
//
// When unmarshaling quoted strings, invalid UTF-8 or
// invalid UTF-16 surrogate pairs are not treated as an error.
// Instead, they are replaced by the Unicode replacement
// character U+FFFD.
func Unmarshal(data []byte, v interface{}) error {
// Check for well-formedness.
// Avoids filling out half a data structure
// before discovering a JSON syntax error.
var d decodeState
err := checkValid(data, &d.scan)
if err != nil {
return err
}
d.init(data)
return d.unmarshal(v)
}
// Unmarshaler is the interface implemented by objects
// that can unmarshal a JSON description of themselves.
// The input can be assumed to be a valid encoding of
// a JSON value. UnmarshalJSON must copy the JSON data
// if it wishes to retain the data after returning.
type Unmarshaler interface {
UnmarshalJSON([]byte) error
}
// An UnmarshalTypeError describes a JSON value that was
// not appropriate for a value of a specific Go type.
type UnmarshalTypeError struct {
Value string // description of JSON value - "bool", "array", "number -5"
Type reflect.Type // type of Go value it could not be assigned to
Offset int64 // error occurred after reading Offset bytes
}
func (e *UnmarshalTypeError) Error() string {
return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
}
// An UnmarshalFieldError describes a JSON object key that
// led to an unexported (and therefore unwritable) struct field.
// (No longer used; kept for compatibility.)
type UnmarshalFieldError struct {
Key string
Type reflect.Type
Field reflect.StructField
}
func (e *UnmarshalFieldError) Error() string {
return "json: cannot unmarshal object key " + strconv.Quote(e.Key) + " into unexported field " + e.Field.Name + " of type " + e.Type.String()
}
// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal.
// (The argument to Unmarshal must be a non-nil pointer.)
type InvalidUnmarshalError struct {
Type reflect.Type
}
func (e *InvalidUnmarshalError) Error() string {
if e.Type == nil {
return "json: Unmarshal(nil)"
}
if e.Type.Kind() != reflect.Ptr {
return "json: Unmarshal(non-pointer " + e.Type.String() + ")"
}
return "json: Unmarshal(nil " + e.Type.String() + ")"
}
func (d *decodeState) unmarshal(v interface{}) (err error) {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
panic(r)
}
err = r.(error)
}
}()
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
return &InvalidUnmarshalError{reflect.TypeOf(v)}
}
d.scan.reset()
// We decode rv not rv.Elem because the Unmarshaler interface
// test must be applied at the top level of the value.
d.value(rv)
return d.savedError
}
// A Number represents a JSON number literal.
type Number string
// String returns the literal text of the number.
func (n Number) String() string { return string(n) }
// Float64 returns the number as a float64.
func (n Number) Float64() (float64, error) {
return strconv.ParseFloat(string(n), 64)
}
// Int64 returns the number as an int64.
func (n Number) Int64() (int64, error) {
return strconv.ParseInt(string(n), 10, 64)
}
// isValidNumber reports whether s is a valid JSON number literal.
func isValidNumber(s string) bool {
// This function implements the JSON numbers grammar.
// See https://tools.ietf.org/html/rfc7159#section-6
// and http://json.org/number.gif
if s == "" {
return false
}
// Optional -
if s[0] == '-' {
s = s[1:]
if s == "" {
return false
}
}
// Digits
switch {
default:
return false
case s[0] == '0':
s = s[1:]
case '1' <= s[0] && s[0] <= '9':
s = s[1:]
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
s = s[1:]
}
}
// . followed by 1 or more digits.
if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
s = s[2:]
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
s = s[1:]
}
}
// e or E followed by an optional - or + and
// 1 or more digits.
if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
s = s[1:]
if s[0] == '+' || s[0] == '-' {
s = s[1:]
if s == "" {
return false
}
}
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
s = s[1:]
}
}
// Make sure we are at the end.
return s == ""
}
type NumberUnmarshalType int
const (
// unmarshal a JSON number into an interface{} as a float64
UnmarshalFloat NumberUnmarshalType = iota
// unmarshal a JSON number into an interface{} as a `json.Number`
UnmarshalJSONNumber
// unmarshal a JSON number into an interface{} as a int64
// if value is an integer otherwise float64
UnmarshalIntOrFloat
)
// decodeState represents the state while decoding a JSON value.
type decodeState struct {
data []byte
off int // read offset in data
scan scanner
nextscan scanner // for calls to nextValue
savedError error
numberType NumberUnmarshalType
}
// errPhase is used for errors that should not happen unless
// there is a bug in the JSON decoder or something is editing
// the data slice while the decoder executes.
var errPhase = errors.New("JSON decoder out of sync - data changing underfoot?")
func (d *decodeState) init(data []byte) *decodeState {
d.data = data
d.off = 0
d.savedError = nil
return d
}
// error aborts the decoding by panicking with err.
func (d *decodeState) error(err error) {
panic(err)
}
// saveError saves the first err it is called with,
// for reporting at the end of the unmarshal.
func (d *decodeState) saveError(err error) {
if d.savedError == nil {
d.savedError = err
}
}
// next cuts off and returns the next full JSON value in d.data[d.off:].
// The next value is known to be an object or array, not a literal.
func (d *decodeState) next() []byte {
c := d.data[d.off]
item, rest, err := nextValue(d.data[d.off:], &d.nextscan)
if err != nil {
d.error(err)
}
d.off = len(d.data) - len(rest)
// Our scanner has seen the opening brace/bracket
// and thinks we're still in the middle of the object.
// invent a closing brace/bracket to get it out.
if c == '{' {
d.scan.step(&d.scan, '}')
} else {
d.scan.step(&d.scan, ']')
}
return item
}
// scanWhile processes bytes in d.data[d.off:] until it
// receives a scan code not equal to op.
// It updates d.off and returns the new scan code.
func (d *decodeState) scanWhile(op int) int {
var newOp int
for {
if d.off >= len(d.data) {
newOp = d.scan.eof()
d.off = len(d.data) + 1 // mark processed EOF with len+1
} else {
c := d.data[d.off]
d.off++
newOp = d.scan.step(&d.scan, c)
}
if newOp != op {
break
}
}
return newOp
}
// value decodes a JSON value from d.data[d.off:] into the value.
// it updates d.off to point past the decoded value.
func (d *decodeState) value(v reflect.Value) {
if !v.IsValid() {
_, rest, err := nextValue(d.data[d.off:], &d.nextscan)
if err != nil {
d.error(err)
}
d.off = len(d.data) - len(rest)
// d.scan thinks we're still at the beginning of the item.
// Feed in an empty string - the shortest, simplest value -
// so that it knows we got to the end of the value.
if d.scan.redo {
// rewind.
d.scan.redo = false
d.scan.step = stateBeginValue
}
d.scan.step(&d.scan, '"')
d.scan.step(&d.scan, '"')
n := len(d.scan.parseState)
if n > 0 && d.scan.parseState[n-1] == parseObjectKey {
// d.scan thinks we just read an object key; finish the object
d.scan.step(&d.scan, ':')
d.scan.step(&d.scan, '"')
d.scan.step(&d.scan, '"')
d.scan.step(&d.scan, '}')
}
return
}
switch op := d.scanWhile(scanSkipSpace); op {
default:
d.error(errPhase)
case scanBeginArray:
d.array(v)
case scanBeginObject:
d.object(v)
case scanBeginLiteral:
d.literal(v)
}
}
type unquotedValue struct{}
// valueQuoted is like value but decodes a
// quoted string literal or literal null into an interface value.
// If it finds anything other than a quoted string literal or null,
// valueQuoted returns unquotedValue{}.
func (d *decodeState) valueQuoted() interface{} {
switch op := d.scanWhile(scanSkipSpace); op {
default:
d.error(errPhase)
case scanBeginArray:
d.array(reflect.Value{})
case scanBeginObject:
d.object(reflect.Value{})
case scanBeginLiteral:
switch v := d.literalInterface().(type) {
case nil, string:
return v
}
}
return unquotedValue{}
}
// indirect walks down v allocating pointers as needed,
// until it gets to a non-pointer.
// if it encounters an Unmarshaler, indirect stops and returns that.
// if decodingNull is true, indirect stops at the last pointer so it can be set to nil.
func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) {
// If v is a named type and is addressable,
// start with its address, so that if the type has pointer methods,
// we find them.
if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() {
v = v.Addr()
}
for {
// Load value from interface, but only if the result will be
// usefully addressable.
if v.Kind() == reflect.Interface && !v.IsNil() {
e := v.Elem()
if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
v = e
continue
}
}
if v.Kind() != reflect.Ptr {
break
}
if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() {
break
}
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
if v.Type().NumMethod() > 0 {
if u, ok := v.Interface().(Unmarshaler); ok {
return u, nil, reflect.Value{}
}
if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
return nil, u, reflect.Value{}
}
}
v = v.Elem()
}
return nil, nil, v
}
// array consumes an array from d.data[d.off-1:], decoding into the value v.
// the first byte of the array ('[') has been read already.
func (d *decodeState) array(v reflect.Value) {
// Check for unmarshaler.
u, ut, pv := d.indirect(v, false)
if u != nil {
d.off--
err := u.UnmarshalJSON(d.next())
if err != nil {
d.error(err)
}
return
}
if ut != nil {
d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)})
d.off--
d.next()
return
}
v = pv
// Check type of target.
switch v.Kind() {
case reflect.Interface:
if v.NumMethod() == 0 {
// Decoding into nil interface? Switch to non-reflect code.
v.Set(reflect.ValueOf(d.arrayInterface()))
return
}
// Otherwise it's invalid.
fallthrough
default:
d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)})
d.off--
d.next()
return
case reflect.Array:
case reflect.Slice:
break
}
i := 0
for {
// Look ahead for ] - can only happen on first iteration.
op := d.scanWhile(scanSkipSpace)
if op == scanEndArray {
break
}
// Back up so d.value can have the byte we just read.
d.off--
d.scan.undo(op)
// Get element of array, growing if necessary.
if v.Kind() == reflect.Slice {
// Grow slice if necessary
if i >= v.Cap() {
newcap := v.Cap() + v.Cap()/2
if newcap < 4 {
newcap = 4
}
newv := reflect.MakeSlice(v.Type(), v.Len(), newcap)
reflect.Copy(newv, v)
v.Set(newv)
}
if i >= v.Len() {
v.SetLen(i + 1)
}
}
if i < v.Len() {
// Decode into element.
d.value(v.Index(i))
} else {
// Ran out of fixed array: skip.
d.value(reflect.Value{})
}
i++
// Next token must be , or ].
op = d.scanWhile(scanSkipSpace)
if op == scanEndArray {
break
}
if op != scanArrayValue {
d.error(errPhase)
}
}
if i < v.Len() {
if v.Kind() == reflect.Array {
// Array. Zero the rest.
z := reflect.Zero(v.Type().Elem())
for ; i < v.Len(); i++ {
v.Index(i).Set(z)
}
} else {
v.SetLen(i)
}
}
if i == 0 && v.Kind() == reflect.Slice {
v.Set(reflect.MakeSlice(v.Type(), 0, 0))
}
}
var nullLiteral = []byte("null")
// object consumes an object from d.data[d.off-1:], decoding into the value v.
// the first byte ('{') of the object has been read already.
func (d *decodeState) object(v reflect.Value) {
// Check for unmarshaler.
u, ut, pv := d.indirect(v, false)
if u != nil {
d.off--
err := u.UnmarshalJSON(d.next())
if err != nil {
d.error(err)
}
return
}
if ut != nil {
d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
d.off--
d.next() // skip over { } in input
return
}
v = pv
// Decoding into nil interface? Switch to non-reflect code.
if v.Kind() == reflect.Interface && v.NumMethod() == 0 {
v.Set(reflect.ValueOf(d.objectInterface()))
return
}
// Check type of target: struct or map[string]T
switch v.Kind() {
case reflect.Map:
// map must have string kind
t := v.Type()
if t.Key().Kind() != reflect.String {
d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
d.off--
d.next() // skip over { } in input
return
}
if v.IsNil() {
v.Set(reflect.MakeMap(t))
}
case reflect.Struct:
default:
d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
d.off--
d.next() // skip over { } in input
return
}
var mapElem reflect.Value
keys := map[string]bool{}
for {
// Read opening " of string key or closing }.
op := d.scanWhile(scanSkipSpace)
if op == scanEndObject {
// closing } - can only happen on first iteration.
break
}
if op != scanBeginLiteral {
d.error(errPhase)
}
// Read key.
start := d.off - 1
op = d.scanWhile(scanContinue)
item := d.data[start : d.off-1]
key, ok := unquote(item)
if !ok {
d.error(errPhase)
}
// Check for duplicate keys.
_, ok = keys[key]
if !ok {
keys[key] = true
} else {
d.error(fmt.Errorf("json: duplicate key '%s' in object", key))
}
// Figure out field corresponding to key.
var subv reflect.Value
destring := false // whether the value is wrapped in a string to be decoded first
if v.Kind() == reflect.Map {
elemType := v.Type().Elem()
if !mapElem.IsValid() {
mapElem = reflect.New(elemType).Elem()
} else {
mapElem.Set(reflect.Zero(elemType))
}
subv = mapElem
} else {
var f *field
fields := cachedTypeFields(v.Type())
for i := range fields {
ff := &fields[i]
if bytes.Equal(ff.nameBytes, []byte(key)) {
f = ff
break
}
}
if f != nil {
subv = v
destring = f.quoted
for _, i := range f.index {
if subv.Kind() == reflect.Ptr {
if subv.IsNil() {
subv.Set(reflect.New(subv.Type().Elem()))
}
subv = subv.Elem()
}
subv = subv.Field(i)
}
}
}
// Read : before value.
if op == scanSkipSpace {
op = d.scanWhile(scanSkipSpace)
}
if op != scanObjectKey {
d.error(errPhase)
}
// Read value.
if destring {
switch qv := d.valueQuoted().(type) {
case nil:
d.literalStore(nullLiteral, subv, false)
case string:
d.literalStore([]byte(qv), subv, true)
default:
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into %v", subv.Type()))
}
} else {
d.value(subv)
}
// Write value back to map;
// if using struct, subv points into struct already.
if v.Kind() == reflect.Map {
kv := reflect.ValueOf(key).Convert(v.Type().Key())
v.SetMapIndex(kv, subv)
}
// Next token must be , or }.
op = d.scanWhile(scanSkipSpace)
if op == scanEndObject {
break
}
if op != scanObjectValue {
d.error(errPhase)
}
}
}
// literal consumes a literal from d.data[d.off-1:], decoding into the value v.
// The first byte of the literal has been read already
// (that's how the caller knows it's a literal).
func (d *decodeState) literal(v reflect.Value) {
// All bytes inside literal return scanContinue op code.
start := d.off - 1
op := d.scanWhile(scanContinue)
// Scan read one byte too far; back up.
d.off--
d.scan.undo(op)
d.literalStore(d.data[start:d.off], v, false)
}
// convertNumber converts the number literal s to a float64, int64 or a Number
// depending on d.numberDecodeType.
func (d *decodeState) convertNumber(s string) (interface{}, error) {
switch d.numberType {
case UnmarshalJSONNumber:
return Number(s), nil
case UnmarshalIntOrFloat:
v, err := strconv.ParseInt(s, 10, 64)
if err == nil {
return v, nil
}
// tries to parse integer number in scientific notation
f, err := strconv.ParseFloat(s, 64)
if err != nil {
return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)}
}
// if it has no decimal value use int64
if fi, fd := math.Modf(f); fd == 0.0 {
return int64(fi), nil
}
return f, nil
default:
f, err := strconv.ParseFloat(s, 64)
if err != nil {
return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)}
}
return f, nil
}
}
var numberType = reflect.TypeOf(Number(""))
// literalStore decodes a literal stored in item into v.
//
// fromQuoted indicates whether this literal came from unwrapping a
// string from the ",string" struct tag option. this is used only to
// produce more helpful error messages.
func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) {
// Check for unmarshaler.
if len(item) == 0 {
//Empty string given
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
return
}
wantptr := item[0] == 'n' // null
u, ut, pv := d.indirect(v, wantptr)
if u != nil {
err := u.UnmarshalJSON(item)
if err != nil {
d.error(err)
}
return
}
if ut != nil {
if item[0] != '"' {
if fromQuoted {
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
} else {
d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
}
return
}
s, ok := unquoteBytes(item)
if !ok {
if fromQuoted {
d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
} else {
d.error(errPhase)
}
}
err := ut.UnmarshalText(s)
if err != nil {
d.error(err)
}
return
}
v = pv
switch c := item[0]; c {
case 'n': // null
switch v.Kind() {
case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
v.Set(reflect.Zero(v.Type()))
// otherwise, ignore null for primitives/string
}
case 't', 'f': // true, false
value := c == 't'
switch v.Kind() {
default:
if fromQuoted {
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
} else {
d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)})
}
case reflect.Bool:
v.SetBool(value)
case reflect.Interface:
if v.NumMethod() == 0 {
v.Set(reflect.ValueOf(value))
} else {
d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)})
}
}
case '"': // string
s, ok := unquoteBytes(item)
if !ok {
if fromQuoted {
d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
} else {
d.error(errPhase)
}
}
switch v.Kind() {
default:
d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
case reflect.Slice:
if v.Type().Elem().Kind() != reflect.Uint8 {
d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
break
}
b := make([]byte, base64.StdEncoding.DecodedLen(len(s)))
n, err := base64.StdEncoding.Decode(b, s)
if err != nil {
d.saveError(err)
break
}
v.SetBytes(b[:n])
case reflect.String:
v.SetString(string(s))
case reflect.Interface:
if v.NumMethod() == 0 {
v.Set(reflect.ValueOf(string(s)))
} else {
d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
}
}
default: // number
if c != '-' && (c < '0' || c > '9') {
if fromQuoted {
d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
} else {
d.error(errPhase)
}
}
s := string(item)
switch v.Kind() {
default:
if v.Kind() == reflect.String && v.Type() == numberType {
v.SetString(s)
if !isValidNumber(s) {
d.error(fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", item))
}
break
}
if fromQuoted {
d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
} else {
d.error(&UnmarshalTypeError{"number", v.Type(), int64(d.off)})
}
case reflect.Interface:
n, err := d.convertNumber(s)
if err != nil {
d.saveError(err)
break
}
if v.NumMethod() != 0 {
d.saveError(&UnmarshalTypeError{"number", v.Type(), int64(d.off)})
break
}
v.Set(reflect.ValueOf(n))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n, err := strconv.ParseInt(s, 10, 64)
if err != nil || v.OverflowInt(n) {
d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
break
}
v.SetInt(n)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n, err := strconv.ParseUint(s, 10, 64)
if err != nil || v.OverflowUint(n) {
d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
break
}
v.SetUint(n)
case reflect.Float32, reflect.Float64:
n, err := strconv.ParseFloat(s, v.Type().Bits())
if err != nil || v.OverflowFloat(n) {
d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
break
}
v.SetFloat(n)
}
}
}
// The xxxInterface routines build up a value to be stored
// in an empty interface. They are not strictly necessary,
// but they avoid the weight of reflection in this common case.
// valueInterface is like value but returns interface{}
func (d *decodeState) valueInterface() interface{} {
switch d.scanWhile(scanSkipSpace) {
default:
d.error(errPhase)
panic("unreachable")
case scanBeginArray:
return d.arrayInterface()
case scanBeginObject:
return d.objectInterface()
case scanBeginLiteral:
return d.literalInterface()
}
}
// arrayInterface is like array but returns []interface{}.
func (d *decodeState) arrayInterface() []interface{} {
var v = make([]interface{}, 0)
for {
// Look ahead for ] - can only happen on first iteration.
op := d.scanWhile(scanSkipSpace)
if op == scanEndArray {
break
}
// Back up so d.value can have the byte we just read.
d.off--
d.scan.undo(op)
v = append(v, d.valueInterface())
// Next token must be , or ].
op = d.scanWhile(scanSkipSpace)
if op == scanEndArray {
break
}
if op != scanArrayValue {
d.error(errPhase)
}
}
return v
}
// objectInterface is like object but returns map[string]interface{}.
func (d *decodeState) objectInterface() map[string]interface{} {
m := make(map[string]interface{})
keys := map[string]bool{}
for {
// Read opening " of string key or closing }.
op := d.scanWhile(scanSkipSpace)
if op == scanEndObject {
// closing } - can only happen on first iteration.
break
}
if op != scanBeginLiteral {
d.error(errPhase)
}
// Read string key.
start := d.off - 1
op = d.scanWhile(scanContinue)
item := d.data[start : d.off-1]
key, ok := unquote(item)
if !ok {
d.error(errPhase)
}
// Check for duplicate keys.
_, ok = keys[key]
if !ok {
keys[key] = true
} else {
d.error(fmt.Errorf("json: duplicate key '%s' in object", key))
}
// Read : before value.
if op == scanSkipSpace {
op = d.scanWhile(scanSkipSpace)
}
if op != scanObjectKey {
d.error(errPhase)
}
// Read value.
m[key] = d.valueInterface()
// Next token must be , or }.
op = d.scanWhile(scanSkipSpace)
if op == scanEndObject {
break
}
if op != scanObjectValue {
d.error(errPhase)
}
}
return m
}
// literalInterface is like literal but returns an interface value.
func (d *decodeState) literalInterface() interface{} {
// All bytes inside literal return scanContinue op code.
start := d.off - 1
op := d.scanWhile(scanContinue)
// Scan read one byte too far; back up.
d.off--
d.scan.undo(op)
item := d.data[start:d.off]
switch c := item[0]; c {
case 'n': // null
return nil
case 't', 'f': // true, false
return c == 't'
case '"': // string
s, ok := unquote(item)
if !ok {
d.error(errPhase)
}
return s
default: // number
if c != '-' && (c < '0' || c > '9') {
d.error(errPhase)
}
n, err := d.convertNumber(string(item))
if err != nil {
d.saveError(err)
}
return n
}
}
// getu4 decodes \uXXXX from the beginning of s, returning the hex value,
// or it returns -1.
func getu4(s []byte) rune {
if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
return -1
}
r, err := strconv.ParseUint(string(s[2:6]), 16, 64)
if err != nil {
return -1
}
return rune(r)
}
// unquote converts a quoted JSON string literal s into an actual string t.
// The rules are different than for Go, so cannot use strconv.Unquote.
func unquote(s []byte) (t string, ok bool) {
s, ok = unquoteBytes(s)
t = string(s)
return
}
func unquoteBytes(s []byte) (t []byte, ok bool) {
if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
return
}
s = s[1 : len(s)-1]
// Check for unusual characters. If there are none,
// then no unquoting is needed, so return a slice of the
// original bytes.
r := 0
for r < len(s) {
c := s[r]
if c == '\\' || c == '"' || c < ' ' {
break
}
if c < utf8.RuneSelf {
r++
continue
}
rr, size := utf8.DecodeRune(s[r:])
if rr == utf8.RuneError && size == 1 {
break
}
r += size
}
if r == len(s) {
return s, true
}
b := make([]byte, len(s)+2*utf8.UTFMax)
w := copy(b, s[0:r])
for r < len(s) {
// Out of room? Can only happen if s is full of
// malformed UTF-8 and we're replacing each
// byte with RuneError.
if w >= len(b)-2*utf8.UTFMax {
nb := make([]byte, (len(b)+utf8.UTFMax)*2)
copy(nb, b[0:w])
b = nb
}
switch c := s[r]; {
case c == '\\':
r++
if r >= len(s) {
return
}
switch s[r] {
default:
return
case '"', '\\', '/', '\'':
b[w] = s[r]
r++
w++
case 'b':
b[w] = '\b'
r++
w++
case 'f':
b[w] = '\f'
r++
w++
case 'n':
b[w] = '\n'
r++
w++
case 'r':
b[w] = '\r'
r++
w++
case 't':
b[w] = '\t'
r++
w++
case 'u':
r--
rr := getu4(s[r:])
if rr < 0 {
return
}
r += 6
if utf16.IsSurrogate(rr) {
rr1 := getu4(s[r:])
if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar {
// A valid pair; consume.
r += 6
w += utf8.EncodeRune(b[w:], dec)
break
}
// Invalid surrogate; fall back to replacement rune.
rr = unicode.ReplacementChar
}
w += utf8.EncodeRune(b[w:], rr)
}
// Quote, control characters are invalid.
case c == '"', c < ' ':
return
// ASCII
case c < utf8.RuneSelf:
b[w] = c
r++
w++
// Coerce to well-formed UTF-8.
default:
rr, size := utf8.DecodeRune(s[r:])
r += size
w += utf8.EncodeRune(b[w:], rr)
}
}
return b[0:w], true
}
golang-github-go-jose-go-jose-4.0.5/json/decode_test.go 0000664 0000000 0000000 00000103107 14761622347 0022751 0 ustar 00root root 0000000 0000000 // Copyright 2010 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 json
import (
"bytes"
"encoding"
"fmt"
"image"
"net"
"reflect"
"strings"
"testing"
"time"
)
type T struct {
X string
Y int
Z int `json:"-"`
}
type U struct {
Alphabet string `json:"alpha"`
}
type V struct {
F1 interface{}
F2 int32
F3 Number
}
// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and
// without UseNumber
var ifaceNumAsFloat64 = map[string]interface{}{
"k1": float64(1),
"k2": "s",
"k3": []interface{}{float64(1), float64(2.0), float64(3e-3)},
"k4": map[string]interface{}{"kk1": "s", "kk2": float64(2)},
}
var ifaceNumAsNumber = map[string]interface{}{
"k1": Number("1"),
"k2": "s",
"k3": []interface{}{Number("1"), Number("2.0"), Number("3e-3")},
"k4": map[string]interface{}{"kk1": "s", "kk2": Number("2")},
}
type tx struct {
x int
}
// A type that can unmarshal itself.
type unmarshaler struct {
T bool
}
func (u *unmarshaler) UnmarshalJSON(b []byte) error {
*u = unmarshaler{true} // All we need to see that UnmarshalJSON is called.
return nil
}
type ustruct struct {
M unmarshaler
}
type unmarshalerText struct {
T bool
}
// needed for re-marshaling tests
func (u *unmarshalerText) MarshalText() ([]byte, error) {
return []byte(""), nil
}
func (u *unmarshalerText) UnmarshalText(b []byte) error {
*u = unmarshalerText{true} // All we need to see that UnmarshalText is called.
return nil
}
var _ encoding.TextUnmarshaler = (*unmarshalerText)(nil)
type ustructText struct {
M unmarshalerText
}
var (
um0, um1 unmarshaler // target2 of unmarshaling
ump = &um1
umtrue = unmarshaler{true}
umslice = []unmarshaler{{true}}
umslicep = new([]unmarshaler)
umstruct = ustruct{unmarshaler{true}}
um0T, um1T unmarshalerText // target2 of unmarshaling
umpT = &um1T
umtrueT = unmarshalerText{true}
umsliceT = []unmarshalerText{{true}}
umslicepT = new([]unmarshalerText)
umstructT = ustructText{unmarshalerText{true}}
)
// Test data structures for anonymous fields.
type Point struct {
Z int
}
type Top struct {
Level0 int
Embed0
*Embed0a
*Embed0b `json:"e,omitempty"` // treated as named
Embed0c `json:"-"` // ignored
Loop
Embed0p // has Point with X, Y, used
Embed0q // has Point with Z, used
embed // contains exported field
}
type Embed0 struct {
Level1a int // overridden by Embed0a's Level1a with json tag
Level1b int // used because Embed0a's Level1b is renamed
Level1c int // used because Embed0a's Level1c is ignored
Level1d int // annihilated by Embed0a's Level1d
Level1e int `json:"x"` // annihilated by Embed0a.Level1e
}
type Embed0a struct {
Level1a int `json:"Level1a,omitempty"`
Level1b int `json:"LEVEL1B,omitempty"`
Level1c int `json:"-"`
Level1d int // annihilated by Embed0's Level1d
Level1f int `json:"x"` // annihilated by Embed0's Level1e
}
type Embed0b Embed0
type Embed0c Embed0
type Embed0p struct {
image.Point
}
type Embed0q struct {
Point
}
type embed struct {
Q int
}
type Loop struct {
Loop1 int `json:",omitempty"`
Loop2 int `json:",omitempty"`
*Loop
}
// From reflect test:
// The X in S6 and S7 annihilate, but they also block the X in S8.S9.
type S5 struct {
S6
S7
S8
}
type S6 struct {
X int
}
type S7 S6
type S8 struct {
S9
}
type S9 struct {
X int
Y int
}
// From reflect test:
// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9.
type S10 struct {
S11
S12
S13
}
type S11 struct {
S6
}
type S12 struct {
S6
}
type S13 struct {
S8
}
type unmarshalTest struct {
in string
ptr interface{}
out interface{}
err error
numberType NumberUnmarshalType
}
type XYZ struct {
X interface{}
Y interface{}
Z interface{}
}
func sliceAddr(x []int) *[]int { return &x }
func mapAddr(x map[string]int) *map[string]int { return &x }
var unmarshalTests = []unmarshalTest{
// basic types
{in: `true`, ptr: new(bool), out: true},
{in: `1`, ptr: new(int), out: 1},
{in: `1.2`, ptr: new(float64), out: 1.2},
{in: `-5`, ptr: new(int16), out: int16(-5)},
{in: `2`, ptr: new(Number), out: Number("2"), numberType: UnmarshalJSONNumber},
{in: `2`, ptr: new(Number), out: Number("2")},
{in: `2`, ptr: new(interface{}), out: float64(2.0)},
{in: `2`, ptr: new(interface{}), out: Number("2"), numberType: UnmarshalJSONNumber},
{in: `2`, ptr: new(interface{}), out: int64(2), numberType: UnmarshalIntOrFloat},
{in: `2.1`, ptr: new(interface{}), out: float64(2.1), numberType: UnmarshalIntOrFloat},
{in: `1.5e2`, ptr: new(interface{}), out: int64(150), numberType: UnmarshalIntOrFloat},
{in: `9223372036854775807`, ptr: new(interface{}), out: int64(9223372036854775807), numberType: UnmarshalIntOrFloat},
{in: `9007199254740992.000000`, ptr: new(interface{}), out: int64(9007199254740992), numberType: UnmarshalIntOrFloat},
{in: `"a\u1234"`, ptr: new(string), out: "a\u1234"},
{in: `"http:\/\/"`, ptr: new(string), out: "http://"},
{in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"},
{in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"},
{in: "null", ptr: new(interface{}), out: nil},
{in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf(""), 7}},
{in: `{"x": 1}`, ptr: new(tx), out: tx{}},
{in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
{in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, numberType: UnmarshalJSONNumber},
{in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsFloat64},
{in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsNumber, numberType: UnmarshalJSONNumber},
// raw values with whitespace
{in: "\n true ", ptr: new(bool), out: true},
{in: "\t 1 ", ptr: new(int), out: 1},
{in: "\r 1.2 ", ptr: new(float64), out: 1.2},
{in: "\t -5 \n", ptr: new(int16), out: int16(-5)},
{in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"},
// Z has a "-" tag.
{in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}},
{in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}},
{in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}},
{in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}},
// syntax errors
{in: `{"X": "foo", "Y"}`, err: &SyntaxError{"invalid character '}' after object key", 17}},
{in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}},
{in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, numberType: UnmarshalJSONNumber},
// raw value errors
{in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
{in: " 42 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 5}},
{in: "\x01 true", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
{in: " false \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 8}},
{in: "\x01 1.2", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
{in: " 3.4 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 6}},
{in: "\x01 \"string\"", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
{in: " \"string\" \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 11}},
// array tests
{in: `[1, 2, 3]`, ptr: new([3]int), out: [3]int{1, 2, 3}},
{in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}},
{in: `[1, 2, 3]`, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}},
// empty array to interface test
{in: `[]`, ptr: new([]interface{}), out: []interface{}{}},
{in: `null`, ptr: new([]interface{}), out: []interface{}(nil)},
{in: `{"T":[]}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": []interface{}{}}},
{in: `{"T":null}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": interface{}(nil)}},
// composite tests
{in: allValueIndent, ptr: new(All), out: allValue},
{in: allValueCompact, ptr: new(All), out: allValue},
{in: allValueIndent, ptr: new(*All), out: &allValue},
{in: allValueCompact, ptr: new(*All), out: &allValue},
{in: pallValueIndent, ptr: new(All), out: pallValue},
{in: pallValueCompact, ptr: new(All), out: pallValue},
{in: pallValueIndent, ptr: new(*All), out: &pallValue},
{in: pallValueCompact, ptr: new(*All), out: &pallValue},
// unmarshal interface test
{in: `{"T":false}`, ptr: &um0, out: umtrue}, // use "false" so test will fail if custom unmarshaler is not called
{in: `{"T":false}`, ptr: &ump, out: &umtrue},
{in: `[{"T":false}]`, ptr: &umslice, out: umslice},
{in: `[{"T":false}]`, ptr: &umslicep, out: &umslice},
{in: `{"M":{"T":false}}`, ptr: &umstruct, out: umstruct},
// UnmarshalText interface test
{in: `"X"`, ptr: &um0T, out: umtrueT}, // use "false" so test will fail if custom unmarshaler is not called
{in: `"X"`, ptr: &umpT, out: &umtrueT},
{in: `["X"]`, ptr: &umsliceT, out: umsliceT},
{in: `["X"]`, ptr: &umslicepT, out: &umsliceT},
{in: `{"M":"X"}`, ptr: &umstructT, out: umstructT},
// Overwriting of data.
// This is different from package xml, but it's what we've always done.
// Now documented and tested.
{in: `[2]`, ptr: sliceAddr([]int{1}), out: []int{2}},
{in: `{"key": 2}`, ptr: mapAddr(map[string]int{"old": 0, "key": 1}), out: map[string]int{"key": 2}},
{
in: `{
"Level0": 1,
"Level1b": 2,
"Level1c": 3,
"x": 4,
"Level1a": 5,
"LEVEL1B": 6,
"e": {
"Level1a": 8,
"Level1b": 9,
"Level1c": 10,
"Level1d": 11,
"x": 12
},
"Loop1": 13,
"Loop2": 14,
"X": 15,
"Y": 16,
"Z": 17,
"Q": 18
}`,
ptr: new(Top),
out: Top{
Level0: 1,
Embed0: Embed0{
Level1b: 2,
Level1c: 3,
},
Embed0a: &Embed0a{
Level1a: 5,
Level1b: 6,
},
Embed0b: &Embed0b{
Level1a: 8,
Level1b: 9,
Level1c: 10,
Level1d: 11,
Level1e: 12,
},
Loop: Loop{
Loop1: 13,
Loop2: 14,
},
Embed0p: Embed0p{
Point: image.Point{X: 15, Y: 16},
},
Embed0q: Embed0q{
Point: Point{Z: 17},
},
embed: embed{
Q: 18,
},
},
},
{
in: `{"X": 1,"Y":2}`,
ptr: new(S5),
out: S5{S8: S8{S9: S9{Y: 2}}},
},
{
in: `{"X": 1,"Y":2}`,
ptr: new(S10),
out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}},
},
// invalid UTF-8 is coerced to valid UTF-8.
{
in: "\"hello\xffworld\"",
ptr: new(string),
out: "hello\ufffdworld",
},
{
in: "\"hello\xc2\xc2world\"",
ptr: new(string),
out: "hello\ufffd\ufffdworld",
},
{
in: "\"hello\xc2\xffworld\"",
ptr: new(string),
out: "hello\ufffd\ufffdworld",
},
{
in: "\"hello\\ud800world\"",
ptr: new(string),
out: "hello\ufffdworld",
},
{
in: "\"hello\\ud800\\ud800world\"",
ptr: new(string),
out: "hello\ufffd\ufffdworld",
},
{
in: "\"hello\\ud800\\ud800world\"",
ptr: new(string),
out: "hello\ufffd\ufffdworld",
},
{
in: "\"hello\xed\xa0\x80\xed\xb0\x80world\"",
ptr: new(string),
out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld",
},
// issue 8305
{
in: `{"2009-11-10T23:00:00Z": "hello world"}`,
ptr: &map[time.Time]string{},
err: &UnmarshalTypeError{"object", reflect.TypeOf(map[time.Time]string{}), 1},
},
}
func TestMarshal(t *testing.T) {
b, err := Marshal(allValue)
if err != nil {
t.Fatalf("Marshal allValue: %v", err)
}
if string(b) != allValueCompact {
t.Errorf("Marshal allValueCompact")
diff(t, b, []byte(allValueCompact))
return
}
b, err = Marshal(pallValue)
if err != nil {
t.Fatalf("Marshal pallValue: %v", err)
}
if string(b) != pallValueCompact {
t.Errorf("Marshal pallValueCompact")
diff(t, b, []byte(pallValueCompact))
return
}
}
var badUTF8 = []struct {
in, out string
}{
{"hello\xffworld", `"hello\ufffdworld"`},
{"", `""`},
{"\xff", `"\ufffd"`},
{"\xff\xff", `"\ufffd\ufffd"`},
{"a\xffb", `"a\ufffdb"`},
{"\xe6\x97\xa5\xe6\x9c\xac\xff\xaa\x9e", `"日本\ufffd\ufffd\ufffd"`},
}
func TestMarshalBadUTF8(t *testing.T) {
for _, tt := range badUTF8 {
b, err := Marshal(tt.in)
if string(b) != tt.out || err != nil {
t.Errorf("Marshal(%q) = %#q, %v, want %#q, nil", tt.in, b, err, tt.out)
}
}
}
func TestMarshalNumberZeroVal(t *testing.T) {
var n Number
out, err := Marshal(n)
if err != nil {
t.Fatal(err)
}
outStr := string(out)
if outStr != "0" {
t.Fatalf("Invalid zero val for Number: %q", outStr)
}
}
func TestMarshalEmbeds(t *testing.T) {
top := &Top{
Level0: 1,
Embed0: Embed0{
Level1b: 2,
Level1c: 3,
},
Embed0a: &Embed0a{
Level1a: 5,
Level1b: 6,
},
Embed0b: &Embed0b{
Level1a: 8,
Level1b: 9,
Level1c: 10,
Level1d: 11,
Level1e: 12,
},
Loop: Loop{
Loop1: 13,
Loop2: 14,
},
Embed0p: Embed0p{
Point: image.Point{X: 15, Y: 16},
},
Embed0q: Embed0q{
Point: Point{Z: 17},
},
embed: embed{
Q: 18,
},
}
b, err := Marshal(top)
if err != nil {
t.Fatal(err)
}
want := "{\"Level0\":1,\"Level1b\":2,\"Level1c\":3,\"Level1a\":5,\"LEVEL1B\":6,\"e\":{\"Level1a\":8,\"Level1b\":9,\"Level1c\":10,\"Level1d\":11,\"x\":12},\"Loop1\":13,\"Loop2\":14,\"X\":15,\"Y\":16,\"Z\":17,\"Q\":18}"
if string(b) != want {
t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want)
}
}
func TestUnmarshal(t *testing.T) {
for i, tt := range unmarshalTests {
var scan scanner
in := []byte(tt.in)
if err := checkValid(in, &scan); err != nil {
if !reflect.DeepEqual(err, tt.err) {
t.Errorf("#%d: checkValid: %#v", i, err)
continue
}
}
if tt.ptr == nil {
continue
}
// v = new(right-type)
v := reflect.New(reflect.TypeOf(tt.ptr).Elem())
dec := NewDecoder(bytes.NewReader(in))
dec.SetNumberType(tt.numberType)
if err := dec.Decode(v.Interface()); !reflect.DeepEqual(err, tt.err) {
t.Errorf("#%d: %v, want %v", i, err, tt.err)
continue
} else if err != nil {
continue
}
if !reflect.DeepEqual(v.Elem().Interface(), tt.out) {
t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), tt.out)
data, _ := Marshal(v.Elem().Interface())
println(string(data))
data, _ = Marshal(tt.out)
println(string(data))
continue
}
// Check round trip.
if tt.err == nil {
enc, err := Marshal(v.Interface())
if err != nil {
t.Errorf("#%d: error re-marshaling: %v", i, err)
continue
}
vv := reflect.New(reflect.TypeOf(tt.ptr).Elem())
dec = NewDecoder(bytes.NewReader(enc))
dec.SetNumberType(tt.numberType)
if err := dec.Decode(vv.Interface()); err != nil {
t.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err)
continue
}
if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) {
t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface())
t.Errorf(" In: %q", strings.Map(noSpace, string(in)))
t.Errorf("Marshal: %q", strings.Map(noSpace, string(enc)))
continue
}
}
}
}
func TestUnmarshalMarshal(t *testing.T) {
initBig()
var v interface{}
if err := Unmarshal(jsonBig, &v); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
b, err := Marshal(v)
if err != nil {
t.Fatalf("Marshal: %v", err)
}
if !bytes.Equal(jsonBig, b) {
t.Errorf("Marshal jsonBig")
diff(t, b, jsonBig)
return
}
}
var numberTests = []struct {
in string
i int64
intErr string
f float64
floatErr string
}{
{in: "-1.23e1", intErr: "strconv.ParseInt: parsing \"-1.23e1\": invalid syntax", f: -1.23e1},
{in: "-12", i: -12, f: -12.0},
{in: "1e1000", intErr: "strconv.ParseInt: parsing \"1e1000\": invalid syntax", floatErr: "strconv.ParseFloat: parsing \"1e1000\": value out of range"},
}
// Independent of Decode, basic coverage of the accessors in Number
func TestNumberAccessors(t *testing.T) {
for _, tt := range numberTests {
n := Number(tt.in)
if s := n.String(); s != tt.in {
t.Errorf("Number(%q).String() is %q", tt.in, s)
}
if i, err := n.Int64(); err == nil && tt.intErr == "" && i != tt.i {
t.Errorf("Number(%q).Int64() is %d", tt.in, i)
} else if (err == nil && tt.intErr != "") || (err != nil && err.Error() != tt.intErr) {
t.Errorf("Number(%q).Int64() wanted error %q but got: %v", tt.in, tt.intErr, err)
}
if f, err := n.Float64(); err == nil && tt.floatErr == "" && f != tt.f {
t.Errorf("Number(%q).Float64() is %g", tt.in, f)
} else if (err == nil && tt.floatErr != "") || (err != nil && err.Error() != tt.floatErr) {
t.Errorf("Number(%q).Float64() wanted error %q but got: %v", tt.in, tt.floatErr, err)
}
}
}
func TestLargeByteSlice(t *testing.T) {
s0 := make([]byte, 2000)
for i := range s0 {
s0[i] = byte(i)
}
b, err := Marshal(s0)
if err != nil {
t.Fatalf("Marshal: %v", err)
}
var s1 []byte
if err := Unmarshal(b, &s1); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if !bytes.Equal(s0, s1) {
t.Errorf("Marshal large byte slice")
diff(t, s0, s1)
}
}
type Xint struct {
X int
}
func TestUnmarshalInterface(t *testing.T) {
var xint Xint
var i interface{} = &xint
if err := Unmarshal([]byte(`{"X":1}`), &i); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if xint.X != 1 {
t.Fatalf("Did not write to xint")
}
}
func TestUnmarshalPtrPtr(t *testing.T) {
var xint Xint
pxint := &xint
if err := Unmarshal([]byte(`{"X":1}`), &pxint); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if xint.X != 1 {
t.Fatalf("Did not write to xint")
}
}
func TestEscape(t *testing.T) {
const input = `"foobar"` + " [\u2028 \u2029]"
const expected = `"\"foobar\"\u003chtml\u003e [\u2028 \u2029]"`
b, err := Marshal(input)
if err != nil {
t.Fatalf("Marshal error: %v", err)
}
if s := string(b); s != expected {
t.Errorf("Encoding of [%s]:\n got [%s]\nwant [%s]", input, s, expected)
}
}
// WrongString is a struct that's misusing the ,string modifier.
type WrongString struct {
Message string `json:"result,string"`
}
type wrongStringTest struct {
in, err string
}
var wrongStringTests = []wrongStringTest{
{`{"result":"x"}`, `json: invalid use of ,string struct tag, trying to unmarshal "x" into string`},
{`{"result":"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "foo" into string`},
{`{"result":"123"}`, `json: invalid use of ,string struct tag, trying to unmarshal "123" into string`},
{`{"result":123}`, `json: invalid use of ,string struct tag, trying to unmarshal unquoted value into string`},
}
// If people misuse the ,string modifier, the error message should be
// helpful, telling the user that they're doing it wrong.
func TestErrorMessageFromMisusedString(t *testing.T) {
for n, tt := range wrongStringTests {
r := strings.NewReader(tt.in)
var s WrongString
err := NewDecoder(r).Decode(&s)
got := fmt.Sprintf("%v", err)
if got != tt.err {
t.Errorf("%d. got err = %q, want %q", n, got, tt.err)
}
}
}
func noSpace(c rune) rune {
if isSpace(byte(c)) { //only used for ascii
return -1
}
return c
}
type All struct {
Bool bool
Int int
Int8 int8
Int16 int16
Int32 int32
Int64 int64
Uint uint
Uint8 uint8
Uint16 uint16
Uint32 uint32
Uint64 uint64
Uintptr uintptr
Float32 float32
Float64 float64
Foo string `json:"bar"`
Foo2 string `json:"bar2,dummyopt"`
IntStr int64 `json:",string"`
PBool *bool
PInt *int
PInt8 *int8
PInt16 *int16
PInt32 *int32
PInt64 *int64
PUint *uint
PUint8 *uint8
PUint16 *uint16
PUint32 *uint32
PUint64 *uint64
PUintptr *uintptr
PFloat32 *float32
PFloat64 *float64
String string
PString *string
Map map[string]Small
MapP map[string]*Small
PMap *map[string]Small
PMapP *map[string]*Small
EmptyMap map[string]Small
NilMap map[string]Small
Slice []Small
SliceP []*Small
PSlice *[]Small
PSliceP *[]*Small
EmptySlice []Small
NilSlice []Small
StringSlice []string
ByteSlice []byte
Small Small
PSmall *Small
PPSmall **Small
Interface interface{}
PInterface *interface{}
unexported int
}
type Small struct {
Tag string
}
var allValue = All{
Bool: true,
Int: 2,
Int8: 3,
Int16: 4,
Int32: 5,
Int64: 6,
Uint: 7,
Uint8: 8,
Uint16: 9,
Uint32: 10,
Uint64: 11,
Uintptr: 12,
Float32: 14.1,
Float64: 15.1,
Foo: "foo",
Foo2: "foo2",
IntStr: 42,
String: "16",
Map: map[string]Small{
"17": {Tag: "tag17"},
"18": {Tag: "tag18"},
},
MapP: map[string]*Small{
"19": {Tag: "tag19"},
"20": nil,
},
EmptyMap: map[string]Small{},
Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}},
SliceP: []*Small{{Tag: "tag22"}, nil, {Tag: "tag23"}},
EmptySlice: []Small{},
StringSlice: []string{"str24", "str25", "str26"},
ByteSlice: []byte{27, 28, 29},
Small: Small{Tag: "tag30"},
PSmall: &Small{Tag: "tag31"},
Interface: 5.2,
}
var pallValue = All{
PBool: &allValue.Bool,
PInt: &allValue.Int,
PInt8: &allValue.Int8,
PInt16: &allValue.Int16,
PInt32: &allValue.Int32,
PInt64: &allValue.Int64,
PUint: &allValue.Uint,
PUint8: &allValue.Uint8,
PUint16: &allValue.Uint16,
PUint32: &allValue.Uint32,
PUint64: &allValue.Uint64,
PUintptr: &allValue.Uintptr,
PFloat32: &allValue.Float32,
PFloat64: &allValue.Float64,
PString: &allValue.String,
PMap: &allValue.Map,
PMapP: &allValue.MapP,
PSlice: &allValue.Slice,
PSliceP: &allValue.SliceP,
PPSmall: &allValue.PSmall,
PInterface: &allValue.Interface,
}
var allValueIndent = `{
"Bool": true,
"Int": 2,
"Int8": 3,
"Int16": 4,
"Int32": 5,
"Int64": 6,
"Uint": 7,
"Uint8": 8,
"Uint16": 9,
"Uint32": 10,
"Uint64": 11,
"Uintptr": 12,
"Float32": 14.1,
"Float64": 15.1,
"bar": "foo",
"bar2": "foo2",
"IntStr": "42",
"PBool": null,
"PInt": null,
"PInt8": null,
"PInt16": null,
"PInt32": null,
"PInt64": null,
"PUint": null,
"PUint8": null,
"PUint16": null,
"PUint32": null,
"PUint64": null,
"PUintptr": null,
"PFloat32": null,
"PFloat64": null,
"String": "16",
"PString": null,
"Map": {
"17": {
"Tag": "tag17"
},
"18": {
"Tag": "tag18"
}
},
"MapP": {
"19": {
"Tag": "tag19"
},
"20": null
},
"PMap": null,
"PMapP": null,
"EmptyMap": {},
"NilMap": null,
"Slice": [
{
"Tag": "tag20"
},
{
"Tag": "tag21"
}
],
"SliceP": [
{
"Tag": "tag22"
},
null,
{
"Tag": "tag23"
}
],
"PSlice": null,
"PSliceP": null,
"EmptySlice": [],
"NilSlice": null,
"StringSlice": [
"str24",
"str25",
"str26"
],
"ByteSlice": "Gxwd",
"Small": {
"Tag": "tag30"
},
"PSmall": {
"Tag": "tag31"
},
"PPSmall": null,
"Interface": 5.2,
"PInterface": null
}`
var allValueCompact = strings.Map(noSpace, allValueIndent)
var pallValueIndent = `{
"Bool": false,
"Int": 0,
"Int8": 0,
"Int16": 0,
"Int32": 0,
"Int64": 0,
"Uint": 0,
"Uint8": 0,
"Uint16": 0,
"Uint32": 0,
"Uint64": 0,
"Uintptr": 0,
"Float32": 0,
"Float64": 0,
"bar": "",
"bar2": "",
"IntStr": "0",
"PBool": true,
"PInt": 2,
"PInt8": 3,
"PInt16": 4,
"PInt32": 5,
"PInt64": 6,
"PUint": 7,
"PUint8": 8,
"PUint16": 9,
"PUint32": 10,
"PUint64": 11,
"PUintptr": 12,
"PFloat32": 14.1,
"PFloat64": 15.1,
"String": "",
"PString": "16",
"Map": null,
"MapP": null,
"PMap": {
"17": {
"Tag": "tag17"
},
"18": {
"Tag": "tag18"
}
},
"PMapP": {
"19": {
"Tag": "tag19"
},
"20": null
},
"EmptyMap": null,
"NilMap": null,
"Slice": null,
"SliceP": null,
"PSlice": [
{
"Tag": "tag20"
},
{
"Tag": "tag21"
}
],
"PSliceP": [
{
"Tag": "tag22"
},
null,
{
"Tag": "tag23"
}
],
"EmptySlice": null,
"NilSlice": null,
"StringSlice": null,
"ByteSlice": null,
"Small": {
"Tag": ""
},
"PSmall": null,
"PPSmall": {
"Tag": "tag31"
},
"Interface": null,
"PInterface": 5.2
}`
var pallValueCompact = strings.Map(noSpace, pallValueIndent)
func TestRefUnmarshal(t *testing.T) {
type S struct {
// Ref is defined in encode_test.go.
R0 Ref
R1 *Ref
R2 RefText
R3 *RefText
}
want := S{
R0: 12,
R1: new(Ref),
R2: 13,
R3: new(RefText),
}
*want.R1 = 12
*want.R3 = 13
var got S
if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref","R2":"ref","R3":"ref"}`), &got); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if !reflect.DeepEqual(got, want) {
t.Errorf("got %+v, want %+v", got, want)
}
}
// Test that the empty string doesn't panic decoding when ,string is specified
// Issue 3450
func TestEmptyString(t *testing.T) {
type T2 struct {
Number1 int `json:",string"`
Number2 int `json:",string"`
}
data := `{"Number1":"1", "Number2":""}`
dec := NewDecoder(strings.NewReader(data))
var t2 T2
err := dec.Decode(&t2)
if err == nil {
t.Fatal("Decode: did not return error")
}
if t2.Number1 != 1 {
t.Fatal("Decode: did not set Number1")
}
}
// Test that a null for ,string is not replaced with the previous quoted string (issue 7046).
// It should also not be an error (issue 2540, issue 8587).
func TestNullString(t *testing.T) {
type T struct {
A int `json:",string"`
B int `json:",string"`
C *int `json:",string"`
}
data := []byte(`{"A": "1", "B": null, "C": null}`)
var s T
s.B = 1
s.C = new(int)
*s.C = 2
err := Unmarshal(data, &s)
if err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if s.B != 1 || s.C != nil {
t.Fatalf("after Unmarshal, s.B=%d, s.C=%p, want 1, nil", s.B, s.C)
}
}
func intp(x int) *int {
p := new(int)
*p = x
return p
}
func intpp(x *int) **int {
pp := new(*int)
*pp = x
return pp
}
var interfaceSetTests = []struct {
pre interface{}
json string
post interface{}
}{
{"foo", `"bar"`, "bar"},
{"foo", `2`, 2.0},
{"foo", `true`, true},
{"foo", `null`, nil},
{nil, `null`, nil},
{new(int), `null`, nil},
{(*int)(nil), `null`, nil},
{new(*int), `null`, new(*int)},
{(**int)(nil), `null`, nil},
{intp(1), `null`, nil},
{intpp(nil), `null`, intpp(nil)},
{intpp(intp(1)), `null`, intpp(nil)},
}
func TestInterfaceSet(t *testing.T) {
for _, tt := range interfaceSetTests {
b := struct{ X interface{} }{tt.pre}
blob := `{"X":` + tt.json + `}`
if err := Unmarshal([]byte(blob), &b); err != nil {
t.Errorf("Unmarshal %#q: %v", blob, err)
continue
}
if !reflect.DeepEqual(b.X, tt.post) {
t.Errorf("Unmarshal %#q into %#v: X=%#v, want %#v", blob, tt.pre, b.X, tt.post)
}
}
}
// JSON null values should be ignored for primitives and string values instead of resulting in an error.
// Issue 2540
func TestUnmarshalNulls(t *testing.T) {
jsonData := []byte(`{
"Bool" : null,
"Int" : null,
"Int8" : null,
"Int16" : null,
"Int32" : null,
"Int64" : null,
"Uint" : null,
"Uint8" : null,
"Uint16" : null,
"Uint32" : null,
"Uint64" : null,
"Float32" : null,
"Float64" : null,
"String" : null}`)
nulls := All{
Bool: true,
Int: 2,
Int8: 3,
Int16: 4,
Int32: 5,
Int64: 6,
Uint: 7,
Uint8: 8,
Uint16: 9,
Uint32: 10,
Uint64: 11,
Float32: 12.1,
Float64: 13.1,
String: "14"}
err := Unmarshal(jsonData, &nulls)
if err != nil {
t.Errorf("Unmarshal of null values failed: %v", err)
}
if !nulls.Bool || nulls.Int != 2 || nulls.Int8 != 3 || nulls.Int16 != 4 || nulls.Int32 != 5 || nulls.Int64 != 6 ||
nulls.Uint != 7 || nulls.Uint8 != 8 || nulls.Uint16 != 9 || nulls.Uint32 != 10 || nulls.Uint64 != 11 ||
nulls.Float32 != 12.1 || nulls.Float64 != 13.1 || nulls.String != "14" {
t.Errorf("Unmarshal of null values affected primitives")
}
}
func TestStringKind(t *testing.T) {
type stringKind string
var m1, m2 map[stringKind]int
m1 = map[stringKind]int{
"foo": 42,
}
data, err := Marshal(m1)
if err != nil {
t.Errorf("Unexpected error marshaling: %v", err)
}
err = Unmarshal(data, &m2)
if err != nil {
t.Errorf("Unexpected error unmarshaling: %v", err)
}
if !reflect.DeepEqual(m1, m2) {
t.Error("Items should be equal after encoding and then decoding")
}
}
// Custom types with []byte as underlying type could not be marshalled
// and then unmarshalled.
// Issue 8962.
func TestByteKind(t *testing.T) {
type byteKind []byte
a := byteKind("hello")
data, err := Marshal(a)
if err != nil {
t.Error(err)
}
var b byteKind
err = Unmarshal(data, &b)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(a, b) {
t.Errorf("expected %v == %v", a, b)
}
}
// The fix for issue 8962 introduced a regression.
// Issue 12921.
func TestSliceOfCustomByte(t *testing.T) {
type Uint8 uint8
a := []Uint8("hello")
data, err := Marshal(a)
if err != nil {
t.Fatal(err)
}
var b []Uint8
err = Unmarshal(data, &b)
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(a, b) {
t.Fatalf("expected %v == %v", a, b)
}
}
var decodeTypeErrorTests = []struct {
dest interface{}
src string
}{
{new(string), `{"user": "name"}`}, // issue 4628.
{new(error), `{}`}, // issue 4222
{new(error), `[]`},
{new(error), `""`},
{new(error), `123`},
{new(error), `true`},
}
func TestUnmarshalTypeError(t *testing.T) {
for _, item := range decodeTypeErrorTests {
err := Unmarshal([]byte(item.src), item.dest)
if _, ok := err.(*UnmarshalTypeError); !ok {
t.Errorf("expected type error for Unmarshal(%q, type %T): got %T",
item.src, item.dest, err)
}
}
}
var unmarshalSyntaxTests = []string{
"tru",
"fals",
"nul",
"123e",
`"hello`,
`[1,2,3`,
`{"key":1`,
`{"key":1,`,
}
func TestUnmarshalSyntax(t *testing.T) {
var x interface{}
for _, src := range unmarshalSyntaxTests {
err := Unmarshal([]byte(src), &x)
if _, ok := err.(*SyntaxError); !ok {
t.Errorf("expected syntax error for Unmarshal(%q): got %T", src, err)
}
}
}
// Test handling of unexported fields that should be ignored.
// Issue 4660
type unexportedFields struct {
Name string
m map[string]interface{} `json:"-"`
m2 map[string]interface{} `json:"abcd"`
}
func TestUnmarshalUnexported(t *testing.T) {
input := `{"Name": "Bob", "m": {"x": 123}, "m2": {"y": 456}, "abcd": {"z": 789}}`
want := &unexportedFields{Name: "Bob"}
out := &unexportedFields{}
err := Unmarshal([]byte(input), out)
if err != nil {
t.Errorf("got error %v, expected nil", err)
}
if !reflect.DeepEqual(out, want) {
t.Errorf("got %q, want %q", out, want)
}
}
// Time3339 is a time.Time which encodes to and from JSON
// as an RFC 3339 time in UTC.
type Time3339 time.Time
func (t *Time3339) UnmarshalJSON(b []byte) error {
if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time", b)
}
tm, err := time.Parse(time.RFC3339, string(b[1:len(b)-1]))
if err != nil {
return err
}
*t = Time3339(tm)
return nil
}
func TestUnmarshalJSONLiteralError(t *testing.T) {
var t3 Time3339
err := Unmarshal([]byte(`"0000-00-00T00:00:00Z"`), &t3)
if err == nil {
t.Fatalf("expected error; got time %v", time.Time(t3))
}
if !strings.Contains(err.Error(), "range") {
t.Errorf("got err = %v; want out of range error", err)
}
}
// Test that extra object elements in an array do not result in a
// "data changing underfoot" error.
// Issue 3717
func TestSkipArrayObjects(t *testing.T) {
json := `[{}]`
var dest [0]interface{}
err := Unmarshal([]byte(json), &dest)
if err != nil {
t.Errorf("got error %q, want nil", err)
}
}
// Test semantics of pre-filled struct fields and pre-filled map fields.
// Issue 4900.
func TestPrefilled(t *testing.T) {
ptrToMap := func(m map[string]interface{}) *map[string]interface{} { return &m }
// Values here change, cannot reuse table across runs.
var prefillTests = []struct {
in string
ptr interface{}
out interface{}
}{
{
in: `{"X": 1, "Y": 2}`,
ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1.5},
out: &XYZ{X: float64(1), Y: float64(2), Z: 1.5},
},
{
in: `{"X": 1, "Y": 2}`,
ptr: ptrToMap(map[string]interface{}{"X": float32(3), "Y": int16(4), "Z": 1.5}),
out: ptrToMap(map[string]interface{}{"X": float64(1), "Y": float64(2), "Z": 1.5}),
},
}
for _, tt := range prefillTests {
ptrstr := fmt.Sprintf("%v", tt.ptr)
err := Unmarshal([]byte(tt.in), tt.ptr) // tt.ptr edited here
if err != nil {
t.Errorf("Unmarshal: %v", err)
}
if !reflect.DeepEqual(tt.ptr, tt.out) {
t.Errorf("Unmarshal(%#q, %s): have %v, want %v", tt.in, ptrstr, tt.ptr, tt.out)
}
}
}
var invalidUnmarshalTests = []struct {
v interface{}
want string
}{
{nil, "json: Unmarshal(nil)"},
{struct{}{}, "json: Unmarshal(non-pointer struct {})"},
{(*int)(nil), "json: Unmarshal(nil *int)"},
}
func TestInvalidUnmarshal(t *testing.T) {
buf := []byte(`{"a":"1"}`)
for _, tt := range invalidUnmarshalTests {
err := Unmarshal(buf, tt.v)
if err == nil {
t.Errorf("Unmarshal expecting error, got nil")
continue
}
if got := err.Error(); got != tt.want {
t.Errorf("Unmarshal = %q; want %q", got, tt.want)
}
}
}
var invalidUnmarshalTextTests = []struct {
v interface{}
want string
}{
{nil, "json: Unmarshal(nil)"},
{struct{}{}, "json: Unmarshal(non-pointer struct {})"},
{(*int)(nil), "json: Unmarshal(nil *int)"},
{new(net.IP), "json: cannot unmarshal string into Go value of type *net.IP"},
}
func TestInvalidUnmarshalText(t *testing.T) {
buf := []byte(`123`)
for _, tt := range invalidUnmarshalTextTests {
err := Unmarshal(buf, tt.v)
if err == nil {
t.Errorf("Unmarshal expecting error, got nil")
continue
}
if got := err.Error(); got != tt.want {
t.Errorf("Unmarshal = %q; want %q", got, tt.want)
}
}
}
// Test that string option is ignored for invalid types.
// Issue 9812.
func TestInvalidStringOption(t *testing.T) {
num := 0
item := struct {
T time.Time `json:",string"`
M map[string]string `json:",string"`
S []string `json:",string"`
A [1]string `json:",string"`
I interface{} `json:",string"`
P *int `json:",string"`
}{M: make(map[string]string), S: make([]string, 0), I: num, P: &num}
data, err := Marshal(item)
if err != nil {
t.Fatalf("Marshal: %v", err)
}
err = Unmarshal(data, &item)
if err != nil {
t.Fatalf("Unmarshal: %v", err)
}
}
golang-github-go-jose-go-jose-4.0.5/json/encode.go 0000664 0000000 0000000 00000075435 14761622347 0021740 0 ustar 00root root 0000000 0000000 // Copyright 2010 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 json implements encoding and decoding of JSON objects as defined in
// RFC 4627. The mapping between JSON objects and Go values is described
// in the documentation for the Marshal and Unmarshal functions.
//
// See "JSON and Go" for an introduction to this package:
// https://golang.org/doc/articles/json_and_go.html
package json
import (
"bytes"
"encoding"
"encoding/base64"
"fmt"
"math"
"reflect"
"runtime"
"sort"
"strconv"
"strings"
"sync"
"unicode"
"unicode/utf8"
)
// Marshal returns the JSON encoding of v.
//
// Marshal traverses the value v recursively.
// If an encountered value implements the Marshaler interface
// and is not a nil pointer, Marshal calls its MarshalJSON method
// to produce JSON. If no MarshalJSON method is present but the
// value implements encoding.TextMarshaler instead, Marshal calls
// its MarshalText method.
// The nil pointer exception is not strictly necessary
// but mimics a similar, necessary exception in the behavior of
// UnmarshalJSON.
//
// Otherwise, Marshal uses the following type-dependent default encodings:
//
// Boolean values encode as JSON booleans.
//
// Floating point, integer, and Number values encode as JSON numbers.
//
// String values encode as JSON strings coerced to valid UTF-8,
// replacing invalid bytes with the Unicode replacement rune.
// The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e"
// to keep some browsers from misinterpreting JSON output as HTML.
// Ampersand "&" is also escaped to "\u0026" for the same reason.
//
// Array and slice values encode as JSON arrays, except that
// []byte encodes as a base64-encoded string, and a nil slice
// encodes as the null JSON object.
//
// Struct values encode as JSON objects. Each exported struct field
// becomes a member of the object unless
// - the field's tag is "-", or
// - the field is empty and its tag specifies the "omitempty" option.
//
// The empty values are false, 0, any
// nil pointer or interface value, and any array, slice, map, or string of
// length zero. The object's default key string is the struct field name
// but can be specified in the struct field's tag value. The "json" key in
// the struct field's tag value is the key name, followed by an optional comma
// and options. Examples:
//
// // Field is ignored by this package.
// Field int `json:"-"`
//
// // Field appears in JSON as key "myName".
// Field int `json:"myName"`
//
// // Field appears in JSON as key "myName" and
// // the field is omitted from the object if its value is empty,
// // as defined above.
// Field int `json:"myName,omitempty"`
//
// // Field appears in JSON as key "Field" (the default), but
// // the field is skipped if empty.
// // Note the leading comma.
// Field int `json:",omitempty"`
//
// The "string" option signals that a field is stored as JSON inside a
// JSON-encoded string. It applies only to fields of string, floating point,
// integer, or boolean types. This extra level of encoding is sometimes used
// when communicating with JavaScript programs:
//
// Int64String int64 `json:",string"`
//
// The key name will be used if it's a non-empty string consisting of
// only Unicode letters, digits, dollar signs, percent signs, hyphens,
// underscores and slashes.
//
// Anonymous struct fields are usually marshaled as if their inner exported fields
// were fields in the outer struct, subject to the usual Go visibility rules amended
// as described in the next paragraph.
// An anonymous struct field with a name given in its JSON tag is treated as
// having that name, rather than being anonymous.
// An anonymous struct field of interface type is treated the same as having
// that type as its name, rather than being anonymous.
//
// The Go visibility rules for struct fields are amended for JSON when
// deciding which field to marshal or unmarshal. If there are
// multiple fields at the same level, and that level is the least
// nested (and would therefore be the nesting level selected by the
// usual Go rules), the following extra rules apply:
//
// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered,
// even if there are multiple untagged fields that would otherwise conflict.
// 2) If there is exactly one field (tagged or not according to the first rule), that is selected.
// 3) Otherwise there are multiple fields, and all are ignored; no error occurs.
//
// Handling of anonymous struct fields is new in Go 1.1.
// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of
// an anonymous struct field in both current and earlier versions, give the field
// a JSON tag of "-".
//
// Map values encode as JSON objects.
// The map's key type must be string; the map keys are used as JSON object
// keys, subject to the UTF-8 coercion described for string values above.
//
// Pointer values encode as the value pointed to.
// A nil pointer encodes as the null JSON object.
//
// Interface values encode as the value contained in the interface.
// A nil interface value encodes as the null JSON object.
//
// Channel, complex, and function values cannot be encoded in JSON.
// Attempting to encode such a value causes Marshal to return
// an UnsupportedTypeError.
//
// JSON cannot represent cyclic data structures and Marshal does not
// handle them. Passing cyclic structures to Marshal will result in
// an infinite recursion.
func Marshal(v interface{}) ([]byte, error) {
e := &encodeState{}
err := e.marshal(v)
if err != nil {
return nil, err
}
return e.Bytes(), nil
}
// MarshalIndent is like Marshal but applies Indent to format the output.
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
b, err := Marshal(v)
if err != nil {
return nil, err
}
var buf bytes.Buffer
err = Indent(&buf, b, prefix, indent)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029
// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029
// so that the JSON will be safe to embed inside HTML