Merge pull request #1779 from dmcgowan/letsencrypt-support
Let's Encrypt support
This commit is contained in:
commit
9198d642ba
88 changed files with 19832 additions and 5 deletions
12
Godeps/Godeps.json
generated
12
Godeps/Godeps.json
generated
|
@ -270,6 +270,10 @@
|
||||||
"ImportPath": "golang.org/x/crypto/blowfish",
|
"ImportPath": "golang.org/x/crypto/blowfish",
|
||||||
"Rev": "c10c31b5e94b6f7a0283272dc2bb27163dcea24b"
|
"Rev": "c10c31b5e94b6f7a0283272dc2bb27163dcea24b"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/crypto/ocsp",
|
||||||
|
"Rev": "c10c31b5e94b6f7a0283272dc2bb27163dcea24b"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"ImportPath": "golang.org/x/net/context",
|
||||||
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
"Rev": "4876518f9e71663000c348837735820161a42df7"
|
||||||
|
@ -314,6 +318,10 @@
|
||||||
"ImportPath": "golang.org/x/oauth2/jwt",
|
"ImportPath": "golang.org/x/oauth2/jwt",
|
||||||
"Rev": "045497edb6234273d67dbc25da3f2ddbc4c4cacf"
|
"Rev": "045497edb6234273d67dbc25da3f2ddbc4c4cacf"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "golang.org/x/time/rate",
|
||||||
|
"Rev": "a4bde12657593d5e90d0533a3e4fd95e635124cb"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "google.golang.org/api/gensupport",
|
"ImportPath": "google.golang.org/api/gensupport",
|
||||||
"Rev": "9bf6e6e569ff057f75d9604a46c52928f17d2b54"
|
"Rev": "9bf6e6e569ff057f75d9604a46c52928f17d2b54"
|
||||||
|
@ -425,6 +433,10 @@
|
||||||
{
|
{
|
||||||
"ImportPath": "gopkg.in/yaml.v2",
|
"ImportPath": "gopkg.in/yaml.v2",
|
||||||
"Rev": "bef53efd0c76e49e6de55ead051f886bea7e9420"
|
"Rev": "bef53efd0c76e49e6de55ead051f886bea7e9420"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "rsc.io/letsencrypt",
|
||||||
|
"Rev": "a019c9e6fce0c7132679dea13bd8df7c86ffe26c"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,6 +95,19 @@ type Configuration struct {
|
||||||
// Specifies the CA certs for client authentication
|
// Specifies the CA certs for client authentication
|
||||||
// A file may contain multiple CA certificates encoded as PEM
|
// A file may contain multiple CA certificates encoded as PEM
|
||||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||||
|
|
||||||
|
// LetsEncrypt is used to configuration setting up TLS through
|
||||||
|
// Let's Encrypt instead of manually specifying certificate and
|
||||||
|
// key. If a TLS certificate is specified, the Let's Encrypt
|
||||||
|
// section will not be used.
|
||||||
|
LetsEncrypt struct {
|
||||||
|
// CacheFile specifies cache file to use for lets encrypt
|
||||||
|
// certificates and keys.
|
||||||
|
CacheFile string `yaml:"cachefile,omitempty"`
|
||||||
|
|
||||||
|
// Email is the email to use during Let's Encrypt registration
|
||||||
|
Email string `yaml:"email,omitempty"`
|
||||||
|
} `yaml:"letsencrypt,omitempty"`
|
||||||
} `yaml:"tls,omitempty"`
|
} `yaml:"tls,omitempty"`
|
||||||
|
|
||||||
// Headers is a set of headers to include in HTTP responses. A common
|
// Headers is a set of headers to include in HTTP responses. A common
|
||||||
|
|
|
@ -73,6 +73,10 @@ var configStruct = Configuration{
|
||||||
Certificate string `yaml:"certificate,omitempty"`
|
Certificate string `yaml:"certificate,omitempty"`
|
||||||
Key string `yaml:"key,omitempty"`
|
Key string `yaml:"key,omitempty"`
|
||||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||||
|
LetsEncrypt struct {
|
||||||
|
CacheFile string `yaml:"cachefile,omitempty"`
|
||||||
|
Email string `yaml:"email,omitempty"`
|
||||||
|
} `yaml:"letsencrypt,omitempty"`
|
||||||
} `yaml:"tls,omitempty"`
|
} `yaml:"tls,omitempty"`
|
||||||
Headers http.Header `yaml:"headers,omitempty"`
|
Headers http.Header `yaml:"headers,omitempty"`
|
||||||
Debug struct {
|
Debug struct {
|
||||||
|
@ -83,6 +87,10 @@ var configStruct = Configuration{
|
||||||
Certificate string `yaml:"certificate,omitempty"`
|
Certificate string `yaml:"certificate,omitempty"`
|
||||||
Key string `yaml:"key,omitempty"`
|
Key string `yaml:"key,omitempty"`
|
||||||
ClientCAs []string `yaml:"clientcas,omitempty"`
|
ClientCAs []string `yaml:"clientcas,omitempty"`
|
||||||
|
LetsEncrypt struct {
|
||||||
|
CacheFile string `yaml:"cachefile,omitempty"`
|
||||||
|
Email string `yaml:"email,omitempty"`
|
||||||
|
} `yaml:"letsencrypt,omitempty"`
|
||||||
}{
|
}{
|
||||||
ClientCAs: []string{"/path/to/ca.pem"},
|
ClientCAs: []string{"/path/to/ca.pem"},
|
||||||
},
|
},
|
||||||
|
|
|
@ -191,6 +191,9 @@ information about each option that appears later in this page.
|
||||||
clientcas:
|
clientcas:
|
||||||
- /path/to/ca.pem
|
- /path/to/ca.pem
|
||||||
- /path/to/another/ca.pem
|
- /path/to/another/ca.pem
|
||||||
|
letsencrypt:
|
||||||
|
cachefile: /path/to/cache-file
|
||||||
|
email: emailused@letsencrypt.com
|
||||||
debug:
|
debug:
|
||||||
addr: localhost:5001
|
addr: localhost:5001
|
||||||
headers:
|
headers:
|
||||||
|
@ -892,6 +895,9 @@ configuration may contain both.
|
||||||
clientcas:
|
clientcas:
|
||||||
- /path/to/ca.pem
|
- /path/to/ca.pem
|
||||||
- /path/to/another/ca.pem
|
- /path/to/another/ca.pem
|
||||||
|
letsencrypt:
|
||||||
|
cachefile: /path/to/cache-file
|
||||||
|
email: emailused@letsencrypt.com
|
||||||
debug:
|
debug:
|
||||||
addr: localhost:5001
|
addr: localhost:5001
|
||||||
headers:
|
headers:
|
||||||
|
@ -1037,6 +1043,40 @@ and proxy connections to the registry server.
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
### letsencrypt
|
||||||
|
|
||||||
|
The `letsencrypt` struct within `tls` is **optional**. Use this to configure TLS
|
||||||
|
certificates provided by [Let's Encrypt](https://letsencrypt.org/how-it-works/).
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Parameter</th>
|
||||||
|
<th>Required</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>cachefile</code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
yes
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
Absolute path to a file for the Let's Encrypt agent to cache data
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>email</code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
yes
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
Email used to register with Let's Encrypt.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
### debug
|
### debug
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,11 @@ A certificate issuer may supply you with an *intermediate* certificate. In this
|
||||||
|
|
||||||
cat domain.crt intermediate-certificates.pem > certs/domain.crt
|
cat domain.crt intermediate-certificates.pem > certs/domain.crt
|
||||||
|
|
||||||
|
### Let's Encrypt
|
||||||
|
|
||||||
|
The registry supports using Let's Encrypt to automatically obtain a browser-trusted certificate. For more
|
||||||
|
information on Let's Encrypt, see [https://letsencrypt.org/how-it-works/](https://letsencrypt.org/how-it-works/) and the relevant section of the [registry configuration](configuration.md#letsencrypt).
|
||||||
|
|
||||||
### Alternatives
|
### Alternatives
|
||||||
|
|
||||||
While rarely advisable, you may want to use self-signed certificates instead, or use your registry in an insecure fashion. You will find instructions [here](insecure.md).
|
While rarely advisable, you may want to use self-signed certificates instead, or use your registry in an insecure fashion. You will find instructions [here](insecure.md).
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"rsc.io/letsencrypt"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/Sirupsen/logrus/formatters/logstash"
|
"github.com/Sirupsen/logrus/formatters/logstash"
|
||||||
"github.com/bugsnag/bugsnag-go"
|
"github.com/bugsnag/bugsnag-go"
|
||||||
|
@ -111,11 +113,10 @@ func (registry *Registry) ListenAndServe() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.HTTP.TLS.Certificate != "" {
|
if config.HTTP.TLS.Certificate != "" || config.HTTP.TLS.LetsEncrypt.CacheFile != "" {
|
||||||
tlsConf := &tls.Config{
|
tlsConf := &tls.Config{
|
||||||
ClientAuth: tls.NoClientCert,
|
ClientAuth: tls.NoClientCert,
|
||||||
NextProtos: []string{"http/1.1"},
|
NextProtos: []string{"http/1.1"},
|
||||||
Certificates: make([]tls.Certificate, 1),
|
|
||||||
MinVersion: tls.VersionTLS10,
|
MinVersion: tls.VersionTLS10,
|
||||||
PreferServerCipherSuites: true,
|
PreferServerCipherSuites: true,
|
||||||
CipherSuites: []uint16{
|
CipherSuites: []uint16{
|
||||||
|
@ -130,10 +131,27 @@ func (registry *Registry) ListenAndServe() error {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.HTTP.TLS.LetsEncrypt.CacheFile != "" {
|
||||||
|
if config.HTTP.TLS.Certificate != "" {
|
||||||
|
return fmt.Errorf("cannot specify both certificate and Let's Encrypt")
|
||||||
|
}
|
||||||
|
var m letsencrypt.Manager
|
||||||
|
if err := m.CacheFile(config.HTTP.TLS.LetsEncrypt.CacheFile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !m.Registered() {
|
||||||
|
if err := m.Register(config.HTTP.TLS.LetsEncrypt.Email, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tlsConf.GetCertificate = m.GetCertificate
|
||||||
|
} else {
|
||||||
|
tlsConf.Certificates = make([]tls.Certificate, 1)
|
||||||
tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key)
|
tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(config.HTTP.TLS.ClientCAs) != 0 {
|
if len(config.HTTP.TLS.ClientCAs) != 0 {
|
||||||
pool := x509.NewCertPool()
|
pool := x509.NewCertPool()
|
||||||
|
|
592
vendor/golang.org/x/crypto/ocsp/ocsp.go
generated
vendored
Normal file
592
vendor/golang.org/x/crypto/ocsp/ocsp.go
generated
vendored
Normal file
|
@ -0,0 +1,592 @@
|
||||||
|
// Copyright 2013 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 ocsp parses OCSP responses as specified in RFC 2560. OCSP responses
|
||||||
|
// are signed messages attesting to the validity of a certificate for a small
|
||||||
|
// period of time. This is used to manage revocation for X.509 certificates.
|
||||||
|
package ocsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1})
|
||||||
|
|
||||||
|
// These are internal structures that reflect the ASN.1 structure of an OCSP
|
||||||
|
// response. See RFC 2560, section 4.2.
|
||||||
|
|
||||||
|
const (
|
||||||
|
ocspSuccess = 0
|
||||||
|
ocspMalformed = 1
|
||||||
|
ocspInternalError = 2
|
||||||
|
ocspTryLater = 3
|
||||||
|
ocspSigRequired = 4
|
||||||
|
ocspUnauthorized = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
type certID struct {
|
||||||
|
HashAlgorithm pkix.AlgorithmIdentifier
|
||||||
|
NameHash []byte
|
||||||
|
IssuerKeyHash []byte
|
||||||
|
SerialNumber *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://tools.ietf.org/html/rfc2560#section-4.1.1
|
||||||
|
type ocspRequest struct {
|
||||||
|
TBSRequest tbsRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
type tbsRequest struct {
|
||||||
|
Version int `asn1:"explicit,tag:0,default:0,optional"`
|
||||||
|
RequestorName pkix.RDNSequence `asn1:"explicit,tag:1,optional"`
|
||||||
|
RequestList []request
|
||||||
|
}
|
||||||
|
|
||||||
|
type request struct {
|
||||||
|
Cert certID
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseASN1 struct {
|
||||||
|
Status asn1.Enumerated
|
||||||
|
Response responseBytes `asn1:"explicit,tag:0"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseBytes struct {
|
||||||
|
ResponseType asn1.ObjectIdentifier
|
||||||
|
Response []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type basicResponse struct {
|
||||||
|
TBSResponseData responseData
|
||||||
|
SignatureAlgorithm pkix.AlgorithmIdentifier
|
||||||
|
Signature asn1.BitString
|
||||||
|
Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type responseData struct {
|
||||||
|
Raw asn1.RawContent
|
||||||
|
Version int `asn1:"optional,default:1,explicit,tag:0"`
|
||||||
|
RawResponderName asn1.RawValue `asn1:"optional,explicit,tag:1"`
|
||||||
|
KeyHash []byte `asn1:"optional,explicit,tag:2"`
|
||||||
|
ProducedAt time.Time `asn1:"generalized"`
|
||||||
|
Responses []singleResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
type singleResponse struct {
|
||||||
|
CertID certID
|
||||||
|
Good asn1.Flag `asn1:"tag:0,optional"`
|
||||||
|
Revoked revokedInfo `asn1:"explicit,tag:1,optional"`
|
||||||
|
Unknown asn1.Flag `asn1:"tag:2,optional"`
|
||||||
|
ThisUpdate time.Time `asn1:"generalized"`
|
||||||
|
NextUpdate time.Time `asn1:"generalized,explicit,tag:0,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type revokedInfo struct {
|
||||||
|
RevocationTime time.Time `asn1:"generalized"`
|
||||||
|
Reason int `asn1:"explicit,tag:0,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
|
||||||
|
oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
|
||||||
|
oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
|
||||||
|
oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
|
||||||
|
oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
|
||||||
|
oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
|
||||||
|
oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
|
||||||
|
oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2}
|
||||||
|
oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
|
||||||
|
oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
|
||||||
|
oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
|
||||||
|
oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
|
||||||
|
)
|
||||||
|
|
||||||
|
var hashOIDs = map[crypto.Hash]asn1.ObjectIdentifier{
|
||||||
|
crypto.SHA1: asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}),
|
||||||
|
crypto.SHA256: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1}),
|
||||||
|
crypto.SHA384: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 2}),
|
||||||
|
crypto.SHA512: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 3}),
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below
|
||||||
|
var signatureAlgorithmDetails = []struct {
|
||||||
|
algo x509.SignatureAlgorithm
|
||||||
|
oid asn1.ObjectIdentifier
|
||||||
|
pubKeyAlgo x509.PublicKeyAlgorithm
|
||||||
|
hash crypto.Hash
|
||||||
|
}{
|
||||||
|
{x509.MD2WithRSA, oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
|
||||||
|
{x509.MD5WithRSA, oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
|
||||||
|
{x509.SHA1WithRSA, oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
|
||||||
|
{x509.SHA256WithRSA, oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
|
||||||
|
{x509.SHA384WithRSA, oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
|
||||||
|
{x509.SHA512WithRSA, oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
|
||||||
|
{x509.DSAWithSHA1, oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
|
||||||
|
{x509.DSAWithSHA256, oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
|
||||||
|
{x509.ECDSAWithSHA1, oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
|
||||||
|
{x509.ECDSAWithSHA256, oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
|
||||||
|
{x509.ECDSAWithSHA384, oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
|
||||||
|
{x509.ECDSAWithSHA512, oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below
|
||||||
|
func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureAlgorithm) (hashFunc crypto.Hash, sigAlgo pkix.AlgorithmIdentifier, err error) {
|
||||||
|
var pubType x509.PublicKeyAlgorithm
|
||||||
|
|
||||||
|
switch pub := pub.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
pubType = x509.RSA
|
||||||
|
hashFunc = crypto.SHA256
|
||||||
|
sigAlgo.Algorithm = oidSignatureSHA256WithRSA
|
||||||
|
sigAlgo.Parameters = asn1.RawValue{
|
||||||
|
Tag: 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
pubType = x509.ECDSA
|
||||||
|
|
||||||
|
switch pub.Curve {
|
||||||
|
case elliptic.P224(), elliptic.P256():
|
||||||
|
hashFunc = crypto.SHA256
|
||||||
|
sigAlgo.Algorithm = oidSignatureECDSAWithSHA256
|
||||||
|
case elliptic.P384():
|
||||||
|
hashFunc = crypto.SHA384
|
||||||
|
sigAlgo.Algorithm = oidSignatureECDSAWithSHA384
|
||||||
|
case elliptic.P521():
|
||||||
|
hashFunc = crypto.SHA512
|
||||||
|
sigAlgo.Algorithm = oidSignatureECDSAWithSHA512
|
||||||
|
default:
|
||||||
|
err = errors.New("x509: unknown elliptic curve")
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
err = errors.New("x509: only RSA and ECDSA keys supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if requestedSigAlgo == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, details := range signatureAlgorithmDetails {
|
||||||
|
if details.algo == requestedSigAlgo {
|
||||||
|
if details.pubKeyAlgo != pubType {
|
||||||
|
err = errors.New("x509: requested SignatureAlgorithm does not match private key type")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sigAlgo.Algorithm, hashFunc = details.oid, details.hash
|
||||||
|
if hashFunc == 0 {
|
||||||
|
err = errors.New("x509: cannot sign with hash function requested")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
err = errors.New("x509: unknown SignatureAlgorithm")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(agl): this is taken from crypto/x509 and so should probably be exported
|
||||||
|
// from crypto/x509 or crypto/x509/pkix.
|
||||||
|
func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) x509.SignatureAlgorithm {
|
||||||
|
for _, details := range signatureAlgorithmDetails {
|
||||||
|
if oid.Equal(details.oid) {
|
||||||
|
return details.algo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x509.UnknownSignatureAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(rlb): This is not taken from crypto/x509, but it's of the same general form.
|
||||||
|
func getHashAlgorithmFromOID(target asn1.ObjectIdentifier) crypto.Hash {
|
||||||
|
for hash, oid := range hashOIDs {
|
||||||
|
if oid.Equal(target) {
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return crypto.Hash(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the exposed reflection of the internal OCSP structures.
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Good means that the certificate is valid.
|
||||||
|
Good = iota
|
||||||
|
// Revoked means that the certificate has been deliberately revoked.
|
||||||
|
Revoked = iota
|
||||||
|
// Unknown means that the OCSP responder doesn't know about the certificate.
|
||||||
|
Unknown = iota
|
||||||
|
// ServerFailed means that the OCSP responder failed to process the request.
|
||||||
|
ServerFailed = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
// Request represents an OCSP request. See RFC 2560.
|
||||||
|
type Request struct {
|
||||||
|
HashAlgorithm crypto.Hash
|
||||||
|
IssuerNameHash []byte
|
||||||
|
IssuerKeyHash []byte
|
||||||
|
SerialNumber *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response represents an OCSP response. See RFC 2560.
|
||||||
|
type Response struct {
|
||||||
|
// Status is one of {Good, Revoked, Unknown, ServerFailed}
|
||||||
|
Status int
|
||||||
|
SerialNumber *big.Int
|
||||||
|
ProducedAt, ThisUpdate, NextUpdate, RevokedAt time.Time
|
||||||
|
RevocationReason int
|
||||||
|
Certificate *x509.Certificate
|
||||||
|
// TBSResponseData contains the raw bytes of the signed response. If
|
||||||
|
// Certificate is nil then this can be used to verify Signature.
|
||||||
|
TBSResponseData []byte
|
||||||
|
Signature []byte
|
||||||
|
SignatureAlgorithm x509.SignatureAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are pre-serialized error responses for the various non-success codes
|
||||||
|
// defined by OCSP. The Unauthorized code in particular can be used by an OCSP
|
||||||
|
// responder that supports only pre-signed responses as a response to requests
|
||||||
|
// for certificates with unknown status. See RFC 5019.
|
||||||
|
var (
|
||||||
|
MalformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}
|
||||||
|
InternalErrorErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x02}
|
||||||
|
TryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
|
||||||
|
SigRequredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
|
||||||
|
UnauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
|
||||||
|
)
|
||||||
|
|
||||||
|
// CheckSignatureFrom checks that the signature in resp is a valid signature
|
||||||
|
// from issuer. This should only be used if resp.Certificate is nil. Otherwise,
|
||||||
|
// the OCSP response contained an intermediate certificate that created the
|
||||||
|
// signature. That signature is checked by ParseResponse and only
|
||||||
|
// resp.Certificate remains to be validated.
|
||||||
|
func (resp *Response) CheckSignatureFrom(issuer *x509.Certificate) error {
|
||||||
|
return issuer.CheckSignature(resp.SignatureAlgorithm, resp.TBSResponseData, resp.Signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseError results from an invalid OCSP response.
|
||||||
|
type ParseError string
|
||||||
|
|
||||||
|
func (p ParseError) Error() string {
|
||||||
|
return string(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseRequest parses an OCSP request in DER form. It only supports
|
||||||
|
// requests for a single certificate. Signed requests are not supported.
|
||||||
|
// If a request includes a signature, it will result in a ParseError.
|
||||||
|
func ParseRequest(bytes []byte) (*Request, error) {
|
||||||
|
var req ocspRequest
|
||||||
|
rest, err := asn1.Unmarshal(bytes, &req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(rest) > 0 {
|
||||||
|
return nil, ParseError("trailing data in OCSP request")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.TBSRequest.RequestList) == 0 {
|
||||||
|
return nil, ParseError("OCSP request contains no request body")
|
||||||
|
}
|
||||||
|
innerRequest := req.TBSRequest.RequestList[0]
|
||||||
|
|
||||||
|
hashFunc := getHashAlgorithmFromOID(innerRequest.Cert.HashAlgorithm.Algorithm)
|
||||||
|
if hashFunc == crypto.Hash(0) {
|
||||||
|
return nil, ParseError("OCSP request uses unknown hash function")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Request{
|
||||||
|
HashAlgorithm: hashFunc,
|
||||||
|
IssuerNameHash: innerRequest.Cert.NameHash,
|
||||||
|
IssuerKeyHash: innerRequest.Cert.IssuerKeyHash,
|
||||||
|
SerialNumber: innerRequest.Cert.SerialNumber,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseResponse parses an OCSP response in DER form. It only supports
|
||||||
|
// responses for a single certificate. If the response contains a certificate
|
||||||
|
// then the signature over the response is checked. If issuer is not nil then
|
||||||
|
// it will be used to validate the signature or embedded certificate. Invalid
|
||||||
|
// signatures or parse failures will result in a ParseError.
|
||||||
|
func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) {
|
||||||
|
var resp responseASN1
|
||||||
|
rest, err := asn1.Unmarshal(bytes, &resp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(rest) > 0 {
|
||||||
|
return nil, ParseError("trailing data in OCSP response")
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := new(Response)
|
||||||
|
if resp.Status != ocspSuccess {
|
||||||
|
ret.Status = ServerFailed
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !resp.Response.ResponseType.Equal(idPKIXOCSPBasic) {
|
||||||
|
return nil, ParseError("bad OCSP response type")
|
||||||
|
}
|
||||||
|
|
||||||
|
var basicResp basicResponse
|
||||||
|
rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(basicResp.Certificates) > 1 {
|
||||||
|
return nil, ParseError("OCSP response contains bad number of certificates")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(basicResp.TBSResponseData.Responses) != 1 {
|
||||||
|
return nil, ParseError("OCSP response contains bad number of responses")
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.TBSResponseData = basicResp.TBSResponseData.Raw
|
||||||
|
ret.Signature = basicResp.Signature.RightAlign()
|
||||||
|
ret.SignatureAlgorithm = getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm)
|
||||||
|
|
||||||
|
if len(basicResp.Certificates) > 0 {
|
||||||
|
ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ret.CheckSignatureFrom(ret.Certificate); err != nil {
|
||||||
|
return nil, ParseError("bad OCSP signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
if issuer != nil {
|
||||||
|
if err := issuer.CheckSignature(ret.Certificate.SignatureAlgorithm, ret.Certificate.RawTBSCertificate, ret.Certificate.Signature); err != nil {
|
||||||
|
return nil, ParseError("bad signature on embedded certificate")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if issuer != nil {
|
||||||
|
if err := ret.CheckSignatureFrom(issuer); err != nil {
|
||||||
|
return nil, ParseError("bad OCSP signature")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r := basicResp.TBSResponseData.Responses[0]
|
||||||
|
|
||||||
|
ret.SerialNumber = r.CertID.SerialNumber
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case bool(r.Good):
|
||||||
|
ret.Status = Good
|
||||||
|
case bool(r.Unknown):
|
||||||
|
ret.Status = Unknown
|
||||||
|
default:
|
||||||
|
ret.Status = Revoked
|
||||||
|
ret.RevokedAt = r.Revoked.RevocationTime
|
||||||
|
ret.RevocationReason = r.Revoked.Reason
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.ProducedAt = basicResp.TBSResponseData.ProducedAt
|
||||||
|
ret.ThisUpdate = r.ThisUpdate
|
||||||
|
ret.NextUpdate = r.NextUpdate
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestOptions contains options for constructing OCSP requests.
|
||||||
|
type RequestOptions struct {
|
||||||
|
// Hash contains the hash function that should be used when
|
||||||
|
// constructing the OCSP request. If zero, SHA-1 will be used.
|
||||||
|
Hash crypto.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *RequestOptions) hash() crypto.Hash {
|
||||||
|
if opts == nil || opts.Hash == 0 {
|
||||||
|
// SHA-1 is nearly universally used in OCSP.
|
||||||
|
return crypto.SHA1
|
||||||
|
}
|
||||||
|
return opts.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRequest returns a DER-encoded, OCSP request for the status of cert. If
|
||||||
|
// opts is nil then sensible defaults are used.
|
||||||
|
func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte, error) {
|
||||||
|
hashFunc := opts.hash()
|
||||||
|
|
||||||
|
// OCSP seems to be the only place where these raw hash identifiers are
|
||||||
|
// used. I took the following from
|
||||||
|
// http://msdn.microsoft.com/en-us/library/ff635603.aspx
|
||||||
|
var hashOID asn1.ObjectIdentifier
|
||||||
|
hashOID, ok := hashOIDs[hashFunc]
|
||||||
|
if !ok {
|
||||||
|
return nil, x509.ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hashFunc.Available() {
|
||||||
|
return nil, x509.ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
h := opts.hash().New()
|
||||||
|
|
||||||
|
var publicKeyInfo struct {
|
||||||
|
Algorithm pkix.AlgorithmIdentifier
|
||||||
|
PublicKey asn1.BitString
|
||||||
|
}
|
||||||
|
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Write(publicKeyInfo.PublicKey.RightAlign())
|
||||||
|
issuerKeyHash := h.Sum(nil)
|
||||||
|
|
||||||
|
h.Reset()
|
||||||
|
h.Write(issuer.RawSubject)
|
||||||
|
issuerNameHash := h.Sum(nil)
|
||||||
|
|
||||||
|
return asn1.Marshal(ocspRequest{
|
||||||
|
tbsRequest{
|
||||||
|
Version: 0,
|
||||||
|
RequestList: []request{
|
||||||
|
{
|
||||||
|
Cert: certID{
|
||||||
|
pkix.AlgorithmIdentifier{
|
||||||
|
Algorithm: hashOID,
|
||||||
|
Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
|
||||||
|
},
|
||||||
|
issuerNameHash,
|
||||||
|
issuerKeyHash,
|
||||||
|
cert.SerialNumber,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResponse returns a DER-encoded OCSP response with the specified contents.
|
||||||
|
// The fields in the response are populated as follows:
|
||||||
|
//
|
||||||
|
// The responder cert is used to populate the ResponderName field, and the certificate
|
||||||
|
// itself is provided alongside the OCSP response signature.
|
||||||
|
//
|
||||||
|
// The issuer cert is used to puplate the IssuerNameHash and IssuerKeyHash fields.
|
||||||
|
// (SHA-1 is used for the hash function; this is not configurable.)
|
||||||
|
//
|
||||||
|
// The template is used to populate the SerialNumber, RevocationStatus, RevokedAt,
|
||||||
|
// RevocationReason, ThisUpdate, and NextUpdate fields.
|
||||||
|
//
|
||||||
|
// The ProducedAt date is automatically set to the current date, to the nearest minute.
|
||||||
|
func CreateResponse(issuer, responderCert *x509.Certificate, template Response, priv crypto.Signer) ([]byte, error) {
|
||||||
|
var publicKeyInfo struct {
|
||||||
|
Algorithm pkix.AlgorithmIdentifier
|
||||||
|
PublicKey asn1.BitString
|
||||||
|
}
|
||||||
|
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h := sha1.New()
|
||||||
|
h.Write(publicKeyInfo.PublicKey.RightAlign())
|
||||||
|
issuerKeyHash := h.Sum(nil)
|
||||||
|
|
||||||
|
h.Reset()
|
||||||
|
h.Write(issuer.RawSubject)
|
||||||
|
issuerNameHash := h.Sum(nil)
|
||||||
|
|
||||||
|
innerResponse := singleResponse{
|
||||||
|
CertID: certID{
|
||||||
|
HashAlgorithm: pkix.AlgorithmIdentifier{
|
||||||
|
Algorithm: hashOIDs[crypto.SHA1],
|
||||||
|
Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
|
||||||
|
},
|
||||||
|
NameHash: issuerNameHash,
|
||||||
|
IssuerKeyHash: issuerKeyHash,
|
||||||
|
SerialNumber: template.SerialNumber,
|
||||||
|
},
|
||||||
|
ThisUpdate: template.ThisUpdate.UTC(),
|
||||||
|
NextUpdate: template.NextUpdate.UTC(),
|
||||||
|
}
|
||||||
|
|
||||||
|
switch template.Status {
|
||||||
|
case Good:
|
||||||
|
innerResponse.Good = true
|
||||||
|
case Unknown:
|
||||||
|
innerResponse.Unknown = true
|
||||||
|
case Revoked:
|
||||||
|
innerResponse.Revoked = revokedInfo{
|
||||||
|
RevocationTime: template.RevokedAt.UTC(),
|
||||||
|
Reason: template.RevocationReason,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
responderName := asn1.RawValue{
|
||||||
|
Class: 2, // context-specific
|
||||||
|
Tag: 1, // explicit tag
|
||||||
|
IsCompound: true,
|
||||||
|
Bytes: responderCert.RawSubject,
|
||||||
|
}
|
||||||
|
tbsResponseData := responseData{
|
||||||
|
Version: 0,
|
||||||
|
RawResponderName: responderName,
|
||||||
|
ProducedAt: time.Now().Truncate(time.Minute).UTC(),
|
||||||
|
Responses: []singleResponse{innerResponse},
|
||||||
|
}
|
||||||
|
|
||||||
|
tbsResponseDataDER, err := asn1.Marshal(tbsResponseData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(priv.Public(), template.SignatureAlgorithm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
responseHash := hashFunc.New()
|
||||||
|
responseHash.Write(tbsResponseDataDER)
|
||||||
|
signature, err := priv.Sign(rand.Reader, responseHash.Sum(nil), hashFunc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := basicResponse{
|
||||||
|
TBSResponseData: tbsResponseData,
|
||||||
|
SignatureAlgorithm: signatureAlgorithm,
|
||||||
|
Signature: asn1.BitString{
|
||||||
|
Bytes: signature,
|
||||||
|
BitLength: 8 * len(signature),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if template.Certificate != nil {
|
||||||
|
response.Certificates = []asn1.RawValue{
|
||||||
|
asn1.RawValue{FullBytes: template.Certificate.Raw},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responseDER, err := asn1.Marshal(response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return asn1.Marshal(responseASN1{
|
||||||
|
Status: ocspSuccess,
|
||||||
|
Response: responseBytes{
|
||||||
|
ResponseType: idPKIXOCSPBasic,
|
||||||
|
Response: responseDER,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
27
vendor/golang.org/x/time/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/time/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2009 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.
|
22
vendor/golang.org/x/time/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/time/PATENTS
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
Additional IP Rights Grant (Patents)
|
||||||
|
|
||||||
|
"This implementation" means the copyrightable works distributed by
|
||||||
|
Google as part of the Go project.
|
||||||
|
|
||||||
|
Google 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,
|
||||||
|
transfer and otherwise run, modify and propagate the contents of this
|
||||||
|
implementation of Go, where such license applies only to those patent
|
||||||
|
claims, both currently owned or controlled by Google and acquired in
|
||||||
|
the future, licensable by Google that are necessarily infringed by this
|
||||||
|
implementation of Go. This grant does not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of this
|
||||||
|
implementation. If you or your agent or exclusive licensee institute or
|
||||||
|
order or agree to the institution of patent litigation against any
|
||||||
|
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that this implementation of Go or any code incorporated within this
|
||||||
|
implementation of Go constitutes direct or contributory patent
|
||||||
|
infringement, or inducement of patent infringement, then any patent
|
||||||
|
rights granted to you under this License for this implementation of Go
|
||||||
|
shall terminate as of the date such litigation is filed.
|
368
vendor/golang.org/x/time/rate/rate.go
generated
vendored
Normal file
368
vendor/golang.org/x/time/rate/rate.go
generated
vendored
Normal file
|
@ -0,0 +1,368 @@
|
||||||
|
// Copyright 2015 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 rate provides a rate limiter.
|
||||||
|
package rate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Limit defines the maximum frequency of some events.
|
||||||
|
// Limit is represented as number of events per second.
|
||||||
|
// A zero Limit allows no events.
|
||||||
|
type Limit float64
|
||||||
|
|
||||||
|
// Inf is the infinite rate limit; it allows all events (even if burst is zero).
|
||||||
|
const Inf = Limit(math.MaxFloat64)
|
||||||
|
|
||||||
|
// Every converts a minimum time interval between events to a Limit.
|
||||||
|
func Every(interval time.Duration) Limit {
|
||||||
|
if interval <= 0 {
|
||||||
|
return Inf
|
||||||
|
}
|
||||||
|
return 1 / Limit(interval.Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Limiter controls how frequently events are allowed to happen.
|
||||||
|
// It implements a "token bucket" of size b, initially full and refilled
|
||||||
|
// at rate r tokens per second.
|
||||||
|
// Informally, in any large enough time interval, the Limiter limits the
|
||||||
|
// rate to r tokens per second, with a maximum burst size of b events.
|
||||||
|
// As a special case, if r == Inf (the infinite rate), b is ignored.
|
||||||
|
// See https://en.wikipedia.org/wiki/Token_bucket for more about token buckets.
|
||||||
|
//
|
||||||
|
// The zero value is a valid Limiter, but it will reject all events.
|
||||||
|
// Use NewLimiter to create non-zero Limiters.
|
||||||
|
//
|
||||||
|
// Limiter has three main methods, Allow, Reserve, and Wait.
|
||||||
|
// Most callers should use Wait.
|
||||||
|
//
|
||||||
|
// Each of the three methods consumes a single token.
|
||||||
|
// They differ in their behavior when no token is available.
|
||||||
|
// If no token is available, Allow returns false.
|
||||||
|
// If no token is available, Reserve returns a reservation for a future token
|
||||||
|
// and the amount of time the caller must wait before using it.
|
||||||
|
// If no token is available, Wait blocks until one can be obtained
|
||||||
|
// or its associated context.Context is canceled.
|
||||||
|
//
|
||||||
|
// The methods AllowN, ReserveN, and WaitN consume n tokens.
|
||||||
|
type Limiter struct {
|
||||||
|
limit Limit
|
||||||
|
burst int
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
tokens float64
|
||||||
|
// last is the last time the limiter's tokens field was updated
|
||||||
|
last time.Time
|
||||||
|
// lastEvent is the latest time of a rate-limited event (past or future)
|
||||||
|
lastEvent time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit returns the maximum overall event rate.
|
||||||
|
func (lim *Limiter) Limit() Limit {
|
||||||
|
lim.mu.Lock()
|
||||||
|
defer lim.mu.Unlock()
|
||||||
|
return lim.limit
|
||||||
|
}
|
||||||
|
|
||||||
|
// Burst returns the maximum burst size. Burst is the maximum number of tokens
|
||||||
|
// that can be consumed in a single call to Allow, Reserve, or Wait, so higher
|
||||||
|
// Burst values allow more events to happen at once.
|
||||||
|
// A zero Burst allows no events, unless limit == Inf.
|
||||||
|
func (lim *Limiter) Burst() int {
|
||||||
|
return lim.burst
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLimiter returns a new Limiter that allows events up to rate r and permits
|
||||||
|
// bursts of at most b tokens.
|
||||||
|
func NewLimiter(r Limit, b int) *Limiter {
|
||||||
|
return &Limiter{
|
||||||
|
limit: r,
|
||||||
|
burst: b,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow is shorthand for AllowN(time.Now(), 1).
|
||||||
|
func (lim *Limiter) Allow() bool {
|
||||||
|
return lim.AllowN(time.Now(), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowN reports whether n events may happen at time now.
|
||||||
|
// Use this method if you intend to drop / skip events that exceed the rate limit.
|
||||||
|
// Otherwise use Reserve or Wait.
|
||||||
|
func (lim *Limiter) AllowN(now time.Time, n int) bool {
|
||||||
|
return lim.reserveN(now, n, 0).ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Reservation holds information about events that are permitted by a Limiter to happen after a delay.
|
||||||
|
// A Reservation may be canceled, which may enable the Limiter to permit additional events.
|
||||||
|
type Reservation struct {
|
||||||
|
ok bool
|
||||||
|
lim *Limiter
|
||||||
|
tokens int
|
||||||
|
timeToAct time.Time
|
||||||
|
// This is the Limit at reservation time, it can change later.
|
||||||
|
limit Limit
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK returns whether the limiter can provide the requested number of tokens
|
||||||
|
// within the maximum wait time. If OK is false, Delay returns InfDuration, and
|
||||||
|
// Cancel does nothing.
|
||||||
|
func (r *Reservation) OK() bool {
|
||||||
|
return r.ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay is shorthand for DelayFrom(time.Now()).
|
||||||
|
func (r *Reservation) Delay() time.Duration {
|
||||||
|
return r.DelayFrom(time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfDuration is the duration returned by Delay when a Reservation is not OK.
|
||||||
|
const InfDuration = time.Duration(1<<63 - 1)
|
||||||
|
|
||||||
|
// DelayFrom returns the duration for which the reservation holder must wait
|
||||||
|
// before taking the reserved action. Zero duration means act immediately.
|
||||||
|
// InfDuration means the limiter cannot grant the tokens requested in this
|
||||||
|
// Reservation within the maximum wait time.
|
||||||
|
func (r *Reservation) DelayFrom(now time.Time) time.Duration {
|
||||||
|
if !r.ok {
|
||||||
|
return InfDuration
|
||||||
|
}
|
||||||
|
delay := r.timeToAct.Sub(now)
|
||||||
|
if delay < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return delay
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel is shorthand for CancelAt(time.Now()).
|
||||||
|
func (r *Reservation) Cancel() {
|
||||||
|
r.CancelAt(time.Now())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CancelAt indicates that the reservation holder will not perform the reserved action
|
||||||
|
// and reverses the effects of this Reservation on the rate limit as much as possible,
|
||||||
|
// considering that other reservations may have already been made.
|
||||||
|
func (r *Reservation) CancelAt(now time.Time) {
|
||||||
|
if !r.ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.lim.mu.Lock()
|
||||||
|
defer r.lim.mu.Unlock()
|
||||||
|
|
||||||
|
if r.lim.limit == Inf || r.tokens == 0 || r.timeToAct.Before(now) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate tokens to restore
|
||||||
|
// The duration between lim.lastEvent and r.timeToAct tells us how many tokens were reserved
|
||||||
|
// after r was obtained. These tokens should not be restored.
|
||||||
|
restoreTokens := float64(r.tokens) - r.limit.tokensFromDuration(r.lim.lastEvent.Sub(r.timeToAct))
|
||||||
|
if restoreTokens <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// advance time to now
|
||||||
|
now, _, tokens := r.lim.advance(now)
|
||||||
|
// calculate new number of tokens
|
||||||
|
tokens += restoreTokens
|
||||||
|
if burst := float64(r.lim.burst); tokens > burst {
|
||||||
|
tokens = burst
|
||||||
|
}
|
||||||
|
// update state
|
||||||
|
r.lim.last = now
|
||||||
|
r.lim.tokens = tokens
|
||||||
|
if r.timeToAct == r.lim.lastEvent {
|
||||||
|
prevEvent := r.timeToAct.Add(r.limit.durationFromTokens(float64(-r.tokens)))
|
||||||
|
if !prevEvent.Before(now) {
|
||||||
|
r.lim.lastEvent = prevEvent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserve is shorthand for ReserveN(time.Now(), 1).
|
||||||
|
func (lim *Limiter) Reserve() *Reservation {
|
||||||
|
return lim.ReserveN(time.Now(), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReserveN returns a Reservation that indicates how long the caller must wait before n events happen.
|
||||||
|
// The Limiter takes this Reservation into account when allowing future events.
|
||||||
|
// ReserveN returns false if n exceeds the Limiter's burst size.
|
||||||
|
// Usage example:
|
||||||
|
// r, ok := lim.ReserveN(time.Now(), 1)
|
||||||
|
// if !ok {
|
||||||
|
// // Not allowed to act! Did you remember to set lim.burst to be > 0 ?
|
||||||
|
// }
|
||||||
|
// time.Sleep(r.Delay())
|
||||||
|
// Act()
|
||||||
|
// Use this method if you wish to wait and slow down in accordance with the rate limit without dropping events.
|
||||||
|
// If you need to respect a deadline or cancel the delay, use Wait instead.
|
||||||
|
// To drop or skip events exceeding rate limit, use Allow instead.
|
||||||
|
func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation {
|
||||||
|
r := lim.reserveN(now, n, InfDuration)
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait is shorthand for WaitN(ctx, 1).
|
||||||
|
func (lim *Limiter) Wait(ctx context.Context) (err error) {
|
||||||
|
return lim.WaitN(ctx, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitN blocks until lim permits n events to happen.
|
||||||
|
// It returns an error if n exceeds the Limiter's burst size, the Context is
|
||||||
|
// canceled, or the expected wait time exceeds the Context's Deadline.
|
||||||
|
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) {
|
||||||
|
if n > lim.burst {
|
||||||
|
return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, lim.burst)
|
||||||
|
}
|
||||||
|
// Check if ctx is already cancelled
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
// Determine wait limit
|
||||||
|
now := time.Now()
|
||||||
|
waitLimit := InfDuration
|
||||||
|
if deadline, ok := ctx.Deadline(); ok {
|
||||||
|
waitLimit = deadline.Sub(now)
|
||||||
|
}
|
||||||
|
// Reserve
|
||||||
|
r := lim.reserveN(now, n, waitLimit)
|
||||||
|
if !r.ok {
|
||||||
|
return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", n)
|
||||||
|
}
|
||||||
|
// Wait
|
||||||
|
t := time.NewTimer(r.DelayFrom(now))
|
||||||
|
defer t.Stop()
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
// We can proceed.
|
||||||
|
return nil
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Context was canceled before we could proceed. Cancel the
|
||||||
|
// reservation, which may permit other events to proceed sooner.
|
||||||
|
r.Cancel()
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLimit is shorthand for SetLimitAt(time.Now(), newLimit).
|
||||||
|
func (lim *Limiter) SetLimit(newLimit Limit) {
|
||||||
|
lim.SetLimitAt(time.Now(), newLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLimitAt sets a new Limit for the limiter. The new Limit, and Burst, may be violated
|
||||||
|
// or underutilized by those which reserved (using Reserve or Wait) but did not yet act
|
||||||
|
// before SetLimitAt was called.
|
||||||
|
func (lim *Limiter) SetLimitAt(now time.Time, newLimit Limit) {
|
||||||
|
lim.mu.Lock()
|
||||||
|
defer lim.mu.Unlock()
|
||||||
|
|
||||||
|
now, _, tokens := lim.advance(now)
|
||||||
|
|
||||||
|
lim.last = now
|
||||||
|
lim.tokens = tokens
|
||||||
|
lim.limit = newLimit
|
||||||
|
}
|
||||||
|
|
||||||
|
// reserveN is a helper method for AllowN, ReserveN, and WaitN.
|
||||||
|
// maxFutureReserve specifies the maximum reservation wait duration allowed.
|
||||||
|
// reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN.
|
||||||
|
func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation {
|
||||||
|
lim.mu.Lock()
|
||||||
|
defer lim.mu.Unlock()
|
||||||
|
|
||||||
|
if lim.limit == Inf {
|
||||||
|
return Reservation{
|
||||||
|
ok: true,
|
||||||
|
lim: lim,
|
||||||
|
tokens: n,
|
||||||
|
timeToAct: now,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
now, last, tokens := lim.advance(now)
|
||||||
|
|
||||||
|
// Calculate the remaining number of tokens resulting from the request.
|
||||||
|
tokens -= float64(n)
|
||||||
|
|
||||||
|
// Calculate the wait duration
|
||||||
|
var waitDuration time.Duration
|
||||||
|
if tokens < 0 {
|
||||||
|
waitDuration = lim.limit.durationFromTokens(-tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decide result
|
||||||
|
ok := n <= lim.burst && waitDuration <= maxFutureReserve
|
||||||
|
|
||||||
|
// Prepare reservation
|
||||||
|
r := Reservation{
|
||||||
|
ok: ok,
|
||||||
|
lim: lim,
|
||||||
|
limit: lim.limit,
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
r.tokens = n
|
||||||
|
r.timeToAct = now.Add(waitDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update state
|
||||||
|
if ok {
|
||||||
|
lim.last = now
|
||||||
|
lim.tokens = tokens
|
||||||
|
lim.lastEvent = r.timeToAct
|
||||||
|
} else {
|
||||||
|
lim.last = last
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// advance calculates and returns an updated state for lim resulting from the passage of time.
|
||||||
|
// lim is not changed.
|
||||||
|
func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) {
|
||||||
|
last := lim.last
|
||||||
|
if now.Before(last) {
|
||||||
|
last = now
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid making delta overflow below when last is very old.
|
||||||
|
maxElapsed := lim.limit.durationFromTokens(float64(lim.burst) - lim.tokens)
|
||||||
|
elapsed := now.Sub(last)
|
||||||
|
if elapsed > maxElapsed {
|
||||||
|
elapsed = maxElapsed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the new number of tokens, due to time that passed.
|
||||||
|
delta := lim.limit.tokensFromDuration(elapsed)
|
||||||
|
tokens := lim.tokens + delta
|
||||||
|
if burst := float64(lim.burst); tokens > burst {
|
||||||
|
tokens = burst
|
||||||
|
}
|
||||||
|
|
||||||
|
return now, last, tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
// durationFromTokens is a unit conversion function from the number of tokens to the duration
|
||||||
|
// of time it takes to accumulate them at a rate of limit tokens per second.
|
||||||
|
func (limit Limit) durationFromTokens(tokens float64) time.Duration {
|
||||||
|
seconds := tokens / float64(limit)
|
||||||
|
return time.Nanosecond * time.Duration(1e9*seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tokensFromDuration is a unit conversion function from a time duration to the number of tokens
|
||||||
|
// which could be accumulated during that duration at a rate of limit tokens per second.
|
||||||
|
func (limit Limit) tokensFromDuration(d time.Duration) float64 {
|
||||||
|
return d.Seconds() * float64(limit)
|
||||||
|
}
|
27
vendor/rsc.io/letsencrypt/LICENSE
generated
vendored
Normal file
27
vendor/rsc.io/letsencrypt/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2009 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.
|
152
vendor/rsc.io/letsencrypt/README
generated
vendored
Normal file
152
vendor/rsc.io/letsencrypt/README
generated
vendored
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
package letsencrypt // import "rsc.io/letsencrypt"
|
||||||
|
|
||||||
|
Package letsencrypt obtains TLS certificates from LetsEncrypt.org.
|
||||||
|
|
||||||
|
LetsEncrypt.org is a service that issues free SSL/TLS certificates to
|
||||||
|
servers that can prove control over the given domain's DNS records or the
|
||||||
|
servers pointed at by those records.
|
||||||
|
|
||||||
|
|
||||||
|
Quick Start
|
||||||
|
|
||||||
|
A complete HTTP/HTTPS web server using TLS certificates from
|
||||||
|
LetsEncrypt.org, redirecting all HTTP access to HTTPS, and maintaining TLS
|
||||||
|
certificates in a file letsencrypt.cache across server restarts.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"rsc.io/letsencrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintf(w, "Hello, TLS!\n")
|
||||||
|
})
|
||||||
|
var m letsencrypt.Manager
|
||||||
|
if err := m.CacheFile("letsencrypt.cache"); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Fatal(m.Serve())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Overview
|
||||||
|
|
||||||
|
The fundamental type in this package is the Manager, which manages obtaining
|
||||||
|
and refreshing a collection of TLS certificates, typically for use by an
|
||||||
|
HTTPS server. The example above shows the most basic use of a Manager. The
|
||||||
|
use can be customized by calling additional methods of the Manager.
|
||||||
|
|
||||||
|
|
||||||
|
Registration
|
||||||
|
|
||||||
|
A Manager m registers anonymously with LetsEncrypt.org, including agreeing
|
||||||
|
to the letsencrypt.org terms of service, the first time it needs to obtain a
|
||||||
|
certificate. To register with a particular email address and with the option
|
||||||
|
of a prompt for agreement with the terms of service, call m.Register.
|
||||||
|
|
||||||
|
|
||||||
|
GetCertificate
|
||||||
|
|
||||||
|
The Manager's GetCertificate method returns certificates from the Manager's
|
||||||
|
cache, filling the cache by requesting certificates from LetsEncrypt.org. In
|
||||||
|
this way, a server with a tls.Config.GetCertificate set to m.GetCertificate
|
||||||
|
will demand load a certificate for any host name it serves. To force loading
|
||||||
|
of certificates ahead of time, install m.GetCertificate as before but then
|
||||||
|
call m.Cert for each host name.
|
||||||
|
|
||||||
|
A Manager can only obtain a certificate for a given host name if it can
|
||||||
|
prove control of that host name to LetsEncrypt.org. By default it proves
|
||||||
|
control by answering an HTTPS-based challenge: when the LetsEncrypt.org
|
||||||
|
servers connect to the named host on port 443 (HTTPS), the TLS SNI handshake
|
||||||
|
must use m.GetCertificate to obtain a per-host certificate. The most common
|
||||||
|
way to satisfy this requirement is for the host name to resolve to the IP
|
||||||
|
address of a (single) computer running m.ServeHTTPS, or at least running a
|
||||||
|
Go TLS server with tls.Config.GetCertificate set to m.GetCertificate.
|
||||||
|
However, other configurations are possible. For example, a group of machines
|
||||||
|
could use an implementation of tls.Config.GetCertificate that cached
|
||||||
|
certificates but handled cache misses by making RPCs to a Manager m on an
|
||||||
|
elected leader machine.
|
||||||
|
|
||||||
|
In typical usage, then, the setting of tls.Config.GetCertificate to
|
||||||
|
m.GetCertificate serves two purposes: it provides certificates to the TLS
|
||||||
|
server for ordinary serving, and it also answers challenges to prove
|
||||||
|
ownership of the domains in order to obtain those certificates.
|
||||||
|
|
||||||
|
To force the loading of a certificate for a given host into the Manager's
|
||||||
|
cache, use m.Cert.
|
||||||
|
|
||||||
|
|
||||||
|
Persistent Storage
|
||||||
|
|
||||||
|
If a server always starts with a zero Manager m, the server effectively
|
||||||
|
fetches a new certificate for each of its host name from LetsEncrypt.org on
|
||||||
|
each restart. This is unfortunate both because the server cannot start if
|
||||||
|
LetsEncrypt.org is unavailable and because LetsEncrypt.org limits how often
|
||||||
|
it will issue a certificate for a given host name (at time of writing, the
|
||||||
|
limit is 5 per week for a given host name). To save server state proactively
|
||||||
|
to a cache file and to reload the server state from that same file when
|
||||||
|
creating a new manager, call m.CacheFile with the name of the file to use.
|
||||||
|
|
||||||
|
For alternate storage uses, m.Marshal returns the current state of the
|
||||||
|
Manager as an opaque string, m.Unmarshal sets the state of the Manager using
|
||||||
|
a string previously returned by m.Marshal (usually a different m), and
|
||||||
|
m.Watch returns a channel that receives notifications about state changes.
|
||||||
|
|
||||||
|
|
||||||
|
Limits
|
||||||
|
|
||||||
|
To avoid hitting basic rate limits on LetsEncrypt.org, a given Manager
|
||||||
|
limits all its interactions to at most one request every minute, with an
|
||||||
|
initial allowed burst of 20 requests.
|
||||||
|
|
||||||
|
By default, if GetCertificate is asked for a certificate it does not have,
|
||||||
|
it will in turn ask LetsEncrypt.org for that certificate. This opens a
|
||||||
|
potential attack where attackers connect to a server by IP address and
|
||||||
|
pretend to be asking for an incorrect host name. Then GetCertificate will
|
||||||
|
attempt to obtain a certificate for that host, incorrectly, eventually
|
||||||
|
hitting LetsEncrypt.org's rate limit for certificate requests and making it
|
||||||
|
impossible to obtain actual certificates. Because servers hold certificates
|
||||||
|
for months at a time, however, an attack would need to be sustained over a
|
||||||
|
time period of at least a month in order to cause real problems.
|
||||||
|
|
||||||
|
To mitigate this kind of attack, a given Manager limits itself to an average
|
||||||
|
of one certificate request for a new host every three hours, with an initial
|
||||||
|
allowed burst of up to 20 requests. Long-running servers will therefore stay
|
||||||
|
within the LetsEncrypt.org limit of 300 failed requests per month.
|
||||||
|
Certificate refreshes are not subject to this limit.
|
||||||
|
|
||||||
|
To eliminate the attack entirely, call m.SetHosts to enumerate the exact set
|
||||||
|
of hosts that are allowed in certificate requests.
|
||||||
|
|
||||||
|
|
||||||
|
Web Servers
|
||||||
|
|
||||||
|
The basic requirement for use of a Manager is that there be an HTTPS server
|
||||||
|
running on port 443 and calling m.GetCertificate to obtain TLS certificates.
|
||||||
|
Using standard primitives, the way to do this is:
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: ":https",
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
GetCertificate: m.GetCertificate,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
srv.ListenAndServeTLS("", "")
|
||||||
|
|
||||||
|
However, this pattern of serving HTTPS with demand-loaded TLS certificates
|
||||||
|
comes up enough to wrap into a single method m.ServeHTTPS.
|
||||||
|
|
||||||
|
Similarly, many HTTPS servers prefer to redirect HTTP clients to the HTTPS
|
||||||
|
URLs. That functionality is provided by RedirectHTTP.
|
||||||
|
|
||||||
|
The combination of serving HTTPS with demand-loaded TLS certificates and
|
||||||
|
serving HTTPS redirects to HTTP clients is provided by m.Serve, as used in
|
||||||
|
the original example above.
|
||||||
|
|
||||||
|
func RedirectHTTP(w http.ResponseWriter, r *http.Request)
|
||||||
|
type Manager struct { ... }
|
753
vendor/rsc.io/letsencrypt/lets.go
generated
vendored
Normal file
753
vendor/rsc.io/letsencrypt/lets.go
generated
vendored
Normal file
|
@ -0,0 +1,753 @@
|
||||||
|
// Copyright 2016 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 letsencrypt obtains TLS certificates from LetsEncrypt.org.
|
||||||
|
//
|
||||||
|
// LetsEncrypt.org is a service that issues free SSL/TLS certificates to servers
|
||||||
|
// that can prove control over the given domain's DNS records or
|
||||||
|
// the servers pointed at by those records.
|
||||||
|
//
|
||||||
|
// Quick Start
|
||||||
|
//
|
||||||
|
// A complete HTTP/HTTPS web server using TLS certificates from LetsEncrypt.org,
|
||||||
|
// redirecting all HTTP access to HTTPS, and maintaining TLS certificates in a file
|
||||||
|
// letsencrypt.cache across server restarts.
|
||||||
|
//
|
||||||
|
// package main
|
||||||
|
//
|
||||||
|
// import (
|
||||||
|
// "fmt"
|
||||||
|
// "log"
|
||||||
|
// "net/http"
|
||||||
|
// "rsc.io/letsencrypt"
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// func main() {
|
||||||
|
// http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// fmt.Fprintf(w, "Hello, TLS!\n")
|
||||||
|
// })
|
||||||
|
// var m letsencrypt.Manager
|
||||||
|
// if err := m.CacheFile("letsencrypt.cache"); err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
// }
|
||||||
|
// log.Fatal(m.Serve())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Overview
|
||||||
|
//
|
||||||
|
// The fundamental type in this package is the Manager, which
|
||||||
|
// manages obtaining and refreshing a collection of TLS certificates,
|
||||||
|
// typically for use by an HTTPS server.
|
||||||
|
// The example above shows the most basic use of a Manager.
|
||||||
|
// The use can be customized by calling additional methods of the Manager.
|
||||||
|
//
|
||||||
|
// Registration
|
||||||
|
//
|
||||||
|
// A Manager m registers anonymously with LetsEncrypt.org, including agreeing to
|
||||||
|
// the letsencrypt.org terms of service, the first time it needs to obtain a certificate.
|
||||||
|
// To register with a particular email address and with the option of a
|
||||||
|
// prompt for agreement with the terms of service, call m.Register.
|
||||||
|
//
|
||||||
|
// GetCertificate
|
||||||
|
//
|
||||||
|
// The Manager's GetCertificate method returns certificates
|
||||||
|
// from the Manager's cache, filling the cache by requesting certificates
|
||||||
|
// from LetsEncrypt.org. In this way, a server with a tls.Config.GetCertificate
|
||||||
|
// set to m.GetCertificate will demand load a certificate for any host name
|
||||||
|
// it serves. To force loading of certificates ahead of time, install m.GetCertificate
|
||||||
|
// as before but then call m.Cert for each host name.
|
||||||
|
//
|
||||||
|
// A Manager can only obtain a certificate for a given host name if it can prove
|
||||||
|
// control of that host name to LetsEncrypt.org. By default it proves control by
|
||||||
|
// answering an HTTPS-based challenge: when
|
||||||
|
// the LetsEncrypt.org servers connect to the named host on port 443 (HTTPS),
|
||||||
|
// the TLS SNI handshake must use m.GetCertificate to obtain a per-host certificate.
|
||||||
|
// The most common way to satisfy this requirement is for the host name to
|
||||||
|
// resolve to the IP address of a (single) computer running m.ServeHTTPS,
|
||||||
|
// or at least running a Go TLS server with tls.Config.GetCertificate set to m.GetCertificate.
|
||||||
|
// However, other configurations are possible. For example, a group of machines
|
||||||
|
// could use an implementation of tls.Config.GetCertificate that cached
|
||||||
|
// certificates but handled cache misses by making RPCs to a Manager m
|
||||||
|
// on an elected leader machine.
|
||||||
|
//
|
||||||
|
// In typical usage, then, the setting of tls.Config.GetCertificate to m.GetCertificate
|
||||||
|
// serves two purposes: it provides certificates to the TLS server for ordinary serving,
|
||||||
|
// and it also answers challenges to prove ownership of the domains in order to
|
||||||
|
// obtain those certificates.
|
||||||
|
//
|
||||||
|
// To force the loading of a certificate for a given host into the Manager's cache,
|
||||||
|
// use m.Cert.
|
||||||
|
//
|
||||||
|
// Persistent Storage
|
||||||
|
//
|
||||||
|
// If a server always starts with a zero Manager m, the server effectively fetches
|
||||||
|
// a new certificate for each of its host name from LetsEncrypt.org on each restart.
|
||||||
|
// This is unfortunate both because the server cannot start if LetsEncrypt.org is
|
||||||
|
// unavailable and because LetsEncrypt.org limits how often it will issue a certificate
|
||||||
|
// for a given host name (at time of writing, the limit is 5 per week for a given host name).
|
||||||
|
// To save server state proactively to a cache file and to reload the server state from
|
||||||
|
// that same file when creating a new manager, call m.CacheFile with the name of
|
||||||
|
// the file to use.
|
||||||
|
//
|
||||||
|
// For alternate storage uses, m.Marshal returns the current state of the Manager
|
||||||
|
// as an opaque string, m.Unmarshal sets the state of the Manager using a string
|
||||||
|
// previously returned by m.Marshal (usually a different m), and m.Watch returns
|
||||||
|
// a channel that receives notifications about state changes.
|
||||||
|
//
|
||||||
|
// Limits
|
||||||
|
//
|
||||||
|
// To avoid hitting basic rate limits on LetsEncrypt.org, a given Manager limits all its
|
||||||
|
// interactions to at most one request every minute, with an initial allowed burst of
|
||||||
|
// 20 requests.
|
||||||
|
//
|
||||||
|
// By default, if GetCertificate is asked for a certificate it does not have, it will in turn
|
||||||
|
// ask LetsEncrypt.org for that certificate. This opens a potential attack where attackers
|
||||||
|
// connect to a server by IP address and pretend to be asking for an incorrect host name.
|
||||||
|
// Then GetCertificate will attempt to obtain a certificate for that host, incorrectly,
|
||||||
|
// eventually hitting LetsEncrypt.org's rate limit for certificate requests and making it
|
||||||
|
// impossible to obtain actual certificates. Because servers hold certificates for months
|
||||||
|
// at a time, however, an attack would need to be sustained over a time period
|
||||||
|
// of at least a month in order to cause real problems.
|
||||||
|
//
|
||||||
|
// To mitigate this kind of attack, a given Manager limits
|
||||||
|
// itself to an average of one certificate request for a new host every three hours,
|
||||||
|
// with an initial allowed burst of up to 20 requests.
|
||||||
|
// Long-running servers will therefore stay
|
||||||
|
// within the LetsEncrypt.org limit of 300 failed requests per month.
|
||||||
|
// Certificate refreshes are not subject to this limit.
|
||||||
|
//
|
||||||
|
// To eliminate the attack entirely, call m.SetHosts to enumerate the exact set
|
||||||
|
// of hosts that are allowed in certificate requests.
|
||||||
|
//
|
||||||
|
// Web Servers
|
||||||
|
//
|
||||||
|
// The basic requirement for use of a Manager is that there be an HTTPS server
|
||||||
|
// running on port 443 and calling m.GetCertificate to obtain TLS certificates.
|
||||||
|
// Using standard primitives, the way to do this is:
|
||||||
|
//
|
||||||
|
// srv := &http.Server{
|
||||||
|
// Addr: ":https",
|
||||||
|
// TLSConfig: &tls.Config{
|
||||||
|
// GetCertificate: m.GetCertificate,
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
// srv.ListenAndServeTLS("", "")
|
||||||
|
//
|
||||||
|
// However, this pattern of serving HTTPS with demand-loaded TLS certificates
|
||||||
|
// comes up enough to wrap into a single method m.ServeHTTPS.
|
||||||
|
//
|
||||||
|
// Similarly, many HTTPS servers prefer to redirect HTTP clients to the HTTPS URLs.
|
||||||
|
// That functionality is provided by RedirectHTTP.
|
||||||
|
//
|
||||||
|
// The combination of serving HTTPS with demand-loaded TLS certificates and
|
||||||
|
// serving HTTPS redirects to HTTP clients is provided by m.Serve, as used in
|
||||||
|
// the original example above.
|
||||||
|
//
|
||||||
|
package letsencrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"golang.org/x/time/rate"
|
||||||
|
|
||||||
|
"github.com/xenolf/lego/acme"
|
||||||
|
)
|
||||||
|
|
||||||
|
const letsEncryptURL = "https://acme-v01.api.letsencrypt.org/directory"
|
||||||
|
const debug = false
|
||||||
|
|
||||||
|
// A Manager m takes care of obtaining and refreshing a collection of TLS certificates
|
||||||
|
// obtained by LetsEncrypt.org.
|
||||||
|
// The zero Manager is not yet registered with LetsEncrypt.org and has no TLS certificates
|
||||||
|
// but is nonetheless ready for use.
|
||||||
|
// See the package comment for an overview of how to use a Manager.
|
||||||
|
type Manager struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
state state
|
||||||
|
rateLimit *rate.Limiter
|
||||||
|
newHostLimit *rate.Limiter
|
||||||
|
certCache map[string]*cacheEntry
|
||||||
|
certTokens map[string]*tls.Certificate
|
||||||
|
watchChan chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serve runs an HTTP/HTTPS web server using TLS certificates obtained by the manager.
|
||||||
|
// The HTTP server redirects all requests to the HTTPS server.
|
||||||
|
// The HTTPS server obtains TLS certificates as needed and responds to requests
|
||||||
|
// by invoking http.DefaultServeMux.
|
||||||
|
//
|
||||||
|
// Serve does not return unitil the HTTPS server fails to start or else stops.
|
||||||
|
// Either way, Serve can only return a non-nil error, never nil.
|
||||||
|
func (m *Manager) Serve() error {
|
||||||
|
l, err := net.Listen("tcp", ":http")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
go http.Serve(l, http.HandlerFunc(RedirectHTTP))
|
||||||
|
|
||||||
|
return m.ServeHTTPS()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTPS runs an HTTPS web server using TLS certificates obtained by the manager.
|
||||||
|
// The HTTPS server obtains TLS certificates as needed and responds to requests
|
||||||
|
// by invoking http.DefaultServeMux.
|
||||||
|
// ServeHTTPS does not return unitil the HTTPS server fails to start or else stops.
|
||||||
|
// Either way, ServeHTTPS can only return a non-nil error, never nil.
|
||||||
|
func (m *Manager) ServeHTTPS() error {
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: ":https",
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
GetCertificate: m.GetCertificate,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return srv.ListenAndServeTLS("", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RedirectHTTP is an HTTP handler (suitable for use with http.HandleFunc)
|
||||||
|
// that responds to all requests by redirecting to the same URL served over HTTPS.
|
||||||
|
// It should only be invoked for requests received over HTTP.
|
||||||
|
func RedirectHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.TLS != nil || r.Host == "" {
|
||||||
|
http.Error(w, "not found", 404)
|
||||||
|
}
|
||||||
|
|
||||||
|
u := r.URL
|
||||||
|
u.Host = r.Host
|
||||||
|
u.Scheme = "https"
|
||||||
|
http.Redirect(w, r, u.String(), 302)
|
||||||
|
}
|
||||||
|
|
||||||
|
// state is the serializable state for the Manager.
|
||||||
|
// It also implements acme.User.
|
||||||
|
type state struct {
|
||||||
|
Email string
|
||||||
|
Reg *acme.RegistrationResource
|
||||||
|
Key string
|
||||||
|
key *ecdsa.PrivateKey
|
||||||
|
Hosts []string
|
||||||
|
Certs map[string]stateCert
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *state) GetEmail() string { return s.Email }
|
||||||
|
func (s *state) GetRegistration() *acme.RegistrationResource { return s.Reg }
|
||||||
|
func (s *state) GetPrivateKey() crypto.PrivateKey { return s.key }
|
||||||
|
|
||||||
|
type stateCert struct {
|
||||||
|
Cert string
|
||||||
|
Key string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cert stateCert) toTLS() (*tls.Certificate, error) {
|
||||||
|
c, err := tls.X509KeyPair([]byte(cert.Cert), []byte(cert.Key))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &c, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type cacheEntry struct {
|
||||||
|
host string
|
||||||
|
m *Manager
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
cert *tls.Certificate
|
||||||
|
timeout time.Time
|
||||||
|
refreshing bool
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) init() {
|
||||||
|
m.mu.Lock()
|
||||||
|
if m.certCache == nil {
|
||||||
|
m.rateLimit = rate.NewLimiter(rate.Every(1*time.Minute), 20)
|
||||||
|
m.newHostLimit = rate.NewLimiter(rate.Every(3*time.Hour), 20)
|
||||||
|
m.certCache = map[string]*cacheEntry{}
|
||||||
|
m.certTokens = map[string]*tls.Certificate{}
|
||||||
|
m.watchChan = make(chan struct{}, 1)
|
||||||
|
m.watchChan <- struct{}{}
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch returns the manager's watch channel,
|
||||||
|
// which delivers a notification after every time the
|
||||||
|
// manager's state (as exposed by Marshal and Unmarshal) changes.
|
||||||
|
// All calls to Watch return the same watch channel.
|
||||||
|
//
|
||||||
|
// The watch channel includes notifications about changes
|
||||||
|
// before the first call to Watch, so that in the pattern below,
|
||||||
|
// the range loop executes once immediately, saving
|
||||||
|
// the result of setup (along with any background updates that
|
||||||
|
// may have raced in quickly).
|
||||||
|
//
|
||||||
|
// m := new(letsencrypt.Manager)
|
||||||
|
// setup(m)
|
||||||
|
// go backgroundUpdates(m)
|
||||||
|
// for range m.Watch() {
|
||||||
|
// save(m.Marshal())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
func (m *Manager) Watch() <-chan struct{} {
|
||||||
|
m.init()
|
||||||
|
m.updated()
|
||||||
|
return m.watchChan
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) updated() {
|
||||||
|
select {
|
||||||
|
case m.watchChan <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) CacheFile(name string) error {
|
||||||
|
f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
data, err := ioutil.ReadFile(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(data) > 0 {
|
||||||
|
if err := m.Unmarshal(string(data)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for range m.Watch() {
|
||||||
|
err := ioutil.WriteFile(name, []byte(m.Marshal()), 0600)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("writing letsencrypt cache: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registered reports whether the manager has registered with letsencrypt.org yet.
|
||||||
|
func (m *Manager) Registered() bool {
|
||||||
|
m.init()
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
return m.registered()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) registered() bool {
|
||||||
|
return m.state.Reg != nil && m.state.Reg.Body.Agreement != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register registers the manager with letsencrypt.org, using the given email address.
|
||||||
|
// Registration may require agreeing to the letsencrypt.org terms of service.
|
||||||
|
// If so, Register calls prompt(url) where url is the URL of the terms of service.
|
||||||
|
// Prompt should report whether the caller agrees to the terms.
|
||||||
|
// A nil prompt func is taken to mean that the user always agrees.
|
||||||
|
// The email address is sent to LetsEncrypt.org but otherwise unchecked;
|
||||||
|
// it can be omitted by passing the empty string.
|
||||||
|
//
|
||||||
|
// Calling Register is only required to make sure registration uses a
|
||||||
|
// particular email address or to insert an explicit prompt into the
|
||||||
|
// registration sequence. If the manager is not registered, it will
|
||||||
|
// automatically register with no email address and automatic
|
||||||
|
// agreement to the terms of service at the first call to Cert or GetCertificate.
|
||||||
|
func (m *Manager) Register(email string, prompt func(string) bool) error {
|
||||||
|
m.init()
|
||||||
|
m.mu.Lock()
|
||||||
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
|
return m.register(email, prompt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) register(email string, prompt func(string) bool) error {
|
||||||
|
if m.registered() {
|
||||||
|
return fmt.Errorf("already registered")
|
||||||
|
}
|
||||||
|
m.state.Email = email
|
||||||
|
if m.state.key == nil {
|
||||||
|
key, err := newKey()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("generating key: %v", err)
|
||||||
|
}
|
||||||
|
Key, err := marshalKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("generating key: %v", err)
|
||||||
|
}
|
||||||
|
m.state.key = key
|
||||||
|
m.state.Key = string(Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := acme.NewClient(letsEncryptURL, &m.state, acme.EC256)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create client: %v", err)
|
||||||
|
}
|
||||||
|
reg, err := c.Register()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("register: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.state.Reg = reg
|
||||||
|
if reg.Body.Agreement == "" {
|
||||||
|
if prompt != nil && !prompt(reg.TosURL) {
|
||||||
|
return fmt.Errorf("did not agree to TOS")
|
||||||
|
}
|
||||||
|
if err := c.AgreeToTOS(); err != nil {
|
||||||
|
return fmt.Errorf("agreeing to TOS: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m.updated()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal returns an encoding of the manager's state,
|
||||||
|
// suitable for writing to disk and reloading by calling Unmarshal.
|
||||||
|
// The state includes registration status, the configured host list
|
||||||
|
// from SetHosts, and all known certificates, including their private
|
||||||
|
// cryptographic keys.
|
||||||
|
// Consequently, the state should be kept private.
|
||||||
|
func (m *Manager) Marshal() string {
|
||||||
|
m.init()
|
||||||
|
js, err := json.MarshalIndent(&m.state, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
panic("unexpected json.Marshal failure")
|
||||||
|
}
|
||||||
|
return string(js)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal restores the state encoded by a previous call to Marshal
|
||||||
|
// (perhaps on a different Manager in a different program).
|
||||||
|
func (m *Manager) Unmarshal(enc string) error {
|
||||||
|
m.init()
|
||||||
|
var st state
|
||||||
|
if err := json.Unmarshal([]byte(enc), &st); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if st.Key != "" {
|
||||||
|
key, err := unmarshalKey(st.Key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
st.key = key
|
||||||
|
}
|
||||||
|
m.state = st
|
||||||
|
for host, cert := range m.state.Certs {
|
||||||
|
c, err := cert.toTLS()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("letsencrypt: ignoring entry for %s: %v", host, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m.certCache[host] = &cacheEntry{host: host, m: m, cert: c}
|
||||||
|
}
|
||||||
|
m.updated()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHosts sets the manager's list of known host names.
|
||||||
|
// If the list is non-nil, the manager will only ever attempt to acquire
|
||||||
|
// certificates for host names on the list.
|
||||||
|
// If the list is nil, the manager does not restrict the hosts it will
|
||||||
|
// ask for certificates for.
|
||||||
|
func (m *Manager) SetHosts(hosts []string) {
|
||||||
|
m.init()
|
||||||
|
m.mu.Lock()
|
||||||
|
m.state.Hosts = append(m.state.Hosts[:0], hosts...)
|
||||||
|
m.mu.Unlock()
|
||||||
|
m.updated()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCertificate can be placed a tls.Config's GetCertificate field to make
|
||||||
|
// the TLS server use Let's Encrypt certificates.
|
||||||
|
// Each time a client connects to the TLS server expecting a new host name,
|
||||||
|
// the TLS server's call to GetCertificate will trigger an exchange with the
|
||||||
|
// Let's Encrypt servers to obtain that certificate, subject to the manager rate limits.
|
||||||
|
//
|
||||||
|
// As noted in the Manager's documentation comment,
|
||||||
|
// to obtain a certificate for a given host name, that name
|
||||||
|
// must resolve to a computer running a TLS server on port 443
|
||||||
|
// that obtains TLS SNI certificates by calling m.GetCertificate.
|
||||||
|
// In the standard usage, then, installing m.GetCertificate in the tls.Config
|
||||||
|
// both automatically provisions the TLS certificates needed for
|
||||||
|
// ordinary HTTPS service and answers the challenges from LetsEncrypt.org.
|
||||||
|
func (m *Manager) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
|
m.init()
|
||||||
|
|
||||||
|
host := clientHello.ServerName
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
log.Printf("GetCertificate %s", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(host, ".acme.invalid") {
|
||||||
|
m.mu.Lock()
|
||||||
|
cert := m.certTokens[host]
|
||||||
|
m.mu.Unlock()
|
||||||
|
if cert == nil {
|
||||||
|
return nil, fmt.Errorf("unknown host")
|
||||||
|
}
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.Cert(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cert returns the certificate for the given host name, obtaining a new one if necessary.
|
||||||
|
//
|
||||||
|
// As noted in the documentation for Manager and for the GetCertificate method,
|
||||||
|
// obtaining a certificate requires that m.GetCertificate be associated with host.
|
||||||
|
// In most servers, simply starting a TLS server with a configuration referring
|
||||||
|
// to m.GetCertificate is sufficient, and Cert need not be called.
|
||||||
|
//
|
||||||
|
// The main use of Cert is to force the manager to obtain a certificate
|
||||||
|
// for a particular host name ahead of time.
|
||||||
|
func (m *Manager) Cert(host string) (*tls.Certificate, error) {
|
||||||
|
host = strings.ToLower(host)
|
||||||
|
if debug {
|
||||||
|
log.Printf("Cert %s", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.init()
|
||||||
|
m.mu.Lock()
|
||||||
|
if !m.registered() {
|
||||||
|
m.register("", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := false
|
||||||
|
if m.state.Hosts == nil {
|
||||||
|
ok = true
|
||||||
|
} else {
|
||||||
|
for _, h := range m.state.Hosts {
|
||||||
|
if host == h {
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
m.mu.Unlock()
|
||||||
|
return nil, fmt.Errorf("unknown host")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise look in our cert cache.
|
||||||
|
entry, ok := m.certCache[host]
|
||||||
|
if !ok {
|
||||||
|
r := m.rateLimit.Reserve()
|
||||||
|
ok := r.OK()
|
||||||
|
if ok {
|
||||||
|
ok = m.newHostLimit.Allow()
|
||||||
|
if !ok {
|
||||||
|
r.Cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
m.mu.Unlock()
|
||||||
|
return nil, fmt.Errorf("rate limited")
|
||||||
|
}
|
||||||
|
entry = &cacheEntry{host: host, m: m}
|
||||||
|
m.certCache[host] = entry
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
entry.mu.Lock()
|
||||||
|
defer entry.mu.Unlock()
|
||||||
|
entry.init()
|
||||||
|
if entry.err != nil {
|
||||||
|
return nil, entry.err
|
||||||
|
}
|
||||||
|
return entry.cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cacheEntry) init() {
|
||||||
|
if e.err != nil && time.Now().Before(e.timeout) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e.cert != nil {
|
||||||
|
if e.timeout.IsZero() {
|
||||||
|
t, err := certRefreshTime(e.cert)
|
||||||
|
if err != nil {
|
||||||
|
e.err = err
|
||||||
|
e.timeout = time.Now().Add(1 * time.Minute)
|
||||||
|
e.cert = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e.timeout = t
|
||||||
|
}
|
||||||
|
if time.Now().After(e.timeout) && !e.refreshing {
|
||||||
|
e.refreshing = true
|
||||||
|
go e.refresh()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, refreshTime, err := e.m.verify(e.host)
|
||||||
|
e.m.mu.Lock()
|
||||||
|
e.m.certCache[e.host] = e
|
||||||
|
e.m.mu.Unlock()
|
||||||
|
e.install(cert, refreshTime, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cacheEntry) install(cert *tls.Certificate, refreshTime time.Time, err error) {
|
||||||
|
e.cert = nil
|
||||||
|
e.timeout = time.Time{}
|
||||||
|
e.err = nil
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
e.err = err
|
||||||
|
e.timeout = time.Now().Add(1 * time.Minute)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.cert = cert
|
||||||
|
e.timeout = refreshTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *cacheEntry) refresh() {
|
||||||
|
e.m.rateLimit.Wait(context.Background())
|
||||||
|
cert, refreshTime, err := e.m.verify(e.host)
|
||||||
|
|
||||||
|
e.mu.Lock()
|
||||||
|
defer e.mu.Unlock()
|
||||||
|
e.refreshing = false
|
||||||
|
if err == nil {
|
||||||
|
e.install(cert, refreshTime, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) verify(host string) (cert *tls.Certificate, refreshTime time.Time, err error) {
|
||||||
|
c, err := acme.NewClient(letsEncryptURL, &m.state, acme.EC256)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = c.SetChallengeProvider(acme.TLSSNI01, tlsProvider{m}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.SetChallengeProvider(acme.TLSSNI01, tlsProvider{m})
|
||||||
|
c.ExcludeChallenges([]acme.Challenge{acme.HTTP01})
|
||||||
|
acmeCert, errmap := c.ObtainCertificate([]string{host}, true, nil)
|
||||||
|
if len(errmap) > 0 {
|
||||||
|
if debug {
|
||||||
|
log.Printf("ObtainCertificate %v => %v", host, errmap)
|
||||||
|
}
|
||||||
|
err = fmt.Errorf("%v", errmap)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
entryCert := stateCert{
|
||||||
|
Cert: string(acmeCert.Certificate),
|
||||||
|
Key: string(acmeCert.PrivateKey),
|
||||||
|
}
|
||||||
|
cert, err = entryCert.toTLS()
|
||||||
|
if err != nil {
|
||||||
|
if debug {
|
||||||
|
log.Printf("ObtainCertificate %v toTLS failure: %v", host, err)
|
||||||
|
}
|
||||||
|
err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if refreshTime, err = certRefreshTime(cert); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
if m.state.Certs == nil {
|
||||||
|
m.state.Certs = make(map[string]stateCert)
|
||||||
|
}
|
||||||
|
m.state.Certs[host] = entryCert
|
||||||
|
m.mu.Unlock()
|
||||||
|
m.updated()
|
||||||
|
|
||||||
|
return cert, refreshTime, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func certRefreshTime(cert *tls.Certificate) (time.Time, error) {
|
||||||
|
xc, err := x509.ParseCertificate(cert.Certificate[0])
|
||||||
|
if err != nil {
|
||||||
|
if debug {
|
||||||
|
log.Printf("ObtainCertificate to X.509 failure: %v", err)
|
||||||
|
}
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
t := xc.NotBefore.Add(xc.NotAfter.Sub(xc.NotBefore) / 2)
|
||||||
|
monthEarly := xc.NotAfter.Add(-30 * 24 * time.Hour)
|
||||||
|
if t.Before(monthEarly) {
|
||||||
|
t = monthEarly
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// tlsProvider implements acme.ChallengeProvider for TLS handshake challenges.
|
||||||
|
type tlsProvider struct {
|
||||||
|
m *Manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p tlsProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
cert, dom, err := acme.TLSSNI01ChallengeCertDomain(keyAuth)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.m.mu.Lock()
|
||||||
|
p.m.certTokens[dom] = &cert
|
||||||
|
p.m.mu.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p tlsProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
_, dom, err := acme.TLSSNI01ChallengeCertDomain(keyAuth)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.m.mu.Lock()
|
||||||
|
delete(p.m.certTokens, dom)
|
||||||
|
p.m.mu.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalKey(key *ecdsa.PrivateKey) ([]byte, error) {
|
||||||
|
data, err := x509.MarshalECPrivateKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: data}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalKey(text string) (*ecdsa.PrivateKey, error) {
|
||||||
|
b, _ := pem.Decode([]byte(text))
|
||||||
|
if b == nil {
|
||||||
|
return nil, fmt.Errorf("unmarshalKey: missing key")
|
||||||
|
}
|
||||||
|
if b.Type != "EC PRIVATE KEY" {
|
||||||
|
return nil, fmt.Errorf("unmarshalKey: found %q, not %q", b.Type, "EC PRIVATE KEY")
|
||||||
|
}
|
||||||
|
k, err := x509.ParseECPrivateKey(b.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshalKey: %v", err)
|
||||||
|
}
|
||||||
|
return k, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newKey() (*ecdsa.PrivateKey, error) {
|
||||||
|
return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||||
|
}
|
21
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/LICENSE
generated
vendored
Normal file
21
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Sebastian Erhart
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
16
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/challenges.go
generated
vendored
Normal file
16
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/challenges.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
// Challenge is a string that identifies a particular type and version of ACME challenge.
|
||||||
|
type Challenge string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// HTTP01 is the "http-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#http
|
||||||
|
// Note: HTTP01ChallengePath returns the URL path to fulfill this challenge
|
||||||
|
HTTP01 = Challenge("http-01")
|
||||||
|
// TLSSNI01 is the "tls-sni-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#tls-with-server-name-indication-tls-sni
|
||||||
|
// Note: TLSSNI01ChallengeCert returns a certificate to fulfill this challenge
|
||||||
|
TLSSNI01 = Challenge("tls-sni-01")
|
||||||
|
// DNS01 is the "dns-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#dns
|
||||||
|
// Note: DNS01Record returns a DNS record which will fulfill this challenge
|
||||||
|
DNS01 = Challenge("dns-01")
|
||||||
|
)
|
638
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
Normal file
638
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
Normal file
|
@ -0,0 +1,638 @@
|
||||||
|
// Package acme implements the ACME protocol for Let's Encrypt and other conforming providers.
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Logger is an optional custom logger.
|
||||||
|
Logger *log.Logger
|
||||||
|
)
|
||||||
|
|
||||||
|
// logf writes a log entry. It uses Logger if not
|
||||||
|
// nil, otherwise it uses the default log.Logger.
|
||||||
|
func logf(format string, args ...interface{}) {
|
||||||
|
if Logger != nil {
|
||||||
|
Logger.Printf(format, args...)
|
||||||
|
} else {
|
||||||
|
log.Printf(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// User interface is to be implemented by users of this library.
|
||||||
|
// It is used by the client type to get user specific information.
|
||||||
|
type User interface {
|
||||||
|
GetEmail() string
|
||||||
|
GetRegistration() *RegistrationResource
|
||||||
|
GetPrivateKey() crypto.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface for all challenge solvers to implement.
|
||||||
|
type solver interface {
|
||||||
|
Solve(challenge challenge, domain string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type validateFunc func(j *jws, domain, uri string, chlng challenge) error
|
||||||
|
|
||||||
|
// Client is the user-friendy way to ACME
|
||||||
|
type Client struct {
|
||||||
|
directory directory
|
||||||
|
user User
|
||||||
|
jws *jws
|
||||||
|
keyType KeyType
|
||||||
|
issuerCert []byte
|
||||||
|
solvers map[Challenge]solver
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient creates a new ACME client on behalf of the user. The client will depend on
|
||||||
|
// the ACME directory located at caDirURL for the rest of its actions. It will
|
||||||
|
// generate private keys for certificates of size keyBits.
|
||||||
|
func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) {
|
||||||
|
privKey := user.GetPrivateKey()
|
||||||
|
if privKey == nil {
|
||||||
|
return nil, errors.New("private key was nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
var dir directory
|
||||||
|
if _, err := getJSON(caDirURL, &dir); err != nil {
|
||||||
|
return nil, fmt.Errorf("get directory at '%s': %v", caDirURL, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dir.NewRegURL == "" {
|
||||||
|
return nil, errors.New("directory missing new registration URL")
|
||||||
|
}
|
||||||
|
if dir.NewAuthzURL == "" {
|
||||||
|
return nil, errors.New("directory missing new authz URL")
|
||||||
|
}
|
||||||
|
if dir.NewCertURL == "" {
|
||||||
|
return nil, errors.New("directory missing new certificate URL")
|
||||||
|
}
|
||||||
|
if dir.RevokeCertURL == "" {
|
||||||
|
return nil, errors.New("directory missing revoke certificate URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
jws := &jws{privKey: privKey, directoryURL: caDirURL}
|
||||||
|
|
||||||
|
// REVIEW: best possibility?
|
||||||
|
// Add all available solvers with the right index as per ACME
|
||||||
|
// spec to this map. Otherwise they won`t be found.
|
||||||
|
solvers := make(map[Challenge]solver)
|
||||||
|
solvers[HTTP01] = &httpChallenge{jws: jws, validate: validate, provider: &HTTPProviderServer{}}
|
||||||
|
solvers[TLSSNI01] = &tlsSNIChallenge{jws: jws, validate: validate, provider: &TLSProviderServer{}}
|
||||||
|
|
||||||
|
return &Client{directory: dir, user: user, jws: jws, keyType: keyType, solvers: solvers}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetChallengeProvider specifies a custom provider that will make the solution available
|
||||||
|
func (c *Client) SetChallengeProvider(challenge Challenge, p ChallengeProvider) error {
|
||||||
|
switch challenge {
|
||||||
|
case HTTP01:
|
||||||
|
c.solvers[challenge] = &httpChallenge{jws: c.jws, validate: validate, provider: p}
|
||||||
|
case TLSSNI01:
|
||||||
|
c.solvers[challenge] = &tlsSNIChallenge{jws: c.jws, validate: validate, provider: p}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Unknown challenge %v", challenge)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHTTPAddress specifies a custom interface:port to be used for HTTP based challenges.
|
||||||
|
// If this option is not used, the default port 80 and all interfaces will be used.
|
||||||
|
// To only specify a port and no interface use the ":port" notation.
|
||||||
|
func (c *Client) SetHTTPAddress(iface string) error {
|
||||||
|
host, port, err := net.SplitHostPort(iface)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if chlng, ok := c.solvers[HTTP01]; ok {
|
||||||
|
chlng.(*httpChallenge).provider = NewHTTPProviderServer(host, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTLSAddress specifies a custom interface:port to be used for TLS based challenges.
|
||||||
|
// If this option is not used, the default port 443 and all interfaces will be used.
|
||||||
|
// To only specify a port and no interface use the ":port" notation.
|
||||||
|
func (c *Client) SetTLSAddress(iface string) error {
|
||||||
|
host, port, err := net.SplitHostPort(iface)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if chlng, ok := c.solvers[TLSSNI01]; ok {
|
||||||
|
chlng.(*tlsSNIChallenge).provider = NewTLSProviderServer(host, port)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExcludeChallenges explicitly removes challenges from the pool for solving.
|
||||||
|
func (c *Client) ExcludeChallenges(challenges []Challenge) {
|
||||||
|
// Loop through all challenges and delete the requested one if found.
|
||||||
|
for _, challenge := range challenges {
|
||||||
|
delete(c.solvers, challenge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the current account to the ACME server.
|
||||||
|
func (c *Client) Register() (*RegistrationResource, error) {
|
||||||
|
if c == nil || c.user == nil {
|
||||||
|
return nil, errors.New("acme: cannot register a nil client or user")
|
||||||
|
}
|
||||||
|
logf("[INFO] acme: Registering account for %s", c.user.GetEmail())
|
||||||
|
|
||||||
|
regMsg := registrationMessage{
|
||||||
|
Resource: "new-reg",
|
||||||
|
}
|
||||||
|
if c.user.GetEmail() != "" {
|
||||||
|
regMsg.Contact = []string{"mailto:" + c.user.GetEmail()}
|
||||||
|
} else {
|
||||||
|
regMsg.Contact = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverReg Registration
|
||||||
|
hdr, err := postJSON(c.jws, c.directory.NewRegURL, regMsg, &serverReg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reg := &RegistrationResource{Body: serverReg}
|
||||||
|
|
||||||
|
links := parseLinks(hdr["Link"])
|
||||||
|
reg.URI = hdr.Get("Location")
|
||||||
|
if links["terms-of-service"] != "" {
|
||||||
|
reg.TosURL = links["terms-of-service"]
|
||||||
|
}
|
||||||
|
|
||||||
|
if links["next"] != "" {
|
||||||
|
reg.NewAuthzURL = links["next"]
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("acme: The server did not return 'next' link to proceed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return reg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgreeToTOS updates the Client registration and sends the agreement to
|
||||||
|
// the server.
|
||||||
|
func (c *Client) AgreeToTOS() error {
|
||||||
|
reg := c.user.GetRegistration()
|
||||||
|
|
||||||
|
reg.Body.Agreement = c.user.GetRegistration().TosURL
|
||||||
|
reg.Body.Resource = "reg"
|
||||||
|
_, err := postJSON(c.jws, c.user.GetRegistration().URI, c.user.GetRegistration().Body, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObtainCertificate tries to obtain a single certificate using all domains passed into it.
|
||||||
|
// The first domain in domains is used for the CommonName field of the certificate, all other
|
||||||
|
// domains are added using the Subject Alternate Names extension. A new private key is generated
|
||||||
|
// for every invocation of this function. If you do not want that you can supply your own private key
|
||||||
|
// in the privKey parameter. If this parameter is non-nil it will be used instead of generating a new one.
|
||||||
|
// If bundle is true, the []byte contains both the issuer certificate and
|
||||||
|
// your issued certificate as a bundle.
|
||||||
|
// This function will never return a partial certificate. If one domain in the list fails,
|
||||||
|
// the whole certificate will fail.
|
||||||
|
func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey) (CertificateResource, map[string]error) {
|
||||||
|
if bundle {
|
||||||
|
logf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
|
||||||
|
} else {
|
||||||
|
logf("[INFO][%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
challenges, failures := c.getChallenges(domains)
|
||||||
|
// If any challenge fails - return. Do not generate partial SAN certificates.
|
||||||
|
if len(failures) > 0 {
|
||||||
|
return CertificateResource{}, failures
|
||||||
|
}
|
||||||
|
|
||||||
|
errs := c.solveChallenges(challenges)
|
||||||
|
// If any challenge fails - return. Do not generate partial SAN certificates.
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return CertificateResource{}, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||||
|
|
||||||
|
cert, err := c.requestCertificate(challenges, bundle, privKey)
|
||||||
|
if err != nil {
|
||||||
|
for _, chln := range challenges {
|
||||||
|
failures[chln.Domain] = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cert, failures
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevokeCertificate takes a PEM encoded certificate or bundle and tries to revoke it at the CA.
|
||||||
|
func (c *Client) RevokeCertificate(certificate []byte) error {
|
||||||
|
certificates, err := parsePEMBundle(certificate)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
x509Cert := certificates[0]
|
||||||
|
if x509Cert.IsCA {
|
||||||
|
return fmt.Errorf("Certificate bundle starts with a CA certificate")
|
||||||
|
}
|
||||||
|
|
||||||
|
encodedCert := base64.URLEncoding.EncodeToString(x509Cert.Raw)
|
||||||
|
|
||||||
|
_, err = postJSON(c.jws, c.directory.RevokeCertURL, revokeCertMessage{Resource: "revoke-cert", Certificate: encodedCert}, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenewCertificate takes a CertificateResource and tries to renew the certificate.
|
||||||
|
// If the renewal process succeeds, the new certificate will ge returned in a new CertResource.
|
||||||
|
// Please be aware that this function will return a new certificate in ANY case that is not an error.
|
||||||
|
// If the server does not provide us with a new cert on a GET request to the CertURL
|
||||||
|
// this function will start a new-cert flow where a new certificate gets generated.
|
||||||
|
// If bundle is true, the []byte contains both the issuer certificate and
|
||||||
|
// your issued certificate as a bundle.
|
||||||
|
// For private key reuse the PrivateKey property of the passed in CertificateResource should be non-nil.
|
||||||
|
func (c *Client) RenewCertificate(cert CertificateResource, bundle bool) (CertificateResource, error) {
|
||||||
|
// Input certificate is PEM encoded. Decode it here as we may need the decoded
|
||||||
|
// cert later on in the renewal process. The input may be a bundle or a single certificate.
|
||||||
|
certificates, err := parsePEMBundle(cert.Certificate)
|
||||||
|
if err != nil {
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
x509Cert := certificates[0]
|
||||||
|
if x509Cert.IsCA {
|
||||||
|
return CertificateResource{}, fmt.Errorf("[%s] Certificate bundle starts with a CA certificate", cert.Domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is just meant to be informal for the user.
|
||||||
|
timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC())
|
||||||
|
logf("[INFO][%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours()))
|
||||||
|
|
||||||
|
// The first step of renewal is to check if we get a renewed cert
|
||||||
|
// directly from the cert URL.
|
||||||
|
resp, err := httpGet(cert.CertURL)
|
||||||
|
if err != nil {
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
serverCertBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
serverCert, err := x509.ParseCertificate(serverCertBytes)
|
||||||
|
if err != nil {
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the server responds with a different certificate we are effectively renewed.
|
||||||
|
// TODO: Further test if we can actually use the new certificate (Our private key works)
|
||||||
|
if !x509Cert.Equal(serverCert) {
|
||||||
|
logf("[INFO][%s] acme: Server responded with renewed certificate", cert.Domain)
|
||||||
|
issuedCert := pemEncode(derCertificateBytes(serverCertBytes))
|
||||||
|
// If bundle is true, we want to return a certificate bundle.
|
||||||
|
// To do this, we need the issuer certificate.
|
||||||
|
if bundle {
|
||||||
|
// The issuer certificate link is always supplied via an "up" link
|
||||||
|
// in the response headers of a new certificate.
|
||||||
|
links := parseLinks(resp.Header["Link"])
|
||||||
|
issuerCert, err := c.getIssuerCertificate(links["up"])
|
||||||
|
if err != nil {
|
||||||
|
// If we fail to acquire the issuer cert, return the issued certificate - do not fail.
|
||||||
|
logf("[ERROR][%s] acme: Could not bundle issuer certificate: %v", cert.Domain, err)
|
||||||
|
} else {
|
||||||
|
// Success - append the issuer cert to the issued cert.
|
||||||
|
issuerCert = pemEncode(derCertificateBytes(issuerCert))
|
||||||
|
issuedCert = append(issuedCert, issuerCert...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cert.Certificate = issuedCert
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var privKey crypto.PrivateKey
|
||||||
|
if cert.PrivateKey != nil {
|
||||||
|
privKey, err = parsePEMPrivateKey(cert.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var domains []string
|
||||||
|
var failures map[string]error
|
||||||
|
// check for SAN certificate
|
||||||
|
if len(x509Cert.DNSNames) > 1 {
|
||||||
|
domains = append(domains, x509Cert.Subject.CommonName)
|
||||||
|
for _, sanDomain := range x509Cert.DNSNames {
|
||||||
|
if sanDomain == x509Cert.Subject.CommonName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
domains = append(domains, sanDomain)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
domains = append(domains, x509Cert.Subject.CommonName)
|
||||||
|
}
|
||||||
|
|
||||||
|
newCert, failures := c.ObtainCertificate(domains, bundle, privKey)
|
||||||
|
return newCert, failures[cert.Domain]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looks through the challenge combinations to find a solvable match.
|
||||||
|
// Then solves the challenges in series and returns.
|
||||||
|
func (c *Client) solveChallenges(challenges []authorizationResource) map[string]error {
|
||||||
|
// loop through the resources, basically through the domains.
|
||||||
|
failures := make(map[string]error)
|
||||||
|
for _, authz := range challenges {
|
||||||
|
// no solvers - no solving
|
||||||
|
if solvers := c.chooseSolvers(authz.Body, authz.Domain); solvers != nil {
|
||||||
|
for i, solver := range solvers {
|
||||||
|
// TODO: do not immediately fail if one domain fails to validate.
|
||||||
|
err := solver.Solve(authz.Body.Challenges[i], authz.Domain)
|
||||||
|
if err != nil {
|
||||||
|
failures[authz.Domain] = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
failures[authz.Domain] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks all combinations from the server and returns an array of
|
||||||
|
// solvers which should get executed in series.
|
||||||
|
func (c *Client) chooseSolvers(auth authorization, domain string) map[int]solver {
|
||||||
|
for _, combination := range auth.Combinations {
|
||||||
|
solvers := make(map[int]solver)
|
||||||
|
for _, idx := range combination {
|
||||||
|
if solver, ok := c.solvers[auth.Challenges[idx].Type]; ok {
|
||||||
|
solvers[idx] = solver
|
||||||
|
} else {
|
||||||
|
logf("[INFO][%s] acme: Could not find solver for: %s", domain, auth.Challenges[idx].Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can solve the whole combination, return the solvers
|
||||||
|
if len(solvers) == len(combination) {
|
||||||
|
return solvers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the challenges needed to proof our identifier to the ACME server.
|
||||||
|
func (c *Client) getChallenges(domains []string) ([]authorizationResource, map[string]error) {
|
||||||
|
resc, errc := make(chan authorizationResource), make(chan domainError)
|
||||||
|
|
||||||
|
for _, domain := range domains {
|
||||||
|
go func(domain string) {
|
||||||
|
authMsg := authorization{Resource: "new-authz", Identifier: identifier{Type: "dns", Value: domain}}
|
||||||
|
var authz authorization
|
||||||
|
hdr, err := postJSON(c.jws, c.user.GetRegistration().NewAuthzURL, authMsg, &authz)
|
||||||
|
if err != nil {
|
||||||
|
errc <- domainError{Domain: domain, Error: err}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
links := parseLinks(hdr["Link"])
|
||||||
|
if links["next"] == "" {
|
||||||
|
logf("[ERROR][%s] acme: Server did not provide next link to proceed", domain)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resc <- authorizationResource{Body: authz, NewCertURL: links["next"], AuthURL: hdr.Get("Location"), Domain: domain}
|
||||||
|
}(domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
responses := make(map[string]authorizationResource)
|
||||||
|
failures := make(map[string]error)
|
||||||
|
for i := 0; i < len(domains); i++ {
|
||||||
|
select {
|
||||||
|
case res := <-resc:
|
||||||
|
responses[res.Domain] = res
|
||||||
|
case err := <-errc:
|
||||||
|
failures[err.Domain] = err.Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
challenges := make([]authorizationResource, 0, len(responses))
|
||||||
|
for _, domain := range domains {
|
||||||
|
if challenge, ok := responses[domain]; ok {
|
||||||
|
challenges = append(challenges, challenge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(resc)
|
||||||
|
close(errc)
|
||||||
|
|
||||||
|
return challenges, failures
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) requestCertificate(authz []authorizationResource, bundle bool, privKey crypto.PrivateKey) (CertificateResource, error) {
|
||||||
|
if len(authz) == 0 {
|
||||||
|
return CertificateResource{}, errors.New("Passed no authorizations to requestCertificate!")
|
||||||
|
}
|
||||||
|
|
||||||
|
commonName := authz[0]
|
||||||
|
var err error
|
||||||
|
if privKey == nil {
|
||||||
|
privKey, err = generatePrivateKey(c.keyType)
|
||||||
|
if err != nil {
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var san []string
|
||||||
|
var authURLs []string
|
||||||
|
for _, auth := range authz[1:] {
|
||||||
|
san = append(san, auth.Domain)
|
||||||
|
authURLs = append(authURLs, auth.AuthURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: should the CSR be customizable?
|
||||||
|
csr, err := generateCsr(privKey, commonName.Domain, san)
|
||||||
|
if err != nil {
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
csrString := base64.URLEncoding.EncodeToString(csr)
|
||||||
|
jsonBytes, err := json.Marshal(csrMessage{Resource: "new-cert", Csr: csrString, Authorizations: authURLs})
|
||||||
|
if err != nil {
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.jws.post(commonName.NewCertURL, jsonBytes)
|
||||||
|
if err != nil {
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
privateKeyPem := pemEncode(privKey)
|
||||||
|
cerRes := CertificateResource{
|
||||||
|
Domain: commonName.Domain,
|
||||||
|
CertURL: resp.Header.Get("Location"),
|
||||||
|
PrivateKey: privateKeyPem}
|
||||||
|
|
||||||
|
for {
|
||||||
|
switch resp.StatusCode {
|
||||||
|
case 201, 202:
|
||||||
|
cert, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
|
||||||
|
resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// The server returns a body with a length of zero if the
|
||||||
|
// certificate was not ready at the time this request completed.
|
||||||
|
// Otherwise the body is the certificate.
|
||||||
|
if len(cert) > 0 {
|
||||||
|
|
||||||
|
cerRes.CertStableURL = resp.Header.Get("Content-Location")
|
||||||
|
cerRes.AccountRef = c.user.GetRegistration().URI
|
||||||
|
|
||||||
|
issuedCert := pemEncode(derCertificateBytes(cert))
|
||||||
|
// If bundle is true, we want to return a certificate bundle.
|
||||||
|
// To do this, we need the issuer certificate.
|
||||||
|
if bundle {
|
||||||
|
// The issuer certificate link is always supplied via an "up" link
|
||||||
|
// in the response headers of a new certificate.
|
||||||
|
links := parseLinks(resp.Header["Link"])
|
||||||
|
issuerCert, err := c.getIssuerCertificate(links["up"])
|
||||||
|
if err != nil {
|
||||||
|
// If we fail to acquire the issuer cert, return the issued certificate - do not fail.
|
||||||
|
logf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", commonName.Domain, err)
|
||||||
|
} else {
|
||||||
|
// Success - append the issuer cert to the issued cert.
|
||||||
|
issuerCert = pemEncode(derCertificateBytes(issuerCert))
|
||||||
|
issuedCert = append(issuedCert, issuerCert...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cerRes.Certificate = issuedCert
|
||||||
|
logf("[INFO][%s] Server responded with a certificate.", commonName.Domain)
|
||||||
|
return cerRes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The certificate was granted but is not yet issued.
|
||||||
|
// Check retry-after and loop.
|
||||||
|
ra := resp.Header.Get("Retry-After")
|
||||||
|
retryAfter, err := strconv.Atoi(ra)
|
||||||
|
if err != nil {
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logf("[INFO][%s] acme: Server responded with status 202; retrying after %ds", commonName.Domain, retryAfter)
|
||||||
|
time.Sleep(time.Duration(retryAfter) * time.Second)
|
||||||
|
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return CertificateResource{}, handleHTTPError(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = httpGet(cerRes.CertURL)
|
||||||
|
if err != nil {
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getIssuerCertificate requests the issuer certificate and caches it for
|
||||||
|
// subsequent requests.
|
||||||
|
func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
|
||||||
|
logf("[INFO] acme: Requesting issuer cert from %s", url)
|
||||||
|
if c.issuerCert != nil {
|
||||||
|
return c.issuerCert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := httpGet(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = x509.ParseCertificate(issuerBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.issuerCert = issuerBytes
|
||||||
|
return issuerBytes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLinks(links []string) map[string]string {
|
||||||
|
aBrkt := regexp.MustCompile("[<>]")
|
||||||
|
slver := regexp.MustCompile("(.+) *= *\"(.+)\"")
|
||||||
|
linkMap := make(map[string]string)
|
||||||
|
|
||||||
|
for _, link := range links {
|
||||||
|
|
||||||
|
link = aBrkt.ReplaceAllString(link, "")
|
||||||
|
parts := strings.Split(link, ";")
|
||||||
|
|
||||||
|
matches := slver.FindStringSubmatch(parts[1])
|
||||||
|
if len(matches) > 0 {
|
||||||
|
linkMap[matches[2]] = parts[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return linkMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate makes the ACME server start validating a
|
||||||
|
// challenge response, only returning once it is done.
|
||||||
|
func validate(j *jws, domain, uri string, chlng challenge) error {
|
||||||
|
var challengeResponse challenge
|
||||||
|
|
||||||
|
hdr, err := postJSON(j, uri, chlng, &challengeResponse)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// After the path is sent, the ACME server will access our server.
|
||||||
|
// Repeatedly check the server for an updated status on our request.
|
||||||
|
for {
|
||||||
|
switch challengeResponse.Status {
|
||||||
|
case "valid":
|
||||||
|
logf("[INFO][%s] The server validated our request", domain)
|
||||||
|
return nil
|
||||||
|
case "pending":
|
||||||
|
break
|
||||||
|
case "invalid":
|
||||||
|
return handleChallengeError(challengeResponse)
|
||||||
|
default:
|
||||||
|
return errors.New("The server returned an unexpected state.")
|
||||||
|
}
|
||||||
|
|
||||||
|
ra, err := strconv.Atoi(hdr.Get("Retry-After"))
|
||||||
|
if err != nil {
|
||||||
|
// The ACME server MUST return a Retry-After.
|
||||||
|
// If it doesn't, we'll just poll hard.
|
||||||
|
ra = 1
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(ra) * time.Second)
|
||||||
|
|
||||||
|
hdr, err = getJSON(uri, &challengeResponse)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
198
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/client_test.go
generated
vendored
Normal file
198
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/client_test.go
generated
vendored
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewClient(t *testing.T) {
|
||||||
|
keyBits := 32 // small value keeps test fast
|
||||||
|
keyType := RSA2048
|
||||||
|
key, err := rsa.GenerateKey(rand.Reader, keyBits)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Could not generate test key:", err)
|
||||||
|
}
|
||||||
|
user := mockUser{
|
||||||
|
email: "test@test.com",
|
||||||
|
regres: new(RegistrationResource),
|
||||||
|
privatekey: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
data, _ := json.Marshal(directory{NewAuthzURL: "http://test", NewCertURL: "http://test", NewRegURL: "http://test", RevokeCertURL: "http://test"})
|
||||||
|
w.Write(data)
|
||||||
|
}))
|
||||||
|
|
||||||
|
client, err := NewClient(ts.URL, user, keyType)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not create client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if client.jws == nil {
|
||||||
|
t.Fatalf("Expected client.jws to not be nil")
|
||||||
|
}
|
||||||
|
if expected, actual := key, client.jws.privKey; actual != expected {
|
||||||
|
t.Errorf("Expected jws.privKey to be %p but was %p", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
if client.keyType != keyType {
|
||||||
|
t.Errorf("Expected keyType to be %s but was %s", keyType, client.keyType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expected, actual := 2, len(client.solvers); actual != expected {
|
||||||
|
t.Fatalf("Expected %d solver(s), got %d", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClientOptPort(t *testing.T) {
|
||||||
|
keyBits := 32 // small value keeps test fast
|
||||||
|
key, err := rsa.GenerateKey(rand.Reader, keyBits)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Could not generate test key:", err)
|
||||||
|
}
|
||||||
|
user := mockUser{
|
||||||
|
email: "test@test.com",
|
||||||
|
regres: new(RegistrationResource),
|
||||||
|
privatekey: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
data, _ := json.Marshal(directory{NewAuthzURL: "http://test", NewCertURL: "http://test", NewRegURL: "http://test", RevokeCertURL: "http://test"})
|
||||||
|
w.Write(data)
|
||||||
|
}))
|
||||||
|
|
||||||
|
optPort := "1234"
|
||||||
|
optHost := ""
|
||||||
|
client, err := NewClient(ts.URL, user, RSA2048)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Could not create client: %v", err)
|
||||||
|
}
|
||||||
|
client.SetHTTPAddress(net.JoinHostPort(optHost, optPort))
|
||||||
|
client.SetTLSAddress(net.JoinHostPort(optHost, optPort))
|
||||||
|
|
||||||
|
httpSolver, ok := client.solvers[HTTP01].(*httpChallenge)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Expected http-01 solver to be httpChallenge type")
|
||||||
|
}
|
||||||
|
if httpSolver.jws != client.jws {
|
||||||
|
t.Error("Expected http-01 to have same jws as client")
|
||||||
|
}
|
||||||
|
if got := httpSolver.provider.(*HTTPProviderServer).port; got != optPort {
|
||||||
|
t.Errorf("Expected http-01 to have port %s but was %s", optPort, got)
|
||||||
|
}
|
||||||
|
if got := httpSolver.provider.(*HTTPProviderServer).iface; got != optHost {
|
||||||
|
t.Errorf("Expected http-01 to have iface %s but was %s", optHost, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
httpsSolver, ok := client.solvers[TLSSNI01].(*tlsSNIChallenge)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Expected tls-sni-01 solver to be httpChallenge type")
|
||||||
|
}
|
||||||
|
if httpsSolver.jws != client.jws {
|
||||||
|
t.Error("Expected tls-sni-01 to have same jws as client")
|
||||||
|
}
|
||||||
|
if got := httpsSolver.provider.(*TLSProviderServer).port; got != optPort {
|
||||||
|
t.Errorf("Expected tls-sni-01 to have port %s but was %s", optPort, got)
|
||||||
|
}
|
||||||
|
if got := httpsSolver.provider.(*TLSProviderServer).iface; got != optHost {
|
||||||
|
t.Errorf("Expected tls-sni-01 to have port %s but was %s", optHost, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test setting different host
|
||||||
|
optHost = "127.0.0.1"
|
||||||
|
client.SetHTTPAddress(net.JoinHostPort(optHost, optPort))
|
||||||
|
client.SetTLSAddress(net.JoinHostPort(optHost, optPort))
|
||||||
|
|
||||||
|
if got := httpSolver.provider.(*HTTPProviderServer).iface; got != optHost {
|
||||||
|
t.Errorf("Expected http-01 to have iface %s but was %s", optHost, got)
|
||||||
|
}
|
||||||
|
if got := httpsSolver.provider.(*TLSProviderServer).port; got != optPort {
|
||||||
|
t.Errorf("Expected tls-sni-01 to have port %s but was %s", optPort, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidate(t *testing.T) {
|
||||||
|
var statuses []string
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Minimal stub ACME server for validation.
|
||||||
|
w.Header().Add("Replay-Nonce", "12345")
|
||||||
|
w.Header().Add("Retry-After", "0")
|
||||||
|
switch r.Method {
|
||||||
|
case "HEAD":
|
||||||
|
case "POST":
|
||||||
|
st := statuses[0]
|
||||||
|
statuses = statuses[1:]
|
||||||
|
writeJSONResponse(w, &challenge{Type: "http-01", Status: st, URI: "http://example.com/", Token: "token"})
|
||||||
|
|
||||||
|
case "GET":
|
||||||
|
st := statuses[0]
|
||||||
|
statuses = statuses[1:]
|
||||||
|
writeJSONResponse(w, &challenge{Type: "http-01", Status: st, URI: "http://example.com/", Token: "token"})
|
||||||
|
|
||||||
|
default:
|
||||||
|
http.Error(w, r.Method, http.StatusMethodNotAllowed)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
privKey, _ := rsa.GenerateKey(rand.Reader, 512)
|
||||||
|
j := &jws{privKey: privKey, directoryURL: ts.URL}
|
||||||
|
|
||||||
|
tsts := []struct {
|
||||||
|
name string
|
||||||
|
statuses []string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"POST-unexpected", []string{"weird"}, "unexpected"},
|
||||||
|
{"POST-valid", []string{"valid"}, ""},
|
||||||
|
{"POST-invalid", []string{"invalid"}, "Error Detail"},
|
||||||
|
{"GET-unexpected", []string{"pending", "weird"}, "unexpected"},
|
||||||
|
{"GET-valid", []string{"pending", "valid"}, ""},
|
||||||
|
{"GET-invalid", []string{"pending", "invalid"}, "Error Detail"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tst := range tsts {
|
||||||
|
statuses = tst.statuses
|
||||||
|
if err := validate(j, "example.com", ts.URL, challenge{Type: "http-01", Token: "token"}); err == nil && tst.want != "" {
|
||||||
|
t.Errorf("[%s] validate: got error %v, want something with %q", tst.name, err, tst.want)
|
||||||
|
} else if err != nil && !strings.Contains(err.Error(), tst.want) {
|
||||||
|
t.Errorf("[%s] validate: got error %v, want something with %q", tst.name, err, tst.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeJSONResponse marshals the body as JSON and writes it to the response.
|
||||||
|
func writeJSONResponse(w http.ResponseWriter, body interface{}) {
|
||||||
|
bs, err := json.Marshal(body)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
if _, err := w.Write(bs); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stubValidate is like validate, except it does nothing.
|
||||||
|
func stubValidate(j *jws, domain, uri string, chlng challenge) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockUser struct {
|
||||||
|
email string
|
||||||
|
regres *RegistrationResource
|
||||||
|
privatekey *rsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u mockUser) GetEmail() string { return u.email }
|
||||||
|
func (u mockUser) GetRegistration() *RegistrationResource { return u.regres }
|
||||||
|
func (u mockUser) GetPrivateKey() crypto.PrivateKey { return u.privatekey }
|
323
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/crypto.go
generated
vendored
Normal file
323
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/crypto.go
generated
vendored
Normal file
|
@ -0,0 +1,323 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ocsp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyType represents the key algo as well as the key size or curve to use.
|
||||||
|
type KeyType string
|
||||||
|
type derCertificateBytes []byte
|
||||||
|
|
||||||
|
// Constants for all key types we support.
|
||||||
|
const (
|
||||||
|
EC256 = KeyType("P256")
|
||||||
|
EC384 = KeyType("P384")
|
||||||
|
RSA2048 = KeyType("2048")
|
||||||
|
RSA4096 = KeyType("4096")
|
||||||
|
RSA8192 = KeyType("8192")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// OCSPGood means that the certificate is valid.
|
||||||
|
OCSPGood = ocsp.Good
|
||||||
|
// OCSPRevoked means that the certificate has been deliberately revoked.
|
||||||
|
OCSPRevoked = ocsp.Revoked
|
||||||
|
// OCSPUnknown means that the OCSP responder doesn't know about the certificate.
|
||||||
|
OCSPUnknown = ocsp.Unknown
|
||||||
|
// OCSPServerFailed means that the OCSP responder failed to process the request.
|
||||||
|
OCSPServerFailed = ocsp.ServerFailed
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetOCSPForCert takes a PEM encoded cert or cert bundle returning the raw OCSP response,
|
||||||
|
// the parsed response, and an error, if any. The returned []byte can be passed directly
|
||||||
|
// into the OCSPStaple property of a tls.Certificate. If the bundle only contains the
|
||||||
|
// issued certificate, this function will try to get the issuer certificate from the
|
||||||
|
// IssuingCertificateURL in the certificate. If the []byte and/or ocsp.Response return
|
||||||
|
// values are nil, the OCSP status may be assumed OCSPUnknown.
|
||||||
|
func GetOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) {
|
||||||
|
certificates, err := parsePEMBundle(bundle)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect the certificate slice to be ordered downwards the chain.
|
||||||
|
// SRV CRT -> CA. We need to pull the leaf and issuer certs out of it,
|
||||||
|
// which should always be the first two certificates. If there's no
|
||||||
|
// OCSP server listed in the leaf cert, there's nothing to do. And if
|
||||||
|
// we have only one certificate so far, we need to get the issuer cert.
|
||||||
|
issuedCert := certificates[0]
|
||||||
|
if len(issuedCert.OCSPServer) == 0 {
|
||||||
|
return nil, nil, errors.New("no OCSP server specified in cert")
|
||||||
|
}
|
||||||
|
if len(certificates) == 1 {
|
||||||
|
// TODO: build fallback. If this fails, check the remaining array entries.
|
||||||
|
if len(issuedCert.IssuingCertificateURL) == 0 {
|
||||||
|
return nil, nil, errors.New("no issuing certificate URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := httpGet(issuedCert.IssuingCertificateURL[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
issuerCert, err := x509.ParseCertificate(issuerBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert it into the slice on position 0
|
||||||
|
// We want it ordered right SRV CRT -> CA
|
||||||
|
certificates = append(certificates, issuerCert)
|
||||||
|
}
|
||||||
|
issuerCert := certificates[1]
|
||||||
|
|
||||||
|
// Finally kick off the OCSP request.
|
||||||
|
ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := bytes.NewReader(ocspReq)
|
||||||
|
req, err := httpPost(issuedCert.OCSPServer[0], "application/ocsp-request", reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
defer req.Body.Close()
|
||||||
|
|
||||||
|
ocspResBytes, err := ioutil.ReadAll(limitReader(req.Body, 1024*1024))
|
||||||
|
ocspRes, err := ocsp.ParseResponse(ocspResBytes, issuerCert)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ocspRes.Certificate == nil {
|
||||||
|
err = ocspRes.CheckSignatureFrom(issuerCert)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ocspResBytes, ocspRes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getKeyAuthorization(token string, key interface{}) (string, error) {
|
||||||
|
var publicKey crypto.PublicKey
|
||||||
|
switch k := key.(type) {
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
publicKey = k.Public()
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
publicKey = k.Public()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the Key Authorization for the challenge
|
||||||
|
jwk := keyAsJWK(publicKey)
|
||||||
|
if jwk == nil {
|
||||||
|
return "", errors.New("Could not generate JWK from key.")
|
||||||
|
}
|
||||||
|
thumbBytes, err := jwk.Thumbprint(crypto.SHA256)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// unpad the base64URL
|
||||||
|
keyThumb := base64.URLEncoding.EncodeToString(thumbBytes)
|
||||||
|
index := strings.Index(keyThumb, "=")
|
||||||
|
if index != -1 {
|
||||||
|
keyThumb = keyThumb[:index]
|
||||||
|
}
|
||||||
|
|
||||||
|
return token + "." + keyThumb, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsePEMBundle parses a certificate bundle from top to bottom and returns
|
||||||
|
// a slice of x509 certificates. This function will error if no certificates are found.
|
||||||
|
func parsePEMBundle(bundle []byte) ([]*x509.Certificate, error) {
|
||||||
|
var certificates []*x509.Certificate
|
||||||
|
var certDERBlock *pem.Block
|
||||||
|
|
||||||
|
for {
|
||||||
|
certDERBlock, bundle = pem.Decode(bundle)
|
||||||
|
if certDERBlock == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if certDERBlock.Type == "CERTIFICATE" {
|
||||||
|
cert, err := x509.ParseCertificate(certDERBlock.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
certificates = append(certificates, cert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(certificates) == 0 {
|
||||||
|
return nil, errors.New("No certificates were found while parsing the bundle.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificates, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePEMPrivateKey(key []byte) (crypto.PrivateKey, error) {
|
||||||
|
keyBlock, _ := pem.Decode(key)
|
||||||
|
|
||||||
|
switch keyBlock.Type {
|
||||||
|
case "RSA PRIVATE KEY":
|
||||||
|
return x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
|
||||||
|
case "EC PRIVATE KEY":
|
||||||
|
return x509.ParseECPrivateKey(keyBlock.Bytes)
|
||||||
|
default:
|
||||||
|
return nil, errors.New("Unknown PEM header value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generatePrivateKey(keyType KeyType) (crypto.PrivateKey, error) {
|
||||||
|
|
||||||
|
switch keyType {
|
||||||
|
case EC256:
|
||||||
|
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
case EC384:
|
||||||
|
return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||||
|
case RSA2048:
|
||||||
|
return rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
case RSA4096:
|
||||||
|
return rsa.GenerateKey(rand.Reader, 4096)
|
||||||
|
case RSA8192:
|
||||||
|
return rsa.GenerateKey(rand.Reader, 8192)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("Invalid KeyType: %s", keyType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateCsr(privateKey crypto.PrivateKey, domain string, san []string) ([]byte, error) {
|
||||||
|
template := x509.CertificateRequest{
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: domain,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(san) > 0 {
|
||||||
|
template.DNSNames = san
|
||||||
|
}
|
||||||
|
|
||||||
|
return x509.CreateCertificateRequest(rand.Reader, &template, privateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pemEncode(data interface{}) []byte {
|
||||||
|
var pemBlock *pem.Block
|
||||||
|
switch key := data.(type) {
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
keyBytes, _ := x509.MarshalECPrivateKey(key)
|
||||||
|
pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
|
||||||
|
break
|
||||||
|
case derCertificateBytes:
|
||||||
|
pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.(derCertificateBytes))}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pem.EncodeToMemory(pemBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pemDecode(data []byte) (*pem.Block, error) {
|
||||||
|
pemBlock, _ := pem.Decode(data)
|
||||||
|
if pemBlock == nil {
|
||||||
|
return nil, fmt.Errorf("Pem decode did not yield a valid block. Is the certificate in the right format?")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pemBlock, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pemDecodeTox509(pem []byte) (*x509.Certificate, error) {
|
||||||
|
pemBlock, err := pemDecode(pem)
|
||||||
|
if pemBlock == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return x509.ParseCertificate(pemBlock.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPEMCertExpiration returns the "NotAfter" date of a PEM encoded certificate.
|
||||||
|
// The certificate has to be PEM encoded. Any other encodings like DER will fail.
|
||||||
|
func GetPEMCertExpiration(cert []byte) (time.Time, error) {
|
||||||
|
pemBlock, err := pemDecode(cert)
|
||||||
|
if pemBlock == nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return getCertExpiration(pemBlock.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCertExpiration returns the "NotAfter" date of a DER encoded certificate.
|
||||||
|
func getCertExpiration(cert []byte) (time.Time, error) {
|
||||||
|
pCert, err := x509.ParseCertificate(cert)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pCert.NotAfter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generatePemCert(privKey *rsa.PrivateKey, domain string) ([]byte, error) {
|
||||||
|
derBytes, err := generateDerCert(privKey, time.Time{}, domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]byte, error) {
|
||||||
|
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||||
|
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if expiration.IsZero() {
|
||||||
|
expiration = time.Now().Add(365)
|
||||||
|
}
|
||||||
|
|
||||||
|
template := x509.Certificate{
|
||||||
|
SerialNumber: serialNumber,
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "ACME Challenge TEMP",
|
||||||
|
},
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: expiration,
|
||||||
|
|
||||||
|
KeyUsage: x509.KeyUsageKeyEncipherment,
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
DNSNames: []string{domain},
|
||||||
|
}
|
||||||
|
|
||||||
|
return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func limitReader(rd io.ReadCloser, numBytes int64) io.ReadCloser {
|
||||||
|
return http.MaxBytesReader(nil, rd, numBytes)
|
||||||
|
}
|
93
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/crypto_test.go
generated
vendored
Normal file
93
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/crypto_test.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGeneratePrivateKey(t *testing.T) {
|
||||||
|
key, err := generatePrivateKey(RSA2048)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Error generating private key:", err)
|
||||||
|
}
|
||||||
|
if key == nil {
|
||||||
|
t.Error("Expected key to not be nil, but it was")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateCSR(t *testing.T) {
|
||||||
|
key, err := rsa.GenerateKey(rand.Reader, 512)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Error generating private key:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
csr, err := generateCsr(key, "fizz.buzz", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Error generating CSR:", err)
|
||||||
|
}
|
||||||
|
if csr == nil || len(csr) == 0 {
|
||||||
|
t.Error("Expected CSR with data, but it was nil or length 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPEMEncode(t *testing.T) {
|
||||||
|
buf := bytes.NewBufferString("TestingRSAIsSoMuchFun")
|
||||||
|
|
||||||
|
reader := MockRandReader{b: buf}
|
||||||
|
key, err := rsa.GenerateKey(reader, 32)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Error generating private key:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := pemEncode(key)
|
||||||
|
|
||||||
|
if data == nil {
|
||||||
|
t.Fatal("Expected result to not be nil, but it was")
|
||||||
|
}
|
||||||
|
if len(data) != 127 {
|
||||||
|
t.Errorf("Expected PEM encoding to be length 127, but it was %d", len(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPEMCertExpiration(t *testing.T) {
|
||||||
|
privKey, err := generatePrivateKey(RSA2048)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Error generating private key:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expiration := time.Now().Add(365)
|
||||||
|
expiration = expiration.Round(time.Second)
|
||||||
|
certBytes, err := generateDerCert(privKey.(*rsa.PrivateKey), expiration, "test.com")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Error generating cert:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBufferString("TestingRSAIsSoMuchFun")
|
||||||
|
|
||||||
|
// Some random string should return an error.
|
||||||
|
if ctime, err := GetPEMCertExpiration(buf.Bytes()); err == nil {
|
||||||
|
t.Errorf("Expected getCertExpiration to return an error for garbage string but returned %v", ctime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A DER encoded certificate should return an error.
|
||||||
|
if _, err := GetPEMCertExpiration(certBytes); err == nil {
|
||||||
|
t.Errorf("Expected getCertExpiration to return an error for DER certificates but returned none.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// A PEM encoded certificate should work ok.
|
||||||
|
pemCert := pemEncode(derCertificateBytes(certBytes))
|
||||||
|
if ctime, err := GetPEMCertExpiration(pemCert); err != nil || !ctime.Equal(expiration.UTC()) {
|
||||||
|
t.Errorf("Expected getCertExpiration to return %v but returned %v. Error: %v", expiration, ctime, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockRandReader struct {
|
||||||
|
b *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r MockRandReader) Read(p []byte) (int, error) {
|
||||||
|
return r.b.Read(p)
|
||||||
|
}
|
73
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/error.go
generated
vendored
Normal file
73
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/error.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tosAgreementError = "Must agree to subscriber agreement before any further actions"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RemoteError is the base type for all errors specific to the ACME protocol.
|
||||||
|
type RemoteError struct {
|
||||||
|
StatusCode int `json:"status,omitempty"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Detail string `json:"detail"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e RemoteError) Error() string {
|
||||||
|
return fmt.Sprintf("acme: Error %d - %s - %s", e.StatusCode, e.Type, e.Detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TOSError represents the error which is returned if the user needs to
|
||||||
|
// accept the TOS.
|
||||||
|
// TODO: include the new TOS url if we can somehow obtain it.
|
||||||
|
type TOSError struct {
|
||||||
|
RemoteError
|
||||||
|
}
|
||||||
|
|
||||||
|
type domainError struct {
|
||||||
|
Domain string
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
type challengeError struct {
|
||||||
|
RemoteError
|
||||||
|
records []validationRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c challengeError) Error() string {
|
||||||
|
|
||||||
|
var errStr string
|
||||||
|
for _, validation := range c.records {
|
||||||
|
errStr = errStr + fmt.Sprintf("\tValidation for %s:%s\n\tResolved to:\n\t\t%s\n\tUsed: %s\n\n",
|
||||||
|
validation.Hostname, validation.Port, strings.Join(validation.ResolvedAddresses, "\n\t\t"), validation.UsedAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s\nError Detail:\n%s", c.RemoteError.Error(), errStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleHTTPError(resp *http.Response) error {
|
||||||
|
var errorDetail RemoteError
|
||||||
|
decoder := json.NewDecoder(resp.Body)
|
||||||
|
err := decoder.Decode(&errorDetail)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
errorDetail.StatusCode = resp.StatusCode
|
||||||
|
|
||||||
|
// Check for errors we handle specifically
|
||||||
|
if errorDetail.StatusCode == http.StatusForbidden && errorDetail.Detail == tosAgreementError {
|
||||||
|
return TOSError{errorDetail}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorDetail
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleChallengeError(chlng challenge) error {
|
||||||
|
return challengeError{chlng.Error, chlng.ValidationRecords}
|
||||||
|
}
|
117
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
Normal file
117
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserAgent (if non-empty) will be tacked onto the User-Agent string in requests.
|
||||||
|
var UserAgent string
|
||||||
|
|
||||||
|
// defaultClient is an HTTP client with a reasonable timeout value.
|
||||||
|
var defaultClient = http.Client{Timeout: 10 * time.Second}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// defaultGoUserAgent is the Go HTTP package user agent string. Too
|
||||||
|
// bad it isn't exported. If it changes, we should update it here, too.
|
||||||
|
defaultGoUserAgent = "Go-http-client/1.1"
|
||||||
|
|
||||||
|
// ourUserAgent is the User-Agent of this underlying library package.
|
||||||
|
ourUserAgent = "xenolf-acme"
|
||||||
|
)
|
||||||
|
|
||||||
|
// httpHead performs a HEAD request with a proper User-Agent string.
|
||||||
|
// The response body (resp.Body) is already closed when this function returns.
|
||||||
|
func httpHead(url string) (resp *http.Response, err error) {
|
||||||
|
req, err := http.NewRequest("HEAD", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("User-Agent", userAgent())
|
||||||
|
|
||||||
|
resp, err = defaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpPost performs a POST request with a proper User-Agent string.
|
||||||
|
// Callers should close resp.Body when done reading from it.
|
||||||
|
func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response, err error) {
|
||||||
|
req, err := http.NewRequest("POST", url, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", bodyType)
|
||||||
|
req.Header.Set("User-Agent", userAgent())
|
||||||
|
|
||||||
|
return defaultClient.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpGet performs a GET request with a proper User-Agent string.
|
||||||
|
// Callers should close resp.Body when done reading from it.
|
||||||
|
func httpGet(url string) (resp *http.Response, err error) {
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("User-Agent", userAgent())
|
||||||
|
|
||||||
|
return defaultClient.Do(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getJSON performs an HTTP GET request and parses the response body
|
||||||
|
// as JSON, into the provided respBody object.
|
||||||
|
func getJSON(uri string, respBody interface{}) (http.Header, error) {
|
||||||
|
resp, err := httpGet(uri)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get %q: %v", uri, err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode >= http.StatusBadRequest {
|
||||||
|
return resp.Header, handleHTTPError(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Header, json.NewDecoder(resp.Body).Decode(respBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
// postJSON performs an HTTP POST request and parses the response body
|
||||||
|
// as JSON, into the provided respBody object.
|
||||||
|
func postJSON(j *jws, uri string, reqBody, respBody interface{}) (http.Header, error) {
|
||||||
|
jsonBytes, err := json.Marshal(reqBody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("Failed to marshal network message...")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := j.post(uri, jsonBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to post JWS message. -> %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode >= http.StatusBadRequest {
|
||||||
|
return resp.Header, handleHTTPError(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if respBody == nil {
|
||||||
|
return resp.Header, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Header, json.NewDecoder(resp.Body).Decode(respBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
// userAgent builds and returns the User-Agent string to use in requests.
|
||||||
|
func userAgent() string {
|
||||||
|
ua := fmt.Sprintf("%s (%s; %s) %s %s", defaultGoUserAgent, runtime.GOOS, runtime.GOARCH, ourUserAgent, UserAgent)
|
||||||
|
return strings.TrimSpace(ua)
|
||||||
|
}
|
41
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/http_challenge.go
generated
vendored
Normal file
41
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/http_challenge.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type httpChallenge struct {
|
||||||
|
jws *jws
|
||||||
|
validate validateFunc
|
||||||
|
provider ChallengeProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP01ChallengePath returns the URL path for the `http-01` challenge
|
||||||
|
func HTTP01ChallengePath(token string) string {
|
||||||
|
return "/.well-known/acme-challenge/" + token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *httpChallenge) Solve(chlng challenge, domain string) error {
|
||||||
|
|
||||||
|
logf("[INFO][%s] acme: Trying to solve HTTP-01", domain)
|
||||||
|
|
||||||
|
// Generate the Key Authorization for the challenge
|
||||||
|
keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.provider.Present(domain, chlng.Token, keyAuth)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("[%s] error presenting token: %v", domain, err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err := s.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[%s] error cleaning up: %v", domain, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return s.validate(s.jws, domain, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
||||||
|
}
|
79
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/http_challenge_server.go
generated
vendored
Normal file
79
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/http_challenge_server.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPProviderServer implements ChallengeProvider for `http-01` challenge
|
||||||
|
// It may be instantiated without using the NewHTTPProviderServer function if
|
||||||
|
// you want only to use the default values.
|
||||||
|
type HTTPProviderServer struct {
|
||||||
|
iface string
|
||||||
|
port string
|
||||||
|
done chan bool
|
||||||
|
listener net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHTTPProviderServer creates a new HTTPProviderServer on the selected interface and port.
|
||||||
|
// Setting iface and / or port to an empty string will make the server fall back to
|
||||||
|
// the "any" interface and port 80 respectively.
|
||||||
|
func NewHTTPProviderServer(iface, port string) *HTTPProviderServer {
|
||||||
|
return &HTTPProviderServer{iface: iface, port: port}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present starts a web server and makes the token available at `HTTP01ChallengePath(token)` for web requests.
|
||||||
|
func (s *HTTPProviderServer) Present(domain, token, keyAuth string) error {
|
||||||
|
if s.port == "" {
|
||||||
|
s.port = "80"
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
s.listener, err = net.Listen("tcp", net.JoinHostPort(s.iface, s.port))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Could not start HTTP server for challenge -> %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.done = make(chan bool)
|
||||||
|
go s.serve(domain, token, keyAuth)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanUp closes the HTTP server and removes the token from `HTTP01ChallengePath(token)`
|
||||||
|
func (s *HTTPProviderServer) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
if s.listener == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.listener.Close()
|
||||||
|
<-s.done
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *HTTPProviderServer) serve(domain, token, keyAuth string) {
|
||||||
|
path := HTTP01ChallengePath(token)
|
||||||
|
|
||||||
|
// The handler validates the HOST header and request type.
|
||||||
|
// For validation it then writes the token the server returned with the challenge
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if strings.HasPrefix(r.Host, domain) && r.Method == "GET" {
|
||||||
|
w.Header().Add("Content-Type", "text/plain")
|
||||||
|
w.Write([]byte(keyAuth))
|
||||||
|
logf("[INFO][%s] Served key authentication", domain)
|
||||||
|
} else {
|
||||||
|
logf("[INFO] Received request for domain %s with method %s", r.Host, r.Method)
|
||||||
|
w.Write([]byte("TEST"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
httpServer := &http.Server{
|
||||||
|
Handler: mux,
|
||||||
|
}
|
||||||
|
// Once httpServer is shut down we don't want any lingering
|
||||||
|
// connections, so disable KeepAlives.
|
||||||
|
httpServer.SetKeepAlivesEnabled(false)
|
||||||
|
httpServer.Serve(s.listener)
|
||||||
|
s.done <- true
|
||||||
|
}
|
57
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/http_challenge_test.go
generated
vendored
Normal file
57
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/http_challenge_test.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHTTPChallenge(t *testing.T) {
|
||||||
|
privKey, _ := rsa.GenerateKey(rand.Reader, 512)
|
||||||
|
j := &jws{privKey: privKey}
|
||||||
|
clientChallenge := challenge{Type: HTTP01, Token: "http1"}
|
||||||
|
mockValidate := func(_ *jws, _, _ string, chlng challenge) error {
|
||||||
|
uri := "http://localhost:23457/.well-known/acme-challenge/" + chlng.Token
|
||||||
|
resp, err := httpGet(uri)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if want := "text/plain"; resp.Header.Get("Content-Type") != want {
|
||||||
|
t.Errorf("Get(%q) Content-Type: got %q, want %q", uri, resp.Header.Get("Content-Type"), want)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bodyStr := string(body)
|
||||||
|
|
||||||
|
if bodyStr != chlng.KeyAuthorization {
|
||||||
|
t.Errorf("Get(%q) Body: got %q, want %q", uri, bodyStr, chlng.KeyAuthorization)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
solver := &httpChallenge{jws: j, validate: mockValidate, provider: &HTTPProviderServer{port: "23457"}}
|
||||||
|
|
||||||
|
if err := solver.Solve(clientChallenge, "localhost:23457"); err != nil {
|
||||||
|
t.Errorf("Solve error: got %v, want nil", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHTTPChallengeInvalidPort(t *testing.T) {
|
||||||
|
privKey, _ := rsa.GenerateKey(rand.Reader, 128)
|
||||||
|
j := &jws{privKey: privKey}
|
||||||
|
clientChallenge := challenge{Type: HTTP01, Token: "http2"}
|
||||||
|
solver := &httpChallenge{jws: j, validate: stubValidate, provider: &HTTPProviderServer{port: "123456"}}
|
||||||
|
|
||||||
|
if err := solver.Solve(clientChallenge, "localhost:123456"); err == nil {
|
||||||
|
t.Errorf("Solve error: got %v, want error", err)
|
||||||
|
} else if want := "invalid port 123456"; !strings.HasSuffix(err.Error(), want) {
|
||||||
|
t.Errorf("Solve error: got %q, want suffix %q", err.Error(), want)
|
||||||
|
}
|
||||||
|
}
|
100
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/http_test.go
generated
vendored
Normal file
100
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/http_test.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHTTPHeadUserAgent(t *testing.T) {
|
||||||
|
var ua, method string
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ua = r.Header.Get("User-Agent")
|
||||||
|
method = r.Method
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
_, err := httpHead(ts.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if method != "HEAD" {
|
||||||
|
t.Errorf("Expected method to be HEAD, got %s", method)
|
||||||
|
}
|
||||||
|
if !strings.Contains(ua, ourUserAgent) {
|
||||||
|
t.Errorf("Expected User-Agent to contain '%s', got: '%s'", ourUserAgent, ua)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHTTPGetUserAgent(t *testing.T) {
|
||||||
|
var ua, method string
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ua = r.Header.Get("User-Agent")
|
||||||
|
method = r.Method
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
res, err := httpGet(ts.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
res.Body.Close()
|
||||||
|
|
||||||
|
if method != "GET" {
|
||||||
|
t.Errorf("Expected method to be GET, got %s", method)
|
||||||
|
}
|
||||||
|
if !strings.Contains(ua, ourUserAgent) {
|
||||||
|
t.Errorf("Expected User-Agent to contain '%s', got: '%s'", ourUserAgent, ua)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHTTPPostUserAgent(t *testing.T) {
|
||||||
|
var ua, method string
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ua = r.Header.Get("User-Agent")
|
||||||
|
method = r.Method
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
res, err := httpPost(ts.URL, "text/plain", strings.NewReader("falalalala"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
res.Body.Close()
|
||||||
|
|
||||||
|
if method != "POST" {
|
||||||
|
t.Errorf("Expected method to be POST, got %s", method)
|
||||||
|
}
|
||||||
|
if !strings.Contains(ua, ourUserAgent) {
|
||||||
|
t.Errorf("Expected User-Agent to contain '%s', got: '%s'", ourUserAgent, ua)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserAgent(t *testing.T) {
|
||||||
|
ua := userAgent()
|
||||||
|
|
||||||
|
if !strings.Contains(ua, defaultGoUserAgent) {
|
||||||
|
t.Errorf("Expected UA to contain %s, got '%s'", defaultGoUserAgent, ua)
|
||||||
|
}
|
||||||
|
if !strings.Contains(ua, ourUserAgent) {
|
||||||
|
t.Errorf("Expected UA to contain %s, got '%s'", ourUserAgent, ua)
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(ua, " ") {
|
||||||
|
t.Errorf("UA should not have trailing spaces; got '%s'", ua)
|
||||||
|
}
|
||||||
|
|
||||||
|
// customize the UA by appending a value
|
||||||
|
UserAgent = "MyApp/1.2.3"
|
||||||
|
ua = userAgent()
|
||||||
|
if !strings.Contains(ua, defaultGoUserAgent) {
|
||||||
|
t.Errorf("Expected UA to contain %s, got '%s'", defaultGoUserAgent, ua)
|
||||||
|
}
|
||||||
|
if !strings.Contains(ua, ourUserAgent) {
|
||||||
|
t.Errorf("Expected UA to contain %s, got '%s'", ourUserAgent, ua)
|
||||||
|
}
|
||||||
|
if !strings.Contains(ua, UserAgent) {
|
||||||
|
t.Errorf("Expected custom UA to contain %s, got '%s'", UserAgent, ua)
|
||||||
|
}
|
||||||
|
}
|
107
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/jws.go
generated
vendored
Normal file
107
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/jws.go
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rsa"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gopkg.in/square/go-jose.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type jws struct {
|
||||||
|
directoryURL string
|
||||||
|
privKey crypto.PrivateKey
|
||||||
|
nonces []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyAsJWK(key interface{}) *jose.JsonWebKey {
|
||||||
|
switch k := key.(type) {
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
return &jose.JsonWebKey{Key: k, Algorithm: "EC"}
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
return &jose.JsonWebKey{Key: k, Algorithm: "RSA"}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Posts a JWS signed message to the specified URL
|
||||||
|
func (j *jws) post(url string, content []byte) (*http.Response, error) {
|
||||||
|
signedContent, err := j.signContent(content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := httpPost(url, "application/jose+json", bytes.NewBuffer([]byte(signedContent.FullSerialize())))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
j.getNonceFromResponse(resp)
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *jws) signContent(content []byte) (*jose.JsonWebSignature, error) {
|
||||||
|
|
||||||
|
var alg jose.SignatureAlgorithm
|
||||||
|
switch k := j.privKey.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
alg = jose.RS256
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
if k.Curve == elliptic.P256() {
|
||||||
|
alg = jose.ES256
|
||||||
|
} else if k.Curve == elliptic.P384() {
|
||||||
|
alg = jose.ES384
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signer, err := jose.NewSigner(alg, j.privKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
signer.SetNonceSource(j)
|
||||||
|
|
||||||
|
signed, err := signer.Sign(content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return signed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *jws) getNonceFromResponse(resp *http.Response) error {
|
||||||
|
nonce := resp.Header.Get("Replay-Nonce")
|
||||||
|
if nonce == "" {
|
||||||
|
return fmt.Errorf("Server did not respond with a proper nonce header.")
|
||||||
|
}
|
||||||
|
|
||||||
|
j.nonces = append(j.nonces, nonce)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *jws) getNonce() error {
|
||||||
|
resp, err := httpHead(j.directoryURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return j.getNonceFromResponse(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *jws) Nonce() (string, error) {
|
||||||
|
nonce := ""
|
||||||
|
if len(j.nonces) == 0 {
|
||||||
|
err := j.getNonce()
|
||||||
|
if err != nil {
|
||||||
|
return nonce, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce, j.nonces = j.nonces[len(j.nonces)-1], j.nonces[:len(j.nonces)-1]
|
||||||
|
return nonce, nil
|
||||||
|
}
|
115
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/messages.go
generated
vendored
Normal file
115
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/messages.go
generated
vendored
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gopkg.in/square/go-jose.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type directory struct {
|
||||||
|
NewAuthzURL string `json:"new-authz"`
|
||||||
|
NewCertURL string `json:"new-cert"`
|
||||||
|
NewRegURL string `json:"new-reg"`
|
||||||
|
RevokeCertURL string `json:"revoke-cert"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type recoveryKeyMessage struct {
|
||||||
|
Length int `json:"length,omitempty"`
|
||||||
|
Client jose.JsonWebKey `json:"client,omitempty"`
|
||||||
|
Server jose.JsonWebKey `json:"client,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type registrationMessage struct {
|
||||||
|
Resource string `json:"resource"`
|
||||||
|
Contact []string `json:"contact"`
|
||||||
|
// RecoveryKey recoveryKeyMessage `json:"recoveryKey,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registration is returned by the ACME server after the registration
|
||||||
|
// The client implementation should save this registration somewhere.
|
||||||
|
type Registration struct {
|
||||||
|
Resource string `json:"resource,omitempty"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Key jose.JsonWebKey `json:"key"`
|
||||||
|
Contact []string `json:"contact"`
|
||||||
|
Agreement string `json:"agreement,omitempty"`
|
||||||
|
Authorizations string `json:"authorizations,omitempty"`
|
||||||
|
Certificates string `json:"certificates,omitempty"`
|
||||||
|
// RecoveryKey recoveryKeyMessage `json:"recoveryKey,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegistrationResource represents all important informations about a registration
|
||||||
|
// of which the client needs to keep track itself.
|
||||||
|
type RegistrationResource struct {
|
||||||
|
Body Registration `json:"body,omitempty"`
|
||||||
|
URI string `json:"uri,omitempty"`
|
||||||
|
NewAuthzURL string `json:"new_authzr_uri,omitempty"`
|
||||||
|
TosURL string `json:"terms_of_service,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type authorizationResource struct {
|
||||||
|
Body authorization
|
||||||
|
Domain string
|
||||||
|
NewCertURL string
|
||||||
|
AuthURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
type authorization struct {
|
||||||
|
Resource string `json:"resource,omitempty"`
|
||||||
|
Identifier identifier `json:"identifier"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
Expires time.Time `json:"expires,omitempty"`
|
||||||
|
Challenges []challenge `json:"challenges,omitempty"`
|
||||||
|
Combinations [][]int `json:"combinations,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type identifier struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type validationRecord struct {
|
||||||
|
URI string `json:"url,omitempty"`
|
||||||
|
Hostname string `json:"hostname,omitempty"`
|
||||||
|
Port string `json:"port,omitempty"`
|
||||||
|
ResolvedAddresses []string `json:"addressesResolved,omitempty"`
|
||||||
|
UsedAddress string `json:"addressUsed,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type challenge struct {
|
||||||
|
Resource string `json:"resource,omitempty"`
|
||||||
|
Type Challenge `json:"type,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
URI string `json:"uri,omitempty"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
KeyAuthorization string `json:"keyAuthorization,omitempty"`
|
||||||
|
TLS bool `json:"tls,omitempty"`
|
||||||
|
Iterations int `json:"n,omitempty"`
|
||||||
|
Error RemoteError `json:"error,omitempty"`
|
||||||
|
ValidationRecords []validationRecord `json:"validationRecord,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type csrMessage struct {
|
||||||
|
Resource string `json:"resource,omitempty"`
|
||||||
|
Csr string `json:"csr"`
|
||||||
|
Authorizations []string `json:"authorizations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type revokeCertMessage struct {
|
||||||
|
Resource string `json:"resource"`
|
||||||
|
Certificate string `json:"certificate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateResource represents a CA issued certificate.
|
||||||
|
// PrivateKey and Certificate are both already PEM encoded
|
||||||
|
// and can be directly written to disk. Certificate may
|
||||||
|
// be a certificate bundle, depending on the options supplied
|
||||||
|
// to create it.
|
||||||
|
type CertificateResource struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
CertURL string `json:"certUrl"`
|
||||||
|
CertStableURL string `json:"certStableUrl"`
|
||||||
|
AccountRef string `json:"accountRef,omitempty"`
|
||||||
|
PrivateKey []byte `json:"-"`
|
||||||
|
Certificate []byte `json:"-"`
|
||||||
|
}
|
28
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/provider.go
generated
vendored
Normal file
28
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/provider.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// ChallengeProvider enables implementing a custom challenge
|
||||||
|
// provider. Present presents the solution to a challenge available to
|
||||||
|
// be solved. CleanUp will be called by the challenge if Present ends
|
||||||
|
// in a non-error state.
|
||||||
|
type ChallengeProvider interface {
|
||||||
|
Present(domain, token, keyAuth string) error
|
||||||
|
CleanUp(domain, token, keyAuth string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChallengeProviderTimeout allows for implementing a
|
||||||
|
// ChallengeProvider where an unusually long timeout is required when
|
||||||
|
// waiting for an ACME challenge to be satisfied, such as when
|
||||||
|
// checking for DNS record progagation. If an implementor of a
|
||||||
|
// ChallengeProvider provides a Timeout method, then the return values
|
||||||
|
// of the Timeout method will be used when appropriate by the acme
|
||||||
|
// package. The interval value is the time between checks.
|
||||||
|
//
|
||||||
|
// The default values used for timeout and interval are 60 seconds and
|
||||||
|
// 2 seconds respectively. These are used when no Timeout method is
|
||||||
|
// defined for the ChallengeProvider.
|
||||||
|
type ChallengeProviderTimeout interface {
|
||||||
|
ChallengeProvider
|
||||||
|
Timeout() (timeout, interval time.Duration)
|
||||||
|
}
|
73
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/tls_sni_challenge.go
generated
vendored
Normal file
73
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/tls_sni_challenge.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tlsSNIChallenge struct {
|
||||||
|
jws *jws
|
||||||
|
validate validateFunc
|
||||||
|
provider ChallengeProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tlsSNIChallenge) Solve(chlng challenge, domain string) error {
|
||||||
|
// FIXME: https://github.com/ietf-wg-acme/acme/pull/22
|
||||||
|
// Currently we implement this challenge to track boulder, not the current spec!
|
||||||
|
|
||||||
|
logf("[INFO][%s] acme: Trying to solve TLS-SNI-01", domain)
|
||||||
|
|
||||||
|
// Generate the Key Authorization for the challenge
|
||||||
|
keyAuth, err := getKeyAuthorization(chlng.Token, t.jws.privKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.provider.Present(domain, chlng.Token, keyAuth)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("[%s] error presenting token: %v", domain, err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err := t.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[%s] error cleaning up: %v", domain, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return t.validate(t.jws, domain, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSSNI01ChallengeCert returns a certificate and target domain for the `tls-sni-01` challenge
|
||||||
|
func TLSSNI01ChallengeCertDomain(keyAuth string) (tls.Certificate, string, error) {
|
||||||
|
// generate a new RSA key for the certificates
|
||||||
|
tempPrivKey, err := generatePrivateKey(RSA2048)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, "", err
|
||||||
|
}
|
||||||
|
rsaPrivKey := tempPrivKey.(*rsa.PrivateKey)
|
||||||
|
rsaPrivPEM := pemEncode(rsaPrivKey)
|
||||||
|
|
||||||
|
zBytes := sha256.Sum256([]byte(keyAuth))
|
||||||
|
z := hex.EncodeToString(zBytes[:sha256.Size])
|
||||||
|
domain := fmt.Sprintf("%s.%s.acme.invalid", z[:32], z[32:])
|
||||||
|
tempCertPEM, err := generatePemCert(rsaPrivKey, domain)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM)
|
||||||
|
if err != nil {
|
||||||
|
return tls.Certificate{}, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificate, domain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSSNI01ChallengeCert returns a certificate for the `tls-sni-01` challenge
|
||||||
|
func TLSSNI01ChallengeCert(keyAuth string) (tls.Certificate, error) {
|
||||||
|
cert, _, err := TLSSNI01ChallengeCertDomain(keyAuth)
|
||||||
|
return cert, err
|
||||||
|
}
|
62
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/tls_sni_challenge_server.go
generated
vendored
Normal file
62
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/tls_sni_challenge_server.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TLSProviderServer implements ChallengeProvider for `TLS-SNI-01` challenge
|
||||||
|
// It may be instantiated without using the NewTLSProviderServer function if
|
||||||
|
// you want only to use the default values.
|
||||||
|
type TLSProviderServer struct {
|
||||||
|
iface string
|
||||||
|
port string
|
||||||
|
done chan bool
|
||||||
|
listener net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTLSProviderServer creates a new TLSProviderServer on the selected interface and port.
|
||||||
|
// Setting iface and / or port to an empty string will make the server fall back to
|
||||||
|
// the "any" interface and port 443 respectively.
|
||||||
|
func NewTLSProviderServer(iface, port string) *TLSProviderServer {
|
||||||
|
return &TLSProviderServer{iface: iface, port: port}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present makes the keyAuth available as a cert
|
||||||
|
func (s *TLSProviderServer) Present(domain, token, keyAuth string) error {
|
||||||
|
if s.port == "" {
|
||||||
|
s.port = "443"
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := TLSSNI01ChallengeCert(keyAuth)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConf := new(tls.Config)
|
||||||
|
tlsConf.Certificates = []tls.Certificate{cert}
|
||||||
|
|
||||||
|
s.listener, err = tls.Listen("tcp", net.JoinHostPort(s.iface, s.port), tlsConf)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Could not start HTTPS server for challenge -> %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.done = make(chan bool)
|
||||||
|
go func() {
|
||||||
|
http.Serve(s.listener, nil)
|
||||||
|
s.done <- true
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanUp closes the HTTP server.
|
||||||
|
func (s *TLSProviderServer) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
if s.listener == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s.listener.Close()
|
||||||
|
<-s.done
|
||||||
|
return nil
|
||||||
|
}
|
65
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/tls_sni_challenge_test.go
generated
vendored
Normal file
65
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/tls_sni_challenge_test.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTLSSNIChallenge(t *testing.T) {
|
||||||
|
privKey, _ := rsa.GenerateKey(rand.Reader, 512)
|
||||||
|
j := &jws{privKey: privKey}
|
||||||
|
clientChallenge := challenge{Type: TLSSNI01, Token: "tlssni1"}
|
||||||
|
mockValidate := func(_ *jws, _, _ string, chlng challenge) error {
|
||||||
|
conn, err := tls.Dial("tcp", "localhost:23457", &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected to connect to challenge server without an error. %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expect the server to only return one certificate
|
||||||
|
connState := conn.ConnectionState()
|
||||||
|
if count := len(connState.PeerCertificates); count != 1 {
|
||||||
|
t.Errorf("Expected the challenge server to return exactly one certificate but got %d", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteCert := connState.PeerCertificates[0]
|
||||||
|
if count := len(remoteCert.DNSNames); count != 1 {
|
||||||
|
t.Errorf("Expected the challenge certificate to have exactly one DNSNames entry but had %d", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
zBytes := sha256.Sum256([]byte(chlng.KeyAuthorization))
|
||||||
|
z := hex.EncodeToString(zBytes[:sha256.Size])
|
||||||
|
domain := fmt.Sprintf("%s.%s.acme.invalid", z[:32], z[32:])
|
||||||
|
|
||||||
|
if remoteCert.DNSNames[0] != domain {
|
||||||
|
t.Errorf("Expected the challenge certificate DNSName to match %s but was %s", domain, remoteCert.DNSNames[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
solver := &tlsSNIChallenge{jws: j, validate: mockValidate, provider: &TLSProviderServer{port: "23457"}}
|
||||||
|
|
||||||
|
if err := solver.Solve(clientChallenge, "localhost:23457"); err != nil {
|
||||||
|
t.Errorf("Solve error: got %v, want nil", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTLSSNIChallengeInvalidPort(t *testing.T) {
|
||||||
|
privKey, _ := rsa.GenerateKey(rand.Reader, 128)
|
||||||
|
j := &jws{privKey: privKey}
|
||||||
|
clientChallenge := challenge{Type: TLSSNI01, Token: "tlssni2"}
|
||||||
|
solver := &tlsSNIChallenge{jws: j, validate: stubValidate, provider: &TLSProviderServer{port: "123456"}}
|
||||||
|
|
||||||
|
if err := solver.Solve(clientChallenge, "localhost:123456"); err == nil {
|
||||||
|
t.Errorf("Solve error: got %v, want error", err)
|
||||||
|
} else if want := "invalid port 123456"; !strings.HasSuffix(err.Error(), want) {
|
||||||
|
t.Errorf("Solve error: got %q, want suffix %q", err.Error(), want)
|
||||||
|
}
|
||||||
|
}
|
29
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/utils.go
generated
vendored
Normal file
29
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WaitFor polls the given function 'f', once every 'interval', up to 'timeout'.
|
||||||
|
func WaitFor(timeout, interval time.Duration, f func() (bool, error)) error {
|
||||||
|
var lastErr string
|
||||||
|
timeup := time.After(timeout)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timeup:
|
||||||
|
return fmt.Errorf("Time limit exceeded. Last error: %s", lastErr)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
stop, err := f()
|
||||||
|
if stop {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
lastErr = err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(interval)
|
||||||
|
}
|
||||||
|
}
|
26
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/utils_test.go
generated
vendored
Normal file
26
vendor/rsc.io/letsencrypt/vendor/github.com/xenolf/lego/acme/utils_test.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package acme
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWaitForTimeout(t *testing.T) {
|
||||||
|
c := make(chan error)
|
||||||
|
go func() {
|
||||||
|
err := WaitFor(3*time.Second, 1*time.Second, func() (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
c <- err
|
||||||
|
}()
|
||||||
|
|
||||||
|
timeout := time.After(4 * time.Second)
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
t.Fatal("timeout exceeded")
|
||||||
|
case err := <-c:
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected timeout error; got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/BUG-BOUNTY.md
generated
vendored
Normal file
10
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/BUG-BOUNTY.md
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Serious about security
|
||||||
|
======================
|
||||||
|
|
||||||
|
Square recognizes the important contributions the security research community
|
||||||
|
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>.
|
||||||
|
|
14
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/CONTRIBUTING.md
generated
vendored
Normal file
14
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# 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][1].
|
||||||
|
|
||||||
|
[1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1
|
202
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/LICENSE
generated
vendored
Normal file
202
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
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.
|
209
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/README.md
generated
vendored
Normal file
209
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/README.md
generated
vendored
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
# Go JOSE
|
||||||
|
|
||||||
|
[![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/gopkg.in/square/go-jose.v1) [![license](http://img.shields.io/badge/license-apache_2.0-red.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)
|
||||||
|
|
||||||
|
Package jose aims to provide an implementation of the Javascript Object Signing
|
||||||
|
and Encryption set of standards. For the moment, it mainly focuses on encryption
|
||||||
|
and signing based on the JSON Web Encryption and JSON Web Signature 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](http://dx.doi.org/10.17487/RFC7516)
|
||||||
|
standard (RFC 7516) and
|
||||||
|
[JSON Web Signature](http://dx.doi.org/10.17487/RFC7515)
|
||||||
|
standard (RFC 7515). Tables of supported algorithms are shown below.
|
||||||
|
The library supports both the compact and full serialization formats, and has
|
||||||
|
optional support for multiple recipients. It also comes with a small
|
||||||
|
command-line utility
|
||||||
|
([`jose-util`](https://github.com/square/go-jose/tree/master/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. If you do not like this behavior, you can use the
|
||||||
|
`std_json` build tag to disable it (though we do not recommend doing so).
|
||||||
|
|
||||||
|
### Versions
|
||||||
|
|
||||||
|
We use [gopkg.in](https://gopkg.in) for versioning.
|
||||||
|
|
||||||
|
[Version 1](https://gopkg.in/square/go-jose.v1) is the current stable version:
|
||||||
|
|
||||||
|
import "gopkg.in/square/go-jose.v1"
|
||||||
|
|
||||||
|
The interface for [go-jose.v1](https://gopkg.in/square/go-jose.v1) will remain
|
||||||
|
backwards compatible. We're currently sketching out ideas for a new version, to
|
||||||
|
clean up the interface a bit. If you have ideas or feature requests [please let
|
||||||
|
us know](https://github.com/square/go-jose/issues/64)!
|
||||||
|
|
||||||
|
### Supported algorithms
|
||||||
|
|
||||||
|
See below for a table of supported algorithms. Algorithm identifiers match
|
||||||
|
the names in the
|
||||||
|
[JSON Web Algorithms](http://dx.doi.org/10.17487/RFC7518)
|
||||||
|
standard where possible. The
|
||||||
|
[Godoc reference](https://godoc.org/github.com/square/go-jose#pkg-constants)
|
||||||
|
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-ES<sup>1</sup>
|
||||||
|
Direct encryption | dir<sup>1</sup>
|
||||||
|
|
||||||
|
<sup>1. Not supported in multi-recipient mode</sup>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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`. Note that if you are creating a new encrypter or signer with a
|
||||||
|
JsonWebKey, the key id of the JsonWebKey (if present) will be added to any
|
||||||
|
resulting messages.
|
||||||
|
|
||||||
|
Algorithm(s) | Corresponding types
|
||||||
|
:------------------------- | -------------------------------
|
||||||
|
RSA | *[rsa.PublicKey](http://golang.org/pkg/crypto/rsa/#PublicKey), *[rsa.PrivateKey](http://golang.org/pkg/crypto/rsa/#PrivateKey), *[jose.JsonWebKey](https://godoc.org/github.com/square/go-jose#JsonWebKey)
|
||||||
|
ECDH, ECDSA | *[ecdsa.PublicKey](http://golang.org/pkg/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](http://golang.org/pkg/crypto/ecdsa/#PrivateKey), *[jose.JsonWebKey](https://godoc.org/github.com/square/go-jose#JsonWebKey)
|
||||||
|
AES, HMAC | []byte, *[jose.JsonWebKey](https://godoc.org/github.com/square/go-jose#JsonWebKey)
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Encryption/decryption example using RSA:
|
||||||
|
|
||||||
|
```Go
|
||||||
|
// Generate a public/private key pair to use for this example. The library
|
||||||
|
// also provides two utility functions (LoadPublicKey and LoadPrivateKey)
|
||||||
|
// that can be used to load keys from PEM/DER-encoded data.
|
||||||
|
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(RSA_OAEP, A128GCM, publicKey)
|
||||||
|
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 full 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)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we can decrypt and get back our original plaintext. An error here
|
||||||
|
// would indicate the 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.Printf(string(decrypted))
|
||||||
|
// output: Lorem ipsum dolor sit amet
|
||||||
|
```
|
||||||
|
|
||||||
|
Signing/verification example using RSA:
|
||||||
|
|
||||||
|
```Go
|
||||||
|
// Generate a public/private key pair to use for this example. The library
|
||||||
|
// also provides two utility functions (LoadPublicKey and LoadPrivateKey)
|
||||||
|
// that can be used to load keys from PEM/DER-encoded data.
|
||||||
|
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(PS512, privateKey)
|
||||||
|
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 encrypted object using the full 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)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we can verify the signature on the payload. An error here would
|
||||||
|
// indicate the 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.Printf(string(output))
|
||||||
|
// output: Lorem ipsum dolor sit amet
|
||||||
|
```
|
||||||
|
|
||||||
|
More examples can be found in the [Godoc
|
||||||
|
reference](https://godoc.org/github.com/square/go-jose) for this package. The
|
||||||
|
[`jose-util`](https://github.com/square/go-jose/tree/master/jose-util)
|
||||||
|
subdirectory also contains a small command-line utility which might
|
||||||
|
be useful as an example.
|
498
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/asymmetric.go
generated
vendored
Normal file
498
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/asymmetric.go
generated
vendored
Normal file
|
@ -0,0 +1,498 @@
|
||||||
|
/*-
|
||||||
|
* 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/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"gopkg.in/square/go-jose.v1/cipher"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
return recipientSigInfo{
|
||||||
|
sigAlg: sigAlg,
|
||||||
|
publicKey: &JsonWebKey{
|
||||||
|
Key: &privateKey.PublicKey,
|
||||||
|
},
|
||||||
|
signer: &rsaDecrypterSigner{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
return recipientSigInfo{
|
||||||
|
sigAlg: sigAlg,
|
||||||
|
publicKey: &JsonWebKey{
|
||||||
|
Key: &privateKey.PublicKey,
|
||||||
|
},
|
||||||
|
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, KeyAlgorithm(headers.Alg), 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 deliberatly 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:
|
||||||
|
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.PSSSaltLengthAuto,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
headers := rawHeader{
|
||||||
|
Epk: &JsonWebKey{
|
||||||
|
Key: &priv.PublicKey,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if headers.Epk == nil {
|
||||||
|
return nil, errors.New("square/go-jose: missing epk header")
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKey, ok := headers.Epk.Key.(*ecdsa.PublicKey)
|
||||||
|
if publicKey == nil || !ok {
|
||||||
|
return nil, errors.New("square/go-jose: invalid epk header")
|
||||||
|
}
|
||||||
|
|
||||||
|
apuData := headers.Apu.bytes()
|
||||||
|
apvData := headers.Apv.bytes()
|
||||||
|
|
||||||
|
deriveKey := func(algID string, size int) []byte {
|
||||||
|
return josecipher.DeriveECDHES(algID, apuData, apvData, ctx.privateKey, publicKey, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
var keySize int
|
||||||
|
|
||||||
|
switch KeyAlgorithm(headers.Alg) {
|
||||||
|
case ECDH_ES:
|
||||||
|
// ECDH-ES uses direct key agreement, no key unwrapping necessary.
|
||||||
|
return deriveKey(string(headers.Enc), 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(headers.Alg, keySize)
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return josecipher.KeyUnwrap(block, recipient.encryptedKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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("square/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 += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// We serialize the outpus (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
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(signature) != 2*keySize {
|
||||||
|
return fmt.Errorf("square/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("square/go-jose: ecdsa signature failed to verify")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
431
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/asymmetric_test.go
generated
vendored
Normal file
431
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/asymmetric_test.go
generated
vendored
Normal file
|
@ -0,0 +1,431 @@
|
||||||
|
/*-
|
||||||
|
* 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"
|
||||||
|
"crypto/rsa"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVectorsRSA(t *testing.T) {
|
||||||
|
// Sources:
|
||||||
|
// http://www.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-rsa-cryptography-standard.htm
|
||||||
|
// ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15crypt-vectors.txt
|
||||||
|
priv := &rsa.PrivateKey{
|
||||||
|
PublicKey: rsa.PublicKey{
|
||||||
|
N: fromHexInt(`
|
||||||
|
a8b3b284af8eb50b387034a860f146c4919f318763cd6c5598c8
|
||||||
|
ae4811a1e0abc4c7e0b082d693a5e7fced675cf4668512772c0c
|
||||||
|
bc64a742c6c630f533c8cc72f62ae833c40bf25842e984bb78bd
|
||||||
|
bf97c0107d55bdb662f5c4e0fab9845cb5148ef7392dd3aaff93
|
||||||
|
ae1e6b667bb3d4247616d4f5ba10d4cfd226de88d39f16fb`),
|
||||||
|
E: 65537,
|
||||||
|
},
|
||||||
|
D: fromHexInt(`
|
||||||
|
53339cfdb79fc8466a655c7316aca85c55fd8f6dd898fdaf1195
|
||||||
|
17ef4f52e8fd8e258df93fee180fa0e4ab29693cd83b152a553d
|
||||||
|
4ac4d1812b8b9fa5af0e7f55fe7304df41570926f3311f15c4d6
|
||||||
|
5a732c483116ee3d3d2d0af3549ad9bf7cbfb78ad884f84d5beb
|
||||||
|
04724dc7369b31def37d0cf539e9cfcdd3de653729ead5d1`),
|
||||||
|
Primes: []*big.Int{
|
||||||
|
fromHexInt(`
|
||||||
|
d32737e7267ffe1341b2d5c0d150a81b586fb3132bed2f8d5262
|
||||||
|
864a9cb9f30af38be448598d413a172efb802c21acf1c11c520c
|
||||||
|
2f26a471dcad212eac7ca39d`),
|
||||||
|
fromHexInt(`
|
||||||
|
cc8853d1d54da630fac004f471f281c7b8982d8224a490edbeb3
|
||||||
|
3d3e3d5cc93c4765703d1dd791642f1f116a0dd852be2419b2af
|
||||||
|
72bfe9a030e860b0288b5d77`),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
input := fromHexBytes(
|
||||||
|
"6628194e12073db03ba94cda9ef9532397d50dba79b987004afefe34")
|
||||||
|
|
||||||
|
expectedPKCS := fromHexBytes(`
|
||||||
|
50b4c14136bd198c2f3c3ed243fce036e168d56517984a263cd66492b808
|
||||||
|
04f169d210f2b9bdfb48b12f9ea05009c77da257cc600ccefe3a6283789d
|
||||||
|
8ea0e607ac58e2690ec4ebc10146e8cbaa5ed4d5cce6fe7b0ff9efc1eabb
|
||||||
|
564dbf498285f449ee61dd7b42ee5b5892cb90601f30cda07bf26489310b
|
||||||
|
cd23b528ceab3c31`)
|
||||||
|
|
||||||
|
expectedOAEP := fromHexBytes(`
|
||||||
|
354fe67b4a126d5d35fe36c777791a3f7ba13def484e2d3908aff722fad4
|
||||||
|
68fb21696de95d0be911c2d3174f8afcc201035f7b6d8e69402de5451618
|
||||||
|
c21a535fa9d7bfc5b8dd9fc243f8cf927db31322d6e881eaa91a996170e6
|
||||||
|
57a05a266426d98c88003f8477c1227094a0d9fa1e8c4024309ce1ecccb5
|
||||||
|
210035d47ac72e8a`)
|
||||||
|
|
||||||
|
// Mock random reader
|
||||||
|
randReader = bytes.NewReader(fromHexBytes(`
|
||||||
|
017341ae3875d5f87101f8cc4fa9b9bc156bb04628fccdb2f4f11e905bd3
|
||||||
|
a155d376f593bd7304210874eba08a5e22bcccb4c9d3882a93a54db022f5
|
||||||
|
03d16338b6b7ce16dc7f4bbf9a96b59772d6606e9747c7649bf9e083db98
|
||||||
|
1884a954ab3c6f18b776ea21069d69776a33e96bad48e1dda0a5ef`))
|
||||||
|
defer resetRandReader()
|
||||||
|
|
||||||
|
// RSA-PKCS1v1.5 encrypt
|
||||||
|
enc := new(rsaEncrypterVerifier)
|
||||||
|
enc.publicKey = &priv.PublicKey
|
||||||
|
encryptedPKCS, err := enc.encrypt(input, RSA1_5)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Encryption failed:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(encryptedPKCS, expectedPKCS) != 0 {
|
||||||
|
t.Error("Output does not match expected value (PKCS1v1.5)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RSA-OAEP encrypt
|
||||||
|
encryptedOAEP, err := enc.encrypt(input, RSA_OAEP)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Encryption failed:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(encryptedOAEP, expectedOAEP) != 0 {
|
||||||
|
t.Error("Output does not match expected value (OAEP)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need fake cipher for PKCS1v1.5 decrypt
|
||||||
|
resetRandReader()
|
||||||
|
aes := newAESGCM(len(input))
|
||||||
|
|
||||||
|
keygen := randomKeyGenerator{
|
||||||
|
size: aes.keySize(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// RSA-PKCS1v1.5 decrypt
|
||||||
|
dec := new(rsaDecrypterSigner)
|
||||||
|
dec.privateKey = priv
|
||||||
|
decryptedPKCS, err := dec.decrypt(encryptedPKCS, RSA1_5, keygen)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Decryption failed:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(input, decryptedPKCS) != 0 {
|
||||||
|
t.Error("Output does not match expected value (PKCS1v1.5)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RSA-OAEP decrypt
|
||||||
|
decryptedOAEP, err := dec.decrypt(encryptedOAEP, RSA_OAEP, keygen)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("decryption failed:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(input, decryptedOAEP) != 0 {
|
||||||
|
t.Error("output does not match expected value (OAEP)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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{
|
||||||
|
Alg: string(ECDH_ES),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := dec.decryptKey(headers, nil, generator)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("ec decrypter accepted object with missing epk header")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid epk header
|
||||||
|
headers.Epk = &JsonWebKey{}
|
||||||
|
|
||||||
|
_, err = dec.decryptKey(headers, nil, generator)
|
||||||
|
if 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")
|
||||||
|
}
|
||||||
|
}
|
196
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/cbc_hmac.go
generated
vendored
Normal file
196
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/cbc_hmac.go
generated
vendored
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
/*-
|
||||||
|
* 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, len(plaintext)+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, len(dst)+len(ciphertext)+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("square/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("square/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{}, []byte(ciphertext[:offset])...)
|
||||||
|
|
||||||
|
if len(buffer)%ctx.blockCipher.BlockSize() > 0 {
|
||||||
|
return nil, errors.New("square/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, len(dst)+len(plaintext))
|
||||||
|
copy(out, plaintext)
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute an authentication tag
|
||||||
|
func (ctx *cbcAEAD) computeAuthTag(aad, nonce, ciphertext []byte) []byte {
|
||||||
|
buffer := make([]byte, len(aad)+len(nonce)+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 the 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 int) (head, tail []byte) {
|
||||||
|
if 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, len(buffer)+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("square/go-jose: invalid padding")
|
||||||
|
}
|
||||||
|
|
||||||
|
last := buffer[len(buffer)-1]
|
||||||
|
count := int(last)
|
||||||
|
|
||||||
|
if count == 0 || count > blockSize || count > len(buffer) {
|
||||||
|
return nil, errors.New("square/go-jose: invalid padding")
|
||||||
|
}
|
||||||
|
|
||||||
|
padding := bytes.Repeat([]byte{last}, count)
|
||||||
|
if !bytes.HasSuffix(buffer, padding) {
|
||||||
|
return nil, errors.New("square/go-jose: invalid padding")
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer[:len(buffer)-count], nil
|
||||||
|
}
|
498
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/cbc_hmac_test.go
generated
vendored
Normal file
498
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/cbc_hmac_test.go
generated
vendored
Normal file
|
@ -0,0 +1,498 @@
|
||||||
|
/*-
|
||||||
|
* 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.Compare(out[:len(out)-16], expectedCiphertext) != 0 {
|
||||||
|
t.Error("Ciphertext did not match")
|
||||||
|
}
|
||||||
|
if bytes.Compare(out[len(out)-16:], expectedAuthtag) != 0 {
|
||||||
|
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.Compare(out[:len(out)-32], expectedCiphertext) != 0 {
|
||||||
|
t.Error("Ciphertext did not match, got", out[:len(out)-32], "wanted", expectedCiphertext)
|
||||||
|
}
|
||||||
|
if bytes.Compare(out[len(out)-32:], expectedAuthtag) != 0 {
|
||||||
|
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.Compare(dst, result[:4]) != 0 {
|
||||||
|
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.Compare(result, plaintext) != 0 {
|
||||||
|
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)
|
||||||
|
|
||||||
|
io.ReadFull(rand.Reader, key)
|
||||||
|
io.ReadFull(rand.Reader, nonce)
|
||||||
|
|
||||||
|
aead, err := NewCBCHMAC(key, aes.NewCipher)
|
||||||
|
if err != nil {
|
||||||
|
panic(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], 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
|
||||||
|
|
||||||
|
io.ReadFull(rand.Reader, key)
|
||||||
|
io.ReadFull(rand.Reader, nonce)
|
||||||
|
|
||||||
|
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 := len(buffer)
|
||||||
|
ciphertext, tail := resize(buffer, size+(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)
|
||||||
|
data, 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)
|
||||||
|
|
||||||
|
io.ReadFull(rand.Reader, key)
|
||||||
|
io.ReadFull(rand.Reader, nonce)
|
||||||
|
|
||||||
|
chunk := make([]byte, chunkSize)
|
||||||
|
|
||||||
|
aead, err := NewCBCHMAC(key, aes.NewCipher)
|
||||||
|
if err != nil {
|
||||||
|
panic(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)
|
||||||
|
|
||||||
|
io.ReadFull(rand.Reader, key)
|
||||||
|
io.ReadFull(rand.Reader, nonce)
|
||||||
|
|
||||||
|
chunk := make([]byte, chunkSize)
|
||||||
|
|
||||||
|
aead, err := NewCBCHMAC(key, aes.NewCipher)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := aead.Seal(nil, nonce, chunk, nil)
|
||||||
|
|
||||||
|
b.SetBytes(int64(chunkSize))
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
aead.Open(nil, nonce, out, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
75
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/concat_kdf.go
generated
vendored
Normal file
75
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/concat_kdf.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/*-
|
||||||
|
* 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, len(algID)+len(ptyUInfo)+len(ptyVInfo)+len(supPubInfo)+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
|
||||||
|
}
|
150
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/concat_kdf_test.go
generated
vendored
Normal file
150
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/concat_kdf_test.go
generated
vendored
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
/*-
|
||||||
|
* 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.Compare(out, expected) != 0 {
|
||||||
|
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.Compare(outputs[i], outputs[(i+1)%len(outputs)]) != 0 {
|
||||||
|
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)
|
||||||
|
}
|
51
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/ecdh_es.go
generated
vendored
Normal file
51
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/ecdh_es.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*-
|
||||||
|
* 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"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"encoding/binary"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeriveECDHES derives a shared encryption key using ECDH/ConcatKDF as described in JWE/JWA.
|
||||||
|
func DeriveECDHES(alg string, apuData, apvData []byte, priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, size int) []byte {
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
z, _ := priv.PublicKey.Curve.ScalarMult(pub.X, pub.Y, priv.D.Bytes())
|
||||||
|
reader := NewConcatKDF(crypto.SHA256, z.Bytes(), algID, ptyUInfo, ptyVInfo, supPubInfo, []byte{})
|
||||||
|
|
||||||
|
key := make([]byte, size)
|
||||||
|
|
||||||
|
// Read on the KDF will never fail
|
||||||
|
_, _ = reader.Read(key)
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
func lengthPrefixed(data []byte) []byte {
|
||||||
|
out := make([]byte, len(data)+4)
|
||||||
|
binary.BigEndian.PutUint32(out, uint32(len(data)))
|
||||||
|
copy(out[4:], data)
|
||||||
|
return out
|
||||||
|
}
|
98
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/ecdh_es_test.go
generated
vendored
Normal file
98
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/ecdh_es_test.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*-
|
||||||
|
* 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")
|
||||||
|
}
|
||||||
|
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.Compare(output, expected) != 0 {
|
||||||
|
t.Error("output did not match what we expect, got", output, "wanted", expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
109
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/key_wrap.go
generated
vendored
Normal file
109
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/key_wrap.go
generated
vendored
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/*-
|
||||||
|
* 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("square/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] = 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("square/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] = 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("square/go-jose: failed to unwrap key")
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make([]byte, n*8)
|
||||||
|
for i := range r {
|
||||||
|
copy(out[i*8:], r[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
133
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/key_wrap_test.go
generated
vendored
Normal file
133
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/cipher/key_wrap_test.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
/*-
|
||||||
|
* 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.Compare(out0, expected0) != 0 {
|
||||||
|
t.Error("output 0 not as expected, got", out0, "wanted", expected0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(out1, expected1) != 0 {
|
||||||
|
t.Error("output 1 not as expected, got", out1, "wanted", expected1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(out2, expected2) != 0 {
|
||||||
|
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.Compare(unwrap0, cek0) != 0 {
|
||||||
|
t.Error("key unwrap did not return original input, got", unwrap0, "wanted", cek0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(unwrap1, cek1) != 0 {
|
||||||
|
t.Error("key unwrap did not return original input, got", unwrap1, "wanted", cek1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(unwrap2, cek2) != 0 {
|
||||||
|
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++ {
|
||||||
|
KeyWrap(block, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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++ {
|
||||||
|
KeyUnwrap(block, input)
|
||||||
|
}
|
||||||
|
}
|
349
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/crypter.go
generated
vendored
Normal file
349
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/crypter.go
generated
vendored
Normal file
|
@ -0,0 +1,349 @@
|
||||||
|
/*-
|
||||||
|
* 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"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
SetCompression(alg CompressionAlgorithm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiEncrypter represents an encrypter which supports multiple recipients.
|
||||||
|
type MultiEncrypter interface {
|
||||||
|
Encrypt(plaintext []byte) (*JsonWebEncryption, error)
|
||||||
|
EncryptWithAuthData(plaintext []byte, aad []byte) (*JsonWebEncryption, error)
|
||||||
|
SetCompression(alg CompressionAlgorithm)
|
||||||
|
AddRecipient(alg KeyAlgorithm, encryptionKey interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
type recipientKeyInfo struct {
|
||||||
|
keyID string
|
||||||
|
keyAlg KeyAlgorithm
|
||||||
|
keyEncrypter keyEncrypter
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCompression sets a compression algorithm to be applied before encryption.
|
||||||
|
func (ctx *genericEncrypter) SetCompression(compressionAlg CompressionAlgorithm) {
|
||||||
|
ctx.compressionAlg = compressionAlg
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncrypter creates an appropriate encrypter based on the key type
|
||||||
|
func NewEncrypter(alg KeyAlgorithm, enc ContentEncryption, encryptionKey interface{}) (Encrypter, error) {
|
||||||
|
encrypter := &genericEncrypter{
|
||||||
|
contentAlg: enc,
|
||||||
|
compressionAlg: NONE,
|
||||||
|
recipients: []recipientKeyInfo{},
|
||||||
|
cipher: getContentCipher(enc),
|
||||||
|
}
|
||||||
|
|
||||||
|
if encrypter.cipher == nil {
|
||||||
|
return nil, ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyID string
|
||||||
|
var rawKey interface{}
|
||||||
|
switch encryptionKey := encryptionKey.(type) {
|
||||||
|
case *JsonWebKey:
|
||||||
|
keyID = encryptionKey.KeyID
|
||||||
|
rawKey = encryptionKey.Key
|
||||||
|
default:
|
||||||
|
rawKey = encryptionKey
|
||||||
|
}
|
||||||
|
|
||||||
|
switch alg {
|
||||||
|
case DIRECT:
|
||||||
|
// Direct encryption mode must be treated differently
|
||||||
|
if reflect.TypeOf(rawKey) != reflect.TypeOf([]byte{}) {
|
||||||
|
return nil, ErrUnsupportedKeyType
|
||||||
|
}
|
||||||
|
encrypter.keyGenerator = staticKeyGenerator{
|
||||||
|
key: rawKey.([]byte),
|
||||||
|
}
|
||||||
|
recipient, _ := newSymmetricRecipient(alg, rawKey.([]byte))
|
||||||
|
if keyID != "" {
|
||||||
|
recipient.keyID = keyID
|
||||||
|
}
|
||||||
|
encrypter.recipients = []recipientKeyInfo{recipient}
|
||||||
|
return encrypter, nil
|
||||||
|
case ECDH_ES:
|
||||||
|
// ECDH-ES (w/o key wrapping) is similar to DIRECT mode
|
||||||
|
typeOf := reflect.TypeOf(rawKey)
|
||||||
|
if typeOf != reflect.TypeOf(&ecdsa.PublicKey{}) {
|
||||||
|
return nil, ErrUnsupportedKeyType
|
||||||
|
}
|
||||||
|
encrypter.keyGenerator = ecKeyGenerator{
|
||||||
|
size: encrypter.cipher.keySize(),
|
||||||
|
algID: string(enc),
|
||||||
|
publicKey: rawKey.(*ecdsa.PublicKey),
|
||||||
|
}
|
||||||
|
recipient, _ := newECDHRecipient(alg, rawKey.(*ecdsa.PublicKey))
|
||||||
|
if keyID != "" {
|
||||||
|
recipient.keyID = keyID
|
||||||
|
}
|
||||||
|
encrypter.recipients = []recipientKeyInfo{recipient}
|
||||||
|
return encrypter, nil
|
||||||
|
default:
|
||||||
|
// Can just add a standard recipient
|
||||||
|
encrypter.keyGenerator = randomKeyGenerator{
|
||||||
|
size: encrypter.cipher.keySize(),
|
||||||
|
}
|
||||||
|
err := encrypter.AddRecipient(alg, encryptionKey)
|
||||||
|
return encrypter, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMultiEncrypter creates a multi-encrypter based on the given parameters
|
||||||
|
func NewMultiEncrypter(enc ContentEncryption) (MultiEncrypter, error) {
|
||||||
|
cipher := getContentCipher(enc)
|
||||||
|
|
||||||
|
if cipher == nil {
|
||||||
|
return nil, ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
encrypter := &genericEncrypter{
|
||||||
|
contentAlg: enc,
|
||||||
|
compressionAlg: NONE,
|
||||||
|
recipients: []recipientKeyInfo{},
|
||||||
|
cipher: cipher,
|
||||||
|
keyGenerator: randomKeyGenerator{
|
||||||
|
size: cipher.keySize(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return encrypter, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *genericEncrypter) AddRecipient(alg KeyAlgorithm, encryptionKey interface{}) (err error) {
|
||||||
|
var recipient recipientKeyInfo
|
||||||
|
|
||||||
|
switch alg {
|
||||||
|
case DIRECT, ECDH_ES:
|
||||||
|
return fmt.Errorf("square/go-jose: key algorithm '%s' not supported in multi-recipient mode", alg)
|
||||||
|
}
|
||||||
|
|
||||||
|
recipient, err = makeJWERecipient(alg, encryptionKey)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
ctx.recipients = append(ctx.recipients, recipient)
|
||||||
|
}
|
||||||
|
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 *JsonWebKey:
|
||||||
|
recipient, err := makeJWERecipient(alg, encryptionKey.Key)
|
||||||
|
if err == nil && encryptionKey.KeyID != "" {
|
||||||
|
recipient.keyID = encryptionKey.KeyID
|
||||||
|
}
|
||||||
|
return recipient, err
|
||||||
|
default:
|
||||||
|
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 *JsonWebKey:
|
||||||
|
return newDecrypter(decryptionKey.Key)
|
||||||
|
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{
|
||||||
|
Enc: ctx.contentAlg,
|
||||||
|
}
|
||||||
|
obj.recipients = make([]recipientInfo, len(ctx.recipients))
|
||||||
|
|
||||||
|
if len(ctx.recipients) == 0 {
|
||||||
|
return nil, fmt.Errorf("square/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
|
||||||
|
}
|
||||||
|
|
||||||
|
recipient.header.Alg = string(info.keyAlg)
|
||||||
|
if info.keyID != "" {
|
||||||
|
recipient.header.Kid = info.keyID
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.protected.Zip = ctx.compressionAlg
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt and validate the object and return the plaintext.
|
||||||
|
func (obj JsonWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) {
|
||||||
|
headers := obj.mergedHeaders(nil)
|
||||||
|
|
||||||
|
if len(headers.Crit) > 0 {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: unsupported crit header")
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypter, err := newDecrypter(decryptionKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cipher := getContentCipher(headers.Enc)
|
||||||
|
if cipher == nil {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: unsupported enc value '%s'", string(headers.Enc))
|
||||||
|
}
|
||||||
|
|
||||||
|
generator := randomKeyGenerator{
|
||||||
|
size: cipher.keySize(),
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := &aeadParts{
|
||||||
|
iv: obj.iv,
|
||||||
|
ciphertext: obj.ciphertext,
|
||||||
|
tag: obj.tag,
|
||||||
|
}
|
||||||
|
|
||||||
|
authData := obj.computeAuthData()
|
||||||
|
|
||||||
|
var plaintext []byte
|
||||||
|
for _, 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 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if plaintext == nil {
|
||||||
|
return nil, ErrCryptoFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
// The "zip" header paramter may only be present in the protected header.
|
||||||
|
if obj.protected.Zip != "" {
|
||||||
|
plaintext, err = decompress(obj.protected.Zip, plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
return plaintext, err
|
||||||
|
}
|
784
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/crypter_test.go
generated
vendored
Normal file
784
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/crypter_test.go
generated
vendored
Normal file
|
@ -0,0 +1,784 @@
|
||||||
|
/*-
|
||||||
|
* 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/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"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)
|
||||||
|
|
||||||
|
func RoundtripJWE(keyAlg KeyAlgorithm, encAlg ContentEncryption, compressionAlg CompressionAlgorithm, serializer func(*JsonWebEncryption) (string, error), corrupter func(*JsonWebEncryption) bool, aad []byte, encryptionKey interface{}, decryptionKey interface{}) error {
|
||||||
|
enc, err := NewEncrypter(keyAlg, encAlg, encryptionKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error on new encrypter: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
enc.SetCompression(compressionAlg)
|
||||||
|
|
||||||
|
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)
|
||||||
|
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.Compare(parsed.GetAuthData(), aad) != 0 {
|
||||||
|
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.Compare(input, output) != 0 {
|
||||||
|
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}
|
||||||
|
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}
|
||||||
|
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
|
||||||
|
return bitflip(obj.protected.Tag.bytes())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(A128KW, A128GCM, &JsonWebKey{
|
||||||
|
KeyID: "test-id",
|
||||||
|
Key: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertext, _ := enc.Encrypt([]byte("Lorem ipsum dolor sit amet"))
|
||||||
|
|
||||||
|
serialized1, _ := ciphertext.CompactSerialize()
|
||||||
|
serialized2 := ciphertext.FullSerialize()
|
||||||
|
|
||||||
|
parsed1, _ := ParseEncrypted(serialized1)
|
||||||
|
parsed2, _ := ParseEncrypted(serialized2)
|
||||||
|
|
||||||
|
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}
|
||||||
|
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", "XYZ", nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("was able to instantiate encrypter with invalid cipher")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = NewMultiEncrypter("XYZ")
|
||||||
|
if err == nil {
|
||||||
|
t.Error("was able to instantiate multi-encrypter with invalid cipher")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = NewEncrypter(DIRECT, A128GCM, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("was able to instantiate encrypter with invalid direct key")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = NewEncrypter(ECDH_ES, A128GCM, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("was able to instantiate encrypter with invalid EC key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiRecipientJWE(t *testing.T) {
|
||||||
|
enc, err := NewMultiEncrypter(A128GCM)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = enc.AddRecipient(RSA_OAEP, &rsaTestKey.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error when adding RSA recipient", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = enc.AddRecipient(A256GCMKW, sharedKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error when adding AES recipient: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
input := []byte("Lorem ipsum dolor sit amet")
|
||||||
|
obj, err := enc.Encrypt(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error in encrypt: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := obj.FullSerialize()
|
||||||
|
|
||||||
|
parsed, err := ParseEncrypted(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error in parse: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := parsed.Decrypt(rsaTestKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error on decrypt with RSA: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(input, output) != 0 {
|
||||||
|
t.Error("Decrypted output does not match input: ", output, input)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err = parsed.Decrypt(sharedKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error on decrypt with AES: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(input, output) != 0 {
|
||||||
|
t.Error("Decrypted output does not match input", output, input)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiRecipientErrors(t *testing.T) {
|
||||||
|
enc, err := NewMultiEncrypter(A128GCM)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
input := []byte("Lorem ipsum dolor sit amet")
|
||||||
|
_, err = enc.Encrypt(input)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should fail when encrypting to zero recipients")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = enc.AddRecipient(DIRECT, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should reject DIRECT mode when encrypting to multiple recipients")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = enc.AddRecipient(ECDH_ES, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should reject ECDH_ES mode when encrypting to multiple recipients")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = enc.AddRecipient(RSA1_5, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should reject invalid recipient key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testKey struct {
|
||||||
|
enc, dec interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func symmetricTestKey(size int) []testKey {
|
||||||
|
key, _, _ := randomKeyGenerator{size: size}.genKey()
|
||||||
|
|
||||||
|
return []testKey{
|
||||||
|
testKey{
|
||||||
|
enc: key,
|
||||||
|
dec: key,
|
||||||
|
},
|
||||||
|
testKey{
|
||||||
|
enc: &JsonWebKey{KeyID: "test", Key: key},
|
||||||
|
dec: &JsonWebKey{KeyID: "test", Key: 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{
|
||||||
|
testKey{
|
||||||
|
dec: ecTestKey256,
|
||||||
|
enc: &ecTestKey256.PublicKey,
|
||||||
|
},
|
||||||
|
testKey{
|
||||||
|
dec: ecTestKey384,
|
||||||
|
enc: &ecTestKey384.PublicKey,
|
||||||
|
},
|
||||||
|
testKey{
|
||||||
|
dec: ecTestKey521,
|
||||||
|
enc: &ecTestKey521.PublicKey,
|
||||||
|
},
|
||||||
|
testKey{
|
||||||
|
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{testKey{
|
||||||
|
dec: rsaTestKey,
|
||||||
|
enc: &rsaTestKey.PublicKey,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("Must update test case")
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunRoundtripsJWE(b *testing.B, alg KeyAlgorithm, enc ContentEncryption, zip CompressionAlgorithm, priv, pub interface{}) {
|
||||||
|
serializer := func(obj *JsonWebEncryption) (string, error) {
|
||||||
|
return obj.CompactSerialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
corrupter := func(obj *JsonWebEncryption) bool { return false }
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err := RoundtripJWE(alg, enc, zip, serializer, corrupter, nil, pub, priv)
|
||||||
|
if err != nil {
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
|
||||||
|
symKey, _, _ = randomKeyGenerator{size: 32}.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, symKey),
|
||||||
|
"DirectCBC128": mustEncrypter(DIRECT, A128CBC_HS256, symKey),
|
||||||
|
"DirectGCM256": mustEncrypter(DIRECT, A256GCM, symKey),
|
||||||
|
"DirectCBC256": mustEncrypter(DIRECT, A256CBC_HS512, symKey),
|
||||||
|
"AESKWAndGCM128": mustEncrypter(A128KW, A128GCM, symKey),
|
||||||
|
"AESKWAndCBC256": mustEncrypter(A256KW, A256GCM, symKey),
|
||||||
|
"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++ {
|
||||||
|
enc.Encrypt(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
decryptionKeys = map[string]interface{}{
|
||||||
|
"OAEPAndGCM": rsaTestKey,
|
||||||
|
"PKCSAndGCM": rsaTestKey,
|
||||||
|
"OAEPAndCBC": rsaTestKey,
|
||||||
|
"PKCSAndCBC": rsaTestKey,
|
||||||
|
|
||||||
|
"DirectGCM128": symKey,
|
||||||
|
"DirectCBC128": symKey,
|
||||||
|
"DirectGCM256": symKey,
|
||||||
|
"DirectCBC256": symKey,
|
||||||
|
|
||||||
|
"AESKWAndGCM128": symKey,
|
||||||
|
"AESKWAndCBC256": symKey,
|
||||||
|
|
||||||
|
"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++ {
|
||||||
|
data.Decrypt(dec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustEncrypter(keyAlg KeyAlgorithm, encAlg ContentEncryption, encryptionKey interface{}) Encrypter {
|
||||||
|
enc, err := NewEncrypter(keyAlg, encAlg, encryptionKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return enc
|
||||||
|
}
|
26
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/doc.go
generated
vendored
Normal file
26
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*-
|
||||||
|
* 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. For the moment, it mainly focuses on
|
||||||
|
encryption and signing based on the JSON Web Encryption and JSON Web Signature
|
||||||
|
standards. The library supports both the compact and full serialization
|
||||||
|
formats, and has optional support for multiple recipients.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package jose // import "gopkg.in/square/go-jose.v1"
|
226
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/doc_test.go
generated
vendored
Normal file
226
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/doc_test.go
generated
vendored
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
/*-
|
||||||
|
* 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, _ = NewEncrypter(DIRECT, A128GCM, []byte{})
|
||||||
|
|
||||||
|
func Example_jWE() {
|
||||||
|
// Generate a public/private key pair to use for this example. The library
|
||||||
|
// also provides two utility functions (LoadPublicKey and LoadPrivateKey)
|
||||||
|
// that can be used to load keys from PEM/DER-encoded data.
|
||||||
|
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(RSA_OAEP, A128GCM, publicKey)
|
||||||
|
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 full 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)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we can decrypt and get back our original plaintext. An error here
|
||||||
|
// would indicate the 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.Printf(string(decrypted))
|
||||||
|
// output: Lorem ipsum dolor sit amet
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example_jWS() {
|
||||||
|
// Generate a public/private key pair to use for this example. The library
|
||||||
|
// also provides two utility functions (LoadPublicKey and LoadPrivateKey)
|
||||||
|
// that can be used to load keys from PEM/DER-encoded data.
|
||||||
|
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(PS512, privateKey)
|
||||||
|
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 encrypted object using the full 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)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we can verify the signature on the payload. An error here would
|
||||||
|
// indicate the 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.Printf(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(RSA_OAEP, A128GCM, publicKey)
|
||||||
|
|
||||||
|
// Instantiate an encrypter using RSA-PKCS1v1.5 with AES128-CBC+HMAC.
|
||||||
|
NewEncrypter(RSA1_5, A128CBC_HS256, publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleNewEncrypter_symmetric() {
|
||||||
|
var sharedKey []byte
|
||||||
|
|
||||||
|
// Instantiate an encrypter using AES128-GCM with AES-GCM key wrap.
|
||||||
|
NewEncrypter(A128GCMKW, A128GCM, sharedKey)
|
||||||
|
|
||||||
|
// Instantiate an encrypter using AES256-GCM directly, w/o key wrapping.
|
||||||
|
NewEncrypter(DIRECT, A256GCM, sharedKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleNewSigner_publicKey() {
|
||||||
|
var rsaPrivateKey *rsa.PrivateKey
|
||||||
|
var ecdsaPrivateKey *ecdsa.PrivateKey
|
||||||
|
|
||||||
|
// Instantiate a signer using RSA-PKCS#1v1.5 with SHA-256.
|
||||||
|
NewSigner(RS256, rsaPrivateKey)
|
||||||
|
|
||||||
|
// Instantiate a signer using ECDSA with SHA-384.
|
||||||
|
NewSigner(ES384, ecdsaPrivateKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleNewSigner_symmetric() {
|
||||||
|
var sharedKey []byte
|
||||||
|
|
||||||
|
// Instantiate an signer using HMAC-SHA256.
|
||||||
|
NewSigner(HS256, sharedKey)
|
||||||
|
|
||||||
|
// Instantiate an signer using HMAC-SHA512.
|
||||||
|
NewSigner(HS512, sharedKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleNewMultiEncrypter() {
|
||||||
|
var publicKey *rsa.PublicKey
|
||||||
|
var sharedKey []byte
|
||||||
|
|
||||||
|
// Instantiate an encrypter using AES-GCM.
|
||||||
|
encrypter, err := NewMultiEncrypter(A128GCM)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a recipient using a shared key with AES-GCM key wap
|
||||||
|
err = encrypter.AddRecipient(A128GCMKW, sharedKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a recipient using an RSA public key with RSA-OAEP
|
||||||
|
err = encrypter.AddRecipient(RSA_OAEP, publicKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleNewMultiSigner() {
|
||||||
|
var privateKey *rsa.PrivateKey
|
||||||
|
var sharedKey []byte
|
||||||
|
|
||||||
|
// Instantiate a signer for multiple recipients.
|
||||||
|
signer := NewMultiSigner()
|
||||||
|
|
||||||
|
// Add a recipient using a shared key with HMAC-SHA256
|
||||||
|
err := signer.AddRecipient(HS256, sharedKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a recipient using an RSA private key with RSASSA-PSS with SHA384
|
||||||
|
err = signer.AddRecipient(PS384, privateKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 full serialization.
|
||||||
|
var plaintext = []byte("This is a secret message")
|
||||||
|
var aad = []byte("This is authenticated, but public data")
|
||||||
|
|
||||||
|
encrypter.EncryptWithAuthData(plaintext, aad)
|
||||||
|
}
|
191
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/encoding.go
generated
vendored
Normal file
191
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/encoding.go
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
/*-
|
||||||
|
* 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"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var stripWhitespaceRegex = regexp.MustCompile("\\s")
|
||||||
|
|
||||||
|
// Url-safe base64 encode that strips padding
|
||||||
|
func base64URLEncode(data []byte) string {
|
||||||
|
var result = base64.URLEncoding.EncodeToString(data)
|
||||||
|
return strings.TrimRight(result, "=")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Url-safe base64 decoder that adds padding
|
||||||
|
func base64URLDecode(data string) ([]byte, error) {
|
||||||
|
var missing = (4 - len(data)%4) % 4
|
||||||
|
data += strings.Repeat("=", missing)
|
||||||
|
return base64.URLEncoding.DecodeString(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to serialize known-good objects.
|
||||||
|
// Precondition: value is not a nil pointer.
|
||||||
|
func mustSerializeJSON(value interface{}) []byte {
|
||||||
|
out, err := MarshalJSON(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/square/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 {
|
||||||
|
return stripWhitespaceRegex.ReplaceAllString(data, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compress with DEFLATE
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decompress with DEFLATE
|
||||||
|
func inflate(input []byte) ([]byte, error) {
|
||||||
|
output := new(bytes.Buffer)
|
||||||
|
reader := flate.NewReader(bytes.NewBuffer(input))
|
||||||
|
|
||||||
|
_, err := io.Copy(output, reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
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("square/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 MarshalJSON(b.base64())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *byteBuffer) UnmarshalJSON(data []byte) error {
|
||||||
|
var encoded string
|
||||||
|
err := UnmarshalJSON(data, &encoded)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if encoded == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded, err := base64URLDecode(encoded)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*b = *newBuffer(decoded)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *byteBuffer) base64() string {
|
||||||
|
return base64URLEncode(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())
|
||||||
|
}
|
173
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/encoding_test.go
generated
vendored
Normal file
173
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/encoding_test.go
generated
vendored
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
/*-
|
||||||
|
* 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"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBase64URLEncode(t *testing.T) {
|
||||||
|
// Test arrays with various sizes
|
||||||
|
if base64URLEncode([]byte{}) != "" {
|
||||||
|
t.Error("failed to encode empty array")
|
||||||
|
}
|
||||||
|
|
||||||
|
if base64URLEncode([]byte{0}) != "AA" {
|
||||||
|
t.Error("failed to encode [0x00]")
|
||||||
|
}
|
||||||
|
|
||||||
|
if base64URLEncode([]byte{0, 1}) != "AAE" {
|
||||||
|
t.Error("failed to encode [0x00, 0x01]")
|
||||||
|
}
|
||||||
|
|
||||||
|
if base64URLEncode([]byte{0, 1, 2}) != "AAEC" {
|
||||||
|
t.Error("failed to encode [0x00, 0x01, 0x02]")
|
||||||
|
}
|
||||||
|
|
||||||
|
if base64URLEncode([]byte{0, 1, 2, 3}) != "AAECAw" {
|
||||||
|
t.Error("failed to encode [0x00, 0x01, 0x02, 0x03]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBase64URLDecode(t *testing.T) {
|
||||||
|
// Test arrays with various sizes
|
||||||
|
val, err := base64URLDecode("")
|
||||||
|
if err != nil || !bytes.Equal(val, []byte{}) {
|
||||||
|
t.Error("failed to decode empty array")
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err = base64URLDecode("AA")
|
||||||
|
if err != nil || !bytes.Equal(val, []byte{0}) {
|
||||||
|
t.Error("failed to decode [0x00]")
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err = base64URLDecode("AAE")
|
||||||
|
if err != nil || !bytes.Equal(val, []byte{0, 1}) {
|
||||||
|
t.Error("failed to decode [0x00, 0x01]")
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err = base64URLDecode("AAEC")
|
||||||
|
if err != nil || !bytes.Equal(val, []byte{0, 1, 2}) {
|
||||||
|
t.Error("failed to decode [0x00, 0x01, 0x02]")
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err = base64URLDecode("AAECAw")
|
||||||
|
if err != nil || !bytes.Equal(val, []byte{0, 1, 2, 3}) {
|
||||||
|
t.Error("failed to decode [0x00, 0x01, 0x02, 0x03]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.Compare(output, original) != 0 {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
27
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/LICENSE
generated
vendored
Normal file
27
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
13
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/README.md
generated
vendored
Normal file
13
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/README.md
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# 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.
|
223
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/bench_test.go
generated
vendored
Normal file
223
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/bench_test.go
generated
vendored
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
// 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/ioutil"
|
||||||
|
"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 := ioutil.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(ioutil.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1183
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/decode.go
generated
vendored
Normal file
1183
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/decode.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1474
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/decode_test.go
generated
vendored
Normal file
1474
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/decode_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
1197
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/encode.go
generated
vendored
Normal file
1197
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/encode.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
538
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/encode_test.go
generated
vendored
Normal file
538
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/encode_test.go
generated
vendored
Normal file
|
@ -0,0 +1,538 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Optionals struct {
|
||||||
|
Sr string `json:"sr"`
|
||||||
|
So string `json:"so,omitempty"`
|
||||||
|
Sw string `json:"-"`
|
||||||
|
|
||||||
|
Ir int `json:"omitempty"` // actually named omitempty, not an option
|
||||||
|
Io int `json:"io,omitempty"`
|
||||||
|
|
||||||
|
Slr []string `json:"slr,random"`
|
||||||
|
Slo []string `json:"slo,omitempty"`
|
||||||
|
|
||||||
|
Mr map[string]interface{} `json:"mr"`
|
||||||
|
Mo map[string]interface{} `json:",omitempty"`
|
||||||
|
|
||||||
|
Fr float64 `json:"fr"`
|
||||||
|
Fo float64 `json:"fo,omitempty"`
|
||||||
|
|
||||||
|
Br bool `json:"br"`
|
||||||
|
Bo bool `json:"bo,omitempty"`
|
||||||
|
|
||||||
|
Ur uint `json:"ur"`
|
||||||
|
Uo uint `json:"uo,omitempty"`
|
||||||
|
|
||||||
|
Str struct{} `json:"str"`
|
||||||
|
Sto struct{} `json:"sto,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var optionalsExpected = `{
|
||||||
|
"sr": "",
|
||||||
|
"omitempty": 0,
|
||||||
|
"slr": null,
|
||||||
|
"mr": {},
|
||||||
|
"fr": 0,
|
||||||
|
"br": false,
|
||||||
|
"ur": 0,
|
||||||
|
"str": {},
|
||||||
|
"sto": {}
|
||||||
|
}`
|
||||||
|
|
||||||
|
func TestOmitEmpty(t *testing.T) {
|
||||||
|
var o Optionals
|
||||||
|
o.Sw = "something"
|
||||||
|
o.Mr = map[string]interface{}{}
|
||||||
|
o.Mo = map[string]interface{}{}
|
||||||
|
|
||||||
|
got, err := MarshalIndent(&o, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got := string(got); got != optionalsExpected {
|
||||||
|
t.Errorf(" got: %s\nwant: %s\n", got, optionalsExpected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type StringTag struct {
|
||||||
|
BoolStr bool `json:",string"`
|
||||||
|
IntStr int64 `json:",string"`
|
||||||
|
StrStr string `json:",string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var stringTagExpected = `{
|
||||||
|
"BoolStr": "true",
|
||||||
|
"IntStr": "42",
|
||||||
|
"StrStr": "\"xzbit\""
|
||||||
|
}`
|
||||||
|
|
||||||
|
func TestStringTag(t *testing.T) {
|
||||||
|
var s StringTag
|
||||||
|
s.BoolStr = true
|
||||||
|
s.IntStr = 42
|
||||||
|
s.StrStr = "xzbit"
|
||||||
|
got, err := MarshalIndent(&s, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if got := string(got); got != stringTagExpected {
|
||||||
|
t.Fatalf(" got: %s\nwant: %s\n", got, stringTagExpected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that it round-trips.
|
||||||
|
var s2 StringTag
|
||||||
|
err = NewDecoder(bytes.NewReader(got)).Decode(&s2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Decode: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(s, s2) {
|
||||||
|
t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", s, string(got), s2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// byte slices are special even if they're renamed types.
|
||||||
|
type renamedByte byte
|
||||||
|
type renamedByteSlice []byte
|
||||||
|
type renamedRenamedByteSlice []renamedByte
|
||||||
|
|
||||||
|
func TestEncodeRenamedByteSlice(t *testing.T) {
|
||||||
|
s := renamedByteSlice("abc")
|
||||||
|
result, err := Marshal(s)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expect := `"YWJj"`
|
||||||
|
if string(result) != expect {
|
||||||
|
t.Errorf(" got %s want %s", result, expect)
|
||||||
|
}
|
||||||
|
r := renamedRenamedByteSlice("abc")
|
||||||
|
result, err = Marshal(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(result) != expect {
|
||||||
|
t.Errorf(" got %s want %s", result, expect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var unsupportedValues = []interface{}{
|
||||||
|
math.NaN(),
|
||||||
|
math.Inf(-1),
|
||||||
|
math.Inf(1),
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnsupportedValues(t *testing.T) {
|
||||||
|
for _, v := range unsupportedValues {
|
||||||
|
if _, err := Marshal(v); err != nil {
|
||||||
|
if _, ok := err.(*UnsupportedValueError); !ok {
|
||||||
|
t.Errorf("for %v, got %T want UnsupportedValueError", v, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("for %v, expected error", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ref has Marshaler and Unmarshaler methods with pointer receiver.
|
||||||
|
type Ref int
|
||||||
|
|
||||||
|
func (*Ref) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(`"ref"`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Ref) UnmarshalJSON([]byte) error {
|
||||||
|
*r = 12
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Val has Marshaler methods with value receiver.
|
||||||
|
type Val int
|
||||||
|
|
||||||
|
func (Val) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(`"val"`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefText has Marshaler and Unmarshaler methods with pointer receiver.
|
||||||
|
type RefText int
|
||||||
|
|
||||||
|
func (*RefText) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(`"ref"`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RefText) UnmarshalText([]byte) error {
|
||||||
|
*r = 13
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValText has Marshaler methods with value receiver.
|
||||||
|
type ValText int
|
||||||
|
|
||||||
|
func (ValText) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(`"val"`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRefValMarshal(t *testing.T) {
|
||||||
|
var s = struct {
|
||||||
|
R0 Ref
|
||||||
|
R1 *Ref
|
||||||
|
R2 RefText
|
||||||
|
R3 *RefText
|
||||||
|
V0 Val
|
||||||
|
V1 *Val
|
||||||
|
V2 ValText
|
||||||
|
V3 *ValText
|
||||||
|
}{
|
||||||
|
R0: 12,
|
||||||
|
R1: new(Ref),
|
||||||
|
R2: 14,
|
||||||
|
R3: new(RefText),
|
||||||
|
V0: 13,
|
||||||
|
V1: new(Val),
|
||||||
|
V2: 15,
|
||||||
|
V3: new(ValText),
|
||||||
|
}
|
||||||
|
const want = `{"R0":"ref","R1":"ref","R2":"\"ref\"","R3":"\"ref\"","V0":"val","V1":"val","V2":"\"val\"","V3":"\"val\""}`
|
||||||
|
b, err := Marshal(&s)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal: %v", err)
|
||||||
|
}
|
||||||
|
if got := string(b); got != want {
|
||||||
|
t.Errorf("got %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// C implements Marshaler and returns unescaped JSON.
|
||||||
|
type C int
|
||||||
|
|
||||||
|
func (C) MarshalJSON() ([]byte, error) {
|
||||||
|
return []byte(`"<&>"`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CText implements Marshaler and returns unescaped text.
|
||||||
|
type CText int
|
||||||
|
|
||||||
|
func (CText) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(`"<&>"`), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalerEscaping(t *testing.T) {
|
||||||
|
var c C
|
||||||
|
want := `"\u003c\u0026\u003e"`
|
||||||
|
b, err := Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal(c): %v", err)
|
||||||
|
}
|
||||||
|
if got := string(b); got != want {
|
||||||
|
t.Errorf("Marshal(c) = %#q, want %#q", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ct CText
|
||||||
|
want = `"\"\u003c\u0026\u003e\""`
|
||||||
|
b, err = Marshal(ct)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal(ct): %v", err)
|
||||||
|
}
|
||||||
|
if got := string(b); got != want {
|
||||||
|
t.Errorf("Marshal(ct) = %#q, want %#q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntType int
|
||||||
|
|
||||||
|
type MyStruct struct {
|
||||||
|
IntType
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnonymousNonstruct(t *testing.T) {
|
||||||
|
var i IntType = 11
|
||||||
|
a := MyStruct{i}
|
||||||
|
const want = `{"IntType":11}`
|
||||||
|
|
||||||
|
b, err := Marshal(a)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal: %v", err)
|
||||||
|
}
|
||||||
|
if got := string(b); got != want {
|
||||||
|
t.Errorf("got %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type BugA struct {
|
||||||
|
S string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BugB struct {
|
||||||
|
BugA
|
||||||
|
S string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BugC struct {
|
||||||
|
S string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legal Go: We never use the repeated embedded field (S).
|
||||||
|
type BugX struct {
|
||||||
|
A int
|
||||||
|
BugA
|
||||||
|
BugB
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue 5245.
|
||||||
|
func TestEmbeddedBug(t *testing.T) {
|
||||||
|
v := BugB{
|
||||||
|
BugA{"A"},
|
||||||
|
"B",
|
||||||
|
}
|
||||||
|
b, err := Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Marshal:", err)
|
||||||
|
}
|
||||||
|
want := `{"S":"B"}`
|
||||||
|
got := string(b)
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("Marshal: got %s want %s", got, want)
|
||||||
|
}
|
||||||
|
// Now check that the duplicate field, S, does not appear.
|
||||||
|
x := BugX{
|
||||||
|
A: 23,
|
||||||
|
}
|
||||||
|
b, err = Marshal(x)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Marshal:", err)
|
||||||
|
}
|
||||||
|
want = `{"A":23}`
|
||||||
|
got = string(b)
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("Marshal: got %s want %s", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type BugD struct { // Same as BugA after tagging.
|
||||||
|
XXX string `json:"S"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BugD's tagged S field should dominate BugA's.
|
||||||
|
type BugY struct {
|
||||||
|
BugA
|
||||||
|
BugD
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that a field with a tag dominates untagged fields.
|
||||||
|
func TestTaggedFieldDominates(t *testing.T) {
|
||||||
|
v := BugY{
|
||||||
|
BugA{"BugA"},
|
||||||
|
BugD{"BugD"},
|
||||||
|
}
|
||||||
|
b, err := Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Marshal:", err)
|
||||||
|
}
|
||||||
|
want := `{"S":"BugD"}`
|
||||||
|
got := string(b)
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("Marshal: got %s want %s", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are no tags here, so S should not appear.
|
||||||
|
type BugZ struct {
|
||||||
|
BugA
|
||||||
|
BugC
|
||||||
|
BugY // Contains a tagged S field through BugD; should not dominate.
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDuplicatedFieldDisappears(t *testing.T) {
|
||||||
|
v := BugZ{
|
||||||
|
BugA{"BugA"},
|
||||||
|
BugC{"BugC"},
|
||||||
|
BugY{
|
||||||
|
BugA{"nested BugA"},
|
||||||
|
BugD{"nested BugD"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
b, err := Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Marshal:", err)
|
||||||
|
}
|
||||||
|
want := `{}`
|
||||||
|
got := string(b)
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("Marshal: got %s want %s", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringBytes(t *testing.T) {
|
||||||
|
// Test that encodeState.stringBytes and encodeState.string use the same encoding.
|
||||||
|
es := &encodeState{}
|
||||||
|
var r []rune
|
||||||
|
for i := '\u0000'; i <= unicode.MaxRune; i++ {
|
||||||
|
r = append(r, i)
|
||||||
|
}
|
||||||
|
s := string(r) + "\xff\xff\xffhello" // some invalid UTF-8 too
|
||||||
|
es.string(s)
|
||||||
|
|
||||||
|
esBytes := &encodeState{}
|
||||||
|
esBytes.stringBytes([]byte(s))
|
||||||
|
|
||||||
|
enc := es.Buffer.String()
|
||||||
|
encBytes := esBytes.Buffer.String()
|
||||||
|
if enc != encBytes {
|
||||||
|
i := 0
|
||||||
|
for i < len(enc) && i < len(encBytes) && enc[i] == encBytes[i] {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
enc = enc[i:]
|
||||||
|
encBytes = encBytes[i:]
|
||||||
|
i = 0
|
||||||
|
for i < len(enc) && i < len(encBytes) && enc[len(enc)-i-1] == encBytes[len(encBytes)-i-1] {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
enc = enc[:len(enc)-i]
|
||||||
|
encBytes = encBytes[:len(encBytes)-i]
|
||||||
|
|
||||||
|
if len(enc) > 20 {
|
||||||
|
enc = enc[:20] + "..."
|
||||||
|
}
|
||||||
|
if len(encBytes) > 20 {
|
||||||
|
encBytes = encBytes[:20] + "..."
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Errorf("encodings differ at %#q vs %#q", enc, encBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue6458(t *testing.T) {
|
||||||
|
type Foo struct {
|
||||||
|
M RawMessage
|
||||||
|
}
|
||||||
|
x := Foo{RawMessage(`"foo"`)}
|
||||||
|
|
||||||
|
b, err := Marshal(&x)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if want := `{"M":"foo"}`; string(b) != want {
|
||||||
|
t.Errorf("Marshal(&x) = %#q; want %#q", b, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err = Marshal(x)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if want := `{"M":"ImZvbyI="}`; string(b) != want {
|
||||||
|
t.Errorf("Marshal(x) = %#q; want %#q", b, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue10281(t *testing.T) {
|
||||||
|
type Foo struct {
|
||||||
|
N Number
|
||||||
|
}
|
||||||
|
x := Foo{Number(`invalid`)}
|
||||||
|
|
||||||
|
b, err := Marshal(&x)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Marshal(&x) = %#q; want error", b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHTMLEscape(t *testing.T) {
|
||||||
|
var b, want bytes.Buffer
|
||||||
|
m := `{"M":"<html>foo &` + "\xe2\x80\xa8 \xe2\x80\xa9" + `</html>"}`
|
||||||
|
want.Write([]byte(`{"M":"\u003chtml\u003efoo \u0026\u2028 \u2029\u003c/html\u003e"}`))
|
||||||
|
HTMLEscape(&b, []byte(m))
|
||||||
|
if !bytes.Equal(b.Bytes(), want.Bytes()) {
|
||||||
|
t.Errorf("HTMLEscape(&b, []byte(m)) = %s; want %s", b.Bytes(), want.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// golang.org/issue/8582
|
||||||
|
func TestEncodePointerString(t *testing.T) {
|
||||||
|
type stringPointer struct {
|
||||||
|
N *int64 `json:"n,string"`
|
||||||
|
}
|
||||||
|
var n int64 = 42
|
||||||
|
b, err := Marshal(stringPointer{N: &n})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal: %v", err)
|
||||||
|
}
|
||||||
|
if got, want := string(b), `{"n":"42"}`; got != want {
|
||||||
|
t.Errorf("Marshal = %s, want %s", got, want)
|
||||||
|
}
|
||||||
|
var back stringPointer
|
||||||
|
err = Unmarshal(b, &back)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if back.N == nil {
|
||||||
|
t.Fatalf("Unmarshalled nil N field")
|
||||||
|
}
|
||||||
|
if *back.N != 42 {
|
||||||
|
t.Fatalf("*N = %d; want 42", *back.N)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var encodeStringTests = []struct {
|
||||||
|
in string
|
||||||
|
out string
|
||||||
|
}{
|
||||||
|
{"\x00", `"\u0000"`},
|
||||||
|
{"\x01", `"\u0001"`},
|
||||||
|
{"\x02", `"\u0002"`},
|
||||||
|
{"\x03", `"\u0003"`},
|
||||||
|
{"\x04", `"\u0004"`},
|
||||||
|
{"\x05", `"\u0005"`},
|
||||||
|
{"\x06", `"\u0006"`},
|
||||||
|
{"\x07", `"\u0007"`},
|
||||||
|
{"\x08", `"\u0008"`},
|
||||||
|
{"\x09", `"\t"`},
|
||||||
|
{"\x0a", `"\n"`},
|
||||||
|
{"\x0b", `"\u000b"`},
|
||||||
|
{"\x0c", `"\u000c"`},
|
||||||
|
{"\x0d", `"\r"`},
|
||||||
|
{"\x0e", `"\u000e"`},
|
||||||
|
{"\x0f", `"\u000f"`},
|
||||||
|
{"\x10", `"\u0010"`},
|
||||||
|
{"\x11", `"\u0011"`},
|
||||||
|
{"\x12", `"\u0012"`},
|
||||||
|
{"\x13", `"\u0013"`},
|
||||||
|
{"\x14", `"\u0014"`},
|
||||||
|
{"\x15", `"\u0015"`},
|
||||||
|
{"\x16", `"\u0016"`},
|
||||||
|
{"\x17", `"\u0017"`},
|
||||||
|
{"\x18", `"\u0018"`},
|
||||||
|
{"\x19", `"\u0019"`},
|
||||||
|
{"\x1a", `"\u001a"`},
|
||||||
|
{"\x1b", `"\u001b"`},
|
||||||
|
{"\x1c", `"\u001c"`},
|
||||||
|
{"\x1d", `"\u001d"`},
|
||||||
|
{"\x1e", `"\u001e"`},
|
||||||
|
{"\x1f", `"\u001f"`},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncodeString(t *testing.T) {
|
||||||
|
for _, tt := range encodeStringTests {
|
||||||
|
b, err := Marshal(tt.in)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Marshal(%q): %v", tt.in, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out := string(b)
|
||||||
|
if out != tt.out {
|
||||||
|
t.Errorf("Marshal(%q) = %#q, want %#q", tt.in, out, tt.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
141
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/indent.go
generated
vendored
Normal file
141
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/indent.go
generated
vendored
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
// Compact appends to dst the JSON-encoded src with
|
||||||
|
// insignificant space characters elided.
|
||||||
|
func Compact(dst *bytes.Buffer, src []byte) error {
|
||||||
|
return compact(dst, src, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compact(dst *bytes.Buffer, src []byte, escape bool) error {
|
||||||
|
origLen := dst.Len()
|
||||||
|
var scan scanner
|
||||||
|
scan.reset()
|
||||||
|
start := 0
|
||||||
|
for i, c := range src {
|
||||||
|
if escape && (c == '<' || c == '>' || c == '&') {
|
||||||
|
if start < i {
|
||||||
|
dst.Write(src[start:i])
|
||||||
|
}
|
||||||
|
dst.WriteString(`\u00`)
|
||||||
|
dst.WriteByte(hex[c>>4])
|
||||||
|
dst.WriteByte(hex[c&0xF])
|
||||||
|
start = i + 1
|
||||||
|
}
|
||||||
|
// Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
|
||||||
|
if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
|
||||||
|
if start < i {
|
||||||
|
dst.Write(src[start:i])
|
||||||
|
}
|
||||||
|
dst.WriteString(`\u202`)
|
||||||
|
dst.WriteByte(hex[src[i+2]&0xF])
|
||||||
|
start = i + 3
|
||||||
|
}
|
||||||
|
v := scan.step(&scan, c)
|
||||||
|
if v >= scanSkipSpace {
|
||||||
|
if v == scanError {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if start < i {
|
||||||
|
dst.Write(src[start:i])
|
||||||
|
}
|
||||||
|
start = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if scan.eof() == scanError {
|
||||||
|
dst.Truncate(origLen)
|
||||||
|
return scan.err
|
||||||
|
}
|
||||||
|
if start < len(src) {
|
||||||
|
dst.Write(src[start:])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newline(dst *bytes.Buffer, prefix, indent string, depth int) {
|
||||||
|
dst.WriteByte('\n')
|
||||||
|
dst.WriteString(prefix)
|
||||||
|
for i := 0; i < depth; i++ {
|
||||||
|
dst.WriteString(indent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indent appends to dst an indented form of the JSON-encoded src.
|
||||||
|
// Each element in a JSON object or array begins on a new,
|
||||||
|
// indented line beginning with prefix followed by one or more
|
||||||
|
// copies of indent according to the indentation nesting.
|
||||||
|
// The data appended to dst does not begin with the prefix nor
|
||||||
|
// any indentation, to make it easier to embed inside other formatted JSON data.
|
||||||
|
// Although leading space characters (space, tab, carriage return, newline)
|
||||||
|
// at the beginning of src are dropped, trailing space characters
|
||||||
|
// at the end of src are preserved and copied to dst.
|
||||||
|
// For example, if src has no trailing spaces, neither will dst;
|
||||||
|
// if src ends in a trailing newline, so will dst.
|
||||||
|
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
|
||||||
|
origLen := dst.Len()
|
||||||
|
var scan scanner
|
||||||
|
scan.reset()
|
||||||
|
needIndent := false
|
||||||
|
depth := 0
|
||||||
|
for _, c := range src {
|
||||||
|
scan.bytes++
|
||||||
|
v := scan.step(&scan, c)
|
||||||
|
if v == scanSkipSpace {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v == scanError {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if needIndent && v != scanEndObject && v != scanEndArray {
|
||||||
|
needIndent = false
|
||||||
|
depth++
|
||||||
|
newline(dst, prefix, indent, depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit semantically uninteresting bytes
|
||||||
|
// (in particular, punctuation in strings) unmodified.
|
||||||
|
if v == scanContinue {
|
||||||
|
dst.WriteByte(c)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add spacing around real punctuation.
|
||||||
|
switch c {
|
||||||
|
case '{', '[':
|
||||||
|
// delay indent so that empty object and array are formatted as {} and [].
|
||||||
|
needIndent = true
|
||||||
|
dst.WriteByte(c)
|
||||||
|
|
||||||
|
case ',':
|
||||||
|
dst.WriteByte(c)
|
||||||
|
newline(dst, prefix, indent, depth)
|
||||||
|
|
||||||
|
case ':':
|
||||||
|
dst.WriteByte(c)
|
||||||
|
dst.WriteByte(' ')
|
||||||
|
|
||||||
|
case '}', ']':
|
||||||
|
if needIndent {
|
||||||
|
// suppress indent in empty object/array
|
||||||
|
needIndent = false
|
||||||
|
} else {
|
||||||
|
depth--
|
||||||
|
newline(dst, prefix, indent, depth)
|
||||||
|
}
|
||||||
|
dst.WriteByte(c)
|
||||||
|
|
||||||
|
default:
|
||||||
|
dst.WriteByte(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if scan.eof() == scanError {
|
||||||
|
dst.Truncate(origLen)
|
||||||
|
return scan.err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
133
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/number_test.go
generated
vendored
Normal file
133
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/number_test.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNumberIsValid(t *testing.T) {
|
||||||
|
// From: http://stackoverflow.com/a/13340826
|
||||||
|
var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`)
|
||||||
|
|
||||||
|
validTests := []string{
|
||||||
|
"0",
|
||||||
|
"-0",
|
||||||
|
"1",
|
||||||
|
"-1",
|
||||||
|
"0.1",
|
||||||
|
"-0.1",
|
||||||
|
"1234",
|
||||||
|
"-1234",
|
||||||
|
"12.34",
|
||||||
|
"-12.34",
|
||||||
|
"12E0",
|
||||||
|
"12E1",
|
||||||
|
"12e34",
|
||||||
|
"12E-0",
|
||||||
|
"12e+1",
|
||||||
|
"12e-34",
|
||||||
|
"-12E0",
|
||||||
|
"-12E1",
|
||||||
|
"-12e34",
|
||||||
|
"-12E-0",
|
||||||
|
"-12e+1",
|
||||||
|
"-12e-34",
|
||||||
|
"1.2E0",
|
||||||
|
"1.2E1",
|
||||||
|
"1.2e34",
|
||||||
|
"1.2E-0",
|
||||||
|
"1.2e+1",
|
||||||
|
"1.2e-34",
|
||||||
|
"-1.2E0",
|
||||||
|
"-1.2E1",
|
||||||
|
"-1.2e34",
|
||||||
|
"-1.2E-0",
|
||||||
|
"-1.2e+1",
|
||||||
|
"-1.2e-34",
|
||||||
|
"0E0",
|
||||||
|
"0E1",
|
||||||
|
"0e34",
|
||||||
|
"0E-0",
|
||||||
|
"0e+1",
|
||||||
|
"0e-34",
|
||||||
|
"-0E0",
|
||||||
|
"-0E1",
|
||||||
|
"-0e34",
|
||||||
|
"-0E-0",
|
||||||
|
"-0e+1",
|
||||||
|
"-0e-34",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range validTests {
|
||||||
|
if !isValidNumber(test) {
|
||||||
|
t.Errorf("%s should be valid", test)
|
||||||
|
}
|
||||||
|
|
||||||
|
var f float64
|
||||||
|
if err := Unmarshal([]byte(test), &f); err != nil {
|
||||||
|
t.Errorf("%s should be valid but Unmarshal failed: %v", test, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !jsonNumberRegexp.MatchString(test) {
|
||||||
|
t.Errorf("%s should be valid but regexp does not match", test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidTests := []string{
|
||||||
|
"",
|
||||||
|
"invalid",
|
||||||
|
"1.0.1",
|
||||||
|
"1..1",
|
||||||
|
"-1-2",
|
||||||
|
"012a42",
|
||||||
|
"01.2",
|
||||||
|
"012",
|
||||||
|
"12E12.12",
|
||||||
|
"1e2e3",
|
||||||
|
"1e+-2",
|
||||||
|
"1e--23",
|
||||||
|
"1e",
|
||||||
|
"e1",
|
||||||
|
"1e+",
|
||||||
|
"1ea",
|
||||||
|
"1a",
|
||||||
|
"1.a",
|
||||||
|
"1.",
|
||||||
|
"01",
|
||||||
|
"1.e1",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range invalidTests {
|
||||||
|
if isValidNumber(test) {
|
||||||
|
t.Errorf("%s should be invalid", test)
|
||||||
|
}
|
||||||
|
|
||||||
|
var f float64
|
||||||
|
if err := Unmarshal([]byte(test), &f); err == nil {
|
||||||
|
t.Errorf("%s should be invalid but unmarshal wrote %v", test, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
if jsonNumberRegexp.MatchString(test) {
|
||||||
|
t.Errorf("%s should be invalid but matches regexp", test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNumberIsValid(b *testing.B) {
|
||||||
|
s := "-61657.61667E+61673"
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
isValidNumber(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNumberIsValidRegexp(b *testing.B) {
|
||||||
|
var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`)
|
||||||
|
s := "-61657.61667E+61673"
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
jsonNumberRegexp.MatchString(s)
|
||||||
|
}
|
||||||
|
}
|
623
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/scanner.go
generated
vendored
Normal file
623
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/scanner.go
generated
vendored
Normal file
|
@ -0,0 +1,623 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// JSON value parser state machine.
|
||||||
|
// Just about at the limit of what is reasonable to write by hand.
|
||||||
|
// Some parts are a bit tedious, but overall it nicely factors out the
|
||||||
|
// otherwise common code from the multiple scanning functions
|
||||||
|
// in this package (Compact, Indent, checkValid, nextValue, etc).
|
||||||
|
//
|
||||||
|
// This file starts with two simple examples using the scanner
|
||||||
|
// before diving into the scanner itself.
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// checkValid verifies that data is valid JSON-encoded data.
|
||||||
|
// scan is passed in for use by checkValid to avoid an allocation.
|
||||||
|
func checkValid(data []byte, scan *scanner) error {
|
||||||
|
scan.reset()
|
||||||
|
for _, c := range data {
|
||||||
|
scan.bytes++
|
||||||
|
if scan.step(scan, c) == scanError {
|
||||||
|
return scan.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if scan.eof() == scanError {
|
||||||
|
return scan.err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// nextValue splits data after the next whole JSON value,
|
||||||
|
// returning that value and the bytes that follow it as separate slices.
|
||||||
|
// scan is passed in for use by nextValue to avoid an allocation.
|
||||||
|
func nextValue(data []byte, scan *scanner) (value, rest []byte, err error) {
|
||||||
|
scan.reset()
|
||||||
|
for i, c := range data {
|
||||||
|
v := scan.step(scan, c)
|
||||||
|
if v >= scanEndObject {
|
||||||
|
switch v {
|
||||||
|
// probe the scanner with a space to determine whether we will
|
||||||
|
// get scanEnd on the next character. Otherwise, if the next character
|
||||||
|
// is not a space, scanEndTop allocates a needless error.
|
||||||
|
case scanEndObject, scanEndArray:
|
||||||
|
if scan.step(scan, ' ') == scanEnd {
|
||||||
|
return data[:i+1], data[i+1:], nil
|
||||||
|
}
|
||||||
|
case scanError:
|
||||||
|
return nil, nil, scan.err
|
||||||
|
case scanEnd:
|
||||||
|
return data[:i], data[i:], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if scan.eof() == scanError {
|
||||||
|
return nil, nil, scan.err
|
||||||
|
}
|
||||||
|
return data, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A SyntaxError is a description of a JSON syntax error.
|
||||||
|
type SyntaxError struct {
|
||||||
|
msg string // description of error
|
||||||
|
Offset int64 // error occurred after reading Offset bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SyntaxError) Error() string { return e.msg }
|
||||||
|
|
||||||
|
// A scanner is a JSON scanning state machine.
|
||||||
|
// Callers call scan.reset() and then pass bytes in one at a time
|
||||||
|
// by calling scan.step(&scan, c) for each byte.
|
||||||
|
// The return value, referred to as an opcode, tells the
|
||||||
|
// caller about significant parsing events like beginning
|
||||||
|
// and ending literals, objects, and arrays, so that the
|
||||||
|
// caller can follow along if it wishes.
|
||||||
|
// The return value scanEnd indicates that a single top-level
|
||||||
|
// JSON value has been completed, *before* the byte that
|
||||||
|
// just got passed in. (The indication must be delayed in order
|
||||||
|
// to recognize the end of numbers: is 123 a whole value or
|
||||||
|
// the beginning of 12345e+6?).
|
||||||
|
type scanner struct {
|
||||||
|
// The step is a func to be called to execute the next transition.
|
||||||
|
// Also tried using an integer constant and a single func
|
||||||
|
// with a switch, but using the func directly was 10% faster
|
||||||
|
// on a 64-bit Mac Mini, and it's nicer to read.
|
||||||
|
step func(*scanner, byte) int
|
||||||
|
|
||||||
|
// Reached end of top-level value.
|
||||||
|
endTop bool
|
||||||
|
|
||||||
|
// Stack of what we're in the middle of - array values, object keys, object values.
|
||||||
|
parseState []int
|
||||||
|
|
||||||
|
// Error that happened, if any.
|
||||||
|
err error
|
||||||
|
|
||||||
|
// 1-byte redo (see undo method)
|
||||||
|
redo bool
|
||||||
|
redoCode int
|
||||||
|
redoState func(*scanner, byte) int
|
||||||
|
|
||||||
|
// total bytes consumed, updated by decoder.Decode
|
||||||
|
bytes int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// These values are returned by the state transition functions
|
||||||
|
// assigned to scanner.state and the method scanner.eof.
|
||||||
|
// They give details about the current state of the scan that
|
||||||
|
// callers might be interested to know about.
|
||||||
|
// It is okay to ignore the return value of any particular
|
||||||
|
// call to scanner.state: if one call returns scanError,
|
||||||
|
// every subsequent call will return scanError too.
|
||||||
|
const (
|
||||||
|
// Continue.
|
||||||
|
scanContinue = iota // uninteresting byte
|
||||||
|
scanBeginLiteral // end implied by next result != scanContinue
|
||||||
|
scanBeginObject // begin object
|
||||||
|
scanObjectKey // just finished object key (string)
|
||||||
|
scanObjectValue // just finished non-last object value
|
||||||
|
scanEndObject // end object (implies scanObjectValue if possible)
|
||||||
|
scanBeginArray // begin array
|
||||||
|
scanArrayValue // just finished array value
|
||||||
|
scanEndArray // end array (implies scanArrayValue if possible)
|
||||||
|
scanSkipSpace // space byte; can skip; known to be last "continue" result
|
||||||
|
|
||||||
|
// Stop.
|
||||||
|
scanEnd // top-level value ended *before* this byte; known to be first "stop" result
|
||||||
|
scanError // hit an error, scanner.err.
|
||||||
|
)
|
||||||
|
|
||||||
|
// These values are stored in the parseState stack.
|
||||||
|
// They give the current state of a composite value
|
||||||
|
// being scanned. If the parser is inside a nested value
|
||||||
|
// the parseState describes the nested state, outermost at entry 0.
|
||||||
|
const (
|
||||||
|
parseObjectKey = iota // parsing object key (before colon)
|
||||||
|
parseObjectValue // parsing object value (after colon)
|
||||||
|
parseArrayValue // parsing array value
|
||||||
|
)
|
||||||
|
|
||||||
|
// reset prepares the scanner for use.
|
||||||
|
// It must be called before calling s.step.
|
||||||
|
func (s *scanner) reset() {
|
||||||
|
s.step = stateBeginValue
|
||||||
|
s.parseState = s.parseState[0:0]
|
||||||
|
s.err = nil
|
||||||
|
s.redo = false
|
||||||
|
s.endTop = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// eof tells the scanner that the end of input has been reached.
|
||||||
|
// It returns a scan status just as s.step does.
|
||||||
|
func (s *scanner) eof() int {
|
||||||
|
if s.err != nil {
|
||||||
|
return scanError
|
||||||
|
}
|
||||||
|
if s.endTop {
|
||||||
|
return scanEnd
|
||||||
|
}
|
||||||
|
s.step(s, ' ')
|
||||||
|
if s.endTop {
|
||||||
|
return scanEnd
|
||||||
|
}
|
||||||
|
if s.err == nil {
|
||||||
|
s.err = &SyntaxError{"unexpected end of JSON input", s.bytes}
|
||||||
|
}
|
||||||
|
return scanError
|
||||||
|
}
|
||||||
|
|
||||||
|
// pushParseState pushes a new parse state p onto the parse stack.
|
||||||
|
func (s *scanner) pushParseState(p int) {
|
||||||
|
s.parseState = append(s.parseState, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// popParseState pops a parse state (already obtained) off the stack
|
||||||
|
// and updates s.step accordingly.
|
||||||
|
func (s *scanner) popParseState() {
|
||||||
|
n := len(s.parseState) - 1
|
||||||
|
s.parseState = s.parseState[0:n]
|
||||||
|
s.redo = false
|
||||||
|
if n == 0 {
|
||||||
|
s.step = stateEndTop
|
||||||
|
s.endTop = true
|
||||||
|
} else {
|
||||||
|
s.step = stateEndValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSpace(c byte) bool {
|
||||||
|
return c == ' ' || c == '\t' || c == '\r' || c == '\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateBeginValueOrEmpty is the state after reading `[`.
|
||||||
|
func stateBeginValueOrEmpty(s *scanner, c byte) int {
|
||||||
|
if c <= ' ' && isSpace(c) {
|
||||||
|
return scanSkipSpace
|
||||||
|
}
|
||||||
|
if c == ']' {
|
||||||
|
return stateEndValue(s, c)
|
||||||
|
}
|
||||||
|
return stateBeginValue(s, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateBeginValue is the state at the beginning of the input.
|
||||||
|
func stateBeginValue(s *scanner, c byte) int {
|
||||||
|
if c <= ' ' && isSpace(c) {
|
||||||
|
return scanSkipSpace
|
||||||
|
}
|
||||||
|
switch c {
|
||||||
|
case '{':
|
||||||
|
s.step = stateBeginStringOrEmpty
|
||||||
|
s.pushParseState(parseObjectKey)
|
||||||
|
return scanBeginObject
|
||||||
|
case '[':
|
||||||
|
s.step = stateBeginValueOrEmpty
|
||||||
|
s.pushParseState(parseArrayValue)
|
||||||
|
return scanBeginArray
|
||||||
|
case '"':
|
||||||
|
s.step = stateInString
|
||||||
|
return scanBeginLiteral
|
||||||
|
case '-':
|
||||||
|
s.step = stateNeg
|
||||||
|
return scanBeginLiteral
|
||||||
|
case '0': // beginning of 0.123
|
||||||
|
s.step = state0
|
||||||
|
return scanBeginLiteral
|
||||||
|
case 't': // beginning of true
|
||||||
|
s.step = stateT
|
||||||
|
return scanBeginLiteral
|
||||||
|
case 'f': // beginning of false
|
||||||
|
s.step = stateF
|
||||||
|
return scanBeginLiteral
|
||||||
|
case 'n': // beginning of null
|
||||||
|
s.step = stateN
|
||||||
|
return scanBeginLiteral
|
||||||
|
}
|
||||||
|
if '1' <= c && c <= '9' { // beginning of 1234.5
|
||||||
|
s.step = state1
|
||||||
|
return scanBeginLiteral
|
||||||
|
}
|
||||||
|
return s.error(c, "looking for beginning of value")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateBeginStringOrEmpty is the state after reading `{`.
|
||||||
|
func stateBeginStringOrEmpty(s *scanner, c byte) int {
|
||||||
|
if c <= ' ' && isSpace(c) {
|
||||||
|
return scanSkipSpace
|
||||||
|
}
|
||||||
|
if c == '}' {
|
||||||
|
n := len(s.parseState)
|
||||||
|
s.parseState[n-1] = parseObjectValue
|
||||||
|
return stateEndValue(s, c)
|
||||||
|
}
|
||||||
|
return stateBeginString(s, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateBeginString is the state after reading `{"key": value,`.
|
||||||
|
func stateBeginString(s *scanner, c byte) int {
|
||||||
|
if c <= ' ' && isSpace(c) {
|
||||||
|
return scanSkipSpace
|
||||||
|
}
|
||||||
|
if c == '"' {
|
||||||
|
s.step = stateInString
|
||||||
|
return scanBeginLiteral
|
||||||
|
}
|
||||||
|
return s.error(c, "looking for beginning of object key string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateEndValue is the state after completing a value,
|
||||||
|
// such as after reading `{}` or `true` or `["x"`.
|
||||||
|
func stateEndValue(s *scanner, c byte) int {
|
||||||
|
n := len(s.parseState)
|
||||||
|
if n == 0 {
|
||||||
|
// Completed top-level before the current byte.
|
||||||
|
s.step = stateEndTop
|
||||||
|
s.endTop = true
|
||||||
|
return stateEndTop(s, c)
|
||||||
|
}
|
||||||
|
if c <= ' ' && isSpace(c) {
|
||||||
|
s.step = stateEndValue
|
||||||
|
return scanSkipSpace
|
||||||
|
}
|
||||||
|
ps := s.parseState[n-1]
|
||||||
|
switch ps {
|
||||||
|
case parseObjectKey:
|
||||||
|
if c == ':' {
|
||||||
|
s.parseState[n-1] = parseObjectValue
|
||||||
|
s.step = stateBeginValue
|
||||||
|
return scanObjectKey
|
||||||
|
}
|
||||||
|
return s.error(c, "after object key")
|
||||||
|
case parseObjectValue:
|
||||||
|
if c == ',' {
|
||||||
|
s.parseState[n-1] = parseObjectKey
|
||||||
|
s.step = stateBeginString
|
||||||
|
return scanObjectValue
|
||||||
|
}
|
||||||
|
if c == '}' {
|
||||||
|
s.popParseState()
|
||||||
|
return scanEndObject
|
||||||
|
}
|
||||||
|
return s.error(c, "after object key:value pair")
|
||||||
|
case parseArrayValue:
|
||||||
|
if c == ',' {
|
||||||
|
s.step = stateBeginValue
|
||||||
|
return scanArrayValue
|
||||||
|
}
|
||||||
|
if c == ']' {
|
||||||
|
s.popParseState()
|
||||||
|
return scanEndArray
|
||||||
|
}
|
||||||
|
return s.error(c, "after array element")
|
||||||
|
}
|
||||||
|
return s.error(c, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateEndTop is the state after finishing the top-level value,
|
||||||
|
// such as after reading `{}` or `[1,2,3]`.
|
||||||
|
// Only space characters should be seen now.
|
||||||
|
func stateEndTop(s *scanner, c byte) int {
|
||||||
|
if c != ' ' && c != '\t' && c != '\r' && c != '\n' {
|
||||||
|
// Complain about non-space byte on next call.
|
||||||
|
s.error(c, "after top-level value")
|
||||||
|
}
|
||||||
|
return scanEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateInString is the state after reading `"`.
|
||||||
|
func stateInString(s *scanner, c byte) int {
|
||||||
|
if c == '"' {
|
||||||
|
s.step = stateEndValue
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
if c == '\\' {
|
||||||
|
s.step = stateInStringEsc
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
if c < 0x20 {
|
||||||
|
return s.error(c, "in string literal")
|
||||||
|
}
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateInStringEsc is the state after reading `"\` during a quoted string.
|
||||||
|
func stateInStringEsc(s *scanner, c byte) int {
|
||||||
|
switch c {
|
||||||
|
case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
|
||||||
|
s.step = stateInString
|
||||||
|
return scanContinue
|
||||||
|
case 'u':
|
||||||
|
s.step = stateInStringEscU
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "in string escape code")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateInStringEscU is the state after reading `"\u` during a quoted string.
|
||||||
|
func stateInStringEscU(s *scanner, c byte) int {
|
||||||
|
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||||
|
s.step = stateInStringEscU1
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
// numbers
|
||||||
|
return s.error(c, "in \\u hexadecimal character escape")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateInStringEscU1 is the state after reading `"\u1` during a quoted string.
|
||||||
|
func stateInStringEscU1(s *scanner, c byte) int {
|
||||||
|
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||||
|
s.step = stateInStringEscU12
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
// numbers
|
||||||
|
return s.error(c, "in \\u hexadecimal character escape")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateInStringEscU12 is the state after reading `"\u12` during a quoted string.
|
||||||
|
func stateInStringEscU12(s *scanner, c byte) int {
|
||||||
|
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||||
|
s.step = stateInStringEscU123
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
// numbers
|
||||||
|
return s.error(c, "in \\u hexadecimal character escape")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateInStringEscU123 is the state after reading `"\u123` during a quoted string.
|
||||||
|
func stateInStringEscU123(s *scanner, c byte) int {
|
||||||
|
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||||
|
s.step = stateInString
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
// numbers
|
||||||
|
return s.error(c, "in \\u hexadecimal character escape")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateNeg is the state after reading `-` during a number.
|
||||||
|
func stateNeg(s *scanner, c byte) int {
|
||||||
|
if c == '0' {
|
||||||
|
s.step = state0
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
if '1' <= c && c <= '9' {
|
||||||
|
s.step = state1
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "in numeric literal")
|
||||||
|
}
|
||||||
|
|
||||||
|
// state1 is the state after reading a non-zero integer during a number,
|
||||||
|
// such as after reading `1` or `100` but not `0`.
|
||||||
|
func state1(s *scanner, c byte) int {
|
||||||
|
if '0' <= c && c <= '9' {
|
||||||
|
s.step = state1
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return state0(s, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// state0 is the state after reading `0` during a number.
|
||||||
|
func state0(s *scanner, c byte) int {
|
||||||
|
if c == '.' {
|
||||||
|
s.step = stateDot
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
if c == 'e' || c == 'E' {
|
||||||
|
s.step = stateE
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return stateEndValue(s, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateDot is the state after reading the integer and decimal point in a number,
|
||||||
|
// such as after reading `1.`.
|
||||||
|
func stateDot(s *scanner, c byte) int {
|
||||||
|
if '0' <= c && c <= '9' {
|
||||||
|
s.step = stateDot0
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "after decimal point in numeric literal")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateDot0 is the state after reading the integer, decimal point, and subsequent
|
||||||
|
// digits of a number, such as after reading `3.14`.
|
||||||
|
func stateDot0(s *scanner, c byte) int {
|
||||||
|
if '0' <= c && c <= '9' {
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
if c == 'e' || c == 'E' {
|
||||||
|
s.step = stateE
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return stateEndValue(s, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateE is the state after reading the mantissa and e in a number,
|
||||||
|
// such as after reading `314e` or `0.314e`.
|
||||||
|
func stateE(s *scanner, c byte) int {
|
||||||
|
if c == '+' || c == '-' {
|
||||||
|
s.step = stateESign
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return stateESign(s, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateESign is the state after reading the mantissa, e, and sign in a number,
|
||||||
|
// such as after reading `314e-` or `0.314e+`.
|
||||||
|
func stateESign(s *scanner, c byte) int {
|
||||||
|
if '0' <= c && c <= '9' {
|
||||||
|
s.step = stateE0
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "in exponent of numeric literal")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateE0 is the state after reading the mantissa, e, optional sign,
|
||||||
|
// and at least one digit of the exponent in a number,
|
||||||
|
// such as after reading `314e-2` or `0.314e+1` or `3.14e0`.
|
||||||
|
func stateE0(s *scanner, c byte) int {
|
||||||
|
if '0' <= c && c <= '9' {
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return stateEndValue(s, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateT is the state after reading `t`.
|
||||||
|
func stateT(s *scanner, c byte) int {
|
||||||
|
if c == 'r' {
|
||||||
|
s.step = stateTr
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "in literal true (expecting 'r')")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateTr is the state after reading `tr`.
|
||||||
|
func stateTr(s *scanner, c byte) int {
|
||||||
|
if c == 'u' {
|
||||||
|
s.step = stateTru
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "in literal true (expecting 'u')")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateTru is the state after reading `tru`.
|
||||||
|
func stateTru(s *scanner, c byte) int {
|
||||||
|
if c == 'e' {
|
||||||
|
s.step = stateEndValue
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "in literal true (expecting 'e')")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateF is the state after reading `f`.
|
||||||
|
func stateF(s *scanner, c byte) int {
|
||||||
|
if c == 'a' {
|
||||||
|
s.step = stateFa
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "in literal false (expecting 'a')")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateFa is the state after reading `fa`.
|
||||||
|
func stateFa(s *scanner, c byte) int {
|
||||||
|
if c == 'l' {
|
||||||
|
s.step = stateFal
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "in literal false (expecting 'l')")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateFal is the state after reading `fal`.
|
||||||
|
func stateFal(s *scanner, c byte) int {
|
||||||
|
if c == 's' {
|
||||||
|
s.step = stateFals
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "in literal false (expecting 's')")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateFals is the state after reading `fals`.
|
||||||
|
func stateFals(s *scanner, c byte) int {
|
||||||
|
if c == 'e' {
|
||||||
|
s.step = stateEndValue
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "in literal false (expecting 'e')")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateN is the state after reading `n`.
|
||||||
|
func stateN(s *scanner, c byte) int {
|
||||||
|
if c == 'u' {
|
||||||
|
s.step = stateNu
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "in literal null (expecting 'u')")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateNu is the state after reading `nu`.
|
||||||
|
func stateNu(s *scanner, c byte) int {
|
||||||
|
if c == 'l' {
|
||||||
|
s.step = stateNul
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "in literal null (expecting 'l')")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateNul is the state after reading `nul`.
|
||||||
|
func stateNul(s *scanner, c byte) int {
|
||||||
|
if c == 'l' {
|
||||||
|
s.step = stateEndValue
|
||||||
|
return scanContinue
|
||||||
|
}
|
||||||
|
return s.error(c, "in literal null (expecting 'l')")
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateError is the state after reaching a syntax error,
|
||||||
|
// such as after reading `[1}` or `5.1.2`.
|
||||||
|
func stateError(s *scanner, c byte) int {
|
||||||
|
return scanError
|
||||||
|
}
|
||||||
|
|
||||||
|
// error records an error and switches to the error state.
|
||||||
|
func (s *scanner) error(c byte, context string) int {
|
||||||
|
s.step = stateError
|
||||||
|
s.err = &SyntaxError{"invalid character " + quoteChar(c) + " " + context, s.bytes}
|
||||||
|
return scanError
|
||||||
|
}
|
||||||
|
|
||||||
|
// quoteChar formats c as a quoted character literal
|
||||||
|
func quoteChar(c byte) string {
|
||||||
|
// special cases - different from quoted strings
|
||||||
|
if c == '\'' {
|
||||||
|
return `'\''`
|
||||||
|
}
|
||||||
|
if c == '"' {
|
||||||
|
return `'"'`
|
||||||
|
}
|
||||||
|
|
||||||
|
// use quoted string with different quotation marks
|
||||||
|
s := strconv.Quote(string(c))
|
||||||
|
return "'" + s[1:len(s)-1] + "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
// undo causes the scanner to return scanCode from the next state transition.
|
||||||
|
// This gives callers a simple 1-byte undo mechanism.
|
||||||
|
func (s *scanner) undo(scanCode int) {
|
||||||
|
if s.redo {
|
||||||
|
panic("json: invalid use of scanner")
|
||||||
|
}
|
||||||
|
s.redoCode = scanCode
|
||||||
|
s.redoState = s.step
|
||||||
|
s.step = stateRedo
|
||||||
|
s.redo = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateRedo helps implement the scanner's 1-byte undo.
|
||||||
|
func stateRedo(s *scanner, c byte) int {
|
||||||
|
s.redo = false
|
||||||
|
s.step = s.redoState
|
||||||
|
return s.redoCode
|
||||||
|
}
|
316
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/scanner_test.go
generated
vendored
Normal file
316
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/scanner_test.go
generated
vendored
Normal file
|
@ -0,0 +1,316 @@
|
||||||
|
// 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"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tests of simple examples.
|
||||||
|
|
||||||
|
type example struct {
|
||||||
|
compact string
|
||||||
|
indent string
|
||||||
|
}
|
||||||
|
|
||||||
|
var examples = []example{
|
||||||
|
{`1`, `1`},
|
||||||
|
{`{}`, `{}`},
|
||||||
|
{`[]`, `[]`},
|
||||||
|
{`{"":2}`, "{\n\t\"\": 2\n}"},
|
||||||
|
{`[3]`, "[\n\t3\n]"},
|
||||||
|
{`[1,2,3]`, "[\n\t1,\n\t2,\n\t3\n]"},
|
||||||
|
{`{"x":1}`, "{\n\t\"x\": 1\n}"},
|
||||||
|
{ex1, ex1i},
|
||||||
|
}
|
||||||
|
|
||||||
|
var ex1 = `[true,false,null,"x",1,1.5,0,-5e+2]`
|
||||||
|
|
||||||
|
var ex1i = `[
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
"x",
|
||||||
|
1,
|
||||||
|
1.5,
|
||||||
|
0,
|
||||||
|
-5e+2
|
||||||
|
]`
|
||||||
|
|
||||||
|
func TestCompact(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for _, tt := range examples {
|
||||||
|
buf.Reset()
|
||||||
|
if err := Compact(&buf, []byte(tt.compact)); err != nil {
|
||||||
|
t.Errorf("Compact(%#q): %v", tt.compact, err)
|
||||||
|
} else if s := buf.String(); s != tt.compact {
|
||||||
|
t.Errorf("Compact(%#q) = %#q, want original", tt.compact, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
if err := Compact(&buf, []byte(tt.indent)); err != nil {
|
||||||
|
t.Errorf("Compact(%#q): %v", tt.indent, err)
|
||||||
|
continue
|
||||||
|
} else if s := buf.String(); s != tt.compact {
|
||||||
|
t.Errorf("Compact(%#q) = %#q, want %#q", tt.indent, s, tt.compact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompactSeparators(t *testing.T) {
|
||||||
|
// U+2028 and U+2029 should be escaped inside strings.
|
||||||
|
// They should not appear outside strings.
|
||||||
|
tests := []struct {
|
||||||
|
in, compact string
|
||||||
|
}{
|
||||||
|
{"{\"\u2028\": 1}", `{"\u2028":1}`},
|
||||||
|
{"{\"\u2029\" :2}", `{"\u2029":2}`},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := Compact(&buf, []byte(tt.in)); err != nil {
|
||||||
|
t.Errorf("Compact(%q): %v", tt.in, err)
|
||||||
|
} else if s := buf.String(); s != tt.compact {
|
||||||
|
t.Errorf("Compact(%q) = %q, want %q", tt.in, s, tt.compact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIndent(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for _, tt := range examples {
|
||||||
|
buf.Reset()
|
||||||
|
if err := Indent(&buf, []byte(tt.indent), "", "\t"); err != nil {
|
||||||
|
t.Errorf("Indent(%#q): %v", tt.indent, err)
|
||||||
|
} else if s := buf.String(); s != tt.indent {
|
||||||
|
t.Errorf("Indent(%#q) = %#q, want original", tt.indent, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
if err := Indent(&buf, []byte(tt.compact), "", "\t"); err != nil {
|
||||||
|
t.Errorf("Indent(%#q): %v", tt.compact, err)
|
||||||
|
continue
|
||||||
|
} else if s := buf.String(); s != tt.indent {
|
||||||
|
t.Errorf("Indent(%#q) = %#q, want %#q", tt.compact, s, tt.indent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests of a large random structure.
|
||||||
|
|
||||||
|
func TestCompactBig(t *testing.T) {
|
||||||
|
initBig()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := Compact(&buf, jsonBig); err != nil {
|
||||||
|
t.Fatalf("Compact: %v", err)
|
||||||
|
}
|
||||||
|
b := buf.Bytes()
|
||||||
|
if !bytes.Equal(b, jsonBig) {
|
||||||
|
t.Error("Compact(jsonBig) != jsonBig")
|
||||||
|
diff(t, b, jsonBig)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIndentBig(t *testing.T) {
|
||||||
|
initBig()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := Indent(&buf, jsonBig, "", "\t"); err != nil {
|
||||||
|
t.Fatalf("Indent1: %v", err)
|
||||||
|
}
|
||||||
|
b := buf.Bytes()
|
||||||
|
if len(b) == len(jsonBig) {
|
||||||
|
// jsonBig is compact (no unnecessary spaces);
|
||||||
|
// indenting should make it bigger
|
||||||
|
t.Fatalf("Indent(jsonBig) did not get bigger")
|
||||||
|
}
|
||||||
|
|
||||||
|
// should be idempotent
|
||||||
|
var buf1 bytes.Buffer
|
||||||
|
if err := Indent(&buf1, b, "", "\t"); err != nil {
|
||||||
|
t.Fatalf("Indent2: %v", err)
|
||||||
|
}
|
||||||
|
b1 := buf1.Bytes()
|
||||||
|
if !bytes.Equal(b1, b) {
|
||||||
|
t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)")
|
||||||
|
diff(t, b1, b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// should get back to original
|
||||||
|
buf1.Reset()
|
||||||
|
if err := Compact(&buf1, b); err != nil {
|
||||||
|
t.Fatalf("Compact: %v", err)
|
||||||
|
}
|
||||||
|
b1 = buf1.Bytes()
|
||||||
|
if !bytes.Equal(b1, jsonBig) {
|
||||||
|
t.Error("Compact(Indent(jsonBig)) != jsonBig")
|
||||||
|
diff(t, b1, jsonBig)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type indentErrorTest struct {
|
||||||
|
in string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var indentErrorTests = []indentErrorTest{
|
||||||
|
{`{"X": "foo", "Y"}`, &SyntaxError{"invalid character '}' after object key", 17}},
|
||||||
|
{`{"X": "foo" "Y": "bar"}`, &SyntaxError{"invalid character '\"' after object key:value pair", 13}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIndentErrors(t *testing.T) {
|
||||||
|
for i, tt := range indentErrorTests {
|
||||||
|
slice := make([]uint8, 0)
|
||||||
|
buf := bytes.NewBuffer(slice)
|
||||||
|
if err := Indent(buf, []uint8(tt.in), "", ""); err != nil {
|
||||||
|
if !reflect.DeepEqual(err, tt.err) {
|
||||||
|
t.Errorf("#%d: Indent: %#v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNextValueBig(t *testing.T) {
|
||||||
|
initBig()
|
||||||
|
var scan scanner
|
||||||
|
item, rest, err := nextValue(jsonBig, &scan)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("nextValue: %s", err)
|
||||||
|
}
|
||||||
|
if len(item) != len(jsonBig) || &item[0] != &jsonBig[0] {
|
||||||
|
t.Errorf("invalid item: %d %d", len(item), len(jsonBig))
|
||||||
|
}
|
||||||
|
if len(rest) != 0 {
|
||||||
|
t.Errorf("invalid rest: %d", len(rest))
|
||||||
|
}
|
||||||
|
|
||||||
|
item, rest, err = nextValue(append(jsonBig, "HELLO WORLD"...), &scan)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("nextValue extra: %s", err)
|
||||||
|
}
|
||||||
|
if len(item) != len(jsonBig) {
|
||||||
|
t.Errorf("invalid item: %d %d", len(item), len(jsonBig))
|
||||||
|
}
|
||||||
|
if string(rest) != "HELLO WORLD" {
|
||||||
|
t.Errorf("invalid rest: %d", len(rest))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var benchScan scanner
|
||||||
|
|
||||||
|
func BenchmarkSkipValue(b *testing.B) {
|
||||||
|
initBig()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
nextValue(jsonBig, &benchScan)
|
||||||
|
}
|
||||||
|
b.SetBytes(int64(len(jsonBig)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func diff(t *testing.T, a, b []byte) {
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
if i >= len(a) || i >= len(b) || a[i] != b[i] {
|
||||||
|
j := i - 10
|
||||||
|
if j < 0 {
|
||||||
|
j = 0
|
||||||
|
}
|
||||||
|
t.Errorf("diverge at %d: «%s» vs «%s»", i, trim(a[j:]), trim(b[j:]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func trim(b []byte) []byte {
|
||||||
|
if len(b) > 20 {
|
||||||
|
return b[0:20]
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a random JSON object.
|
||||||
|
|
||||||
|
var jsonBig []byte
|
||||||
|
|
||||||
|
func initBig() {
|
||||||
|
n := 10000
|
||||||
|
if testing.Short() {
|
||||||
|
n = 100
|
||||||
|
}
|
||||||
|
b, err := Marshal(genValue(n))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
jsonBig = b
|
||||||
|
}
|
||||||
|
|
||||||
|
func genValue(n int) interface{} {
|
||||||
|
if n > 1 {
|
||||||
|
switch rand.Intn(2) {
|
||||||
|
case 0:
|
||||||
|
return genArray(n)
|
||||||
|
case 1:
|
||||||
|
return genMap(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch rand.Intn(3) {
|
||||||
|
case 0:
|
||||||
|
return rand.Intn(2) == 0
|
||||||
|
case 1:
|
||||||
|
return rand.NormFloat64()
|
||||||
|
case 2:
|
||||||
|
return genString(30)
|
||||||
|
}
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
|
||||||
|
func genString(stddev float64) string {
|
||||||
|
n := int(math.Abs(rand.NormFloat64()*stddev + stddev/2))
|
||||||
|
c := make([]rune, n)
|
||||||
|
for i := range c {
|
||||||
|
f := math.Abs(rand.NormFloat64()*64 + 32)
|
||||||
|
if f > 0x10ffff {
|
||||||
|
f = 0x10ffff
|
||||||
|
}
|
||||||
|
c[i] = rune(f)
|
||||||
|
}
|
||||||
|
return string(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func genArray(n int) []interface{} {
|
||||||
|
f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
|
||||||
|
if f > n {
|
||||||
|
f = n
|
||||||
|
}
|
||||||
|
if f < 1 {
|
||||||
|
f = 1
|
||||||
|
}
|
||||||
|
x := make([]interface{}, f)
|
||||||
|
for i := range x {
|
||||||
|
x[i] = genValue(((i+1)*n)/f - (i*n)/f)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func genMap(n int) map[string]interface{} {
|
||||||
|
f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
|
||||||
|
if f > n {
|
||||||
|
f = n
|
||||||
|
}
|
||||||
|
if n > 0 && f == 0 {
|
||||||
|
f = 1
|
||||||
|
}
|
||||||
|
x := make(map[string]interface{})
|
||||||
|
for i := 0; i < f; i++ {
|
||||||
|
x[genString(10)] = genValue(((i+1)*n)/f - (i*n)/f)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
480
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/stream.go
generated
vendored
Normal file
480
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/stream.go
generated
vendored
Normal file
|
@ -0,0 +1,480 @@
|
||||||
|
// 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"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Decoder reads and decodes JSON objects from an input stream.
|
||||||
|
type Decoder struct {
|
||||||
|
r io.Reader
|
||||||
|
buf []byte
|
||||||
|
d decodeState
|
||||||
|
scanp int // start of unread data in buf
|
||||||
|
scan scanner
|
||||||
|
err error
|
||||||
|
|
||||||
|
tokenState int
|
||||||
|
tokenStack []int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder returns a new decoder that reads from r.
|
||||||
|
//
|
||||||
|
// The decoder introduces its own buffering and may
|
||||||
|
// read data from r beyond the JSON values requested.
|
||||||
|
func NewDecoder(r io.Reader) *Decoder {
|
||||||
|
return &Decoder{r: r}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
|
||||||
|
// Number instead of as a float64.
|
||||||
|
func (dec *Decoder) UseNumber() { dec.d.useNumber = true }
|
||||||
|
|
||||||
|
// Decode reads the next JSON-encoded value from its
|
||||||
|
// input and stores it in the value pointed to by v.
|
||||||
|
//
|
||||||
|
// See the documentation for Unmarshal for details about
|
||||||
|
// the conversion of JSON into a Go value.
|
||||||
|
func (dec *Decoder) Decode(v interface{}) error {
|
||||||
|
if dec.err != nil {
|
||||||
|
return dec.err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dec.tokenPrepareForDecode(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !dec.tokenValueAllowed() {
|
||||||
|
return &SyntaxError{msg: "not at beginning of value"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read whole value into buffer.
|
||||||
|
n, err := dec.readValue()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dec.d.init(dec.buf[dec.scanp : dec.scanp+n])
|
||||||
|
dec.scanp += n
|
||||||
|
|
||||||
|
// Don't save err from unmarshal into dec.err:
|
||||||
|
// the connection is still usable since we read a complete JSON
|
||||||
|
// object from it before the error happened.
|
||||||
|
err = dec.d.unmarshal(v)
|
||||||
|
|
||||||
|
// fixup token streaming state
|
||||||
|
dec.tokenValueEnd()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffered returns a reader of the data remaining in the Decoder's
|
||||||
|
// buffer. The reader is valid until the next call to Decode.
|
||||||
|
func (dec *Decoder) Buffered() io.Reader {
|
||||||
|
return bytes.NewReader(dec.buf[dec.scanp:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// readValue reads a JSON value into dec.buf.
|
||||||
|
// It returns the length of the encoding.
|
||||||
|
func (dec *Decoder) readValue() (int, error) {
|
||||||
|
dec.scan.reset()
|
||||||
|
|
||||||
|
scanp := dec.scanp
|
||||||
|
var err error
|
||||||
|
Input:
|
||||||
|
for {
|
||||||
|
// Look in the buffer for a new value.
|
||||||
|
for i, c := range dec.buf[scanp:] {
|
||||||
|
dec.scan.bytes++
|
||||||
|
v := dec.scan.step(&dec.scan, c)
|
||||||
|
if v == scanEnd {
|
||||||
|
scanp += i
|
||||||
|
break Input
|
||||||
|
}
|
||||||
|
// scanEnd is delayed one byte.
|
||||||
|
// We might block trying to get that byte from src,
|
||||||
|
// so instead invent a space byte.
|
||||||
|
if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd {
|
||||||
|
scanp += i + 1
|
||||||
|
break Input
|
||||||
|
}
|
||||||
|
if v == scanError {
|
||||||
|
dec.err = dec.scan.err
|
||||||
|
return 0, dec.scan.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scanp = len(dec.buf)
|
||||||
|
|
||||||
|
// Did the last read have an error?
|
||||||
|
// Delayed until now to allow buffer scan.
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
if dec.scan.step(&dec.scan, ' ') == scanEnd {
|
||||||
|
break Input
|
||||||
|
}
|
||||||
|
if nonSpace(dec.buf) {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dec.err = err
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n := scanp - dec.scanp
|
||||||
|
err = dec.refill()
|
||||||
|
scanp = dec.scanp + n
|
||||||
|
}
|
||||||
|
return scanp - dec.scanp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *Decoder) refill() error {
|
||||||
|
// Make room to read more into the buffer.
|
||||||
|
// First slide down data already consumed.
|
||||||
|
if dec.scanp > 0 {
|
||||||
|
n := copy(dec.buf, dec.buf[dec.scanp:])
|
||||||
|
dec.buf = dec.buf[:n]
|
||||||
|
dec.scanp = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grow buffer if not large enough.
|
||||||
|
const minRead = 512
|
||||||
|
if cap(dec.buf)-len(dec.buf) < minRead {
|
||||||
|
newBuf := make([]byte, len(dec.buf), 2*cap(dec.buf)+minRead)
|
||||||
|
copy(newBuf, dec.buf)
|
||||||
|
dec.buf = newBuf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read. Delay error for next iteration (after scan).
|
||||||
|
n, err := dec.r.Read(dec.buf[len(dec.buf):cap(dec.buf)])
|
||||||
|
dec.buf = dec.buf[0 : len(dec.buf)+n]
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func nonSpace(b []byte) bool {
|
||||||
|
for _, c := range b {
|
||||||
|
if !isSpace(c) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Encoder writes JSON objects to an output stream.
|
||||||
|
type Encoder struct {
|
||||||
|
w io.Writer
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder returns a new encoder that writes to w.
|
||||||
|
func NewEncoder(w io.Writer) *Encoder {
|
||||||
|
return &Encoder{w: w}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode writes the JSON encoding of v to the stream,
|
||||||
|
// followed by a newline character.
|
||||||
|
//
|
||||||
|
// See the documentation for Marshal for details about the
|
||||||
|
// conversion of Go values to JSON.
|
||||||
|
func (enc *Encoder) Encode(v interface{}) error {
|
||||||
|
if enc.err != nil {
|
||||||
|
return enc.err
|
||||||
|
}
|
||||||
|
e := newEncodeState()
|
||||||
|
err := e.marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Terminate each value with a newline.
|
||||||
|
// This makes the output look a little nicer
|
||||||
|
// when debugging, and some kind of space
|
||||||
|
// is required if the encoded value was a number,
|
||||||
|
// so that the reader knows there aren't more
|
||||||
|
// digits coming.
|
||||||
|
e.WriteByte('\n')
|
||||||
|
|
||||||
|
if _, err = enc.w.Write(e.Bytes()); err != nil {
|
||||||
|
enc.err = err
|
||||||
|
}
|
||||||
|
encodeStatePool.Put(e)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawMessage is a raw encoded JSON object.
|
||||||
|
// It implements Marshaler and Unmarshaler and can
|
||||||
|
// be used to delay JSON decoding or precompute a JSON encoding.
|
||||||
|
type RawMessage []byte
|
||||||
|
|
||||||
|
// MarshalJSON returns *m as the JSON encoding of m.
|
||||||
|
func (m *RawMessage) MarshalJSON() ([]byte, error) {
|
||||||
|
return *m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON sets *m to a copy of data.
|
||||||
|
func (m *RawMessage) UnmarshalJSON(data []byte) error {
|
||||||
|
if m == nil {
|
||||||
|
return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
|
||||||
|
}
|
||||||
|
*m = append((*m)[0:0], data...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Marshaler = (*RawMessage)(nil)
|
||||||
|
var _ Unmarshaler = (*RawMessage)(nil)
|
||||||
|
|
||||||
|
// A Token holds a value of one of these types:
|
||||||
|
//
|
||||||
|
// Delim, for the four JSON delimiters [ ] { }
|
||||||
|
// bool, for JSON booleans
|
||||||
|
// float64, for JSON numbers
|
||||||
|
// Number, for JSON numbers
|
||||||
|
// string, for JSON string literals
|
||||||
|
// nil, for JSON null
|
||||||
|
//
|
||||||
|
type Token interface{}
|
||||||
|
|
||||||
|
const (
|
||||||
|
tokenTopValue = iota
|
||||||
|
tokenArrayStart
|
||||||
|
tokenArrayValue
|
||||||
|
tokenArrayComma
|
||||||
|
tokenObjectStart
|
||||||
|
tokenObjectKey
|
||||||
|
tokenObjectColon
|
||||||
|
tokenObjectValue
|
||||||
|
tokenObjectComma
|
||||||
|
)
|
||||||
|
|
||||||
|
// advance tokenstate from a separator state to a value state
|
||||||
|
func (dec *Decoder) tokenPrepareForDecode() error {
|
||||||
|
// Note: Not calling peek before switch, to avoid
|
||||||
|
// putting peek into the standard Decode path.
|
||||||
|
// peek is only called when using the Token API.
|
||||||
|
switch dec.tokenState {
|
||||||
|
case tokenArrayComma:
|
||||||
|
c, err := dec.peek()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c != ',' {
|
||||||
|
return &SyntaxError{"expected comma after array element", 0}
|
||||||
|
}
|
||||||
|
dec.scanp++
|
||||||
|
dec.tokenState = tokenArrayValue
|
||||||
|
case tokenObjectColon:
|
||||||
|
c, err := dec.peek()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c != ':' {
|
||||||
|
return &SyntaxError{"expected colon after object key", 0}
|
||||||
|
}
|
||||||
|
dec.scanp++
|
||||||
|
dec.tokenState = tokenObjectValue
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *Decoder) tokenValueAllowed() bool {
|
||||||
|
switch dec.tokenState {
|
||||||
|
case tokenTopValue, tokenArrayStart, tokenArrayValue, tokenObjectValue:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *Decoder) tokenValueEnd() {
|
||||||
|
switch dec.tokenState {
|
||||||
|
case tokenArrayStart, tokenArrayValue:
|
||||||
|
dec.tokenState = tokenArrayComma
|
||||||
|
case tokenObjectValue:
|
||||||
|
dec.tokenState = tokenObjectComma
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Delim is a JSON array or object delimiter, one of [ ] { or }.
|
||||||
|
type Delim rune
|
||||||
|
|
||||||
|
func (d Delim) String() string {
|
||||||
|
return string(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token returns the next JSON token in the input stream.
|
||||||
|
// At the end of the input stream, Token returns nil, io.EOF.
|
||||||
|
//
|
||||||
|
// Token guarantees that the delimiters [ ] { } it returns are
|
||||||
|
// properly nested and matched: if Token encounters an unexpected
|
||||||
|
// delimiter in the input, it will return an error.
|
||||||
|
//
|
||||||
|
// The input stream consists of basic JSON values—bool, string,
|
||||||
|
// number, and null—along with delimiters [ ] { } of type Delim
|
||||||
|
// to mark the start and end of arrays and objects.
|
||||||
|
// Commas and colons are elided.
|
||||||
|
func (dec *Decoder) Token() (Token, error) {
|
||||||
|
for {
|
||||||
|
c, err := dec.peek()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch c {
|
||||||
|
case '[':
|
||||||
|
if !dec.tokenValueAllowed() {
|
||||||
|
return dec.tokenError(c)
|
||||||
|
}
|
||||||
|
dec.scanp++
|
||||||
|
dec.tokenStack = append(dec.tokenStack, dec.tokenState)
|
||||||
|
dec.tokenState = tokenArrayStart
|
||||||
|
return Delim('['), nil
|
||||||
|
|
||||||
|
case ']':
|
||||||
|
if dec.tokenState != tokenArrayStart && dec.tokenState != tokenArrayComma {
|
||||||
|
return dec.tokenError(c)
|
||||||
|
}
|
||||||
|
dec.scanp++
|
||||||
|
dec.tokenState = dec.tokenStack[len(dec.tokenStack)-1]
|
||||||
|
dec.tokenStack = dec.tokenStack[:len(dec.tokenStack)-1]
|
||||||
|
dec.tokenValueEnd()
|
||||||
|
return Delim(']'), nil
|
||||||
|
|
||||||
|
case '{':
|
||||||
|
if !dec.tokenValueAllowed() {
|
||||||
|
return dec.tokenError(c)
|
||||||
|
}
|
||||||
|
dec.scanp++
|
||||||
|
dec.tokenStack = append(dec.tokenStack, dec.tokenState)
|
||||||
|
dec.tokenState = tokenObjectStart
|
||||||
|
return Delim('{'), nil
|
||||||
|
|
||||||
|
case '}':
|
||||||
|
if dec.tokenState != tokenObjectStart && dec.tokenState != tokenObjectComma {
|
||||||
|
return dec.tokenError(c)
|
||||||
|
}
|
||||||
|
dec.scanp++
|
||||||
|
dec.tokenState = dec.tokenStack[len(dec.tokenStack)-1]
|
||||||
|
dec.tokenStack = dec.tokenStack[:len(dec.tokenStack)-1]
|
||||||
|
dec.tokenValueEnd()
|
||||||
|
return Delim('}'), nil
|
||||||
|
|
||||||
|
case ':':
|
||||||
|
if dec.tokenState != tokenObjectColon {
|
||||||
|
return dec.tokenError(c)
|
||||||
|
}
|
||||||
|
dec.scanp++
|
||||||
|
dec.tokenState = tokenObjectValue
|
||||||
|
continue
|
||||||
|
|
||||||
|
case ',':
|
||||||
|
if dec.tokenState == tokenArrayComma {
|
||||||
|
dec.scanp++
|
||||||
|
dec.tokenState = tokenArrayValue
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if dec.tokenState == tokenObjectComma {
|
||||||
|
dec.scanp++
|
||||||
|
dec.tokenState = tokenObjectKey
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return dec.tokenError(c)
|
||||||
|
|
||||||
|
case '"':
|
||||||
|
if dec.tokenState == tokenObjectStart || dec.tokenState == tokenObjectKey {
|
||||||
|
var x string
|
||||||
|
old := dec.tokenState
|
||||||
|
dec.tokenState = tokenTopValue
|
||||||
|
err := dec.Decode(&x)
|
||||||
|
dec.tokenState = old
|
||||||
|
if err != nil {
|
||||||
|
clearOffset(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dec.tokenState = tokenObjectColon
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
|
||||||
|
default:
|
||||||
|
if !dec.tokenValueAllowed() {
|
||||||
|
return dec.tokenError(c)
|
||||||
|
}
|
||||||
|
var x interface{}
|
||||||
|
if err := dec.Decode(&x); err != nil {
|
||||||
|
clearOffset(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearOffset(err error) {
|
||||||
|
if s, ok := err.(*SyntaxError); ok {
|
||||||
|
s.Offset = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *Decoder) tokenError(c byte) (Token, error) {
|
||||||
|
var context string
|
||||||
|
switch dec.tokenState {
|
||||||
|
case tokenTopValue:
|
||||||
|
context = " looking for beginning of value"
|
||||||
|
case tokenArrayStart, tokenArrayValue, tokenObjectValue:
|
||||||
|
context = " looking for beginning of value"
|
||||||
|
case tokenArrayComma:
|
||||||
|
context = " after array element"
|
||||||
|
case tokenObjectKey:
|
||||||
|
context = " looking for beginning of object key string"
|
||||||
|
case tokenObjectColon:
|
||||||
|
context = " after object key"
|
||||||
|
case tokenObjectComma:
|
||||||
|
context = " after object key:value pair"
|
||||||
|
}
|
||||||
|
return nil, &SyntaxError{"invalid character " + quoteChar(c) + " " + context, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
// More reports whether there is another element in the
|
||||||
|
// current array or object being parsed.
|
||||||
|
func (dec *Decoder) More() bool {
|
||||||
|
c, err := dec.peek()
|
||||||
|
return err == nil && c != ']' && c != '}'
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dec *Decoder) peek() (byte, error) {
|
||||||
|
var err error
|
||||||
|
for {
|
||||||
|
for i := dec.scanp; i < len(dec.buf); i++ {
|
||||||
|
c := dec.buf[i]
|
||||||
|
if isSpace(c) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dec.scanp = i
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
// buffer has been scanned, now report any error
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
err = dec.refill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO
|
||||||
|
|
||||||
|
// EncodeToken writes the given JSON token to the stream.
|
||||||
|
// It returns an error if the delimiters [ ] { } are not properly used.
|
||||||
|
//
|
||||||
|
// EncodeToken does not call Flush, because usually it is part of
|
||||||
|
// a larger operation such as Encode, and those will call Flush when finished.
|
||||||
|
// Callers that create an Encoder and then invoke EncodeToken directly,
|
||||||
|
// without using Encode, need to call Flush when finished to ensure that
|
||||||
|
// the JSON is written to the underlying writer.
|
||||||
|
func (e *Encoder) EncodeToken(t Token) error {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
354
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/stream_test.go
generated
vendored
Normal file
354
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/stream_test.go
generated
vendored
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
// 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"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Test values for the stream test.
|
||||||
|
// One of each JSON kind.
|
||||||
|
var streamTest = []interface{}{
|
||||||
|
0.1,
|
||||||
|
"hello",
|
||||||
|
nil,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
[]interface{}{"a", "b", "c"},
|
||||||
|
map[string]interface{}{"K": "Kelvin", "ß": "long s"},
|
||||||
|
3.14, // another value to make sure something can follow map
|
||||||
|
}
|
||||||
|
|
||||||
|
var streamEncoded = `0.1
|
||||||
|
"hello"
|
||||||
|
null
|
||||||
|
true
|
||||||
|
false
|
||||||
|
["a","b","c"]
|
||||||
|
{"ß":"long s","K":"Kelvin"}
|
||||||
|
3.14
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestEncoder(t *testing.T) {
|
||||||
|
for i := 0; i <= len(streamTest); i++ {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := NewEncoder(&buf)
|
||||||
|
for j, v := range streamTest[0:i] {
|
||||||
|
if err := enc.Encode(v); err != nil {
|
||||||
|
t.Fatalf("encode #%d: %v", j, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if have, want := buf.String(), nlines(streamEncoded, i); have != want {
|
||||||
|
t.Errorf("encoding %d items: mismatch", i)
|
||||||
|
diff(t, []byte(have), []byte(want))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecoder(t *testing.T) {
|
||||||
|
for i := 0; i <= len(streamTest); i++ {
|
||||||
|
// Use stream without newlines as input,
|
||||||
|
// just to stress the decoder even more.
|
||||||
|
// Our test input does not include back-to-back numbers.
|
||||||
|
// Otherwise stripping the newlines would
|
||||||
|
// merge two adjacent JSON values.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for _, c := range nlines(streamEncoded, i) {
|
||||||
|
if c != '\n' {
|
||||||
|
buf.WriteRune(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out := make([]interface{}, i)
|
||||||
|
dec := NewDecoder(&buf)
|
||||||
|
for j := range out {
|
||||||
|
if err := dec.Decode(&out[j]); err != nil {
|
||||||
|
t.Fatalf("decode #%d/%d: %v", j, i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(out, streamTest[0:i]) {
|
||||||
|
t.Errorf("decoding %d items: mismatch", i)
|
||||||
|
for j := range out {
|
||||||
|
if !reflect.DeepEqual(out[j], streamTest[j]) {
|
||||||
|
t.Errorf("#%d: have %v want %v", j, out[j], streamTest[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecoderBuffered(t *testing.T) {
|
||||||
|
r := strings.NewReader(`{"Name": "Gopher"} extra `)
|
||||||
|
var m struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
d := NewDecoder(r)
|
||||||
|
err := d.Decode(&m)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if m.Name != "Gopher" {
|
||||||
|
t.Errorf("Name = %q; want Gopher", m.Name)
|
||||||
|
}
|
||||||
|
rest, err := ioutil.ReadAll(d.Buffered())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if g, w := string(rest), " extra "; g != w {
|
||||||
|
t.Errorf("Remaining = %q; want %q", g, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nlines(s string, n int) string {
|
||||||
|
if n <= 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
for i, c := range s {
|
||||||
|
if c == '\n' {
|
||||||
|
if n--; n == 0 {
|
||||||
|
return s[0 : i+1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRawMessage(t *testing.T) {
|
||||||
|
// TODO(rsc): Should not need the * in *RawMessage
|
||||||
|
var data struct {
|
||||||
|
X float64
|
||||||
|
Id *RawMessage
|
||||||
|
Y float32
|
||||||
|
}
|
||||||
|
const raw = `["\u0056",null]`
|
||||||
|
const msg = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}`
|
||||||
|
err := Unmarshal([]byte(msg), &data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if string([]byte(*data.Id)) != raw {
|
||||||
|
t.Fatalf("Raw mismatch: have %#q want %#q", []byte(*data.Id), raw)
|
||||||
|
}
|
||||||
|
b, err := Marshal(&data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal: %v", err)
|
||||||
|
}
|
||||||
|
if string(b) != msg {
|
||||||
|
t.Fatalf("Marshal: have %#q want %#q", b, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNullRawMessage(t *testing.T) {
|
||||||
|
// TODO(rsc): Should not need the * in *RawMessage
|
||||||
|
var data struct {
|
||||||
|
X float64
|
||||||
|
Id *RawMessage
|
||||||
|
Y float32
|
||||||
|
}
|
||||||
|
data.Id = new(RawMessage)
|
||||||
|
const msg = `{"X":0.1,"Id":null,"Y":0.2}`
|
||||||
|
err := Unmarshal([]byte(msg), &data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unmarshal: %v", err)
|
||||||
|
}
|
||||||
|
if data.Id != nil {
|
||||||
|
t.Fatalf("Raw mismatch: have non-nil, want nil")
|
||||||
|
}
|
||||||
|
b, err := Marshal(&data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal: %v", err)
|
||||||
|
}
|
||||||
|
if string(b) != msg {
|
||||||
|
t.Fatalf("Marshal: have %#q want %#q", b, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var blockingTests = []string{
|
||||||
|
`{"x": 1}`,
|
||||||
|
`[1, 2, 3]`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBlocking(t *testing.T) {
|
||||||
|
for _, enc := range blockingTests {
|
||||||
|
r, w := net.Pipe()
|
||||||
|
go w.Write([]byte(enc))
|
||||||
|
var val interface{}
|
||||||
|
|
||||||
|
// If Decode reads beyond what w.Write writes above,
|
||||||
|
// it will block, and the test will deadlock.
|
||||||
|
if err := NewDecoder(r).Decode(&val); err != nil {
|
||||||
|
t.Errorf("decoding %s: %v", enc, err)
|
||||||
|
}
|
||||||
|
r.Close()
|
||||||
|
w.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkEncoderEncode(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
type T struct {
|
||||||
|
X, Y string
|
||||||
|
}
|
||||||
|
v := &T{"foo", "bar"}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if err := NewEncoder(ioutil.Discard).Encode(v); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenStreamCase struct {
|
||||||
|
json string
|
||||||
|
expTokens []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type decodeThis struct {
|
||||||
|
v interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokenStreamCases []tokenStreamCase = []tokenStreamCase{
|
||||||
|
// streaming token cases
|
||||||
|
{json: `10`, expTokens: []interface{}{float64(10)}},
|
||||||
|
{json: ` [10] `, expTokens: []interface{}{
|
||||||
|
Delim('['), float64(10), Delim(']')}},
|
||||||
|
{json: ` [false,10,"b"] `, expTokens: []interface{}{
|
||||||
|
Delim('['), false, float64(10), "b", Delim(']')}},
|
||||||
|
{json: `{ "a": 1 }`, expTokens: []interface{}{
|
||||||
|
Delim('{'), "a", float64(1), Delim('}')}},
|
||||||
|
{json: `{"a": 1, "b":"3"}`, expTokens: []interface{}{
|
||||||
|
Delim('{'), "a", float64(1), "b", "3", Delim('}')}},
|
||||||
|
{json: ` [{"a": 1},{"a": 2}] `, expTokens: []interface{}{
|
||||||
|
Delim('['),
|
||||||
|
Delim('{'), "a", float64(1), Delim('}'),
|
||||||
|
Delim('{'), "a", float64(2), Delim('}'),
|
||||||
|
Delim(']')}},
|
||||||
|
{json: `{"obj": {"a": 1}}`, expTokens: []interface{}{
|
||||||
|
Delim('{'), "obj", Delim('{'), "a", float64(1), Delim('}'),
|
||||||
|
Delim('}')}},
|
||||||
|
{json: `{"obj": [{"a": 1}]}`, expTokens: []interface{}{
|
||||||
|
Delim('{'), "obj", Delim('['),
|
||||||
|
Delim('{'), "a", float64(1), Delim('}'),
|
||||||
|
Delim(']'), Delim('}')}},
|
||||||
|
|
||||||
|
// streaming tokens with intermittent Decode()
|
||||||
|
{json: `{ "a": 1 }`, expTokens: []interface{}{
|
||||||
|
Delim('{'), "a",
|
||||||
|
decodeThis{float64(1)},
|
||||||
|
Delim('}')}},
|
||||||
|
{json: ` [ { "a" : 1 } ] `, expTokens: []interface{}{
|
||||||
|
Delim('['),
|
||||||
|
decodeThis{map[string]interface{}{"a": float64(1)}},
|
||||||
|
Delim(']')}},
|
||||||
|
{json: ` [{"a": 1},{"a": 2}] `, expTokens: []interface{}{
|
||||||
|
Delim('['),
|
||||||
|
decodeThis{map[string]interface{}{"a": float64(1)}},
|
||||||
|
decodeThis{map[string]interface{}{"a": float64(2)}},
|
||||||
|
Delim(']')}},
|
||||||
|
{json: `{ "obj" : [ { "a" : 1 } ] }`, expTokens: []interface{}{
|
||||||
|
Delim('{'), "obj", Delim('['),
|
||||||
|
decodeThis{map[string]interface{}{"a": float64(1)}},
|
||||||
|
Delim(']'), Delim('}')}},
|
||||||
|
|
||||||
|
{json: `{"obj": {"a": 1}}`, expTokens: []interface{}{
|
||||||
|
Delim('{'), "obj",
|
||||||
|
decodeThis{map[string]interface{}{"a": float64(1)}},
|
||||||
|
Delim('}')}},
|
||||||
|
{json: `{"obj": [{"a": 1}]}`, expTokens: []interface{}{
|
||||||
|
Delim('{'), "obj",
|
||||||
|
decodeThis{[]interface{}{
|
||||||
|
map[string]interface{}{"a": float64(1)},
|
||||||
|
}},
|
||||||
|
Delim('}')}},
|
||||||
|
{json: ` [{"a": 1} {"a": 2}] `, expTokens: []interface{}{
|
||||||
|
Delim('['),
|
||||||
|
decodeThis{map[string]interface{}{"a": float64(1)}},
|
||||||
|
decodeThis{&SyntaxError{"expected comma after array element", 0}},
|
||||||
|
}},
|
||||||
|
{json: `{ "a" 1 }`, expTokens: []interface{}{
|
||||||
|
Delim('{'), "a",
|
||||||
|
decodeThis{&SyntaxError{"expected colon after object key", 0}},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeInStream(t *testing.T) {
|
||||||
|
|
||||||
|
for ci, tcase := range tokenStreamCases {
|
||||||
|
|
||||||
|
dec := NewDecoder(strings.NewReader(tcase.json))
|
||||||
|
for i, etk := range tcase.expTokens {
|
||||||
|
|
||||||
|
var tk interface{}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if dt, ok := etk.(decodeThis); ok {
|
||||||
|
etk = dt.v
|
||||||
|
err = dec.Decode(&tk)
|
||||||
|
} else {
|
||||||
|
tk, err = dec.Token()
|
||||||
|
}
|
||||||
|
if experr, ok := etk.(error); ok {
|
||||||
|
if err == nil || err.Error() != experr.Error() {
|
||||||
|
t.Errorf("case %v: Expected error %v in %q, but was %v", ci, experr, tcase.json, err)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
} else if err == io.EOF {
|
||||||
|
t.Errorf("case %v: Unexpected EOF in %q", ci, tcase.json)
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
t.Errorf("case %v: Unexpected error '%v' in %q", ci, err, tcase.json)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(tk, etk) {
|
||||||
|
t.Errorf(`case %v: %q @ %v expected %T(%v) was %T(%v)`, ci, tcase.json, i, etk, etk, tk, tk)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test from golang.org/issue/11893
|
||||||
|
func TestHTTPDecoding(t *testing.T) {
|
||||||
|
const raw = `{ "foo": "bar" }`
|
||||||
|
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte(raw))
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
res, err := http.Get(ts.URL)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("GET failed: %v", err)
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
foo := struct {
|
||||||
|
Foo string `json:"foo"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
d := NewDecoder(res.Body)
|
||||||
|
err = d.Decode(&foo)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Decode: %v", err)
|
||||||
|
}
|
||||||
|
if foo.Foo != "bar" {
|
||||||
|
t.Errorf("decoded %q; want \"bar\"", foo.Foo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we get the EOF the second time
|
||||||
|
err = d.Decode(&foo)
|
||||||
|
if err != io.EOF {
|
||||||
|
t.Errorf("err = %v; want io.EOF", err)
|
||||||
|
}
|
||||||
|
}
|
115
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/tagkey_test.go
generated
vendored
Normal file
115
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/tagkey_test.go
generated
vendored
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type basicLatin2xTag struct {
|
||||||
|
V string `json:"$%-/"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type basicLatin3xTag struct {
|
||||||
|
V string `json:"0123456789"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type basicLatin4xTag struct {
|
||||||
|
V string `json:"ABCDEFGHIJKLMO"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type basicLatin5xTag struct {
|
||||||
|
V string `json:"PQRSTUVWXYZ_"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type basicLatin6xTag struct {
|
||||||
|
V string `json:"abcdefghijklmno"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type basicLatin7xTag struct {
|
||||||
|
V string `json:"pqrstuvwxyz"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type miscPlaneTag struct {
|
||||||
|
V string `json:"色は匂へど"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type percentSlashTag struct {
|
||||||
|
V string `json:"text/html%"` // https://golang.org/issue/2718
|
||||||
|
}
|
||||||
|
|
||||||
|
type punctuationTag struct {
|
||||||
|
V string `json:"!#$%&()*+-./:<=>?@[]^_{|}~"` // https://golang.org/issue/3546
|
||||||
|
}
|
||||||
|
|
||||||
|
type emptyTag struct {
|
||||||
|
W string
|
||||||
|
}
|
||||||
|
|
||||||
|
type misnamedTag struct {
|
||||||
|
X string `jsom:"Misnamed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type badFormatTag struct {
|
||||||
|
Y string `:"BadFormat"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type badCodeTag struct {
|
||||||
|
Z string `json:" !\"#&'()*+,."`
|
||||||
|
}
|
||||||
|
|
||||||
|
type spaceTag struct {
|
||||||
|
Q string `json:"With space"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type unicodeTag struct {
|
||||||
|
W string `json:"Ελλάδα"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var structTagObjectKeyTests = []struct {
|
||||||
|
raw interface{}
|
||||||
|
value string
|
||||||
|
key string
|
||||||
|
}{
|
||||||
|
{basicLatin2xTag{"2x"}, "2x", "$%-/"},
|
||||||
|
{basicLatin3xTag{"3x"}, "3x", "0123456789"},
|
||||||
|
{basicLatin4xTag{"4x"}, "4x", "ABCDEFGHIJKLMO"},
|
||||||
|
{basicLatin5xTag{"5x"}, "5x", "PQRSTUVWXYZ_"},
|
||||||
|
{basicLatin6xTag{"6x"}, "6x", "abcdefghijklmno"},
|
||||||
|
{basicLatin7xTag{"7x"}, "7x", "pqrstuvwxyz"},
|
||||||
|
{miscPlaneTag{"いろはにほへと"}, "いろはにほへと", "色は匂へど"},
|
||||||
|
{emptyTag{"Pour Moi"}, "Pour Moi", "W"},
|
||||||
|
{misnamedTag{"Animal Kingdom"}, "Animal Kingdom", "X"},
|
||||||
|
{badFormatTag{"Orfevre"}, "Orfevre", "Y"},
|
||||||
|
{badCodeTag{"Reliable Man"}, "Reliable Man", "Z"},
|
||||||
|
{percentSlashTag{"brut"}, "brut", "text/html%"},
|
||||||
|
{punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"},
|
||||||
|
{spaceTag{"Perreddu"}, "Perreddu", "With space"},
|
||||||
|
{unicodeTag{"Loukanikos"}, "Loukanikos", "Ελλάδα"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStructTagObjectKey(t *testing.T) {
|
||||||
|
for _, tt := range structTagObjectKeyTests {
|
||||||
|
b, err := Marshal(tt.raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Marshal(%#q) failed: %v", tt.raw, err)
|
||||||
|
}
|
||||||
|
var f interface{}
|
||||||
|
err = Unmarshal(b, &f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unmarshal(%#q) failed: %v", b, err)
|
||||||
|
}
|
||||||
|
for i, v := range f.(map[string]interface{}) {
|
||||||
|
switch i {
|
||||||
|
case tt.key:
|
||||||
|
if s, ok := v.(string); !ok || s != tt.value {
|
||||||
|
t.Fatalf("Unexpected value: %#q, want %v", s, tt.value)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Fatalf("Unexpected key: %#q, from %#q", i, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/tags.go
generated
vendored
Normal file
44
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/tags.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// tagOptions is the string following a comma in a struct field's "json"
|
||||||
|
// tag, or the empty string. It does not include the leading comma.
|
||||||
|
type tagOptions string
|
||||||
|
|
||||||
|
// parseTag splits a struct field's json tag into its name and
|
||||||
|
// comma-separated options.
|
||||||
|
func parseTag(tag string) (string, tagOptions) {
|
||||||
|
if idx := strings.Index(tag, ","); idx != -1 {
|
||||||
|
return tag[:idx], tagOptions(tag[idx+1:])
|
||||||
|
}
|
||||||
|
return tag, tagOptions("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains reports whether a comma-separated list of options
|
||||||
|
// contains a particular substr flag. substr must be surrounded by a
|
||||||
|
// string boundary or commas.
|
||||||
|
func (o tagOptions) Contains(optionName string) bool {
|
||||||
|
if len(o) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s := string(o)
|
||||||
|
for s != "" {
|
||||||
|
var next string
|
||||||
|
i := strings.Index(s, ",")
|
||||||
|
if i >= 0 {
|
||||||
|
s, next = s[:i], s[i+1:]
|
||||||
|
}
|
||||||
|
if s == optionName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
s = next
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
28
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/tags_test.go
generated
vendored
Normal file
28
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/tags_test.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTagParsing(t *testing.T) {
|
||||||
|
name, opts := parseTag("field,foobar,foo")
|
||||||
|
if name != "field" {
|
||||||
|
t.Fatalf("name = %q, want field", name)
|
||||||
|
}
|
||||||
|
for _, tt := range []struct {
|
||||||
|
opt string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"foobar", true},
|
||||||
|
{"foo", true},
|
||||||
|
{"bar", false},
|
||||||
|
} {
|
||||||
|
if opts.Contains(tt.opt) != tt.want {
|
||||||
|
t.Errorf("Contains(%q) = %v", tt.opt, !tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/testdata/code.json.gz
generated
vendored
Normal file
BIN
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json/testdata/code.json.gz
generated
vendored
Normal file
Binary file not shown.
31
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json_fork.go
generated
vendored
Normal file
31
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json_fork.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// +build !std_json
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* 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 (
|
||||||
|
"gopkg.in/square/go-jose.v1/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MarshalJSON(v interface{}) ([]byte, error) {
|
||||||
|
return json.Marshal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnmarshalJSON(data []byte, v interface{}) error {
|
||||||
|
return json.Unmarshal(data, v)
|
||||||
|
}
|
76
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json_fork_test.go
generated
vendored
Normal file
76
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json_fork_test.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// +build !std_json
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* 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 (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CaseSensitive struct {
|
||||||
|
A int `json:"Test"`
|
||||||
|
B int `json:"test"`
|
||||||
|
C int `json:"TEST"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCaseSensitiveJSON(t *testing.T) {
|
||||||
|
raw := []byte(`{"test":42}`)
|
||||||
|
var cs CaseSensitive
|
||||||
|
err := UnmarshalJSON(raw, &cs)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cs.A != 0 || cs.B != 42 || cs.C != 0 {
|
||||||
|
t.Errorf("parsing JSON should be case-sensitive (got %v)", cs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRejectDuplicateKeysObject(t *testing.T) {
|
||||||
|
raw := []byte(`{"test":42,"test":43}`)
|
||||||
|
var cs CaseSensitive
|
||||||
|
err := UnmarshalJSON(raw, &cs)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should reject JSON with duplicate keys, but didn't")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRejectDuplicateKeysInterface(t *testing.T) {
|
||||||
|
raw := []byte(`{"test":42,"test":43}`)
|
||||||
|
var m interface{}
|
||||||
|
err := UnmarshalJSON(raw, &m)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should reject JSON with duplicate keys, but didn't")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseCaseSensitiveJWE(t *testing.T) {
|
||||||
|
invalidJWE := `{"protected":"eyJlbmMiOiJYWVoiLCJBTEciOiJYWVoifQo","encrypted_key":"QUJD","iv":"QUJD","ciphertext":"QUJD","tag":"QUJD"}`
|
||||||
|
_, err := ParseEncrypted(invalidJWE)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Able to parse message with case-invalid headers", invalidJWE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseCaseSensitiveJWS(t *testing.T) {
|
||||||
|
invalidJWS := `{"PAYLOAD":"CUJD","signatures":[{"protected":"e30","signature":"CUJD"}]}`
|
||||||
|
_, err := ParseSigned(invalidJWS)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Able to parse message with case-invalid headers", invalidJWS)
|
||||||
|
}
|
||||||
|
}
|
31
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json_std.go
generated
vendored
Normal file
31
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json_std.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// +build std_json
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* 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 (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MarshalJSON(v interface{}) ([]byte, error) {
|
||||||
|
return json.Marshal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnmarshalJSON(data []byte, v interface{}) error {
|
||||||
|
return json.Unmarshal(data, v)
|
||||||
|
}
|
106
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json_std_test.go
generated
vendored
Normal file
106
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/json_std_test.go
generated
vendored
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
// +build std_json
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* 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 (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CaseInsensitive struct {
|
||||||
|
A int `json:"TEST"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCaseInsensitiveJSON(t *testing.T) {
|
||||||
|
raw := []byte(`{"test":42}`)
|
||||||
|
var ci CaseInsensitive
|
||||||
|
err := UnmarshalJSON(raw, &ci)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ci.A != 42 {
|
||||||
|
t.Errorf("parsing JSON should be case-insensitive (got %v)", ci)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseCaseInsensitiveJWE(t *testing.T) {
|
||||||
|
invalidJWE := `{"protected":"eyJlbmMiOiJYWVoiLCJBTEciOiJYWVoifQo","encrypted_key":"QUJD","iv":"QUJD","ciphertext":"QUJD","tag":"QUJD"}`
|
||||||
|
_, err := ParseEncrypted(invalidJWE)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Unable to parse message with case-invalid headers", invalidJWE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseCaseInsensitiveJWS(t *testing.T) {
|
||||||
|
invalidJWS := `{"PAYLOAD":"CUJD","signatures":[{"protected":"e30","signature":"CUJD"}]}`
|
||||||
|
_, err := ParseSigned(invalidJWS)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Unable to parse message with case-invalid headers", invalidJWS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var JWKSetDuplicates = stripWhitespace(`{
|
||||||
|
"keys": [{
|
||||||
|
"kty": "RSA",
|
||||||
|
"kid": "exclude-me",
|
||||||
|
"use": "sig",
|
||||||
|
"n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT
|
||||||
|
-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV
|
||||||
|
wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-
|
||||||
|
oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde
|
||||||
|
3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC
|
||||||
|
LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g
|
||||||
|
HdrNP5zw",
|
||||||
|
"e": "AQAB"
|
||||||
|
}],
|
||||||
|
"keys": [{
|
||||||
|
"kty": "RSA",
|
||||||
|
"kid": "include-me",
|
||||||
|
"use": "sig",
|
||||||
|
"n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT
|
||||||
|
-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV
|
||||||
|
wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-
|
||||||
|
oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde
|
||||||
|
3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC
|
||||||
|
LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g
|
||||||
|
HdrNP5zw",
|
||||||
|
"e": "AQAB"
|
||||||
|
}],
|
||||||
|
"custom": "exclude-me",
|
||||||
|
"custom": "include-me"
|
||||||
|
}`)
|
||||||
|
|
||||||
|
func TestDuplicateJWKSetMembersIgnored(t *testing.T) {
|
||||||
|
type CustomSet struct {
|
||||||
|
JsonWebKeySet
|
||||||
|
CustomMember string `json:"custom"`
|
||||||
|
}
|
||||||
|
data := []byte(JWKSetDuplicates)
|
||||||
|
var set CustomSet
|
||||||
|
UnmarshalJSON(data, &set)
|
||||||
|
if len(set.Keys) != 1 {
|
||||||
|
t.Error("expected only one key in set")
|
||||||
|
}
|
||||||
|
if set.Keys[0].KeyID != "include-me" {
|
||||||
|
t.Errorf("expected key with kid: \"include-me\", got: %s", set.Keys[0].KeyID)
|
||||||
|
}
|
||||||
|
if set.CustomMember != "include-me" {
|
||||||
|
t.Errorf("expected custom member value: \"include-me\", got: %s", set.CustomMember)
|
||||||
|
}
|
||||||
|
}
|
278
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/jwe.go
generated
vendored
Normal file
278
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/jwe.go
generated
vendored
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
/*-
|
||||||
|
* 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 (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rawJsonWebEncryption represents a raw JWE JSON object. Used for parsing/serializing.
|
||||||
|
type rawJsonWebEncryption struct {
|
||||||
|
Protected *byteBuffer `json:"protected,omitempty"`
|
||||||
|
Unprotected *rawHeader `json:"unprotected,omitempty"`
|
||||||
|
Header *rawHeader `json:"header,omitempty"`
|
||||||
|
Recipients []rawRecipientInfo `json:"recipients,omitempty"`
|
||||||
|
Aad *byteBuffer `json:"aad,omitempty"`
|
||||||
|
EncryptedKey *byteBuffer `json:"encrypted_key,omitempty"`
|
||||||
|
Iv *byteBuffer `json:"iv,omitempty"`
|
||||||
|
Ciphertext *byteBuffer `json:"ciphertext,omitempty"`
|
||||||
|
Tag *byteBuffer `json:"tag,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// rawRecipientInfo represents a raw JWE Per-Recipient header JSON object. Used for parsing/serializing.
|
||||||
|
type rawRecipientInfo struct {
|
||||||
|
Header *rawHeader `json:"header,omitempty"`
|
||||||
|
EncryptedKey string `json:"encrypted_key,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// JsonWebEncryption represents an encrypted JWE object after parsing.
|
||||||
|
type JsonWebEncryption struct {
|
||||||
|
Header JoseHeader
|
||||||
|
protected, unprotected *rawHeader
|
||||||
|
recipients []recipientInfo
|
||||||
|
aad, iv, ciphertext, tag []byte
|
||||||
|
original *rawJsonWebEncryption
|
||||||
|
}
|
||||||
|
|
||||||
|
// recipientInfo represents a raw JWE Per-Recipient header JSON object after parsing.
|
||||||
|
type recipientInfo struct {
|
||||||
|
header *rawHeader
|
||||||
|
encryptedKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthData retrieves the (optional) authenticated data attached to the object.
|
||||||
|
func (obj JsonWebEncryption) GetAuthData() []byte {
|
||||||
|
if obj.aad != nil {
|
||||||
|
out := make([]byte, len(obj.aad))
|
||||||
|
copy(out, obj.aad)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the merged header values
|
||||||
|
func (obj JsonWebEncryption) mergedHeaders(recipient *recipientInfo) rawHeader {
|
||||||
|
out := rawHeader{}
|
||||||
|
out.merge(obj.protected)
|
||||||
|
out.merge(obj.unprotected)
|
||||||
|
|
||||||
|
if recipient != nil {
|
||||||
|
out.merge(recipient.header)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the additional authenticated data from a JWE object.
|
||||||
|
func (obj JsonWebEncryption) computeAuthData() []byte {
|
||||||
|
var protected string
|
||||||
|
|
||||||
|
if obj.original != nil {
|
||||||
|
protected = obj.original.Protected.base64()
|
||||||
|
} else {
|
||||||
|
protected = base64URLEncode(mustSerializeJSON((obj.protected)))
|
||||||
|
}
|
||||||
|
|
||||||
|
output := []byte(protected)
|
||||||
|
if obj.aad != nil {
|
||||||
|
output = append(output, '.')
|
||||||
|
output = append(output, []byte(base64URLEncode(obj.aad))...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseEncrypted parses an encrypted message in compact or full serialization format.
|
||||||
|
func ParseEncrypted(input string) (*JsonWebEncryption, error) {
|
||||||
|
input = stripWhitespace(input)
|
||||||
|
if strings.HasPrefix(input, "{") {
|
||||||
|
return parseEncryptedFull(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseEncryptedCompact(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseEncryptedFull parses a message in compact format.
|
||||||
|
func parseEncryptedFull(input string) (*JsonWebEncryption, error) {
|
||||||
|
var parsed rawJsonWebEncryption
|
||||||
|
err := UnmarshalJSON([]byte(input), &parsed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed.sanitized()
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanitized produces a cleaned-up JWE object from the raw JSON.
|
||||||
|
func (parsed *rawJsonWebEncryption) sanitized() (*JsonWebEncryption, error) {
|
||||||
|
obj := &JsonWebEncryption{
|
||||||
|
original: parsed,
|
||||||
|
unprotected: parsed.Unprotected,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that there is not a nonce in the unprotected headers
|
||||||
|
if (parsed.Unprotected != nil && parsed.Unprotected.Nonce != "") ||
|
||||||
|
(parsed.Header != nil && parsed.Header.Nonce != "") {
|
||||||
|
return nil, ErrUnprotectedNonce
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 {
|
||||||
|
err := UnmarshalJSON(parsed.Protected.bytes(), &obj.protected)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: invalid protected header: %s, %s", err, parsed.Protected.base64())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: this must be called _after_ we parse the protected header,
|
||||||
|
// otherwise fields from the protected header will not get picked up.
|
||||||
|
obj.Header = obj.mergedHeaders(nil).sanitized()
|
||||||
|
|
||||||
|
if len(parsed.Recipients) == 0 {
|
||||||
|
obj.recipients = []recipientInfo{
|
||||||
|
recipientInfo{
|
||||||
|
header: parsed.Header,
|
||||||
|
encryptedKey: parsed.EncryptedKey.bytes(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
obj.recipients = make([]recipientInfo, len(parsed.Recipients))
|
||||||
|
for r := range parsed.Recipients {
|
||||||
|
encryptedKey, err := base64URLDecode(parsed.Recipients[r].EncryptedKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that there is not a nonce in the unprotected header
|
||||||
|
if parsed.Recipients[r].Header != nil && parsed.Recipients[r].Header.Nonce != "" {
|
||||||
|
return nil, ErrUnprotectedNonce
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.recipients[r].header = parsed.Recipients[r].Header
|
||||||
|
obj.recipients[r].encryptedKey = encryptedKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, recipient := range obj.recipients {
|
||||||
|
headers := obj.mergedHeaders(&recipient)
|
||||||
|
if headers.Alg == "" || headers.Enc == "" {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: message is missing alg/enc headers")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.iv = parsed.Iv.bytes()
|
||||||
|
obj.ciphertext = parsed.Ciphertext.bytes()
|
||||||
|
obj.tag = parsed.Tag.bytes()
|
||||||
|
obj.aad = parsed.Aad.bytes()
|
||||||
|
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseEncryptedCompact parses a message in compact format.
|
||||||
|
func parseEncryptedCompact(input string) (*JsonWebEncryption, error) {
|
||||||
|
parts := strings.Split(input, ".")
|
||||||
|
if len(parts) != 5 {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: compact JWE format must have five parts")
|
||||||
|
}
|
||||||
|
|
||||||
|
rawProtected, err := base64URLDecode(parts[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedKey, err := base64URLDecode(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
iv, err := base64URLDecode(parts[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertext, err := base64URLDecode(parts[3])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tag, err := base64URLDecode(parts[4])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
raw := &rawJsonWebEncryption{
|
||||||
|
Protected: newBuffer(rawProtected),
|
||||||
|
EncryptedKey: newBuffer(encryptedKey),
|
||||||
|
Iv: newBuffer(iv),
|
||||||
|
Ciphertext: newBuffer(ciphertext),
|
||||||
|
Tag: newBuffer(tag),
|
||||||
|
}
|
||||||
|
|
||||||
|
return raw.sanitized()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompactSerialize serializes an object using the compact serialization format.
|
||||||
|
func (obj JsonWebEncryption) CompactSerialize() (string, error) {
|
||||||
|
if len(obj.recipients) != 1 || obj.unprotected != nil ||
|
||||||
|
obj.protected == nil || obj.recipients[0].header != nil {
|
||||||
|
return "", ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedProtected := mustSerializeJSON(obj.protected)
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"%s.%s.%s.%s.%s",
|
||||||
|
base64URLEncode(serializedProtected),
|
||||||
|
base64URLEncode(obj.recipients[0].encryptedKey),
|
||||||
|
base64URLEncode(obj.iv),
|
||||||
|
base64URLEncode(obj.ciphertext),
|
||||||
|
base64URLEncode(obj.tag)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FullSerialize serializes an object using the full JSON serialization format.
|
||||||
|
func (obj JsonWebEncryption) FullSerialize() string {
|
||||||
|
raw := rawJsonWebEncryption{
|
||||||
|
Unprotected: obj.unprotected,
|
||||||
|
Iv: newBuffer(obj.iv),
|
||||||
|
Ciphertext: newBuffer(obj.ciphertext),
|
||||||
|
EncryptedKey: newBuffer(obj.recipients[0].encryptedKey),
|
||||||
|
Tag: newBuffer(obj.tag),
|
||||||
|
Aad: newBuffer(obj.aad),
|
||||||
|
Recipients: []rawRecipientInfo{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(obj.recipients) > 1 {
|
||||||
|
for _, recipient := range obj.recipients {
|
||||||
|
info := rawRecipientInfo{
|
||||||
|
Header: recipient.header,
|
||||||
|
EncryptedKey: base64URLEncode(recipient.encryptedKey),
|
||||||
|
}
|
||||||
|
raw.Recipients = append(raw.Recipients, info)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Use flattened serialization
|
||||||
|
raw.Header = obj.recipients[0].header
|
||||||
|
raw.EncryptedKey = newBuffer(obj.recipients[0].encryptedKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.protected != nil {
|
||||||
|
raw.Protected = newBuffer(mustSerializeJSON(obj.protected))
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(mustSerializeJSON(raw))
|
||||||
|
}
|
537
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/jwe_test.go
generated
vendored
Normal file
537
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/jwe_test.go
generated
vendored
Normal file
|
@ -0,0 +1,537 @@
|
||||||
|
/*-
|
||||||
|
* 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/elliptic"
|
||||||
|
"crypto/rsa"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCompactParseJWE(t *testing.T) {
|
||||||
|
// Should parse
|
||||||
|
msg := "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA.dGVzdA"
|
||||||
|
_, err := ParseEncrypted(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Unable to parse valid message:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages that should fail to parse
|
||||||
|
failures := []string{
|
||||||
|
// Too many parts
|
||||||
|
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
|
||||||
|
// Not enough parts
|
||||||
|
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA",
|
||||||
|
// Invalid encrypted key
|
||||||
|
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.//////.dGVzdA.dGVzdA.dGVzdA",
|
||||||
|
// Invalid IV
|
||||||
|
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.//////.dGVzdA.dGVzdA",
|
||||||
|
// Invalid ciphertext
|
||||||
|
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.//////.dGVzdA",
|
||||||
|
// Invalid tag
|
||||||
|
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA.//////",
|
||||||
|
// Invalid header
|
||||||
|
"W10.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
|
||||||
|
// Invalid header
|
||||||
|
"######.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
|
||||||
|
// Missing alc/enc params
|
||||||
|
"e30.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, msg := range failures {
|
||||||
|
_, err = ParseEncrypted(msg)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Able to parse invalid message", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFullParseJWE(t *testing.T) {
|
||||||
|
// Messages that should succeed to parse
|
||||||
|
successes := []string{
|
||||||
|
// Flattened serialization, single recipient
|
||||||
|
"{\"protected\":\"eyJhbGciOiJYWVoiLCJlbmMiOiJYWVoifQo\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
|
||||||
|
// Unflattened serialization, single recipient
|
||||||
|
"{\"protected\":\"\",\"unprotected\":{\"enc\":\"XYZ\"},\"recipients\":[{\"header\":{\"alg\":\"XYZ\"},\"encrypted_key\":\"QUJD\"}],\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range successes {
|
||||||
|
_, err := ParseEncrypted(successes[i])
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Unble to parse valid message", err, successes[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages that should fail to parse
|
||||||
|
failures := []string{
|
||||||
|
// Empty
|
||||||
|
"{}",
|
||||||
|
// Invalid JSON
|
||||||
|
"{XX",
|
||||||
|
// Invalid protected header
|
||||||
|
"{\"protected\":\"###\"}",
|
||||||
|
// Invalid protected header
|
||||||
|
"{\"protected\":\"e1gK\"}",
|
||||||
|
// Invalid encrypted key
|
||||||
|
"{\"protected\":\"e30\",\"encrypted_key\":\"###\"}",
|
||||||
|
// Invalid IV
|
||||||
|
"{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"###\"}",
|
||||||
|
// Invalid ciphertext
|
||||||
|
"{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"###\"}",
|
||||||
|
// Invalid tag
|
||||||
|
"{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"###\"}",
|
||||||
|
// Invalid AAD
|
||||||
|
"{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\",\"aad\":\"###\"}",
|
||||||
|
// Missing alg/enc headers
|
||||||
|
"{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
|
||||||
|
// Missing enc header
|
||||||
|
"{\"protected\":\"eyJhbGciOiJYWVoifQ\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
|
||||||
|
// Missing alg header
|
||||||
|
"{\"protected\":\"eyJlbmMiOiJYWVoifQ\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
|
||||||
|
// Unflattened serialization, single recipient, invalid encrypted_key
|
||||||
|
"{\"protected\":\"\",\"recipients\":[{\"header\":{\"alg\":\"XYZ\", \"enc\":\"XYZ\"},\"encrypted_key\":\"###\"}],\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
|
||||||
|
// Unflattened serialization, single recipient, missing alg
|
||||||
|
"{\"protected\":\"eyJhbGciOiJYWVoifQ\",\"recipients\":[{\"encrypted_key\":\"QUJD\"}],\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range failures {
|
||||||
|
_, err := ParseEncrypted(failures[i])
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Able to parse invalid message", err, failures[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMissingInvalidHeaders(t *testing.T) {
|
||||||
|
obj := &JsonWebEncryption{
|
||||||
|
protected: &rawHeader{Enc: A128GCM},
|
||||||
|
unprotected: &rawHeader{},
|
||||||
|
recipients: []recipientInfo{
|
||||||
|
recipientInfo{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := obj.Decrypt(nil)
|
||||||
|
if err != ErrUnsupportedKeyType {
|
||||||
|
t.Error("should detect invalid key")
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.unprotected.Crit = []string{"1", "2"}
|
||||||
|
|
||||||
|
_, err = obj.Decrypt(nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should reject message with crit header")
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.unprotected.Crit = nil
|
||||||
|
obj.protected = &rawHeader{Alg: string(RSA1_5)}
|
||||||
|
|
||||||
|
_, err = obj.Decrypt(rsaTestKey)
|
||||||
|
if err == nil || err == ErrCryptoFailure {
|
||||||
|
t.Error("should detect missing enc header")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRejectUnprotectedJWENonce(t *testing.T) {
|
||||||
|
// No need to test compact, since that's always protected
|
||||||
|
|
||||||
|
// Flattened JSON
|
||||||
|
input := `{
|
||||||
|
"header": {
|
||||||
|
"alg": "XYZ", "enc": "XYZ",
|
||||||
|
"nonce": "should-cause-an-error"
|
||||||
|
},
|
||||||
|
"encrypted_key": "does-not-matter",
|
||||||
|
"aad": "does-not-matter",
|
||||||
|
"iv": "does-not-matter",
|
||||||
|
"ciphertext": "does-not-matter",
|
||||||
|
"tag": "does-not-matter"
|
||||||
|
}`
|
||||||
|
_, err := ParseEncrypted(input)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("JWE with an unprotected nonce parsed as valid.")
|
||||||
|
} else if err.Error() != "square/go-jose: Nonce parameter included in unprotected header" {
|
||||||
|
t.Errorf("Improper error for unprotected nonce: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
input = `{
|
||||||
|
"unprotected": {
|
||||||
|
"alg": "XYZ", "enc": "XYZ",
|
||||||
|
"nonce": "should-cause-an-error"
|
||||||
|
},
|
||||||
|
"encrypted_key": "does-not-matter",
|
||||||
|
"aad": "does-not-matter",
|
||||||
|
"iv": "does-not-matter",
|
||||||
|
"ciphertext": "does-not-matter",
|
||||||
|
"tag": "does-not-matter"
|
||||||
|
}`
|
||||||
|
_, err = ParseEncrypted(input)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("JWE with an unprotected nonce parsed as valid.")
|
||||||
|
} else if err.Error() != "square/go-jose: Nonce parameter included in unprotected header" {
|
||||||
|
t.Errorf("Improper error for unprotected nonce: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full JSON
|
||||||
|
input = `{
|
||||||
|
"header": { "alg": "XYZ", "enc": "XYZ" },
|
||||||
|
"aad": "does-not-matter",
|
||||||
|
"iv": "does-not-matter",
|
||||||
|
"ciphertext": "does-not-matter",
|
||||||
|
"tag": "does-not-matter",
|
||||||
|
"recipients": [{
|
||||||
|
"header": { "nonce": "should-cause-an-error" },
|
||||||
|
"encrypted_key": "does-not-matter"
|
||||||
|
}]
|
||||||
|
}`
|
||||||
|
_, err = ParseEncrypted(input)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("JWS with an unprotected nonce parsed as valid.")
|
||||||
|
} else if err.Error() != "square/go-jose: Nonce parameter included in unprotected header" {
|
||||||
|
t.Errorf("Improper error for unprotected nonce: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompactSerialize(t *testing.T) {
|
||||||
|
// Compact serialization must fail if we have unprotected headers
|
||||||
|
obj := &JsonWebEncryption{
|
||||||
|
unprotected: &rawHeader{Alg: "XYZ"},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := obj.CompactSerialize()
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Object with unprotected headers can't be compact serialized")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVectorsJWE(t *testing.T) {
|
||||||
|
plaintext := []byte("The true sign of intelligence is not knowledge but imagination.")
|
||||||
|
|
||||||
|
publicKey := &rsa.PublicKey{
|
||||||
|
N: fromBase64Int(`
|
||||||
|
oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW
|
||||||
|
cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S
|
||||||
|
psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a
|
||||||
|
sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS
|
||||||
|
tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj
|
||||||
|
YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw`),
|
||||||
|
E: 65537,
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedCompact := stripWhitespace(`
|
||||||
|
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.ROQCfge4JPm_
|
||||||
|
yACxv1C1NSXmwNbL6kvmCuyxBRGpW57DvlwByjyjsb6g8m7wtLMqKEyhFCn
|
||||||
|
tV7sjippEePIlKln6BvVnz5ZLXHNYQgmubuNq8MC0KTwcaGJ_C0z_T8j4PZ
|
||||||
|
a1nfpbhSe-ePYaALrf_nIsSRKu7cWsrwOSlaRPecRnYeDd_ytAxEQWYEKFi
|
||||||
|
Pszc70fP9geZOB_09y9jq0vaOF0jGmpIAmgk71lCcUpSdrhNokTKo5y8MH8
|
||||||
|
3NcbIvmuZ51cjXQj1f0_AwM9RW3oCh2Hu0z0C5l4BujZVsDuGgMsGZsjUhS
|
||||||
|
RZsAQSXHCAmlJ2NlnN60U7y4SPJhKv5tKYw.48V1_ALb6US04U3b.5eym8T
|
||||||
|
W_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiS
|
||||||
|
diwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ`)
|
||||||
|
|
||||||
|
expectedFull := stripWhitespace(`
|
||||||
|
{ "protected":"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ",
|
||||||
|
"encrypted_key":
|
||||||
|
"ROQCfge4JPm_yACxv1C1NSXmwNbL6kvmCuyxBRGpW57DvlwByjyjsb
|
||||||
|
6g8m7wtLMqKEyhFCntV7sjippEePIlKln6BvVnz5ZLXHNYQgmubuNq
|
||||||
|
8MC0KTwcaGJ_C0z_T8j4PZa1nfpbhSe-ePYaALrf_nIsSRKu7cWsrw
|
||||||
|
OSlaRPecRnYeDd_ytAxEQWYEKFiPszc70fP9geZOB_09y9jq0vaOF0
|
||||||
|
jGmpIAmgk71lCcUpSdrhNokTKo5y8MH83NcbIvmuZ51cjXQj1f0_Aw
|
||||||
|
M9RW3oCh2Hu0z0C5l4BujZVsDuGgMsGZsjUhSRZsAQSXHCAmlJ2Nln
|
||||||
|
N60U7y4SPJhKv5tKYw",
|
||||||
|
"iv": "48V1_ALb6US04U3b",
|
||||||
|
"ciphertext":
|
||||||
|
"5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFS
|
||||||
|
hS8iB7j6jiSdiwkIr3ajwQzaBtQD_A",
|
||||||
|
"tag":"XFBoMYUZodetZdvTiFvSkQ" }`)
|
||||||
|
|
||||||
|
// Mock random reader
|
||||||
|
randReader = bytes.NewReader([]byte{
|
||||||
|
// Encryption key
|
||||||
|
177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154,
|
||||||
|
212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122,
|
||||||
|
234, 64, 252,
|
||||||
|
// Randomness for RSA-OAEP
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
// Initialization vector
|
||||||
|
227, 197, 117, 252, 2, 219, 233, 68, 180, 225, 77, 219})
|
||||||
|
defer resetRandReader()
|
||||||
|
|
||||||
|
// Encrypt with a dummy key
|
||||||
|
encrypter, err := NewEncrypter(RSA_OAEP, A256GCM, publicKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
object, err := encrypter.Encrypt(plaintext)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
serialized, err := object.CompactSerialize()
|
||||||
|
if serialized != expectedCompact {
|
||||||
|
t.Error("Compact serialization is not what we expected", serialized, expectedCompact)
|
||||||
|
}
|
||||||
|
|
||||||
|
serialized = object.FullSerialize()
|
||||||
|
if serialized != expectedFull {
|
||||||
|
t.Error("Full serialization is not what we expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVectorsJWECorrupt(t *testing.T) {
|
||||||
|
priv := &rsa.PrivateKey{
|
||||||
|
PublicKey: rsa.PublicKey{
|
||||||
|
N: fromHexInt(`
|
||||||
|
a8b3b284af8eb50b387034a860f146c4919f318763cd6c5598c8
|
||||||
|
ae4811a1e0abc4c7e0b082d693a5e7fced675cf4668512772c0c
|
||||||
|
bc64a742c6c630f533c8cc72f62ae833c40bf25842e984bb78bd
|
||||||
|
bf97c0107d55bdb662f5c4e0fab9845cb5148ef7392dd3aaff93
|
||||||
|
ae1e6b667bb3d4247616d4f5ba10d4cfd226de88d39f16fb`),
|
||||||
|
E: 65537,
|
||||||
|
},
|
||||||
|
D: fromHexInt(`
|
||||||
|
53339cfdb79fc8466a655c7316aca85c55fd8f6dd898fdaf1195
|
||||||
|
17ef4f52e8fd8e258df93fee180fa0e4ab29693cd83b152a553d
|
||||||
|
4ac4d1812b8b9fa5af0e7f55fe7304df41570926f3311f15c4d6
|
||||||
|
5a732c483116ee3d3d2d0af3549ad9bf7cbfb78ad884f84d5beb
|
||||||
|
04724dc7369b31def37d0cf539e9cfcdd3de653729ead5d1`),
|
||||||
|
Primes: []*big.Int{
|
||||||
|
fromHexInt(`
|
||||||
|
d32737e7267ffe1341b2d5c0d150a81b586fb3132bed2f8d5262
|
||||||
|
864a9cb9f30af38be448598d413a172efb802c21acf1c11c520c
|
||||||
|
2f26a471dcad212eac7ca39d`),
|
||||||
|
fromHexInt(`
|
||||||
|
cc8853d1d54da630fac004f471f281c7b8982d8224a490edbeb3
|
||||||
|
3d3e3d5cc93c4765703d1dd791642f1f116a0dd852be2419b2af
|
||||||
|
72bfe9a030e860b0288b5d77`),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
corruptCiphertext := stripWhitespace(`
|
||||||
|
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.NFl09dehy
|
||||||
|
IR2Oh5iSsvEa82Ps7DLjRHeo0RnuTuSR45OsaIP6U8yu7vLlWaZKSZMy
|
||||||
|
B2qRBSujf-5XIRoNhtyIyjk81eJRXGa_Bxaor1XBCMyyhGchW2H2P71f
|
||||||
|
PhDO6ufSC7kV4bNqgHR-4ziS7KXwzN83_5kogXqxUpymUoJDNc.tk-GT
|
||||||
|
W_VVhiTIKFF.D_BE6ImZUl9F.52a-zFnRb3YQwIC7UrhVyQ`)
|
||||||
|
|
||||||
|
corruptAuthtag := stripWhitespace(`
|
||||||
|
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.NFl09dehy
|
||||||
|
IR2Oh5iSsvEa82Ps7DLjRHeo0RnuTuSR45OsaIP6U8yu7vLlWaZKSZMy
|
||||||
|
B2qRBSujf-5XIRoNhtyIyjk81eJRXGa_Bxaor1XBCMyyhGchW2H2P71f
|
||||||
|
PhDO6ufSC7kV4bNqgHR-4ziS7KNwzN83_5kogXqxUpymUoJDNc.tk-GT
|
||||||
|
W_VVhiTIKFF.D_BE6ImZUl9F.52a-zFnRb3YQwiC7UrhVyQ`)
|
||||||
|
|
||||||
|
msg, _ := ParseEncrypted(corruptCiphertext)
|
||||||
|
_, err := msg.Decrypt(priv)
|
||||||
|
if err != ErrCryptoFailure {
|
||||||
|
t.Error("should detect corrupt ciphertext")
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, _ = ParseEncrypted(corruptAuthtag)
|
||||||
|
_, err = msg.Decrypt(priv)
|
||||||
|
if err != ErrCryptoFailure {
|
||||||
|
t.Error("should detect corrupt auth tag")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test vectors generated with nimbus-jose-jwt
|
||||||
|
func TestSampleNimbusJWEMessagesRSA(t *testing.T) {
|
||||||
|
rsaPrivateKey, err := LoadPrivateKey(fromBase64Bytes(`
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCNRCEmf5PlbXKuT4uwnb
|
||||||
|
wGKvFrtpi+bDYxOZxxqxdVkZM/bYATAnD1fg9pNvLMKeF+MWJ9kPIMmDgOh9RdnRdLvQGb
|
||||||
|
BzhLmxwhhcua2QYiHEZizXmiaXvNP12bzEBhebdX7ObW8izMVW0p0lqHPNzkK3K75B0Sxo
|
||||||
|
FMVKkZ7KtBHgepBT5yPhPPcNe5lXQeTne5bo3I60DRcN9jTBgMJOXdq0I9o4y6ZmoXdNTm
|
||||||
|
0EyLzn9/EYiHqBxtKFh791EHR7wYgyi/t+nOKr4sO74NbEByP0mHDil+mPvZSzFW4l7fPx
|
||||||
|
OclRZvpRIKIub2TroZA9s2WsshGf79eqqXYbBB9NNRAgMBAAECggEAIExbZ/nzTplfhwsY
|
||||||
|
3SCzRJW87OuqsJ79JPQPGM4NX7sQ94eJqM7+FKLl0yCFErjgnYGdCyiArvB+oJPdsimgke
|
||||||
|
h83X0hGeg03lVA3/6OsG3WifCAxulnLN44AM8KST8S9D9t5+cm5vEBLHazzAfWWTS13s+g
|
||||||
|
9hH8rf8NSqgZ36EutjKlvLdHx1mWcKX7SREFVHT8FWPAbdhTLEHUjoWHrfSektnczaSHnt
|
||||||
|
q8fFJy6Ld13QkF1ZJRUhtA24XrD+qLTc+M36IuedjeZaLHFB+KyhYR3YvXEtrbCug7dCRd
|
||||||
|
uG6uTlDCSaSy7xHeTPolWtWo9F202jal54otxiAJFGUHgQKBgQDRAT0s6YQZUfwE0wluXV
|
||||||
|
k0JdhDdCo8sC1aMmKlRKWUkBAqrDl7BI3MF56VOr4ybr90buuscshFf9TtrtBOjHSGcfDI
|
||||||
|
tSKfhhkW5ewQKB0YqyHzoD6UKT0/XAshFY3esc3uCxuJ/6vOiXV0og9o7eFvr51O0TfDFh
|
||||||
|
mcTvW4wirKlQKBgQCtB7UAu8I9Nn8czkd6oXLDRyTWYviuiqFmxR+PM9klgZtsumkeSxO1
|
||||||
|
lkfFoj9+G8nFaqYEBA9sPeNtJVTSROCvj/iQtoqpV2NiI/wWeVszpBwsswx2mlks4LJa8a
|
||||||
|
Yz9xrsfNoroKYVppefc/MCoSx4M+99RSm3FSpLGZQHAUGyzQKBgQDMQmq4JuuMF1y2lk0E
|
||||||
|
SESyuz21BqV0tDVOjilsHT+5hmXWXoS6nkO6L2czrrpM7YE82F6JJZBmo7zEIXHBInGLJ3
|
||||||
|
XLoYLZ5qNEhqYDUEDHaBCBWZ1vDTKnZlwWFEuXVavNNZvPbUhKTHq25t8qjDki/r09Vykp
|
||||||
|
BsM2yNBKpbBOVQKBgCJyUVd3CaFUExQyAMrqD0XPCQdhJq7gzGcAQVsp8EXmOoH3zmuIeM
|
||||||
|
ECzQEMXuWFNLMHm0tbX5Kl83vMHcnKioyI9ewhWxOBYTitf0ceG8j5F97SOl32NmCXzwoJ
|
||||||
|
55Oa0xJXfLuIvOe8hZzp4WwZmBfKBxiCR166aPQQgIawelrVAoGAEJsHomfCI4epxH4oMw
|
||||||
|
qYJMCGy95zloB+2+c86BZCOJAGwnfzbtc2eutWZw61/9sSO8sQCfzA8oX+5HwAgnFVzwW4
|
||||||
|
lNMZohppYcpwN9EyjkPaCXuALC7p5rF2o63wY7JLvnjS2aYZliknh2yW6X6fSB0PK0Cpvd
|
||||||
|
lAIyRw6Kud0zI=`))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rsaSampleMessages := []string{
|
||||||
|
"eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBMV81In0.EW0KOhHeoAxTBnLjYhh2T6HjwI-srNs6RpcSdZvE-GJ5iww3EYWBCmeGGj1UVz6OcBfwW3wllZ6GPOHU-hxVQH5KYpVOjkmrFIYU6-8BHhxBP_PjSJEBCZzjOgsCm9Th4-zmlO7UWTdK_UtwE7nk4X-kkmEy-aZBCShA8nFe2MVvqD5F7nvEWNFBOHh8ae_juo-kvycoIzvxLV9g1B0Zn8K9FAlu8YF1KiL5NFekn76f3jvAwlExuRbFPUx4gJN6CeBDK_D57ABsY2aBVDSiQceuYZxvCIAajqSS6dMT382FNJzAiQhToOpo_1w5FnnBjzJLLEKDk_I-Eo2YCWxxsQ.5mCMuxJqLRuPXGAr.Ghe4INeBhP3MDWGvyNko7qanKdZIzKjfeiU.ja3UlVWJXKNFJ-rZsJWycw",
|
||||||
|
"eyJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiUlNBMV81In0.JsJeYoP0St1bRYNUaAmA34DAA27usE7RNuC2grGikBRmh1xrwUOpnEIXXpwr7fjVmNi52zzWkNHC8JkkRTrLcCh2VXvnOnarpH8DCr9qM6440bSrahzbxIvDds8z8q0wT1W4kjVnq1mGwGxg8RQNBWTV6Sp2FLQkZyjzt_aXsgYzr3zEmLZxB-d41lBS81Mguk_hdFJIg_WO4ao54lozvxkCn_uMiIZ8eLb8qHy0h-N21tiHGCaiC2vV8KXomwoqbJ0SXrEH4r9_R2J844H80TBZdbvNBd8whvoQNHvOX659LNs9EQ9xxvHU2kqGZekXBu7sDXXTjctMkMITobGSzw.1v5govaDvanP3LGp.llwYNBDrD7MwVLaFHesljlratfmndWs4XPQ.ZGT1zk9_yIKi2GzW6CuAyA",
|
||||||
|
"eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBMV81In0.fBv3fA3TMS3ML8vlsCuvwdsKvB0ym8R30jJrlOiqkWKk7WVUkjDInFzr1zw3Owla6c5BqOJNoACXt4IWbkLbkoWV3tweXlWwpafuaWPkjLOUH_K31rS2fCX5x-MTj8_hScquVQXpbz3vk2EfulRmGXZc_8JU2NqQCAsYy3a28houqP3rDe5jEAvZS2SOFvJkKW--f5S-z39t1D7fNz1N8Btd9SmXWQzjbul5YNxI9ctqxhJpkKYpxOLlvrzdA6YdJjOlDx3n6S-HnSZGM6kQd_xKtAf8l1EGwhQmhbXhMhjVxMvGwE5BX7PAb8Ccde5bzOCJx-PVbVetuLb169ZYqQ._jiZbOPRR82FEWMZ.88j68LI-K2KT6FMBEdlz6amG5nvaJU8a-90.EnEbUTJsWNqJYKzfO0x4Yw",
|
||||||
|
"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBMV81In0.bN6FN0qmGxhkESiVukrCaDVG3woL0xE-0bHN_Mu0WZXTQWbzzT-7jOvaN1xhGK8nzi8qpCSRgE5onONNB9i8OnJm3MMIxF7bUUEAXO9SUAFn2v--wNc4drPc5OjIu0RiJrDVDkkGjNrBDIuBaEQcke7A0v91PH58dXE7o4TLPzC8UJmRtXWhUSwjXVF3-UmYRMht2rjHJlvRbtm6Tu2LMBIopRL0zj6tlPP4Dm7I7sz9OEB3VahYAhpXnFR7D_f8RjLSXQmBvB1FiI5l_vMz2NFt2hYUmQF3EJMLIEdHvvPp3iHDGiXC1obJrDID_CCf3qs9UY7DMYL622KLvP2NIg.qb72oxECzxd_aNuHVR0aNg.Gwet9Ms8hB8rKEb0h4RGdFNRq97Qs2LQaJM0HWrCqoI.03ljVThOFvgXzMmQJ79VjQ",
|
||||||
|
"eyJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiUlNBMV81In0.ZbEOP6rqdiIP4g7Nl1PL5gwhgDwv9RinyiUQxZXPOmD7kwEZrZ093dJnhqI9kEd3QGFlHDpB7HgNz53d27z2zmEj1-27v6miizq6tH4sN2MoeZLwSyk16O1_n3bVdDmROawsTYYFJfHsuLwyVJxPd37duIYnbUCFO9J8lLIv-2VI50KJ1t47YfE4P-Wt9jVzxP2CVUQaJwTlcwfiDLJTagYmfyrDjf525WlQFlgfJGqsJKp8BX9gmKvAo-1iCBAM8VpEjS0u0_hW9VSye36yh8BthVV-VJkhJ-0tMpto3bbBmj7M25Xf4gbTrrVU7Nz6wb18YZuhHZWmj2Y2nHV6Jg.AjnS44blTrIIfFlqVw0_Mg.muCRgaEXNKKpW8rMfW7jf7Zpn3VwSYDz-JTRg16jZxY.qjc9OGlMaaWKDWQSIwVpR4K556Pp6SF9",
|
||||||
|
"eyJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiUlNBMV81In0.c7_F1lMlRHQQE3WbKmtHBYTosdZrG9hPfs-F9gNQYet61zKG8NXVkSy0Zf2UFHt0vhcO8hP2qrqOFsy7vmRj20xnGHQ2EE29HH6hwX5bx1Jj3uE5WT9Gvh0OewpvF9VubbwWTIObBpdEG7XdJsMAQlIxtXUmQYAtLTWcy2ZJipyJtVlWQLaPuE8BKfZH-XAsp2CpQNiRPI8Ftza3EAspiyRfVQbjKt7nF8nuZ2sESjt7Y50q4CSiiCuGT28T3diMN0_rWrH-I-xx7OQvJlrQaNGglGtu3jKUcrJDcvxW2e1OxriaTeuQ848ayuRvGUNeSv6WoVYmkiK1x_gNwUAAbw.7XtSqHJA7kjt6JrfxJMwiA.Yvi4qukAbdT-k-Fd2s4G8xzL4VFxaFC0ZIzgFDAI6n0.JSWPJ-HjOE3SK9Lm0yHclmjS7Z1ahtQga9FHGCWVRcc",
|
||||||
|
"eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.SYVxJbCnJ_tcR13LJpaqHQj-nGNkMxre4A1FmnUdxnvzeJwuvyrLiUdRsZR1IkP4fqLtDON2mumx39QeJQf0WIObPBYlIxycRLkwxDHRVlyTmPvdZHAxN26jPrk09wa5SgK1UF1W1VSQIPm-Tek8jNAmarF1Yxzxl-t54wZFlQiHP4TuaczugO5f-J4nlWenfla2mU1snDgdUMlEZGOAQ_gTEtwSgd1MqXmK_7LZBkoDqqoCujMZhziafJPXPDaUUqBLW3hHkkDA7GpVec3XcTtNUWQJqOpMyQhqo1KQMc8jg3fuirILp-hjvvNVtBnCRBvbrKUCPzu2_yH3HM_agA.2VsdijtonAxShNIW.QzzB3P9CxYP3foNKN0Ma1Z9tMwijAlkWo08.ZdQkIPDY_M-hxqi5fD4NGw",
|
||||||
|
"eyJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.Z2oTJXXib1u-S38Vn3DRKE3JnhnwgUa92UhsefzY2Wpdn0dmxMfYt9iRoJGFfSAcA97MOfjyvXVRCKWXGrG5AZCMAXEqU8SNQwKPRjlcqojcVzQyMucXI0ikLC4mUgeRlfKTwsBicq6JZZylzRoLGGSNJQbni3_BLsf7H3Qor0BYg0FPCLG9Z2OVvrFzvjTLmZtV6gFlVrMHBxJub_aUet9gAkxiu1Wx_Kx46TlLX2tkumXIpTGlzX6pef6jLeZ5EIg_K-Uz4tkWgWQIEkLD7qmTyk5pAGmzukHa_08jIh5-U-Sd8XGZdx4J1pVPJ5CPg0qDJGZ_cfgkgpWbP_wB6A.4qgKfokK1EwYxz20._Md82bv_KH2Vru0Ue2Eb6oAqHP2xBBP5jF8.WFRojvQpD5VmZlOr_dN0rQ",
|
||||||
|
"eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.JzCUgJcBJmBgByp4PBAABUfhezPvndxBIVzaoZ96DAS0HPni0OjMbsOGsz6JwNsiTr1gSn_S6R1WpZM8GJc9R2z0EKKVP67TR62ZSG0MEWyLpHmG_4ug0fAp1HWWMa9bT4ApSaOLgwlpVAb_-BPZZgIu6c8cREuMon6UBHDqW1euTBbzk8zix3-FTZ6p5b_3soDL1wXfRiRBEsxxUGMnpryx1OFb8Od0JdyGF0GgfLt6OoaujDJpo-XtLRawu1Xlg6GqRs0NQwSHZ5jXgQ6-zgCufXonAmYTiIyBXY2no9XmECTexjwrS_05nA7H-UyIZEBOCp3Yhz2zxrt5j_0pvQ.SJR-ghhaUKP4zXtZ.muiuzLfZA0y0BDNsroGTw2r2-l73SLf9lK8.XFMH1oHr1G6ByP3dWSUUPA",
|
||||||
|
"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBLU9BRVAifQ.U946MVfIm4Dpk_86HrnIA-QXyiUu0LZ67PL93CMLmEtJemMNDqmRd9fXyenCIhAC7jPIV1aaqW7gS194xyrrnUpBoJBdbegiPqOfquy493Iq_GQ8OXnFxFibPNQ6rU0l8BwIfh28ei_VIF2jqN6bhxFURCVW7fG6n6zkCCuEyc7IcxWafSHjH2FNttREuVj-jS-4LYDZsFzSKbpqoYF6mHt8H3btNEZDTSmy_6v0fV1foNtUKNfWopCp-iE4hNh4EzJfDuU8eXLhDb03aoOockrUiUCh-E0tQx9su4rOv-mDEOHHAQK7swm5etxoa7__9PC3Hg97_p4GM9gC9ykNgw.pnXwvoSPi0kMQP54of-HGg.RPJt1CMWs1nyotx1fOIfZ8760mYQ69HlyDp3XmdVsZ8.Yxw2iPVWaBROFE_FGbvodA",
|
||||||
|
"eyJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiUlNBLU9BRVAifQ.eKEOIJUJpXmO_ghH_nGCJmoEspqKyiy3D5l0P8lKutlo8AuYHPQlgOsaFYnDkypyUVWd9zi-JaQuCeo7dzoBiS1L71nAZo-SUoN0anQBkVuyuRjr-deJMhPPfq1H86tTk-4rKzPr1Ivd2RGXMtWsrUpNGk81r1v8DdMntLE7UxZQqT34ONuZg1IXnD_U6di7k07unI29zuU1ySeUr6w1YPw5aUDErMlpZcEJWrgOEYWaS2nuC8sWGlPGYEjqkACMFGn-y40UoS_JatNZO6gHK3SKZnXD7vN5NAaMo_mFNbh50e1t_zO8DaUdLtXPOBLcx_ULoteNd9H8HyDGWqwAPw.0xmtzJfeVMoIT1Cp68QrXA.841l1aA4c3uvSYfw6l180gn5JZQjL53WQ5fr8ejtvoI.lojzeWql_3gDq-AoaIbl_aGQRH_54w_f",
|
||||||
|
"eyJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiUlNBLU9BRVAifQ.D0QkvIXR1TL7dIHWuPNMybmmD8UPyQd1bRKjRDNbA2HmKGpamCtcJmpNB_EetNFe-LDmhe44BYI_XN2wIBbYURKgDK_WG9BH0LQw_nCVqQ-sKqjtj3yQeytXhLHYTDmiF0TO-uW-RFR7GbPAdARBfuf4zj82r_wDD9sD5WSCGx89iPfozDOYQ_OLwdL2WD99VvDyfwS3ZhxA-9IMSYv5pwqPkxj4C0JdjCqrN0YNrZn_1ORgjtsVmcWXsmusObTozUGA7n5GeVepfZdU1vrMulAwdRYqOYtlqKaOpFowe9xFN3ncBG7wb4f9pmzbS_Dgt-1_Ii_4SEB9GQ4NiuBZ0w.N4AZeCxMGUv52A0UVJsaZw.5eHOGbZdtahnp3l_PDY-YojYib4ft4SRmdsQ2kggrTs.WsmGH8ZDv4ctBFs7qsQvw2obe4dVToRcAQaZ3PYL34E",
|
||||||
|
"eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.fDTxO_ZzZ3Jdrdw-bxvg7u-xWB2q1tp3kI5zH6JfhLUm4h6rt9qDA_wZlRym8-GzEtkUjkTtQGs6HgQx_qlyy8ylCakY5GHsNhCG4m0UNhRiNfcasAs03JSXfON9-tfTJimWD9n4k5OHHhvcrsCW1G3jYeLsK9WHCGRIhNz5ULbo8HBrCTbmZ6bOEQ9mqhdssLpdV24HDpebotf3bgPJqoaTfWU6Uy7tLmPiNuuNRLQ-iTpLyNMTVvGqqZhpcV3lAEN5l77QabI5xLJYucvYjrXQhAEZ7YXO8oRYhGkdG2XXIRcwr87rBeRH-47HAyhZgF_PBPBhhrJNS9UNMqdfBw.FvU4_s7Md6vxnXWd.fw29Q4_gHt4f026DPPV-CNebQ8plJ6IVLX8._apBZrw7WsT8HOmxgCrTwA",
|
||||||
|
"eyJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.bYuorK-rHMbO4c2CRWtvyOEaM1EN-o-wLRZ0wFWRX9mCXQ-iTNarZn7ksYM1XnGmZ4u3CSowX1Hpca9Rg72_VJCmKapqCT7r3YfasN4_oeLwuSKI_gT-uVOznod97tn3Gf_EDv0y1V4H0k9BEIFGbajAcG1znTD_ODY3j2KZJxisfrsBoslc6N-HI0kKZMC2hSGuHOcOf8HN1sTE-BLqZCtoj-zxQECJK8Wh14Ih4jzzdmmiu_qmSR780K6su-4PRt3j8uY7oCiLBfwpCsCmhJgp8rKd91zoedZmamfvX38mJIfE52j4fG6HmIYw9Ov814fk9OffV6tzixjcg54Q2g.yeVJz4aSh2s-GUr9.TBzzWP5llEiDdugpP2SmPf2U4MEGG9EoPWk.g25UoWpsBaOd45J__FX7mA",
|
||||||
|
"eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.h9tFtmh762JuffBxlSQbJujCyI4Zs9yc3IOb1yR8g65W4ZHosIvzVGHWbShj4EY9MNrz-RbKtHfqQGGzDeo3Xb4-HcQ2ZDHyWoUg7VfA8JafJ5zIKL1npz8eUExOVMLsAaRfHg8qNfczodg3egoSmX5Q-nrx4DeidDSXYZaZjV0C72stLTPcuQ7XPV7z1tvERAkqpvcsRmJn_PiRNxIbAgoyHMJ4Gijuzt1bWZwezlxYmw0TEuwCTVC2fl9NJTZyxOntS1Lcm-WQGlPkVYeVgYTOQXLlp7tF9t-aAvYpth2oWGT6Y-hbPrjx_19WaKD0XyWCR46V32DlXEVDP3Xl2A.NUgfnzQyEaJjzt9r.k2To43B2YVWMeR-w3n4Pr2b5wYq2o87giHk.X8_QYCg0IGnn1pJqe8p_KA",
|
||||||
|
"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.EDq6cNP6Yp1sds5HZ4CkXYp7bs9plIYVZScKvuyxUy0H1VyBC_YWg0HvndPNb-vwh1LA6KMxRazlOwJ9iPR9YzHnYmGgPM3Je_ZzBfiPlRfq6hQBpGnNaypBI1XZ2tyFBhulsVLqyJe2SmM2Ud00kasOdMYgcN8FNFzq7IOE7E0FUQkIwLdUL1nrzepiYDp-5bGkxWRcL02cYfdqdm00G4m0GkUxAmdxa3oPNxZlt2NeBI_UVWQSgJE-DJVJQkDcyA0id27TV2RCDnmujYauNT_wYlyb0bFDx3pYzzNXfAXd4wHZxt75QaLZ5APJ0EVfiXJ0qki6kT-GRVmOimUbQA.vTULZL7LvS0WD8kR8ZUtLg.mb2f0StEmmkuuvsyz8UplMvF58FtZzlu8eEwzvPUvN0.hbhveEN40V-pgG2hSVgyKg",
|
||||||
|
"eyJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.DuYk92p7u-YIN-JKn-XThmlVcnhU9x5TieQ2uhsLQVNlo0iWC9JJPP6bT6aI6u_1BIS3yE8_tSGGL7eM-zyEk6LuTqSWFRaZcZC06d0MnS9eYZcw1T2D17fL-ki-NtCaTahJD7jE2s0HevRVW49YtL-_V8whnO_EyVjvXIAQlPYqhH_o-0Nzcpng9ggdAnuF2rY1_6iRPYFJ3BLQvG1oWhyJ9s6SBttlOa0i6mmFCVLHx6sRpdGAB3lbCL3wfmHq4tpIv77gfoYUNP0SNff-zNmBXF_wp3dCntLZFTjbfMpGyHlruF_uoaLqwdjYpUGNUFVUoeSiMnSbMKm9NxiDgQ.6Mdgcqz7bMU1UeoAwFC8pg.W36QWOlBaJezakUX5FMZzbAgeAu_R14AYKZCQmuhguw.5OeyIJ03olxmJft8uBmjuOFQPWNZMYLI",
|
||||||
|
"eyJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.ECulJArWFsPL2FlpCN0W8E7IseSjJg1cZqE3wz5jk9gvwgNForAUEv5KYZqhNI-p5IxkGV0f8K6Y2X8pWzbLwiPIjZe8_dVqHYJoINxqCSgWLBhz0V36qL9Nc_xARTBk4-ZteIu75NoXVeos9gNvFnkOCj4tm-jGo8z8EFO9XfODgjhiR4xv8VqUtvrkjo9GQConaga5zpV-J4JQlXbdqbDjnuwacnJAxYpFyuemqcgqsl6BnFX3tovGkmSUPqcvF1A6tiHqr-TEmcgVqo5C3xswknRBKTQRM00iAmJ92WlVdkoOCx6E6O7cVHFawZ14BLzWzm66Crb4tv0ucYvk_Q.mxolwUaoj5S5kHCfph0w8g.nFpgYdnYg3blHCCEi2XXQGkkKQBXs2OkZaH11m3PRvk.k8BAVT4EcyrUFVIKr-KOSPbF89xyL0Vri2rFTu2iIWM",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, msg := range rsaSampleMessages {
|
||||||
|
obj, err := ParseEncrypted(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to parse message", msg, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
plaintext, err := obj.Decrypt(rsaPrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to decrypt message", msg, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if string(plaintext) != "Lorem ipsum dolor sit amet" {
|
||||||
|
t.Error("plaintext is not what we expected for msg", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test vectors generated with nimbus-jose-jwt
|
||||||
|
func TestSampleNimbusJWEMessagesAESKW(t *testing.T) {
|
||||||
|
aesTestKeys := [][]byte{
|
||||||
|
fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D"),
|
||||||
|
fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D95EC9CDC2D82233C"),
|
||||||
|
fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D95EC9CDC2D82233C333C35BA29044E90"),
|
||||||
|
}
|
||||||
|
|
||||||
|
aesSampleMessages := [][]string{
|
||||||
|
[]string{
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwidGFnIjoib2ZMd2Q5NGloVWFRckJ0T1pQUDdjUSIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiV2Z3TnN5cjEwWUFjY2p2diJ9.9x3RxdqIS6P9xjh93Eu1bQ.6fs3_fSGt2jull_5.YDlzr6sWACkFg_GU5MEc-ZEWxNLwI_JMKe_jFA.f-pq-V7rlSSg_q2e1gDygw",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwidGFnIjoic2RneXB1ckFjTEFzTmZJU0lkZUNpUSIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoieVFMR0dCdDJFZ0c1THdyViJ9.arslKo4aKlh6f4s0z1_-U-8JbmhAoZHN.Xw2Q-GX98YXwuc4i.halTEWMWAYZbv-qOD52G6bte4x6sxlh1_VpGEA.Z1spn016v58cW6Q2o0Qxag",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwidGFnIjoicTNzejF5VUlhbVBDYXJfZ05kSVJqQSIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiM0ZRM0FsLWJWdWhmcEIyQyJ9.dhVipWbzIdsINttuZM4hnjpHvwEHf0VsVrOp4GAg01g.dk7dUyt1Qj13Pipw.5Tt70ONATF0BZAS8dBkYmCV7AQUrfb8qmKNLmw.A6ton9MQjZg0b3C0QcW-hg",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidGFnIjoiUHNpTGphZnJZNE16UlRmNlBPLTZfdyIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiSUFPbnd2ODR5YXFEaUxtbSJ9.swf92_LyCvjsvkynHTuMNXRl_MX2keU-fMDWIMezHG4.LOp9SVIXzs4yTnOtMyXZYQ.HUlXrzqJ1qXYl3vUA-ydezCg77WvJNtKdmZ3FPABoZw.8UYl1LOofQLAxHHvWqoTbg",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwidGFnIjoiWGRndHQ5dUVEMVlVeU1rVHl6M3lqZyIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiWF90V2RhSmh6X3J1SHJvQSJ9.JQ3dS1JSgzIFi5M9ig63FoFU1nHBTmPwXY_ovNE2m1JOSUvHtalmihIuraPDloCf.e920JVryUIWt7zJJQM-www.8DUrl4LmsxIEhRr9RLTHG9tBTOcwXqEbQHAJd_qMHzE.wHinoqGUhL4O7lx125kponpwNtlp8VGJ",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwidGFnIjoicGgyaTdoY0FWNlh3ZkQta1RHYlVXdyIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiaG41Smk4Wm1rUmRrSUxWVSJ9._bQlJXl22dhsBgYPhkxUyinBNi871teGWbviOueWj2PqG9OPxIc9SDS8a27YLSVDMircd5Q1Df28--vcXIABQA.DssmhrAg6w_f2VDaPpxTbQ.OGclEmqrxwvZqAfn7EgXlIfXgr0wiGvEbZz3zADnqJs.YZeP0uKVEiDl8VyC-s20YN-RbdyGNsbdtoGDP3eMof8",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiQTEyOEtXIn0.TEMcXEoY8WyqGjYs5GZgS-M_Niwu6wDY.i-26KtTt51Td6Iwd.wvhkagvPsLj3QxhPBbfH_th8OqxisUtme2UadQ.vlfvBPv3bw2Zk2H60JVNLQ",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiQTEyOEtXIn0.gPaR6mgQ9TUx05V6DRfgTQeZxl0ZSzBa5uQd-qw6yLs.MojplOD77FkMooS-.2yuD7dKR_C3sFbhgwiBccKKOF8DrSvNiwX7wPQ.qDKUbSvMnJv0qifjpWC14g",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiQTEyOEtXIn0.Fg-dgSkUW1KEaL5YDPoWHNL8fpX1WxWVLA9OOWsjIFhQVDKyUZI7BQ.mjRBpyJTZf7H-quf.YlNHezMadtaSKp23G-ozmYhHOeHwuJnvWGTtGg.YagnR7awBItUlMDo4uklvg",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTEyOEtXIn0.x1vYzUE-E2XBWva9OPuwtqfQaf9rlJCIBAyAe6N2q2kWfJrkxGxFsQ.gAwe78dyODFaoP2IOityAA.Yh5YfovkWxGBNAs1sVhvXow_2izHHsBiYEc9JYD6kVg.mio1p3ncp2wLEaEaRa7P0w",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiQTEyOEtXIn0.szGrdnmF7D5put2aRBvSSFfp0vRgkRGYaafijJIqAF6PWd1IxsysZRV8aQkQOW1cB6d0fXsTfYM.Ru25LVOOk4xhaK-cIZ0ThA.pF9Ok5zot7elVqXFW5YYHV8MuF9gVGzpQnG1XDs_g_w.-7la0uwcNPpteev185pMHZjbVDXlrec8",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiQTEyOEtXIn0.cz-hRv0xR5CnOcnoRWNK8Q9poyVYzRCVTjfmEXQN6xPOZUkJ3zKNqb8Pir_FS0o2TVvxmIbuxeISeATTR2Ttx_YGCNgMkc93.SF5rEQT94lZR-UORcMKqGw.xphygoU7zE0ZggOczXCi_ytt-Evln8CL-7WLDlWcUHg.5h99r8xCCwP2PgDbZqzCJ13oFfB2vZWetD5qZjmmVho",
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwidGFnIjoiVWR5WUVKdEJ5ZTA5dzdjclY0cXI1QSIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiZlBBV0QwUmdSbHlFdktQcCJ9.P1uTfTuH-imL-NJJMpuTRA.22yqZ1NIfx3KNPgc.hORWZaTSgni1FS-JT90vJly-cU37qTn-tWSqTg.gMN0ufXF92rSXupTtBNkhA",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwidGFnIjoiOU9qX3B2LTJSNW5lZl9YbWVkUWltUSIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiY3BybGEwYUYzREVQNmFJTSJ9.6NVpAm_APiC7km2v-oNR8g23K9U_kf1-.jIg-p8tNwSvwxch0.1i-GPaxS4qR6Gy4tzeVtSdRFRSKQSMpmn-VhzA.qhFWPqtA6vVPl7OM3DThsA",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwidGFnIjoiOVc3THg3MVhGQVJCb3NaLVZ5dXc4ZyIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiZ1N4ZE5heFdBSVBRR0tHYiJ9.3YjPz6dVQwAtCekvtXiHZrooOUlmCsMSvyfwmGwdrOA.hA_C0IDJmGaRzsB0.W4l7OPqpFxiVOZTGfAlRktquyRTo4cEOk9KurQ.l4bGxOkO_ql_jlPo3Oz3TQ",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidGFnIjoiOHJYbWl2WXFWZjNfbHhhd2NUbHJoUSIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiVXBWeXprVTNKcjEwYXRqYyJ9.8qft-Q_xqUbo5j_aVrVNHchooeLttR4Kb6j01O8k98M.hXO-5IKBYCL9UdwBFVm0tg.EBM4lCZX_K6tfqYmfoDxVPHcf6cT--AegXTTjfSqsIw.Of8xUvEQSh3xgFT3uENnAg",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwidGFnIjoiVnItSnVaX0tqV2hSWWMzdzFwZ3cwdyIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiRGg2R3dISVBVS3ljZGNZeCJ9.YSEDjCnGWr_n9H94AvLoRnwm6bdU9w6-Q67k-QQRVcKRd6673pgH9zEF9A9Dt6o1.gcmVN4kxqBuMq6c7GrK3UQ.vWzJb0He6OY1lhYYjYS7CLh55REAAq1O7yNN-ND4R5Q.OD0B6nwyFaDr_92ysDOtlVnJaeoIqhGw",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwidGFnIjoieEtad1BGYURpQ3NqUnBqZUprZHhmZyIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoieTVHRFdteXdkb2R1SDJlYyJ9.AW0gbhWqlptOQ1y9aoNVwrTIIkBfrp33C2OWJsbrDRk6lhxg_IgFhMDTE37moReySGUtttC4CXQD_7etHmd3Hw.OvKXK-aRKlXHOpJQ9ZY_YQ.Ngv7WarDDvR2uBj_DavPAR3DYuIaygvSSdcHrc8-ZqM.MJ6ElitzFCKf_0h5fIJw8uOLC6ps7dKZPozF8juQmUY",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiQTE5MktXIn0.8qu63pppcSvp1vv37WrZ44qcCTg7dQMA.cDp-f8dJTrDEpZW4.H6OBJYs4UvFR_IZHLYQZxB6u9a0wOdAif2LNfQ.1dB-id0UIwRSlmwHx5BJCg",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiQTE5MktXIn0._FdoKQvC8qUs7K0upriEihUwztK8gOwonXpOxdIwrfs.UO38ok8gDdpLVa1T.x1GvHdVCy4fxoQRg-OQK4Ez3jDOvu9gllLPeEA.3dLeZGIprh_nHizOTVi1xw",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiQTE5MktXIn0.uzCJskgSIK6VkjJIu-dQi18biqaY0INc_A1Ehx0oESafgtR99_n4IA.W2eKK8Y14WwTowI_.J2cJC7R6Bz6maR0s1UBMPyRi5BebNUAmof4pvw.-7w6htAlc4iUsOJ6I04rFg",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTE5MktXIn0.gImQeQETp_6dfJypFDPLlv7c5pCzuq86U16gzrLiCXth6X9XfxJpvQ.YlC4MxjtLWrsyEvlFhvsqw.Vlpvmg9F3gkz4e1xG01Yl2RXx-jG99rF5UvCxOBXSLc.RZUrU_FoR5bG3M-j3GY0Dw",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiQTE5MktXIn0.T2EfQ6Tu2wJyRMgZzfvBYmQNCCfdMudMrg86ibEMVAOUKJPtR3WMPEb_Syy9p2VjrLKRlv7nebo.GPc8VbarPPRtzIRATB8NsA.ugPCqLvVLwh55bWlwjsFkmWzJ31z5z-wuih2oJqmG_U.m7FY3EjvV6mKosEYJ5cY7ezFoVQoJS8X",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiQTE5MktXIn0.OgLMhZ-2ZhslQyHfzOfyC-qmT6bNg9AdpP59B4jtyxWkQu3eW475WCdiAjojjeyBtVRGQ5vOomwaOIFejY_IekzH6I_taii3.U9x44MF6Wyz5TIwIzwhoxQ.vK7yvSF2beKdNxNY_7n4XdF7JluCGZoxdFJyTJVkSmI.bXRlI8KL-g7gpprQxGmXjVYjYghhWJq7mlCfWI8q2uA",
|
||||||
|
},
|
||||||
|
[]string{
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwidGFnIjoiR3BjX3pfbjduZjJVZlEtWGdsaTBaQSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiUk40eUdhOVlvYlFhUmZ1TCJ9.Q4ukD6_hZpmASAVcqWJ9Wg.Zfhny_1WNdlp4fH-.3sekDCjkExQCcv28ZW4yrcFnz0vma3vgoenSXA.g8_Ird2Y0itTCDP61du-Yg",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwidGFnIjoiWC05UkNVWVh4U3NRelcwelVJS01VUSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiY3JNMnJfa3RrdWpyQ1h5OSJ9.c0q2jCxxV4y1h9u_Xvn7FqUDnbkmNEG4.S_noOTZKuUo9z1l6.ez0RdA25vXMUGH96iXmj3DEVox0J7TasJMnzgg.RbuSPTte_NzTtEEokbc5Ig",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwidGFnIjoiWmwyaDFpUW11QWZWd2lJeVp5RHloZyIsImFsZyI6IkEyNTZHQ01LVyIsIml2Ijoib19xZmljb0N0NzNzRWo1QyJ9.NpJxRJ0aqcpekD6HU2u9e6_pL_11JXjWvjfeQnAKkZU.4c5qBcBBrMWi27Lf.NKwNIb4b6cRDJ1TwMKsPrjs7ADn6aNoBdQClVw.yNWmSSRBqQfIQObzj8zDqw",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidGFnIjoiMXdwVEI3LWhjdzZUVXhCbVh2UzdhUSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiOUdIVnZJaDZ0a09vX2pHUSJ9.MFgIhp9mzlq9hoPqqKVKHJ3HL79EBYtV4iNhD63yqiU.UzW5iq8ou21VpZYJgKEN8A.1gOEzA4uAPvHP76GMfs9uLloAV10mKaxiZVAeL7iQA0.i1X_2i0bCAz-soXF9bI_zw",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwidGFnIjoiNThocUtsSk15Y1BFUEFRUlNfSzlNUSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiUDh3aTBWMTluVnZqNXpkOSJ9.FXidOWHNFJODO74Thq3J2cC-Z2B8UZkn7SikeosU0bUK6Jx_lzzmUZ-Lafadpdpj.iLfcDbpuBKFiSfiBzUQc7Q.VZK-aD7BFspqfvbwa0wE2wwWxdomzk2IKMetFe8bI44.7wC6rJRGa4x48xbYMd6NH9VzK8uNn4Cb",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwidGFnIjoicGcwOEpUcXdzMXdEaXBaRUlpVExoQSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiSlpodk9CdU1RUDFFZTZTNSJ9.wqVgTPm6TcYCTkpbwmn9sW4mgJROH2A3dIdSXo5oKIQUIVbQsmy7KXH8UYO2RS9slMGtb869C8o0My67GKg9dQ.ogrRiLlqjB1S5j-7a05OwA.2Y_LyqhU4S_RXMsB74bxcBacd23J2Sp5Lblw-sOkaUY.XGMiYoU-f3GaEzSvG41vpJP2DMGbeDFoWmkUGLUjc4M",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiQTI1NktXIn0.QiIZm9NYfahqYFIbiaoUhCCHjotHMkup.EsU0XLn4FjzzCILn.WuCoQkm9vzo95E7hxBtfYpt-Mooc_vmSTyzj6Q.NbeeYVy6gQPlmhoWDrZwaQ",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiQTI1NktXIn0.1ol3j_Lt0Os3UMe2Gypj0o8b77k0FSmqD7kNRNoMa9U.vZ2HMTgN2dgUd42h.JvNcy8-c8sYzOC089VtFSg2BOQx3YF8CqSTuJw.t03LRioWWKN3d7SjinU6SQ",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiQTI1NktXIn0.gbkk03l1gyrE9qGEMVtORiyyUqKsgzbqjLd8lw0RQ07WWn--TV4BgA.J8ThH4ac2UhSsMIP.g-W1piEGrdi3tNwQDJXpYm3fQjTf82mtVCrCOg.-vY05P4kiB9FgF2vwrSeXQ",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTI1NktXIn0.k86pQs7gmQIzuIWRFwesF32XY2xi1WbYxi7XUf_CYlOlehwGCTINHg.3NcC9VzfQgsECISKf4xy-g.v2amdo-rgeGsg-II_tvPukX9D-KAP27xxf2uQJ277Ws.E4LIE3fte3glAnPpnd8D9Q",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiQTI1NktXIn0.b8iN0Am3fCUvj7sBd7Z0lpfzBjh1MOgojV7J5rDfrcTU3b35RGYgEV1RdcrtUTBgUwITDjmU7jM.wsSDBFghDga_ERv36I2AOg.6uJsucCb2YReFOJGBdo4zidTIKLUmZBIXfm_M0AJpKk.YwdAfXI3HHcw2wLSnfCRtw4huZQtSKhz",
|
||||||
|
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiQTI1NktXIn0.akY9pHCbkHPh5VpXIrX0At41XnJIKBR9iMMkf301vKeJNAZYJTxWzeJhFd-DhQ47tMctc3YYkwZkQ5I_9fGYb_f0oBcw4esh.JNwuuHud78h6S99NO1oBQQ.0RwckPYATBgvw67upkAQ1AezETHc-gh3rryz19i5ryc.3XClRTScgzfMgLCHxHHoRF8mm9VVGXv_Ahtx65PskKQ",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, msgs := range aesSampleMessages {
|
||||||
|
for _, msg := range msgs {
|
||||||
|
obj, err := ParseEncrypted(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to parse message", msg, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
plaintext, err := obj.Decrypt(aesTestKeys[i])
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to decrypt message", msg, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if string(plaintext) != "Lorem ipsum dolor sit amet" {
|
||||||
|
t.Error("plaintext is not what we expected for msg", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test vectors generated with jose4j
|
||||||
|
func TestSampleJose4jJWEMessagesECDH(t *testing.T) {
|
||||||
|
ecTestKey := &ecdsa.PrivateKey{
|
||||||
|
PublicKey: ecdsa.PublicKey{
|
||||||
|
Curve: elliptic.P256(),
|
||||||
|
X: fromBase64Int("weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ"),
|
||||||
|
Y: fromBase64Int("e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck"),
|
||||||
|
},
|
||||||
|
D: fromBase64Int("VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw"),
|
||||||
|
}
|
||||||
|
|
||||||
|
ecSampleMessages := []string{
|
||||||
|
"eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOENCQy1IUzI1NiIsImVwayI6eyJrdHkiOiJFQyIsIngiOiJTQzAtRnJHUkVvVkpKSmg1TGhORmZqZnFXMC1XSUFyd3RZMzJzQmFQVVh3IiwieSI6ImFQMWlPRENveU9laTVyS1l2VENMNlRMZFN5UEdUN0djMnFsRnBwNXdiWFEiLCJjcnYiOiJQLTI1NiJ9fQ..3mifklTnTTGuA_etSUBBCw.dj8KFM8OlrQ3rT35nHcHZ7A5p84VB2OZb054ghSjS-M.KOIgnJjz87LGqMtikXGxXw",
|
||||||
|
"eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTE5MkNCQy1IUzM4NCIsImVwayI6eyJrdHkiOiJFQyIsIngiOiJUaHRGc0lRZ1E5MkZOYWFMbUFDQURLbE93dmNGVlRORHc4ampfWlJidUxjIiwieSI6IjJmRDZ3UXc3YmpYTm1nVThXMGpFbnl5ZUZkX3Y4ZmpDa3l1R29vTFhGM0EiLCJjcnYiOiJQLTI1NiJ9fQ..90zFayMkKc-fQC_19f6P3A.P1Y_7lMnfkUQOXW_en31lKZ3zAn1nEYn6fXLjmyVPrQ.hrgwy1cePVfhMWT0h-crKTXldglHZ-4g",
|
||||||
|
"eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTI1NkNCQy1IUzUxMiIsImVwayI6eyJrdHkiOiJFQyIsIngiOiI5R1Z6c3VKNWgySl96UURVUFR3WU5zUkFzVzZfY2RzN0pELVQ2RDREQ1ZVIiwieSI6InFZVGl1dVU4aTB1WFpoaS14VGlRNlZJQm5vanFoWENPVnpmWm1pR2lRTEUiLCJjcnYiOiJQLTI1NiJ9fQ..v2reRlDkIsw3eWEsTCc1NA.0qakrFdbhtBCTSl7EREf9sxgHBP9I-Xw29OTJYnrqP8.54ozViEBYYmRkcKp7d2Ztt4hzjQ9Vb5zCeijN_RQrcI",
|
||||||
|
"eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiOElUemg3VVFaaUthTWtfME9qX1hFaHZENXpUWjE2Ti13WVdjeTJYUC1tdyIsInkiOiJPNUJiVEk0bUFpU005ZmpCejBRU3pXaU5vbnl3cWlQLUN0RGgwdnNGYXNRIiwiY3J2IjoiUC0yNTYifX0.D3DP3wqPvJv4TYYfhnfrOG6nsM-MMH_CqGfnOGjgdXHNF7xRwEJBOA.WL9Kz3gNYA7S5Rs5mKcXmA.EmQkXhO_nFqAwxJWaM0DH4s3pmCscZovB8YWJ3Ru4N8.Bf88uzwfxiyTjpejU5B0Ng",
|
||||||
|
"eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkExOTJDQkMtSFMzODQiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiMjlJMk4zRkF0UlBlNGhzYjRLWlhTbmVyV0wyTVhtSUN1LXJJaXhNSHpJQSIsInkiOiJvMjY1bzFReEdmbDhzMHQ0U1JROS00RGNpc3otbXh4NlJ6WVF4SktyeWpJIiwiY3J2IjoiUC0yNTYifX0.DRmsmXz6fCnLc_njDIKdpM7Oc4jTqd_yd9J94TOUksAstEUkAl9Ie3Wg-Ji_LzbdX2xRLXIimcw.FwJOHPQhnqKJCfxt1_qRnQ.ssx3q1ZYILsMTln5q-K8HVn93BVPI5ViusstKMxZzRs.zzcfzWNYSdNDdQ4CiHfymj0bePaAbVaT",
|
||||||
|
"eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiRUp6bTViQnRzVXJNYTl2Y1Q2d1hZRXI3ZjNMcjB0N1V4SDZuZzdGcFF0VSIsInkiOiJRYTNDSDllVTFXYjItdFdVSDN3Sk9fTDVMZXRsRUlMQWNkNE9XR2tFd0hZIiwiY3J2IjoiUC0yNTYifX0.5WxwluZpVWAOJdVrsnDIlEc4_wfRE1gXOaQyx_rKkElNz157Ykf-JsAD7aEvXfx--NKF4js5zYyjeCtxWBhRWPOoNNZJlqV_.Iuo82-qsP2S1SgQQklAnrw.H4wB6XoLKOKWCu6Y3LPAEuHkvyvr-xAh4IBm53uRF8g._fOLKq0bqDZ8KNjni_MJ4olHNaYz376dV9eNmp9O9PU",
|
||||||
|
"eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiZktNSG5sRkoxajBTSnJ3WGtVWlpaX3BtWHdUQlJtcHhlaTkxdUpaczUycyIsInkiOiJLRkxKaXhEUTJQcjEybWp1aFdYb3pna2U1V3lhWnhmTWlxZkJ0OEJpbkRvIiwiY3J2IjoiUC0yNTYifX0.2LSD2Mw4tyYJyfsmpVmzBtJRd12jMEYGdlhFbaXIbKi5A33CGNQ1tg.s40aAjmZOvK8Us86FCBdHg.jpYSMAKp___oMCoWM495mTfbi_YC80ObeoCmGE3H_gs.A6V-jJJRY1yz24CaXGUbzg",
|
||||||
|
"eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkExOTJDQkMtSFMzODQiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiSDRxcFUzeWtuRktWRnV4SmxLa3NZSE5ieHF3aXM0WWtCVVFHVE1Td05JQSIsInkiOiJHb0lpRUZaUGRRSHJCbVR4ZTA3akJoZmxrdWNqUjVoX1QwNWVXc3Zib0prIiwiY3J2IjoiUC0yNTYifX0.KTrwwV2uzD--gf3PGG-kjEAGgi7u0eMqZPZfa4kpyFGm3x8t2m1NHdz3t9rfiqjuaqsxPKhF4gs.cu16fEOzYaSxhHu_Ht9w4g.BRJdxVBI9spVtY5KQ6gTR4CNcKvmLUMKZap0AO-RF2I.DZyUaa2p6YCIaYtjWOjC9GN_VIYgySlZ",
|
||||||
|
"eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoieDBYSGRkSGM2Q0ktSnlfbUVMOEZZRExhWnV0UkVFczR4c3BMQmcwZk1jbyIsInkiOiJEa0xzOUJGTlBkTTVTNkpLYVJ3cnV1TWMwcUFzWW9yNW9fZWp6NXBNVXFrIiwiY3J2IjoiUC0yNTYifX0.mfCxJ7JYIqTMqcAh5Vp2USF0eF7OhOeluqda7YagOUJNwxA9wC9o23DSoLUylfrZUfanZrJJJcG69awlv-LY7anOLHlp3Ht5.ec48A_JWb4qa_PVHWZaTfQ.kDAjIDb3LzJpfxNh-DiAmAuaKMYaOGSTb0rkiJLuVeY.oxGCpPlii4pr89XMk4b9s084LucTqPGU6TLbOW2MZoc",
|
||||||
|
"eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiQXB5TnlqU2d0bmRUcFg0eENYenNDRnZva1l3X18weXg2dGRUYzdPUUhIMCIsInkiOiJYUHdHMDVDaW1vOGlhWmxZbDNsMEp3ZllhY1FZWHFuM2RRZEJUWFpldDZBIiwiY3J2IjoiUC0yNTYifX0.yTA2PwK9IPqkaGPenZ9R-gOn9m9rvcSEfuX_Nm8AkuwHIYLzzYeAEA.ZW1F1iyHYKfo-YoanNaIVg.PouKQD94DlPA5lbpfGJXY-EJhidC7l4vSayVN2vVzvA.MexquqtGaXKUvX7WBmD4bA",
|
||||||
|
"eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExOTJDQkMtSFMzODQiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiaDRWeGNzNVUzWk1fTlp4WmJxQ3hMTVB5UmEtR2ktSVNZa0xDTzE1RHJkZyIsInkiOiJFeVotS3dWNVE5OXlnWk5zU0lpSldpR3hqbXNLUk1WVE5sTTNSd1VYTFRvIiwiY3J2IjoiUC0yNTYifX0.wo56VISyL1QAbi2HLuVut5NGF2FvxKt7B8zHzJ3FpmavPozfbVZV08-GSYQ6jLQWJ4xsO80I4Kg.3_9Bo5ozvD96WHGhqp_tfQ.48UkJ6jk6WK70QItb2QZr0edKH7O-aMuVahTEeqyfW4.ulMlY2tbC341ct20YSmNdtc84FRz1I4g",
|
||||||
|
"eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiN0xZRzZZWTJkel9ZaGNvNnRCcG1IX0tPREQ2X2hwX05tajdEc1c2RXgxcyIsInkiOiI5Y2lPeDcwUkdGT0tpVnBRX0NHQXB5NVlyeThDazBmUkpwNHVrQ2tjNmQ0IiwiY3J2IjoiUC0yNTYifX0.bWwW3J80k46HG1fQAZxUroko2OO8OKkeRavr_o3AnhJDMvp78OR229x-fZUaBm4uWv27_Yjm0X9T2H2lhlIli2Rl9v1PNC77.1NmsJBDGI1fDjRzyc4mtyA.9KfCFynQj7LmJq08qxAG4c-6ZPz1Lh3h3nUbgVwB0TI.cqech0d8XHzWfkWqgKZq1SlAfmO0PUwOsNVkuByVGWk",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, msg := range ecSampleMessages {
|
||||||
|
obj, err := ParseEncrypted(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to parse message", msg, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
plaintext, err := obj.Decrypt(ecTestKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to decrypt message", msg, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if string(plaintext) != "Lorem ipsum dolor sit amet." {
|
||||||
|
t.Error("plaintext is not what we expected for msg", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
380
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/jwk.go
generated
vendored
Normal file
380
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/jwk.go
generated
vendored
Normal file
|
@ -0,0 +1,380 @@
|
||||||
|
/*-
|
||||||
|
* 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/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rsa"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rawJsonWebKey represents a public or private key in JWK format, used for parsing/serializing.
|
||||||
|
type rawJsonWebKey struct {
|
||||||
|
Use string `json:"use,omitempty"`
|
||||||
|
Kty string `json:"kty,omitempty"`
|
||||||
|
Kid string `json:"kid,omitempty"`
|
||||||
|
Crv string `json:"crv,omitempty"`
|
||||||
|
Alg string `json:"alg,omitempty"`
|
||||||
|
K *byteBuffer `json:"k,omitempty"`
|
||||||
|
X *byteBuffer `json:"x,omitempty"`
|
||||||
|
Y *byteBuffer `json:"y,omitempty"`
|
||||||
|
N *byteBuffer `json:"n,omitempty"`
|
||||||
|
E *byteBuffer `json:"e,omitempty"`
|
||||||
|
// -- Following fields are only used for private keys --
|
||||||
|
// RSA uses D, P and Q, while ECDSA uses only D. Fields Dp, Dq, and Qi are
|
||||||
|
// completely optional. Therefore for RSA/ECDSA, D != nil is a contract that
|
||||||
|
// we have a private key whereas D == nil means we have only a public key.
|
||||||
|
D *byteBuffer `json:"d,omitempty"`
|
||||||
|
P *byteBuffer `json:"p,omitempty"`
|
||||||
|
Q *byteBuffer `json:"q,omitempty"`
|
||||||
|
Dp *byteBuffer `json:"dp,omitempty"`
|
||||||
|
Dq *byteBuffer `json:"dq,omitempty"`
|
||||||
|
Qi *byteBuffer `json:"qi,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// JsonWebKey represents a public or private key in JWK format.
|
||||||
|
type JsonWebKey struct {
|
||||||
|
Key interface{}
|
||||||
|
KeyID string
|
||||||
|
Algorithm string
|
||||||
|
Use string
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON serializes the given key to its JSON representation.
|
||||||
|
func (k JsonWebKey) MarshalJSON() ([]byte, error) {
|
||||||
|
var raw *rawJsonWebKey
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch key := k.Key.(type) {
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
raw, err = fromEcPublicKey(key)
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
raw = fromRsaPublicKey(key)
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
raw, err = fromEcPrivateKey(key)
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
raw, err = fromRsaPrivateKey(key)
|
||||||
|
case []byte:
|
||||||
|
raw, err = fromSymmetricKey(key)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("square/go-jose: unknown key type '%s'", reflect.TypeOf(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
raw.Kid = k.KeyID
|
||||||
|
raw.Alg = k.Algorithm
|
||||||
|
raw.Use = k.Use
|
||||||
|
|
||||||
|
return MarshalJSON(raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON reads a key from its JSON representation.
|
||||||
|
func (k *JsonWebKey) UnmarshalJSON(data []byte) (err error) {
|
||||||
|
var raw rawJsonWebKey
|
||||||
|
err = UnmarshalJSON(data, &raw)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var key interface{}
|
||||||
|
switch raw.Kty {
|
||||||
|
case "EC":
|
||||||
|
if raw.D != nil {
|
||||||
|
key, err = raw.ecPrivateKey()
|
||||||
|
} else {
|
||||||
|
key, err = raw.ecPublicKey()
|
||||||
|
}
|
||||||
|
case "RSA":
|
||||||
|
if raw.D != nil {
|
||||||
|
key, err = raw.rsaPrivateKey()
|
||||||
|
} else {
|
||||||
|
key, err = raw.rsaPublicKey()
|
||||||
|
}
|
||||||
|
case "oct":
|
||||||
|
key, err = raw.symmetricKey()
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("square/go-jose: unkown json web key type '%s'", raw.Kty)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
*k = JsonWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// JsonWebKeySet represents a JWK Set object.
|
||||||
|
type JsonWebKeySet struct {
|
||||||
|
Keys []JsonWebKey `json:"keys"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key convenience method returns keys by key ID. Specification states
|
||||||
|
// that a JWK Set "SHOULD" use distinct key IDs, but allows for some
|
||||||
|
// cases where they are not distinct. Hence method returns a slice
|
||||||
|
// of JsonWebKeys.
|
||||||
|
func (s *JsonWebKeySet) Key(kid string) []JsonWebKey {
|
||||||
|
var keys []JsonWebKey
|
||||||
|
for _, key := range s.Keys {
|
||||||
|
if key.KeyID == kid {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
const rsaThumbprintTemplate = `{"e":"%s","kty":"RSA","n":"%s"}`
|
||||||
|
const ecThumbprintTemplate = `{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`
|
||||||
|
|
||||||
|
func ecThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) {
|
||||||
|
coordLength := curveSize(curve)
|
||||||
|
crv, err := curveName(curve)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(ecThumbprintTemplate, crv,
|
||||||
|
newFixedSizeBuffer(x.Bytes(), coordLength).base64(),
|
||||||
|
newFixedSizeBuffer(y.Bytes(), coordLength).base64()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func rsaThumbprintInput(n *big.Int, e int) (string, error) {
|
||||||
|
return fmt.Sprintf(rsaThumbprintTemplate,
|
||||||
|
newBufferFromInt(uint64(e)).base64(),
|
||||||
|
newBuffer(n.Bytes()).base64()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thumbprint computes the JWK Thumbprint of a key using the
|
||||||
|
// indicated hash algorithm.
|
||||||
|
func (k *JsonWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
|
||||||
|
var input string
|
||||||
|
var err error
|
||||||
|
switch key := k.Key.(type) {
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
input, err = ecThumbprintInput(key.Curve, key.X, key.Y)
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
input, err = ecThumbprintInput(key.Curve, key.X, key.Y)
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
input, err = rsaThumbprintInput(key.N, key.E)
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
input, err = rsaThumbprintInput(key.N, key.E)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("square/go-jose: unkown key type '%s'", reflect.TypeOf(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
h := hash.New()
|
||||||
|
h.Write([]byte(input))
|
||||||
|
return h.Sum(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key rawJsonWebKey) rsaPublicKey() (*rsa.PublicKey, error) {
|
||||||
|
if key.N == nil || key.E == nil {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: invalid RSA key, missing n/e values")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &rsa.PublicKey{
|
||||||
|
N: key.N.bigInt(),
|
||||||
|
E: key.E.toInt(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromRsaPublicKey(pub *rsa.PublicKey) *rawJsonWebKey {
|
||||||
|
return &rawJsonWebKey{
|
||||||
|
Kty: "RSA",
|
||||||
|
N: newBuffer(pub.N.Bytes()),
|
||||||
|
E: newBufferFromInt(uint64(pub.E)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key rawJsonWebKey) ecPublicKey() (*ecdsa.PublicKey, error) {
|
||||||
|
var curve elliptic.Curve
|
||||||
|
switch key.Crv {
|
||||||
|
case "P-256":
|
||||||
|
curve = elliptic.P256()
|
||||||
|
case "P-384":
|
||||||
|
curve = elliptic.P384()
|
||||||
|
case "P-521":
|
||||||
|
curve = elliptic.P521()
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("square/go-jose: unsupported elliptic curve '%s'", key.Crv)
|
||||||
|
}
|
||||||
|
|
||||||
|
if key.X == nil || key.Y == nil {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: invalid EC key, missing x/y values")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ecdsa.PublicKey{
|
||||||
|
Curve: curve,
|
||||||
|
X: key.X.bigInt(),
|
||||||
|
Y: key.Y.bigInt(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromEcPublicKey(pub *ecdsa.PublicKey) (*rawJsonWebKey, error) {
|
||||||
|
if pub == nil || pub.X == nil || pub.Y == nil {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: invalid EC key (nil, or X/Y missing)")
|
||||||
|
}
|
||||||
|
|
||||||
|
name, err := curveName(pub.Curve)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
size := curveSize(pub.Curve)
|
||||||
|
|
||||||
|
xBytes := pub.X.Bytes()
|
||||||
|
yBytes := pub.Y.Bytes()
|
||||||
|
|
||||||
|
if len(xBytes) > size || len(yBytes) > size {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: invalid EC key (X/Y too large)")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := &rawJsonWebKey{
|
||||||
|
Kty: "EC",
|
||||||
|
Crv: name,
|
||||||
|
X: newFixedSizeBuffer(xBytes, size),
|
||||||
|
Y: newFixedSizeBuffer(yBytes, size),
|
||||||
|
}
|
||||||
|
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key rawJsonWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) {
|
||||||
|
var missing []string
|
||||||
|
switch {
|
||||||
|
case key.N == nil:
|
||||||
|
missing = append(missing, "N")
|
||||||
|
case key.E == nil:
|
||||||
|
missing = append(missing, "E")
|
||||||
|
case key.D == nil:
|
||||||
|
missing = append(missing, "D")
|
||||||
|
case key.P == nil:
|
||||||
|
missing = append(missing, "P")
|
||||||
|
case key.Q == nil:
|
||||||
|
missing = append(missing, "Q")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(missing) > 0 {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: invalid RSA private key, missing %s value(s)", strings.Join(missing, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
rv := &rsa.PrivateKey{
|
||||||
|
PublicKey: rsa.PublicKey{
|
||||||
|
N: key.N.bigInt(),
|
||||||
|
E: key.E.toInt(),
|
||||||
|
},
|
||||||
|
D: key.D.bigInt(),
|
||||||
|
Primes: []*big.Int{
|
||||||
|
key.P.bigInt(),
|
||||||
|
key.Q.bigInt(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if key.Dp != nil {
|
||||||
|
rv.Precomputed.Dp = key.Dp.bigInt()
|
||||||
|
}
|
||||||
|
if key.Dq != nil {
|
||||||
|
rv.Precomputed.Dq = key.Dq.bigInt()
|
||||||
|
}
|
||||||
|
if key.Qi != nil {
|
||||||
|
rv.Precomputed.Qinv = key.Qi.bigInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := rv.Validate()
|
||||||
|
return rv, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromRsaPrivateKey(rsa *rsa.PrivateKey) (*rawJsonWebKey, error) {
|
||||||
|
if len(rsa.Primes) != 2 {
|
||||||
|
return nil, ErrUnsupportedKeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
raw := fromRsaPublicKey(&rsa.PublicKey)
|
||||||
|
|
||||||
|
raw.D = newBuffer(rsa.D.Bytes())
|
||||||
|
raw.P = newBuffer(rsa.Primes[0].Bytes())
|
||||||
|
raw.Q = newBuffer(rsa.Primes[1].Bytes())
|
||||||
|
|
||||||
|
return raw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key rawJsonWebKey) ecPrivateKey() (*ecdsa.PrivateKey, error) {
|
||||||
|
var curve elliptic.Curve
|
||||||
|
switch key.Crv {
|
||||||
|
case "P-256":
|
||||||
|
curve = elliptic.P256()
|
||||||
|
case "P-384":
|
||||||
|
curve = elliptic.P384()
|
||||||
|
case "P-521":
|
||||||
|
curve = elliptic.P521()
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("square/go-jose: unsupported elliptic curve '%s'", key.Crv)
|
||||||
|
}
|
||||||
|
|
||||||
|
if key.X == nil || key.Y == nil || key.D == nil {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: invalid EC private key, missing x/y/d values")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ecdsa.PrivateKey{
|
||||||
|
PublicKey: ecdsa.PublicKey{
|
||||||
|
Curve: curve,
|
||||||
|
X: key.X.bigInt(),
|
||||||
|
Y: key.Y.bigInt(),
|
||||||
|
},
|
||||||
|
D: key.D.bigInt(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromEcPrivateKey(ec *ecdsa.PrivateKey) (*rawJsonWebKey, error) {
|
||||||
|
raw, err := fromEcPublicKey(&ec.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ec.D == nil {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: invalid EC private key")
|
||||||
|
}
|
||||||
|
|
||||||
|
raw.D = newBuffer(ec.D.Bytes())
|
||||||
|
|
||||||
|
return raw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fromSymmetricKey(key []byte) (*rawJsonWebKey, error) {
|
||||||
|
return &rawJsonWebKey{
|
||||||
|
Kty: "oct",
|
||||||
|
K: newBuffer(key),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (key rawJsonWebKey) symmetricKey() ([]byte, error) {
|
||||||
|
if key.K == nil {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: invalid OCT (symmetric) key, missing k value")
|
||||||
|
}
|
||||||
|
return key.K.bytes(), nil
|
||||||
|
}
|
525
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/jwk_test.go
generated
vendored
Normal file
525
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/jwk_test.go
generated
vendored
Normal file
|
@ -0,0 +1,525 @@
|
||||||
|
/*-
|
||||||
|
* 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"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rsa"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCurveSize(t *testing.T) {
|
||||||
|
size256 := curveSize(elliptic.P256())
|
||||||
|
size384 := curveSize(elliptic.P384())
|
||||||
|
size521 := curveSize(elliptic.P521())
|
||||||
|
if size256 != 32 {
|
||||||
|
t.Error("P-256 have 32 bytes")
|
||||||
|
}
|
||||||
|
if size384 != 48 {
|
||||||
|
t.Error("P-384 have 48 bytes")
|
||||||
|
}
|
||||||
|
if size521 != 66 {
|
||||||
|
t.Error("P-521 have 66 bytes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoundtripRsaPrivate(t *testing.T) {
|
||||||
|
jwk, err := fromRsaPrivateKey(rsaTestKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem constructing JWK from rsa key", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rsa2, err := jwk.rsaPrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem converting RSA private -> JWK", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsa2.N.Cmp(rsaTestKey.N) != 0 {
|
||||||
|
t.Error("RSA private N mismatch")
|
||||||
|
}
|
||||||
|
if rsa2.E != rsaTestKey.E {
|
||||||
|
t.Error("RSA private E mismatch")
|
||||||
|
}
|
||||||
|
if rsa2.D.Cmp(rsaTestKey.D) != 0 {
|
||||||
|
t.Error("RSA private D mismatch")
|
||||||
|
}
|
||||||
|
if len(rsa2.Primes) != 2 {
|
||||||
|
t.Error("RSA private roundtrip expected two primes")
|
||||||
|
}
|
||||||
|
if rsa2.Primes[0].Cmp(rsaTestKey.Primes[0]) != 0 {
|
||||||
|
t.Error("RSA private P mismatch")
|
||||||
|
}
|
||||||
|
if rsa2.Primes[1].Cmp(rsaTestKey.Primes[1]) != 0 {
|
||||||
|
t.Error("RSA private Q mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRsaPrivateInsufficientPrimes(t *testing.T) {
|
||||||
|
brokenRsaPrivateKey := rsa.PrivateKey{
|
||||||
|
PublicKey: rsa.PublicKey{
|
||||||
|
N: rsaTestKey.N,
|
||||||
|
E: rsaTestKey.E,
|
||||||
|
},
|
||||||
|
D: rsaTestKey.D,
|
||||||
|
Primes: []*big.Int{rsaTestKey.Primes[0]},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := fromRsaPrivateKey(&brokenRsaPrivateKey)
|
||||||
|
if err != ErrUnsupportedKeyType {
|
||||||
|
t.Error("expected unsupported key type error, got", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRsaPrivateExcessPrimes(t *testing.T) {
|
||||||
|
brokenRsaPrivateKey := rsa.PrivateKey{
|
||||||
|
PublicKey: rsa.PublicKey{
|
||||||
|
N: rsaTestKey.N,
|
||||||
|
E: rsaTestKey.E,
|
||||||
|
},
|
||||||
|
D: rsaTestKey.D,
|
||||||
|
Primes: []*big.Int{
|
||||||
|
rsaTestKey.Primes[0],
|
||||||
|
rsaTestKey.Primes[1],
|
||||||
|
big.NewInt(3),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := fromRsaPrivateKey(&brokenRsaPrivateKey)
|
||||||
|
if err != ErrUnsupportedKeyType {
|
||||||
|
t.Error("expected unsupported key type error, got", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoundtripEcPublic(t *testing.T) {
|
||||||
|
for i, ecTestKey := range []*ecdsa.PrivateKey{ecTestKey256, ecTestKey384, ecTestKey521} {
|
||||||
|
jwk, err := fromEcPublicKey(&ecTestKey.PublicKey)
|
||||||
|
|
||||||
|
ec2, err := jwk.ecPublicKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem converting ECDSA private -> JWK", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(ec2.Curve, ecTestKey.Curve) {
|
||||||
|
t.Error("ECDSA private curve mismatch", i)
|
||||||
|
}
|
||||||
|
if ec2.X.Cmp(ecTestKey.X) != 0 {
|
||||||
|
t.Error("ECDSA X mismatch", i)
|
||||||
|
}
|
||||||
|
if ec2.Y.Cmp(ecTestKey.Y) != 0 {
|
||||||
|
t.Error("ECDSA Y mismatch", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoundtripEcPrivate(t *testing.T) {
|
||||||
|
for i, ecTestKey := range []*ecdsa.PrivateKey{ecTestKey256, ecTestKey384, ecTestKey521} {
|
||||||
|
jwk, err := fromEcPrivateKey(ecTestKey)
|
||||||
|
|
||||||
|
ec2, err := jwk.ecPrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem converting ECDSA private -> JWK", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(ec2.Curve, ecTestKey.Curve) {
|
||||||
|
t.Error("ECDSA private curve mismatch", i)
|
||||||
|
}
|
||||||
|
if ec2.X.Cmp(ecTestKey.X) != 0 {
|
||||||
|
t.Error("ECDSA X mismatch", i)
|
||||||
|
}
|
||||||
|
if ec2.Y.Cmp(ecTestKey.Y) != 0 {
|
||||||
|
t.Error("ECDSA Y mismatch", i)
|
||||||
|
}
|
||||||
|
if ec2.D.Cmp(ecTestKey.D) != 0 {
|
||||||
|
t.Error("ECDSA D mismatch", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalUnmarshal(t *testing.T) {
|
||||||
|
kid := "DEADBEEF"
|
||||||
|
|
||||||
|
for i, key := range []interface{}{ecTestKey256, ecTestKey384, ecTestKey521, rsaTestKey} {
|
||||||
|
for _, use := range []string{"", "sig", "enc"} {
|
||||||
|
jwk := JsonWebKey{Key: key, KeyID: kid, Algorithm: "foo"}
|
||||||
|
if use != "" {
|
||||||
|
jwk.Use = use
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonbar, err := jwk.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem marshaling", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var jwk2 JsonWebKey
|
||||||
|
err = jwk2.UnmarshalJSON(jsonbar)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem unmarshalling", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonbar2, err := jwk2.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem marshaling", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(jsonbar, jsonbar2) {
|
||||||
|
t.Error("roundtrip should not lose information", i)
|
||||||
|
}
|
||||||
|
if jwk2.KeyID != kid {
|
||||||
|
t.Error("kid did not roundtrip JSON marshalling", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if jwk2.Algorithm != "foo" {
|
||||||
|
t.Error("alg did not roundtrip JSON marshalling", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if jwk2.Use != use {
|
||||||
|
t.Error("use did not roundtrip JSON marshalling", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalNonPointer(t *testing.T) {
|
||||||
|
type EmbedsKey struct {
|
||||||
|
Key JsonWebKey
|
||||||
|
}
|
||||||
|
|
||||||
|
keyJson := []byte(`{
|
||||||
|
"e": "AQAB",
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": "vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw"
|
||||||
|
}`)
|
||||||
|
var parsedKey JsonWebKey
|
||||||
|
err := UnmarshalJSON(keyJson, &parsedKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(fmt.Sprintf("Error unmarshalling key: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ek := EmbedsKey{
|
||||||
|
Key: parsedKey,
|
||||||
|
}
|
||||||
|
out, err := MarshalJSON(ek)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(fmt.Sprintf("Error marshalling JSON: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
expected := "{\"Key\":{\"kty\":\"RSA\",\"n\":\"vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw\",\"e\":\"AQAB\"}}"
|
||||||
|
if string(out) != expected {
|
||||||
|
t.Error("Failed to marshal embedded non-pointer JWK properly:", string(out))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalUnmarshalInvalid(t *testing.T) {
|
||||||
|
// Make an invalid curve coordinate by creating a byte array that is one
|
||||||
|
// byte too large, and setting the first byte to 1 (otherwise it's just zero).
|
||||||
|
invalidCoord := make([]byte, curveSize(ecTestKey256.Curve)+1)
|
||||||
|
invalidCoord[0] = 1
|
||||||
|
|
||||||
|
keys := []interface{}{
|
||||||
|
// Empty keys
|
||||||
|
&rsa.PrivateKey{},
|
||||||
|
&ecdsa.PrivateKey{},
|
||||||
|
// Invalid keys
|
||||||
|
&ecdsa.PrivateKey{
|
||||||
|
PublicKey: ecdsa.PublicKey{
|
||||||
|
// Missing values in pub key
|
||||||
|
Curve: elliptic.P256(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&ecdsa.PrivateKey{
|
||||||
|
PublicKey: ecdsa.PublicKey{
|
||||||
|
// Invalid curve
|
||||||
|
Curve: nil,
|
||||||
|
X: ecTestKey256.X,
|
||||||
|
Y: ecTestKey256.Y,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&ecdsa.PrivateKey{
|
||||||
|
// Valid pub key, but missing priv key values
|
||||||
|
PublicKey: ecTestKey256.PublicKey,
|
||||||
|
},
|
||||||
|
&ecdsa.PrivateKey{
|
||||||
|
// Invalid pub key, values too large
|
||||||
|
PublicKey: ecdsa.PublicKey{
|
||||||
|
Curve: ecTestKey256.Curve,
|
||||||
|
X: big.NewInt(0).SetBytes(invalidCoord),
|
||||||
|
Y: big.NewInt(0).SetBytes(invalidCoord),
|
||||||
|
},
|
||||||
|
D: ecTestKey256.D,
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, key := range keys {
|
||||||
|
jwk := JsonWebKey{Key: key}
|
||||||
|
_, err := jwk.MarshalJSON()
|
||||||
|
if err == nil {
|
||||||
|
t.Error("managed to serialize invalid key", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWebKeyVectorsInvalid(t *testing.T) {
|
||||||
|
keys := []string{
|
||||||
|
// Invalid JSON
|
||||||
|
"{X",
|
||||||
|
// Empty key
|
||||||
|
"{}",
|
||||||
|
// Invalid RSA keys
|
||||||
|
`{"kty":"RSA"}`,
|
||||||
|
`{"kty":"RSA","e":""}`,
|
||||||
|
`{"kty":"RSA","e":"XXXX"}`,
|
||||||
|
`{"kty":"RSA","d":"XXXX"}`,
|
||||||
|
// Invalid EC keys
|
||||||
|
`{"kty":"EC","crv":"ABC"}`,
|
||||||
|
`{"kty":"EC","crv":"P-256"}`,
|
||||||
|
`{"kty":"EC","crv":"P-256","d":"XXX"}`,
|
||||||
|
`{"kty":"EC","crv":"ABC","d":"dGVzdA","x":"dGVzdA"}`,
|
||||||
|
`{"kty":"EC","crv":"P-256","d":"dGVzdA","x":"dGVzdA"}`,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range keys {
|
||||||
|
var jwk2 JsonWebKey
|
||||||
|
err := jwk2.UnmarshalJSON([]byte(key))
|
||||||
|
if err == nil {
|
||||||
|
t.Error("managed to parse invalid key:", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test vectors from RFC 7520
|
||||||
|
var cookbookJWKs = []string{
|
||||||
|
// EC Public
|
||||||
|
stripWhitespace(`{
|
||||||
|
"kty": "EC",
|
||||||
|
"kid": "bilbo.baggins@hobbiton.example",
|
||||||
|
"use": "sig",
|
||||||
|
"crv": "P-521",
|
||||||
|
"x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9
|
||||||
|
A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt",
|
||||||
|
"y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy
|
||||||
|
SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1"
|
||||||
|
}`),
|
||||||
|
|
||||||
|
// EC Private
|
||||||
|
stripWhitespace(`{
|
||||||
|
"kty": "EC",
|
||||||
|
"kid": "bilbo.baggins@hobbiton.example",
|
||||||
|
"use": "sig",
|
||||||
|
"crv": "P-521",
|
||||||
|
"x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9
|
||||||
|
A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt",
|
||||||
|
"y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy
|
||||||
|
SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1",
|
||||||
|
"d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zb
|
||||||
|
KipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt"
|
||||||
|
}`),
|
||||||
|
|
||||||
|
// RSA Public
|
||||||
|
stripWhitespace(`{
|
||||||
|
"kty": "RSA",
|
||||||
|
"kid": "bilbo.baggins@hobbiton.example",
|
||||||
|
"use": "sig",
|
||||||
|
"n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT
|
||||||
|
-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV
|
||||||
|
wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-
|
||||||
|
oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde
|
||||||
|
3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC
|
||||||
|
LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g
|
||||||
|
HdrNP5zw",
|
||||||
|
"e": "AQAB"
|
||||||
|
}`),
|
||||||
|
|
||||||
|
// RSA Private
|
||||||
|
stripWhitespace(`{"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"}`),
|
||||||
|
}
|
||||||
|
|
||||||
|
// SHA-256 thumbprints of the above keys, hex-encoded
|
||||||
|
var cookbookJWKThumbprints = []string{
|
||||||
|
"747ae2dd2003664aeeb21e4753fe7402846170a16bc8df8f23a8cf06d3cbe793",
|
||||||
|
"747ae2dd2003664aeeb21e4753fe7402846170a16bc8df8f23a8cf06d3cbe793",
|
||||||
|
"f63838e96077ad1fc01c3f8405774dedc0641f558ebb4b40dccf5f9b6d66a932",
|
||||||
|
"0fc478f8579325fcee0d4cbc6d9d1ce21730a6e97e435d6008fb379b0ebe47d4",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWebKeyVectorsValid(t *testing.T) {
|
||||||
|
for _, key := range cookbookJWKs {
|
||||||
|
var jwk2 JsonWebKey
|
||||||
|
err := jwk2.UnmarshalJSON([]byte(key))
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to parse valid key:", key, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestThumbprint(t *testing.T) {
|
||||||
|
for i, key := range cookbookJWKs {
|
||||||
|
var jwk2 JsonWebKey
|
||||||
|
err := jwk2.UnmarshalJSON([]byte(key))
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to parse valid key:", key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tp, err := jwk2.Thumbprint(crypto.SHA256)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to compute thumbprint:", key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tpHex := hex.EncodeToString(tp)
|
||||||
|
if cookbookJWKThumbprints[i] != tpHex {
|
||||||
|
t.Error("incorrect thumbprint:", i, cookbookJWKThumbprints[i], tpHex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalUnmarshalJWKSet(t *testing.T) {
|
||||||
|
jwk1 := JsonWebKey{Key: rsaTestKey, KeyID: "ABCDEFG", Algorithm: "foo"}
|
||||||
|
jwk2 := JsonWebKey{Key: rsaTestKey, KeyID: "GFEDCBA", Algorithm: "foo"}
|
||||||
|
var set JsonWebKeySet
|
||||||
|
set.Keys = append(set.Keys, jwk1)
|
||||||
|
set.Keys = append(set.Keys, jwk2)
|
||||||
|
|
||||||
|
jsonbar, err := MarshalJSON(&set)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem marshalling set", err)
|
||||||
|
}
|
||||||
|
var set2 JsonWebKeySet
|
||||||
|
err = UnmarshalJSON(jsonbar, &set2)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem unmarshalling set", err)
|
||||||
|
}
|
||||||
|
jsonbar2, err := MarshalJSON(&set2)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem marshalling set", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(jsonbar, jsonbar2) {
|
||||||
|
t.Error("roundtrip should not lose information")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJWKSetKey(t *testing.T) {
|
||||||
|
jwk1 := JsonWebKey{Key: rsaTestKey, KeyID: "ABCDEFG", Algorithm: "foo"}
|
||||||
|
jwk2 := JsonWebKey{Key: rsaTestKey, KeyID: "GFEDCBA", Algorithm: "foo"}
|
||||||
|
var set JsonWebKeySet
|
||||||
|
set.Keys = append(set.Keys, jwk1)
|
||||||
|
set.Keys = append(set.Keys, jwk2)
|
||||||
|
k := set.Key("ABCDEFG")
|
||||||
|
if len(k) != 1 {
|
||||||
|
t.Errorf("method should return slice with one key not %d", len(k))
|
||||||
|
}
|
||||||
|
if k[0].KeyID != "ABCDEFG" {
|
||||||
|
t.Error("method should return key with ID ABCDEFG")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJWKSymmetricKey(t *testing.T) {
|
||||||
|
sample1 := `{"kty":"oct","alg":"A128KW","k":"GawgguFyGrWKav7AX4VKUg"}`
|
||||||
|
sample2 := `{"kty":"oct","k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow","kid":"HMAC key used in JWS spec Appendix A.1 example"}`
|
||||||
|
|
||||||
|
var jwk1 JsonWebKey
|
||||||
|
UnmarshalJSON([]byte(sample1), &jwk1)
|
||||||
|
|
||||||
|
if jwk1.Algorithm != "A128KW" {
|
||||||
|
t.Errorf("expected Algorithm to be A128KW, but was '%s'", jwk1.Algorithm)
|
||||||
|
}
|
||||||
|
expected1 := fromHexBytes("19ac2082e1721ab58a6afec05f854a52")
|
||||||
|
if !bytes.Equal(jwk1.Key.([]byte), expected1) {
|
||||||
|
t.Errorf("expected Key to be '%s', but was '%s'", hex.EncodeToString(expected1), hex.EncodeToString(jwk1.Key.([]byte)))
|
||||||
|
}
|
||||||
|
|
||||||
|
var jwk2 JsonWebKey
|
||||||
|
UnmarshalJSON([]byte(sample2), &jwk2)
|
||||||
|
|
||||||
|
if jwk2.KeyID != "HMAC key used in JWS spec Appendix A.1 example" {
|
||||||
|
t.Errorf("expected KeyID to be 'HMAC key used in JWS spec Appendix A.1 example', but was '%s'", jwk2.KeyID)
|
||||||
|
}
|
||||||
|
expected2 := fromHexBytes(`
|
||||||
|
0323354b2b0fa5bc837e0665777ba68f5ab328e6f054c928a90f84b2d2502ebf
|
||||||
|
d3fb5a92d20647ef968ab4c377623d223d2e2172052e4f08c0cd9af567d080a3`)
|
||||||
|
if !bytes.Equal(jwk2.Key.([]byte), expected2) {
|
||||||
|
t.Errorf("expected Key to be '%s', but was '%s'", hex.EncodeToString(expected2), hex.EncodeToString(jwk2.Key.([]byte)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJWKSymmetricRoundtrip(t *testing.T) {
|
||||||
|
jwk1 := JsonWebKey{Key: []byte{1, 2, 3, 4}}
|
||||||
|
marshaled, err := jwk1.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to marshal valid JWK object", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var jwk2 JsonWebKey
|
||||||
|
err = jwk2.UnmarshalJSON(marshaled)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to unmarshal valid JWK object", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(jwk1.Key.([]byte), jwk2.Key.([]byte)) {
|
||||||
|
t.Error("round-trip of symmetric JWK gave different raw keys")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJWKSymmetricInvalid(t *testing.T) {
|
||||||
|
invalid := JsonWebKey{}
|
||||||
|
_, err := invalid.MarshalJSON()
|
||||||
|
if err == nil {
|
||||||
|
t.Error("excepted error on marshaling invalid symmetric JWK object")
|
||||||
|
}
|
||||||
|
|
||||||
|
var jwk JsonWebKey
|
||||||
|
err = jwk.UnmarshalJSON([]byte(`{"kty":"oct"}`))
|
||||||
|
if err == nil {
|
||||||
|
t.Error("excepted error on unmarshaling invalid symmetric JWK object")
|
||||||
|
}
|
||||||
|
}
|
252
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/jws.go
generated
vendored
Normal file
252
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/jws.go
generated
vendored
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
/*-
|
||||||
|
* 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 (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rawJsonWebSignature represents a raw JWS JSON object. Used for parsing/serializing.
|
||||||
|
type rawJsonWebSignature struct {
|
||||||
|
Payload *byteBuffer `json:"payload,omitempty"`
|
||||||
|
Signatures []rawSignatureInfo `json:"signatures,omitempty"`
|
||||||
|
Protected *byteBuffer `json:"protected,omitempty"`
|
||||||
|
Header *rawHeader `json:"header,omitempty"`
|
||||||
|
Signature *byteBuffer `json:"signature,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// rawSignatureInfo represents a single JWS signature over the JWS payload and protected header.
|
||||||
|
type rawSignatureInfo struct {
|
||||||
|
Protected *byteBuffer `json:"protected,omitempty"`
|
||||||
|
Header *rawHeader `json:"header,omitempty"`
|
||||||
|
Signature *byteBuffer `json:"signature,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// JsonWebSignature represents a signed JWS object after parsing.
|
||||||
|
type JsonWebSignature struct {
|
||||||
|
payload []byte
|
||||||
|
Signatures []Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signature represents a single signature over the JWS payload and protected header.
|
||||||
|
type Signature struct {
|
||||||
|
// Header fields, such as the signature algorithm
|
||||||
|
Header JoseHeader
|
||||||
|
|
||||||
|
// The actual signature value
|
||||||
|
Signature []byte
|
||||||
|
|
||||||
|
protected *rawHeader
|
||||||
|
header *rawHeader
|
||||||
|
original *rawSignatureInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSigned parses an encrypted message in compact or full serialization format.
|
||||||
|
func ParseSigned(input string) (*JsonWebSignature, error) {
|
||||||
|
input = stripWhitespace(input)
|
||||||
|
if strings.HasPrefix(input, "{") {
|
||||||
|
return parseSignedFull(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseSignedCompact(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a header value
|
||||||
|
func (sig Signature) mergedHeaders() rawHeader {
|
||||||
|
out := rawHeader{}
|
||||||
|
out.merge(sig.protected)
|
||||||
|
out.merge(sig.header)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute data to be signed
|
||||||
|
func (obj JsonWebSignature) computeAuthData(signature *Signature) []byte {
|
||||||
|
var serializedProtected string
|
||||||
|
|
||||||
|
if signature.original != nil && signature.original.Protected != nil {
|
||||||
|
serializedProtected = signature.original.Protected.base64()
|
||||||
|
} else if signature.protected != nil {
|
||||||
|
serializedProtected = base64URLEncode(mustSerializeJSON(signature.protected))
|
||||||
|
} else {
|
||||||
|
serializedProtected = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(fmt.Sprintf("%s.%s",
|
||||||
|
serializedProtected,
|
||||||
|
base64URLEncode(obj.payload)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseSignedFull parses a message in full format.
|
||||||
|
func parseSignedFull(input string) (*JsonWebSignature, error) {
|
||||||
|
var parsed rawJsonWebSignature
|
||||||
|
err := UnmarshalJSON([]byte(input), &parsed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsed.sanitized()
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanitized produces a cleaned-up JWS object from the raw JSON.
|
||||||
|
func (parsed *rawJsonWebSignature) sanitized() (*JsonWebSignature, error) {
|
||||||
|
if parsed.Payload == nil {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: missing payload in JWS message")
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := &JsonWebSignature{
|
||||||
|
payload: parsed.Payload.bytes(),
|
||||||
|
Signatures: make([]Signature, len(parsed.Signatures)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parsed.Signatures) == 0 {
|
||||||
|
// No signatures array, must be flattened serialization
|
||||||
|
signature := Signature{}
|
||||||
|
if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 {
|
||||||
|
signature.protected = &rawHeader{}
|
||||||
|
err := UnmarshalJSON(parsed.Protected.bytes(), signature.protected)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsed.Header != nil && parsed.Header.Nonce != "" {
|
||||||
|
return nil, ErrUnprotectedNonce
|
||||||
|
}
|
||||||
|
|
||||||
|
signature.header = parsed.Header
|
||||||
|
signature.Signature = parsed.Signature.bytes()
|
||||||
|
// Make a fake "original" rawSignatureInfo to store the unprocessed
|
||||||
|
// Protected header. This is necessary because the Protected header can
|
||||||
|
// contain arbitrary fields not registered as part of the spec. See
|
||||||
|
// https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4
|
||||||
|
// If we unmarshal Protected into a rawHeader with its explicit list of fields,
|
||||||
|
// we cannot marshal losslessly. So we have to keep around the original bytes.
|
||||||
|
// This is used in computeAuthData, which will first attempt to use
|
||||||
|
// the original bytes of a protected header, and fall back on marshaling the
|
||||||
|
// header struct only if those bytes are not available.
|
||||||
|
signature.original = &rawSignatureInfo{
|
||||||
|
Protected: parsed.Protected,
|
||||||
|
Header: parsed.Header,
|
||||||
|
Signature: parsed.Signature,
|
||||||
|
}
|
||||||
|
|
||||||
|
signature.Header = signature.mergedHeaders().sanitized()
|
||||||
|
obj.Signatures = append(obj.Signatures, signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, sig := range parsed.Signatures {
|
||||||
|
if sig.Protected != nil && len(sig.Protected.bytes()) > 0 {
|
||||||
|
obj.Signatures[i].protected = &rawHeader{}
|
||||||
|
err := UnmarshalJSON(sig.Protected.bytes(), obj.Signatures[i].protected)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that there is not a nonce in the unprotected header
|
||||||
|
if sig.Header != nil && sig.Header.Nonce != "" {
|
||||||
|
return nil, ErrUnprotectedNonce
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.Signatures[i].Signature = sig.Signature.bytes()
|
||||||
|
|
||||||
|
// Copy value of sig
|
||||||
|
original := sig
|
||||||
|
|
||||||
|
obj.Signatures[i].header = sig.Header
|
||||||
|
obj.Signatures[i].original = &original
|
||||||
|
obj.Signatures[i].Header = obj.Signatures[i].mergedHeaders().sanitized()
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseSignedCompact parses a message in compact format.
|
||||||
|
func parseSignedCompact(input string) (*JsonWebSignature, error) {
|
||||||
|
parts := strings.Split(input, ".")
|
||||||
|
if len(parts) != 3 {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: compact JWS format must have three parts")
|
||||||
|
}
|
||||||
|
|
||||||
|
rawProtected, err := base64URLDecode(parts[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := base64URLDecode(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
signature, err := base64URLDecode(parts[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
raw := &rawJsonWebSignature{
|
||||||
|
Payload: newBuffer(payload),
|
||||||
|
Protected: newBuffer(rawProtected),
|
||||||
|
Signature: newBuffer(signature),
|
||||||
|
}
|
||||||
|
return raw.sanitized()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompactSerialize serializes an object using the compact serialization format.
|
||||||
|
func (obj JsonWebSignature) CompactSerialize() (string, error) {
|
||||||
|
if len(obj.Signatures) != 1 || obj.Signatures[0].header != nil || obj.Signatures[0].protected == nil {
|
||||||
|
return "", ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedProtected := mustSerializeJSON(obj.Signatures[0].protected)
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"%s.%s.%s",
|
||||||
|
base64URLEncode(serializedProtected),
|
||||||
|
base64URLEncode(obj.payload),
|
||||||
|
base64URLEncode(obj.Signatures[0].Signature)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FullSerialize serializes an object using the full JSON serialization format.
|
||||||
|
func (obj JsonWebSignature) FullSerialize() string {
|
||||||
|
raw := rawJsonWebSignature{
|
||||||
|
Payload: newBuffer(obj.payload),
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(obj.Signatures) == 1 {
|
||||||
|
if obj.Signatures[0].protected != nil {
|
||||||
|
serializedProtected := mustSerializeJSON(obj.Signatures[0].protected)
|
||||||
|
raw.Protected = newBuffer(serializedProtected)
|
||||||
|
}
|
||||||
|
raw.Header = obj.Signatures[0].header
|
||||||
|
raw.Signature = newBuffer(obj.Signatures[0].Signature)
|
||||||
|
} else {
|
||||||
|
raw.Signatures = make([]rawSignatureInfo, len(obj.Signatures))
|
||||||
|
for i, signature := range obj.Signatures {
|
||||||
|
raw.Signatures[i] = rawSignatureInfo{
|
||||||
|
Header: signature.header,
|
||||||
|
Signature: newBuffer(signature.Signature),
|
||||||
|
}
|
||||||
|
|
||||||
|
if signature.protected != nil {
|
||||||
|
raw.Signatures[i].Protected = newBuffer(mustSerializeJSON(signature.protected))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(mustSerializeJSON(raw))
|
||||||
|
}
|
302
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/jws_test.go
generated
vendored
Normal file
302
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/jws_test.go
generated
vendored
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
/*-
|
||||||
|
* 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 (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCompactParseJWS(t *testing.T) {
|
||||||
|
// Should parse
|
||||||
|
msg := "eyJhbGciOiJYWVoifQ.cGF5bG9hZA.c2lnbmF0dXJl"
|
||||||
|
_, err := ParseSigned(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Unable to parse valid message:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages that should fail to parse
|
||||||
|
failures := []string{
|
||||||
|
// Not enough parts
|
||||||
|
"eyJhbGciOiJYWVoifQ.cGF5bG9hZA",
|
||||||
|
// Invalid signature
|
||||||
|
"eyJhbGciOiJYWVoifQ.cGF5bG9hZA.////",
|
||||||
|
// Invalid payload
|
||||||
|
"eyJhbGciOiJYWVoifQ.////.c2lnbmF0dXJl",
|
||||||
|
// Invalid header
|
||||||
|
"////.eyJhbGciOiJYWVoifQ.c2lnbmF0dXJl",
|
||||||
|
// Invalid header
|
||||||
|
"cGF5bG9hZA.cGF5bG9hZA.c2lnbmF0dXJl",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range failures {
|
||||||
|
_, err = ParseSigned(failures[i])
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Able to parse invalid message")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFullParseJWS(t *testing.T) {
|
||||||
|
// Messages that should succeed to parse
|
||||||
|
successes := []string{
|
||||||
|
"{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"e30\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"},{\"protected\":\"e30\",\"signature\":\"CUJD\"}]}",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range successes {
|
||||||
|
_, err := ParseSigned(successes[i])
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Unble to parse valid message", err, successes[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages that should fail to parse
|
||||||
|
failures := []string{
|
||||||
|
// Empty
|
||||||
|
"{}",
|
||||||
|
// Invalid JSON
|
||||||
|
"{XX",
|
||||||
|
// Invalid protected header
|
||||||
|
"{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}",
|
||||||
|
// Invalid protected header
|
||||||
|
"{\"payload\":\"CUJD\",\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}",
|
||||||
|
// Invalid protected header
|
||||||
|
"{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"###\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}",
|
||||||
|
// Invalid payload
|
||||||
|
"{\"payload\":\"###\",\"signatures\":[{\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}",
|
||||||
|
// Invalid payload
|
||||||
|
"{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"e30\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"###\"}]}",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range failures {
|
||||||
|
_, err := ParseSigned(failures[i])
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Able to parse invalid message", err, failures[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRejectUnprotectedJWSNonce(t *testing.T) {
|
||||||
|
// No need to test compact, since that's always protected
|
||||||
|
|
||||||
|
// Flattened JSON
|
||||||
|
input := `{
|
||||||
|
"header": { "nonce": "should-cause-an-error" },
|
||||||
|
"payload": "does-not-matter",
|
||||||
|
"signature": "does-not-matter"
|
||||||
|
}`
|
||||||
|
_, err := ParseSigned(input)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("JWS with an unprotected nonce parsed as valid.")
|
||||||
|
} else if err != ErrUnprotectedNonce {
|
||||||
|
t.Errorf("Improper error for unprotected nonce: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full JSON
|
||||||
|
input = `{
|
||||||
|
"payload": "does-not-matter",
|
||||||
|
"signatures": [{
|
||||||
|
"header": { "nonce": "should-cause-an-error" },
|
||||||
|
"signature": "does-not-matter"
|
||||||
|
}]
|
||||||
|
}`
|
||||||
|
_, err = ParseSigned(input)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("JWS with an unprotected nonce parsed as valid.")
|
||||||
|
} else if err != ErrUnprotectedNonce {
|
||||||
|
t.Errorf("Improper error for unprotected nonce: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyFlattenedWithIncludedUnprotectedKey(t *testing.T) {
|
||||||
|
input := `{
|
||||||
|
"header": {
|
||||||
|
"alg": "RS256",
|
||||||
|
"jwk": {
|
||||||
|
"e": "AQAB",
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": "tSwgy3ORGvc7YJI9B2qqkelZRUC6F1S5NwXFvM4w5-M0TsxbFsH5UH6adigV0jzsDJ5imAechcSoOhAh9POceCbPN1sTNwLpNbOLiQQ7RD5mY_pSUHWXNmS9R4NZ3t2fQAzPeW7jOfF0LKuJRGkekx6tXP1uSnNibgpJULNc4208dgBaCHo3mvaE2HV2GmVl1yxwWX5QZZkGQGjNDZYnjFfa2DKVvFs0QbAk21ROm594kAxlRlMMrvqlf24Eq4ERO0ptzpZgm_3j_e4hGRD39gJS7kAzK-j2cacFQ5Qi2Y6wZI2p-FCq_wiYsfEAIkATPBiLKl_6d_Jfcvs_impcXQ"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"payload": "Zm9vCg",
|
||||||
|
"signature": "hRt2eYqBd_MyMRNIh8PEIACoFtmBi7BHTLBaAhpSU6zyDAFdEBaX7us4VB9Vo1afOL03Q8iuoRA0AT4akdV_mQTAQ_jhTcVOAeXPr0tB8b8Q11UPQ0tXJYmU4spAW2SapJIvO50ntUaqU05kZd0qw8-noH1Lja-aNnU-tQII4iYVvlTiRJ5g8_CADsvJqOk6FcHuo2mG643TRnhkAxUtazvHyIHeXMxydMMSrpwUwzMtln4ZJYBNx4QGEq6OhpAD_VSp-w8Lq5HOwGQoNs0bPxH1SGrArt67LFQBfjlVr94E1sn26p4vigXm83nJdNhWAMHHE9iV67xN-r29LT-FjA"
|
||||||
|
}`
|
||||||
|
|
||||||
|
jws, err := ParseSigned(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Unable to parse valid message.")
|
||||||
|
}
|
||||||
|
if len(jws.Signatures) != 1 {
|
||||||
|
t.Error("Too many or too few signatures.")
|
||||||
|
}
|
||||||
|
sig := jws.Signatures[0]
|
||||||
|
if sig.Header.JsonWebKey == nil {
|
||||||
|
t.Error("No JWK in signature header.")
|
||||||
|
}
|
||||||
|
payload, err := jws.Verify(sig.Header.JsonWebKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(fmt.Sprintf("Signature did not validate: %v", err))
|
||||||
|
}
|
||||||
|
if string(payload) != "foo\n" {
|
||||||
|
t.Error(fmt.Sprintf("Payload was incorrect: '%s' should have been 'foo\\n'", string(payload)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVerifyFlattenedWithPrivateProtected(t *testing.T) {
|
||||||
|
// The protected field contains a Private Header Parameter name, per
|
||||||
|
// https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4
|
||||||
|
// Base64-decoded, it's '{"nonce":"8HIepUNFZUa-exKTrXVf4g"}'
|
||||||
|
input := `{"header":{"alg":"RS256","jwk":{"kty":"RSA","n":"7ixeydcbxxppzxrBphrW1atUiEZqTpiHDpI-79olav5XxAgWolHmVsJyxzoZXRxmtED8PF9-EICZWBGdSAL9ZTD0hLUCIsPcpdgT_LqNW3Sh2b2caPL2hbMF7vsXvnCGg9varpnHWuYTyRrCLUF9vM7ES-V3VCYTa7LcCSRm56Gg9r19qar43Z9kIKBBxpgt723v2cC4bmLmoAX2s217ou3uCpCXGLOeV_BesG4--Nl3pso1VhCfO85wEWjmW6lbv7Kg4d7Jdkv5DjDZfJ086fkEAYZVYGRpIgAvJBH3d3yKDCrSByUEud1bWuFjQBmMaeYOrVDXO_mbYg5PwUDMhw","e":"AQAB"}},"protected":"eyJub25jZSI6IjhISWVwVU5GWlVhLWV4S1RyWFZmNGcifQ","payload":"eyJjb250YWN0IjpbIm1haWx0bzpmb29AYmFyLmNvbSJdfQ","signature":"AyvVGMgXsQ1zTdXrZxE_gyO63pQgotL1KbI7gv6Wi8I7NRy0iAOkDAkWcTQT9pcCYApJ04lXfEDZfP5i0XgcFUm_6spxi5mFBZU-NemKcvK9dUiAbXvb4hB3GnaZtZiuVnMQUb_ku4DOaFFKbteA6gOYCnED_x7v0kAPHIYrQnvIa-KZ6pTajbV9348zgh9TL7NgGIIsTcMHd-Jatr4z1LQ0ubGa8tS300hoDhVzfoDQaEetYjCo1drR1RmdEN1SIzXdHOHfubjA3ZZRbrF_AJnNKpRRoIwzu1VayOhRmdy1qVSQZq_tENF4VrQFycEL7DhG7JLoXC4T2p1urwMlsw"}`
|
||||||
|
|
||||||
|
jws, err := ParseSigned(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Unable to parse valid message.")
|
||||||
|
}
|
||||||
|
if len(jws.Signatures) != 1 {
|
||||||
|
t.Error("Too many or too few signatures.")
|
||||||
|
}
|
||||||
|
sig := jws.Signatures[0]
|
||||||
|
if sig.Header.JsonWebKey == nil {
|
||||||
|
t.Error("No JWK in signature header.")
|
||||||
|
}
|
||||||
|
payload, err := jws.Verify(sig.Header.JsonWebKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(fmt.Sprintf("Signature did not validate: %v", err))
|
||||||
|
}
|
||||||
|
expected := "{\"contact\":[\"mailto:foo@bar.com\"]}"
|
||||||
|
if string(payload) != expected {
|
||||||
|
t.Error(fmt.Sprintf("Payload was incorrect: '%s' should have been '%s'", string(payload), expected))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test vectors generated with nimbus-jose-jwt
|
||||||
|
func TestSampleNimbusJWSMessagesRSA(t *testing.T) {
|
||||||
|
rsaPublicKey, err := LoadPublicKey(fromBase64Bytes(`
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3aLSGwbeX0ZA2Ha+EvELaIFGzO
|
||||||
|
91+Q15JQc/tdGdCgGW3XAbrh7ZUhDh1XKzbs+UOQxqn3Eq4YOx18IG0WsJSuCaHQIxnDlZ
|
||||||
|
t/GP8WLwjMC0izlJLm2SyfM/EEoNpmTC3w6MQ2dHK7SZ9Zoq+sKijQd+V7CYdr8zHMpDrd
|
||||||
|
NKoEcR0HjmvzzdMoUChhkGH5TaNbZyollULTggepaYUKS8QphqdSDMWiSetKG+g6V87lv6
|
||||||
|
CVYyK1FF6g7Esp5OOj5pNn3/bmF+7V+b7TvK91NCIlURCjE9toRgNoIP4TDnWRn/vvfZ3G
|
||||||
|
zNrtWmlizqz3r5KdvIs71ahWgMUSD4wfazrwIDAQAB`))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rsaSampleMessages := []string{
|
||||||
|
"eyJhbGciOiJSUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.YHX849fvekz6wJGeyqnQhFqyHFcUXNJKj3o2w3ddR46YLlsCopUJrlifRU_ZuTWzpYxt5oC--T2eoqMhlCvltSWrE5_1_EumqiMfAYsZULx9E6Jns7q3w7mttonYFSIh7aR3-yg2HMMfTCgoAY1y_AZ4VjXwHDcZ5gu1oZDYgvZF4uXtCmwT6e5YtR1m8abiWPF8BgoTG_BD3KV6ClLj_QQiNFdfdxAMDw7vKVOKG1T7BFtz6cDs2Q3ILS4To5E2IjcVSSYS8mi77EitCrWmrqbK_G3WCdKeUFGnMnyuKXaCDy_7FLpAZ6Z5RomRr5iskXeJZdZqIKcJV8zl4fpsPA",
|
||||||
|
"eyJhbGciOiJSUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.meyfoOTjAAjXHFYiNlU7EEnsYtbeUYeEglK6BL_cxISEr2YAGLr1Gwnn2HnucTnH6YilyRio7ZC1ohy_ZojzmaljPHqpr8kn1iqNFu9nFE2M16ZPgJi38-PGzppcDNliyzOQO-c7L-eA-v8Gfww5uyRaOJdiWg-hUJmeGBIngPIeLtSVmhJtz8oTeqeNdUOqQv7f7VRCuvagLhW1PcEM91VUS-gS0WEUXoXWZ2lp91No0v1O24izgX3__FKiX_16XhrOfAgJ82F61vjbTIQYwhexHPZyYTlXYt_scNRzFGhSKeGFin4zVdFLOXWJqKWdUd5IrDP5Nya3FSoWbWDXAg",
|
||||||
|
"eyJhbGciOiJSUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.rQPz0PDh8KyE2AX6JorgI0MLwv-qi1tcWlz6tuZuWQG1hdrlzq5tR1tQg1evYNc_SDDX87DWTSKXT7JEqhKoFixLfZa13IJrOc7FB8r5ZLx7OwOBC4F--OWrvxMA9Y3MTJjPN3FemQePUo-na2vNUZv-YgkcbuOgbO3hTxwQ7j1JGuqy-YutXOFnccdXvntp3t8zYZ4Mg1It_IyL9pzgGqHIEmMV1pCFGHsDa-wStB4ffmdhrADdYZc0q_SvxUdobyC_XzZCz9ENzGIhgwYxyyrqg7kjqUGoKmCLmoSlUFW7goTk9IC5SXdUyLPuESxOWNfHoRClGav230GYjPFQFA",
|
||||||
|
"eyJhbGciOiJQUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.UTtxjsv_6x4CdlAmZfAW6Lun3byMjJbcwRp_OlPH2W4MZaZar7aql052mIB_ddK45O9VUz2aphYVRvKPZY8WHmvlTUU30bk0z_cDJRYB9eIJVMOiRCYj0oNkz1iEZqsP0YgngxwuUDv4Q4A6aJ0Bo5E_rZo3AnrVHMHUjPp_ZRRSBFs30tQma1qQ0ApK4Gxk0XYCYAcxIv99e78vldVRaGzjEZmQeAVZx4tGcqZP20vG1L84nlhSGnOuZ0FhR8UjRFLXuob6M7EqtMRoqPgRYw47EI3fYBdeSivAg98E5S8R7R1NJc7ef-l03RvfUSY0S3_zBq_4PlHK6A-2kHb__w",
|
||||||
|
"eyJhbGciOiJSUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.meyfoOTjAAjXHFYiNlU7EEnsYtbeUYeEglK6BL_cxISEr2YAGLr1Gwnn2HnucTnH6YilyRio7ZC1ohy_ZojzmaljPHqpr8kn1iqNFu9nFE2M16ZPgJi38-PGzppcDNliyzOQO-c7L-eA-v8Gfww5uyRaOJdiWg-hUJmeGBIngPIeLtSVmhJtz8oTeqeNdUOqQv7f7VRCuvagLhW1PcEM91VUS-gS0WEUXoXWZ2lp91No0v1O24izgX3__FKiX_16XhrOfAgJ82F61vjbTIQYwhexHPZyYTlXYt_scNRzFGhSKeGFin4zVdFLOXWJqKWdUd5IrDP5Nya3FSoWbWDXAg",
|
||||||
|
"eyJhbGciOiJSUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.rQPz0PDh8KyE2AX6JorgI0MLwv-qi1tcWlz6tuZuWQG1hdrlzq5tR1tQg1evYNc_SDDX87DWTSKXT7JEqhKoFixLfZa13IJrOc7FB8r5ZLx7OwOBC4F--OWrvxMA9Y3MTJjPN3FemQePUo-na2vNUZv-YgkcbuOgbO3hTxwQ7j1JGuqy-YutXOFnccdXvntp3t8zYZ4Mg1It_IyL9pzgGqHIEmMV1pCFGHsDa-wStB4ffmdhrADdYZc0q_SvxUdobyC_XzZCz9ENzGIhgwYxyyrqg7kjqUGoKmCLmoSlUFW7goTk9IC5SXdUyLPuESxOWNfHoRClGav230GYjPFQFA",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, msg := range rsaSampleMessages {
|
||||||
|
obj, err := ParseSigned(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to parse message", msg, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
payload, err := obj.Verify(rsaPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to verify message", msg, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if string(payload) != "Lorem ipsum dolor sit amet" {
|
||||||
|
t.Error("payload is not what we expected for msg", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test vectors generated with nimbus-jose-jwt
|
||||||
|
func TestSampleNimbusJWSMessagesEC(t *testing.T) {
|
||||||
|
ecPublicKeyP256, err := LoadPublicKey(fromBase64Bytes("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIg62jq6FyL1otEj9Up7S35BUrwGF9TVrAzrrY1rHUKZqYIGEg67u/imjgadVcr7y9Q32I0gB8W8FHqbqt696rA=="))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ecPublicKeyP384, err := LoadPublicKey(fromBase64Bytes("MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEPXsVlqCtN2oTY+F+hFZm3M0ldYpb7IeeJM5wYmT0k1RaqzBFDhDMNnYK5Q5x+OyssZrAtHgYDFw02AVJhhng/eHRp7mqmL/vI3wbxJtrLKYldIbBA+9fYBQcKeibjlu5"))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ecPublicKeyP521, err := LoadPublicKey(fromBase64Bytes("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAa2w3MMJ5FWD6tSf68G+Wy5jIhWXOD3IA7pE5IC/myQzo1lWcD8KS57SM6nm4POtPcxyLmDhL7FLuh8DKoIZyvtAAdK8+tOQP7XXRlT2bkvzIuazp05It3TAPu00YzTIpKfDlc19Y1lvf7etrbFqhShD92B+hHmhT4ddrdbPCBDW8hvU="))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ecPublicKeys := []interface{}{ecPublicKeyP256, ecPublicKeyP384, ecPublicKeyP521}
|
||||||
|
|
||||||
|
ecSampleMessages := []string{
|
||||||
|
"eyJhbGciOiJFUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.MEWJVlvGRQyzMEGOYm4rwuiwxrX-6LjnlbaRDAuhwmnBm2Gtn7pRpGXRTMFZUXsSGDz2L1p-Hz1qn8j9bFIBtQ",
|
||||||
|
"eyJhbGciOiJFUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.nbdjPnJPYQtVNNdBIx8-KbFKplTxrz-hnW5UNhYUY7SBkwHK4NZnqc2Lv4DXoA0aWHq9eiypgOh1kmyPWGEmqKAHUx0xdIEkBoHk3ZsbmhOQuq2jL_wcMUG6nTWNhLrB",
|
||||||
|
"eyJhbGciOiJFUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.AeYNFC1rwIgQv-5fwd8iRyYzvTaSCYTEICepgu9gRId-IW99kbSVY7yH0MvrQnqI-a0L8zwKWDR35fW5dukPAYRkADp3Y1lzqdShFcEFziUVGo46vqbiSajmKFrjBktJcCsfjKSaLHwxErF-T10YYPCQFHWb2nXJOOI3CZfACYqgO84g",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, msg := range ecSampleMessages {
|
||||||
|
obj, err := ParseSigned(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to parse message", msg, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
payload, err := obj.Verify(ecPublicKeys[i])
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to verify message", msg, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if string(payload) != "Lorem ipsum dolor sit amet" {
|
||||||
|
t.Error("payload is not what we expected for msg", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test vectors generated with nimbus-jose-jwt
|
||||||
|
func TestSampleNimbusJWSMessagesHMAC(t *testing.T) {
|
||||||
|
hmacTestKey := fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D")
|
||||||
|
|
||||||
|
hmacSampleMessages := []string{
|
||||||
|
"eyJhbGciOiJIUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.W5tc_EUhxexcvLYEEOckyyvdb__M5DQIVpg6Nmk1XGM",
|
||||||
|
"eyJhbGciOiJIUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.sBu44lXOJa4Nd10oqOdYH2uz3lxlZ6o32QSGHaoGdPtYTDG5zvSja6N48CXKqdAh",
|
||||||
|
"eyJhbGciOiJIUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.M0yR4tmipsORIix-BitIbxEPGaxPchDfj8UNOpKuhDEfnb7URjGvCKn4nOlyQ1z9mG1FKbwnqR1hOVAWSzAU_w",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, msg := range hmacSampleMessages {
|
||||||
|
obj, err := ParseSigned(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to parse message", msg, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
payload, err := obj.Verify(hmacTestKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to verify message", msg, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if string(payload) != "Lorem ipsum dolor sit amet" {
|
||||||
|
t.Error("payload is not what we expected for msg", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test vectors generated with nimbus-jose-jwt
|
||||||
|
func TestErrorMissingPayloadJWS(t *testing.T) {
|
||||||
|
_, err := (&rawJsonWebSignature{}).sanitized()
|
||||||
|
if err == nil {
|
||||||
|
t.Error("was able to parse message with missing payload")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "missing payload") {
|
||||||
|
t.Errorf("unexpected error message, should contain 'missing payload': %s", err)
|
||||||
|
}
|
||||||
|
}
|
224
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/shared.go
generated
vendored
Normal file
224
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/shared.go
generated
vendored
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
/*-
|
||||||
|
* 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/elliptic"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyAlgorithm represents a key management algorithm.
|
||||||
|
type KeyAlgorithm string
|
||||||
|
|
||||||
|
// SignatureAlgorithm represents a signature (or MAC) algorithm.
|
||||||
|
type SignatureAlgorithm string
|
||||||
|
|
||||||
|
// ContentEncryption represents a content encryption algorithm.
|
||||||
|
type ContentEncryption string
|
||||||
|
|
||||||
|
// CompressionAlgorithm represents an algorithm used for plaintext compression.
|
||||||
|
type CompressionAlgorithm string
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrCryptoFailure represents an error in cryptographic primitive. This
|
||||||
|
// occurs when, for example, a message had an invalid authentication tag or
|
||||||
|
// could not be decrypted.
|
||||||
|
ErrCryptoFailure = errors.New("square/go-jose: error in cryptographic primitive")
|
||||||
|
|
||||||
|
// ErrUnsupportedAlgorithm indicates that a selected algorithm is not
|
||||||
|
// supported. This occurs when trying to instantiate an encrypter for an
|
||||||
|
// algorithm that is not yet implemented.
|
||||||
|
ErrUnsupportedAlgorithm = errors.New("square/go-jose: unknown/unsupported algorithm")
|
||||||
|
|
||||||
|
// ErrUnsupportedKeyType indicates that the given key type/format is not
|
||||||
|
// supported. This occurs when trying to instantiate an encrypter and passing
|
||||||
|
// it a key of an unrecognized type or with unsupported parameters, such as
|
||||||
|
// an RSA private key with more than two primes.
|
||||||
|
ErrUnsupportedKeyType = errors.New("square/go-jose: unsupported key type/format")
|
||||||
|
|
||||||
|
// ErrNotSupported serialization of object is not supported. This occurs when
|
||||||
|
// trying to compact-serialize an object which can't be represented in
|
||||||
|
// compact form.
|
||||||
|
ErrNotSupported = errors.New("square/go-jose: compact serialization not supported for object")
|
||||||
|
|
||||||
|
// ErrUnprotectedNonce indicates that while parsing a JWS or JWE object, a
|
||||||
|
// nonce header parameter was included in an unprotected header object.
|
||||||
|
ErrUnprotectedNonce = errors.New("square/go-jose: Nonce parameter included in unprotected header")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Key management algorithms
|
||||||
|
const (
|
||||||
|
RSA1_5 = KeyAlgorithm("RSA1_5") // RSA-PKCS1v1.5
|
||||||
|
RSA_OAEP = KeyAlgorithm("RSA-OAEP") // RSA-OAEP-SHA1
|
||||||
|
RSA_OAEP_256 = KeyAlgorithm("RSA-OAEP-256") // RSA-OAEP-SHA256
|
||||||
|
A128KW = KeyAlgorithm("A128KW") // AES key wrap (128)
|
||||||
|
A192KW = KeyAlgorithm("A192KW") // AES key wrap (192)
|
||||||
|
A256KW = KeyAlgorithm("A256KW") // AES key wrap (256)
|
||||||
|
DIRECT = KeyAlgorithm("dir") // Direct encryption
|
||||||
|
ECDH_ES = KeyAlgorithm("ECDH-ES") // ECDH-ES
|
||||||
|
ECDH_ES_A128KW = KeyAlgorithm("ECDH-ES+A128KW") // ECDH-ES + AES key wrap (128)
|
||||||
|
ECDH_ES_A192KW = KeyAlgorithm("ECDH-ES+A192KW") // ECDH-ES + AES key wrap (192)
|
||||||
|
ECDH_ES_A256KW = KeyAlgorithm("ECDH-ES+A256KW") // ECDH-ES + AES key wrap (256)
|
||||||
|
A128GCMKW = KeyAlgorithm("A128GCMKW") // AES-GCM key wrap (128)
|
||||||
|
A192GCMKW = KeyAlgorithm("A192GCMKW") // AES-GCM key wrap (192)
|
||||||
|
A256GCMKW = KeyAlgorithm("A256GCMKW") // AES-GCM key wrap (256)
|
||||||
|
PBES2_HS256_A128KW = KeyAlgorithm("PBES2-HS256+A128KW") // PBES2 + HMAC-SHA256 + AES key wrap (128)
|
||||||
|
PBES2_HS384_A192KW = KeyAlgorithm("PBES2-HS384+A192KW") // PBES2 + HMAC-SHA384 + AES key wrap (192)
|
||||||
|
PBES2_HS512_A256KW = KeyAlgorithm("PBES2-HS512+A256KW") // PBES2 + HMAC-SHA512 + AES key wrap (256)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Signature algorithms
|
||||||
|
const (
|
||||||
|
HS256 = SignatureAlgorithm("HS256") // HMAC using SHA-256
|
||||||
|
HS384 = SignatureAlgorithm("HS384") // HMAC using SHA-384
|
||||||
|
HS512 = SignatureAlgorithm("HS512") // HMAC using SHA-512
|
||||||
|
RS256 = SignatureAlgorithm("RS256") // RSASSA-PKCS-v1.5 using SHA-256
|
||||||
|
RS384 = SignatureAlgorithm("RS384") // RSASSA-PKCS-v1.5 using SHA-384
|
||||||
|
RS512 = SignatureAlgorithm("RS512") // RSASSA-PKCS-v1.5 using SHA-512
|
||||||
|
ES256 = SignatureAlgorithm("ES256") // ECDSA using P-256 and SHA-256
|
||||||
|
ES384 = SignatureAlgorithm("ES384") // ECDSA using P-384 and SHA-384
|
||||||
|
ES512 = SignatureAlgorithm("ES512") // ECDSA using P-521 and SHA-512
|
||||||
|
PS256 = SignatureAlgorithm("PS256") // RSASSA-PSS using SHA256 and MGF1-SHA256
|
||||||
|
PS384 = SignatureAlgorithm("PS384") // RSASSA-PSS using SHA384 and MGF1-SHA384
|
||||||
|
PS512 = SignatureAlgorithm("PS512") // RSASSA-PSS using SHA512 and MGF1-SHA512
|
||||||
|
)
|
||||||
|
|
||||||
|
// Content encryption algorithms
|
||||||
|
const (
|
||||||
|
A128CBC_HS256 = ContentEncryption("A128CBC-HS256") // AES-CBC + HMAC-SHA256 (128)
|
||||||
|
A192CBC_HS384 = ContentEncryption("A192CBC-HS384") // AES-CBC + HMAC-SHA384 (192)
|
||||||
|
A256CBC_HS512 = ContentEncryption("A256CBC-HS512") // AES-CBC + HMAC-SHA512 (256)
|
||||||
|
A128GCM = ContentEncryption("A128GCM") // AES-GCM (128)
|
||||||
|
A192GCM = ContentEncryption("A192GCM") // AES-GCM (192)
|
||||||
|
A256GCM = ContentEncryption("A256GCM") // AES-GCM (256)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compression algorithms
|
||||||
|
const (
|
||||||
|
NONE = CompressionAlgorithm("") // No compression
|
||||||
|
DEFLATE = CompressionAlgorithm("DEF") // DEFLATE (RFC 1951)
|
||||||
|
)
|
||||||
|
|
||||||
|
// rawHeader represents the JOSE header for JWE/JWS objects (used for parsing).
|
||||||
|
type rawHeader struct {
|
||||||
|
Alg string `json:"alg,omitempty"`
|
||||||
|
Enc ContentEncryption `json:"enc,omitempty"`
|
||||||
|
Zip CompressionAlgorithm `json:"zip,omitempty"`
|
||||||
|
Crit []string `json:"crit,omitempty"`
|
||||||
|
Apu *byteBuffer `json:"apu,omitempty"`
|
||||||
|
Apv *byteBuffer `json:"apv,omitempty"`
|
||||||
|
Epk *JsonWebKey `json:"epk,omitempty"`
|
||||||
|
Iv *byteBuffer `json:"iv,omitempty"`
|
||||||
|
Tag *byteBuffer `json:"tag,omitempty"`
|
||||||
|
Jwk *JsonWebKey `json:"jwk,omitempty"`
|
||||||
|
Kid string `json:"kid,omitempty"`
|
||||||
|
Nonce string `json:"nonce,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoseHeader represents the read-only JOSE header for JWE/JWS objects.
|
||||||
|
type JoseHeader struct {
|
||||||
|
KeyID string
|
||||||
|
JsonWebKey *JsonWebKey
|
||||||
|
Algorithm string
|
||||||
|
Nonce string
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanitized produces a cleaned-up header object from the raw JSON.
|
||||||
|
func (parsed rawHeader) sanitized() JoseHeader {
|
||||||
|
return JoseHeader{
|
||||||
|
KeyID: parsed.Kid,
|
||||||
|
JsonWebKey: parsed.Jwk,
|
||||||
|
Algorithm: parsed.Alg,
|
||||||
|
Nonce: parsed.Nonce,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge headers from src into dst, giving precedence to headers from l.
|
||||||
|
func (dst *rawHeader) merge(src *rawHeader) {
|
||||||
|
if src == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if dst.Alg == "" {
|
||||||
|
dst.Alg = src.Alg
|
||||||
|
}
|
||||||
|
if dst.Enc == "" {
|
||||||
|
dst.Enc = src.Enc
|
||||||
|
}
|
||||||
|
if dst.Zip == "" {
|
||||||
|
dst.Zip = src.Zip
|
||||||
|
}
|
||||||
|
if dst.Crit == nil {
|
||||||
|
dst.Crit = src.Crit
|
||||||
|
}
|
||||||
|
if dst.Crit == nil {
|
||||||
|
dst.Crit = src.Crit
|
||||||
|
}
|
||||||
|
if dst.Apu == nil {
|
||||||
|
dst.Apu = src.Apu
|
||||||
|
}
|
||||||
|
if dst.Apv == nil {
|
||||||
|
dst.Apv = src.Apv
|
||||||
|
}
|
||||||
|
if dst.Epk == nil {
|
||||||
|
dst.Epk = src.Epk
|
||||||
|
}
|
||||||
|
if dst.Iv == nil {
|
||||||
|
dst.Iv = src.Iv
|
||||||
|
}
|
||||||
|
if dst.Tag == nil {
|
||||||
|
dst.Tag = src.Tag
|
||||||
|
}
|
||||||
|
if dst.Kid == "" {
|
||||||
|
dst.Kid = src.Kid
|
||||||
|
}
|
||||||
|
if dst.Jwk == nil {
|
||||||
|
dst.Jwk = src.Jwk
|
||||||
|
}
|
||||||
|
if dst.Nonce == "" {
|
||||||
|
dst.Nonce = src.Nonce
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get JOSE name of curve
|
||||||
|
func curveName(crv elliptic.Curve) (string, error) {
|
||||||
|
switch crv {
|
||||||
|
case elliptic.P256():
|
||||||
|
return "P-256", nil
|
||||||
|
case elliptic.P384():
|
||||||
|
return "P-384", nil
|
||||||
|
case elliptic.P521():
|
||||||
|
return "P-521", nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("square/go-jose: unsupported/unknown elliptic curve")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get size of curve in bytes
|
||||||
|
func curveSize(crv elliptic.Curve) int {
|
||||||
|
bits := crv.Params().BitSize
|
||||||
|
|
||||||
|
div := bits / 8
|
||||||
|
mod := bits % 8
|
||||||
|
|
||||||
|
if mod == 0 {
|
||||||
|
return div
|
||||||
|
}
|
||||||
|
|
||||||
|
return div + 1
|
||||||
|
}
|
218
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/signing.go
generated
vendored
Normal file
218
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/signing.go
generated
vendored
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
/*-
|
||||||
|
* 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"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NonceSource represents a source of random nonces to go into JWS objects
|
||||||
|
type NonceSource interface {
|
||||||
|
Nonce() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signer represents a signer which takes a payload and produces a signed JWS object.
|
||||||
|
type Signer interface {
|
||||||
|
Sign(payload []byte) (*JsonWebSignature, error)
|
||||||
|
SetNonceSource(source NonceSource)
|
||||||
|
SetEmbedJwk(embed bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiSigner represents a signer which supports multiple recipients.
|
||||||
|
type MultiSigner interface {
|
||||||
|
Sign(payload []byte) (*JsonWebSignature, error)
|
||||||
|
SetNonceSource(source NonceSource)
|
||||||
|
SetEmbedJwk(embed bool)
|
||||||
|
AddRecipient(alg SignatureAlgorithm, signingKey interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type payloadSigner interface {
|
||||||
|
signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type payloadVerifier interface {
|
||||||
|
verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type genericSigner struct {
|
||||||
|
recipients []recipientSigInfo
|
||||||
|
nonceSource NonceSource
|
||||||
|
embedJwk bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type recipientSigInfo struct {
|
||||||
|
sigAlg SignatureAlgorithm
|
||||||
|
keyID string
|
||||||
|
publicKey *JsonWebKey
|
||||||
|
signer payloadSigner
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSigner creates an appropriate signer based on the key type
|
||||||
|
func NewSigner(alg SignatureAlgorithm, signingKey interface{}) (Signer, error) {
|
||||||
|
// NewMultiSigner never fails (currently)
|
||||||
|
signer := NewMultiSigner()
|
||||||
|
|
||||||
|
err := signer.AddRecipient(alg, signingKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMultiSigner creates a signer for multiple recipients
|
||||||
|
func NewMultiSigner() MultiSigner {
|
||||||
|
return &genericSigner{
|
||||||
|
recipients: []recipientSigInfo{},
|
||||||
|
embedJwk: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newVerifier creates a verifier based on the key type
|
||||||
|
func newVerifier(verificationKey interface{}) (payloadVerifier, error) {
|
||||||
|
switch verificationKey := verificationKey.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
return &rsaEncrypterVerifier{
|
||||||
|
publicKey: verificationKey,
|
||||||
|
}, nil
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
return &ecEncrypterVerifier{
|
||||||
|
publicKey: verificationKey,
|
||||||
|
}, nil
|
||||||
|
case []byte:
|
||||||
|
return &symmetricMac{
|
||||||
|
key: verificationKey,
|
||||||
|
}, nil
|
||||||
|
case *JsonWebKey:
|
||||||
|
return newVerifier(verificationKey.Key)
|
||||||
|
default:
|
||||||
|
return nil, ErrUnsupportedKeyType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *genericSigner) AddRecipient(alg SignatureAlgorithm, signingKey interface{}) error {
|
||||||
|
recipient, err := makeJWSRecipient(alg, signingKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.recipients = append(ctx.recipients, recipient)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeJWSRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipientSigInfo, error) {
|
||||||
|
switch signingKey := signingKey.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
return newRSASigner(alg, signingKey)
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
return newECDSASigner(alg, signingKey)
|
||||||
|
case []byte:
|
||||||
|
return newSymmetricSigner(alg, signingKey)
|
||||||
|
case *JsonWebKey:
|
||||||
|
recipient, err := makeJWSRecipient(alg, signingKey.Key)
|
||||||
|
if err != nil {
|
||||||
|
return recipientSigInfo{}, err
|
||||||
|
}
|
||||||
|
recipient.keyID = signingKey.KeyID
|
||||||
|
return recipient, nil
|
||||||
|
default:
|
||||||
|
return recipientSigInfo{}, ErrUnsupportedKeyType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *genericSigner) Sign(payload []byte) (*JsonWebSignature, error) {
|
||||||
|
obj := &JsonWebSignature{}
|
||||||
|
obj.payload = payload
|
||||||
|
obj.Signatures = make([]Signature, len(ctx.recipients))
|
||||||
|
|
||||||
|
for i, recipient := range ctx.recipients {
|
||||||
|
protected := &rawHeader{
|
||||||
|
Alg: string(recipient.sigAlg),
|
||||||
|
}
|
||||||
|
|
||||||
|
if recipient.publicKey != nil && ctx.embedJwk {
|
||||||
|
protected.Jwk = recipient.publicKey
|
||||||
|
}
|
||||||
|
if recipient.keyID != "" {
|
||||||
|
protected.Kid = recipient.keyID
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.nonceSource != nil {
|
||||||
|
nonce, err := ctx.nonceSource.Nonce()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: Error generating nonce: %v", err)
|
||||||
|
}
|
||||||
|
protected.Nonce = nonce
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedProtected := mustSerializeJSON(protected)
|
||||||
|
|
||||||
|
input := []byte(fmt.Sprintf("%s.%s",
|
||||||
|
base64URLEncode(serializedProtected),
|
||||||
|
base64URLEncode(payload)))
|
||||||
|
|
||||||
|
signatureInfo, err := recipient.signer.signPayload(input, recipient.sigAlg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
signatureInfo.protected = protected
|
||||||
|
obj.Signatures[i] = signatureInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNonceSource provides or updates a nonce pool to the first recipients.
|
||||||
|
// After this method is called, the signer will consume one nonce per
|
||||||
|
// signature, returning an error it is unable to get a nonce.
|
||||||
|
func (ctx *genericSigner) SetNonceSource(source NonceSource) {
|
||||||
|
ctx.nonceSource = source
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEmbedJwk specifies if the signing key should be embedded in the protected header,
|
||||||
|
// if any. It defaults to 'true'.
|
||||||
|
func (ctx *genericSigner) SetEmbedJwk(embed bool) {
|
||||||
|
ctx.embedJwk = embed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify validates the signature on the object and returns the payload.
|
||||||
|
func (obj JsonWebSignature) Verify(verificationKey interface{}) ([]byte, error) {
|
||||||
|
verifier, err := newVerifier(verificationKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, signature := range obj.Signatures {
|
||||||
|
headers := signature.mergedHeaders()
|
||||||
|
if len(headers.Crit) > 0 {
|
||||||
|
// Unsupported crit header
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
input := obj.computeAuthData(&signature)
|
||||||
|
alg := SignatureAlgorithm(headers.Alg)
|
||||||
|
err := verifier.verifyPayload(input, signature.Signature, alg)
|
||||||
|
if err == nil {
|
||||||
|
return obj.payload, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrCryptoFailure
|
||||||
|
}
|
447
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/signing_test.go
generated
vendored
Normal file
447
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/signing_test.go
generated
vendored
Normal file
|
@ -0,0 +1,447 @@
|
||||||
|
/*-
|
||||||
|
* 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/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type staticNonceSource string
|
||||||
|
|
||||||
|
func (sns staticNonceSource) Nonce() (string, error) {
|
||||||
|
return string(sns), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RoundtripJWS(sigAlg SignatureAlgorithm, serializer func(*JsonWebSignature) (string, error), corrupter func(*JsonWebSignature), signingKey interface{}, verificationKey interface{}, nonce string) error {
|
||||||
|
signer, err := NewSigner(sigAlg, signingKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error on new signer: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if nonce != "" {
|
||||||
|
signer.SetNonceSource(staticNonceSource(nonce))
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = ParseSigned(msg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error on parse: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// (Maybe) mangle the object
|
||||||
|
corrupter(obj)
|
||||||
|
|
||||||
|
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.Compare(output, input) != 0 {
|
||||||
|
return fmt.Errorf("input/output do not match, got '%s', expected '%s'", output, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoundtripsJWS(t *testing.T) {
|
||||||
|
// Test matrix
|
||||||
|
sigAlgs := []SignatureAlgorithm{RS256, RS384, RS512, PS256, PS384, PS512, HS256, HS384, HS512, ES256, ES384, ES512}
|
||||||
|
|
||||||
|
serializers := []func(*JsonWebSignature) (string, error){
|
||||||
|
func(obj *JsonWebSignature) (string, error) { return obj.CompactSerialize() },
|
||||||
|
func(obj *JsonWebSignature) (string, error) { return obj.FullSerialize(), nil },
|
||||||
|
}
|
||||||
|
|
||||||
|
corrupter := func(obj *JsonWebSignature) {}
|
||||||
|
|
||||||
|
for _, alg := range sigAlgs {
|
||||||
|
signingKey, verificationKey := GenerateSigningTestKey(alg)
|
||||||
|
|
||||||
|
for i, serializer := range serializers {
|
||||||
|
err := RoundtripJWS(alg, serializer, corrupter, signingKey, verificationKey, "test_nonce")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err, alg, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoundtripsJWSCorruptSignature(t *testing.T) {
|
||||||
|
// Test matrix
|
||||||
|
sigAlgs := []SignatureAlgorithm{RS256, RS384, RS512, PS256, PS384, PS512, HS256, HS384, HS512, ES256, ES384, ES512}
|
||||||
|
|
||||||
|
serializers := []func(*JsonWebSignature) (string, error){
|
||||||
|
func(obj *JsonWebSignature) (string, error) { return obj.CompactSerialize() },
|
||||||
|
func(obj *JsonWebSignature) (string, error) { return obj.FullSerialize(), nil },
|
||||||
|
}
|
||||||
|
|
||||||
|
corrupters := []func(*JsonWebSignature){
|
||||||
|
func(obj *JsonWebSignature) {
|
||||||
|
// Changes bytes in signature
|
||||||
|
obj.Signatures[0].Signature[10]++
|
||||||
|
},
|
||||||
|
func(obj *JsonWebSignature) {
|
||||||
|
// Set totally invalid signature
|
||||||
|
obj.Signatures[0].Signature = []byte("###")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test all different configurations
|
||||||
|
for _, alg := range sigAlgs {
|
||||||
|
signingKey, verificationKey := GenerateSigningTestKey(alg)
|
||||||
|
|
||||||
|
for i, serializer := range serializers {
|
||||||
|
for j, corrupter := range corrupters {
|
||||||
|
err := RoundtripJWS(alg, serializer, corrupter, signingKey, verificationKey, "test_nonce")
|
||||||
|
if err == nil {
|
||||||
|
t.Error("failed to detect corrupt signature", err, alg, i, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignerWithBrokenRand(t *testing.T) {
|
||||||
|
sigAlgs := []SignatureAlgorithm{RS256, RS384, RS512, PS256, PS384, PS512}
|
||||||
|
|
||||||
|
serializer := func(obj *JsonWebSignature) (string, error) { return obj.CompactSerialize() }
|
||||||
|
corrupter := func(obj *JsonWebSignature) {}
|
||||||
|
|
||||||
|
// 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 sigAlgs {
|
||||||
|
signingKey, verificationKey := GenerateSigningTestKey(alg)
|
||||||
|
for i, getReader := range readers {
|
||||||
|
randReader = getReader()
|
||||||
|
err := RoundtripJWS(alg, serializer, corrupter, signingKey, verificationKey, "test_nonce")
|
||||||
|
if err == nil {
|
||||||
|
t.Error("signer should fail if rand is broken", alg, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJWSInvalidKey(t *testing.T) {
|
||||||
|
signingKey0, verificationKey0 := GenerateSigningTestKey(RS256)
|
||||||
|
_, verificationKey1 := GenerateSigningTestKey(ES256)
|
||||||
|
|
||||||
|
signer, err := NewSigner(RS256, signingKey0)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
input := []byte("Lorem ipsum dolor sit amet")
|
||||||
|
obj, err := signer.Sign(input)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must work with correct key
|
||||||
|
_, err = obj.Verify(verificationKey0)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error on verify", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must not work with incorrect key
|
||||||
|
_, err = obj.Verify(verificationKey1)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("verification should fail with incorrect key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must not work with invalid key
|
||||||
|
_, err = obj.Verify("")
|
||||||
|
if err == nil {
|
||||||
|
t.Error("verification should fail with incorrect key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiRecipientJWS(t *testing.T) {
|
||||||
|
signer := NewMultiSigner()
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
signer.AddRecipient(RS256, rsaTestKey)
|
||||||
|
signer.AddRecipient(HS384, sharedKey)
|
||||||
|
|
||||||
|
input := []byte("Lorem ipsum dolor sit amet")
|
||||||
|
obj, err := signer.Sign(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error on sign: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = obj.CompactSerialize()
|
||||||
|
if err == nil {
|
||||||
|
t.Error("message with multiple recipient was compact serialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := obj.FullSerialize()
|
||||||
|
|
||||||
|
obj, err = ParseSigned(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error on parse: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := obj.Verify(&rsaTestKey.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error on verify: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(output, input) != 0 {
|
||||||
|
t.Error("input/output do not match", output, input)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err = obj.Verify(sharedKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("error on verify: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(output, input) != 0 {
|
||||||
|
t.Error("input/output do not match", output, input)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateSigningTestKey(sigAlg SignatureAlgorithm) (sig, ver interface{}) {
|
||||||
|
switch sigAlg {
|
||||||
|
case RS256, RS384, RS512, PS256, PS384, PS512:
|
||||||
|
sig = rsaTestKey
|
||||||
|
ver = &rsaTestKey.PublicKey
|
||||||
|
case HS256, HS384, HS512:
|
||||||
|
sig, _, _ = randomKeyGenerator{size: 16}.genKey()
|
||||||
|
ver = sig
|
||||||
|
case ES256:
|
||||||
|
key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
sig = key
|
||||||
|
ver = &key.PublicKey
|
||||||
|
case ES384:
|
||||||
|
key, _ := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||||
|
sig = key
|
||||||
|
ver = &key.PublicKey
|
||||||
|
case ES512:
|
||||||
|
key, _ := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||||
|
sig = key
|
||||||
|
ver = &key.PublicKey
|
||||||
|
default:
|
||||||
|
panic("Must update test case")
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidSignerAlg(t *testing.T) {
|
||||||
|
_, err := NewSigner("XYZ", nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should not accept invalid algorithm")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = NewSigner("XYZ", []byte{})
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should not accept invalid algorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidJWS(t *testing.T) {
|
||||||
|
signer, err := NewSigner(PS256, rsaTestKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj, err := signer.Sign([]byte("Lorem ipsum dolor sit amet"))
|
||||||
|
obj.Signatures[0].header = &rawHeader{
|
||||||
|
Crit: []string{"TEST"},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = obj.Verify(&rsaTestKey.PublicKey)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should not verify message with unknown crit header")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try without alg header
|
||||||
|
obj.Signatures[0].protected = &rawHeader{}
|
||||||
|
obj.Signatures[0].header = &rawHeader{}
|
||||||
|
|
||||||
|
_, err = obj.Verify(&rsaTestKey.PublicKey)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should not verify message with missing headers")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignerKid(t *testing.T) {
|
||||||
|
kid := "DEADBEEF"
|
||||||
|
payload := []byte("Lorem ipsum dolor sit amet")
|
||||||
|
|
||||||
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem generating test signing key", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
basejwk := JsonWebKey{Key: key}
|
||||||
|
jsonbar, err := basejwk.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem marshalling base JWK", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonmsi map[string]interface{}
|
||||||
|
err = UnmarshalJSON(jsonbar, &jsonmsi)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem unmarshalling base JWK", err)
|
||||||
|
}
|
||||||
|
jsonmsi["kid"] = kid
|
||||||
|
jsonbar2, err := MarshalJSON(jsonmsi)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem marshalling kided JWK", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var jwk JsonWebKey
|
||||||
|
err = jwk.UnmarshalJSON(jsonbar2)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem unmarshalling kided JWK", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
signer, err := NewSigner(ES256, &jwk)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem creating signer", err)
|
||||||
|
}
|
||||||
|
signed, err := signer.Sign(payload)
|
||||||
|
|
||||||
|
serialized := signed.FullSerialize()
|
||||||
|
|
||||||
|
parsed, err := ParseSigned(serialized)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("problem parsing signed object", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsed.Signatures[0].Header.KeyID != kid {
|
||||||
|
t.Error("KeyID did not survive trip")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmbedJwk(t *testing.T) {
|
||||||
|
var payload = []byte("Lorem ipsum dolor sit amet")
|
||||||
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to generate key")
|
||||||
|
}
|
||||||
|
|
||||||
|
signer, err := NewSigner(ES256, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to create signer")
|
||||||
|
}
|
||||||
|
|
||||||
|
object, err := signer.Sign(payload)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to sign payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
object, err = ParseSigned(object.FullSerialize())
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to parse jws")
|
||||||
|
}
|
||||||
|
|
||||||
|
if object.Signatures[0].protected.Jwk == nil {
|
||||||
|
t.Error("JWK isn't set in protected header")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now sign it again, but don't embed JWK.
|
||||||
|
signer.SetEmbedJwk(false)
|
||||||
|
|
||||||
|
object, err = signer.Sign(payload)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to sign payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
object, err = ParseSigned(object.FullSerialize())
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Failed to parse jws")
|
||||||
|
}
|
||||||
|
|
||||||
|
if object.Signatures[0].protected.Jwk != nil {
|
||||||
|
t.Error("JWK is set in protected header")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignerWithJWKAndKeyID(t *testing.T) {
|
||||||
|
enc, err := NewSigner(HS256, &JsonWebKey{
|
||||||
|
KeyID: "test-id",
|
||||||
|
Key: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
signed, _ := enc.Sign([]byte("Lorem ipsum dolor sit amet"))
|
||||||
|
|
||||||
|
serialized1, _ := signed.CompactSerialize()
|
||||||
|
serialized2 := signed.FullSerialize()
|
||||||
|
|
||||||
|
parsed1, _ := ParseSigned(serialized1)
|
||||||
|
parsed2, _ := ParseSigned(serialized2)
|
||||||
|
|
||||||
|
if parsed1.Signatures[0].Header.KeyID != "test-id" {
|
||||||
|
t.Errorf("expected message to have key id from JWK, but found '%s' instead", parsed1.Signatures[0].Header.KeyID)
|
||||||
|
}
|
||||||
|
if parsed2.Signatures[0].Header.KeyID != "test-id" {
|
||||||
|
t.Errorf("expected message to have key id from JWK, but found '%s' instead", parsed2.Signatures[0].Header.KeyID)
|
||||||
|
}
|
||||||
|
}
|
349
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/symmetric.go
generated
vendored
Normal file
349
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/symmetric.go
generated
vendored
Normal file
|
@ -0,0 +1,349 @@
|
||||||
|
/*-
|
||||||
|
* 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/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
"crypto/subtle"
|
||||||
|
"errors"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"gopkg.in/square/go-jose.v1/cipher"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Random reader (stubbed out in tests)
|
||||||
|
var randReader = rand.Reader
|
||||||
|
|
||||||
|
// Dummy key cipher for shared symmetric key mode
|
||||||
|
type symmetricKeyCipher struct {
|
||||||
|
key []byte // Pre-shared content-encryption key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signer/verifier for MAC modes
|
||||||
|
type symmetricMac struct {
|
||||||
|
key []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input/output from an AEAD operation
|
||||||
|
type aeadParts struct {
|
||||||
|
iv, ciphertext, tag []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// A content cipher based on an AEAD construction
|
||||||
|
type aeadContentCipher struct {
|
||||||
|
keyBytes int
|
||||||
|
authtagBytes int
|
||||||
|
getAead func(key []byte) (cipher.AEAD, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Random key generator
|
||||||
|
type randomKeyGenerator struct {
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static key generator
|
||||||
|
type staticKeyGenerator struct {
|
||||||
|
key []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new content cipher based on AES-GCM
|
||||||
|
func newAESGCM(keySize int) contentCipher {
|
||||||
|
return &aeadContentCipher{
|
||||||
|
keyBytes: keySize,
|
||||||
|
authtagBytes: 16,
|
||||||
|
getAead: func(key []byte) (cipher.AEAD, error) {
|
||||||
|
aes, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cipher.NewGCM(aes)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new content cipher based on AES-CBC+HMAC
|
||||||
|
func newAESCBC(keySize int) contentCipher {
|
||||||
|
return &aeadContentCipher{
|
||||||
|
keyBytes: keySize * 2,
|
||||||
|
authtagBytes: 16,
|
||||||
|
getAead: func(key []byte) (cipher.AEAD, error) {
|
||||||
|
return josecipher.NewCBCHMAC(key, aes.NewCipher)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an AEAD cipher object for the given content encryption algorithm
|
||||||
|
func getContentCipher(alg ContentEncryption) contentCipher {
|
||||||
|
switch alg {
|
||||||
|
case A128GCM:
|
||||||
|
return newAESGCM(16)
|
||||||
|
case A192GCM:
|
||||||
|
return newAESGCM(24)
|
||||||
|
case A256GCM:
|
||||||
|
return newAESGCM(32)
|
||||||
|
case A128CBC_HS256:
|
||||||
|
return newAESCBC(16)
|
||||||
|
case A192CBC_HS384:
|
||||||
|
return newAESCBC(24)
|
||||||
|
case A256CBC_HS512:
|
||||||
|
return newAESCBC(32)
|
||||||
|
default:
|
||||||
|
return 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:
|
||||||
|
default:
|
||||||
|
return recipientKeyInfo{}, ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
return recipientKeyInfo{
|
||||||
|
keyAlg: keyAlg,
|
||||||
|
keyEncrypter: &symmetricKeyCipher{
|
||||||
|
key: key,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSymmetricSigner creates a recipientSigInfo based on the given key.
|
||||||
|
func newSymmetricSigner(sigAlg SignatureAlgorithm, key []byte) (recipientSigInfo, error) {
|
||||||
|
// Verify that key management algorithm is supported by this encrypter
|
||||||
|
switch sigAlg {
|
||||||
|
case HS256, HS384, HS512:
|
||||||
|
default:
|
||||||
|
return recipientSigInfo{}, ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
return recipientSigInfo{
|
||||||
|
sigAlg: sigAlg,
|
||||||
|
signer: &symmetricMac{
|
||||||
|
key: key,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
if err != nil {
|
||||||
|
return nil, rawHeader{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return key, rawHeader{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key size for random generator
|
||||||
|
func (ctx randomKeyGenerator) keySize() int {
|
||||||
|
return ctx.size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a static key (for direct mode)
|
||||||
|
func (ctx staticKeyGenerator) genKey() ([]byte, rawHeader, error) {
|
||||||
|
cek := make([]byte, len(ctx.key))
|
||||||
|
copy(cek, ctx.key)
|
||||||
|
return cek, rawHeader{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key size for static generator
|
||||||
|
func (ctx staticKeyGenerator) keySize() int {
|
||||||
|
return len(ctx.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get key size for this cipher
|
||||||
|
func (ctx aeadContentCipher) keySize() int {
|
||||||
|
return ctx.keyBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt some data
|
||||||
|
func (ctx aeadContentCipher) encrypt(key, aad, pt []byte) (*aeadParts, error) {
|
||||||
|
// Get a new AEAD instance
|
||||||
|
aead, err := ctx.getAead(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a new nonce
|
||||||
|
iv := make([]byte, aead.NonceSize())
|
||||||
|
_, err = io.ReadFull(randReader, iv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertextAndTag := aead.Seal(nil, iv, pt, aad)
|
||||||
|
offset := len(ciphertextAndTag) - ctx.authtagBytes
|
||||||
|
|
||||||
|
return &aeadParts{
|
||||||
|
iv: iv,
|
||||||
|
ciphertext: ciphertextAndTag[:offset],
|
||||||
|
tag: ciphertextAndTag[offset:],
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt some data
|
||||||
|
func (ctx aeadContentCipher) decrypt(key, aad []byte, parts *aeadParts) ([]byte, error) {
|
||||||
|
aead, err := ctx.getAead(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return aead.Open(nil, parts.iv, append(parts.ciphertext, parts.tag...), aad)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt the content encryption key.
|
||||||
|
func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) {
|
||||||
|
switch alg {
|
||||||
|
case DIRECT:
|
||||||
|
return recipientInfo{
|
||||||
|
header: &rawHeader{},
|
||||||
|
}, nil
|
||||||
|
case A128GCMKW, A192GCMKW, A256GCMKW:
|
||||||
|
aead := newAESGCM(len(ctx.key))
|
||||||
|
|
||||||
|
parts, err := aead.encrypt(ctx.key, []byte{}, cek)
|
||||||
|
if err != nil {
|
||||||
|
return recipientInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return recipientInfo{
|
||||||
|
header: &rawHeader{
|
||||||
|
Iv: newBuffer(parts.iv),
|
||||||
|
Tag: newBuffer(parts.tag),
|
||||||
|
},
|
||||||
|
encryptedKey: parts.ciphertext,
|
||||||
|
}, nil
|
||||||
|
case A128KW, A192KW, A256KW:
|
||||||
|
block, err := aes.NewCipher(ctx.key)
|
||||||
|
if err != nil {
|
||||||
|
return recipientInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
jek, err := josecipher.KeyWrap(block, cek)
|
||||||
|
if err != nil {
|
||||||
|
return recipientInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return recipientInfo{
|
||||||
|
encryptedKey: jek,
|
||||||
|
header: &rawHeader{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return recipientInfo{}, ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt the content encryption key.
|
||||||
|
func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
|
||||||
|
switch KeyAlgorithm(headers.Alg) {
|
||||||
|
case DIRECT:
|
||||||
|
cek := make([]byte, len(ctx.key))
|
||||||
|
copy(cek, ctx.key)
|
||||||
|
return cek, nil
|
||||||
|
case A128GCMKW, A192GCMKW, A256GCMKW:
|
||||||
|
aead := newAESGCM(len(ctx.key))
|
||||||
|
|
||||||
|
parts := &aeadParts{
|
||||||
|
iv: headers.Iv.bytes(),
|
||||||
|
ciphertext: recipient.encryptedKey,
|
||||||
|
tag: headers.Tag.bytes(),
|
||||||
|
}
|
||||||
|
|
||||||
|
cek, err := aead.decrypt(ctx.key, []byte{}, parts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cek, nil
|
||||||
|
case A128KW, A192KW, A256KW:
|
||||||
|
block, err := aes.NewCipher(ctx.key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cek, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign the given payload
|
||||||
|
func (ctx symmetricMac) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
|
||||||
|
mac, err := ctx.hmac(payload, alg)
|
||||||
|
if err != nil {
|
||||||
|
return Signature{}, errors.New("square/go-jose: failed to compute hmac")
|
||||||
|
}
|
||||||
|
|
||||||
|
return Signature{
|
||||||
|
Signature: mac,
|
||||||
|
protected: &rawHeader{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the given payload
|
||||||
|
func (ctx symmetricMac) verifyPayload(payload []byte, mac []byte, alg SignatureAlgorithm) error {
|
||||||
|
expected, err := ctx.hmac(payload, alg)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("square/go-jose: failed to compute hmac")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(mac) != len(expected) {
|
||||||
|
return errors.New("square/go-jose: invalid hmac")
|
||||||
|
}
|
||||||
|
|
||||||
|
match := subtle.ConstantTimeCompare(mac, expected)
|
||||||
|
if match != 1 {
|
||||||
|
return errors.New("square/go-jose: invalid hmac")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the HMAC based on the given alg value
|
||||||
|
func (ctx symmetricMac) hmac(payload []byte, alg SignatureAlgorithm) ([]byte, error) {
|
||||||
|
var hash func() hash.Hash
|
||||||
|
|
||||||
|
switch alg {
|
||||||
|
case HS256:
|
||||||
|
hash = sha256.New
|
||||||
|
case HS384:
|
||||||
|
hash = sha512.New384
|
||||||
|
case HS512:
|
||||||
|
hash = sha512.New
|
||||||
|
default:
|
||||||
|
return nil, ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
hmac := hmac.New(hash, ctx.key)
|
||||||
|
|
||||||
|
// According to documentation, Write() on hash never fails
|
||||||
|
_, _ = hmac.Write(payload)
|
||||||
|
return hmac.Sum(nil), nil
|
||||||
|
}
|
131
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/symmetric_test.go
generated
vendored
Normal file
131
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/symmetric_test.go
generated
vendored
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
/*-
|
||||||
|
* 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/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInvalidSymmetricAlgorithms(t *testing.T) {
|
||||||
|
_, err := newSymmetricRecipient("XYZ", []byte{})
|
||||||
|
if err != ErrUnsupportedAlgorithm {
|
||||||
|
t.Error("should not accept invalid algorithm")
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := &symmetricKeyCipher{}
|
||||||
|
_, err = enc.encryptKey([]byte{}, "XYZ")
|
||||||
|
if err != ErrUnsupportedAlgorithm {
|
||||||
|
t.Error("should not accept invalid algorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAeadErrors(t *testing.T) {
|
||||||
|
aead := &aeadContentCipher{
|
||||||
|
keyBytes: 16,
|
||||||
|
authtagBytes: 16,
|
||||||
|
getAead: func(key []byte) (cipher.AEAD, error) {
|
||||||
|
return nil, ErrCryptoFailure
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
parts, err := aead.encrypt([]byte{}, []byte{}, []byte{})
|
||||||
|
if err != ErrCryptoFailure {
|
||||||
|
t.Error("should handle aead failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = aead.decrypt([]byte{}, []byte{}, parts)
|
||||||
|
if err != ErrCryptoFailure {
|
||||||
|
t.Error("should handle aead failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidKey(t *testing.T) {
|
||||||
|
gcm := newAESGCM(16).(*aeadContentCipher)
|
||||||
|
_, err := gcm.getAead([]byte{})
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should not accept invalid key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStaticKeyGen(t *testing.T) {
|
||||||
|
key := make([]byte, 32)
|
||||||
|
io.ReadFull(rand.Reader, key)
|
||||||
|
|
||||||
|
gen := &staticKeyGenerator{key: key}
|
||||||
|
if gen.keySize() != len(key) {
|
||||||
|
t.Error("static key generator reports incorrect size")
|
||||||
|
}
|
||||||
|
|
||||||
|
generated, _, err := gen.genKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Error("static key generator should always succeed", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(generated, key) {
|
||||||
|
t.Error("static key generator returns different data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVectorsAESGCM(t *testing.T) {
|
||||||
|
// Source: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-29#appendix-A.1
|
||||||
|
plaintext := []byte{
|
||||||
|
84, 104, 101, 32, 116, 114, 117, 101, 32, 115, 105, 103, 110, 32,
|
||||||
|
111, 102, 32, 105, 110, 116, 101, 108, 108, 105, 103, 101, 110, 99,
|
||||||
|
101, 32, 105, 115, 32, 110, 111, 116, 32, 107, 110, 111, 119, 108,
|
||||||
|
101, 100, 103, 101, 32, 98, 117, 116, 32, 105, 109, 97, 103, 105,
|
||||||
|
110, 97, 116, 105, 111, 110, 46}
|
||||||
|
|
||||||
|
aad := []byte{
|
||||||
|
101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 83, 85, 48, 69,
|
||||||
|
116, 84, 48, 70, 70, 85, 67, 73, 115, 73, 109, 86, 117, 89, 121, 73,
|
||||||
|
54, 73, 107, 69, 121, 78, 84, 90, 72, 81, 48, 48, 105, 102, 81}
|
||||||
|
|
||||||
|
expectedCiphertext := []byte{
|
||||||
|
229, 236, 166, 241, 53, 191, 115, 196, 174, 43, 73, 109, 39, 122,
|
||||||
|
233, 96, 140, 206, 120, 52, 51, 237, 48, 11, 190, 219, 186, 80, 111,
|
||||||
|
104, 50, 142, 47, 167, 59, 61, 181, 127, 196, 21, 40, 82, 242, 32,
|
||||||
|
123, 143, 168, 226, 73, 216, 176, 144, 138, 247, 106, 60, 16, 205,
|
||||||
|
160, 109, 64, 63, 192}
|
||||||
|
|
||||||
|
expectedAuthtag := []byte{
|
||||||
|
92, 80, 104, 49, 133, 25, 161, 215, 173, 101, 219, 211, 136, 91, 210, 145}
|
||||||
|
|
||||||
|
// Mock random reader
|
||||||
|
randReader = bytes.NewReader([]byte{
|
||||||
|
177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154,
|
||||||
|
212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122,
|
||||||
|
234, 64, 252, 227, 197, 117, 252, 2, 219, 233, 68, 180, 225, 77, 219})
|
||||||
|
defer resetRandReader()
|
||||||
|
|
||||||
|
enc := newAESGCM(32)
|
||||||
|
key, _, _ := randomKeyGenerator{size: 32}.genKey()
|
||||||
|
out, err := enc.encrypt(key, aad, plaintext)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("Unable to encrypt:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(out.ciphertext, expectedCiphertext) != 0 {
|
||||||
|
t.Error("Ciphertext did not match")
|
||||||
|
}
|
||||||
|
if bytes.Compare(out.tag, expectedAuthtag) != 0 {
|
||||||
|
t.Error("Auth tag did not match")
|
||||||
|
}
|
||||||
|
}
|
74
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/utils.go
generated
vendored
Normal file
74
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*-
|
||||||
|
* 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/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoadPublicKey loads a public key from PEM/DER-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
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("square/go-jose: parse error, got '%s' and '%s'", err0, err1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadPrivateKey loads a private key from PEM/DER-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
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("square/go-jose: parse error, got '%s', '%s' and '%s'", err0, err1, err2)
|
||||||
|
}
|
225
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/utils_test.go
generated
vendored
Normal file
225
vendor/rsc.io/letsencrypt/vendor/gopkg.in/square/go-jose.v1/utils_test.go
generated
vendored
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
/*-
|
||||||
|
* 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"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"math/big"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reset random reader to original value
|
||||||
|
func resetRandReader() {
|
||||||
|
randReader = rand.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build big int from hex-encoded string. Strips whitespace (for testing).
|
||||||
|
func fromHexInt(base16 string) *big.Int {
|
||||||
|
re := regexp.MustCompile(`\s+`)
|
||||||
|
val, ok := new(big.Int).SetString(re.ReplaceAllString(base16, ""), 16)
|
||||||
|
if !ok {
|
||||||
|
panic("Invalid test data")
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build big int from base64-encoded string. Strips whitespace (for testing).
|
||||||
|
func fromBase64Int(base64 string) *big.Int {
|
||||||
|
re := regexp.MustCompile(`\s+`)
|
||||||
|
val, err := base64URLDecode(re.ReplaceAllString(base64, ""))
|
||||||
|
if err != nil {
|
||||||
|
panic("Invalid test data")
|
||||||
|
}
|
||||||
|
return new(big.Int).SetBytes(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode hex-encoded string into byte array. Strips whitespace (for testing).
|
||||||
|
func fromHexBytes(base16 string) []byte {
|
||||||
|
re := regexp.MustCompile(`\s+`)
|
||||||
|
val, err := hex.DecodeString(re.ReplaceAllString(base16, ""))
|
||||||
|
if err != nil {
|
||||||
|
panic("Invalid test data")
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode base64-encoded string into byte array. Strips whitespace (for testing).
|
||||||
|
func fromBase64Bytes(b64 string) []byte {
|
||||||
|
re := regexp.MustCompile(`\s+`)
|
||||||
|
val, err := base64.StdEncoding.DecodeString(re.ReplaceAllString(b64, ""))
|
||||||
|
if err != nil {
|
||||||
|
panic("Invalid test data")
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test vectors below taken from crypto/x509/x509_test.go in the Go std lib.
|
||||||
|
|
||||||
|
var pkixPublicKey = `-----BEGIN PUBLIC KEY-----
|
||||||
|
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3VoPN9PKUjKFLMwOge6+
|
||||||
|
wnDi8sbETGIx2FKXGgqtAKpzmem53kRGEQg8WeqRmp12wgp74TGpkEXsGae7RS1k
|
||||||
|
enJCnma4fii+noGH7R0qKgHvPrI2Bwa9hzsH8tHxpyM3qrXslOmD45EH9SxIDUBJ
|
||||||
|
FehNdaPbLP1gFyahKMsdfxFJLUvbUycuZSJ2ZnIgeVxwm4qbSvZInL9Iu4FzuPtg
|
||||||
|
fINKcbbovy1qq4KvPIrXzhbY3PWDc6btxCf3SE0JdE1MCPThntB62/bLMSQ7xdDR
|
||||||
|
FF53oIpvxe/SCOymfWq/LW849Ytv3Xwod0+wzAP8STXG4HSELS4UedPYeHJJJYcZ
|
||||||
|
+QIDAQAB
|
||||||
|
-----END PUBLIC KEY-----`
|
||||||
|
|
||||||
|
var pkcs1PrivateKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
|
||||||
|
fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
|
||||||
|
/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
|
||||||
|
RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
|
||||||
|
EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
|
||||||
|
IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
|
||||||
|
tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
|
||||||
|
-----END RSA PRIVATE KEY-----`
|
||||||
|
|
||||||
|
var ecdsaSHA256p384CertPem = `
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICSjCCAdECCQDje/no7mXkVzAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCVVMx
|
||||||
|
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS
|
||||||
|
BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
|
||||||
|
CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMDYxMDM0
|
||||||
|
WhcNMjIwNTE5MDYxMDM0WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
|
||||||
|
b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg
|
||||||
|
SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s
|
||||||
|
YW5nLWRldkBnbWFpbC5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARRuzRNIKRK
|
||||||
|
jIktEmXanNmrTR/q/FaHXLhWRZ6nHWe26Fw7Rsrbk+VjGy4vfWtNn7xSFKrOu5ze
|
||||||
|
qxKnmE0h5E480MNgrUiRkaGO2GMJJVmxx20aqkXOk59U8yGA4CghE6MwCgYIKoZI
|
||||||
|
zj0EAwIDZwAwZAIwBZEN8gvmRmfeP/9C1PRLzODIY4JqWub2PLRT4mv9GU+yw3Gr
|
||||||
|
PU9A3CHMdEcdw/MEAjBBO1lId8KOCh9UZunsSMfqXiVurpzmhWd6VYZ/32G+M+Mh
|
||||||
|
3yILeYQzllt/g0rKVRk=
|
||||||
|
-----END CERTIFICATE-----`
|
||||||
|
|
||||||
|
var ecdsaSHA256p384CertDer = fromBase64Bytes(`
|
||||||
|
MIICSjCCAdECCQDje/no7mXkVzAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCVVMx
|
||||||
|
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS
|
||||||
|
BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
|
||||||
|
CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMDYxMDM0
|
||||||
|
WhcNMjIwNTE5MDYxMDM0WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
|
||||||
|
b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg
|
||||||
|
SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s
|
||||||
|
YW5nLWRldkBnbWFpbC5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARRuzRNIKRK
|
||||||
|
jIktEmXanNmrTR/q/FaHXLhWRZ6nHWe26Fw7Rsrbk+VjGy4vfWtNn7xSFKrOu5ze
|
||||||
|
qxKnmE0h5E480MNgrUiRkaGO2GMJJVmxx20aqkXOk59U8yGA4CghE6MwCgYIKoZI
|
||||||
|
zj0EAwIDZwAwZAIwBZEN8gvmRmfeP/9C1PRLzODIY4JqWub2PLRT4mv9GU+yw3Gr
|
||||||
|
PU9A3CHMdEcdw/MEAjBBO1lId8KOCh9UZunsSMfqXiVurpzmhWd6VYZ/32G+M+Mh
|
||||||
|
3yILeYQzllt/g0rKVRk=`)
|
||||||
|
|
||||||
|
var pkcs8ECPrivateKey = `
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIHtAgEAMBAGByqGSM49AgEGBSuBBAAjBIHVMIHSAgEBBEHqkl65VsjYDQWIHfgv
|
||||||
|
zQLPa0JZBsaJI16mjiH8k6VA4lgfK/KNldlEsY433X7wIzo43u8OpX7Nv7n8pVRH
|
||||||
|
15XWK6GBiQOBhgAEAfDuikMI4bWsyse7t8iSCmjt9fneW/qStZuIPuVLo7mSJdud
|
||||||
|
Cs3J/x9wOnnhLv1u+0atnq5HKKdL4ff3itJPlhmSAQzByKQ5LTvB7d6fn95GJVK/
|
||||||
|
hNuS5qGBpB7qeMXVFoki0/2RZIOway8/fXjmNYwe4v/XB5LLn4hcTvEUGYcF8M9K
|
||||||
|
-----END PRIVATE KEY-----`
|
||||||
|
|
||||||
|
var ecPrivateKey = `
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MIHcAgEBBEIBv2rdY9mWGD/UgiuXB0LJcUzgaB6TXq/Ra1jrZKBV3IGSacM5QDFu
|
||||||
|
N8yrywiQaTDEqn1zVcLwrnqoQux3gWN1jxugBwYFK4EEACOhgYkDgYYABAFJgaM/
|
||||||
|
2a3+gE6Khm/1PYftqNwAzQ21HSLp27q2lTN+GBFho691ARFRkr9UzlQ8gRnhkTbu
|
||||||
|
yGfASamlHsYlr3Tv+gFc4BY8SU0q8kzpQ0dOHWFk7dfGFmKwhJrSFIIOeRn/LY03
|
||||||
|
XsVFctNDsGhobS2JguQrxhGx8Ll7vQCakV/PEmCQJA==
|
||||||
|
-----END EC PRIVATE KEY-----`
|
||||||
|
|
||||||
|
var ecPrivateKeyDer = fromBase64Bytes(`
|
||||||
|
MIHcAgEBBEIBv2rdY9mWGD/UgiuXB0LJcUzgaB6TXq/Ra1jrZKBV3IGSacM5QDFu
|
||||||
|
N8yrywiQaTDEqn1zVcLwrnqoQux3gWN1jxugBwYFK4EEACOhgYkDgYYABAFJgaM/
|
||||||
|
2a3+gE6Khm/1PYftqNwAzQ21HSLp27q2lTN+GBFho691ARFRkr9UzlQ8gRnhkTbu
|
||||||
|
yGfASamlHsYlr3Tv+gFc4BY8SU0q8kzpQ0dOHWFk7dfGFmKwhJrSFIIOeRn/LY03
|
||||||
|
XsVFctNDsGhobS2JguQrxhGx8Ll7vQCakV/PEmCQJA==`)
|
||||||
|
|
||||||
|
var invalidPemKey = `
|
||||||
|
-----BEGIN PUBLIC KEY-----
|
||||||
|
MIHcAgEBBEIBv2rdY9mWGD/UgiuXB0LJcUzgaB6TXq/Ra1jrZKBV3IGSacM5QDFu
|
||||||
|
XsVFctNDsGhobS2JguQrxhGx8Ll7vQCakV/PEmCQJA==
|
||||||
|
-----END PUBLIC KEY-----`
|
||||||
|
|
||||||
|
func TestLoadPublicKey(t *testing.T) {
|
||||||
|
pub, err := LoadPublicKey([]byte(pkixPublicKey))
|
||||||
|
switch pub.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
default:
|
||||||
|
t.Error("failed to parse RSA PKIX public key:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, err = LoadPublicKey([]byte(ecdsaSHA256p384CertPem))
|
||||||
|
switch pub.(type) {
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
default:
|
||||||
|
t.Error("failed to parse ECDSA X.509 cert:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, err = LoadPublicKey([]byte(ecdsaSHA256p384CertDer))
|
||||||
|
switch pub.(type) {
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
default:
|
||||||
|
t.Error("failed to parse ECDSA X.509 cert:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, err = LoadPublicKey([]byte("###"))
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should not parse invalid key")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, err = LoadPublicKey([]byte(invalidPemKey))
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should not parse invalid key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadPrivateKey(t *testing.T) {
|
||||||
|
priv, err := LoadPrivateKey([]byte(pkcs1PrivateKey))
|
||||||
|
switch priv.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
default:
|
||||||
|
t.Error("failed to parse RSA PKCS1 private key:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err = LoadPrivateKey([]byte(pkcs8ECPrivateKey))
|
||||||
|
if _, ok := priv.(*ecdsa.PrivateKey); !ok {
|
||||||
|
t.Error("failed to parse EC PKCS8 private key:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err = LoadPrivateKey([]byte(ecPrivateKey))
|
||||||
|
if _, ok := priv.(*ecdsa.PrivateKey); !ok {
|
||||||
|
t.Error("failed to parse EC private key:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err = LoadPrivateKey([]byte(ecPrivateKeyDer))
|
||||||
|
if _, ok := priv.(*ecdsa.PrivateKey); !ok {
|
||||||
|
t.Error("failed to parse EC private key:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err = LoadPrivateKey([]byte("###"))
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should not parse invalid key")
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err = LoadPrivateKey([]byte(invalidPemKey))
|
||||||
|
if err == nil {
|
||||||
|
t.Error("should not parse invalid key")
|
||||||
|
}
|
||||||
|
}
|
31
vendor/rsc.io/letsencrypt/vendor/vendor.json
generated
vendored
Normal file
31
vendor/rsc.io/letsencrypt/vendor/vendor.json
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"comment": "",
|
||||||
|
"ignore": "",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"checksumSHA1": "CHmdoMriAboKW2nHYSXo0yBizaE=",
|
||||||
|
"path": "github.com/xenolf/lego/acme",
|
||||||
|
"revision": "ca19a90028e242e878585941c2a27c8f3b3efc25",
|
||||||
|
"revisionTime": "2016-03-28T16:28:34Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "jrheBzltbBE1frmNXQiu911T7dE=",
|
||||||
|
"path": "gopkg.in/square/go-jose.v1",
|
||||||
|
"revision": "40d457b439244b546f023d056628e5184136899b",
|
||||||
|
"revisionTime": "2016-03-29T20:33:11Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "fX4KSC9E1oX9yRx20Zjb3rVJHn4=",
|
||||||
|
"path": "gopkg.in/square/go-jose.v1/cipher",
|
||||||
|
"revision": "40d457b439244b546f023d056628e5184136899b",
|
||||||
|
"revisionTime": "2016-03-29T20:33:11Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "NxdXsIcLGuuX654ygsaOhoLsg6s=",
|
||||||
|
"path": "gopkg.in/square/go-jose.v1/json",
|
||||||
|
"revision": "40d457b439244b546f023d056628e5184136899b",
|
||||||
|
"revisionTime": "2016-03-29T20:33:11Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rootPath": "rsc.io/letsencrypt"
|
||||||
|
}
|
Loading…
Reference in a new issue