Merge branch 'master' into store-ips
This commit is contained in:
commit
6777b276a7
|
|
@ -2,6 +2,7 @@
|
|||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.pyc
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/letsencrypt/go-jose",
|
||||
"Rev": "924fcf0aaa98274fd0d586edf64a517ebe24797e"
|
||||
"Rev": "1e94de308e9f6348370af5e409ce552aeb8f1019"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mattn/go-sqlite3",
|
||||
|
|
|
|||
|
|
@ -13,15 +13,21 @@ go:
|
|||
- 1.4
|
||||
- tip
|
||||
|
||||
before_script:
|
||||
- export PATH=$HOME/.local/bin:$PATH
|
||||
|
||||
before_install:
|
||||
- go get github.com/axw/gocov/gocov
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get golang.org/x/tools/cmd/cover || true
|
||||
- go get code.google.com/p/go.tools/cmd/cover || true
|
||||
- pip install cram --user `whoami`
|
||||
|
||||
script:
|
||||
- go test . -v -covermode=count -coverprofile=profile.cov
|
||||
- go test ./cipher -v -covermode=count -coverprofile=cipher/profile.cov
|
||||
- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t
|
||||
- cd ..
|
||||
|
||||
after_success:
|
||||
- tail -n+2 cipher/profile.cov >> profile.cov
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ US maintained blocked list.
|
|||
## Overview
|
||||
|
||||
The implementation follows the
|
||||
[JSON Web Encryption](http://www.ietf.org/id/draft-ietf-jose-json-web-encryption-40.txt)
|
||||
standard (as of version 40) and
|
||||
[JSON Web Signature](http://www.ietf.org/id/draft-ietf-jose-json-web-signature-41.txt)
|
||||
standard (as of version 41). Tables of supported algorithms are shown below.
|
||||
[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`) for encrypting/decrypting JWE messages in a
|
||||
|
|
@ -30,7 +30,7 @@ shell.
|
|||
|
||||
See below for a table of supported algorithms. Algorithm identifiers match
|
||||
the names in the
|
||||
[JSON Web Algorithms](http://www.ietf.org/id/draft-ietf-jose-json-web-algorithms-40.txt)
|
||||
[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.
|
||||
|
|
|
|||
189
Godeps/_workspace/src/github.com/letsencrypt/go-jose/cipher/cbc_hmac.go
generated
vendored
Normal file
189
Godeps/_workspace/src/github.com/letsencrypt/go-jose/cipher/cbc_hmac.go
generated
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/*-
|
||||
* 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)
|
||||
buffer := []byte(ciphertext[:offset])
|
||||
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 > 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
|
||||
}
|
||||
458
Godeps/_workspace/src/github.com/letsencrypt/go-jose/cipher/cbc_hmac_test.go
generated
vendored
Normal file
458
Godeps/_workspace/src/github.com/letsencrypt/go-jose/cipher/cbc_hmac_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,458 @@
|
|||
/*-
|
||||
* 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/rand"
|
||||
"io"
|
||||
"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 TestInvalidCiphertext(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)
|
||||
|
||||
// Mutated ciphertext, but with correct auth tag
|
||||
ct[len(ct)-ctx.authtagBytes-1] ^= 0xFF
|
||||
tag := ctx.computeAuthTag(nil, nonce, ct[:len(ct)-ctx.authtagBytes])
|
||||
copy(ct[len(ct)-ctx.authtagBytes:], tag)
|
||||
|
||||
// Open should fail (b/c of invalid padding, even though tag matches)
|
||||
_, err = aead.Open(nil, nonce, ct, nil)
|
||||
if err == nil {
|
||||
t.Error("open on mutated ciphertext should fail")
|
||||
}
|
||||
}
|
||||
|
||||
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 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
Godeps/_workspace/src/github.com/letsencrypt/go-jose/cipher/concat_kdf.go
generated
vendored
Normal file
75
Godeps/_workspace/src/github.com/letsencrypt/go-jose/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
|
||||
}
|
||||
148
Godeps/_workspace/src/github.com/letsencrypt/go-jose/cipher/concat_kdf_test.go
generated
vendored
Normal file
148
Godeps/_workspace/src/github.com/letsencrypt/go-jose/cipher/concat_kdf_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/*-
|
||||
* 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
|
||||
for i := 10; i <= 100; i++ {
|
||||
out := make([]byte, 1024)
|
||||
reader := NewConcatKDF(crypto.SHA256, z, algID, ptyUInfo, ptyVInfo, supPubInfo, supPrivInfo)
|
||||
|
||||
for j := 0; j < 1024/i; j++ {
|
||||
_, _ = reader.Read(out[j*i:])
|
||||
}
|
||||
|
||||
outputs = append(outputs, out)
|
||||
}
|
||||
|
||||
for i := range outputs {
|
||||
if bytes.Compare(outputs[i], outputs[i%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
Godeps/_workspace/src/github.com/letsencrypt/go-jose/cipher/ecdh_es.go
generated
vendored
Normal file
51
Godeps/_workspace/src/github.com/letsencrypt/go-jose/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
Godeps/_workspace/src/github.com/letsencrypt/go-jose/cipher/ecdh_es_test.go
generated
vendored
Normal file
98
Godeps/_workspace/src/github.com/letsencrypt/go-jose/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
Godeps/_workspace/src/github.com/letsencrypt/go-jose/cipher/key_wrap.go
generated
vendored
Normal file
109
Godeps/_workspace/src/github.com/letsencrypt/go-jose/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
Godeps/_workspace/src/github.com/letsencrypt/go-jose/cipher/key_wrap_test.go
generated
vendored
Normal file
133
Godeps/_workspace/src/github.com/letsencrypt/go-jose/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)
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"bytes"
|
||||
"compress/flate"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"math/big"
|
||||
|
|
@ -131,6 +132,12 @@ func newBuffer(data []byte) *byteBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
func newBufferFromInt(num uint64) *byteBuffer {
|
||||
data := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(data, num)
|
||||
return newBuffer(bytes.TrimLeft(data, "\x00"))
|
||||
}
|
||||
|
||||
func (b *byteBuffer) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(b.base64())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,3 +106,15 @@ func TestInvalidCompression(t *testing.T) {
|
|||
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]")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ The utility includes the subcommands `encrypt`, `decrypt`, `sign`, `verify` and
|
|||
`expand`. Examples for each command can be found below.
|
||||
|
||||
Algorithms are selected via the `--alg` and `--enc` flags, which influence the
|
||||
`alg` and `enc` headers in JWE/JWS messages respectively. For JWE, `--alg`
|
||||
specified the key managment algorithm (e.g. RSA-OAEP) and `--enc` specifies the
|
||||
content encryption (e.g. A128GCM). For JWS, `--alg` specified the signature
|
||||
algorithm (e.g. `PS256`).
|
||||
`alg` and `enc` headers in respectively. For JWE, `--alg` specifies the key
|
||||
managment algorithm (e.g. `RSA-OAEP`) and `--enc` specifies the content
|
||||
encryption (e.g. `A128GCM`). For JWS, `--alg` specifies the signature algorithm
|
||||
(e.g. `PS256`).
|
||||
|
||||
Input and output files can be specified via the `--in` and `--out` flags.
|
||||
Either flag can be omitted, in which case `jose-util` uses stdin/stdout for
|
||||
|
|
|
|||
88
Godeps/_workspace/src/github.com/letsencrypt/go-jose/jose-util/jose-util.t
generated
vendored
Normal file
88
Godeps/_workspace/src/github.com/letsencrypt/go-jose/jose-util/jose-util.t
generated
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
Set up test keys.
|
||||
|
||||
$ cat > rsa.pub <<EOF
|
||||
> -----BEGIN PUBLIC KEY-----
|
||||
> MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAslWybuiNYR7uOgKuvaBw
|
||||
> qVk8saEutKhOAaW+3hWF65gJei+ZV8QFfYDxs9ZaRZlWAUMtncQPnw7ZQlXO9ogN
|
||||
> 5cMcN50C6qMOOZzghK7danalhF5lUETC4Hk3Eisbi/PR3IfVyXaRmqL6X66MKj/J
|
||||
> AKyD9NFIDVy52K8A198Jojnrw2+XXQW72U68fZtvlyl/BTBWQ9Re5JSTpEcVmpCR
|
||||
> 8FrFc0RPMBm+G5dRs08vvhZNiTT2JACO5V+J5ZrgP3s5hnGFcQFZgDnXLInDUdoi
|
||||
> 1MuCjaAU0ta8/08pHMijNix5kFofdPEB954MiZ9k4kQ5/utt02I9x2ssHqw71ojj
|
||||
> vwIDAQAB
|
||||
> -----END PUBLIC KEY-----
|
||||
> EOF
|
||||
|
||||
$ cat > rsa.key <<EOF
|
||||
> -----BEGIN RSA PRIVATE KEY-----
|
||||
> MIIEogIBAAKCAQEAslWybuiNYR7uOgKuvaBwqVk8saEutKhOAaW+3hWF65gJei+Z
|
||||
> V8QFfYDxs9ZaRZlWAUMtncQPnw7ZQlXO9ogN5cMcN50C6qMOOZzghK7danalhF5l
|
||||
> UETC4Hk3Eisbi/PR3IfVyXaRmqL6X66MKj/JAKyD9NFIDVy52K8A198Jojnrw2+X
|
||||
> XQW72U68fZtvlyl/BTBWQ9Re5JSTpEcVmpCR8FrFc0RPMBm+G5dRs08vvhZNiTT2
|
||||
> JACO5V+J5ZrgP3s5hnGFcQFZgDnXLInDUdoi1MuCjaAU0ta8/08pHMijNix5kFof
|
||||
> dPEB954MiZ9k4kQ5/utt02I9x2ssHqw71ojjvwIDAQABAoIBABrYDYDmXom1BzUS
|
||||
> PE1s/ihvt1QhqA8nmn5i/aUeZkc9XofW7GUqq4zlwPxKEtKRL0IHY7Fw1s0hhhCX
|
||||
> LA0uE7F3OiMg7lR1cOm5NI6kZ83jyCxxrRx1DUSO2nxQotfhPsDMbaDiyS4WxEts
|
||||
> 0cp2SYJhdYd/jTH9uDfmt+DGwQN7Jixio1Dj3vwB7krDY+mdre4SFY7Gbk9VxkDg
|
||||
> LgCLMoq52m+wYufP8CTgpKFpMb2/yJrbLhuJxYZrJ3qd/oYo/91k6v7xlBKEOkwD
|
||||
> 2veGk9Dqi8YPNxaRktTEjnZb6ybhezat93+VVxq4Oem3wMwou1SfXrSUKtgM/p2H
|
||||
> vfw/76ECgYEA2fNL9tC8u9M0wjA+kvvtDG96qO6O66Hksssy6RWInD+Iqk3MtHQt
|
||||
> LeoCjvX+zERqwOb6SI6empk5pZ9E3/9vJ0dBqkxx3nqn4M/nRWnExGgngJsL959t
|
||||
> f50cdxva8y1RjNhT4kCwTrupX/TP8lAG8SfG1Alo2VFR8iWd8hDQcTECgYEA0Xfj
|
||||
> EgqAsVh4U0s3lFxKjOepEyp0G1Imty5J16SvcOEAD1Mrmz94aSSp0bYhXNVdbf7n
|
||||
> Rk77htWC7SE29fGjOzZRS76wxj/SJHF+rktHB2Zt23k1jBeZ4uLMPMnGLY/BJ099
|
||||
> 5DTGo0yU0rrPbyXosx+ukfQLAHFuggX4RNeM5+8CgYB7M1J/hGMLcUpjcs4MXCgV
|
||||
> XXbiw2c6v1r9zmtK4odEe42PZ0cNwpY/XAZyNZAAe7Q0stxL44K4NWEmxC80x7lX
|
||||
> ZKozz96WOpNnO16qGC3IMHAT/JD5Or+04WTT14Ue7UEp8qcIQDTpbJ9DxKk/eglS
|
||||
> jH+SIHeKULOXw7fSu7p4IQKBgBnyVchIUMSnBtCagpn4DKwDjif3nEY+GNmb/D2g
|
||||
> ArNiy5UaYk5qwEmV5ws5GkzbiSU07AUDh5ieHgetk5dHhUayZcOSLWeBRFCLVnvU
|
||||
> i0nZYEZNb1qZGdDG8zGcdNXz9qMd76Qy/WAA/nZT+Zn1AiweAovFxQ8a/etRPf2Z
|
||||
> DbU1AoGAHpCgP7B/4GTBe49H0AQueQHBn4RIkgqMy9xiMeR+U+U0vaY0TlfLhnX+
|
||||
> 5PkNfkPXohXlfL7pxwZNYa6FZhCAubzvhKCdUASivkoGaIEk6g1VTVYS/eDVQ4CA
|
||||
> slfl+elXtLq/l1kQ8C14jlHrQzSXx4PQvjDEnAmaHSJNz4mP9Fg=
|
||||
> -----END RSA PRIVATE KEY-----
|
||||
> EOF
|
||||
|
||||
$ cat > ec.pub <<EOF
|
||||
> -----BEGIN PUBLIC KEY-----
|
||||
> MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE9yoUEAgxTd9svwe9oPqjhcP+f2jcdTL2
|
||||
> Wq8Aw2v9ht1dBy00tFRPNrCxFCkvMcJFhSPoDUV5NL7zfh3/psiSNYziGPrWEJYf
|
||||
> gmYihjSeoOf0ru1erpBrTflImPrMftCy
|
||||
> -----END PUBLIC KEY-----
|
||||
> EOF
|
||||
|
||||
$ cat > ec.key <<EOF
|
||||
> -----BEGIN EC PRIVATE KEY-----
|
||||
> MIGkAgEBBDDvoj/bM1HokUjYWO/IDFs26Jo0GIFtU3tMQQu7ZabKscDMK3dZA0mK
|
||||
> v97ij7BBFbCgBwYFK4EEACKhZANiAAT3KhQQCDFN32y/B72g+qOFw/5/aNx1MvZa
|
||||
> rwDDa/2G3V0HLTS0VE82sLEUKS8xwkWFI+gNRXk0vvN+Hf+myJI1jOIY+tYQlh+C
|
||||
> ZiKGNJ6g5/Su7V6ukGtN+UiY+sx+0LI=
|
||||
> -----END EC PRIVATE KEY-----
|
||||
> EOF
|
||||
|
||||
Encrypt and then decrypt a test message (RSA).
|
||||
|
||||
$ echo "Lorem ipsum dolor sit amet" |
|
||||
> jose-util encrypt --alg RSA-OAEP --enc A128GCM --key rsa.pub |
|
||||
> jose-util decrypt --key rsa.key
|
||||
Lorem ipsum dolor sit amet
|
||||
|
||||
Encrypt and then decrypt a test message (EC).
|
||||
|
||||
$ echo "Lorem ipsum dolor sit amet" |
|
||||
> jose-util encrypt --alg ECDH-ES+A128KW --enc A128GCM --key ec.pub |
|
||||
> jose-util decrypt --key ec.key
|
||||
Lorem ipsum dolor sit amet
|
||||
|
||||
Sign and verify a test message (RSA).
|
||||
|
||||
$ echo "Lorem ipsum dolor sit amet" |
|
||||
> jose-util sign --alg PS256 --key rsa.key |
|
||||
> jose-util verify --key rsa.pub
|
||||
Lorem ipsum dolor sit amet
|
||||
|
||||
Sign and verify a test message (EC).
|
||||
|
||||
$ echo "Lorem ipsum dolor sit amet" |
|
||||
> jose-util sign --alg ES384 --key ec.key |
|
||||
> jose-util verify --key ec.pub
|
||||
Lorem ipsum dolor sit amet
|
||||
|
|
@ -20,7 +20,6 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
|
@ -128,13 +127,10 @@ func (key rawJsonWebKey) rsaPublicKey() (*rsa.PublicKey, error) {
|
|||
}
|
||||
|
||||
func fromRsaPublicKey(pub *rsa.PublicKey) *rawJsonWebKey {
|
||||
e := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(e, uint32(pub.E))
|
||||
|
||||
return &rawJsonWebKey{
|
||||
Kty: "RSA",
|
||||
N: newBuffer(pub.N.Bytes()),
|
||||
E: newBuffer(e),
|
||||
E: newBufferFromInt(uint64(pub.E)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ package jose
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
|
@ -178,7 +178,7 @@ func TestMarshalNonPointer(t *testing.T) {
|
|||
t.Error(fmt.Sprintf("Error marshalling JSON: %v", err))
|
||||
return
|
||||
}
|
||||
expected := "{\"Key\":{\"kty\":\"RSA\",\"n\":\"vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw\",\"e\":\"AAEAAQ\"}}"
|
||||
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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -474,7 +474,7 @@ func (ca *CertificateAuthorityImpl) IssueCertificate(csr x509.CertificateRequest
|
|||
return cert, nil
|
||||
}
|
||||
|
||||
ca.SA.UpdateOCSP(serial, ocspResponse)
|
||||
err = ca.SA.UpdateOCSP(serial, ocspResponse)
|
||||
if err != nil {
|
||||
ca.log.Warning(fmt.Sprintf("Post-Issuance OCSP failed storing: %s", err))
|
||||
return cert, nil
|
||||
|
|
|
|||
|
|
@ -436,8 +436,9 @@ func TestRevoke(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Failed to get cert status")
|
||||
|
||||
test.AssertEquals(t, status.Status, core.OCSPStatusRevoked)
|
||||
test.Assert(t, time.Now().Sub(status.OCSPLastUpdated) > time.Second,
|
||||
fmt.Sprintf("OCSP LastUpdated was wrong: %v", status.OCSPLastUpdated))
|
||||
secondAgo := time.Now().Add(-time.Second)
|
||||
test.Assert(t, status.OCSPLastUpdated.After(secondAgo),
|
||||
fmt.Sprintf("OCSP LastUpdated was more than a second old: %v", status.OCSPLastUpdated))
|
||||
}
|
||||
|
||||
func TestIssueCertificate(t *testing.T) {
|
||||
|
|
|
|||
36
core/dns.go
36
core/dns.go
|
|
@ -122,13 +122,18 @@ func (dnsResolver *DNSResolverImpl) LookupHost(hostname string) ([]net.IP, time.
|
|||
return addrs, aRtt, aaaaRtt, nil
|
||||
}
|
||||
|
||||
// LookupCNAME sends a DNS query to find a CNAME record associated hostname and returns the
|
||||
// record target.
|
||||
// LookupCNAME returns the target name if a CNAME record exists for
|
||||
// the given domain name. If the CNAME does not exist (NXDOMAIN,
|
||||
// NXRRSET, or a successful response with no CNAME records), it
|
||||
// returns the empty string and a nil error.
|
||||
func (dnsResolver *DNSResolverImpl) LookupCNAME(hostname string) (string, time.Duration, error) {
|
||||
r, rtt, err := dnsResolver.ExchangeOne(hostname, dns.TypeCNAME)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
if r.Rcode == dns.RcodeNXRrset || r.Rcode == dns.RcodeNameError {
|
||||
return "", rtt, nil
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
err = fmt.Errorf("DNS failure: %d-%s for CNAME query", r.Rcode, dns.RcodeToString[r.Rcode])
|
||||
return "", rtt, err
|
||||
|
|
@ -143,9 +148,32 @@ func (dnsResolver *DNSResolverImpl) LookupCNAME(hostname string) (string, time.D
|
|||
return "", rtt, nil
|
||||
}
|
||||
|
||||
// LookupDNAME is LookupCNAME, but for DNAME.
|
||||
func (dnsResolver *DNSResolverImpl) LookupDNAME(hostname string) (string, time.Duration, error) {
|
||||
r, rtt, err := dnsResolver.ExchangeOne(hostname, dns.TypeDNAME)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
if r.Rcode == dns.RcodeNXRrset || r.Rcode == dns.RcodeNameError {
|
||||
return "", rtt, nil
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
err = fmt.Errorf("DNS failure: %d-%s for DNAME query", r.Rcode, dns.RcodeToString[r.Rcode])
|
||||
return "", rtt, err
|
||||
}
|
||||
|
||||
for _, answer := range r.Answer {
|
||||
if cname, ok := answer.(*dns.DNAME); ok {
|
||||
return cname.Target, rtt, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", rtt, nil
|
||||
}
|
||||
|
||||
// LookupCAA sends a DNS query to find all CAA records associated with
|
||||
// the provided hostname. If the response code from the resolver is SERVFAIL
|
||||
// an empty slice of CAA records is returned.
|
||||
// the provided hostname. If the response code from the resolver is
|
||||
// SERVFAIL an empty slice of CAA records is returned.
|
||||
func (dnsResolver *DNSResolverImpl) LookupCAA(hostname string) ([]*dns.CAA, time.Duration, error) {
|
||||
r, rtt, err := dnsResolver.ExchangeOne(hostname, dns.TypeCAA)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
|
@ -25,11 +26,14 @@ func mockDNSQuery(w dns.ResponseWriter, r *dns.Msg) {
|
|||
m.SetReply(r)
|
||||
m.Compress = false
|
||||
|
||||
appendAnswer := func(rr dns.RR) {
|
||||
m.Answer = append(m.Answer, rr)
|
||||
}
|
||||
for _, q := range r.Question {
|
||||
q.Name = strings.ToLower(q.Name)
|
||||
if q.Name == "servfail.com." {
|
||||
m.Rcode = dns.RcodeServerFailure
|
||||
w.WriteMsg(m)
|
||||
return
|
||||
break
|
||||
}
|
||||
switch q.Qtype {
|
||||
case dns.TypeSOA:
|
||||
|
|
@ -42,31 +46,50 @@ func mockDNSQuery(w dns.ResponseWriter, r *dns.Msg) {
|
|||
record.Retry = 1
|
||||
record.Expire = 1
|
||||
record.Minttl = 1
|
||||
|
||||
m.Answer = append(m.Answer, record)
|
||||
w.WriteMsg(m)
|
||||
return
|
||||
appendAnswer(record)
|
||||
case dns.TypeA:
|
||||
if q.Name == "cps.letsencrypt.org." {
|
||||
record := new(dns.A)
|
||||
record.Hdr = dns.RR_Header{Name: "cps.letsencrypt.org.", Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}
|
||||
record.A = net.ParseIP("127.0.0.1")
|
||||
|
||||
m.Answer = append(m.Answer, record)
|
||||
w.WriteMsg(m)
|
||||
return
|
||||
appendAnswer(record)
|
||||
}
|
||||
case dns.TypeCNAME:
|
||||
if q.Name == "cname.letsencrypt.org." {
|
||||
record := new(dns.CNAME)
|
||||
record.Hdr = dns.RR_Header{Name: "cname.letsencrypt.org.", Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: 30}
|
||||
record.Target = "cps.letsencrypt.org."
|
||||
appendAnswer(record)
|
||||
}
|
||||
if q.Name == "cname.example.com." {
|
||||
record := new(dns.CNAME)
|
||||
record.Hdr = dns.RR_Header{Name: "cname.example.com.", Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: 30}
|
||||
record.Target = "CAA.example.com."
|
||||
appendAnswer(record)
|
||||
}
|
||||
case dns.TypeDNAME:
|
||||
if q.Name == "dname.letsencrypt.org." {
|
||||
record := new(dns.DNAME)
|
||||
record.Hdr = dns.RR_Header{Name: "dname.letsencrypt.org.", Rrtype: dns.TypeDNAME, Class: dns.ClassINET, Ttl: 30}
|
||||
record.Target = "cps.letsencrypt.org."
|
||||
appendAnswer(record)
|
||||
}
|
||||
case dns.TypeCAA:
|
||||
if q.Name == "bracewel.net." {
|
||||
if q.Name == "bracewel.net." || q.Name == "caa.example.com." {
|
||||
record := new(dns.CAA)
|
||||
record.Hdr = dns.RR_Header{Name: "bracewel.net.", Rrtype: dns.TypeCAA, Class: dns.ClassINET, Ttl: 0}
|
||||
record.Hdr = dns.RR_Header{Name: q.Name, Rrtype: dns.TypeCAA, Class: dns.ClassINET, Ttl: 0}
|
||||
record.Tag = "issue"
|
||||
record.Value = "letsencrypt.org"
|
||||
record.Flag = 1
|
||||
|
||||
m.Answer = append(m.Answer, record)
|
||||
w.WriteMsg(m)
|
||||
return
|
||||
appendAnswer(record)
|
||||
}
|
||||
if q.Name == "cname.example.com." {
|
||||
record := new(dns.CAA)
|
||||
record.Hdr = dns.RR_Header{Name: "caa.example.com.", Rrtype: dns.TypeCAA, Class: dns.ClassINET, Ttl: 0}
|
||||
record.Tag = "issue"
|
||||
record.Value = "letsencrypt.org"
|
||||
record.Flag = 1
|
||||
appendAnswer(record)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -157,7 +180,7 @@ func TestDNSServFail(t *testing.T) {
|
|||
test.AssertError(t, err, "LookupCNAME didn't return an error")
|
||||
|
||||
_, _, _, err = obj.LookupHost(bad)
|
||||
test.AssertError(t, err, "LookupCNAME didn't return an error")
|
||||
test.AssertError(t, err, "LookupHost didn't return an error")
|
||||
|
||||
// CAA lookup ignores validation failures from the resolver for now
|
||||
// and returns an empty list of CAA records.
|
||||
|
|
@ -204,4 +227,32 @@ func TestDNSLookupCAA(t *testing.T) {
|
|||
caas, _, err = obj.LookupCAA("nonexistent.letsencrypt.org")
|
||||
test.AssertNotError(t, err, "CAA lookup failed")
|
||||
test.Assert(t, len(caas) == 0, "Shouldn't have CAA records")
|
||||
|
||||
caas, _, err = obj.LookupCAA("cname.example.com")
|
||||
test.AssertNotError(t, err, "CAA lookup failed")
|
||||
test.Assert(t, len(caas) > 0, "Should follow CNAME to find CAA")
|
||||
}
|
||||
|
||||
func TestDNSLookupCNAME(t *testing.T) {
|
||||
obj := NewDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr})
|
||||
|
||||
target, _, err := obj.LookupCNAME("cps.letsencrypt.org")
|
||||
test.AssertNotError(t, err, "CNAME lookup failed")
|
||||
test.AssertEquals(t, target, "")
|
||||
|
||||
target, _, err = obj.LookupCNAME("cname.letsencrypt.org")
|
||||
test.AssertNotError(t, err, "CNAME lookup failed")
|
||||
test.AssertEquals(t, target, "cps.letsencrypt.org.")
|
||||
}
|
||||
|
||||
func TestDNSLookupDNAME(t *testing.T) {
|
||||
obj := NewDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr})
|
||||
|
||||
target, _, err := obj.LookupDNAME("cps.letsencrypt.org")
|
||||
test.AssertNotError(t, err, "DNAME lookup failed")
|
||||
test.AssertEquals(t, target, "")
|
||||
|
||||
target, _, err = obj.LookupDNAME("dname.letsencrypt.org")
|
||||
test.AssertNotError(t, err, "DNAME lookup failed")
|
||||
test.AssertEquals(t, target, "cps.letsencrypt.org.")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ type DNSResolver interface {
|
|||
LookupTXT(string) ([]string, time.Duration, error)
|
||||
LookupHost(string) ([]net.IP, time.Duration, time.Duration, error)
|
||||
LookupCNAME(string) (string, time.Duration, error)
|
||||
LookupDNAME(string) (string, time.Duration, error)
|
||||
LookupCAA(string) ([]*dns.CAA, time.Duration, error)
|
||||
LookupMX(string) ([]string, time.Duration, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -450,11 +450,11 @@ type Certificate struct {
|
|||
// * "revoked" - revoked
|
||||
Status AcmeStatus `db:"status"`
|
||||
|
||||
Serial string `db:"serial"`
|
||||
Digest string `db:"digest"`
|
||||
DER JSONBuffer `db:"der"`
|
||||
Issued time.Time `db:"issued"`
|
||||
Expires time.Time `db:"expires"`
|
||||
Serial string `db:"serial"`
|
||||
Digest string `db:"digest"`
|
||||
DER []byte `db:"der"`
|
||||
Issued time.Time `db:"issued"`
|
||||
Expires time.Time `db:"expires"`
|
||||
}
|
||||
|
||||
// MatchesCSR tests the contents of a generated certificate to make sure
|
||||
|
|
|
|||
|
|
@ -54,13 +54,13 @@ func TestBuildID(t *testing.T) {
|
|||
const JWK1JSON = `{
|
||||
"kty": "RSA",
|
||||
"n": "vuc785P8lBj3fUxyZchF_uZw6WtbxcorqgTyq-qapF5lrO1U82Tp93rpXlmctj6fyFHBVVB5aXnUHJ7LZeVPod7Wnfl8p5OyhlHQHC8BnzdzCqCMKmWZNX5DtETDId0qzU7dPzh0LP0idt5buU7L9QNaabChw3nnaL47iu_1Di5Wp264p2TwACeedv2hfRDjDlJmaQXuS8Rtv9GnRWyC9JBu7XmGvGDziumnJH7Hyzh3VNu-kSPQD3vuAFgMZS6uUzOztCkT0fpOalZI6hqxtWLvXUMj-crXrn-Maavz8qRhpAyp5kcYk3jiHGgQIi7QSK2JIdRJ8APyX9HlmTN5AQ",
|
||||
"e": "AAEAAQ"
|
||||
"e": "AQAB"
|
||||
}`
|
||||
const JWK1Digest = `ul04Iq07ulKnnrebv2hv3yxCGgVvoHs8hjq2tVKx3mc=`
|
||||
const JWK2JSON = `{
|
||||
"kty":"RSA",
|
||||
"n":"yTsLkI8n4lg9UuSKNRC0UPHsVjNdCYk8rGXIqeb_rRYaEev3D9-kxXY8HrYfGkVt5CiIVJ-n2t50BKT8oBEMuilmypSQqJw0pCgtUm-e6Z0Eg3Ly6DMXFlycyikegiZ0b-rVX7i5OCEZRDkENAYwFNX4G7NNCwEZcH7HUMUmty9dchAqDS9YWzPh_dde1A9oy9JMH07nRGDcOzIh1rCPwc71nwfPPYeeS4tTvkjanjeigOYBFkBLQuv7iBB4LPozsGF1XdoKiIIi-8ye44McdhOTPDcQp3xKxj89aO02pQhBECv61rmbPinvjMG9DYxJmZvjsKF4bN2oy0DxdC1jDw",
|
||||
"e":"AAEAAQ"
|
||||
"e":"AQAB"
|
||||
}`
|
||||
|
||||
func TestKeyDigest(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"database/sql"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
// Load SQLite3 for test purposes
|
||||
|
|
@ -75,14 +76,56 @@ func (mock *MockDNS) LookupHost(hostname string) ([]net.IP, time.Duration, time.
|
|||
|
||||
// LookupCNAME is a mock
|
||||
func (mock *MockDNS) LookupCNAME(domain string) (string, time.Duration, error) {
|
||||
return "hostname", 0, nil
|
||||
switch strings.TrimRight(domain, ".") {
|
||||
case "cname-absent.com":
|
||||
return "absent.com.", 30, nil
|
||||
case "cname-critical.com":
|
||||
return "critical.com.", 30, nil
|
||||
case "cname-present.com", "cname-and-dname.com":
|
||||
return "cname-target.present.com.", 30, nil
|
||||
case "cname2-present.com":
|
||||
return "cname-present.com.", 30, nil
|
||||
case "a.cname-loop.com":
|
||||
return "b.cname-loop.com.", 30, nil
|
||||
case "b.cname-loop.com":
|
||||
return "a.cname-loop.com.", 30, nil
|
||||
case "www.caa-loop.com":
|
||||
// nothing wrong with CNAME, but prevents CAA algorithm from terminating
|
||||
return "oops.www.caa-loop.com.", 30, nil
|
||||
case "cname2servfail.com":
|
||||
return "servfail.com.", 30, nil
|
||||
case "cname-servfail.com":
|
||||
return "", 0, fmt.Errorf("SERVFAIL")
|
||||
case "cname2dname.com":
|
||||
return "dname2cname.com.", 30, nil
|
||||
default:
|
||||
return "", 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
// LookupDNAME is a mock
|
||||
func (mock *MockDNS) LookupDNAME(domain string) (string, time.Duration, error) {
|
||||
switch strings.TrimRight(domain, ".") {
|
||||
case "cname-and-dname.com", "dname-present.com":
|
||||
return "dname-target.present.com.", time.Minute, nil
|
||||
case "a.dname-loop.com":
|
||||
return "b.dname-loop.com.", time.Minute, nil
|
||||
case "b.dname-loop.com":
|
||||
return "a.dname-loop.com.", time.Minute, nil
|
||||
case "dname2cname.com":
|
||||
return "cname2-present.com.", time.Minute, nil
|
||||
case "dname-servfail.com":
|
||||
return "", time.Minute, fmt.Errorf("SERVFAIL")
|
||||
default:
|
||||
return "", 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
// LookupCAA is a mock
|
||||
func (mock *MockDNS) LookupCAA(domain string) ([]*dns.CAA, time.Duration, error) {
|
||||
var results []*dns.CAA
|
||||
var record dns.CAA
|
||||
switch domain {
|
||||
switch strings.TrimRight(domain, ".") {
|
||||
case "reserved.com":
|
||||
record.Tag = "issue"
|
||||
record.Value = "symantec.com"
|
||||
|
|
@ -104,7 +147,7 @@ func (mock *MockDNS) LookupCAA(domain string) ([]*dns.CAA, time.Duration, error)
|
|||
|
||||
// LookupMX is a mock
|
||||
func (mock *MockDNS) LookupMX(domain string) ([]string, time.Duration, error) {
|
||||
switch domain {
|
||||
switch strings.TrimRight(domain, ".") {
|
||||
case "letsencrypt.org":
|
||||
fallthrough
|
||||
case "email.com":
|
||||
|
|
|
|||
|
|
@ -57,14 +57,14 @@ var (
|
|||
AccountKeyJSONB = []byte(`{
|
||||
"kty":"RSA",
|
||||
"n":"z8bp-jPtHt4lKBqepeKF28g_QAEOuEsCIou6sZ9ndsQsEjxEOQxQ0xNOQezsKa63eogw8YS3vzjUcPP5BJuVzfPfGd5NVUdT-vSSwxk3wvk_jtNqhrpcoG0elRPQfMVsQWmxCAXCVRz3xbcFI8GTe-syynG3l-g1IzYIIZVNI6jdljCZML1HOMTTW4f7uJJ8mM-08oQCeHbr5ejK7O2yMSSYxW03zY-Tj1iVEebROeMv6IEEJNFSS4yM-hLpNAqVuQxFGetwtwjDMC1Drs1dTWrPuUAAjKGrP151z1_dE74M5evpAhZUmpKv1hY-x85DC6N0hFPgowsanmTNNiV75w",
|
||||
"e":"AAEAAQ"
|
||||
"e":"AQAB"
|
||||
}`)
|
||||
AccountKeyB = jose.JsonWebKey{}
|
||||
|
||||
AccountKeyJSONC = []byte(`{
|
||||
"kty":"RSA",
|
||||
"n":"rFH5kUBZrlPj73epjJjyCxzVzZuV--JjKgapoqm9pOuOt20BUTdHqVfC2oDclqM7HFhkkX9OSJMTHgZ7WaVqZv9u1X2yjdx9oVmMLuspX7EytW_ZKDZSzL-sCOFCuQAuYKkLbsdcA3eHBK_lwc4zwdeHFMKIulNvLqckkqYB9s8GpgNXBDIQ8GjR5HuJke_WUNjYHSd8jY1LU9swKWsLQe2YoQUz_ekQvBvBCoaFEtrtRaSJKNLIVDObXFr2TLIiFiM0Em90kK01-eQ7ZiruZTKomll64bRFPoNo4_uwubddg3xTqur2vdF3NyhTrYdvAgTem4uC0PFjEQ1bK_djBQ",
|
||||
"e":"AAEAAQ"
|
||||
"e":"AQAB"
|
||||
}`)
|
||||
AccountKeyC = jose.JsonWebKey{}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ var log = mocks.UseMockLog()
|
|||
const JWK1JSON = `{
|
||||
"kty": "RSA",
|
||||
"n": "vuc785P8lBj3fUxyZchF_uZw6WtbxcorqgTyq-qapF5lrO1U82Tp93rpXlmctj6fyFHBVVB5aXnUHJ7LZeVPod7Wnfl8p5OyhlHQHC8BnzdzCqCMKmWZNX5DtETDId0qzU7dPzh0LP0idt5buU7L9QNaabChw3nnaL47iu_1Di5Wp264p2TwACeedv2hfRDjDlJmaQXuS8Rtv9GnRWyC9JBu7XmGvGDziumnJH7Hyzh3VNu-kSPQD3vuAFgMZS6uUzOztCkT0fpOalZI6hqxtWLvXUMj-crXrn-Maavz8qRhpAyp5kcYk3jiHGgQIi7QSK2JIdRJ8APyX9HlmTN5AQ",
|
||||
"e": "AAEAAQ"
|
||||
"e": "AQAB"
|
||||
}`
|
||||
|
||||
type MockRPCClient struct {
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ func (ssa *SQLStorageAuthority) UpdateOCSP(serial string, ocspResponse []byte) (
|
|||
|
||||
// Reset the update clock
|
||||
status.OCSPLastUpdated = timeStamp
|
||||
_, err = tx.Update(status)
|
||||
_, err = tx.Update(&status)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -211,3 +211,32 @@ func TestDeniedCSR(t *testing.T) {
|
|||
test.AssertNotError(t, err, "AlreadyDeniedCSR failed")
|
||||
test.Assert(t, !exists, "Found non-existent CSR")
|
||||
}
|
||||
|
||||
func TestUpdateOCSP(t *testing.T) {
|
||||
sa := initSA(t)
|
||||
|
||||
// Add a cert to the DB to test with.
|
||||
certDER, err := ioutil.ReadFile("www.eff.org.der")
|
||||
test.AssertNotError(t, err, "Couldn't read example cert DER")
|
||||
_, err = sa.AddCertificate(certDER, 1)
|
||||
test.AssertNotError(t, err, "Couldn't add www.eff.org.der")
|
||||
|
||||
serial := "00000000000000000000000000021bd4"
|
||||
const ocspResponse = "this is a fake OCSP response"
|
||||
err = sa.UpdateOCSP(serial, []byte(ocspResponse))
|
||||
test.AssertNotError(t, err, "UpdateOCSP failed")
|
||||
|
||||
certificateStatusObj, err := sa.dbMap.Get(core.CertificateStatus{}, serial)
|
||||
certificateStatus := certificateStatusObj.(*core.CertificateStatus)
|
||||
test.AssertNotError(t, err, "Failed to fetch certificate status")
|
||||
test.Assert(t,
|
||||
certificateStatus.OCSPLastUpdated.After(time.Now().Add(-time.Second)),
|
||||
"OCSP last updated too old.")
|
||||
|
||||
var fetchedOcspResponse core.OCSPResponse
|
||||
err = sa.dbMap.SelectOne(&fetchedOcspResponse,
|
||||
`SELECT * from ocspResponses where serial = ? order by createdAt DESC limit 1;`,
|
||||
serial)
|
||||
test.AssertNotError(t, err, "Failed to fetch OCSP response")
|
||||
test.AssertEquals(t, ocspResponse, string(fetchedOcspResponse.Response))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import (
|
|||
const JWK1JSON = `{
|
||||
"kty": "RSA",
|
||||
"n": "vuc785P8lBj3fUxyZchF_uZw6WtbxcorqgTyq-qapF5lrO1U82Tp93rpXlmctj6fyFHBVVB5aXnUHJ7LZeVPod7Wnfl8p5OyhlHQHC8BnzdzCqCMKmWZNX5DtETDId0qzU7dPzh0LP0idt5buU7L9QNaabChw3nnaL47iu_1Di5Wp264p2TwACeedv2hfRDjDlJmaQXuS8Rtv9GnRWyC9JBu7XmGvGDziumnJH7Hyzh3VNu-kSPQD3vuAFgMZS6uUzOztCkT0fpOalZI6hqxtWLvXUMj-crXrn-Maavz8qRhpAyp5kcYk3jiHGgQIi7QSK2JIdRJ8APyX9HlmTN5AQ",
|
||||
"e": "AAEAAQ"
|
||||
"e": "AQAB"
|
||||
}`
|
||||
|
||||
func TestAcmeIdentifier(t *testing.T) {
|
||||
|
|
|
|||
53
start.py
53
start.py
|
|
@ -3,46 +3,29 @@
|
|||
Run a local instance of Boulder for testing purposes.
|
||||
|
||||
This runs in non-monolithic mode and requires RabbitMQ on localhost.
|
||||
|
||||
Keeps servers alive until ^C. Exit non-zero if any servers fail to
|
||||
start, or die before ^C.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
tempdir = tempfile.mkdtemp()
|
||||
sys.path.append('./test')
|
||||
import startservers
|
||||
|
||||
config = os.environ.get('BOULDER_CONFIG')
|
||||
if config is None:
|
||||
config = 'test/boulder-config.json'
|
||||
|
||||
def run(path):
|
||||
binary = os.path.join(tempdir, os.path.basename(path))
|
||||
cmd = 'go build -o %s %s' % (binary, path)
|
||||
print(cmd)
|
||||
if subprocess.Popen(cmd, shell=True).wait() != 0:
|
||||
sys.exit(1)
|
||||
return subprocess.Popen('''
|
||||
exec %s --config %s
|
||||
''' % (binary, config), shell=True)
|
||||
|
||||
processes = []
|
||||
|
||||
def start():
|
||||
global processes
|
||||
processes = [
|
||||
run('./cmd/boulder-wfe'),
|
||||
run('./cmd/boulder-ra'),
|
||||
run('./cmd/boulder-sa'),
|
||||
run('./cmd/boulder-ca'),
|
||||
run('./cmd/boulder-va')]
|
||||
time.sleep(100000)
|
||||
|
||||
if not startservers.start():
|
||||
sys.exit(1)
|
||||
try:
|
||||
start()
|
||||
finally:
|
||||
for p in processes:
|
||||
if p.poll() is None:
|
||||
p.kill()
|
||||
time.sleep(1)
|
||||
print("All servers are running. To stop, hit ^C.")
|
||||
|
||||
os.wait()
|
||||
|
||||
# If we reach here, a child died early. Log what died:
|
||||
startservers.check()
|
||||
sys.exit(1)
|
||||
except KeyboardInterrupt:
|
||||
print "\nstopping servers."
|
||||
|
|
|
|||
|
|
@ -1,26 +1,18 @@
|
|||
#!/usr/bin/env python2.7
|
||||
import atexit
|
||||
import os
|
||||
import shutil
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
tempdir = tempfile.mkdtemp()
|
||||
import startservers
|
||||
|
||||
|
||||
class ExitStatus:
|
||||
OK, PythonFailure, NodeFailure, Error = range(4)
|
||||
|
||||
exit_status = 0
|
||||
|
||||
def die(status):
|
||||
global exit_status
|
||||
exit_status = status
|
||||
sys.exit(1)
|
||||
|
||||
processes = []
|
||||
|
||||
|
||||
class ProcInfo:
|
||||
"""
|
||||
|
|
@ -33,31 +25,18 @@ class ProcInfo:
|
|||
self.cmd = cmd
|
||||
self.proc = proc
|
||||
|
||||
def run(path, args=""):
|
||||
global processes
|
||||
binary = os.path.join(tempdir, os.path.basename(path))
|
||||
cmd = 'GORACE="halt_on_error=1" go build -race -o %s %s' % (binary, path)
|
||||
print(cmd)
|
||||
if subprocess.Popen(cmd, shell=True).wait() != 0:
|
||||
die(ExitStatus.Error)
|
||||
runCmd = "exec %s %s" % (binary, args)
|
||||
print(runCmd)
|
||||
info = ProcInfo(runCmd, subprocess.Popen(runCmd, shell=True))
|
||||
processes.append(info)
|
||||
return info
|
||||
|
||||
def start():
|
||||
run('./cmd/boulder-wfe', '--config test/boulder-test-config.json')
|
||||
run('./cmd/boulder-ra', '--config test/boulder-test-config.json')
|
||||
run('./cmd/boulder-sa', '--config test/boulder-test-config.json')
|
||||
run('./cmd/boulder-ca', '--config test/boulder-test-config.json')
|
||||
run('./cmd/boulder-va', '--config test/boulder-test-config.json')
|
||||
run('./test/dns-test-srv')
|
||||
def die(status):
|
||||
global exit_status
|
||||
# Set exit_status so cleanup handler knows what to report.
|
||||
exit_status = status
|
||||
sys.exit(exit_status)
|
||||
|
||||
|
||||
def run_node_test():
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
s.connect(('localhost', 4300))
|
||||
s.connect(('localhost', 4000))
|
||||
except socket.error, e:
|
||||
print("Cannot connect to WFE")
|
||||
die(ExitStatus.Error)
|
||||
|
|
@ -69,57 +48,46 @@ def run_node_test():
|
|||
die(ExitStatus.Error)
|
||||
if subprocess.Popen('''
|
||||
node test.js --email foo@letsencrypt.org --agree true \
|
||||
--domains foo.com --new-reg http://localhost:4300/acme/new-reg \
|
||||
--domains foo.com --new-reg http://localhost:4000/acme/new-reg \
|
||||
--certKey %s/key.pem --cert %s/cert.der
|
||||
''' % (tempdir, tempdir), shell=True).wait() != 0:
|
||||
print("\nIssuing failed")
|
||||
die(ExitStatus.NodeFailure)
|
||||
if subprocess.Popen('''
|
||||
node revoke.js %s/cert.der %s/key.pem http://localhost:4300/acme/revoke-cert
|
||||
node revoke.js %s/cert.der %s/key.pem http://localhost:4000/acme/revoke-cert
|
||||
''' % (tempdir, tempdir), shell=True).wait() != 0:
|
||||
print("\nRevoking failed")
|
||||
die(ExitStatus.NodeFailure)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def run_client_tests():
|
||||
root = os.environ.get("LETSENCRYPT_PATH")
|
||||
assert root is not None, (
|
||||
"Please set LETSENCRYPT_PATH env variable to point at "
|
||||
"initialized (virtualenv) client repo root")
|
||||
os.environ['SERVER'] = 'http://localhost:4300/acme/new-reg'
|
||||
os.environ['SERVER'] = 'http://localhost:4000/acme/new-reg'
|
||||
test_script_path = os.path.join(root, 'tests', 'boulder-integration.sh')
|
||||
if subprocess.Popen(test_script_path, shell=True, cwd=root).wait() != 0:
|
||||
die(ExitStatus.PythonFailure)
|
||||
|
||||
try:
|
||||
start()
|
||||
busted = []
|
||||
for pinfo in processes:
|
||||
if pinfo.proc.poll() is not None:
|
||||
busted.append(pinfo)
|
||||
if len(busted) != 0:
|
||||
print "\n\nThese processes didn't start up successfully (check above for their output):"
|
||||
for pinfo in busted:
|
||||
print "\t'%s' exited with %d" % (pinfo.cmd, pinfo.proc.returncode)
|
||||
sys.exit(1)
|
||||
|
||||
run_node_test()
|
||||
run_client_tests()
|
||||
except Exception as e:
|
||||
exit_status = ExitStatus.Error
|
||||
print e
|
||||
finally:
|
||||
for pinfo in processes:
|
||||
if pinfo.proc.poll() is None:
|
||||
pinfo.proc.kill()
|
||||
else:
|
||||
exit_status = 1
|
||||
|
||||
@atexit.register
|
||||
def cleanup():
|
||||
import shutil
|
||||
shutil.rmtree(tempdir)
|
||||
|
||||
if exit_status == 0:
|
||||
if exit_status == ExitStatus.OK:
|
||||
print("\n\nSUCCESS")
|
||||
else:
|
||||
print("\n\nFAILURE")
|
||||
sys.exit(exit_status)
|
||||
|
||||
|
||||
exit_status = ExitStatus.OK
|
||||
tempdir = tempfile.mkdtemp()
|
||||
if not startservers.start():
|
||||
die(ExitStatus.Error)
|
||||
run_node_test()
|
||||
run_client_tests()
|
||||
if not startservers.check():
|
||||
die(ExitStatus.Error)
|
||||
|
|
|
|||
|
|
@ -172,5 +172,5 @@
|
|||
"dnsTimeout": "10s"
|
||||
},
|
||||
|
||||
"subscriberAgreementURL": "http://localhost:4300/terms"
|
||||
"subscriberAgreementURL": "http://localhost:4001/terms/v1"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
import atexit
|
||||
import BaseHTTPServer
|
||||
import os
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
import tempfile
|
||||
import threading
|
||||
|
||||
|
||||
class ToSServerThread(threading.Thread):
|
||||
class ToSHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
self.wfile.write("Do What Ye Will (An it Harm None).\n")
|
||||
def run(self):
|
||||
BaseHTTPServer.HTTPServer(("localhost", 4001), self.ToSHandler).serve_forever()
|
||||
|
||||
|
||||
config = os.environ.get('BOULDER_CONFIG')
|
||||
if config is None:
|
||||
config = 'test/boulder-config.json'
|
||||
processes = []
|
||||
tempdir = tempfile.mkdtemp()
|
||||
|
||||
|
||||
def run(path):
|
||||
binary = os.path.join(tempdir, os.path.basename(path))
|
||||
|
||||
buildcmd = 'GORACE="halt_on_error=1" go build -race -o %s ./%s' % (binary, path)
|
||||
print(buildcmd)
|
||||
subprocess.check_call(buildcmd, shell=True)
|
||||
|
||||
srvcmd = [binary, '--config', config]
|
||||
p = subprocess.Popen(srvcmd)
|
||||
p.cmd = srvcmd
|
||||
print('started %s with pid %d' % (p.cmd, p.pid))
|
||||
return p
|
||||
|
||||
|
||||
def start():
|
||||
"""Return True if everything builds and starts.
|
||||
|
||||
Give up and return False if anything fails to build, or dies at
|
||||
startup. Anything that did start before this point can be cleaned
|
||||
up explicitly by calling stop(), or automatically atexit.
|
||||
"""
|
||||
global processes
|
||||
t = ToSServerThread()
|
||||
t.daemon = True
|
||||
t.start()
|
||||
for prog in [
|
||||
'cmd/boulder-wfe',
|
||||
'cmd/boulder-ra',
|
||||
'cmd/boulder-sa',
|
||||
'cmd/boulder-ca',
|
||||
'cmd/boulder-va',
|
||||
'test/dns-test-srv']:
|
||||
try:
|
||||
processes.append(run(prog))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
if not check():
|
||||
# Don't keep building stuff if a server has already died.
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def check():
|
||||
"""Return true if all started processes are still alive.
|
||||
|
||||
Log about anything that died.
|
||||
"""
|
||||
global processes
|
||||
busted = []
|
||||
stillok = []
|
||||
for p in processes:
|
||||
if p.poll() is None:
|
||||
stillok.append(p)
|
||||
else:
|
||||
busted.append(p)
|
||||
if busted:
|
||||
print "\n\nThese processes exited early (check above for their output):"
|
||||
for p in busted:
|
||||
print "\t'%s' with pid %d exited %d" % (p.cmd, p.pid, p.returncode)
|
||||
processes = stillok
|
||||
return not busted
|
||||
|
||||
|
||||
@atexit.register
|
||||
def stop():
|
||||
for p in processes:
|
||||
if p.poll() is None:
|
||||
p.kill()
|
||||
shutil.rmtree(tempdir)
|
||||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
|
|
@ -24,6 +25,12 @@ import (
|
|||
"github.com/letsencrypt/boulder/policy"
|
||||
)
|
||||
|
||||
const maxCNAME = 16 // Prevents infinite loops. Same limit as BIND.
|
||||
|
||||
// Returned by CheckCAARecords if it has to follow too many
|
||||
// consecutive CNAME lookups.
|
||||
var ErrTooManyCNAME = errors.New("too many CNAME/DNAME lookups")
|
||||
|
||||
// ValidationAuthorityImpl represents a VA
|
||||
type ValidationAuthorityImpl struct {
|
||||
RA core.RegistrationAuthority
|
||||
|
|
@ -510,36 +517,50 @@ func newCAASet(CAAs []*dns.CAA) *CAASet {
|
|||
}
|
||||
|
||||
func (va *ValidationAuthorityImpl) getCAASet(hostname string) (*CAASet, error) {
|
||||
hostname = strings.TrimRight(hostname, ".")
|
||||
splitDomain := strings.Split(hostname, ".")
|
||||
// RFC 6844 CAA set query sequence, 'x.y.z.com' => ['x.y.z.com', 'y.z.com', 'z.com']
|
||||
for i := range splitDomain {
|
||||
queryDomain := strings.Join(splitDomain[i:], ".")
|
||||
// Don't query a public suffix
|
||||
if _, present := policy.PublicSuffixList[queryDomain]; present {
|
||||
label := strings.TrimRight(hostname, ".")
|
||||
cnames := 0
|
||||
// See RFC 6844 "Certification Authority Processing" for pseudocode.
|
||||
for {
|
||||
if strings.IndexRune(label, '.') == -1 {
|
||||
// Reached TLD
|
||||
break
|
||||
}
|
||||
|
||||
// Query CAA records for domain and its alias if it has a CNAME
|
||||
for _, alias := range []bool{false, true} {
|
||||
if alias {
|
||||
target, _, err := va.DNSResolver.LookupCNAME(queryDomain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queryDomain = target
|
||||
}
|
||||
CAAs, _, err := va.DNSResolver.LookupCAA(queryDomain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(CAAs) > 0 {
|
||||
return newCAASet(CAAs), nil
|
||||
}
|
||||
if _, present := policy.PublicSuffixList[label]; present {
|
||||
break
|
||||
}
|
||||
CAAs, _, err := va.DNSResolver.LookupCAA(label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(CAAs) > 0 {
|
||||
return newCAASet(CAAs), nil
|
||||
}
|
||||
cname, _, err := va.DNSResolver.LookupCNAME(label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dname, _, err := va.DNSResolver.LookupDNAME(label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cname == "" && dname == "" {
|
||||
// Try parent domain (note we confirmed
|
||||
// earlier that label contains '.')
|
||||
label = label[strings.IndexRune(label, '.')+1:]
|
||||
continue
|
||||
}
|
||||
if cname != "" && dname != "" && cname != dname {
|
||||
return nil, errors.New("both CNAME and DNAME exist for " + label)
|
||||
}
|
||||
if cname != "" {
|
||||
label = cname
|
||||
} else {
|
||||
label = dname
|
||||
}
|
||||
if cnames++; cnames > maxCNAME {
|
||||
return nil, ErrTooManyCNAME
|
||||
}
|
||||
}
|
||||
|
||||
// no CAA records found
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -591,10 +591,23 @@ func TestCAAChecking(t *testing.T) {
|
|||
CAATest{"reserved.com", true, false},
|
||||
// Critical
|
||||
CAATest{"critical.com", true, false},
|
||||
CAATest{"nx.critical.com", true, false},
|
||||
CAATest{"cname-critical.com", true, false},
|
||||
CAATest{"nx.cname-critical.com", true, false},
|
||||
// Good (absent)
|
||||
CAATest{"absent.com", false, true},
|
||||
CAATest{"cname-absent.com", false, true},
|
||||
CAATest{"nx.cname-absent.com", false, true},
|
||||
CAATest{"cname-nx.com", false, true},
|
||||
CAATest{"example.co.uk", false, true},
|
||||
// Good (present)
|
||||
CAATest{"present.com", true, true},
|
||||
CAATest{"cname-present.com", true, true},
|
||||
CAATest{"cname2-present.com", true, true},
|
||||
CAATest{"nx.cname2-present.com", true, true},
|
||||
CAATest{"dname-present.com", true, true},
|
||||
CAATest{"dname2cname.com", true, true},
|
||||
// CNAME to critical
|
||||
}
|
||||
|
||||
va := NewValidationAuthorityImpl(true)
|
||||
|
|
@ -612,6 +625,20 @@ func TestCAAChecking(t *testing.T) {
|
|||
test.AssertError(t, err, "servfail.com")
|
||||
test.Assert(t, !present, "Present should be false")
|
||||
test.Assert(t, !valid, "Valid should be false")
|
||||
|
||||
for _, name := range []string{
|
||||
"www.caa-loop.com",
|
||||
"a.cname-loop.com",
|
||||
"a.dname-loop.com",
|
||||
"cname-servfail.com",
|
||||
"cname2servfail.com",
|
||||
"dname-servfail.com",
|
||||
"cname-and-dname.com",
|
||||
"servfail.com",
|
||||
} {
|
||||
_, _, err = va.CheckCAARecords(core.AcmeIdentifier{Type: "dns", Value: name})
|
||||
test.AssertError(t, err, name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNSValidationFailure(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
|
@ -220,7 +219,7 @@ func (wfe *WebFrontEndImpl) Handler() (http.Handler, error) {
|
|||
wfe.HandleFunc(m, NewCertPath, wfe.NewCertificate, "POST")
|
||||
wfe.HandleFunc(m, RegPath, wfe.Registration, "POST")
|
||||
wfe.HandleFunc(m, AuthzPath, wfe.Authorization, "GET", "POST")
|
||||
wfe.HandleFunc(m, CertPath, wfe.Certificate, "GET", "POST")
|
||||
wfe.HandleFunc(m, CertPath, wfe.Certificate, "GET")
|
||||
wfe.HandleFunc(m, RevokeCertPath, wfe.RevokeCertificate, "POST")
|
||||
wfe.HandleFunc(m, TermsPath, wfe.Terms, "GET")
|
||||
wfe.HandleFunc(m, IssuerPath, wfe.Issuer, "GET")
|
||||
|
|
@ -911,6 +910,10 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *
|
|||
return
|
||||
}
|
||||
response.Header().Set("Content-Type", "application/json")
|
||||
response.Header().Add("Link", link(wfe.NewAuthz, "next"))
|
||||
if len(wfe.SubscriberAgreementURL) > 0 {
|
||||
response.Header().Add("Link", link(wfe.SubscriberAgreementURL, "terms-of-service"))
|
||||
}
|
||||
response.WriteHeader(http.StatusAccepted)
|
||||
response.Write(jsonReply)
|
||||
}
|
||||
|
|
@ -942,9 +945,9 @@ func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request
|
|||
return
|
||||
}
|
||||
|
||||
// Blank out ID and regID
|
||||
switch request.Method {
|
||||
case "GET":
|
||||
// Blank out ID and regID
|
||||
authz.ID = ""
|
||||
authz.RegistrationID = 0
|
||||
|
||||
|
|
@ -974,54 +977,47 @@ func (wfe *WebFrontEndImpl) Certificate(response http.ResponseWriter, request *h
|
|||
defer wfe.logRequestDetails(&logEvent)
|
||||
|
||||
path := request.URL.Path
|
||||
switch request.Method {
|
||||
case "GET":
|
||||
// Certificate paths consist of the CertBase path, plus exactly sixteen hex
|
||||
// digits.
|
||||
if !strings.HasPrefix(path, CertPath) {
|
||||
logEvent.Error = "Certificate not found"
|
||||
wfe.sendError(response, logEvent.Error, path, http.StatusNotFound)
|
||||
addNoCacheHeader(response)
|
||||
return
|
||||
}
|
||||
serial := path[len(CertPath):]
|
||||
if len(serial) != 16 || !allHex.Match([]byte(serial)) {
|
||||
logEvent.Error = "Certificate not found"
|
||||
wfe.sendError(response, logEvent.Error, serial, http.StatusNotFound)
|
||||
addNoCacheHeader(response)
|
||||
return
|
||||
}
|
||||
wfe.log.Debug(fmt.Sprintf("Requested certificate ID %s", serial))
|
||||
logEvent.Extra["RequestedSerial"] = serial
|
||||
|
||||
cert, err := wfe.SA.GetCertificateByShortSerial(serial)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
if strings.HasPrefix(err.Error(), "gorp: multiple rows returned") {
|
||||
wfe.sendError(response, "Multiple certificates with same short serial", err, http.StatusConflict)
|
||||
} else {
|
||||
addNoCacheHeader(response)
|
||||
wfe.sendError(response, "Certificate not found", err, http.StatusNotFound)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
addCacheHeader(response, wfe.CertCacheDuration.Seconds())
|
||||
|
||||
// TODO Content negotiation
|
||||
response.Header().Set("Content-Type", "application/pkix-cert")
|
||||
response.Header().Add("Link", link(IssuerPath, "up"))
|
||||
response.WriteHeader(http.StatusOK)
|
||||
if _, err = response.Write(cert.DER); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
|
||||
}
|
||||
return
|
||||
case "POST":
|
||||
logEvent.Error = "Not yet supported"
|
||||
wfe.sendError(response, logEvent.Error, "", http.StatusNotFound)
|
||||
// Certificate paths consist of the CertBase path, plus exactly sixteen hex
|
||||
// digits.
|
||||
if !strings.HasPrefix(path, CertPath) {
|
||||
logEvent.Error = "Certificate not found"
|
||||
wfe.sendError(response, logEvent.Error, path, http.StatusNotFound)
|
||||
addNoCacheHeader(response)
|
||||
return
|
||||
}
|
||||
serial := path[len(CertPath):]
|
||||
if len(serial) != 16 || !allHex.Match([]byte(serial)) {
|
||||
logEvent.Error = "Certificate not found"
|
||||
wfe.sendError(response, logEvent.Error, serial, http.StatusNotFound)
|
||||
addNoCacheHeader(response)
|
||||
return
|
||||
}
|
||||
wfe.log.Debug(fmt.Sprintf("Requested certificate ID %s", serial))
|
||||
logEvent.Extra["RequestedSerial"] = serial
|
||||
|
||||
cert, err := wfe.SA.GetCertificateByShortSerial(serial)
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
if strings.HasPrefix(err.Error(), "gorp: multiple rows returned") {
|
||||
wfe.sendError(response, "Multiple certificates with same short serial", err, http.StatusConflict)
|
||||
} else {
|
||||
addNoCacheHeader(response)
|
||||
wfe.sendError(response, "Certificate not found", err, http.StatusNotFound)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
addCacheHeader(response, wfe.CertCacheDuration.Seconds())
|
||||
|
||||
// TODO Content negotiation
|
||||
response.Header().Set("Content-Type", "application/pkix-cert")
|
||||
response.Header().Add("Link", link(IssuerPath, "up"))
|
||||
response.WriteHeader(http.StatusOK)
|
||||
if _, err = response.Write(cert.DER); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Terms is used by the client to obtain the current Terms of Service /
|
||||
|
|
@ -1056,7 +1052,7 @@ func (wfe *WebFrontEndImpl) BuildID(response http.ResponseWriter, request *http.
|
|||
|
||||
response.Header().Set("Content-Type", "text/plain")
|
||||
response.WriteHeader(http.StatusOK)
|
||||
detailsString := fmt.Sprintf("Boulder=(%s %s) Golang=(%s) BuildHost=(%s)", core.GetBuildID(), core.GetBuildTime(), runtime.Version(), core.GetBuildHost())
|
||||
detailsString := fmt.Sprintf("Boulder=(%s %s)", core.GetBuildID(), core.GetBuildTime())
|
||||
if _, err := fmt.Fprintln(response, detailsString); err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ const (
|
|||
{
|
||||
"kty":"RSA",
|
||||
"n":"yNWVhtYEKJR21y9xsHV-PD_bYwbXSeNuFal46xYxVfRL5mqha7vttvjB_vc7Xg2RvgCxHPCqoxgMPTzHrZT75LjCwIW2K_klBYN8oYvTwwmeSkAz6ut7ZxPv-nZaT5TJhGk0NT2kh_zSpdriEJ_3vW-mqxYbbBmpvHqsa1_zx9fSuHYctAZJWzxzUZXykbWMWQZpEiE0J4ajj51fInEzVn7VxV-mzfMyboQjujPh7aNJxAWSq4oQEJJDgWwSh9leyoJoPpONHxh5nEE5AjE01FkGICSxjpZsF-w8hOTI3XXohUdu29Se26k2B0PolDSuj0GIQU6-W9TdLXSjBb2SpQ",
|
||||
"e":"AAEAAQ"
|
||||
"e":"AQAB"
|
||||
}`
|
||||
|
||||
test1KeyPrivatePEM = `
|
||||
|
|
@ -79,7 +79,7 @@ eROL1ve1vmQF3kjrMPhhK2kr6qdWnTE5XlPllVSZFQenSTzj98AO
|
|||
test2KeyPublicJSON = `{
|
||||
"kty":"RSA",
|
||||
"n":"qnARLrT7Xz4gRcKyLdydmCr-ey9OuPImX4X40thk3on26FkMznR3fRjs66eLK7mmPcBZ6uOJseURU6wAaZNmemoYx1dMvqvWWIyiQleHSD7Q8vBrhR6uIoO4jAzJZR-ChzZuSDt7iHN-3xUVspu5XGwXU_MVJZshTwp4TaFx5elHIT_ObnTvTOU3Xhish07AbgZKmWsVbXh5s-CrIicU4OexJPgunWZ_YJJueOKmTvnLlTV4MzKR2oZlBKZ27S0-SfdV_QDx_ydle5oMAyKVtlAV35cyPMIsYNwgUGBCdY_2Uzi5eX0lTc7MPRwz6qR1kip-i59VcGcUQgqHV6Fyqw",
|
||||
"e":"AAEAAQ"
|
||||
"e":"AQAB"
|
||||
}`
|
||||
|
||||
test2KeyPrivatePEM = `
|
||||
|
|
@ -306,7 +306,7 @@ func makeBody(s string) io.ReadCloser {
|
|||
}
|
||||
|
||||
func signRequest(t *testing.T, req string, nonceService *core.NonceService) string {
|
||||
accountKeyJSON := []byte(`{"kty":"RSA","n":"z2NsNdHeqAiGdPP8KuxfQXat_uatOK9y12SyGpfKw1sfkizBIsNxERjNDke6Wp9MugN9srN3sr2TDkmQ-gK8lfWo0v1uG_QgzJb1vBdf_hH7aejgETRGLNJZOdaKDsyFnWq1WGJq36zsHcd0qhggTk6zVwqczSxdiWIAZzEakIUZ13KxXvoepYLY0Q-rEEQiuX71e4hvhfeJ4l7m_B-awn22UUVvo3kCqmaRlZT-36vmQhDGoBsoUo1KBEU44jfeK5PbNRk7vDJuH0B7qinr_jczHcvyD-2TtPzKaCioMtNh_VZbPNDaG67sYkQlC15-Ff3HPzKKJW2XvkVG91qMvQ","e":"AAEAAQ","d":"BhAmDbzBAbCeHbU0Xhzi_Ar4M0eTMOEQPnPXMSfW6bc0SRW938JO_-z1scEvFY8qsxV_C0Zr7XHVZsmHz4dc9BVmhiSan36XpuOS85jLWaY073e7dUVN9-l-ak53Ys9f6KZB_v-BmGB51rUKGB70ctWiMJ1C0EzHv0h6Moog-LCd_zo03uuZD5F5wtnPrAB3SEM3vRKeZHzm5eiGxNUsaCEzGDApMYgt6YkQuUlkJwD8Ky2CkAE6lLQSPwddAfPDhsCug-12SkSIKw1EepSHz86ZVfJEnvY-h9jHIdI57mR1v7NTCDcWqy6c6qIzxwh8n2X94QTbtWT3vGQ6HXM5AQ","p":"2uhvZwNS5i-PzeI9vGx89XbdsVmeNjVxjH08V3aRBVY0dzUzwVDYk3z7sqBIj6de53Lx6W1hjmhPIqAwqQgjIKH5Z3uUCinGguKkfGDL3KgLCzYL2UIvZMvTzr9NWLc0AHMZdee5utxWKCGnZBOqy1Rd4V-6QrqjEDBvanoqA60","q":"8odNkMEiriaDKmvwDv-vOOu3LaWbu03yB7VhABu-hK5Xx74bHcvDP2HuCwDGGJY2H-xKdMdUPs0HPwbfHMUicD2vIEUDj6uyrMMZHtbcZ3moh3-WESg3TaEaJ6vhwcWXWG7Wc46G-HbCChkuVenFYYkoi68BAAjloqEUl1JBT1E"}`)
|
||||
accountKeyJSON := []byte(`{"kty":"RSA","n":"z2NsNdHeqAiGdPP8KuxfQXat_uatOK9y12SyGpfKw1sfkizBIsNxERjNDke6Wp9MugN9srN3sr2TDkmQ-gK8lfWo0v1uG_QgzJb1vBdf_hH7aejgETRGLNJZOdaKDsyFnWq1WGJq36zsHcd0qhggTk6zVwqczSxdiWIAZzEakIUZ13KxXvoepYLY0Q-rEEQiuX71e4hvhfeJ4l7m_B-awn22UUVvo3kCqmaRlZT-36vmQhDGoBsoUo1KBEU44jfeK5PbNRk7vDJuH0B7qinr_jczHcvyD-2TtPzKaCioMtNh_VZbPNDaG67sYkQlC15-Ff3HPzKKJW2XvkVG91qMvQ","e":"AQAB","d":"BhAmDbzBAbCeHbU0Xhzi_Ar4M0eTMOEQPnPXMSfW6bc0SRW938JO_-z1scEvFY8qsxV_C0Zr7XHVZsmHz4dc9BVmhiSan36XpuOS85jLWaY073e7dUVN9-l-ak53Ys9f6KZB_v-BmGB51rUKGB70ctWiMJ1C0EzHv0h6Moog-LCd_zo03uuZD5F5wtnPrAB3SEM3vRKeZHzm5eiGxNUsaCEzGDApMYgt6YkQuUlkJwD8Ky2CkAE6lLQSPwddAfPDhsCug-12SkSIKw1EepSHz86ZVfJEnvY-h9jHIdI57mR1v7NTCDcWqy6c6qIzxwh8n2X94QTbtWT3vGQ6HXM5AQ","p":"2uhvZwNS5i-PzeI9vGx89XbdsVmeNjVxjH08V3aRBVY0dzUzwVDYk3z7sqBIj6de53Lx6W1hjmhPIqAwqQgjIKH5Z3uUCinGguKkfGDL3KgLCzYL2UIvZMvTzr9NWLc0AHMZdee5utxWKCGnZBOqy1Rd4V-6QrqjEDBvanoqA60","q":"8odNkMEiriaDKmvwDv-vOOu3LaWbu03yB7VhABu-hK5Xx74bHcvDP2HuCwDGGJY2H-xKdMdUPs0HPwbfHMUicD2vIEUDj6uyrMMZHtbcZ3moh3-WESg3TaEaJ6vhwcWXWG7Wc46G-HbCChkuVenFYYkoi68BAAjloqEUl1JBT1E"}`)
|
||||
var accountKey jose.JsonWebKey
|
||||
err := json.Unmarshal(accountKeyJSON, &accountKey)
|
||||
test.AssertNotError(t, err, "Failed to unmarshal key")
|
||||
|
|
@ -425,7 +425,7 @@ func TestStandardHeaders(t *testing.T) {
|
|||
{wfe.NewAuthz, []string{"POST"}},
|
||||
{wfe.AuthzBase, []string{"GET", "POST"}},
|
||||
{wfe.NewCert, []string{"POST"}},
|
||||
{wfe.CertBase, []string{"GET", "POST"}},
|
||||
{wfe.CertBase, []string{"GET"}},
|
||||
{wfe.SubscriberAgreementURL, []string{"GET"}},
|
||||
}
|
||||
|
||||
|
|
@ -788,7 +788,7 @@ func TestNewRegistration(t *testing.T) {
|
|||
Body: makeBody(result.FullSerialize()),
|
||||
})
|
||||
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"id":0,"key":{"kty":"RSA","n":"qnARLrT7Xz4gRcKyLdydmCr-ey9OuPImX4X40thk3on26FkMznR3fRjs66eLK7mmPcBZ6uOJseURU6wAaZNmemoYx1dMvqvWWIyiQleHSD7Q8vBrhR6uIoO4jAzJZR-ChzZuSDt7iHN-3xUVspu5XGwXU_MVJZshTwp4TaFx5elHIT_ObnTvTOU3Xhish07AbgZKmWsVbXh5s-CrIicU4OexJPgunWZ_YJJueOKmTvnLlTV4MzKR2oZlBKZ27S0-SfdV_QDx_ydle5oMAyKVtlAV35cyPMIsYNwgUGBCdY_2Uzi5eX0lTc7MPRwz6qR1kip-i59VcGcUQgqHV6Fyqw","e":"AAEAAQ"},"recoveryToken":"","contact":["tel:123456789"],"agreement":"http://example.invalid/terms"}`)
|
||||
test.AssertEquals(t, responseWriter.Body.String(), `{"id":0,"key":{"kty":"RSA","n":"qnARLrT7Xz4gRcKyLdydmCr-ey9OuPImX4X40thk3on26FkMznR3fRjs66eLK7mmPcBZ6uOJseURU6wAaZNmemoYx1dMvqvWWIyiQleHSD7Q8vBrhR6uIoO4jAzJZR-ChzZuSDt7iHN-3xUVspu5XGwXU_MVJZshTwp4TaFx5elHIT_ObnTvTOU3Xhish07AbgZKmWsVbXh5s-CrIicU4OexJPgunWZ_YJJueOKmTvnLlTV4MzKR2oZlBKZ27S0-SfdV_QDx_ydle5oMAyKVtlAV35cyPMIsYNwgUGBCdY_2Uzi5eX0lTc7MPRwz6qR1kip-i59VcGcUQgqHV6Fyqw","e":"AQAB"},"recoveryToken":"","contact":["tel:123456789"],"agreement":"http://example.invalid/terms"}`)
|
||||
var reg core.Registration
|
||||
err = json.Unmarshal([]byte(responseWriter.Body.String()), ®)
|
||||
test.AssertNotError(t, err, "Couldn't unmarshal returned registration object")
|
||||
|
|
@ -799,6 +799,10 @@ func TestNewRegistration(t *testing.T) {
|
|||
test.AssertEquals(
|
||||
t, responseWriter.Header().Get("Location"),
|
||||
"/acme/reg/0")
|
||||
links := responseWriter.Header()["Link"]
|
||||
test.AssertEquals(t, contains(links, "</acme/new-authz>;rel=\"next\""), true)
|
||||
test.AssertEquals(t, contains(links, "<"+agreementURL+">;rel=\"terms-of-service\""), true)
|
||||
|
||||
test.AssertEquals(
|
||||
t, responseWriter.Header().Get("Link"),
|
||||
"</acme/new-authz>;rel=\"next\"")
|
||||
|
|
@ -1019,6 +1023,15 @@ func TestAuthorization(t *testing.T) {
|
|||
test.AssertNotError(t, err, "Couldn't unmarshal returned authorization object")
|
||||
}
|
||||
|
||||
func contains(s []string, e string) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestRegistration(t *testing.T) {
|
||||
wfe := setupWFE(t)
|
||||
mux, err := wfe.Handler()
|
||||
|
|
@ -1121,6 +1134,10 @@ func TestRegistration(t *testing.T) {
|
|||
URL: path,
|
||||
})
|
||||
test.AssertNotContains(t, responseWriter.Body.String(), "urn:acme:error")
|
||||
links := responseWriter.Header()["Link"]
|
||||
test.AssertEquals(t, contains(links, "</acme/new-authz>;rel=\"next\""), true)
|
||||
test.AssertEquals(t, contains(links, "<"+agreementURL+">;rel=\"terms-of-service\""), true)
|
||||
|
||||
responseWriter.Body.Reset()
|
||||
}
|
||||
|
||||
|
|
@ -1246,5 +1263,5 @@ func TestLogCsrPem(t *testing.T) {
|
|||
test.Assert(t, len(matches) == 1,
|
||||
"Incorrect number of certificate request log entries")
|
||||
test.AssertEquals(t, matches[0].Priority, syslog.LOG_NOTICE)
|
||||
test.AssertEquals(t, matches[0].Message, `[AUDIT] Certificate request JSON={"RemoteAddr":"12.34.98.76","CsrBase64":"MIICWTCCAUECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAycX3ca+fViOuRWF38mssORISFxbJvspDfhPGRBZDxJ63NIqQzupB+6dp48xkcX7Z/KDaRJStcpJT2S0u33moNT4FHLklQBETLhExDk66cmlz6Xibp3LGZAwhWuec7wJoEwIgY8oq4rxihIyGq7HVIJoq9DqZGrUgfZMDeEJqbphukQOaXGEop7mD+eeu8+z5EVkB1LiJ6Yej6R8MAhVPHzG5fyOu6YVo6vY6QgwjRLfZHNj5XthxgPIEETZlUbiSoI6J19GYHvLURBTy5Ys54lYAPIGfNwcIBAH4gtH9FrYcDY68R22rp4iuxdvkf03ZWiT0F2W1y7/C9B2jayTzvQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAHd6Do9DIZ2hvdt1GwBXYjsqprZidT/DYOMfYcK17KlvdkFT58XrBH88ulLZ72NXEpiFMeTyzfs3XEyGq/Bbe7TBGVYZabUEh+LOskYwhgcOuThVN7tHnH5rhN+gb7cEdysjTb1QL+vOUwYgV75CB6PE5JVYK+cQsMIVvo0Kz4TpNgjJnWzbcH7h0mtvub+fCv92vBPjvYq8gUDLNrok6rbg05tdOJkXsF2G/W+Q6sf2Fvx0bK5JeH4an7P7cXF9VG9nd4sRt5zd+L3IcyvHVKxNhIJXZVH0AOqh/1YrKI9R0QKQiZCEy0xN1okPlcaIVaFhb7IKAHPxTI3r5f72LXY=","Registration":{"id":789,"key":{"kty":"RSA","n":"yNWVhtYEKJR21y9xsHV-PD_bYwbXSeNuFal46xYxVfRL5mqha7vttvjB_vc7Xg2RvgCxHPCqoxgMPTzHrZT75LjCwIW2K_klBYN8oYvTwwmeSkAz6ut7ZxPv-nZaT5TJhGk0NT2kh_zSpdriEJ_3vW-mqxYbbBmpvHqsa1_zx9fSuHYctAZJWzxzUZXykbWMWQZpEiE0J4ajj51fInEzVn7VxV-mzfMyboQjujPh7aNJxAWSq4oQEJJDgWwSh9leyoJoPpONHxh5nEE5AjE01FkGICSxjpZsF-w8hOTI3XXohUdu29Se26k2B0PolDSuj0GIQU6-W9TdLXSjBb2SpQ","e":"AAEAAQ"},"recoveryToken":"","agreement":"http://example.invalid/terms"}}`)
|
||||
test.AssertEquals(t, matches[0].Message, `[AUDIT] Certificate request JSON={"RemoteAddr":"12.34.98.76","CsrBase64":"MIICWTCCAUECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAycX3ca+fViOuRWF38mssORISFxbJvspDfhPGRBZDxJ63NIqQzupB+6dp48xkcX7Z/KDaRJStcpJT2S0u33moNT4FHLklQBETLhExDk66cmlz6Xibp3LGZAwhWuec7wJoEwIgY8oq4rxihIyGq7HVIJoq9DqZGrUgfZMDeEJqbphukQOaXGEop7mD+eeu8+z5EVkB1LiJ6Yej6R8MAhVPHzG5fyOu6YVo6vY6QgwjRLfZHNj5XthxgPIEETZlUbiSoI6J19GYHvLURBTy5Ys54lYAPIGfNwcIBAH4gtH9FrYcDY68R22rp4iuxdvkf03ZWiT0F2W1y7/C9B2jayTzvQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAHd6Do9DIZ2hvdt1GwBXYjsqprZidT/DYOMfYcK17KlvdkFT58XrBH88ulLZ72NXEpiFMeTyzfs3XEyGq/Bbe7TBGVYZabUEh+LOskYwhgcOuThVN7tHnH5rhN+gb7cEdysjTb1QL+vOUwYgV75CB6PE5JVYK+cQsMIVvo0Kz4TpNgjJnWzbcH7h0mtvub+fCv92vBPjvYq8gUDLNrok6rbg05tdOJkXsF2G/W+Q6sf2Fvx0bK5JeH4an7P7cXF9VG9nd4sRt5zd+L3IcyvHVKxNhIJXZVH0AOqh/1YrKI9R0QKQiZCEy0xN1okPlcaIVaFhb7IKAHPxTI3r5f72LXY=","Registration":{"id":789,"key":{"kty":"RSA","n":"yNWVhtYEKJR21y9xsHV-PD_bYwbXSeNuFal46xYxVfRL5mqha7vttvjB_vc7Xg2RvgCxHPCqoxgMPTzHrZT75LjCwIW2K_klBYN8oYvTwwmeSkAz6ut7ZxPv-nZaT5TJhGk0NT2kh_zSpdriEJ_3vW-mqxYbbBmpvHqsa1_zx9fSuHYctAZJWzxzUZXykbWMWQZpEiE0J4ajj51fInEzVn7VxV-mzfMyboQjujPh7aNJxAWSq4oQEJJDgWwSh9leyoJoPpONHxh5nEE5AjE01FkGICSxjpZsF-w8hOTI3XXohUdu29Se26k2B0PolDSuj0GIQU6-W9TdLXSjBb2SpQ","e":"AQAB"},"recoveryToken":"","agreement":"http://example.invalid/terms"}}`)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue