Update square/go-jose to v2.3.1 (#4299)

Also excises the existing bad padding metrics code, adds a special error for when we encounter badly padded keys, and adds a test for the new special error.

Fixes #4070 and fixes #3964.
This commit is contained in:
Roland Bracewell Shoemaker 2019-06-26 16:27:50 -07:00 committed by GitHub
parent 1c8cfb3cba
commit 14d34e9075
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 601 additions and 197 deletions

2
go.mod
View File

@ -45,6 +45,6 @@ require (
google.golang.org/genproto v0.0.0-20190415143225-d1146b9035b9 // indirect
google.golang.org/grpc v1.20.0
gopkg.in/go-gorp/gorp.v2 v2.0.0-20180410155428-6032c66e0f5f
gopkg.in/square/go-jose.v2 v2.1.4
gopkg.in/square/go-jose.v2 v2.3.1
gopkg.in/yaml.v2 v2.2.2
)

2
go.sum
View File

@ -169,6 +169,8 @@ gopkg.in/go-gorp/gorp.v2 v2.0.0-20180410155428-6032c66e0f5f h1:zundYZrPliLrlQV98
gopkg.in/go-gorp/gorp.v2 v2.0.0-20180410155428-6032c66e0f5f/go.mod h1:eJwu1bWCXesk9aw26U78PFtctx3Y8haXGmL7x3VJlrw=
gopkg.in/square/go-jose.v2 v2.1.4 h1:2F80z1AzUrkIpMSw+HIa5WJBzPTFEzQiTTPLES3hbfI=
gopkg.in/square/go-jose.v2 v2.1.4/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

77
vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go generated vendored Normal file
View File

@ -0,0 +1,77 @@
// Copyright 2012 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 pbkdf2 implements the key derivation function PBKDF2 as defined in RFC
2898 / PKCS #5 v2.0.
A key derivation function is useful when encrypting data based on a password
or any other not-fully-random data. It uses a pseudorandom function to derive
a secure encryption key based on the password.
While v2.0 of the standard defines only one pseudorandom function to use,
HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved
Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To
choose, you can pass the `New` functions from the different SHA packages to
pbkdf2.Key.
*/
package pbkdf2 // import "golang.org/x/crypto/pbkdf2"
import (
"crypto/hmac"
"hash"
)
// Key derives a key from the password, salt and iteration count, returning a
// []byte of length keylen that can be used as cryptographic key. The key is
// derived based on the method described as PBKDF2 with the HMAC variant using
// the supplied hash function.
//
// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
// doing:
//
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
//
// Remember to get a good random salt. At least 8 bytes is recommended by the
// RFC.
//
// Using a higher iteration count will increase the cost of an exhaustive
// search but will also make derivation proportionally slower.
func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
prf := hmac.New(h, password)
hashLen := prf.Size()
numBlocks := (keyLen + hashLen - 1) / hashLen
var buf [4]byte
dk := make([]byte, 0, numBlocks*hashLen)
U := make([]byte, hashLen)
for block := 1; block <= numBlocks; block++ {
// N.B.: || means concatenation, ^ means XOR
// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
// U_1 = PRF(password, salt || uint(i))
prf.Reset()
prf.Write(salt)
buf[0] = byte(block >> 24)
buf[1] = byte(block >> 16)
buf[2] = byte(block >> 8)
buf[3] = byte(block)
prf.Write(buf[:4])
dk = prf.Sum(dk)
T := dk[len(dk)-hashLen:]
copy(U, T)
// U_n = PRF(password, U_(n-1))
for n := 2; n <= iter; n++ {
prf.Reset()
prf.Write(U)
U = U[:0]
U = prf.Sum(U)
for x := range U {
T[x] ^= U[x]
}
}
}
return dk[:keyLen]
}

View File

@ -8,13 +8,11 @@ matrix:
- go: tip
go:
- '1.5.x'
- '1.6.x'
- '1.7.x'
- '1.8.x'
- '1.9.x'
- '1.10.x'
- tip
- '1.11.x'
go_import_path: gopkg.in/square/go-jose.v2

View File

@ -6,5 +6,5 @@ can make. We therefore encourage reporting security issues with the code
contained in this repository.
If you believe you have discovered a security vulnerability, please follow the
guidelines at <https://hackerone.com/square-open-source>.
guidelines at <https://bugcrowd.com/squareopensource>.

View File

@ -3,8 +3,8 @@
[![godoc](http://img.shields.io/badge/godoc-version_1-blue.svg?style=flat)](https://godoc.org/gopkg.in/square/go-jose.v1)
[![godoc](http://img.shields.io/badge/godoc-version_2-blue.svg?style=flat)](https://godoc.org/gopkg.in/square/go-jose.v2)
[![license](http://img.shields.io/badge/license-apache_2.0-blue.svg?style=flat)](https://raw.githubusercontent.com/square/go-jose/master/LICENSE)
[![build](https://travis-ci.org/square/go-jose.svg?branch=master)](https://travis-ci.org/square/go-jose)
[![coverage](https://coveralls.io/repos/github/square/go-jose/badge.svg?branch=master)](https://coveralls.io/r/square/go-jose)
[![build](https://travis-ci.org/square/go-jose.svg?branch=v2)](https://travis-ci.org/square/go-jose)
[![coverage](https://coveralls.io/repos/github/square/go-jose/badge.svg?branch=v2)](https://coveralls.io/r/square/go-jose)
Package jose aims to provide an implementation of the Javascript Object Signing
and Encryption set of standards. This includes support for JSON Web Encryption,
@ -40,24 +40,17 @@ libraries in other languages.
We use [gopkg.in](https://gopkg.in) for versioning.
[Version 1](https://gopkg.in/square/go-jose.v1) is the old stable version:
import "gopkg.in/square/go-jose.v1"
[Version 2](https://gopkg.in/square/go-jose.v2) is for new development:
[Version 2](https://gopkg.in/square/go-jose.v2)
([branch](https://github.com/square/go-jose/tree/v2),
[doc](https://godoc.org/gopkg.in/square/go-jose.v2)) is the current version:
import "gopkg.in/square/go-jose.v2"
The interface for [go-jose.v1](https://gopkg.in/square/go-jose.v1) will remain
backwards compatible. No new feature development will take place on the `v1` branch,
however bug fixes and security fixes will be backported.
The interface for [go-jose.v2](https://gopkg.in/square/go-jose.v2) is mostly
stable, but we suggest pinning to a particular revision for now as we still reserve
the right to make changes. New feature development happens on this branch.
New in [go-jose.v2](https://gopkg.in/square/go-jose.v2) is a
[jwt](https://godoc.org/gopkg.in/square/go-jose.v2/jwt) sub-package
The old `v1` branch ([go-jose.v1](https://gopkg.in/square/go-jose.v1)) will
still receive backported bug fixes and security fixes, but otherwise
development is frozen. All new feature development takes place on the `v2`
branch. Version 2 also contains additional sub-packages such as the
[jwt](https://godoc.org/gopkg.in/square/go-jose.v2/jwt) implementation
contributed by [@shaxbee](https://github.com/shaxbee).
### Supported algorithms
@ -84,7 +77,9 @@ standard where possible. The Godoc reference has a list of constants.
RSASSA-PSS | PS256, PS384, PS512
HMAC | HS256, HS384, HS512
ECDSA | ES256, ES384, ES512
Ed25519 | EdDSA
Ed25519 | EdDSA<sup>2</sup>
<sup>2. Only available in version 2 of the package</sup>
Content encryption | Algorithm identifier(s)
:------------------------- | :------------------------------
@ -106,8 +101,11 @@ allows attaching a key id.
:------------------------- | -------------------------------
RSA | *[rsa.PublicKey](http://golang.org/pkg/crypto/rsa/#PublicKey), *[rsa.PrivateKey](http://golang.org/pkg/crypto/rsa/#PrivateKey)
ECDH, ECDSA | *[ecdsa.PublicKey](http://golang.org/pkg/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](http://golang.org/pkg/crypto/ecdsa/#PrivateKey)
EdDSA<sup>1</sup> | [ed25519.PublicKey](https://godoc.org/golang.org/x/crypto/ed25519#PublicKey), [ed25519.PrivateKey](https://godoc.org/golang.org/x/crypto/ed25519#PrivateKey)
AES, HMAC | []byte
<sup>1. Only available in version 2 of the package</sup>
## Examples
[![godoc](http://img.shields.io/badge/godoc-version_1-blue.svg?style=flat)](https://godoc.org/gopkg.in/square/go-jose.v1)

View File

@ -104,9 +104,9 @@ func newRSASigner(sigAlg SignatureAlgorithm, privateKey *rsa.PrivateKey) (recipi
return recipientSigInfo{
sigAlg: sigAlg,
publicKey: &JSONWebKey{
Key: &privateKey.PublicKey,
},
publicKey: staticPublicKey(&JSONWebKey{
Key: privateKey.Public(),
}),
signer: &rsaDecrypterSigner{
privateKey: privateKey,
},
@ -123,9 +123,9 @@ func newEd25519Signer(sigAlg SignatureAlgorithm, privateKey ed25519.PrivateKey)
}
return recipientSigInfo{
sigAlg: sigAlg,
publicKey: &JSONWebKey{
publicKey: staticPublicKey(&JSONWebKey{
Key: privateKey.Public(),
},
}),
signer: &edDecrypterSigner{
privateKey: privateKey,
},
@ -168,9 +168,9 @@ func newECDSASigner(sigAlg SignatureAlgorithm, privateKey *ecdsa.PrivateKey) (re
return recipientSigInfo{
sigAlg: sigAlg,
publicKey: &JSONWebKey{
Key: &privateKey.PublicKey,
},
publicKey: staticPublicKey(&JSONWebKey{
Key: privateKey.Public(),
}),
signer: &ecDecrypterSigner{
privateKey: privateKey,
},
@ -195,11 +195,11 @@ func (ctx rsaEncrypterVerifier) encryptKey(cek []byte, alg KeyAlgorithm) (recipi
func (ctx rsaEncrypterVerifier) encrypt(cek []byte, alg KeyAlgorithm) ([]byte, error) {
switch alg {
case RSA1_5:
return rsa.EncryptPKCS1v15(randReader, ctx.publicKey, cek)
return rsa.EncryptPKCS1v15(RandReader, ctx.publicKey, cek)
case RSA_OAEP:
return rsa.EncryptOAEP(sha1.New(), randReader, ctx.publicKey, cek, []byte{})
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 rsa.EncryptOAEP(sha256.New(), RandReader, ctx.publicKey, cek, []byte{})
}
return nil, ErrUnsupportedAlgorithm
@ -285,9 +285,9 @@ func (ctx rsaDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm
switch alg {
case RS256, RS384, RS512:
out, err = rsa.SignPKCS1v15(randReader, ctx.privateKey, hash, hashed)
out, err = rsa.SignPKCS1v15(RandReader, ctx.privateKey, hash, hashed)
case PS256, PS384, PS512:
out, err = rsa.SignPSS(randReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{
out, err = rsa.SignPSS(RandReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthAuto,
})
}
@ -388,7 +388,7 @@ func (ctx ecKeyGenerator) keySize() int {
// Get a content encryption key for ECDH-ES
func (ctx ecKeyGenerator) genKey() ([]byte, rawHeader, error) {
priv, err := ecdsa.GenerateKey(ctx.publicKey.Curve, randReader)
priv, err := ecdsa.GenerateKey(ctx.publicKey.Curve, RandReader)
if err != nil {
return nil, rawHeader{}, err
}
@ -466,12 +466,13 @@ func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientI
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))
sig, err := ctx.privateKey.Sign(RandReader, payload, crypto.Hash(0))
if err != nil {
return Signature{}, err
}
@ -521,7 +522,7 @@ func (ctx ecDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm)
_, _ = hasher.Write(payload)
hashed := hasher.Sum(nil)
r, s, err := ecdsa.Sign(randReader, ctx.privateKey, hashed)
r, s, err := ecdsa.Sign(RandReader, ctx.privateKey, hashed)
if err != nil {
return Signature{}, err
}
@ -531,7 +532,7 @@ func (ctx ecDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm)
keyBytes++
}
// We serialize the outpus (r and s) into big-endian byte arrays and pad
// 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()

View File

@ -104,10 +104,18 @@ func (eo *EncrypterOptions) WithType(typ ContentType) *EncrypterOptions {
}
// 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 interface{}
KeyID string
Algorithm KeyAlgorithm
Key interface{}
KeyID string
PBES2Count int
PBES2Salt []byte
}
// NewEncrypter creates an appropriate encrypter based on the key type
@ -143,6 +151,9 @@ func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions)
if reflect.TypeOf(rawKey) != reflect.TypeOf([]byte{}) {
return nil, ErrUnsupportedKeyType
}
if encrypter.cipher.keySize() != len(rawKey.([]byte)) {
return nil, ErrInvalidKeySize
}
encrypter.keyGenerator = staticKeyGenerator{
key: rawKey.([]byte),
}
@ -228,6 +239,14 @@ func (ctx *genericEncrypter) addRecipient(recipient Recipient) (err error) {
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)
}
@ -242,6 +261,8 @@ func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKey
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
@ -266,6 +287,10 @@ func newDecrypter(decryptionKey interface{}) (keyDecrypter, error) {
return &symmetricKeyCipher{
key: decryptionKey,
}, nil
case string:
return &symmetricKeyCipher{
key: []byte(decryptionKey),
}, nil
case JSONWebKey:
return newDecrypter(decryptionKey.Key)
case *JSONWebKey:

View File

@ -21,10 +21,11 @@ import (
"compress/flate"
"encoding/base64"
"encoding/binary"
"encoding/json"
"io"
"math/big"
"regexp"
"gopkg.in/square/go-jose.v2/json"
)
var stripWhitespaceRegex = regexp.MustCompile("\\s")

View File

@ -18,9 +18,10 @@ package jose
import (
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"gopkg.in/square/go-jose.v2/json"
)
// rawJSONWebEncryption represents a raw JWE JSON object. Used for parsing/serializing.
@ -85,10 +86,12 @@ func (obj JSONWebEncryption) mergedHeaders(recipient *recipientInfo) rawHeader {
func (obj JSONWebEncryption) computeAuthData() []byte {
var protected string
if obj.original != nil {
if obj.original != nil && obj.original.Protected != nil {
protected = obj.original.Protected.base64()
} else {
} else if obj.protected != nil {
protected = base64.RawURLEncoding.EncodeToString(mustSerializeJSON((obj.protected)))
} else {
protected = ""
}
output := []byte(protected)

View File

@ -148,17 +148,10 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
if err == nil {
*k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use}
}
k.Certificates = make([]*x509.Certificate, len(raw.X5c))
for i, cert := range raw.X5c {
raw, err := base64.StdEncoding.DecodeString(cert)
k.Certificates, err = parseCertificateChain(raw.X5c)
if err != nil {
return err
}
k.Certificates[i], err = x509.ParseCertificate(raw)
if err != nil {
return err
return fmt.Errorf("failed to unmarshal x5c field: %s", err)
}
}
@ -196,6 +189,10 @@ func ecThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) {
return "", err
}
if len(x.Bytes()) > coordLength || len(y.Bytes()) > coordLength {
return "", errors.New("square/go-jose: invalid elliptic key (too large)")
}
return fmt.Sprintf(ecThumbprintTemplate, crv,
newFixedSizeBuffer(x.Bytes(), coordLength).base64(),
newFixedSizeBuffer(y.Bytes(), coordLength).base64()), nil
@ -209,6 +206,9 @@ func rsaThumbprintInput(n *big.Int, e int) (string, error) {
func edThumbprintInput(ed ed25519.PublicKey) (string, error) {
crv := "Ed25519"
if len(ed) > 32 {
return "", errors.New("square/go-jose: invalid elliptic key (too large)")
}
return fmt.Sprintf(edThumbprintTemplate, crv,
newFixedSizeBuffer(ed, 32).base64()), nil
}
@ -230,7 +230,7 @@ func (k *JSONWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
case *rsa.PrivateKey:
input, err = rsaThumbprintInput(key.N, key.E)
case ed25519.PrivateKey:
input, err = edThumbprintInput(ed25519.PublicKey(key[0:32]))
input, err = edThumbprintInput(ed25519.PublicKey(key[32:]))
default:
return nil, fmt.Errorf("square/go-jose: unknown key type '%s'", reflect.TypeOf(key))
}
@ -353,6 +353,17 @@ func (key rawJSONWebKey) ecPublicKey() (*ecdsa.PublicKey, error) {
return nil, errors.New("square/go-jose: invalid EC key, missing x/y values")
}
// The length of this octet string MUST be the full size of a coordinate for
// the curve specified in the "crv" parameter.
// https://tools.ietf.org/html/rfc7518#section-6.2.1.2
if curveSize(curve) != len(key.X.data) {
return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for x")
}
if curveSize(curve) != len(key.Y.data) {
return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for y")
}
x := key.X.bigInt()
y := key.Y.bigInt()
@ -410,8 +421,8 @@ func (key rawJSONWebKey) edPrivateKey() (ed25519.PrivateKey, error) {
}
privateKey := make([]byte, ed25519.PrivateKeySize)
copy(privateKey[0:32], key.X.bytes())
copy(privateKey[32:], key.D.bytes())
copy(privateKey[0:32], key.D.bytes())
copy(privateKey[32:], key.X.bytes())
rv := ed25519.PrivateKey(privateKey)
return rv, nil
}
@ -472,9 +483,9 @@ func (key rawJSONWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) {
}
func fromEdPrivateKey(ed ed25519.PrivateKey) (*rawJSONWebKey, error) {
raw := fromEdPublicKey(ed25519.PublicKey(ed[0:32]))
raw := fromEdPublicKey(ed25519.PublicKey(ed[32:]))
raw.D = newBuffer(ed[32:])
raw.D = newBuffer(ed[0:32])
return raw, nil
}
@ -489,6 +500,16 @@ func fromRsaPrivateKey(rsa *rsa.PrivateKey) (*rawJSONWebKey, error) {
raw.P = newBuffer(rsa.Primes[0].Bytes())
raw.Q = newBuffer(rsa.Primes[1].Bytes())
if rsa.Precomputed.Dp != nil {
raw.Dp = newBuffer(rsa.Precomputed.Dp.Bytes())
}
if rsa.Precomputed.Dq != nil {
raw.Dq = newBuffer(rsa.Precomputed.Dq.Bytes())
}
if rsa.Precomputed.Qinv != nil {
raw.Qi = newBuffer(rsa.Precomputed.Qinv.Bytes())
}
return raw, nil
}
@ -509,6 +530,22 @@ func (key rawJSONWebKey) ecPrivateKey() (*ecdsa.PrivateKey, error) {
return nil, fmt.Errorf("square/go-jose: invalid EC private key, missing x/y/d values")
}
// The length of this octet string MUST be the full size of a coordinate for
// the curve specified in the "crv" parameter.
// https://tools.ietf.org/html/rfc7518#section-6.2.1.2
if curveSize(curve) != len(key.X.data) {
return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for x")
}
if curveSize(curve) != len(key.Y.data) {
return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for y")
}
// https://tools.ietf.org/html/rfc7518#section-6.2.2.1
if dSize(curve) != len(key.D.data) {
return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for d")
}
x := key.X.bigInt()
y := key.Y.bigInt()
@ -536,11 +573,26 @@ func fromEcPrivateKey(ec *ecdsa.PrivateKey) (*rawJSONWebKey, error) {
return nil, fmt.Errorf("square/go-jose: invalid EC private key")
}
raw.D = newBuffer(ec.D.Bytes())
raw.D = newFixedSizeBuffer(ec.D.Bytes(), dSize(ec.PublicKey.Curve))
return raw, nil
}
// dSize returns the size in octets for the "d" member of an elliptic curve
// private key.
// The length of this octet string MUST be ceiling(log-base-2(n)/8)
// octets (where n is the order of the curve).
// https://tools.ietf.org/html/rfc7518#section-6.2.2.1
func dSize(curve elliptic.Curve) int {
order := curve.Params().P
bitLen := order.BitLen()
size := bitLen / 8
if bitLen%8 != 0 {
size = size + 1
}
return size
}
func fromSymmetricKey(key []byte) (*rawJSONWebKey, error) {
return &rawJSONWebKey{
Kty: "oct",

View File

@ -93,7 +93,7 @@ func (sig Signature) mergedHeaders() rawHeader {
}
// Compute data to be signed
func (obj JSONWebSignature) computeAuthData(signature *Signature) []byte {
func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) []byte {
var serializedProtected string
if signature.original != nil && signature.original.Protected != nil {
@ -106,7 +106,7 @@ func (obj JSONWebSignature) computeAuthData(signature *Signature) []byte {
return []byte(fmt.Sprintf("%s.%s",
serializedProtected,
base64.RawURLEncoding.EncodeToString(obj.payload)))
base64.RawURLEncoding.EncodeToString(payload)))
}
// parseSignedFull parses a message in full format.

83
vendor/gopkg.in/square/go-jose.v2/opaque.go generated vendored Normal file
View File

@ -0,0 +1,83 @@
/*-
* 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 jose
// OpaqueSigner is an interface that supports signing payloads with opaque
// private key(s). Private key operations preformed by implementors may, for
// example, occur in a hardware module. An OpaqueSigner may rotate signing keys
// transparently to the user of this interface.
type OpaqueSigner interface {
// Public returns the public key of the current signing key.
Public() *JSONWebKey
// Algs returns a list of supported signing algorithms.
Algs() []SignatureAlgorithm
// SignPayload signs a payload with the current signing key using the given
// algorithm.
SignPayload(payload []byte, alg SignatureAlgorithm) ([]byte, error)
}
type opaqueSigner struct {
signer OpaqueSigner
}
func newOpaqueSigner(alg SignatureAlgorithm, signer OpaqueSigner) (recipientSigInfo, error) {
var algSupported bool
for _, salg := range signer.Algs() {
if alg == salg {
algSupported = true
break
}
}
if !algSupported {
return recipientSigInfo{}, ErrUnsupportedAlgorithm
}
return recipientSigInfo{
sigAlg: alg,
publicKey: signer.Public,
signer: &opaqueSigner{
signer: signer,
},
}, nil
}
func (o *opaqueSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
out, err := o.signer.SignPayload(payload, alg)
if err != nil {
return Signature{}, err
}
return Signature{
Signature: out,
protected: &rawHeader{},
}, nil
}
// OpaqueVerifier is an interface that supports verifying payloads with opaque
// public key(s). An OpaqueSigner may rotate signing keys transparently to the
// user of this interface.
type OpaqueVerifier interface {
VerifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error
}
type opaqueVerifier struct {
verifier OpaqueVerifier
}
func (o *opaqueVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error {
return o.verifier.VerifyPayload(payload, signature, alg)
}

View File

@ -18,6 +18,8 @@ package jose
import (
"crypto/elliptic"
"crypto/x509"
"encoding/base64"
"errors"
"fmt"
@ -56,6 +58,11 @@ var (
// an RSA private key with more than two primes.
ErrUnsupportedKeyType = errors.New("square/go-jose: unsupported key type/format")
// ErrInvalidKeySize indicates that the given key is not the correct size
// for the selected algorithm. This can occur, for example, when trying to
// encrypt with AES-256 but passing only a 128-bit key as input.
ErrInvalidKeySize = errors.New("square/go-jose: invalid key size for algorithm")
// ErrNotSupported serialization of object is not supported. This occurs when
// trying to compact-serialize an object which can't be represented in
// compact form.
@ -141,10 +148,15 @@ const (
headerEPK = "epk" // *JSONWebKey
headerIV = "iv" // *byteBuffer
headerTag = "tag" // *byteBuffer
headerX5c = "x5c" // []*x509.Certificate
headerJWK = "jwk" // *JSONWebKey
headerKeyID = "kid" // string
headerNonce = "nonce" // string
headerP2C = "p2c" // *byteBuffer (int)
headerP2S = "p2s" // *byteBuffer ([]byte)
)
// rawHeader represents the JOSE header for JWE/JWS objects (used for parsing).
@ -162,11 +174,34 @@ type Header struct {
Algorithm string
Nonce string
// Any headers not recognised above get unmarshaled from JSON in a generic
// manner and placed in this map.
// Unverified certificate chain parsed from x5c header.
certificates []*x509.Certificate
// Any headers not recognised above get unmarshaled
// from JSON in a generic manner and placed in this map.
ExtraHeaders map[HeaderKey]interface{}
}
// Certificates verifies & returns the certificate chain present
// in the x5c header field of a message, if one was present. Returns
// an error if there was no x5c header present or the chain could
// not be validated with the given verify options.
func (h Header) Certificates(opts x509.VerifyOptions) ([][]*x509.Certificate, error) {
if len(h.certificates) == 0 {
return nil, errors.New("square/go-jose: no x5c header present in message")
}
leaf := h.certificates[0]
if opts.Intermediates == nil {
opts.Intermediates = x509.NewCertPool()
for _, intermediate := range h.certificates[1:] {
opts.Intermediates.AddCert(intermediate)
}
}
return leaf.Verify(opts)
}
func (parsed rawHeader) set(k HeaderKey, v interface{}) error {
b, err := json.Marshal(v)
if err != nil {
@ -180,7 +215,7 @@ func (parsed rawHeader) set(k HeaderKey, v interface{}) error {
// getString gets a string from the raw JSON, defaulting to "".
func (parsed rawHeader) getString(k HeaderKey) string {
v, ok := parsed[k]
if !ok {
if !ok || v == nil {
return ""
}
var s string
@ -294,6 +329,26 @@ func (parsed rawHeader) getCritical() ([]string, error) {
return q, nil
}
// getS2C extracts parsed "p2c" from the raw JSON.
func (parsed rawHeader) getP2C() (int, error) {
v := parsed[headerP2C]
if v == nil {
return 0, nil
}
var p2c int
err := json.Unmarshal(*v, &p2c)
if err != nil {
return 0, err
}
return p2c, nil
}
// getS2S extracts parsed "p2s" from the raw JSON.
func (parsed rawHeader) getP2S() (*byteBuffer, error) {
return parsed.getByteBuffer(headerP2S)
}
// sanitized produces a cleaned-up header object from the raw JSON.
func (parsed rawHeader) sanitized() (h Header, err error) {
for k, v := range parsed {
@ -333,6 +388,18 @@ func (parsed rawHeader) sanitized() (h Header, err error) {
return
}
h.Nonce = s
case headerX5c:
c := []string{}
err = json.Unmarshal(*v, &c)
if err != nil {
err = fmt.Errorf("failed to unmarshal x5c header: %v: %#v", err, string(*v))
return
}
h.certificates, err = parseCertificateChain(c)
if err != nil {
err = fmt.Errorf("failed to unmarshal x5c header: %v: %#v", err, string(*v))
return
}
default:
if h.ExtraHeaders == nil {
h.ExtraHeaders = map[HeaderKey]interface{}{}
@ -349,6 +416,21 @@ func (parsed rawHeader) sanitized() (h Header, err error) {
return
}
func parseCertificateChain(chain []string) ([]*x509.Certificate, error) {
out := make([]*x509.Certificate, len(chain))
for i, cert := range chain {
raw, err := base64.StdEncoding.DecodeString(cert)
if err != nil {
return nil, err
}
out[i], err = x509.ParseCertificate(raw)
if err != nil {
return nil, err
}
}
return out, nil
}
func (dst rawHeader) isSet(k HeaderKey) bool {
dvr := dst[k]
if dvr == nil {

View File

@ -94,10 +94,16 @@ type genericSigner struct {
type recipientSigInfo struct {
sigAlg SignatureAlgorithm
publicKey *JSONWebKey
publicKey func() *JSONWebKey
signer payloadSigner
}
func staticPublicKey(jwk *JSONWebKey) func() *JSONWebKey {
return func() *JSONWebKey {
return jwk
}
}
// NewSigner creates an appropriate signer based on the key type
func NewSigner(sig SigningKey, opts *SignerOptions) (Signer, error) {
return NewMultiSigner([]SigningKey{sig}, opts)
@ -146,9 +152,11 @@ func newVerifier(verificationKey interface{}) (payloadVerifier, error) {
return newVerifier(verificationKey.Key)
case *JSONWebKey:
return newVerifier(verificationKey.Key)
default:
return nil, ErrUnsupportedKeyType
}
if ov, ok := verificationKey.(OpaqueVerifier); ok {
return &opaqueVerifier{verifier: ov}, nil
}
return nil, ErrUnsupportedKeyType
}
func (ctx *genericSigner) addRecipient(alg SignatureAlgorithm, signingKey interface{}) error {
@ -175,9 +183,11 @@ func makeJWSRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipient
return newJWKSigner(alg, signingKey)
case *JSONWebKey:
return newJWKSigner(alg, *signingKey)
default:
return recipientSigInfo{}, ErrUnsupportedKeyType
}
if signer, ok := signingKey.(OpaqueSigner); ok {
return newOpaqueSigner(alg, signer)
}
return recipientSigInfo{}, ErrUnsupportedKeyType
}
func newJWKSigner(alg SignatureAlgorithm, signingKey JSONWebKey) (recipientSigInfo, error) {
@ -185,16 +195,16 @@ func newJWKSigner(alg SignatureAlgorithm, signingKey JSONWebKey) (recipientSigIn
if err != nil {
return recipientSigInfo{}, err
}
if recipient.publicKey != nil {
if recipient.publicKey != nil && recipient.publicKey() != nil {
// recipient.publicKey is a JWK synthesized for embedding when recipientSigInfo
// was created for the inner key (such as a RSA or ECDSA public key). It contains
// the pub key for embedding, but doesn't have extra params like key id.
publicKey := signingKey
publicKey.Key = recipient.publicKey.Key
recipient.publicKey = &publicKey
publicKey.Key = recipient.publicKey().Key
recipient.publicKey = staticPublicKey(&publicKey)
// This should be impossible, but let's check anyway.
if !recipient.publicKey.IsPublic() {
if !recipient.publicKey().IsPublic() {
return recipientSigInfo{}, errors.New("square/go-jose: public key was unexpectedly not public")
}
}
@ -211,7 +221,7 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
headerAlgorithm: string(recipient.sigAlg),
}
if recipient.publicKey != nil {
if recipient.publicKey != nil && recipient.publicKey() != nil {
// We want to embed the JWK or set the kid header, but not both. Having a protected
// header that contains an embedded JWK while also simultaneously containing the kid
// header is confusing, and at least in ACME the two are considered to be mutually
@ -221,9 +231,9 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
//
// See https://github.com/square/go-jose/issues/157 for more context.
if ctx.embedJWK {
protected[headerJWK] = recipient.publicKey
protected[headerJWK] = recipient.publicKey()
} else {
protected[headerKeyID] = recipient.publicKey.KeyID
protected[headerKeyID] = recipient.publicKey().KeyID
}
}
@ -280,34 +290,53 @@ func (ctx *genericSigner) Options() SignerOptions {
// payload header. You cannot assume that the key received in a payload is
// trusted.
func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error) {
verifier, err := newVerifier(verificationKey)
err := obj.DetachedVerify(obj.payload, verificationKey)
if err != nil {
return nil, err
}
return obj.payload, nil
}
// UnsafePayloadWithoutVerification returns the payload without
// verifying it. The content returned from this function cannot be
// trusted.
func (obj JSONWebSignature) UnsafePayloadWithoutVerification() []byte {
return obj.payload
}
// DetachedVerify validates a detached signature on the given payload. In
// most cases, you will probably want to use Verify instead. DetachedVerify
// is only useful if you have a payload and signature that are separated from
// each other.
func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey interface{}) error {
verifier, err := newVerifier(verificationKey)
if err != nil {
return err
}
if len(obj.Signatures) > 1 {
return nil, errors.New("square/go-jose: too many signatures in payload; expecting only one")
return errors.New("square/go-jose: too many signatures in payload; expecting only one")
}
signature := obj.Signatures[0]
headers := signature.mergedHeaders()
critical, err := headers.getCritical()
if err != nil {
return nil, err
return err
}
if len(critical) > 0 {
// Unsupported crit header
return nil, ErrCryptoFailure
return ErrCryptoFailure
}
input := obj.computeAuthData(&signature)
input := obj.computeAuthData(payload, &signature)
alg := headers.getSignatureAlgorithm()
err = verifier.verifyPayload(input, signature.Signature, alg)
if err == nil {
return obj.payload, nil
return nil
}
return nil, ErrCryptoFailure
return ErrCryptoFailure
}
// VerifyMulti validates (one of the multiple) signatures on the object and
@ -315,10 +344,27 @@ func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error)
// object and the payload. We return the signature and index to guarantee that
// callers are getting the verified value.
func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signature, []byte, error) {
verifier, err := newVerifier(verificationKey)
idx, sig, err := obj.DetachedVerifyMulti(obj.payload, verificationKey)
if err != nil {
return -1, Signature{}, nil, err
}
return idx, sig, obj.payload, nil
}
// DetachedVerifyMulti validates a detached signature on the given payload with
// a signature/object that has potentially multiple signers. This returns the index
// of the signature that was verified, along with the signature object. We return
// the signature and index to guarantee that callers are getting the verified value.
//
// In most cases, you will probably want to use Verify or VerifyMulti instead.
// DetachedVerifyMulti is only useful if you have a payload and signature that are
// separated from each other, and the signature can have multiple signers at the
// same time.
func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey interface{}) (int, Signature, error) {
verifier, err := newVerifier(verificationKey)
if err != nil {
return -1, Signature{}, err
}
for i, signature := range obj.Signatures {
headers := signature.mergedHeaders()
@ -331,13 +377,13 @@ func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signa
continue
}
input := obj.computeAuthData(&signature)
input := obj.computeAuthData(payload, &signature)
alg := headers.getSignatureAlgorithm()
err = verifier.verifyPayload(input, signature.Signature, alg)
if err == nil {
return i, signature, obj.payload, nil
return i, signature, nil
}
}
return -1, Signature{}, nil, ErrCryptoFailure
return -1, Signature{}, ErrCryptoFailure
}

View File

@ -17,6 +17,7 @@
package jose
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
@ -29,15 +30,30 @@ import (
"hash"
"io"
"golang.org/x/crypto/pbkdf2"
"gopkg.in/square/go-jose.v2/cipher"
)
// Random reader (stubbed out in tests)
var randReader = rand.Reader
var RandReader = rand.Reader
const (
// RFC7518 recommends a minimum of 1,000 iterations:
// https://tools.ietf.org/html/rfc7518#section-4.8.1.2
// NIST recommends a minimum of 10,000:
// https://pages.nist.gov/800-63-3/sp800-63b.html
// 1Password uses 100,000:
// https://support.1password.com/pbkdf2/
defaultP2C = 100000
// Default salt size: 128 bits
defaultP2SSize = 16
)
// Dummy key cipher for shared symmetric key mode
type symmetricKeyCipher struct {
key []byte // Pre-shared content-encryption key
p2c int // PBES2 Count
p2s []byte // PBES2 Salt Input
}
// Signer/verifier for MAC modes
@ -87,7 +103,7 @@ func newAESGCM(keySize int) contentCipher {
func newAESCBC(keySize int) contentCipher {
return &aeadContentCipher{
keyBytes: keySize * 2,
authtagBytes: 16,
authtagBytes: keySize,
getAead: func(key []byte) (cipher.AEAD, error) {
return josecipher.NewCBCHMAC(key, aes.NewCipher)
},
@ -114,10 +130,37 @@ func getContentCipher(alg ContentEncryption) contentCipher {
}
}
// getPbkdf2Params returns the key length and hash function used in
// pbkdf2.Key.
func getPbkdf2Params(alg KeyAlgorithm) (int, func() hash.Hash) {
switch alg {
case PBES2_HS256_A128KW:
return 16, sha256.New
case PBES2_HS384_A192KW:
return 24, sha512.New384
case PBES2_HS512_A256KW:
return 32, sha512.New
default:
panic("invalid algorithm")
}
}
// getRandomSalt generates a new salt of the given size.
func getRandomSalt(size int) ([]byte, error) {
salt := make([]byte, size)
_, err := io.ReadFull(RandReader, salt)
if err != nil {
return nil, err
}
return salt, nil
}
// newSymmetricRecipient creates a JWE encrypter based on AES-GCM key wrap.
func newSymmetricRecipient(keyAlg KeyAlgorithm, key []byte) (recipientKeyInfo, error) {
switch keyAlg {
case DIRECT, A128GCMKW, A192GCMKW, A256GCMKW, A128KW, A192KW, A256KW:
case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
default:
return recipientKeyInfo{}, ErrUnsupportedAlgorithm
}
@ -150,7 +193,7 @@ func newSymmetricSigner(sigAlg SignatureAlgorithm, key []byte) (recipientSigInfo
// Generate a random key for the given content cipher
func (ctx randomKeyGenerator) genKey() ([]byte, rawHeader, error) {
key := make([]byte, ctx.size)
_, err := io.ReadFull(randReader, key)
_, err := io.ReadFull(RandReader, key)
if err != nil {
return nil, rawHeader{}, err
}
@ -190,7 +233,7 @@ func (ctx aeadContentCipher) encrypt(key, aad, pt []byte) (*aeadParts, error) {
// Initialize a new nonce
iv := make([]byte, aead.NonceSize())
_, err = io.ReadFull(randReader, iv)
_, err = io.ReadFull(RandReader, iv)
if err != nil {
return nil, err
}
@ -212,6 +255,10 @@ func (ctx aeadContentCipher) decrypt(key, aad []byte, parts *aeadParts) ([]byte,
return nil, err
}
if len(parts.iv) != aead.NonceSize() || len(parts.tag) < ctx.authtagBytes {
return nil, ErrCryptoFailure
}
return aead.Open(nil, parts.iv, append(parts.ciphertext, parts.tag...), aad)
}
@ -253,6 +300,45 @@ func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipie
encryptedKey: jek,
header: &rawHeader{},
}, nil
case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
if len(ctx.p2s) == 0 {
salt, err := getRandomSalt(defaultP2SSize)
if err != nil {
return recipientInfo{}, err
}
ctx.p2s = salt
}
if ctx.p2c <= 0 {
ctx.p2c = defaultP2C
}
// salt is UTF8(Alg) || 0x00 || Salt Input
salt := bytes.Join([][]byte{[]byte(alg), ctx.p2s}, []byte{0x00})
// derive key
keyLen, h := getPbkdf2Params(alg)
key := pbkdf2.Key(ctx.key, salt, ctx.p2c, keyLen, h)
// use AES cipher with derived key
block, err := aes.NewCipher(key)
if err != nil {
return recipientInfo{}, err
}
jek, err := josecipher.KeyWrap(block, cek)
if err != nil {
return recipientInfo{}, err
}
header := &rawHeader{}
header.set(headerP2C, ctx.p2c)
header.set(headerP2S, newBuffer(ctx.p2s))
return recipientInfo{
encryptedKey: jek,
header: header,
}, nil
}
return recipientInfo{}, ErrUnsupportedAlgorithm
@ -295,6 +381,42 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
return nil, err
}
cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey)
if err != nil {
return nil, err
}
return cek, nil
case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
p2s, err := headers.getP2S()
if err != nil {
return nil, fmt.Errorf("square/go-jose: invalid P2S: %v", err)
}
if p2s == nil || len(p2s.data) == 0 {
return nil, fmt.Errorf("square/go-jose: invalid P2S: must be present")
}
p2c, err := headers.getP2C()
if err != nil {
return nil, fmt.Errorf("square/go-jose: invalid P2C: %v", err)
}
if p2c <= 0 {
return nil, fmt.Errorf("square/go-jose: invalid P2C: must be a positive integer")
}
// salt is UTF8(Alg) || 0x00 || Salt Input
alg := headers.getAlgorithm()
salt := bytes.Join([][]byte{[]byte(alg), p2s.bytes()}, []byte{0x00})
// derive key
keyLen, h := getPbkdf2Params(alg)
key := pbkdf2.Key(ctx.key, salt, p2c, keyLen, h)
// use AES cipher with derived key
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey)
if err != nil {
return nil, err

3
vendor/modules.txt vendored
View File

@ -105,6 +105,7 @@ golang.org/x/crypto/ed25519
golang.org/x/crypto/pkcs12
golang.org/x/crypto/cryptobyte
golang.org/x/crypto/cryptobyte/asn1
golang.org/x/crypto/pbkdf2
golang.org/x/crypto/ed25519/internal/edwards25519
golang.org/x/crypto/pkcs12/internal/rc2
# golang.org/x/net v0.0.0-20190415214537-1da14a5a36f2
@ -165,7 +166,7 @@ google.golang.org/grpc/binarylog/grpc_binarylog_v1
google.golang.org/grpc/internal/syscall
# gopkg.in/go-gorp/gorp.v2 v2.0.0-20180410155428-6032c66e0f5f
gopkg.in/go-gorp/gorp.v2
# gopkg.in/square/go-jose.v2 v2.1.4
# gopkg.in/square/go-jose.v2 v2.3.1
gopkg.in/square/go-jose.v2
gopkg.in/square/go-jose.v2/cipher
gopkg.in/square/go-jose.v2/json

View File

@ -4,9 +4,7 @@ import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
@ -318,6 +316,14 @@ func (wfe *WebFrontEndImpl) parseJWS(body []byte) (*jose.JSONWebSignature, *prob
parsedJWS, err := jose.ParseSigned(bodyStr)
if err != nil {
wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSParseError"}).Inc()
// TODO(#4300): This error references private keys, while the issue is
// actually with public keys. A fix has been merged upstream but we
// are waiting until a tagged release containing the change is made.
// Once there is a release containing the fix we'll need to update
// the dep and fix this check.
if strings.HasPrefix(err.Error(), "failed to unmarshal JWK: square/go-jose: invalid EC private key, wrong length") {
return nil, probs.Malformed("Parse error reading JWS: EC public key has incorrect padding")
}
return nil, probs.Malformed("Parse error reading JWS")
}
if len(parsedJWS.Signatures) > 1 {
@ -356,16 +362,6 @@ func (wfe *WebFrontEndImpl) parseJWSRequest(request *http.Request) (*jose.JSONWe
return nil, prob
}
// if the protected header contains a JWK and it's a EC key verify that it has
// proper field lengths in a goroutine as the caller doesn't care about the
// result of this check
if jws.Signatures[0].Header.JSONWebKey != nil {
switch jws.Signatures[0].Header.JSONWebKey.Key.(type) {
case *ecdsa.PublicKey:
go wfe.verifyECFieldLengths(bodyBytes, request)
}
}
return jws, nil
}
@ -767,63 +763,3 @@ func (wfe *WebFrontEndImpl) validKeyRollover(
NewKey: *jwk,
}, nil
}
func bitSizeToByteSize(bitSize int) int {
byteSize := (bitSize / 8)
if bitSize%8 > 0 {
return byteSize + 1
}
return byteSize
}
var curvesToSize = map[string]int{
"P-256": bitSizeToByteSize(elliptic.P256().Params().BitSize),
"P-384": bitSizeToByteSize(elliptic.P384().Params().BitSize),
"P-521": bitSizeToByteSize(elliptic.P521().Params().BitSize),
}
func (wfe *WebFrontEndImpl) verifyECFieldLengths(body []byte, request *http.Request) {
var payload struct {
Protected string
}
if err := json.Unmarshal(body, &payload); err != nil {
return
}
b, err := base64.RawURLEncoding.DecodeString(payload.Protected)
if err != nil {
return
}
var keyData struct {
JWK struct {
X string
Y string
Crv string
}
}
if err := json.Unmarshal(b, &keyData); err != nil {
// this really shouldn't happen as we've already done this once before
// successfully, in order to avoid a headache just return and pretend
// it didn't happen
return
}
x, err := base64.RawURLEncoding.DecodeString(keyData.JWK.X)
if err != nil {
return
}
y, err := base64.RawURLEncoding.DecodeString(keyData.JWK.Y)
if err != nil {
return
}
xLen, yLen := len(x), len(y)
curveSize, present := curvesToSize[keyData.JWK.Crv]
if !present {
return
}
if xLen != curveSize || yLen != curveSize {
wfe.stats.improperECFieldLengths.Inc()
// if extractRequesterIP fails just continue on as we still want to know
// that the key was badly padded, and a empty net.IP will render just fine
ip, _ := extractRequesterIP(request)
wfe.log.Infof("Incorrectly padded account EC JWK key from UA=%q IP=%s", request.UserAgent(), ip)
}
}

View File

@ -1566,36 +1566,13 @@ func TestMatchJWSURLs(t *testing.T) {
}
}
func TestVerifyECFieldLengths(t *testing.T) {
func TestBadPaddingError(t *testing.T) {
wfe, _ := setupWFE(t)
var testCases = []struct {
Name string
Body string
ExpectedStat int
}{
{
Name: "Correct lengths",
Body: `{"protected":"eyJhbGciOiJFUzI1NiIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IlM4Rk9tclozeXdqNHl5RnF0MGV0QUQ5MFUtRW5rTmFPQlNMZlFtZjdwTmciLCJ5Ijoidk12cER5cUZEUkhqR2ZaMXNpRE9tNUxTNnhOZFI1eFRweW9RR0xET1gyUSJ9fQ"}`,
ExpectedStat: 0,
},
{
Name: "Incorrect X length",
Body: `{"protected":"eyJhbGciOiJFUzI1NiIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6Im5QVElBQmNEQVNZNkZOR1NOZkhDQjUxdFk3cUNodGd6ZVZhek90THJ3USIsInkiOiJ2RUVzNFYwZWdKa055TTJRNHBwMDAxenUxNFZjcFEwX0VpOHhPT1B4S1pzIn19"}`,
ExpectedStat: 1,
},
{
Name: "Incorrect Y length",
Body: `{"protected":"eyJhbGciOiJFUzI1NiIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6InZFRXM0VjBlZ0prTnlNMlE0cHAwMDF6dTE0VmNwUTBfRWk4eE9PUHhLWnMiLCJ5IjoiblBUSUFCY0RBU1k2Rk5HU05mSENCNTF0WTdxQ2h0Z3plVmF6T3RMcndRIn19"}`,
ExpectedStat: 1,
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
wfe.stats.improperECFieldLengths.Set(0)
wfe.verifyECFieldLengths([]byte(tc.Body), &http.Request{})
test.AssertEquals(t, test.CountCounter(wfe.stats.improperECFieldLengths), tc.ExpectedStat)
})
}
_, prob := wfe.parseJWS([]byte(`{"protected":"eyJhbGciOiJFUzI1NiIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6Im5QVElBQmNEQVNZNkZOR1NOZkhDQjUxdFk3cUNodGd6ZVZhek90THJ3USIsInkiOiJ2RUVzNFYwZWdKa055TTJRNHBwMDAxenUxNFZjcFEwX0VpOHhPT1B4S1pzIn19","payload":""}`))
test.Assert(t, prob != nil, "parseJWS didn't return a problem")
test.AssertEquals(t, prob.Detail, "Parse error reading JWS: EC public key has incorrect padding")
_, prob = wfe.parseJWS([]byte(`{"protected":"eyJhbGciOiJFUzI1NiIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6InZFRXM0VjBlZ0prTnlNMlE0cHAwMDF6dTE0VmNwUTBfRWk4eE9PUHhLWnMiLCJ5IjoiblBUSUFCY0RBU1k2Rk5HU05mSENCNTF0WTdxQ2h0Z3plVmF6T3RMcndRIn19","payload":""}`))
test.Assert(t, prob != nil, "parseJWS didn't return a problem")
test.AssertEquals(t, prob.Detail, "Parse error reading JWS: EC public key has incorrect padding")
}