pax_global_header00006660000000000000000000000064146455656620014535gustar00rootroot0000000000000052 comment=d9b2efbb065f058fc442f53aebd5053d88fbaff9 golang-github-go-jose-go-jose-4.0.3/000077500000000000000000000000001464556566200171725ustar00rootroot00000000000000golang-github-go-jose-go-jose-4.0.3/.github/000077500000000000000000000000001464556566200205325ustar00rootroot00000000000000golang-github-go-jose-go-jose-4.0.3/.github/dependabot.yml000066400000000000000000000003171464556566200233630ustar00rootroot00000000000000version: 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.3/.github/workflows/000077500000000000000000000000001464556566200225675ustar00rootroot00000000000000golang-github-go-jose-go-jose-4.0.3/.github/workflows/cram.yml000066400000000000000000000007371464556566200242430ustar00rootroot00000000000000name: 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.3/.github/workflows/go.yml000066400000000000000000000023321464556566200237170ustar00rootroot00000000000000name: 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.3/.gitignore000066400000000000000000000000431464556566200211570ustar00rootroot00000000000000jose-util/jose-util jose-util.t.errgolang-github-go-jose-go-jose-4.0.3/.golangci.yml000066400000000000000000000017371464556566200215660ustar00rootroot00000000000000# 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.3/.travis.yml000066400000000000000000000020071464556566200213020ustar00rootroot00000000000000language: 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.3/CHANGELOG.md000066400000000000000000000064261464556566200210130ustar00rootroot00000000000000# 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.3/CONTRIBUTING.md000066400000000000000000000012411464556566200214210ustar00rootroot00000000000000# 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`. Before your code can be accepted into the project you must also sign the Individual Contributor License Agreement. We use [cla-assistant.io][1] and you will be prompted to sign once a pull request is opened. [1]: https://cla-assistant.io/ golang-github-go-jose-go-jose-4.0.3/LICENSE000066400000000000000000000261361464556566200202070ustar00rootroot00000000000000 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.3/README.md000066400000000000000000000133431464556566200204550ustar00rootroot00000000000000# Go JOSE [![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v4) [![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4/jwt.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v4/jwt) [![license](https://img.shields.io/badge/license-apache_2.0-blue.svg?style=flat)](https://raw.githubusercontent.com/go-jose/go-jose/master/LICENSE) [![test](https://img.shields.io/github/checks-status/go-jose/go-jose/v4)](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. **Disclaimer**: This library contains encryption software that is subject to the U.S. Export Administration Regulations. You may not export, re-export, transfer or download this code or any part of it in violation of any United States law, directive or regulation. In particular this software may not be exported or re-exported in any form or on any media to Iran, North Sudan, Syria, Cuba, or North Korea, or to denied persons or entities mentioned on any US maintained blocked list. ## 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 [![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v4) [![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v4/jwt.svg)](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/v4/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.3/SECURITY.md000066400000000000000000000012521464556566200207630ustar00rootroot00000000000000# 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.3/asymmetric.go000066400000000000000000000373541464556566200217120ustar00rootroot00000000000000/*- * 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.3/asymmetric_test.go000066400000000000000000000231041464556566200227350ustar00rootroot00000000000000/*- * 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.3/cipher/000077500000000000000000000000001464556566200204445ustar00rootroot00000000000000golang-github-go-jose-go-jose-4.0.3/cipher/cbc_hmac.go000066400000000000000000000124351464556566200225170ustar00rootroot00000000000000/*- * 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.3/cipher/cbc_hmac_test.go000066400000000000000000000353051464556566200235570ustar00rootroot00000000000000/*- * 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.3/cipher/concat_kdf.go000066400000000000000000000035161464556566200230730ustar00rootroot00000000000000/*- * 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.3/cipher/concat_kdf_test.go000066400000000000000000000073571464556566200241410ustar00rootroot00000000000000/*- * 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.3/cipher/ecdh_es.go000066400000000000000000000054161464556566200223730ustar00rootroot00000000000000/*- * 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.3/cipher/ecdh_es_test.go000066400000000000000000000061721464556566200234320ustar00rootroot00000000000000/*- * 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.3/cipher/key_wrap.go000066400000000000000000000050331464556566200226150ustar00rootroot00000000000000/*- * 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.3/cipher/key_wrap_test.go000066400000000000000000000076761464556566200236730ustar00rootroot00000000000000/*- * 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.3/crypter.go000066400000000000000000000410541464556566200212150ustar00rootroot00000000000000/*- * 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.3/crypter_test.go000066400000000000000000001261241464556566200222560ustar00rootroot00000000000000/*- * 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.3/cryptosigner/000077500000000000000000000000001464556566200217225ustar00rootroot00000000000000golang-github-go-jose-go-jose-4.0.3/cryptosigner/cryptosigner.go000066400000000000000000000067411464556566200250110ustar00rootroot00000000000000/*- * 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.3/cryptosigner/cryptosigner_test.go000066400000000000000000000130531464556566200260420ustar00rootroot00000000000000/*- * 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.3/doc.go000066400000000000000000000020021464556566200202600ustar00rootroot00000000000000/*- * 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.3/doc_test.go000066400000000000000000000144501464556566200213310ustar00rootroot00000000000000/*- * 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.3/encoding.go000066400000000000000000000127541464556566200213200ustar00rootroot00000000000000/*- * 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.3/encoding_test.go000066400000000000000000000077551464556566200223640ustar00rootroot00000000000000/*- * 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.3/go.mod000066400000000000000000000004601464556566200203000ustar00rootroot00000000000000module github.com/go-jose/go-jose/v4 go 1.21 require ( github.com/google/go-cmp v0.6.0 github.com/stretchr/testify v1.9.0 golang.org/x/crypto v0.25.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.3/go.sum000066400000000000000000000022671464556566200203340ustar00rootroot00000000000000github.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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= 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.3/jose-util/000077500000000000000000000000001464556566200211055ustar00rootroot00000000000000golang-github-go-jose-go-jose-4.0.3/jose-util/README.md000066400000000000000000000072111464556566200223650ustar00rootroot00000000000000# 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.3/jose-util/crypto.go000066400000000000000000000122341464556566200227560ustar00rootroot00000000000000/*- * 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.3/jose-util/format.go000066400000000000000000000031061464556566200227240ustar00rootroot00000000000000/* * 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.3/jose-util/generate.go000066400000000000000000000067341464556566200232400ustar00rootroot00000000000000/*- * 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.3/jose-util/generator/000077500000000000000000000000001464556566200230735ustar00rootroot00000000000000golang-github-go-jose-go-jose-4.0.3/jose-util/generator/encoding.go000066400000000000000000000023421464556566200252110ustar00rootroot00000000000000/*- * 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.3/jose-util/generator/generate.go000066400000000000000000000066611464556566200252250ustar00rootroot00000000000000/*- * 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.3/jose-util/generator/utils.go000066400000000000000000000043251464556566200245660ustar00rootroot00000000000000/*- * 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.3/jose-util/io.go000066400000000000000000000035731464556566200220530ustar00rootroot00000000000000/*- * 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.3/jose-util/jose-util.t000066400000000000000000000112321464556566200232040ustar00rootroot00000000000000This 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.3/jose-util/main.go000066400000000000000000000043151464556566200223630ustar00rootroot00000000000000/*- * 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.3/json/000077500000000000000000000000001464556566200201435ustar00rootroot00000000000000golang-github-go-jose-go-jose-4.0.3/json/LICENSE000066400000000000000000000027071464556566200211560ustar00rootroot00000000000000Copyright (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.3/json/README.md000066400000000000000000000011701464556566200214210ustar00rootroot00000000000000# 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.3/json/bench_test.go000066400000000000000000000110071464556566200226070ustar00rootroot00000000000000// 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.3/json/decode.go000066400000000000000000000727571464556566200217370ustar00rootroot00000000000000// 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.3/json/decode_test.go000066400000000000000000001031071464556566200227560ustar00rootroot00000000000000// 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.3/json/encode.go000066400000000000000000000754351464556566200217450ustar00rootroot00000000000000// 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