Merge branch 'master' into send_error_use_error

This commit is contained in:
Jeff Hodges 2015-11-20 16:39:11 -08:00
commit 1b9e858681
75 changed files with 6554 additions and 5835 deletions

4
Godeps/Godeps.json generated
View File

@ -110,7 +110,7 @@
},
{
"ImportPath": "github.com/letsencrypt/go-jose",
"Rev": "77671e91fe89678878df4bde9e6b653a0d190dfb"
"Rev": "15b73d6f0363b256ddb080db1c64563064a4e773"
},
{
"ImportPath": "github.com/letsencrypt/go-safe-browsing-api",
@ -123,7 +123,7 @@
},
{
"ImportPath": "github.com/miekg/dns",
"Rev": "7ff8d29c8b70b10f383a11f03b7bf5b7408bf41a"
"Rev": "d27455715200c7d3e321a1e5cadb27c9ee0b0f02"
},
{
"ImportPath": "github.com/miekg/pkcs11",

View File

@ -11,6 +11,7 @@ go:
- 1.2
- 1.3
- 1.4
- 1.5
- tip
before_script:

View File

@ -0,0 +1,431 @@
/*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"errors"
"io"
"math/big"
"testing"
)
func TestVectorsRSA(t *testing.T) {
// Sources:
// http://www.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-rsa-cryptography-standard.htm
// ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15crypt-vectors.txt
priv := &rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: fromHexInt(`
a8b3b284af8eb50b387034a860f146c4919f318763cd6c5598c8
ae4811a1e0abc4c7e0b082d693a5e7fced675cf4668512772c0c
bc64a742c6c630f533c8cc72f62ae833c40bf25842e984bb78bd
bf97c0107d55bdb662f5c4e0fab9845cb5148ef7392dd3aaff93
ae1e6b667bb3d4247616d4f5ba10d4cfd226de88d39f16fb`),
E: 65537,
},
D: fromHexInt(`
53339cfdb79fc8466a655c7316aca85c55fd8f6dd898fdaf1195
17ef4f52e8fd8e258df93fee180fa0e4ab29693cd83b152a553d
4ac4d1812b8b9fa5af0e7f55fe7304df41570926f3311f15c4d6
5a732c483116ee3d3d2d0af3549ad9bf7cbfb78ad884f84d5beb
04724dc7369b31def37d0cf539e9cfcdd3de653729ead5d1`),
Primes: []*big.Int{
fromHexInt(`
d32737e7267ffe1341b2d5c0d150a81b586fb3132bed2f8d5262
864a9cb9f30af38be448598d413a172efb802c21acf1c11c520c
2f26a471dcad212eac7ca39d`),
fromHexInt(`
cc8853d1d54da630fac004f471f281c7b8982d8224a490edbeb3
3d3e3d5cc93c4765703d1dd791642f1f116a0dd852be2419b2af
72bfe9a030e860b0288b5d77`),
},
}
input := fromHexBytes(
"6628194e12073db03ba94cda9ef9532397d50dba79b987004afefe34")
expectedPKCS := fromHexBytes(`
50b4c14136bd198c2f3c3ed243fce036e168d56517984a263cd66492b808
04f169d210f2b9bdfb48b12f9ea05009c77da257cc600ccefe3a6283789d
8ea0e607ac58e2690ec4ebc10146e8cbaa5ed4d5cce6fe7b0ff9efc1eabb
564dbf498285f449ee61dd7b42ee5b5892cb90601f30cda07bf26489310b
cd23b528ceab3c31`)
expectedOAEP := fromHexBytes(`
354fe67b4a126d5d35fe36c777791a3f7ba13def484e2d3908aff722fad4
68fb21696de95d0be911c2d3174f8afcc201035f7b6d8e69402de5451618
c21a535fa9d7bfc5b8dd9fc243f8cf927db31322d6e881eaa91a996170e6
57a05a266426d98c88003f8477c1227094a0d9fa1e8c4024309ce1ecccb5
210035d47ac72e8a`)
// Mock random reader
randReader = bytes.NewReader(fromHexBytes(`
017341ae3875d5f87101f8cc4fa9b9bc156bb04628fccdb2f4f11e905bd3
a155d376f593bd7304210874eba08a5e22bcccb4c9d3882a93a54db022f5
03d16338b6b7ce16dc7f4bbf9a96b59772d6606e9747c7649bf9e083db98
1884a954ab3c6f18b776ea21069d69776a33e96bad48e1dda0a5ef`))
defer resetRandReader()
// RSA-PKCS1v1.5 encrypt
enc := new(rsaEncrypterVerifier)
enc.publicKey = &priv.PublicKey
encryptedPKCS, err := enc.encrypt(input, RSA1_5)
if err != nil {
t.Error("Encryption failed:", err)
return
}
if bytes.Compare(encryptedPKCS, expectedPKCS) != 0 {
t.Error("Output does not match expected value (PKCS1v1.5)")
}
// RSA-OAEP encrypt
encryptedOAEP, err := enc.encrypt(input, RSA_OAEP)
if err != nil {
t.Error("Encryption failed:", err)
return
}
if bytes.Compare(encryptedOAEP, expectedOAEP) != 0 {
t.Error("Output does not match expected value (OAEP)")
}
// Need fake cipher for PKCS1v1.5 decrypt
resetRandReader()
aes := newAESGCM(len(input))
keygen := randomKeyGenerator{
size: aes.keySize(),
}
// RSA-PKCS1v1.5 decrypt
dec := new(rsaDecrypterSigner)
dec.privateKey = priv
decryptedPKCS, err := dec.decrypt(encryptedPKCS, RSA1_5, keygen)
if err != nil {
t.Error("Decryption failed:", err)
return
}
if bytes.Compare(input, decryptedPKCS) != 0 {
t.Error("Output does not match expected value (PKCS1v1.5)")
}
// RSA-OAEP decrypt
decryptedOAEP, err := dec.decrypt(encryptedOAEP, RSA_OAEP, keygen)
if err != nil {
t.Error("decryption failed:", err)
return
}
if bytes.Compare(input, decryptedOAEP) != 0 {
t.Error("output does not match expected value (OAEP)")
}
}
func TestInvalidAlgorithmsRSA(t *testing.T) {
_, err := newRSARecipient("XYZ", nil)
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
_, err = newRSASigner("XYZ", nil)
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
enc := new(rsaEncrypterVerifier)
enc.publicKey = &rsaTestKey.PublicKey
_, err = enc.encryptKey([]byte{}, "XYZ")
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
err = enc.verifyPayload([]byte{}, []byte{}, "XYZ")
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
dec := new(rsaDecrypterSigner)
dec.privateKey = rsaTestKey
_, err = dec.decrypt(make([]byte, 256), "XYZ", randomKeyGenerator{size: 16})
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
_, err = dec.signPayload([]byte{}, "XYZ")
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
}
type failingKeyGenerator struct{}
func (ctx failingKeyGenerator) keySize() int {
return 0
}
func (ctx failingKeyGenerator) genKey() ([]byte, rawHeader, error) {
return nil, rawHeader{}, errors.New("failed to generate key")
}
func TestPKCSKeyGeneratorFailure(t *testing.T) {
dec := new(rsaDecrypterSigner)
dec.privateKey = rsaTestKey
generator := failingKeyGenerator{}
_, err := dec.decrypt(make([]byte, 256), RSA1_5, generator)
if err != ErrCryptoFailure {
t.Error("should return error on invalid algorithm")
}
}
func TestInvalidAlgorithmsEC(t *testing.T) {
_, err := newECDHRecipient("XYZ", nil)
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
_, err = newECDSASigner("XYZ", nil)
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
enc := new(ecEncrypterVerifier)
enc.publicKey = &ecTestKey256.PublicKey
_, err = enc.encryptKey([]byte{}, "XYZ")
if err != ErrUnsupportedAlgorithm {
t.Error("should return error on invalid algorithm")
}
}
func TestInvalidECKeyGen(t *testing.T) {
gen := ecKeyGenerator{
size: 16,
algID: "A128GCM",
publicKey: &ecTestKey256.PublicKey,
}
if gen.keySize() != 16 {
t.Error("ec key generator reported incorrect key size")
}
_, _, err := gen.genKey()
if err != nil {
t.Error("ec key generator failed to generate key", err)
}
}
func TestInvalidECDecrypt(t *testing.T) {
dec := ecDecrypterSigner{
privateKey: ecTestKey256,
}
generator := randomKeyGenerator{size: 16}
// Missing epk header
headers := rawHeader{
Alg: string(ECDH_ES),
}
_, err := dec.decryptKey(headers, nil, generator)
if err == nil {
t.Error("ec decrypter accepted object with missing epk header")
}
// Invalid epk header
headers.Epk = &JsonWebKey{}
_, err = dec.decryptKey(headers, nil, generator)
if err == nil {
t.Error("ec decrypter accepted object with invalid epk header")
}
}
func TestDecryptWithIncorrectSize(t *testing.T) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Error(err)
return
}
dec := new(rsaDecrypterSigner)
dec.privateKey = priv
aes := newAESGCM(16)
keygen := randomKeyGenerator{
size: aes.keySize(),
}
payload := make([]byte, 254)
_, err = dec.decrypt(payload, RSA1_5, keygen)
if err == nil {
t.Error("Invalid payload size should return error")
}
payload = make([]byte, 257)
_, err = dec.decrypt(payload, RSA1_5, keygen)
if err == nil {
t.Error("Invalid payload size should return error")
}
}
func TestPKCSDecryptNeverFails(t *testing.T) {
// We don't want RSA-PKCS1 v1.5 decryption to ever fail, in order to prevent
// side-channel timing attacks (Bleichenbacher attack in particular).
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Error(err)
return
}
dec := new(rsaDecrypterSigner)
dec.privateKey = priv
aes := newAESGCM(16)
keygen := randomKeyGenerator{
size: aes.keySize(),
}
for i := 1; i < 50; i++ {
payload := make([]byte, 256)
_, err := io.ReadFull(rand.Reader, payload)
if err != nil {
t.Error("Unable to get random data:", err)
return
}
_, err = dec.decrypt(payload, RSA1_5, keygen)
if err != nil {
t.Error("PKCS1v1.5 decrypt should never fail:", err)
return
}
}
}
func BenchmarkPKCSDecryptWithValidPayloads(b *testing.B) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
enc := new(rsaEncrypterVerifier)
enc.publicKey = &priv.PublicKey
dec := new(rsaDecrypterSigner)
dec.privateKey = priv
aes := newAESGCM(32)
b.StopTimer()
b.ResetTimer()
for i := 0; i < b.N; i++ {
plaintext := make([]byte, 32)
_, err = io.ReadFull(rand.Reader, plaintext)
if err != nil {
panic(err)
}
ciphertext, err := enc.encrypt(plaintext, RSA1_5)
if err != nil {
panic(err)
}
keygen := randomKeyGenerator{
size: aes.keySize(),
}
b.StartTimer()
_, err = dec.decrypt(ciphertext, RSA1_5, keygen)
b.StopTimer()
if err != nil {
panic(err)
}
}
}
func BenchmarkPKCSDecryptWithInvalidPayloads(b *testing.B) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
enc := new(rsaEncrypterVerifier)
enc.publicKey = &priv.PublicKey
dec := new(rsaDecrypterSigner)
dec.privateKey = priv
aes := newAESGCM(16)
keygen := randomKeyGenerator{
size: aes.keySize(),
}
b.StopTimer()
b.ResetTimer()
for i := 0; i < b.N; i++ {
plaintext := make([]byte, 16)
_, err = io.ReadFull(rand.Reader, plaintext)
if err != nil {
panic(err)
}
ciphertext, err := enc.encrypt(plaintext, RSA1_5)
if err != nil {
panic(err)
}
// Do some simple scrambling
ciphertext[128] ^= 0xFF
b.StartTimer()
_, err = dec.decrypt(ciphertext, RSA1_5, keygen)
b.StopTimer()
if err != nil {
panic(err)
}
}
}
func TestInvalidEllipticCurve(t *testing.T) {
signer256 := ecDecrypterSigner{privateKey: ecTestKey256}
signer384 := ecDecrypterSigner{privateKey: ecTestKey384}
signer521 := ecDecrypterSigner{privateKey: ecTestKey521}
_, err := signer256.signPayload([]byte{}, ES384)
if err == nil {
t.Error("should not generate ES384 signature with P-256 key")
}
_, err = signer256.signPayload([]byte{}, ES512)
if err == nil {
t.Error("should not generate ES512 signature with P-256 key")
}
_, err = signer384.signPayload([]byte{}, ES256)
if err == nil {
t.Error("should not generate ES256 signature with P-384 key")
}
_, err = signer384.signPayload([]byte{}, ES512)
if err == nil {
t.Error("should not generate ES512 signature with P-384 key")
}
_, err = signer521.signPayload([]byte{}, ES256)
if err == nil {
t.Error("should not generate ES256 signature with P-521 key")
}
_, err = signer521.signPayload([]byte{}, ES384)
if err == nil {
t.Error("should not generate ES384 signature with P-521 key")
}
}

View File

@ -112,7 +112,14 @@ func (ctx *cbcAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
}
cbc := cipher.NewCBCDecrypter(ctx.blockCipher, nonce)
buffer := []byte(ciphertext[:offset])
// Make copy of ciphertext buffer, don't want to modify in place
buffer := append([]byte{}, []byte(ciphertext[:offset])...)
if len(buffer)%ctx.blockCipher.BlockSize() > 0 {
return nil, errors.New("square/go-jose: invalid ciphertext (invalid length)")
}
cbc.CryptBlocks(buffer, buffer)
// Remove padding
@ -176,7 +183,7 @@ func unpadBuffer(buffer []byte, blockSize int) ([]byte, error) {
last := buffer[len(buffer)-1]
count := int(last)
if count > blockSize || count > len(buffer) {
if count == 0 || count > blockSize || count > len(buffer) {
return nil, errors.New("square/go-jose: invalid padding")
}

View File

@ -0,0 +1,498 @@
/*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package josecipher
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"io"
"strings"
"testing"
)
func TestInvalidInputs(t *testing.T) {
key := []byte{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
}
nonce := []byte{
92, 80, 104, 49, 133, 25, 161, 215, 173, 101, 219, 211, 136, 91, 210, 145}
aead, _ := NewCBCHMAC(key, aes.NewCipher)
ciphertext := aead.Seal(nil, nonce, []byte("plaintext"), []byte("aad"))
// Changed AAD, must fail
_, err := aead.Open(nil, nonce, ciphertext, []byte("INVALID"))
if err == nil {
t.Error("must detect invalid aad")
}
// Empty ciphertext, must fail
_, err = aead.Open(nil, nonce, []byte{}, []byte("aad"))
if err == nil {
t.Error("must detect invalid/empty ciphertext")
}
// Corrupt ciphertext, must fail
corrupt := make([]byte, len(ciphertext))
copy(corrupt, ciphertext)
corrupt[0] ^= 0xFF
_, err = aead.Open(nil, nonce, corrupt, []byte("aad"))
if err == nil {
t.Error("must detect corrupt ciphertext")
}
// Corrupt authtag, must fail
copy(corrupt, ciphertext)
corrupt[len(ciphertext)-1] ^= 0xFF
_, err = aead.Open(nil, nonce, corrupt, []byte("aad"))
if err == nil {
t.Error("must detect corrupt authtag")
}
// Truncated data, must fail
_, err = aead.Open(nil, nonce, ciphertext[:10], []byte("aad"))
if err == nil {
t.Error("must detect corrupt authtag")
}
}
func TestVectorsAESCBC128(t *testing.T) {
// Source: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-29#appendix-A.2
plaintext := []byte{
76, 105, 118, 101, 32, 108, 111, 110, 103, 32, 97, 110, 100, 32,
112, 114, 111, 115, 112, 101, 114, 46}
aad := []byte{
101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 83, 85, 48, 69,
120, 88, 122, 85, 105, 76, 67, 74, 108, 98, 109, 77, 105, 79, 105,
74, 66, 77, 84, 73, 52, 81, 48, 74, 68, 76, 85, 104, 84, 77, 106, 85,
50, 73, 110, 48}
expectedCiphertext := []byte{
40, 57, 83, 181, 119, 33, 133, 148, 198, 185, 243, 24, 152, 230, 6,
75, 129, 223, 127, 19, 210, 82, 183, 230, 168, 33, 215, 104, 143,
112, 56, 102}
expectedAuthtag := []byte{
246, 17, 244, 190, 4, 95, 98, 3, 231, 0, 115, 157, 242, 203, 100,
191}
key := []byte{
4, 211, 31, 197, 84, 157, 252, 254, 11, 100, 157, 250, 63, 170, 106, 206,
107, 124, 212, 45, 111, 107, 9, 219, 200, 177, 0, 240, 143, 156, 44, 207}
nonce := []byte{
3, 22, 60, 12, 43, 67, 104, 105, 108, 108, 105, 99, 111, 116, 104, 101}
enc, err := NewCBCHMAC(key, aes.NewCipher)
out := enc.Seal(nil, nonce, plaintext, aad)
if err != nil {
t.Error("Unable to encrypt:", err)
return
}
if bytes.Compare(out[:len(out)-16], expectedCiphertext) != 0 {
t.Error("Ciphertext did not match")
}
if bytes.Compare(out[len(out)-16:], expectedAuthtag) != 0 {
t.Error("Auth tag did not match")
}
}
func TestVectorsAESCBC256(t *testing.T) {
// Source: https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05#section-5.4
plaintext := []byte{
0x41, 0x20, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20,
0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75,
0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65,
0x74, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62,
0x65, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x20, 0x69,
0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x20, 0x6f, 0x66,
0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x65, 0x6d, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f,
0x75, 0x74, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65}
aad := []byte{
0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63,
0x69, 0x70, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x41, 0x75, 0x67, 0x75, 0x73, 0x74, 0x65, 0x20,
0x4b, 0x65, 0x72, 0x63, 0x6b, 0x68, 0x6f, 0x66, 0x66, 0x73}
expectedCiphertext := []byte{
0x4a, 0xff, 0xaa, 0xad, 0xb7, 0x8c, 0x31, 0xc5, 0xda, 0x4b, 0x1b, 0x59, 0x0d, 0x10, 0xff, 0xbd,
0x3d, 0xd8, 0xd5, 0xd3, 0x02, 0x42, 0x35, 0x26, 0x91, 0x2d, 0xa0, 0x37, 0xec, 0xbc, 0xc7, 0xbd,
0x82, 0x2c, 0x30, 0x1d, 0xd6, 0x7c, 0x37, 0x3b, 0xcc, 0xb5, 0x84, 0xad, 0x3e, 0x92, 0x79, 0xc2,
0xe6, 0xd1, 0x2a, 0x13, 0x74, 0xb7, 0x7f, 0x07, 0x75, 0x53, 0xdf, 0x82, 0x94, 0x10, 0x44, 0x6b,
0x36, 0xeb, 0xd9, 0x70, 0x66, 0x29, 0x6a, 0xe6, 0x42, 0x7e, 0xa7, 0x5c, 0x2e, 0x08, 0x46, 0xa1,
0x1a, 0x09, 0xcc, 0xf5, 0x37, 0x0d, 0xc8, 0x0b, 0xfe, 0xcb, 0xad, 0x28, 0xc7, 0x3f, 0x09, 0xb3,
0xa3, 0xb7, 0x5e, 0x66, 0x2a, 0x25, 0x94, 0x41, 0x0a, 0xe4, 0x96, 0xb2, 0xe2, 0xe6, 0x60, 0x9e,
0x31, 0xe6, 0xe0, 0x2c, 0xc8, 0x37, 0xf0, 0x53, 0xd2, 0x1f, 0x37, 0xff, 0x4f, 0x51, 0x95, 0x0b,
0xbe, 0x26, 0x38, 0xd0, 0x9d, 0xd7, 0xa4, 0x93, 0x09, 0x30, 0x80, 0x6d, 0x07, 0x03, 0xb1, 0xf6}
expectedAuthtag := []byte{
0x4d, 0xd3, 0xb4, 0xc0, 0x88, 0xa7, 0xf4, 0x5c, 0x21, 0x68, 0x39, 0x64, 0x5b, 0x20, 0x12, 0xbf,
0x2e, 0x62, 0x69, 0xa8, 0xc5, 0x6a, 0x81, 0x6d, 0xbc, 0x1b, 0x26, 0x77, 0x61, 0x95, 0x5b, 0xc5}
key := []byte{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f}
nonce := []byte{
0x1a, 0xf3, 0x8c, 0x2d, 0xc2, 0xb9, 0x6f, 0xfd, 0xd8, 0x66, 0x94, 0x09, 0x23, 0x41, 0xbc, 0x04}
enc, err := NewCBCHMAC(key, aes.NewCipher)
out := enc.Seal(nil, nonce, plaintext, aad)
if err != nil {
t.Error("Unable to encrypt:", err)
return
}
if bytes.Compare(out[:len(out)-32], expectedCiphertext) != 0 {
t.Error("Ciphertext did not match, got", out[:len(out)-32], "wanted", expectedCiphertext)
}
if bytes.Compare(out[len(out)-32:], expectedAuthtag) != 0 {
t.Error("Auth tag did not match, got", out[len(out)-32:], "wanted", expectedAuthtag)
}
}
func TestAESCBCRoundtrip(t *testing.T) {
key128 := []byte{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
key192 := []byte{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7}
key256 := []byte{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
nonce := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
RunRoundtrip(t, key128, nonce)
RunRoundtrip(t, key192, nonce)
RunRoundtrip(t, key256, nonce)
}
func RunRoundtrip(t *testing.T, key, nonce []byte) {
aead, err := NewCBCHMAC(key, aes.NewCipher)
if err != nil {
panic(err)
}
if aead.NonceSize() != len(nonce) {
panic("invalid nonce")
}
// Test pre-existing data in dst buffer
dst := []byte{15, 15, 15, 15}
plaintext := []byte{0, 0, 0, 0}
aad := []byte{4, 3, 2, 1}
result := aead.Seal(dst, nonce, plaintext, aad)
if bytes.Compare(dst, result[:4]) != 0 {
t.Error("Existing data in dst not preserved")
}
// Test pre-existing (empty) dst buffer with sufficient capacity
dst = make([]byte, 256)[:0]
result, err = aead.Open(dst, nonce, result[4:], aad)
if err != nil {
panic(err)
}
if bytes.Compare(result, plaintext) != 0 {
t.Error("Plaintext does not match output")
}
}
func TestAESCBCOverhead(t *testing.T) {
aead, err := NewCBCHMAC(make([]byte, 32), aes.NewCipher)
if err != nil {
panic(err)
}
if aead.Overhead() != 32 {
t.Error("CBC-HMAC reports incorrect overhead value")
}
}
func TestPadding(t *testing.T) {
for i := 0; i < 256; i++ {
slice := make([]byte, i)
padded := padBuffer(slice, 16)
if len(padded)%16 != 0 {
t.Error("failed to pad slice properly", i)
return
}
unpadded, err := unpadBuffer(padded, 16)
if err != nil || len(unpadded) != i {
t.Error("failed to unpad slice properly", i)
return
}
}
}
func TestInvalidKey(t *testing.T) {
key := make([]byte, 30)
_, err := NewCBCHMAC(key, aes.NewCipher)
if err == nil {
t.Error("should not be able to instantiate CBC-HMAC with invalid key")
}
}
func TestTruncatedCiphertext(t *testing.T) {
key := make([]byte, 32)
nonce := make([]byte, 16)
data := make([]byte, 32)
io.ReadFull(rand.Reader, key)
io.ReadFull(rand.Reader, nonce)
aead, err := NewCBCHMAC(key, aes.NewCipher)
if err != nil {
panic(err)
}
ctx := aead.(*cbcAEAD)
ct := aead.Seal(nil, nonce, data, nil)
// Truncated ciphertext, but with correct auth tag
truncated, tail := resize(ct[:len(ct)-ctx.authtagBytes-2], len(ct)-2)
copy(tail, ctx.computeAuthTag(nil, nonce, truncated[:len(truncated)-ctx.authtagBytes]))
// Open should fail
_, err = aead.Open(nil, nonce, truncated, nil)
if err == nil {
t.Error("open on truncated ciphertext should fail")
}
}
func TestInvalidPaddingOpen(t *testing.T) {
key := make([]byte, 32)
nonce := make([]byte, 16)
// Plaintext with invalid padding
plaintext := padBuffer(make([]byte, 28), aes.BlockSize)
plaintext[len(plaintext)-1] = 0xFF
io.ReadFull(rand.Reader, key)
io.ReadFull(rand.Reader, nonce)
block, _ := aes.NewCipher(key)
cbc := cipher.NewCBCEncrypter(block, nonce)
buffer := append([]byte{}, plaintext...)
cbc.CryptBlocks(buffer, buffer)
aead, _ := NewCBCHMAC(key, aes.NewCipher)
ctx := aead.(*cbcAEAD)
// Mutated ciphertext, but with correct auth tag
size := len(buffer)
ciphertext, tail := resize(buffer, size+(len(key)/2))
copy(tail, ctx.computeAuthTag(nil, nonce, ciphertext[:size]))
// Open should fail (b/c of invalid padding, even though tag matches)
_, err := aead.Open(nil, nonce, ciphertext, nil)
if err == nil || !strings.Contains(err.Error(), "invalid padding") {
t.Error("no or unexpected error on open with invalid padding:", err)
}
}
func TestInvalidPadding(t *testing.T) {
for i := 0; i < 256; i++ {
slice := make([]byte, i)
padded := padBuffer(slice, 16)
if len(padded)%16 != 0 {
t.Error("failed to pad slice properly", i)
return
}
paddingBytes := 16 - (i % 16)
// Mutate padding for testing
for j := 1; j <= paddingBytes; j++ {
mutated := make([]byte, len(padded))
copy(mutated, padded)
mutated[len(mutated)-j] ^= 0xFF
_, err := unpadBuffer(mutated, 16)
if err == nil {
t.Error("unpad on invalid padding should fail", i)
return
}
}
// Test truncated padding
_, err := unpadBuffer(padded[:len(padded)-1], 16)
if err == nil {
t.Error("unpad on truncated padding should fail", i)
return
}
}
}
func TestZeroLengthPadding(t *testing.T) {
data := make([]byte, 16)
data, err := unpadBuffer(data, 16)
if err == nil {
t.Error("padding with 0x00 should never be valid")
}
}
func benchEncryptCBCHMAC(b *testing.B, keySize, chunkSize int) {
key := make([]byte, keySize*2)
nonce := make([]byte, 16)
io.ReadFull(rand.Reader, key)
io.ReadFull(rand.Reader, nonce)
chunk := make([]byte, chunkSize)
aead, err := NewCBCHMAC(key, aes.NewCipher)
if err != nil {
panic(err)
}
b.SetBytes(int64(chunkSize))
b.ResetTimer()
for i := 0; i < b.N; i++ {
aead.Seal(nil, nonce, chunk, nil)
}
}
func benchDecryptCBCHMAC(b *testing.B, keySize, chunkSize int) {
key := make([]byte, keySize*2)
nonce := make([]byte, 16)
io.ReadFull(rand.Reader, key)
io.ReadFull(rand.Reader, nonce)
chunk := make([]byte, chunkSize)
aead, err := NewCBCHMAC(key, aes.NewCipher)
if err != nil {
panic(err)
}
out := aead.Seal(nil, nonce, chunk, nil)
b.SetBytes(int64(chunkSize))
b.ResetTimer()
for i := 0; i < b.N; i++ {
aead.Open(nil, nonce, out, nil)
}
}
func BenchmarkEncryptAES128_CBCHMAC_1k(b *testing.B) {
benchEncryptCBCHMAC(b, 16, 1024)
}
func BenchmarkEncryptAES128_CBCHMAC_64k(b *testing.B) {
benchEncryptCBCHMAC(b, 16, 65536)
}
func BenchmarkEncryptAES128_CBCHMAC_1MB(b *testing.B) {
benchEncryptCBCHMAC(b, 16, 1048576)
}
func BenchmarkEncryptAES128_CBCHMAC_64MB(b *testing.B) {
benchEncryptCBCHMAC(b, 16, 67108864)
}
func BenchmarkDecryptAES128_CBCHMAC_1k(b *testing.B) {
benchDecryptCBCHMAC(b, 16, 1024)
}
func BenchmarkDecryptAES128_CBCHMAC_64k(b *testing.B) {
benchDecryptCBCHMAC(b, 16, 65536)
}
func BenchmarkDecryptAES128_CBCHMAC_1MB(b *testing.B) {
benchDecryptCBCHMAC(b, 16, 1048576)
}
func BenchmarkDecryptAES128_CBCHMAC_64MB(b *testing.B) {
benchDecryptCBCHMAC(b, 16, 67108864)
}
func BenchmarkEncryptAES192_CBCHMAC_64k(b *testing.B) {
benchEncryptCBCHMAC(b, 24, 65536)
}
func BenchmarkEncryptAES192_CBCHMAC_1MB(b *testing.B) {
benchEncryptCBCHMAC(b, 24, 1048576)
}
func BenchmarkEncryptAES192_CBCHMAC_64MB(b *testing.B) {
benchEncryptCBCHMAC(b, 24, 67108864)
}
func BenchmarkDecryptAES192_CBCHMAC_1k(b *testing.B) {
benchDecryptCBCHMAC(b, 24, 1024)
}
func BenchmarkDecryptAES192_CBCHMAC_64k(b *testing.B) {
benchDecryptCBCHMAC(b, 24, 65536)
}
func BenchmarkDecryptAES192_CBCHMAC_1MB(b *testing.B) {
benchDecryptCBCHMAC(b, 24, 1048576)
}
func BenchmarkDecryptAES192_CBCHMAC_64MB(b *testing.B) {
benchDecryptCBCHMAC(b, 24, 67108864)
}
func BenchmarkEncryptAES256_CBCHMAC_64k(b *testing.B) {
benchEncryptCBCHMAC(b, 32, 65536)
}
func BenchmarkEncryptAES256_CBCHMAC_1MB(b *testing.B) {
benchEncryptCBCHMAC(b, 32, 1048576)
}
func BenchmarkEncryptAES256_CBCHMAC_64MB(b *testing.B) {
benchEncryptCBCHMAC(b, 32, 67108864)
}
func BenchmarkDecryptAES256_CBCHMAC_1k(b *testing.B) {
benchDecryptCBCHMAC(b, 32, 1032)
}
func BenchmarkDecryptAES256_CBCHMAC_64k(b *testing.B) {
benchDecryptCBCHMAC(b, 32, 65536)
}
func BenchmarkDecryptAES256_CBCHMAC_1MB(b *testing.B) {
benchDecryptCBCHMAC(b, 32, 1048576)
}
func BenchmarkDecryptAES256_CBCHMAC_64MB(b *testing.B) {
benchDecryptCBCHMAC(b, 32, 67108864)
}

View 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)
}

View 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)
}
}

View 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)
}
}

View File

@ -0,0 +1,751 @@
/*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"fmt"
"io"
"testing"
)
// We generate only a single RSA and EC key for testing, speeds up tests.
var rsaTestKey, _ = rsa.GenerateKey(rand.Reader, 2048)
var ecTestKey256, _ = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
var ecTestKey384, _ = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
var ecTestKey521, _ = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
func RoundtripJWE(keyAlg KeyAlgorithm, encAlg ContentEncryption, compressionAlg CompressionAlgorithm, serializer func(*JsonWebEncryption) (string, error), corrupter func(*JsonWebEncryption) bool, aad []byte, encryptionKey interface{}, decryptionKey interface{}) error {
enc, err := NewEncrypter(keyAlg, encAlg, encryptionKey)
if err != nil {
return fmt.Errorf("error on new encrypter: %s", err)
}
enc.SetCompression(compressionAlg)
input := []byte("Lorem ipsum dolor sit amet")
obj, err := enc.EncryptWithAuthData(input, aad)
if err != nil {
return fmt.Errorf("error in encrypt: %s", err)
}
msg, err := serializer(obj)
if err != nil {
return fmt.Errorf("error in serializer: %s", err)
}
parsed, err := ParseEncrypted(msg)
if err != nil {
return fmt.Errorf("error in parse: %s, on msg '%s'", err, msg)
}
// (Maybe) mangle object
skip := corrupter(parsed)
if skip {
return fmt.Errorf("corrupter indicated message should be skipped")
}
if bytes.Compare(parsed.GetAuthData(), aad) != 0 {
return fmt.Errorf("auth data in parsed object does not match")
}
output, err := parsed.Decrypt(decryptionKey)
if err != nil {
return fmt.Errorf("error on decrypt: %s", err)
}
if bytes.Compare(input, output) != 0 {
return fmt.Errorf("Decrypted output does not match input, got '%s' but wanted '%s'", output, input)
}
return nil
}
func TestRoundtripsJWE(t *testing.T) {
// Test matrix
keyAlgs := []KeyAlgorithm{
DIRECT, ECDH_ES, ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW, A128KW, A192KW, A256KW,
RSA1_5, RSA_OAEP, RSA_OAEP_256, A128GCMKW, A192GCMKW, A256GCMKW}
encAlgs := []ContentEncryption{A128GCM, A192GCM, A256GCM, A128CBC_HS256, A192CBC_HS384, A256CBC_HS512}
zipAlgs := []CompressionAlgorithm{NONE, DEFLATE}
serializers := []func(*JsonWebEncryption) (string, error){
func(obj *JsonWebEncryption) (string, error) { return obj.CompactSerialize() },
func(obj *JsonWebEncryption) (string, error) { return obj.FullSerialize(), nil },
}
corrupter := func(obj *JsonWebEncryption) bool { return false }
// Note: can't use AAD with compact serialization
aads := [][]byte{
nil,
[]byte("Ut enim ad minim veniam"),
}
// Test all different configurations
for _, alg := range keyAlgs {
for _, enc := range encAlgs {
for _, key := range generateTestKeys(alg, enc) {
for _, zip := range zipAlgs {
for i, serializer := range serializers {
err := RoundtripJWE(alg, enc, zip, serializer, corrupter, aads[i], key.enc, key.dec)
if err != nil {
t.Error(err, alg, enc, zip, i)
}
}
}
}
}
}
}
func TestRoundtripsJWECorrupted(t *testing.T) {
// Test matrix
keyAlgs := []KeyAlgorithm{DIRECT, ECDH_ES, ECDH_ES_A128KW, A128KW, RSA1_5, RSA_OAEP, RSA_OAEP_256, A128GCMKW}
encAlgs := []ContentEncryption{A128GCM, A192GCM, A256GCM, A128CBC_HS256, A192CBC_HS384, A256CBC_HS512}
zipAlgs := []CompressionAlgorithm{NONE, DEFLATE}
serializers := []func(*JsonWebEncryption) (string, error){
func(obj *JsonWebEncryption) (string, error) { return obj.CompactSerialize() },
func(obj *JsonWebEncryption) (string, error) { return obj.FullSerialize(), nil },
}
bitflip := func(slice []byte) bool {
if len(slice) > 0 {
slice[0] ^= 0xFF
return false
}
return true
}
corrupters := []func(*JsonWebEncryption) bool{
func(obj *JsonWebEncryption) bool {
// Set invalid ciphertext
return bitflip(obj.ciphertext)
},
func(obj *JsonWebEncryption) bool {
// Set invalid auth tag
return bitflip(obj.tag)
},
func(obj *JsonWebEncryption) bool {
// Set invalid AAD
return bitflip(obj.aad)
},
func(obj *JsonWebEncryption) bool {
// Mess with encrypted key
return bitflip(obj.recipients[0].encryptedKey)
},
func(obj *JsonWebEncryption) bool {
// Mess with GCM-KW auth tag
return bitflip(obj.protected.Tag.bytes())
},
}
// Note: can't use AAD with compact serialization
aads := [][]byte{
nil,
[]byte("Ut enim ad minim veniam"),
}
// Test all different configurations
for _, alg := range keyAlgs {
for _, enc := range encAlgs {
for _, key := range generateTestKeys(alg, enc) {
for _, zip := range zipAlgs {
for i, serializer := range serializers {
for j, corrupter := range corrupters {
err := RoundtripJWE(alg, enc, zip, serializer, corrupter, aads[i], key.enc, key.dec)
if err == nil {
t.Error("failed to detect corrupt data", err, alg, enc, zip, i, j)
}
}
}
}
}
}
}
}
func TestEncrypterWithBrokenRand(t *testing.T) {
keyAlgs := []KeyAlgorithm{ECDH_ES_A128KW, A128KW, RSA1_5, RSA_OAEP, RSA_OAEP_256, A128GCMKW}
encAlgs := []ContentEncryption{A128GCM, A192GCM, A256GCM, A128CBC_HS256, A192CBC_HS384, A256CBC_HS512}
serializer := func(obj *JsonWebEncryption) (string, error) { return obj.CompactSerialize() }
corrupter := func(obj *JsonWebEncryption) bool { return false }
// Break rand reader
readers := []func() io.Reader{
// Totally broken
func() io.Reader { return bytes.NewReader([]byte{}) },
// Not enough bytes
func() io.Reader { return io.LimitReader(rand.Reader, 20) },
}
defer resetRandReader()
for _, alg := range keyAlgs {
for _, enc := range encAlgs {
for _, key := range generateTestKeys(alg, enc) {
for i, getReader := range readers {
randReader = getReader()
err := RoundtripJWE(alg, enc, NONE, serializer, corrupter, nil, key.enc, key.dec)
if err == nil {
t.Error("encrypter should fail if rand is broken", i)
}
}
}
}
}
}
func TestNewEncrypterErrors(t *testing.T) {
_, err := NewEncrypter("XYZ", "XYZ", nil)
if err == nil {
t.Error("was able to instantiate encrypter with invalid cipher")
}
_, err = NewMultiEncrypter("XYZ")
if err == nil {
t.Error("was able to instantiate multi-encrypter with invalid cipher")
}
_, err = NewEncrypter(DIRECT, A128GCM, nil)
if err == nil {
t.Error("was able to instantiate encrypter with invalid direct key")
}
_, err = NewEncrypter(ECDH_ES, A128GCM, nil)
if err == nil {
t.Error("was able to instantiate encrypter with invalid EC key")
}
}
func TestMultiRecipientJWE(t *testing.T) {
enc, err := NewMultiEncrypter(A128GCM)
if err != nil {
panic(err)
}
err = enc.AddRecipient(RSA_OAEP, &rsaTestKey.PublicKey)
if err != nil {
t.Error("error when adding RSA recipient", err)
}
sharedKey := []byte{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
}
err = enc.AddRecipient(A256GCMKW, sharedKey)
if err != nil {
t.Error("error when adding AES recipient: ", err)
return
}
input := []byte("Lorem ipsum dolor sit amet")
obj, err := enc.Encrypt(input)
if err != nil {
t.Error("error in encrypt: ", err)
return
}
msg := obj.FullSerialize()
parsed, err := ParseEncrypted(msg)
if err != nil {
t.Error("error in parse: ", err)
return
}
output, err := parsed.Decrypt(rsaTestKey)
if err != nil {
t.Error("error on decrypt with RSA: ", err)
return
}
if bytes.Compare(input, output) != 0 {
t.Error("Decrypted output does not match input: ", output, input)
return
}
output, err = parsed.Decrypt(sharedKey)
if err != nil {
t.Error("error on decrypt with AES: ", err)
return
}
if bytes.Compare(input, output) != 0 {
t.Error("Decrypted output does not match input", output, input)
return
}
}
func TestMultiRecipientErrors(t *testing.T) {
enc, err := NewMultiEncrypter(A128GCM)
if err != nil {
panic(err)
}
input := []byte("Lorem ipsum dolor sit amet")
_, err = enc.Encrypt(input)
if err == nil {
t.Error("should fail when encrypting to zero recipients")
}
err = enc.AddRecipient(DIRECT, nil)
if err == nil {
t.Error("should reject DIRECT mode when encrypting to multiple recipients")
}
err = enc.AddRecipient(ECDH_ES, nil)
if err == nil {
t.Error("should reject ECDH_ES mode when encrypting to multiple recipients")
}
err = enc.AddRecipient(RSA1_5, nil)
if err == nil {
t.Error("should reject invalid recipient key")
}
}
type testKey struct {
enc, dec interface{}
}
func symmetricTestKey(size int) []testKey {
key, _, _ := randomKeyGenerator{size: size}.genKey()
return []testKey{
testKey{
enc: key,
dec: key,
},
}
}
func generateTestKeys(keyAlg KeyAlgorithm, encAlg ContentEncryption) []testKey {
switch keyAlg {
case DIRECT:
return symmetricTestKey(getContentCipher(encAlg).keySize())
case ECDH_ES, ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW:
return []testKey{
testKey{
dec: ecTestKey256,
enc: &ecTestKey256.PublicKey,
},
testKey{
dec: ecTestKey384,
enc: &ecTestKey384.PublicKey,
},
testKey{
dec: ecTestKey521,
enc: &ecTestKey521.PublicKey,
},
}
case A128GCMKW, A128KW:
return symmetricTestKey(16)
case A192GCMKW, A192KW:
return symmetricTestKey(24)
case A256GCMKW, A256KW:
return symmetricTestKey(32)
case RSA1_5, RSA_OAEP, RSA_OAEP_256:
return []testKey{testKey{
dec: rsaTestKey,
enc: &rsaTestKey.PublicKey,
}}
}
panic("Must update test case")
}
func RunRoundtripsJWE(b *testing.B, alg KeyAlgorithm, enc ContentEncryption, zip CompressionAlgorithm, priv, pub interface{}) {
serializer := func(obj *JsonWebEncryption) (string, error) {
return obj.CompactSerialize()
}
corrupter := func(obj *JsonWebEncryption) bool { return false }
b.ResetTimer()
for i := 0; i < b.N; i++ {
err := RoundtripJWE(alg, enc, zip, serializer, corrupter, nil, pub, priv)
if err != nil {
b.Error(err)
}
}
}
var (
chunks = map[string][]byte{
"1B": make([]byte, 1),
"64B": make([]byte, 64),
"1KB": make([]byte, 1024),
"64KB": make([]byte, 65536),
"1MB": make([]byte, 1048576),
"64MB": make([]byte, 67108864),
}
symKey, _, _ = randomKeyGenerator{size: 32}.genKey()
encrypters = map[string]Encrypter{
"OAEPAndGCM": mustEncrypter(RSA_OAEP, A128GCM, &rsaTestKey.PublicKey),
"PKCSAndGCM": mustEncrypter(RSA1_5, A128GCM, &rsaTestKey.PublicKey),
"OAEPAndCBC": mustEncrypter(RSA_OAEP, A128CBC_HS256, &rsaTestKey.PublicKey),
"PKCSAndCBC": mustEncrypter(RSA1_5, A128CBC_HS256, &rsaTestKey.PublicKey),
"DirectGCM128": mustEncrypter(DIRECT, A128GCM, symKey),
"DirectCBC128": mustEncrypter(DIRECT, A128CBC_HS256, symKey),
"DirectGCM256": mustEncrypter(DIRECT, A256GCM, symKey),
"DirectCBC256": mustEncrypter(DIRECT, A256CBC_HS512, symKey),
"AESKWAndGCM128": mustEncrypter(A128KW, A128GCM, symKey),
"AESKWAndCBC256": mustEncrypter(A256KW, A256GCM, symKey),
"ECDHOnP256AndGCM128": mustEncrypter(ECDH_ES, A128GCM, &ecTestKey256.PublicKey),
"ECDHOnP384AndGCM128": mustEncrypter(ECDH_ES, A128GCM, &ecTestKey384.PublicKey),
"ECDHOnP521AndGCM128": mustEncrypter(ECDH_ES, A128GCM, &ecTestKey521.PublicKey),
}
)
func BenchmarkEncrypt1BWithOAEPAndGCM(b *testing.B) { benchEncrypt("1B", "OAEPAndGCM", b) }
func BenchmarkEncrypt64BWithOAEPAndGCM(b *testing.B) { benchEncrypt("64B", "OAEPAndGCM", b) }
func BenchmarkEncrypt1KBWithOAEPAndGCM(b *testing.B) { benchEncrypt("1KB", "OAEPAndGCM", b) }
func BenchmarkEncrypt64KBWithOAEPAndGCM(b *testing.B) { benchEncrypt("64KB", "OAEPAndGCM", b) }
func BenchmarkEncrypt1MBWithOAEPAndGCM(b *testing.B) { benchEncrypt("1MB", "OAEPAndGCM", b) }
func BenchmarkEncrypt64MBWithOAEPAndGCM(b *testing.B) { benchEncrypt("64MB", "OAEPAndGCM", b) }
func BenchmarkEncrypt1BWithPKCSAndGCM(b *testing.B) { benchEncrypt("1B", "PKCSAndGCM", b) }
func BenchmarkEncrypt64BWithPKCSAndGCM(b *testing.B) { benchEncrypt("64B", "PKCSAndGCM", b) }
func BenchmarkEncrypt1KBWithPKCSAndGCM(b *testing.B) { benchEncrypt("1KB", "PKCSAndGCM", b) }
func BenchmarkEncrypt64KBWithPKCSAndGCM(b *testing.B) { benchEncrypt("64KB", "PKCSAndGCM", b) }
func BenchmarkEncrypt1MBWithPKCSAndGCM(b *testing.B) { benchEncrypt("1MB", "PKCSAndGCM", b) }
func BenchmarkEncrypt64MBWithPKCSAndGCM(b *testing.B) { benchEncrypt("64MB", "PKCSAndGCM", b) }
func BenchmarkEncrypt1BWithOAEPAndCBC(b *testing.B) { benchEncrypt("1B", "OAEPAndCBC", b) }
func BenchmarkEncrypt64BWithOAEPAndCBC(b *testing.B) { benchEncrypt("64B", "OAEPAndCBC", b) }
func BenchmarkEncrypt1KBWithOAEPAndCBC(b *testing.B) { benchEncrypt("1KB", "OAEPAndCBC", b) }
func BenchmarkEncrypt64KBWithOAEPAndCBC(b *testing.B) { benchEncrypt("64KB", "OAEPAndCBC", b) }
func BenchmarkEncrypt1MBWithOAEPAndCBC(b *testing.B) { benchEncrypt("1MB", "OAEPAndCBC", b) }
func BenchmarkEncrypt64MBWithOAEPAndCBC(b *testing.B) { benchEncrypt("64MB", "OAEPAndCBC", b) }
func BenchmarkEncrypt1BWithPKCSAndCBC(b *testing.B) { benchEncrypt("1B", "PKCSAndCBC", b) }
func BenchmarkEncrypt64BWithPKCSAndCBC(b *testing.B) { benchEncrypt("64B", "PKCSAndCBC", b) }
func BenchmarkEncrypt1KBWithPKCSAndCBC(b *testing.B) { benchEncrypt("1KB", "PKCSAndCBC", b) }
func BenchmarkEncrypt64KBWithPKCSAndCBC(b *testing.B) { benchEncrypt("64KB", "PKCSAndCBC", b) }
func BenchmarkEncrypt1MBWithPKCSAndCBC(b *testing.B) { benchEncrypt("1MB", "PKCSAndCBC", b) }
func BenchmarkEncrypt64MBWithPKCSAndCBC(b *testing.B) { benchEncrypt("64MB", "PKCSAndCBC", b) }
func BenchmarkEncrypt1BWithDirectGCM128(b *testing.B) { benchEncrypt("1B", "DirectGCM128", b) }
func BenchmarkEncrypt64BWithDirectGCM128(b *testing.B) { benchEncrypt("64B", "DirectGCM128", b) }
func BenchmarkEncrypt1KBWithDirectGCM128(b *testing.B) { benchEncrypt("1KB", "DirectGCM128", b) }
func BenchmarkEncrypt64KBWithDirectGCM128(b *testing.B) { benchEncrypt("64KB", "DirectGCM128", b) }
func BenchmarkEncrypt1MBWithDirectGCM128(b *testing.B) { benchEncrypt("1MB", "DirectGCM128", b) }
func BenchmarkEncrypt64MBWithDirectGCM128(b *testing.B) { benchEncrypt("64MB", "DirectGCM128", b) }
func BenchmarkEncrypt1BWithDirectCBC128(b *testing.B) { benchEncrypt("1B", "DirectCBC128", b) }
func BenchmarkEncrypt64BWithDirectCBC128(b *testing.B) { benchEncrypt("64B", "DirectCBC128", b) }
func BenchmarkEncrypt1KBWithDirectCBC128(b *testing.B) { benchEncrypt("1KB", "DirectCBC128", b) }
func BenchmarkEncrypt64KBWithDirectCBC128(b *testing.B) { benchEncrypt("64KB", "DirectCBC128", b) }
func BenchmarkEncrypt1MBWithDirectCBC128(b *testing.B) { benchEncrypt("1MB", "DirectCBC128", b) }
func BenchmarkEncrypt64MBWithDirectCBC128(b *testing.B) { benchEncrypt("64MB", "DirectCBC128", b) }
func BenchmarkEncrypt1BWithDirectGCM256(b *testing.B) { benchEncrypt("1B", "DirectGCM256", b) }
func BenchmarkEncrypt64BWithDirectGCM256(b *testing.B) { benchEncrypt("64B", "DirectGCM256", b) }
func BenchmarkEncrypt1KBWithDirectGCM256(b *testing.B) { benchEncrypt("1KB", "DirectGCM256", b) }
func BenchmarkEncrypt64KBWithDirectGCM256(b *testing.B) { benchEncrypt("64KB", "DirectGCM256", b) }
func BenchmarkEncrypt1MBWithDirectGCM256(b *testing.B) { benchEncrypt("1MB", "DirectGCM256", b) }
func BenchmarkEncrypt64MBWithDirectGCM256(b *testing.B) { benchEncrypt("64MB", "DirectGCM256", b) }
func BenchmarkEncrypt1BWithDirectCBC256(b *testing.B) { benchEncrypt("1B", "DirectCBC256", b) }
func BenchmarkEncrypt64BWithDirectCBC256(b *testing.B) { benchEncrypt("64B", "DirectCBC256", b) }
func BenchmarkEncrypt1KBWithDirectCBC256(b *testing.B) { benchEncrypt("1KB", "DirectCBC256", b) }
func BenchmarkEncrypt64KBWithDirectCBC256(b *testing.B) { benchEncrypt("64KB", "DirectCBC256", b) }
func BenchmarkEncrypt1MBWithDirectCBC256(b *testing.B) { benchEncrypt("1MB", "DirectCBC256", b) }
func BenchmarkEncrypt64MBWithDirectCBC256(b *testing.B) { benchEncrypt("64MB", "DirectCBC256", b) }
func BenchmarkEncrypt1BWithAESKWAndGCM128(b *testing.B) { benchEncrypt("1B", "AESKWAndGCM128", b) }
func BenchmarkEncrypt64BWithAESKWAndGCM128(b *testing.B) { benchEncrypt("64B", "AESKWAndGCM128", b) }
func BenchmarkEncrypt1KBWithAESKWAndGCM128(b *testing.B) { benchEncrypt("1KB", "AESKWAndGCM128", b) }
func BenchmarkEncrypt64KBWithAESKWAndGCM128(b *testing.B) { benchEncrypt("64KB", "AESKWAndGCM128", b) }
func BenchmarkEncrypt1MBWithAESKWAndGCM128(b *testing.B) { benchEncrypt("1MB", "AESKWAndGCM128", b) }
func BenchmarkEncrypt64MBWithAESKWAndGCM128(b *testing.B) { benchEncrypt("64MB", "AESKWAndGCM128", b) }
func BenchmarkEncrypt1BWithAESKWAndCBC256(b *testing.B) { benchEncrypt("1B", "AESKWAndCBC256", b) }
func BenchmarkEncrypt64BWithAESKWAndCBC256(b *testing.B) { benchEncrypt("64B", "AESKWAndCBC256", b) }
func BenchmarkEncrypt1KBWithAESKWAndCBC256(b *testing.B) { benchEncrypt("1KB", "AESKWAndCBC256", b) }
func BenchmarkEncrypt64KBWithAESKWAndCBC256(b *testing.B) { benchEncrypt("64KB", "AESKWAndCBC256", b) }
func BenchmarkEncrypt1MBWithAESKWAndCBC256(b *testing.B) { benchEncrypt("1MB", "AESKWAndCBC256", b) }
func BenchmarkEncrypt64MBWithAESKWAndCBC256(b *testing.B) { benchEncrypt("64MB", "AESKWAndCBC256", b) }
func BenchmarkEncrypt1BWithECDHOnP256AndGCM128(b *testing.B) {
benchEncrypt("1B", "ECDHOnP256AndGCM128", b)
}
func BenchmarkEncrypt64BWithECDHOnP256AndGCM128(b *testing.B) {
benchEncrypt("64B", "ECDHOnP256AndGCM128", b)
}
func BenchmarkEncrypt1KBWithECDHOnP256AndGCM128(b *testing.B) {
benchEncrypt("1KB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkEncrypt64KBWithECDHOnP256AndGCM128(b *testing.B) {
benchEncrypt("64KB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkEncrypt1MBWithECDHOnP256AndGCM128(b *testing.B) {
benchEncrypt("1MB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkEncrypt64MBWithECDHOnP256AndGCM128(b *testing.B) {
benchEncrypt("64MB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkEncrypt1BWithECDHOnP384AndGCM128(b *testing.B) {
benchEncrypt("1B", "ECDHOnP384AndGCM128", b)
}
func BenchmarkEncrypt64BWithECDHOnP384AndGCM128(b *testing.B) {
benchEncrypt("64B", "ECDHOnP384AndGCM128", b)
}
func BenchmarkEncrypt1KBWithECDHOnP384AndGCM128(b *testing.B) {
benchEncrypt("1KB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkEncrypt64KBWithECDHOnP384AndGCM128(b *testing.B) {
benchEncrypt("64KB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkEncrypt1MBWithECDHOnP384AndGCM128(b *testing.B) {
benchEncrypt("1MB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkEncrypt64MBWithECDHOnP384AndGCM128(b *testing.B) {
benchEncrypt("64MB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkEncrypt1BWithECDHOnP521AndGCM128(b *testing.B) {
benchEncrypt("1B", "ECDHOnP521AndGCM128", b)
}
func BenchmarkEncrypt64BWithECDHOnP521AndGCM128(b *testing.B) {
benchEncrypt("64B", "ECDHOnP521AndGCM128", b)
}
func BenchmarkEncrypt1KBWithECDHOnP521AndGCM128(b *testing.B) {
benchEncrypt("1KB", "ECDHOnP521AndGCM128", b)
}
func BenchmarkEncrypt64KBWithECDHOnP521AndGCM128(b *testing.B) {
benchEncrypt("64KB", "ECDHOnP521AndGCM128", b)
}
func BenchmarkEncrypt1MBWithECDHOnP521AndGCM128(b *testing.B) {
benchEncrypt("1MB", "ECDHOnP521AndGCM128", b)
}
func BenchmarkEncrypt64MBWithECDHOnP521AndGCM128(b *testing.B) {
benchEncrypt("64MB", "ECDHOnP521AndGCM128", b)
}
func benchEncrypt(chunkKey, primKey string, b *testing.B) {
data, ok := chunks[chunkKey]
if !ok {
b.Fatalf("unknown chunk size %s", chunkKey)
}
enc, ok := encrypters[primKey]
if !ok {
b.Fatalf("unknown encrypter %s", primKey)
}
b.SetBytes(int64(len(data)))
for i := 0; i < b.N; i++ {
enc.Encrypt(data)
}
}
var (
decryptionKeys = map[string]interface{}{
"OAEPAndGCM": rsaTestKey,
"PKCSAndGCM": rsaTestKey,
"OAEPAndCBC": rsaTestKey,
"PKCSAndCBC": rsaTestKey,
"DirectGCM128": symKey,
"DirectCBC128": symKey,
"DirectGCM256": symKey,
"DirectCBC256": symKey,
"AESKWAndGCM128": symKey,
"AESKWAndCBC256": symKey,
"ECDHOnP256AndGCM128": ecTestKey256,
"ECDHOnP384AndGCM128": ecTestKey384,
"ECDHOnP521AndGCM128": ecTestKey521,
}
)
func BenchmarkDecrypt1BWithOAEPAndGCM(b *testing.B) { benchDecrypt("1B", "OAEPAndGCM", b) }
func BenchmarkDecrypt64BWithOAEPAndGCM(b *testing.B) { benchDecrypt("64B", "OAEPAndGCM", b) }
func BenchmarkDecrypt1KBWithOAEPAndGCM(b *testing.B) { benchDecrypt("1KB", "OAEPAndGCM", b) }
func BenchmarkDecrypt64KBWithOAEPAndGCM(b *testing.B) { benchDecrypt("64KB", "OAEPAndGCM", b) }
func BenchmarkDecrypt1MBWithOAEPAndGCM(b *testing.B) { benchDecrypt("1MB", "OAEPAndGCM", b) }
func BenchmarkDecrypt64MBWithOAEPAndGCM(b *testing.B) { benchDecrypt("64MB", "OAEPAndGCM", b) }
func BenchmarkDecrypt1BWithPKCSAndGCM(b *testing.B) { benchDecrypt("1B", "PKCSAndGCM", b) }
func BenchmarkDecrypt64BWithPKCSAndGCM(b *testing.B) { benchDecrypt("64B", "PKCSAndGCM", b) }
func BenchmarkDecrypt1KBWithPKCSAndGCM(b *testing.B) { benchDecrypt("1KB", "PKCSAndGCM", b) }
func BenchmarkDecrypt64KBWithPKCSAndGCM(b *testing.B) { benchDecrypt("64KB", "PKCSAndGCM", b) }
func BenchmarkDecrypt1MBWithPKCSAndGCM(b *testing.B) { benchDecrypt("1MB", "PKCSAndGCM", b) }
func BenchmarkDecrypt64MBWithPKCSAndGCM(b *testing.B) { benchDecrypt("64MB", "PKCSAndGCM", b) }
func BenchmarkDecrypt1BWithOAEPAndCBC(b *testing.B) { benchDecrypt("1B", "OAEPAndCBC", b) }
func BenchmarkDecrypt64BWithOAEPAndCBC(b *testing.B) { benchDecrypt("64B", "OAEPAndCBC", b) }
func BenchmarkDecrypt1KBWithOAEPAndCBC(b *testing.B) { benchDecrypt("1KB", "OAEPAndCBC", b) }
func BenchmarkDecrypt64KBWithOAEPAndCBC(b *testing.B) { benchDecrypt("64KB", "OAEPAndCBC", b) }
func BenchmarkDecrypt1MBWithOAEPAndCBC(b *testing.B) { benchDecrypt("1MB", "OAEPAndCBC", b) }
func BenchmarkDecrypt64MBWithOAEPAndCBC(b *testing.B) { benchDecrypt("64MB", "OAEPAndCBC", b) }
func BenchmarkDecrypt1BWithPKCSAndCBC(b *testing.B) { benchDecrypt("1B", "PKCSAndCBC", b) }
func BenchmarkDecrypt64BWithPKCSAndCBC(b *testing.B) { benchDecrypt("64B", "PKCSAndCBC", b) }
func BenchmarkDecrypt1KBWithPKCSAndCBC(b *testing.B) { benchDecrypt("1KB", "PKCSAndCBC", b) }
func BenchmarkDecrypt64KBWithPKCSAndCBC(b *testing.B) { benchDecrypt("64KB", "PKCSAndCBC", b) }
func BenchmarkDecrypt1MBWithPKCSAndCBC(b *testing.B) { benchDecrypt("1MB", "PKCSAndCBC", b) }
func BenchmarkDecrypt64MBWithPKCSAndCBC(b *testing.B) { benchDecrypt("64MB", "PKCSAndCBC", b) }
func BenchmarkDecrypt1BWithDirectGCM128(b *testing.B) { benchDecrypt("1B", "DirectGCM128", b) }
func BenchmarkDecrypt64BWithDirectGCM128(b *testing.B) { benchDecrypt("64B", "DirectGCM128", b) }
func BenchmarkDecrypt1KBWithDirectGCM128(b *testing.B) { benchDecrypt("1KB", "DirectGCM128", b) }
func BenchmarkDecrypt64KBWithDirectGCM128(b *testing.B) { benchDecrypt("64KB", "DirectGCM128", b) }
func BenchmarkDecrypt1MBWithDirectGCM128(b *testing.B) { benchDecrypt("1MB", "DirectGCM128", b) }
func BenchmarkDecrypt64MBWithDirectGCM128(b *testing.B) { benchDecrypt("64MB", "DirectGCM128", b) }
func BenchmarkDecrypt1BWithDirectCBC128(b *testing.B) { benchDecrypt("1B", "DirectCBC128", b) }
func BenchmarkDecrypt64BWithDirectCBC128(b *testing.B) { benchDecrypt("64B", "DirectCBC128", b) }
func BenchmarkDecrypt1KBWithDirectCBC128(b *testing.B) { benchDecrypt("1KB", "DirectCBC128", b) }
func BenchmarkDecrypt64KBWithDirectCBC128(b *testing.B) { benchDecrypt("64KB", "DirectCBC128", b) }
func BenchmarkDecrypt1MBWithDirectCBC128(b *testing.B) { benchDecrypt("1MB", "DirectCBC128", b) }
func BenchmarkDecrypt64MBWithDirectCBC128(b *testing.B) { benchDecrypt("64MB", "DirectCBC128", b) }
func BenchmarkDecrypt1BWithDirectGCM256(b *testing.B) { benchDecrypt("1B", "DirectGCM256", b) }
func BenchmarkDecrypt64BWithDirectGCM256(b *testing.B) { benchDecrypt("64B", "DirectGCM256", b) }
func BenchmarkDecrypt1KBWithDirectGCM256(b *testing.B) { benchDecrypt("1KB", "DirectGCM256", b) }
func BenchmarkDecrypt64KBWithDirectGCM256(b *testing.B) { benchDecrypt("64KB", "DirectGCM256", b) }
func BenchmarkDecrypt1MBWithDirectGCM256(b *testing.B) { benchDecrypt("1MB", "DirectGCM256", b) }
func BenchmarkDecrypt64MBWithDirectGCM256(b *testing.B) { benchDecrypt("64MB", "DirectGCM256", b) }
func BenchmarkDecrypt1BWithDirectCBC256(b *testing.B) { benchDecrypt("1B", "DirectCBC256", b) }
func BenchmarkDecrypt64BWithDirectCBC256(b *testing.B) { benchDecrypt("64B", "DirectCBC256", b) }
func BenchmarkDecrypt1KBWithDirectCBC256(b *testing.B) { benchDecrypt("1KB", "DirectCBC256", b) }
func BenchmarkDecrypt64KBWithDirectCBC256(b *testing.B) { benchDecrypt("64KB", "DirectCBC256", b) }
func BenchmarkDecrypt1MBWithDirectCBC256(b *testing.B) { benchDecrypt("1MB", "DirectCBC256", b) }
func BenchmarkDecrypt64MBWithDirectCBC256(b *testing.B) { benchDecrypt("64MB", "DirectCBC256", b) }
func BenchmarkDecrypt1BWithAESKWAndGCM128(b *testing.B) { benchDecrypt("1B", "AESKWAndGCM128", b) }
func BenchmarkDecrypt64BWithAESKWAndGCM128(b *testing.B) { benchDecrypt("64B", "AESKWAndGCM128", b) }
func BenchmarkDecrypt1KBWithAESKWAndGCM128(b *testing.B) { benchDecrypt("1KB", "AESKWAndGCM128", b) }
func BenchmarkDecrypt64KBWithAESKWAndGCM128(b *testing.B) { benchDecrypt("64KB", "AESKWAndGCM128", b) }
func BenchmarkDecrypt1MBWithAESKWAndGCM128(b *testing.B) { benchDecrypt("1MB", "AESKWAndGCM128", b) }
func BenchmarkDecrypt64MBWithAESKWAndGCM128(b *testing.B) { benchDecrypt("64MB", "AESKWAndGCM128", b) }
func BenchmarkDecrypt1BWithAESKWAndCBC256(b *testing.B) { benchDecrypt("1B", "AESKWAndCBC256", b) }
func BenchmarkDecrypt64BWithAESKWAndCBC256(b *testing.B) { benchDecrypt("64B", "AESKWAndCBC256", b) }
func BenchmarkDecrypt1KBWithAESKWAndCBC256(b *testing.B) { benchDecrypt("1KB", "AESKWAndCBC256", b) }
func BenchmarkDecrypt64KBWithAESKWAndCBC256(b *testing.B) { benchDecrypt("64KB", "AESKWAndCBC256", b) }
func BenchmarkDecrypt1MBWithAESKWAndCBC256(b *testing.B) { benchDecrypt("1MB", "AESKWAndCBC256", b) }
func BenchmarkDecrypt64MBWithAESKWAndCBC256(b *testing.B) { benchDecrypt("64MB", "AESKWAndCBC256", b) }
func BenchmarkDecrypt1BWithECDHOnP256AndGCM128(b *testing.B) {
benchDecrypt("1B", "ECDHOnP256AndGCM128", b)
}
func BenchmarkDecrypt64BWithECDHOnP256AndGCM128(b *testing.B) {
benchDecrypt("64B", "ECDHOnP256AndGCM128", b)
}
func BenchmarkDecrypt1KBWithECDHOnP256AndGCM128(b *testing.B) {
benchDecrypt("1KB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkDecrypt64KBWithECDHOnP256AndGCM128(b *testing.B) {
benchDecrypt("64KB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkDecrypt1MBWithECDHOnP256AndGCM128(b *testing.B) {
benchDecrypt("1MB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkDecrypt64MBWithECDHOnP256AndGCM128(b *testing.B) {
benchDecrypt("64MB", "ECDHOnP256AndGCM128", b)
}
func BenchmarkDecrypt1BWithECDHOnP384AndGCM128(b *testing.B) {
benchDecrypt("1B", "ECDHOnP384AndGCM128", b)
}
func BenchmarkDecrypt64BWithECDHOnP384AndGCM128(b *testing.B) {
benchDecrypt("64B", "ECDHOnP384AndGCM128", b)
}
func BenchmarkDecrypt1KBWithECDHOnP384AndGCM128(b *testing.B) {
benchDecrypt("1KB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkDecrypt64KBWithECDHOnP384AndGCM128(b *testing.B) {
benchDecrypt("64KB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkDecrypt1MBWithECDHOnP384AndGCM128(b *testing.B) {
benchDecrypt("1MB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkDecrypt64MBWithECDHOnP384AndGCM128(b *testing.B) {
benchDecrypt("64MB", "ECDHOnP384AndGCM128", b)
}
func BenchmarkDecrypt1BWithECDHOnP521AndGCM128(b *testing.B) {
benchDecrypt("1B", "ECDHOnP521AndGCM128", b)
}
func BenchmarkDecrypt64BWithECDHOnP521AndGCM128(b *testing.B) {
benchDecrypt("64B", "ECDHOnP521AndGCM128", b)
}
func BenchmarkDecrypt1KBWithECDHOnP521AndGCM128(b *testing.B) {
benchDecrypt("1KB", "ECDHOnP521AndGCM128", b)
}
func BenchmarkDecrypt64KBWithECDHOnP521AndGCM128(b *testing.B) {
benchDecrypt("64KB", "ECDHOnP521AndGCM128", b)
}
func BenchmarkDecrypt1MBWithECDHOnP521AndGCM128(b *testing.B) {
benchDecrypt("1MB", "ECDHOnP521AndGCM128", b)
}
func BenchmarkDecrypt64MBWithECDHOnP521AndGCM128(b *testing.B) {
benchDecrypt("64MB", "ECDHOnP521AndGCM128", b)
}
func benchDecrypt(chunkKey, primKey string, b *testing.B) {
chunk, ok := chunks[chunkKey]
if !ok {
b.Fatalf("unknown chunk size %s", chunkKey)
}
enc, ok := encrypters[primKey]
if !ok {
b.Fatalf("unknown encrypter %s", primKey)
}
dec, ok := decryptionKeys[primKey]
if !ok {
b.Fatalf("unknown decryption key %s", primKey)
}
data, err := enc.Encrypt(chunk)
if err != nil {
b.Fatal(err)
}
b.SetBytes(int64(len(chunk)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
data.Decrypt(dec)
}
}
func mustEncrypter(keyAlg KeyAlgorithm, encAlg ContentEncryption, encryptionKey interface{}) Encrypter {
enc, err := NewEncrypter(keyAlg, encAlg, encryptionKey)
if err != nil {
panic(err)
}
return enc
}

View File

@ -0,0 +1,226 @@
/*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"fmt"
)
// Dummy encrypter for use in examples
var encrypter, _ = NewEncrypter(DIRECT, A128GCM, []byte{})
func Example_jWE() {
// Generate a public/private key pair to use for this example. The library
// also provides two utility functions (LoadPublicKey and LoadPrivateKey)
// that can be used to load keys from PEM/DER-encoded data.
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
// Instantiate an encrypter using RSA-OAEP with AES128-GCM. An error would
// indicate that the selected algorithm(s) are not currently supported.
publicKey := &privateKey.PublicKey
encrypter, err := NewEncrypter(RSA_OAEP, A128GCM, publicKey)
if err != nil {
panic(err)
}
// Encrypt a sample plaintext. Calling the encrypter returns an encrypted
// JWE object, which can then be serialized for output afterwards. An error
// would indicate a problem in an underlying cryptographic primitive.
var plaintext = []byte("Lorem ipsum dolor sit amet")
object, err := encrypter.Encrypt(plaintext)
if err != nil {
panic(err)
}
// Serialize the encrypted object using the full serialization format.
// Alternatively you can also use the compact format here by calling
// object.CompactSerialize() instead.
serialized := object.FullSerialize()
// Parse the serialized, encrypted JWE object. An error would indicate that
// the given input did not represent a valid message.
object, err = ParseEncrypted(serialized)
if err != nil {
panic(err)
}
// Now we can decrypt and get back our original plaintext. An error here
// would indicate the the message failed to decrypt, e.g. because the auth
// tag was broken or the message was tampered with.
decrypted, err := object.Decrypt(privateKey)
if err != nil {
panic(err)
}
fmt.Printf(string(decrypted))
// output: Lorem ipsum dolor sit amet
}
func Example_jWS() {
// Generate a public/private key pair to use for this example. The library
// also provides two utility functions (LoadPublicKey and LoadPrivateKey)
// that can be used to load keys from PEM/DER-encoded data.
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
// Instantiate a signer using RSASSA-PSS (SHA512) with the given private key.
signer, err := NewSigner(PS512, privateKey)
if err != nil {
panic(err)
}
// Sign a sample payload. Calling the signer returns a protected JWS object,
// which can then be serialized for output afterwards. An error would
// indicate a problem in an underlying cryptographic primitive.
var payload = []byte("Lorem ipsum dolor sit amet")
object, err := signer.Sign(payload)
if err != nil {
panic(err)
}
// Serialize the encrypted object using the full serialization format.
// Alternatively you can also use the compact format here by calling
// object.CompactSerialize() instead.
serialized := object.FullSerialize()
// Parse the serialized, protected JWS object. An error would indicate that
// the given input did not represent a valid message.
object, err = ParseSigned(serialized)
if err != nil {
panic(err)
}
// Now we can verify the signature on the payload. An error here would
// indicate the the message failed to verify, e.g. because the signature was
// broken or the message was tampered with.
output, err := object.Verify(&privateKey.PublicKey)
if err != nil {
panic(err)
}
fmt.Printf(string(output))
// output: Lorem ipsum dolor sit amet
}
func ExampleNewEncrypter_publicKey() {
var publicKey *rsa.PublicKey
// Instantiate an encrypter using RSA-OAEP with AES128-GCM.
NewEncrypter(RSA_OAEP, A128GCM, publicKey)
// Instantiate an encrypter using RSA-PKCS1v1.5 with AES128-CBC+HMAC.
NewEncrypter(RSA1_5, A128CBC_HS256, publicKey)
}
func ExampleNewEncrypter_symmetric() {
var sharedKey []byte
// Instantiate an encrypter using AES128-GCM with AES-GCM key wrap.
NewEncrypter(A128GCMKW, A128GCM, sharedKey)
// Instantiate an encrypter using AES256-GCM directly, w/o key wrapping.
NewEncrypter(DIRECT, A256GCM, sharedKey)
}
func ExampleNewSigner_publicKey() {
var rsaPrivateKey *rsa.PrivateKey
var ecdsaPrivateKey *ecdsa.PrivateKey
// Instantiate a signer using RSA-PKCS#1v1.5 with SHA-256.
NewSigner(RS256, rsaPrivateKey)
// Instantiate a signer using ECDSA with SHA-384.
NewSigner(ES384, ecdsaPrivateKey)
}
func ExampleNewSigner_symmetric() {
var sharedKey []byte
// Instantiate an signer using HMAC-SHA256.
NewSigner(HS256, sharedKey)
// Instantiate an signer using HMAC-SHA512.
NewSigner(HS512, sharedKey)
}
func ExampleNewMultiEncrypter() {
var publicKey *rsa.PublicKey
var sharedKey []byte
// Instantiate an encrypter using AES-GCM.
encrypter, err := NewMultiEncrypter(A128GCM)
if err != nil {
panic(err)
}
// Add a recipient using a shared key with AES-GCM key wap
err = encrypter.AddRecipient(A128GCMKW, sharedKey)
if err != nil {
panic(err)
}
// Add a recipient using an RSA public key with RSA-OAEP
err = encrypter.AddRecipient(RSA_OAEP, publicKey)
if err != nil {
panic(err)
}
}
func ExampleNewMultiSigner() {
var privateKey *rsa.PrivateKey
var sharedKey []byte
// Instantiate a signer for multiple recipients.
signer := NewMultiSigner()
// Add a recipient using a shared key with HMAC-SHA256
err := signer.AddRecipient(HS256, sharedKey)
if err != nil {
panic(err)
}
// Add a recipient using an RSA private key with RSASSA-PSS with SHA384
err = signer.AddRecipient(PS384, privateKey)
if err != nil {
panic(err)
}
}
func ExampleEncrypter_encrypt() {
// Encrypt a plaintext in order to get an encrypted JWE object.
var plaintext = []byte("This is a secret message")
encrypter.Encrypt(plaintext)
}
func ExampleEncrypter_encryptWithAuthData() {
// Encrypt a plaintext in order to get an encrypted JWE object. Also attach
// some additional authenticated data (AAD) to the object. Note that objects
// with attached AAD can only be represented using full serialization.
var plaintext = []byte("This is a secret message")
var aad = []byte("This is authenticated, but public data")
encrypter.EncryptWithAuthData(plaintext, aad)
}

View File

@ -132,6 +132,14 @@ func newBuffer(data []byte) *byteBuffer {
}
}
func newFixedSizeBuffer(data []byte, length int) *byteBuffer {
if len(data) > length {
panic("square/go-jose: invalid call to newFixedSizeBuffer (len(data) > length)")
}
pad := make([]byte, length-len(data))
return newBuffer(append(pad, data...))
}
func newBufferFromInt(num uint64) *byteBuffer {
data := make([]byte, 8)
binary.BigEndian.PutUint64(data, num)

View File

@ -0,0 +1,173 @@
/*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"bytes"
"strings"
"testing"
)
func TestBase64URLEncode(t *testing.T) {
// Test arrays with various sizes
if base64URLEncode([]byte{}) != "" {
t.Error("failed to encode empty array")
}
if base64URLEncode([]byte{0}) != "AA" {
t.Error("failed to encode [0x00]")
}
if base64URLEncode([]byte{0, 1}) != "AAE" {
t.Error("failed to encode [0x00, 0x01]")
}
if base64URLEncode([]byte{0, 1, 2}) != "AAEC" {
t.Error("failed to encode [0x00, 0x01, 0x02]")
}
if base64URLEncode([]byte{0, 1, 2, 3}) != "AAECAw" {
t.Error("failed to encode [0x00, 0x01, 0x02, 0x03]")
}
}
func TestBase64URLDecode(t *testing.T) {
// Test arrays with various sizes
val, err := base64URLDecode("")
if err != nil || !bytes.Equal(val, []byte{}) {
t.Error("failed to decode empty array")
}
val, err = base64URLDecode("AA")
if err != nil || !bytes.Equal(val, []byte{0}) {
t.Error("failed to decode [0x00]")
}
val, err = base64URLDecode("AAE")
if err != nil || !bytes.Equal(val, []byte{0, 1}) {
t.Error("failed to decode [0x00, 0x01]")
}
val, err = base64URLDecode("AAEC")
if err != nil || !bytes.Equal(val, []byte{0, 1, 2}) {
t.Error("failed to decode [0x00, 0x01, 0x02]")
}
val, err = base64URLDecode("AAECAw")
if err != nil || !bytes.Equal(val, []byte{0, 1, 2, 3}) {
t.Error("failed to decode [0x00, 0x01, 0x02, 0x03]")
}
}
func TestDeflateRoundtrip(t *testing.T) {
original := []byte("Lorem ipsum dolor sit amet")
compressed, err := deflate(original)
if err != nil {
panic(err)
}
output, err := inflate(compressed)
if err != nil {
panic(err)
}
if bytes.Compare(output, original) != 0 {
t.Error("Input and output do not match")
}
}
func TestInvalidCompression(t *testing.T) {
_, err := compress("XYZ", []byte{})
if err == nil {
t.Error("should not accept invalid algorithm")
}
_, err = decompress("XYZ", []byte{})
if err == nil {
t.Error("should not accept invalid algorithm")
}
_, err = decompress(DEFLATE, []byte{1, 2, 3, 4})
if err == nil {
t.Error("should not accept invalid data")
}
}
func TestByteBufferTrim(t *testing.T) {
buf := newBufferFromInt(1)
if !bytes.Equal(buf.data, []byte{1}) {
t.Error("Byte buffer for integer '1' should contain [0x01]")
}
buf = newBufferFromInt(65537)
if !bytes.Equal(buf.data, []byte{1, 0, 1}) {
t.Error("Byte buffer for integer '65537' should contain [0x01, 0x00, 0x01]")
}
}
func TestFixedSizeBuffer(t *testing.T) {
data0 := []byte{}
data1 := []byte{1}
data2 := []byte{1, 2}
data3 := []byte{1, 2, 3}
data4 := []byte{1, 2, 3, 4}
buf0 := newFixedSizeBuffer(data0, 4)
buf1 := newFixedSizeBuffer(data1, 4)
buf2 := newFixedSizeBuffer(data2, 4)
buf3 := newFixedSizeBuffer(data3, 4)
buf4 := newFixedSizeBuffer(data4, 4)
if !bytes.Equal(buf0.data, []byte{0, 0, 0, 0}) {
t.Error("Invalid padded buffer for buf0")
}
if !bytes.Equal(buf1.data, []byte{0, 0, 0, 1}) {
t.Error("Invalid padded buffer for buf1")
}
if !bytes.Equal(buf2.data, []byte{0, 0, 1, 2}) {
t.Error("Invalid padded buffer for buf2")
}
if !bytes.Equal(buf3.data, []byte{0, 1, 2, 3}) {
t.Error("Invalid padded buffer for buf3")
}
if !bytes.Equal(buf4.data, []byte{1, 2, 3, 4}) {
t.Error("Invalid padded buffer for buf4")
}
}
func TestSerializeJSONRejectsNil(t *testing.T) {
defer func() {
r := recover()
if r == nil || !strings.Contains(r.(string), "nil pointer") {
t.Error("serialize function should not accept nil pointer")
}
}()
mustSerializeJSON(nil)
}
func TestFixedSizeBufferTooLarge(t *testing.T) {
defer func() {
r := recover()
if r == nil {
t.Error("should not be able to create fixed size buffer with oversized data")
}
}()
newFixedSizeBuffer(make([]byte, 2), 1)
}

View File

@ -129,6 +129,12 @@ func (parsed *rawJsonWebEncryption) sanitized() (*JsonWebEncryption, error) {
obj.Header = obj.mergedHeaders(nil).sanitized()
// Check that there is not a nonce in the unprotected headers
if (parsed.Unprotected != nil && parsed.Unprotected.Nonce != "") ||
(parsed.Header != nil && parsed.Header.Nonce != "") {
return nil, ErrUnprotectedNonce
}
if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 {
err := json.Unmarshal(parsed.Protected.bytes(), &obj.protected)
if err != nil {
@ -151,6 +157,11 @@ func (parsed *rawJsonWebEncryption) sanitized() (*JsonWebEncryption, error) {
return nil, err
}
// Check that there is not a nonce in the unprotected header
if parsed.Recipients[r].Header != nil && parsed.Recipients[r].Header.Nonce != "" {
return nil, ErrUnprotectedNonce
}
obj.recipients[r].header = parsed.Recipients[r].Header
obj.recipients[r].encryptedKey = encryptedKey
}

View File

@ -0,0 +1,537 @@
/*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"math/big"
"testing"
)
func TestCompactParseJWE(t *testing.T) {
// Should parse
msg := "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA.dGVzdA"
_, err := ParseEncrypted(msg)
if err != nil {
t.Error("Unable to parse valid message:", err)
}
// Messages that should fail to parse
failures := []string{
// Too many parts
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
// Not enough parts
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA",
// Invalid encrypted key
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.//////.dGVzdA.dGVzdA.dGVzdA",
// Invalid IV
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.//////.dGVzdA.dGVzdA",
// Invalid ciphertext
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.//////.dGVzdA",
// Invalid tag
"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.dGVzdA.dGVzdA.dGVzdA.//////",
// Invalid header
"W10.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
// Invalid header
"######.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
// Missing alc/enc params
"e30.dGVzdA.dGVzdA.dGVzdA.dGVzdA",
}
for _, msg := range failures {
_, err = ParseEncrypted(msg)
if err == nil {
t.Error("Able to parse invalid message", msg)
}
}
}
func TestFullParseJWE(t *testing.T) {
// Messages that should succeed to parse
successes := []string{
// Flattened serialization, single recipient
"{\"protected\":\"eyJhbGciOiJYWVoiLCJlbmMiOiJYWVoifQo\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
// Unflattened serialization, single recipient
"{\"protected\":\"\",\"unprotected\":{\"enc\":\"XYZ\"},\"recipients\":[{\"header\":{\"alg\":\"XYZ\"},\"encrypted_key\":\"QUJD\"}],\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
}
for i := range successes {
_, err := ParseEncrypted(successes[i])
if err != nil {
t.Error("Unble to parse valid message", err, successes[i])
}
}
// Messages that should fail to parse
failures := []string{
// Empty
"{}",
// Invalid JSON
"{XX",
// Invalid protected header
"{\"protected\":\"###\"}",
// Invalid protected header
"{\"protected\":\"e1gK\"}",
// Invalid encrypted key
"{\"protected\":\"e30\",\"encrypted_key\":\"###\"}",
// Invalid IV
"{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"###\"}",
// Invalid ciphertext
"{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"###\"}",
// Invalid tag
"{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"###\"}",
// Invalid AAD
"{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\",\"aad\":\"###\"}",
// Missing alg/enc headers
"{\"protected\":\"e30\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
// Missing enc header
"{\"protected\":\"eyJhbGciOiJYWVoifQ\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
// Missing alg header
"{\"protected\":\"eyJlbmMiOiJYWVoifQ\",\"encrypted_key\":\"QUJD\",\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
// Unflattened serialization, single recipient, invalid encrypted_key
"{\"protected\":\"\",\"recipients\":[{\"header\":{\"alg\":\"XYZ\", \"enc\":\"XYZ\"},\"encrypted_key\":\"###\"}],\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
// Unflattened serialization, single recipient, missing alg
"{\"protected\":\"eyJhbGciOiJYWVoifQ\",\"recipients\":[{\"encrypted_key\":\"QUJD\"}],\"iv\":\"QUJD\",\"ciphertext\":\"QUJD\",\"tag\":\"QUJD\"}",
}
for i := range failures {
_, err := ParseEncrypted(failures[i])
if err == nil {
t.Error("Able to parse invalid message", err, failures[i])
}
}
}
func TestMissingInvalidHeaders(t *testing.T) {
obj := &JsonWebEncryption{
protected: &rawHeader{Enc: A128GCM},
unprotected: &rawHeader{},
recipients: []recipientInfo{
recipientInfo{},
},
}
_, err := obj.Decrypt(nil)
if err != ErrUnsupportedKeyType {
t.Error("should detect invalid key")
}
obj.unprotected.Crit = []string{"1", "2"}
_, err = obj.Decrypt(nil)
if err == nil {
t.Error("should reject message with crit header")
}
obj.unprotected.Crit = nil
obj.protected = &rawHeader{Alg: string(RSA1_5)}
_, err = obj.Decrypt(rsaTestKey)
if err == nil || err == ErrCryptoFailure {
t.Error("should detect missing enc header")
}
}
func TestRejectUnprotectedJWENonce(t *testing.T) {
// No need to test compact, since that's always protected
// Flattened JSON
input := `{
"header": {
"alg": "XYZ", "enc": "XYZ",
"nonce": "should-cause-an-error"
},
"encrypted_key": "does-not-matter",
"aad": "does-not-matter",
"iv": "does-not-matter",
"ciphertext": "does-not-matter",
"tag": "does-not-matter"
}`
_, err := ParseEncrypted(input)
if err == nil {
t.Error("JWE with an unprotected nonce parsed as valid.")
} else if err.Error() != "square/go-jose: Nonce parameter included in unprotected header" {
t.Errorf("Improper error for unprotected nonce: %v", err)
}
input = `{
"unprotected": {
"alg": "XYZ", "enc": "XYZ",
"nonce": "should-cause-an-error"
},
"encrypted_key": "does-not-matter",
"aad": "does-not-matter",
"iv": "does-not-matter",
"ciphertext": "does-not-matter",
"tag": "does-not-matter"
}`
_, err = ParseEncrypted(input)
if err == nil {
t.Error("JWE with an unprotected nonce parsed as valid.")
} else if err.Error() != "square/go-jose: Nonce parameter included in unprotected header" {
t.Errorf("Improper error for unprotected nonce: %v", err)
}
// Full JSON
input = `{
"header": { "alg": "XYZ", "enc": "XYZ" },
"aad": "does-not-matter",
"iv": "does-not-matter",
"ciphertext": "does-not-matter",
"tag": "does-not-matter",
"recipients": [{
"header": { "nonce": "should-cause-an-error" },
"encrypted_key": "does-not-matter"
}]
}`
_, err = ParseEncrypted(input)
if err == nil {
t.Error("JWS with an unprotected nonce parsed as valid.")
} else if err.Error() != "square/go-jose: Nonce parameter included in unprotected header" {
t.Errorf("Improper error for unprotected nonce: %v", err)
}
}
func TestCompactSerialize(t *testing.T) {
// Compact serialization must fail if we have unprotected headers
obj := &JsonWebEncryption{
unprotected: &rawHeader{Alg: "XYZ"},
}
_, err := obj.CompactSerialize()
if err == nil {
t.Error("Object with unprotected headers can't be compact serialized")
}
}
func TestVectorsJWE(t *testing.T) {
plaintext := []byte("The true sign of intelligence is not knowledge but imagination.")
publicKey := &rsa.PublicKey{
N: fromBase64Int(`
oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW
cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S
psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a
sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS
tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj
YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw`),
E: 65537,
}
expectedCompact := stripWhitespace(`
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.ROQCfge4JPm_
yACxv1C1NSXmwNbL6kvmCuyxBRGpW57DvlwByjyjsb6g8m7wtLMqKEyhFCn
tV7sjippEePIlKln6BvVnz5ZLXHNYQgmubuNq8MC0KTwcaGJ_C0z_T8j4PZ
a1nfpbhSe-ePYaALrf_nIsSRKu7cWsrwOSlaRPecRnYeDd_ytAxEQWYEKFi
Pszc70fP9geZOB_09y9jq0vaOF0jGmpIAmgk71lCcUpSdrhNokTKo5y8MH8
3NcbIvmuZ51cjXQj1f0_AwM9RW3oCh2Hu0z0C5l4BujZVsDuGgMsGZsjUhS
RZsAQSXHCAmlJ2NlnN60U7y4SPJhKv5tKYw.48V1_ALb6US04U3b.5eym8T
W_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiS
diwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ`)
expectedFull := stripWhitespace(`
{ "protected":"eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ",
"encrypted_key":
"ROQCfge4JPm_yACxv1C1NSXmwNbL6kvmCuyxBRGpW57DvlwByjyjsb
6g8m7wtLMqKEyhFCntV7sjippEePIlKln6BvVnz5ZLXHNYQgmubuNq
8MC0KTwcaGJ_C0z_T8j4PZa1nfpbhSe-ePYaALrf_nIsSRKu7cWsrw
OSlaRPecRnYeDd_ytAxEQWYEKFiPszc70fP9geZOB_09y9jq0vaOF0
jGmpIAmgk71lCcUpSdrhNokTKo5y8MH83NcbIvmuZ51cjXQj1f0_Aw
M9RW3oCh2Hu0z0C5l4BujZVsDuGgMsGZsjUhSRZsAQSXHCAmlJ2Nln
N60U7y4SPJhKv5tKYw",
"iv": "48V1_ALb6US04U3b",
"ciphertext":
"5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFS
hS8iB7j6jiSdiwkIr3ajwQzaBtQD_A",
"tag":"XFBoMYUZodetZdvTiFvSkQ" }`)
// Mock random reader
randReader = bytes.NewReader([]byte{
// Encryption key
177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154,
212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122,
234, 64, 252,
// Randomness for RSA-OAEP
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// Initialization vector
227, 197, 117, 252, 2, 219, 233, 68, 180, 225, 77, 219})
defer resetRandReader()
// Encrypt with a dummy key
encrypter, err := NewEncrypter(RSA_OAEP, A256GCM, publicKey)
if err != nil {
panic(err)
}
object, err := encrypter.Encrypt(plaintext)
if err != nil {
panic(err)
}
serialized, err := object.CompactSerialize()
if serialized != expectedCompact {
t.Error("Compact serialization is not what we expected", serialized, expectedCompact)
}
serialized = object.FullSerialize()
if serialized != expectedFull {
t.Error("Full serialization is not what we expected")
}
}
func TestVectorsJWECorrupt(t *testing.T) {
priv := &rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: fromHexInt(`
a8b3b284af8eb50b387034a860f146c4919f318763cd6c5598c8
ae4811a1e0abc4c7e0b082d693a5e7fced675cf4668512772c0c
bc64a742c6c630f533c8cc72f62ae833c40bf25842e984bb78bd
bf97c0107d55bdb662f5c4e0fab9845cb5148ef7392dd3aaff93
ae1e6b667bb3d4247616d4f5ba10d4cfd226de88d39f16fb`),
E: 65537,
},
D: fromHexInt(`
53339cfdb79fc8466a655c7316aca85c55fd8f6dd898fdaf1195
17ef4f52e8fd8e258df93fee180fa0e4ab29693cd83b152a553d
4ac4d1812b8b9fa5af0e7f55fe7304df41570926f3311f15c4d6
5a732c483116ee3d3d2d0af3549ad9bf7cbfb78ad884f84d5beb
04724dc7369b31def37d0cf539e9cfcdd3de653729ead5d1`),
Primes: []*big.Int{
fromHexInt(`
d32737e7267ffe1341b2d5c0d150a81b586fb3132bed2f8d5262
864a9cb9f30af38be448598d413a172efb802c21acf1c11c520c
2f26a471dcad212eac7ca39d`),
fromHexInt(`
cc8853d1d54da630fac004f471f281c7b8982d8224a490edbeb3
3d3e3d5cc93c4765703d1dd791642f1f116a0dd852be2419b2af
72bfe9a030e860b0288b5d77`),
},
}
corruptCiphertext := stripWhitespace(`
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.NFl09dehy
IR2Oh5iSsvEa82Ps7DLjRHeo0RnuTuSR45OsaIP6U8yu7vLlWaZKSZMy
B2qRBSujf-5XIRoNhtyIyjk81eJRXGa_Bxaor1XBCMyyhGchW2H2P71f
PhDO6ufSC7kV4bNqgHR-4ziS7KXwzN83_5kogXqxUpymUoJDNc.tk-GT
W_VVhiTIKFF.D_BE6ImZUl9F.52a-zFnRb3YQwIC7UrhVyQ`)
corruptAuthtag := stripWhitespace(`
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.NFl09dehy
IR2Oh5iSsvEa82Ps7DLjRHeo0RnuTuSR45OsaIP6U8yu7vLlWaZKSZMy
B2qRBSujf-5XIRoNhtyIyjk81eJRXGa_Bxaor1XBCMyyhGchW2H2P71f
PhDO6ufSC7kV4bNqgHR-4ziS7KNwzN83_5kogXqxUpymUoJDNc.tk-GT
W_VVhiTIKFF.D_BE6ImZUl9F.52a-zFnRb3YQwiC7UrhVyQ`)
msg, _ := ParseEncrypted(corruptCiphertext)
_, err := msg.Decrypt(priv)
if err != ErrCryptoFailure {
t.Error("should detect corrupt ciphertext")
}
msg, _ = ParseEncrypted(corruptAuthtag)
_, err = msg.Decrypt(priv)
if err != ErrCryptoFailure {
t.Error("should detect corrupt auth tag")
}
}
// Test vectors generated with nimbus-jose-jwt
func TestSampleNimbusJWEMessagesRSA(t *testing.T) {
rsaPrivateKey, err := LoadPrivateKey(fromBase64Bytes(`
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCNRCEmf5PlbXKuT4uwnb
wGKvFrtpi+bDYxOZxxqxdVkZM/bYATAnD1fg9pNvLMKeF+MWJ9kPIMmDgOh9RdnRdLvQGb
BzhLmxwhhcua2QYiHEZizXmiaXvNP12bzEBhebdX7ObW8izMVW0p0lqHPNzkK3K75B0Sxo
FMVKkZ7KtBHgepBT5yPhPPcNe5lXQeTne5bo3I60DRcN9jTBgMJOXdq0I9o4y6ZmoXdNTm
0EyLzn9/EYiHqBxtKFh791EHR7wYgyi/t+nOKr4sO74NbEByP0mHDil+mPvZSzFW4l7fPx
OclRZvpRIKIub2TroZA9s2WsshGf79eqqXYbBB9NNRAgMBAAECggEAIExbZ/nzTplfhwsY
3SCzRJW87OuqsJ79JPQPGM4NX7sQ94eJqM7+FKLl0yCFErjgnYGdCyiArvB+oJPdsimgke
h83X0hGeg03lVA3/6OsG3WifCAxulnLN44AM8KST8S9D9t5+cm5vEBLHazzAfWWTS13s+g
9hH8rf8NSqgZ36EutjKlvLdHx1mWcKX7SREFVHT8FWPAbdhTLEHUjoWHrfSektnczaSHnt
q8fFJy6Ld13QkF1ZJRUhtA24XrD+qLTc+M36IuedjeZaLHFB+KyhYR3YvXEtrbCug7dCRd
uG6uTlDCSaSy7xHeTPolWtWo9F202jal54otxiAJFGUHgQKBgQDRAT0s6YQZUfwE0wluXV
k0JdhDdCo8sC1aMmKlRKWUkBAqrDl7BI3MF56VOr4ybr90buuscshFf9TtrtBOjHSGcfDI
tSKfhhkW5ewQKB0YqyHzoD6UKT0/XAshFY3esc3uCxuJ/6vOiXV0og9o7eFvr51O0TfDFh
mcTvW4wirKlQKBgQCtB7UAu8I9Nn8czkd6oXLDRyTWYviuiqFmxR+PM9klgZtsumkeSxO1
lkfFoj9+G8nFaqYEBA9sPeNtJVTSROCvj/iQtoqpV2NiI/wWeVszpBwsswx2mlks4LJa8a
Yz9xrsfNoroKYVppefc/MCoSx4M+99RSm3FSpLGZQHAUGyzQKBgQDMQmq4JuuMF1y2lk0E
SESyuz21BqV0tDVOjilsHT+5hmXWXoS6nkO6L2czrrpM7YE82F6JJZBmo7zEIXHBInGLJ3
XLoYLZ5qNEhqYDUEDHaBCBWZ1vDTKnZlwWFEuXVavNNZvPbUhKTHq25t8qjDki/r09Vykp
BsM2yNBKpbBOVQKBgCJyUVd3CaFUExQyAMrqD0XPCQdhJq7gzGcAQVsp8EXmOoH3zmuIeM
ECzQEMXuWFNLMHm0tbX5Kl83vMHcnKioyI9ewhWxOBYTitf0ceG8j5F97SOl32NmCXzwoJ
55Oa0xJXfLuIvOe8hZzp4WwZmBfKBxiCR166aPQQgIawelrVAoGAEJsHomfCI4epxH4oMw
qYJMCGy95zloB+2+c86BZCOJAGwnfzbtc2eutWZw61/9sSO8sQCfzA8oX+5HwAgnFVzwW4
lNMZohppYcpwN9EyjkPaCXuALC7p5rF2o63wY7JLvnjS2aYZliknh2yW6X6fSB0PK0Cpvd
lAIyRw6Kud0zI=`))
if err != nil {
panic(err)
}
rsaSampleMessages := []string{
"eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBMV81In0.EW0KOhHeoAxTBnLjYhh2T6HjwI-srNs6RpcSdZvE-GJ5iww3EYWBCmeGGj1UVz6OcBfwW3wllZ6GPOHU-hxVQH5KYpVOjkmrFIYU6-8BHhxBP_PjSJEBCZzjOgsCm9Th4-zmlO7UWTdK_UtwE7nk4X-kkmEy-aZBCShA8nFe2MVvqD5F7nvEWNFBOHh8ae_juo-kvycoIzvxLV9g1B0Zn8K9FAlu8YF1KiL5NFekn76f3jvAwlExuRbFPUx4gJN6CeBDK_D57ABsY2aBVDSiQceuYZxvCIAajqSS6dMT382FNJzAiQhToOpo_1w5FnnBjzJLLEKDk_I-Eo2YCWxxsQ.5mCMuxJqLRuPXGAr.Ghe4INeBhP3MDWGvyNko7qanKdZIzKjfeiU.ja3UlVWJXKNFJ-rZsJWycw",
"eyJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiUlNBMV81In0.JsJeYoP0St1bRYNUaAmA34DAA27usE7RNuC2grGikBRmh1xrwUOpnEIXXpwr7fjVmNi52zzWkNHC8JkkRTrLcCh2VXvnOnarpH8DCr9qM6440bSrahzbxIvDds8z8q0wT1W4kjVnq1mGwGxg8RQNBWTV6Sp2FLQkZyjzt_aXsgYzr3zEmLZxB-d41lBS81Mguk_hdFJIg_WO4ao54lozvxkCn_uMiIZ8eLb8qHy0h-N21tiHGCaiC2vV8KXomwoqbJ0SXrEH4r9_R2J844H80TBZdbvNBd8whvoQNHvOX659LNs9EQ9xxvHU2kqGZekXBu7sDXXTjctMkMITobGSzw.1v5govaDvanP3LGp.llwYNBDrD7MwVLaFHesljlratfmndWs4XPQ.ZGT1zk9_yIKi2GzW6CuAyA",
"eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBMV81In0.fBv3fA3TMS3ML8vlsCuvwdsKvB0ym8R30jJrlOiqkWKk7WVUkjDInFzr1zw3Owla6c5BqOJNoACXt4IWbkLbkoWV3tweXlWwpafuaWPkjLOUH_K31rS2fCX5x-MTj8_hScquVQXpbz3vk2EfulRmGXZc_8JU2NqQCAsYy3a28houqP3rDe5jEAvZS2SOFvJkKW--f5S-z39t1D7fNz1N8Btd9SmXWQzjbul5YNxI9ctqxhJpkKYpxOLlvrzdA6YdJjOlDx3n6S-HnSZGM6kQd_xKtAf8l1EGwhQmhbXhMhjVxMvGwE5BX7PAb8Ccde5bzOCJx-PVbVetuLb169ZYqQ._jiZbOPRR82FEWMZ.88j68LI-K2KT6FMBEdlz6amG5nvaJU8a-90.EnEbUTJsWNqJYKzfO0x4Yw",
"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBMV81In0.bN6FN0qmGxhkESiVukrCaDVG3woL0xE-0bHN_Mu0WZXTQWbzzT-7jOvaN1xhGK8nzi8qpCSRgE5onONNB9i8OnJm3MMIxF7bUUEAXO9SUAFn2v--wNc4drPc5OjIu0RiJrDVDkkGjNrBDIuBaEQcke7A0v91PH58dXE7o4TLPzC8UJmRtXWhUSwjXVF3-UmYRMht2rjHJlvRbtm6Tu2LMBIopRL0zj6tlPP4Dm7I7sz9OEB3VahYAhpXnFR7D_f8RjLSXQmBvB1FiI5l_vMz2NFt2hYUmQF3EJMLIEdHvvPp3iHDGiXC1obJrDID_CCf3qs9UY7DMYL622KLvP2NIg.qb72oxECzxd_aNuHVR0aNg.Gwet9Ms8hB8rKEb0h4RGdFNRq97Qs2LQaJM0HWrCqoI.03ljVThOFvgXzMmQJ79VjQ",
"eyJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiUlNBMV81In0.ZbEOP6rqdiIP4g7Nl1PL5gwhgDwv9RinyiUQxZXPOmD7kwEZrZ093dJnhqI9kEd3QGFlHDpB7HgNz53d27z2zmEj1-27v6miizq6tH4sN2MoeZLwSyk16O1_n3bVdDmROawsTYYFJfHsuLwyVJxPd37duIYnbUCFO9J8lLIv-2VI50KJ1t47YfE4P-Wt9jVzxP2CVUQaJwTlcwfiDLJTagYmfyrDjf525WlQFlgfJGqsJKp8BX9gmKvAo-1iCBAM8VpEjS0u0_hW9VSye36yh8BthVV-VJkhJ-0tMpto3bbBmj7M25Xf4gbTrrVU7Nz6wb18YZuhHZWmj2Y2nHV6Jg.AjnS44blTrIIfFlqVw0_Mg.muCRgaEXNKKpW8rMfW7jf7Zpn3VwSYDz-JTRg16jZxY.qjc9OGlMaaWKDWQSIwVpR4K556Pp6SF9",
"eyJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiUlNBMV81In0.c7_F1lMlRHQQE3WbKmtHBYTosdZrG9hPfs-F9gNQYet61zKG8NXVkSy0Zf2UFHt0vhcO8hP2qrqOFsy7vmRj20xnGHQ2EE29HH6hwX5bx1Jj3uE5WT9Gvh0OewpvF9VubbwWTIObBpdEG7XdJsMAQlIxtXUmQYAtLTWcy2ZJipyJtVlWQLaPuE8BKfZH-XAsp2CpQNiRPI8Ftza3EAspiyRfVQbjKt7nF8nuZ2sESjt7Y50q4CSiiCuGT28T3diMN0_rWrH-I-xx7OQvJlrQaNGglGtu3jKUcrJDcvxW2e1OxriaTeuQ848ayuRvGUNeSv6WoVYmkiK1x_gNwUAAbw.7XtSqHJA7kjt6JrfxJMwiA.Yvi4qukAbdT-k-Fd2s4G8xzL4VFxaFC0ZIzgFDAI6n0.JSWPJ-HjOE3SK9Lm0yHclmjS7Z1ahtQga9FHGCWVRcc",
"eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.SYVxJbCnJ_tcR13LJpaqHQj-nGNkMxre4A1FmnUdxnvzeJwuvyrLiUdRsZR1IkP4fqLtDON2mumx39QeJQf0WIObPBYlIxycRLkwxDHRVlyTmPvdZHAxN26jPrk09wa5SgK1UF1W1VSQIPm-Tek8jNAmarF1Yxzxl-t54wZFlQiHP4TuaczugO5f-J4nlWenfla2mU1snDgdUMlEZGOAQ_gTEtwSgd1MqXmK_7LZBkoDqqoCujMZhziafJPXPDaUUqBLW3hHkkDA7GpVec3XcTtNUWQJqOpMyQhqo1KQMc8jg3fuirILp-hjvvNVtBnCRBvbrKUCPzu2_yH3HM_agA.2VsdijtonAxShNIW.QzzB3P9CxYP3foNKN0Ma1Z9tMwijAlkWo08.ZdQkIPDY_M-hxqi5fD4NGw",
"eyJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.Z2oTJXXib1u-S38Vn3DRKE3JnhnwgUa92UhsefzY2Wpdn0dmxMfYt9iRoJGFfSAcA97MOfjyvXVRCKWXGrG5AZCMAXEqU8SNQwKPRjlcqojcVzQyMucXI0ikLC4mUgeRlfKTwsBicq6JZZylzRoLGGSNJQbni3_BLsf7H3Qor0BYg0FPCLG9Z2OVvrFzvjTLmZtV6gFlVrMHBxJub_aUet9gAkxiu1Wx_Kx46TlLX2tkumXIpTGlzX6pef6jLeZ5EIg_K-Uz4tkWgWQIEkLD7qmTyk5pAGmzukHa_08jIh5-U-Sd8XGZdx4J1pVPJ5CPg0qDJGZ_cfgkgpWbP_wB6A.4qgKfokK1EwYxz20._Md82bv_KH2Vru0Ue2Eb6oAqHP2xBBP5jF8.WFRojvQpD5VmZlOr_dN0rQ",
"eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.JzCUgJcBJmBgByp4PBAABUfhezPvndxBIVzaoZ96DAS0HPni0OjMbsOGsz6JwNsiTr1gSn_S6R1WpZM8GJc9R2z0EKKVP67TR62ZSG0MEWyLpHmG_4ug0fAp1HWWMa9bT4ApSaOLgwlpVAb_-BPZZgIu6c8cREuMon6UBHDqW1euTBbzk8zix3-FTZ6p5b_3soDL1wXfRiRBEsxxUGMnpryx1OFb8Od0JdyGF0GgfLt6OoaujDJpo-XtLRawu1Xlg6GqRs0NQwSHZ5jXgQ6-zgCufXonAmYTiIyBXY2no9XmECTexjwrS_05nA7H-UyIZEBOCp3Yhz2zxrt5j_0pvQ.SJR-ghhaUKP4zXtZ.muiuzLfZA0y0BDNsroGTw2r2-l73SLf9lK8.XFMH1oHr1G6ByP3dWSUUPA",
"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBLU9BRVAifQ.U946MVfIm4Dpk_86HrnIA-QXyiUu0LZ67PL93CMLmEtJemMNDqmRd9fXyenCIhAC7jPIV1aaqW7gS194xyrrnUpBoJBdbegiPqOfquy493Iq_GQ8OXnFxFibPNQ6rU0l8BwIfh28ei_VIF2jqN6bhxFURCVW7fG6n6zkCCuEyc7IcxWafSHjH2FNttREuVj-jS-4LYDZsFzSKbpqoYF6mHt8H3btNEZDTSmy_6v0fV1foNtUKNfWopCp-iE4hNh4EzJfDuU8eXLhDb03aoOockrUiUCh-E0tQx9su4rOv-mDEOHHAQK7swm5etxoa7__9PC3Hg97_p4GM9gC9ykNgw.pnXwvoSPi0kMQP54of-HGg.RPJt1CMWs1nyotx1fOIfZ8760mYQ69HlyDp3XmdVsZ8.Yxw2iPVWaBROFE_FGbvodA",
"eyJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiUlNBLU9BRVAifQ.eKEOIJUJpXmO_ghH_nGCJmoEspqKyiy3D5l0P8lKutlo8AuYHPQlgOsaFYnDkypyUVWd9zi-JaQuCeo7dzoBiS1L71nAZo-SUoN0anQBkVuyuRjr-deJMhPPfq1H86tTk-4rKzPr1Ivd2RGXMtWsrUpNGk81r1v8DdMntLE7UxZQqT34ONuZg1IXnD_U6di7k07unI29zuU1ySeUr6w1YPw5aUDErMlpZcEJWrgOEYWaS2nuC8sWGlPGYEjqkACMFGn-y40UoS_JatNZO6gHK3SKZnXD7vN5NAaMo_mFNbh50e1t_zO8DaUdLtXPOBLcx_ULoteNd9H8HyDGWqwAPw.0xmtzJfeVMoIT1Cp68QrXA.841l1aA4c3uvSYfw6l180gn5JZQjL53WQ5fr8ejtvoI.lojzeWql_3gDq-AoaIbl_aGQRH_54w_f",
"eyJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiUlNBLU9BRVAifQ.D0QkvIXR1TL7dIHWuPNMybmmD8UPyQd1bRKjRDNbA2HmKGpamCtcJmpNB_EetNFe-LDmhe44BYI_XN2wIBbYURKgDK_WG9BH0LQw_nCVqQ-sKqjtj3yQeytXhLHYTDmiF0TO-uW-RFR7GbPAdARBfuf4zj82r_wDD9sD5WSCGx89iPfozDOYQ_OLwdL2WD99VvDyfwS3ZhxA-9IMSYv5pwqPkxj4C0JdjCqrN0YNrZn_1ORgjtsVmcWXsmusObTozUGA7n5GeVepfZdU1vrMulAwdRYqOYtlqKaOpFowe9xFN3ncBG7wb4f9pmzbS_Dgt-1_Ii_4SEB9GQ4NiuBZ0w.N4AZeCxMGUv52A0UVJsaZw.5eHOGbZdtahnp3l_PDY-YojYib4ft4SRmdsQ2kggrTs.WsmGH8ZDv4ctBFs7qsQvw2obe4dVToRcAQaZ3PYL34E",
"eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.fDTxO_ZzZ3Jdrdw-bxvg7u-xWB2q1tp3kI5zH6JfhLUm4h6rt9qDA_wZlRym8-GzEtkUjkTtQGs6HgQx_qlyy8ylCakY5GHsNhCG4m0UNhRiNfcasAs03JSXfON9-tfTJimWD9n4k5OHHhvcrsCW1G3jYeLsK9WHCGRIhNz5ULbo8HBrCTbmZ6bOEQ9mqhdssLpdV24HDpebotf3bgPJqoaTfWU6Uy7tLmPiNuuNRLQ-iTpLyNMTVvGqqZhpcV3lAEN5l77QabI5xLJYucvYjrXQhAEZ7YXO8oRYhGkdG2XXIRcwr87rBeRH-47HAyhZgF_PBPBhhrJNS9UNMqdfBw.FvU4_s7Md6vxnXWd.fw29Q4_gHt4f026DPPV-CNebQ8plJ6IVLX8._apBZrw7WsT8HOmxgCrTwA",
"eyJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.bYuorK-rHMbO4c2CRWtvyOEaM1EN-o-wLRZ0wFWRX9mCXQ-iTNarZn7ksYM1XnGmZ4u3CSowX1Hpca9Rg72_VJCmKapqCT7r3YfasN4_oeLwuSKI_gT-uVOznod97tn3Gf_EDv0y1V4H0k9BEIFGbajAcG1znTD_ODY3j2KZJxisfrsBoslc6N-HI0kKZMC2hSGuHOcOf8HN1sTE-BLqZCtoj-zxQECJK8Wh14Ih4jzzdmmiu_qmSR780K6su-4PRt3j8uY7oCiLBfwpCsCmhJgp8rKd91zoedZmamfvX38mJIfE52j4fG6HmIYw9Ov814fk9OffV6tzixjcg54Q2g.yeVJz4aSh2s-GUr9.TBzzWP5llEiDdugpP2SmPf2U4MEGG9EoPWk.g25UoWpsBaOd45J__FX7mA",
"eyJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.h9tFtmh762JuffBxlSQbJujCyI4Zs9yc3IOb1yR8g65W4ZHosIvzVGHWbShj4EY9MNrz-RbKtHfqQGGzDeo3Xb4-HcQ2ZDHyWoUg7VfA8JafJ5zIKL1npz8eUExOVMLsAaRfHg8qNfczodg3egoSmX5Q-nrx4DeidDSXYZaZjV0C72stLTPcuQ7XPV7z1tvERAkqpvcsRmJn_PiRNxIbAgoyHMJ4Gijuzt1bWZwezlxYmw0TEuwCTVC2fl9NJTZyxOntS1Lcm-WQGlPkVYeVgYTOQXLlp7tF9t-aAvYpth2oWGT6Y-hbPrjx_19WaKD0XyWCR46V32DlXEVDP3Xl2A.NUgfnzQyEaJjzt9r.k2To43B2YVWMeR-w3n4Pr2b5wYq2o87giHk.X8_QYCg0IGnn1pJqe8p_KA",
"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.EDq6cNP6Yp1sds5HZ4CkXYp7bs9plIYVZScKvuyxUy0H1VyBC_YWg0HvndPNb-vwh1LA6KMxRazlOwJ9iPR9YzHnYmGgPM3Je_ZzBfiPlRfq6hQBpGnNaypBI1XZ2tyFBhulsVLqyJe2SmM2Ud00kasOdMYgcN8FNFzq7IOE7E0FUQkIwLdUL1nrzepiYDp-5bGkxWRcL02cYfdqdm00G4m0GkUxAmdxa3oPNxZlt2NeBI_UVWQSgJE-DJVJQkDcyA0id27TV2RCDnmujYauNT_wYlyb0bFDx3pYzzNXfAXd4wHZxt75QaLZ5APJ0EVfiXJ0qki6kT-GRVmOimUbQA.vTULZL7LvS0WD8kR8ZUtLg.mb2f0StEmmkuuvsyz8UplMvF58FtZzlu8eEwzvPUvN0.hbhveEN40V-pgG2hSVgyKg",
"eyJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.DuYk92p7u-YIN-JKn-XThmlVcnhU9x5TieQ2uhsLQVNlo0iWC9JJPP6bT6aI6u_1BIS3yE8_tSGGL7eM-zyEk6LuTqSWFRaZcZC06d0MnS9eYZcw1T2D17fL-ki-NtCaTahJD7jE2s0HevRVW49YtL-_V8whnO_EyVjvXIAQlPYqhH_o-0Nzcpng9ggdAnuF2rY1_6iRPYFJ3BLQvG1oWhyJ9s6SBttlOa0i6mmFCVLHx6sRpdGAB3lbCL3wfmHq4tpIv77gfoYUNP0SNff-zNmBXF_wp3dCntLZFTjbfMpGyHlruF_uoaLqwdjYpUGNUFVUoeSiMnSbMKm9NxiDgQ.6Mdgcqz7bMU1UeoAwFC8pg.W36QWOlBaJezakUX5FMZzbAgeAu_R14AYKZCQmuhguw.5OeyIJ03olxmJft8uBmjuOFQPWNZMYLI",
"eyJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.ECulJArWFsPL2FlpCN0W8E7IseSjJg1cZqE3wz5jk9gvwgNForAUEv5KYZqhNI-p5IxkGV0f8K6Y2X8pWzbLwiPIjZe8_dVqHYJoINxqCSgWLBhz0V36qL9Nc_xARTBk4-ZteIu75NoXVeos9gNvFnkOCj4tm-jGo8z8EFO9XfODgjhiR4xv8VqUtvrkjo9GQConaga5zpV-J4JQlXbdqbDjnuwacnJAxYpFyuemqcgqsl6BnFX3tovGkmSUPqcvF1A6tiHqr-TEmcgVqo5C3xswknRBKTQRM00iAmJ92WlVdkoOCx6E6O7cVHFawZ14BLzWzm66Crb4tv0ucYvk_Q.mxolwUaoj5S5kHCfph0w8g.nFpgYdnYg3blHCCEi2XXQGkkKQBXs2OkZaH11m3PRvk.k8BAVT4EcyrUFVIKr-KOSPbF89xyL0Vri2rFTu2iIWM",
}
for _, msg := range rsaSampleMessages {
obj, err := ParseEncrypted(msg)
if err != nil {
t.Error("unable to parse message", msg, err)
continue
}
plaintext, err := obj.Decrypt(rsaPrivateKey)
if err != nil {
t.Error("unable to decrypt message", msg, err)
continue
}
if string(plaintext) != "Lorem ipsum dolor sit amet" {
t.Error("plaintext is not what we expected for msg", msg)
}
}
}
// Test vectors generated with nimbus-jose-jwt
func TestSampleNimbusJWEMessagesAESKW(t *testing.T) {
aesTestKeys := [][]byte{
fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D"),
fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D95EC9CDC2D82233C"),
fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D95EC9CDC2D82233C333C35BA29044E90"),
}
aesSampleMessages := [][]string{
[]string{
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwidGFnIjoib2ZMd2Q5NGloVWFRckJ0T1pQUDdjUSIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiV2Z3TnN5cjEwWUFjY2p2diJ9.9x3RxdqIS6P9xjh93Eu1bQ.6fs3_fSGt2jull_5.YDlzr6sWACkFg_GU5MEc-ZEWxNLwI_JMKe_jFA.f-pq-V7rlSSg_q2e1gDygw",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwidGFnIjoic2RneXB1ckFjTEFzTmZJU0lkZUNpUSIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoieVFMR0dCdDJFZ0c1THdyViJ9.arslKo4aKlh6f4s0z1_-U-8JbmhAoZHN.Xw2Q-GX98YXwuc4i.halTEWMWAYZbv-qOD52G6bte4x6sxlh1_VpGEA.Z1spn016v58cW6Q2o0Qxag",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwidGFnIjoicTNzejF5VUlhbVBDYXJfZ05kSVJqQSIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiM0ZRM0FsLWJWdWhmcEIyQyJ9.dhVipWbzIdsINttuZM4hnjpHvwEHf0VsVrOp4GAg01g.dk7dUyt1Qj13Pipw.5Tt70ONATF0BZAS8dBkYmCV7AQUrfb8qmKNLmw.A6ton9MQjZg0b3C0QcW-hg",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidGFnIjoiUHNpTGphZnJZNE16UlRmNlBPLTZfdyIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiSUFPbnd2ODR5YXFEaUxtbSJ9.swf92_LyCvjsvkynHTuMNXRl_MX2keU-fMDWIMezHG4.LOp9SVIXzs4yTnOtMyXZYQ.HUlXrzqJ1qXYl3vUA-ydezCg77WvJNtKdmZ3FPABoZw.8UYl1LOofQLAxHHvWqoTbg",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwidGFnIjoiWGRndHQ5dUVEMVlVeU1rVHl6M3lqZyIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiWF90V2RhSmh6X3J1SHJvQSJ9.JQ3dS1JSgzIFi5M9ig63FoFU1nHBTmPwXY_ovNE2m1JOSUvHtalmihIuraPDloCf.e920JVryUIWt7zJJQM-www.8DUrl4LmsxIEhRr9RLTHG9tBTOcwXqEbQHAJd_qMHzE.wHinoqGUhL4O7lx125kponpwNtlp8VGJ",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwidGFnIjoicGgyaTdoY0FWNlh3ZkQta1RHYlVXdyIsImFsZyI6IkExMjhHQ01LVyIsIml2IjoiaG41Smk4Wm1rUmRrSUxWVSJ9._bQlJXl22dhsBgYPhkxUyinBNi871teGWbviOueWj2PqG9OPxIc9SDS8a27YLSVDMircd5Q1Df28--vcXIABQA.DssmhrAg6w_f2VDaPpxTbQ.OGclEmqrxwvZqAfn7EgXlIfXgr0wiGvEbZz3zADnqJs.YZeP0uKVEiDl8VyC-s20YN-RbdyGNsbdtoGDP3eMof8",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiQTEyOEtXIn0.TEMcXEoY8WyqGjYs5GZgS-M_Niwu6wDY.i-26KtTt51Td6Iwd.wvhkagvPsLj3QxhPBbfH_th8OqxisUtme2UadQ.vlfvBPv3bw2Zk2H60JVNLQ",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiQTEyOEtXIn0.gPaR6mgQ9TUx05V6DRfgTQeZxl0ZSzBa5uQd-qw6yLs.MojplOD77FkMooS-.2yuD7dKR_C3sFbhgwiBccKKOF8DrSvNiwX7wPQ.qDKUbSvMnJv0qifjpWC14g",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiQTEyOEtXIn0.Fg-dgSkUW1KEaL5YDPoWHNL8fpX1WxWVLA9OOWsjIFhQVDKyUZI7BQ.mjRBpyJTZf7H-quf.YlNHezMadtaSKp23G-ozmYhHOeHwuJnvWGTtGg.YagnR7awBItUlMDo4uklvg",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTEyOEtXIn0.x1vYzUE-E2XBWva9OPuwtqfQaf9rlJCIBAyAe6N2q2kWfJrkxGxFsQ.gAwe78dyODFaoP2IOityAA.Yh5YfovkWxGBNAs1sVhvXow_2izHHsBiYEc9JYD6kVg.mio1p3ncp2wLEaEaRa7P0w",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiQTEyOEtXIn0.szGrdnmF7D5put2aRBvSSFfp0vRgkRGYaafijJIqAF6PWd1IxsysZRV8aQkQOW1cB6d0fXsTfYM.Ru25LVOOk4xhaK-cIZ0ThA.pF9Ok5zot7elVqXFW5YYHV8MuF9gVGzpQnG1XDs_g_w.-7la0uwcNPpteev185pMHZjbVDXlrec8",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiQTEyOEtXIn0.cz-hRv0xR5CnOcnoRWNK8Q9poyVYzRCVTjfmEXQN6xPOZUkJ3zKNqb8Pir_FS0o2TVvxmIbuxeISeATTR2Ttx_YGCNgMkc93.SF5rEQT94lZR-UORcMKqGw.xphygoU7zE0ZggOczXCi_ytt-Evln8CL-7WLDlWcUHg.5h99r8xCCwP2PgDbZqzCJ13oFfB2vZWetD5qZjmmVho",
},
[]string{
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwidGFnIjoiVWR5WUVKdEJ5ZTA5dzdjclY0cXI1QSIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiZlBBV0QwUmdSbHlFdktQcCJ9.P1uTfTuH-imL-NJJMpuTRA.22yqZ1NIfx3KNPgc.hORWZaTSgni1FS-JT90vJly-cU37qTn-tWSqTg.gMN0ufXF92rSXupTtBNkhA",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwidGFnIjoiOU9qX3B2LTJSNW5lZl9YbWVkUWltUSIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiY3BybGEwYUYzREVQNmFJTSJ9.6NVpAm_APiC7km2v-oNR8g23K9U_kf1-.jIg-p8tNwSvwxch0.1i-GPaxS4qR6Gy4tzeVtSdRFRSKQSMpmn-VhzA.qhFWPqtA6vVPl7OM3DThsA",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwidGFnIjoiOVc3THg3MVhGQVJCb3NaLVZ5dXc4ZyIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiZ1N4ZE5heFdBSVBRR0tHYiJ9.3YjPz6dVQwAtCekvtXiHZrooOUlmCsMSvyfwmGwdrOA.hA_C0IDJmGaRzsB0.W4l7OPqpFxiVOZTGfAlRktquyRTo4cEOk9KurQ.l4bGxOkO_ql_jlPo3Oz3TQ",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidGFnIjoiOHJYbWl2WXFWZjNfbHhhd2NUbHJoUSIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiVXBWeXprVTNKcjEwYXRqYyJ9.8qft-Q_xqUbo5j_aVrVNHchooeLttR4Kb6j01O8k98M.hXO-5IKBYCL9UdwBFVm0tg.EBM4lCZX_K6tfqYmfoDxVPHcf6cT--AegXTTjfSqsIw.Of8xUvEQSh3xgFT3uENnAg",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwidGFnIjoiVnItSnVaX0tqV2hSWWMzdzFwZ3cwdyIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoiRGg2R3dISVBVS3ljZGNZeCJ9.YSEDjCnGWr_n9H94AvLoRnwm6bdU9w6-Q67k-QQRVcKRd6673pgH9zEF9A9Dt6o1.gcmVN4kxqBuMq6c7GrK3UQ.vWzJb0He6OY1lhYYjYS7CLh55REAAq1O7yNN-ND4R5Q.OD0B6nwyFaDr_92ysDOtlVnJaeoIqhGw",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwidGFnIjoieEtad1BGYURpQ3NqUnBqZUprZHhmZyIsImFsZyI6IkExOTJHQ01LVyIsIml2IjoieTVHRFdteXdkb2R1SDJlYyJ9.AW0gbhWqlptOQ1y9aoNVwrTIIkBfrp33C2OWJsbrDRk6lhxg_IgFhMDTE37moReySGUtttC4CXQD_7etHmd3Hw.OvKXK-aRKlXHOpJQ9ZY_YQ.Ngv7WarDDvR2uBj_DavPAR3DYuIaygvSSdcHrc8-ZqM.MJ6ElitzFCKf_0h5fIJw8uOLC6ps7dKZPozF8juQmUY",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiQTE5MktXIn0.8qu63pppcSvp1vv37WrZ44qcCTg7dQMA.cDp-f8dJTrDEpZW4.H6OBJYs4UvFR_IZHLYQZxB6u9a0wOdAif2LNfQ.1dB-id0UIwRSlmwHx5BJCg",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiQTE5MktXIn0._FdoKQvC8qUs7K0upriEihUwztK8gOwonXpOxdIwrfs.UO38ok8gDdpLVa1T.x1GvHdVCy4fxoQRg-OQK4Ez3jDOvu9gllLPeEA.3dLeZGIprh_nHizOTVi1xw",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiQTE5MktXIn0.uzCJskgSIK6VkjJIu-dQi18biqaY0INc_A1Ehx0oESafgtR99_n4IA.W2eKK8Y14WwTowI_.J2cJC7R6Bz6maR0s1UBMPyRi5BebNUAmof4pvw.-7w6htAlc4iUsOJ6I04rFg",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTE5MktXIn0.gImQeQETp_6dfJypFDPLlv7c5pCzuq86U16gzrLiCXth6X9XfxJpvQ.YlC4MxjtLWrsyEvlFhvsqw.Vlpvmg9F3gkz4e1xG01Yl2RXx-jG99rF5UvCxOBXSLc.RZUrU_FoR5bG3M-j3GY0Dw",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiQTE5MktXIn0.T2EfQ6Tu2wJyRMgZzfvBYmQNCCfdMudMrg86ibEMVAOUKJPtR3WMPEb_Syy9p2VjrLKRlv7nebo.GPc8VbarPPRtzIRATB8NsA.ugPCqLvVLwh55bWlwjsFkmWzJ31z5z-wuih2oJqmG_U.m7FY3EjvV6mKosEYJ5cY7ezFoVQoJS8X",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiQTE5MktXIn0.OgLMhZ-2ZhslQyHfzOfyC-qmT6bNg9AdpP59B4jtyxWkQu3eW475WCdiAjojjeyBtVRGQ5vOomwaOIFejY_IekzH6I_taii3.U9x44MF6Wyz5TIwIzwhoxQ.vK7yvSF2beKdNxNY_7n4XdF7JluCGZoxdFJyTJVkSmI.bXRlI8KL-g7gpprQxGmXjVYjYghhWJq7mlCfWI8q2uA",
},
[]string{
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwidGFnIjoiR3BjX3pfbjduZjJVZlEtWGdsaTBaQSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiUk40eUdhOVlvYlFhUmZ1TCJ9.Q4ukD6_hZpmASAVcqWJ9Wg.Zfhny_1WNdlp4fH-.3sekDCjkExQCcv28ZW4yrcFnz0vma3vgoenSXA.g8_Ird2Y0itTCDP61du-Yg",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwidGFnIjoiWC05UkNVWVh4U3NRelcwelVJS01VUSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiY3JNMnJfa3RrdWpyQ1h5OSJ9.c0q2jCxxV4y1h9u_Xvn7FqUDnbkmNEG4.S_noOTZKuUo9z1l6.ez0RdA25vXMUGH96iXmj3DEVox0J7TasJMnzgg.RbuSPTte_NzTtEEokbc5Ig",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwidGFnIjoiWmwyaDFpUW11QWZWd2lJeVp5RHloZyIsImFsZyI6IkEyNTZHQ01LVyIsIml2Ijoib19xZmljb0N0NzNzRWo1QyJ9.NpJxRJ0aqcpekD6HU2u9e6_pL_11JXjWvjfeQnAKkZU.4c5qBcBBrMWi27Lf.NKwNIb4b6cRDJ1TwMKsPrjs7ADn6aNoBdQClVw.yNWmSSRBqQfIQObzj8zDqw",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidGFnIjoiMXdwVEI3LWhjdzZUVXhCbVh2UzdhUSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiOUdIVnZJaDZ0a09vX2pHUSJ9.MFgIhp9mzlq9hoPqqKVKHJ3HL79EBYtV4iNhD63yqiU.UzW5iq8ou21VpZYJgKEN8A.1gOEzA4uAPvHP76GMfs9uLloAV10mKaxiZVAeL7iQA0.i1X_2i0bCAz-soXF9bI_zw",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwidGFnIjoiNThocUtsSk15Y1BFUEFRUlNfSzlNUSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiUDh3aTBWMTluVnZqNXpkOSJ9.FXidOWHNFJODO74Thq3J2cC-Z2B8UZkn7SikeosU0bUK6Jx_lzzmUZ-Lafadpdpj.iLfcDbpuBKFiSfiBzUQc7Q.VZK-aD7BFspqfvbwa0wE2wwWxdomzk2IKMetFe8bI44.7wC6rJRGa4x48xbYMd6NH9VzK8uNn4Cb",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwidGFnIjoicGcwOEpUcXdzMXdEaXBaRUlpVExoQSIsImFsZyI6IkEyNTZHQ01LVyIsIml2IjoiSlpodk9CdU1RUDFFZTZTNSJ9.wqVgTPm6TcYCTkpbwmn9sW4mgJROH2A3dIdSXo5oKIQUIVbQsmy7KXH8UYO2RS9slMGtb869C8o0My67GKg9dQ.ogrRiLlqjB1S5j-7a05OwA.2Y_LyqhU4S_RXMsB74bxcBacd23J2Sp5Lblw-sOkaUY.XGMiYoU-f3GaEzSvG41vpJP2DMGbeDFoWmkUGLUjc4M",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiQTI1NktXIn0.QiIZm9NYfahqYFIbiaoUhCCHjotHMkup.EsU0XLn4FjzzCILn.WuCoQkm9vzo95E7hxBtfYpt-Mooc_vmSTyzj6Q.NbeeYVy6gQPlmhoWDrZwaQ",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyR0NNIiwiYWxnIjoiQTI1NktXIn0.1ol3j_Lt0Os3UMe2Gypj0o8b77k0FSmqD7kNRNoMa9U.vZ2HMTgN2dgUd42h.JvNcy8-c8sYzOC089VtFSg2BOQx3YF8CqSTuJw.t03LRioWWKN3d7SjinU6SQ",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiQTI1NktXIn0.gbkk03l1gyrE9qGEMVtORiyyUqKsgzbqjLd8lw0RQ07WWn--TV4BgA.J8ThH4ac2UhSsMIP.g-W1piEGrdi3tNwQDJXpYm3fQjTf82mtVCrCOg.-vY05P4kiB9FgF2vwrSeXQ",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiQTI1NktXIn0.k86pQs7gmQIzuIWRFwesF32XY2xi1WbYxi7XUf_CYlOlehwGCTINHg.3NcC9VzfQgsECISKf4xy-g.v2amdo-rgeGsg-II_tvPukX9D-KAP27xxf2uQJ277Ws.E4LIE3fte3glAnPpnd8D9Q",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0IiwiYWxnIjoiQTI1NktXIn0.b8iN0Am3fCUvj7sBd7Z0lpfzBjh1MOgojV7J5rDfrcTU3b35RGYgEV1RdcrtUTBgUwITDjmU7jM.wsSDBFghDga_ERv36I2AOg.6uJsucCb2YReFOJGBdo4zidTIKLUmZBIXfm_M0AJpKk.YwdAfXI3HHcw2wLSnfCRtw4huZQtSKhz",
"eyJ6aXAiOiJERUYiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYWxnIjoiQTI1NktXIn0.akY9pHCbkHPh5VpXIrX0At41XnJIKBR9iMMkf301vKeJNAZYJTxWzeJhFd-DhQ47tMctc3YYkwZkQ5I_9fGYb_f0oBcw4esh.JNwuuHud78h6S99NO1oBQQ.0RwckPYATBgvw67upkAQ1AezETHc-gh3rryz19i5ryc.3XClRTScgzfMgLCHxHHoRF8mm9VVGXv_Ahtx65PskKQ",
},
}
for i, msgs := range aesSampleMessages {
for _, msg := range msgs {
obj, err := ParseEncrypted(msg)
if err != nil {
t.Error("unable to parse message", msg, err)
continue
}
plaintext, err := obj.Decrypt(aesTestKeys[i])
if err != nil {
t.Error("unable to decrypt message", msg, err)
continue
}
if string(plaintext) != "Lorem ipsum dolor sit amet" {
t.Error("plaintext is not what we expected for msg", msg)
}
}
}
}
// Test vectors generated with jose4j
func TestSampleJose4jJWEMessagesECDH(t *testing.T) {
ecTestKey := &ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
Curve: elliptic.P256(),
X: fromBase64Int("weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ"),
Y: fromBase64Int("e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck"),
},
D: fromBase64Int("VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw"),
}
ecSampleMessages := []string{
"eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOENCQy1IUzI1NiIsImVwayI6eyJrdHkiOiJFQyIsIngiOiJTQzAtRnJHUkVvVkpKSmg1TGhORmZqZnFXMC1XSUFyd3RZMzJzQmFQVVh3IiwieSI6ImFQMWlPRENveU9laTVyS1l2VENMNlRMZFN5UEdUN0djMnFsRnBwNXdiWFEiLCJjcnYiOiJQLTI1NiJ9fQ..3mifklTnTTGuA_etSUBBCw.dj8KFM8OlrQ3rT35nHcHZ7A5p84VB2OZb054ghSjS-M.KOIgnJjz87LGqMtikXGxXw",
"eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTE5MkNCQy1IUzM4NCIsImVwayI6eyJrdHkiOiJFQyIsIngiOiJUaHRGc0lRZ1E5MkZOYWFMbUFDQURLbE93dmNGVlRORHc4ampfWlJidUxjIiwieSI6IjJmRDZ3UXc3YmpYTm1nVThXMGpFbnl5ZUZkX3Y4ZmpDa3l1R29vTFhGM0EiLCJjcnYiOiJQLTI1NiJ9fQ..90zFayMkKc-fQC_19f6P3A.P1Y_7lMnfkUQOXW_en31lKZ3zAn1nEYn6fXLjmyVPrQ.hrgwy1cePVfhMWT0h-crKTXldglHZ-4g",
"eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTI1NkNCQy1IUzUxMiIsImVwayI6eyJrdHkiOiJFQyIsIngiOiI5R1Z6c3VKNWgySl96UURVUFR3WU5zUkFzVzZfY2RzN0pELVQ2RDREQ1ZVIiwieSI6InFZVGl1dVU4aTB1WFpoaS14VGlRNlZJQm5vanFoWENPVnpmWm1pR2lRTEUiLCJjcnYiOiJQLTI1NiJ9fQ..v2reRlDkIsw3eWEsTCc1NA.0qakrFdbhtBCTSl7EREf9sxgHBP9I-Xw29OTJYnrqP8.54ozViEBYYmRkcKp7d2Ztt4hzjQ9Vb5zCeijN_RQrcI",
"eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiOElUemg3VVFaaUthTWtfME9qX1hFaHZENXpUWjE2Ti13WVdjeTJYUC1tdyIsInkiOiJPNUJiVEk0bUFpU005ZmpCejBRU3pXaU5vbnl3cWlQLUN0RGgwdnNGYXNRIiwiY3J2IjoiUC0yNTYifX0.D3DP3wqPvJv4TYYfhnfrOG6nsM-MMH_CqGfnOGjgdXHNF7xRwEJBOA.WL9Kz3gNYA7S5Rs5mKcXmA.EmQkXhO_nFqAwxJWaM0DH4s3pmCscZovB8YWJ3Ru4N8.Bf88uzwfxiyTjpejU5B0Ng",
"eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkExOTJDQkMtSFMzODQiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiMjlJMk4zRkF0UlBlNGhzYjRLWlhTbmVyV0wyTVhtSUN1LXJJaXhNSHpJQSIsInkiOiJvMjY1bzFReEdmbDhzMHQ0U1JROS00RGNpc3otbXh4NlJ6WVF4SktyeWpJIiwiY3J2IjoiUC0yNTYifX0.DRmsmXz6fCnLc_njDIKdpM7Oc4jTqd_yd9J94TOUksAstEUkAl9Ie3Wg-Ji_LzbdX2xRLXIimcw.FwJOHPQhnqKJCfxt1_qRnQ.ssx3q1ZYILsMTln5q-K8HVn93BVPI5ViusstKMxZzRs.zzcfzWNYSdNDdQ4CiHfymj0bePaAbVaT",
"eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiRUp6bTViQnRzVXJNYTl2Y1Q2d1hZRXI3ZjNMcjB0N1V4SDZuZzdGcFF0VSIsInkiOiJRYTNDSDllVTFXYjItdFdVSDN3Sk9fTDVMZXRsRUlMQWNkNE9XR2tFd0hZIiwiY3J2IjoiUC0yNTYifX0.5WxwluZpVWAOJdVrsnDIlEc4_wfRE1gXOaQyx_rKkElNz157Ykf-JsAD7aEvXfx--NKF4js5zYyjeCtxWBhRWPOoNNZJlqV_.Iuo82-qsP2S1SgQQklAnrw.H4wB6XoLKOKWCu6Y3LPAEuHkvyvr-xAh4IBm53uRF8g._fOLKq0bqDZ8KNjni_MJ4olHNaYz376dV9eNmp9O9PU",
"eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiZktNSG5sRkoxajBTSnJ3WGtVWlpaX3BtWHdUQlJtcHhlaTkxdUpaczUycyIsInkiOiJLRkxKaXhEUTJQcjEybWp1aFdYb3pna2U1V3lhWnhmTWlxZkJ0OEJpbkRvIiwiY3J2IjoiUC0yNTYifX0.2LSD2Mw4tyYJyfsmpVmzBtJRd12jMEYGdlhFbaXIbKi5A33CGNQ1tg.s40aAjmZOvK8Us86FCBdHg.jpYSMAKp___oMCoWM495mTfbi_YC80ObeoCmGE3H_gs.A6V-jJJRY1yz24CaXGUbzg",
"eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkExOTJDQkMtSFMzODQiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiSDRxcFUzeWtuRktWRnV4SmxLa3NZSE5ieHF3aXM0WWtCVVFHVE1Td05JQSIsInkiOiJHb0lpRUZaUGRRSHJCbVR4ZTA3akJoZmxrdWNqUjVoX1QwNWVXc3Zib0prIiwiY3J2IjoiUC0yNTYifX0.KTrwwV2uzD--gf3PGG-kjEAGgi7u0eMqZPZfa4kpyFGm3x8t2m1NHdz3t9rfiqjuaqsxPKhF4gs.cu16fEOzYaSxhHu_Ht9w4g.BRJdxVBI9spVtY5KQ6gTR4CNcKvmLUMKZap0AO-RF2I.DZyUaa2p6YCIaYtjWOjC9GN_VIYgySlZ",
"eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoieDBYSGRkSGM2Q0ktSnlfbUVMOEZZRExhWnV0UkVFczR4c3BMQmcwZk1jbyIsInkiOiJEa0xzOUJGTlBkTTVTNkpLYVJ3cnV1TWMwcUFzWW9yNW9fZWp6NXBNVXFrIiwiY3J2IjoiUC0yNTYifX0.mfCxJ7JYIqTMqcAh5Vp2USF0eF7OhOeluqda7YagOUJNwxA9wC9o23DSoLUylfrZUfanZrJJJcG69awlv-LY7anOLHlp3Ht5.ec48A_JWb4qa_PVHWZaTfQ.kDAjIDb3LzJpfxNh-DiAmAuaKMYaOGSTb0rkiJLuVeY.oxGCpPlii4pr89XMk4b9s084LucTqPGU6TLbOW2MZoc",
"eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiQXB5TnlqU2d0bmRUcFg0eENYenNDRnZva1l3X18weXg2dGRUYzdPUUhIMCIsInkiOiJYUHdHMDVDaW1vOGlhWmxZbDNsMEp3ZllhY1FZWHFuM2RRZEJUWFpldDZBIiwiY3J2IjoiUC0yNTYifX0.yTA2PwK9IPqkaGPenZ9R-gOn9m9rvcSEfuX_Nm8AkuwHIYLzzYeAEA.ZW1F1iyHYKfo-YoanNaIVg.PouKQD94DlPA5lbpfGJXY-EJhidC7l4vSayVN2vVzvA.MexquqtGaXKUvX7WBmD4bA",
"eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExOTJDQkMtSFMzODQiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiaDRWeGNzNVUzWk1fTlp4WmJxQ3hMTVB5UmEtR2ktSVNZa0xDTzE1RHJkZyIsInkiOiJFeVotS3dWNVE5OXlnWk5zU0lpSldpR3hqbXNLUk1WVE5sTTNSd1VYTFRvIiwiY3J2IjoiUC0yNTYifX0.wo56VISyL1QAbi2HLuVut5NGF2FvxKt7B8zHzJ3FpmavPozfbVZV08-GSYQ6jLQWJ4xsO80I4Kg.3_9Bo5ozvD96WHGhqp_tfQ.48UkJ6jk6WK70QItb2QZr0edKH7O-aMuVahTEeqyfW4.ulMlY2tbC341ct20YSmNdtc84FRz1I4g",
"eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJlcGsiOnsia3R5IjoiRUMiLCJ4IjoiN0xZRzZZWTJkel9ZaGNvNnRCcG1IX0tPREQ2X2hwX05tajdEc1c2RXgxcyIsInkiOiI5Y2lPeDcwUkdGT0tpVnBRX0NHQXB5NVlyeThDazBmUkpwNHVrQ2tjNmQ0IiwiY3J2IjoiUC0yNTYifX0.bWwW3J80k46HG1fQAZxUroko2OO8OKkeRavr_o3AnhJDMvp78OR229x-fZUaBm4uWv27_Yjm0X9T2H2lhlIli2Rl9v1PNC77.1NmsJBDGI1fDjRzyc4mtyA.9KfCFynQj7LmJq08qxAG4c-6ZPz1Lh3h3nUbgVwB0TI.cqech0d8XHzWfkWqgKZq1SlAfmO0PUwOsNVkuByVGWk",
}
for _, msg := range ecSampleMessages {
obj, err := ParseEncrypted(msg)
if err != nil {
t.Error("unable to parse message", msg, err)
continue
}
plaintext, err := obj.Decrypt(ecTestKey)
if err != nil {
t.Error("unable to decrypt message", msg, err)
continue
}
if string(plaintext) != "Lorem ipsum dolor sit amet." {
t.Error("plaintext is not what we expected for msg", msg)
}
}
}

View File

@ -17,6 +17,7 @@
package jose
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
@ -24,10 +25,12 @@ import (
"fmt"
"math/big"
"reflect"
"strings"
)
// rawJsonWebKey represents a public or private key in JWK format, used for parsing/serializing.
type rawJsonWebKey struct {
Use string `json:"use,omitempty"`
Kty string `json:"kty,omitempty"`
Kid string `json:"kid,omitempty"`
Crv string `json:"crv,omitempty"`
@ -53,6 +56,7 @@ type JsonWebKey struct {
Key interface{}
KeyID string
Algorithm string
Use string
}
// MarshalJSON serializes the given key to its JSON representation.
@ -70,7 +74,7 @@ func (k JsonWebKey) MarshalJSON() ([]byte, error) {
case *rsa.PrivateKey:
raw, err = fromRsaPrivateKey(key)
default:
return nil, fmt.Errorf("square/go-jose: unkown key type '%s'", reflect.TypeOf(key))
return nil, fmt.Errorf("square/go-jose: unknown key type '%s'", reflect.TypeOf(key))
}
if err != nil {
@ -79,6 +83,7 @@ func (k JsonWebKey) MarshalJSON() ([]byte, error) {
raw.Kid = k.KeyID
raw.Alg = k.Algorithm
raw.Use = k.Use
return json.Marshal(raw)
}
@ -110,11 +115,79 @@ func (k *JsonWebKey) UnmarshalJSON(data []byte) (err error) {
}
if err == nil {
*k = JsonWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg}
*k = JsonWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use}
}
return
}
// JsonWebKeySet represents a JWK Set object.
type JsonWebKeySet struct {
Keys []JsonWebKey `json:"keys"`
}
// Key convenience method returns keys by key ID. Specification states
// that a JWK Set "SHOULD" use distinct key IDs, but allows for some
// cases where they are not distinct. Hence method returns a slice
// of JsonWebKeys.
func (s *JsonWebKeySet) Key(kid string) []JsonWebKey {
var keys []JsonWebKey
for _, key := range s.Keys {
if key.KeyID == kid {
keys = append(keys, key)
}
}
return keys
}
const rsaThumbprintTemplate = `{"e":"%s","kty":"RSA","n":"%s"}`
const ecThumbprintTemplate = `{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`
func ecThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) {
coordLength := curveSize(curve)
crv, err := curveName(curve)
if err != nil {
return "", err
}
return fmt.Sprintf(ecThumbprintTemplate, crv,
newFixedSizeBuffer(x.Bytes(), coordLength).base64(),
newFixedSizeBuffer(y.Bytes(), coordLength).base64()), nil
}
func rsaThumbprintInput(n *big.Int, e int) (string, error) {
return fmt.Sprintf(rsaThumbprintTemplate,
newBufferFromInt(uint64(e)).base64(),
newBuffer(n.Bytes()).base64()), nil
}
// Thumbprint computes the JWK Thumbprint of a key using the
// indicated hash algorithm.
func (k *JsonWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
var input string
var err error
switch key := k.Key.(type) {
case *ecdsa.PublicKey:
input, err = ecThumbprintInput(key.Curve, key.X, key.Y)
case *ecdsa.PrivateKey:
input, err = ecThumbprintInput(key.Curve, key.X, key.Y)
case *rsa.PublicKey:
input, err = rsaThumbprintInput(key.N, key.E)
case *rsa.PrivateKey:
input, err = rsaThumbprintInput(key.N, key.E)
default:
return nil, fmt.Errorf("square/go-jose: unkown key type '%s'", reflect.TypeOf(key))
}
if err != nil {
return nil, err
}
h := hash.New()
h.Write([]byte(input))
return h.Sum(nil), nil
}
func (key rawJsonWebKey) rsaPublicKey() (*rsa.PublicKey, error) {
if key.N == nil || key.E == nil {
return nil, fmt.Errorf("square/go-jose: invalid RSA key, missing n/e values")
@ -160,32 +233,50 @@ func (key rawJsonWebKey) ecPublicKey() (*ecdsa.PublicKey, error) {
func fromEcPublicKey(pub *ecdsa.PublicKey) (*rawJsonWebKey, error) {
if pub == nil || pub.X == nil || pub.Y == nil {
return nil, fmt.Errorf("square/go-jose: invalid EC key")
return nil, fmt.Errorf("square/go-jose: invalid EC key (nil, or X/Y missing)")
}
name, err := curveName(pub.Curve)
if err != nil {
return nil, err
}
size := curveSize(pub.Curve)
xBytes := pub.X.Bytes()
yBytes := pub.Y.Bytes()
if len(xBytes) > size || len(yBytes) > size {
return nil, fmt.Errorf("square/go-jose: invalid EC key (X/Y too large)")
}
key := &rawJsonWebKey{
Kty: "EC",
X: newBuffer(pub.X.Bytes()),
Y: newBuffer(pub.Y.Bytes()),
}
switch pub.Curve {
case elliptic.P256():
key.Crv = "P-256"
case elliptic.P384():
key.Crv = "P-384"
case elliptic.P521():
key.Crv = "P-521"
default:
return nil, fmt.Errorf("square/go-jose: unsupported/unknown elliptic curve")
Crv: name,
X: newFixedSizeBuffer(xBytes, size),
Y: newFixedSizeBuffer(yBytes, size),
}
return key, nil
}
func (key rawJsonWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) {
if key.N == nil || key.E == nil || key.D == nil || key.P == nil || key.Q == nil {
return nil, fmt.Errorf("square/go-jose: invalid RSA private key, missing values")
var missing []string
switch {
case key.N == nil:
missing = append(missing, "N")
case key.E == nil:
missing = append(missing, "E")
case key.D == nil:
missing = append(missing, "D")
case key.P == nil:
missing = append(missing, "P")
case key.Q == nil:
missing = append(missing, "Q")
}
if len(missing) > 0 {
return nil, fmt.Errorf("square/go-jose: invalid RSA private key, missing %s value(s)", strings.Join(missing, ", "))
}
rv := &rsa.PrivateKey{

View File

@ -0,0 +1,515 @@
/*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"encoding/hex"
"encoding/json"
"fmt"
"math/big"
"reflect"
"testing"
)
func TestCurveSize(t *testing.T) {
size256 := curveSize(elliptic.P256())
size384 := curveSize(elliptic.P384())
size521 := curveSize(elliptic.P521())
if size256 != 32 {
t.Error("P-256 have 32 bytes")
}
if size384 != 48 {
t.Error("P-384 have 48 bytes")
}
if size521 != 66 {
t.Error("P-521 have 66 bytes")
}
}
func TestRoundtripRsaPrivate(t *testing.T) {
jwk, err := fromRsaPrivateKey(rsaTestKey)
if err != nil {
t.Error("problem constructing JWK from rsa key", err)
}
rsa2, err := jwk.rsaPrivateKey()
if err != nil {
t.Error("problem converting RSA private -> JWK", err)
}
if rsa2.N.Cmp(rsaTestKey.N) != 0 {
t.Error("RSA private N mismatch")
}
if rsa2.E != rsaTestKey.E {
t.Error("RSA private E mismatch")
}
if rsa2.D.Cmp(rsaTestKey.D) != 0 {
t.Error("RSA private D mismatch")
}
if len(rsa2.Primes) != 2 {
t.Error("RSA private roundtrip expected two primes")
}
if rsa2.Primes[0].Cmp(rsaTestKey.Primes[0]) != 0 {
t.Error("RSA private P mismatch")
}
if rsa2.Primes[1].Cmp(rsaTestKey.Primes[1]) != 0 {
t.Error("RSA private Q mismatch")
}
}
func TestRsaPrivateInsufficientPrimes(t *testing.T) {
brokenRsaPrivateKey := rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: rsaTestKey.N,
E: rsaTestKey.E,
},
D: rsaTestKey.D,
Primes: []*big.Int{rsaTestKey.Primes[0]},
}
_, err := fromRsaPrivateKey(&brokenRsaPrivateKey)
if err != ErrUnsupportedKeyType {
t.Error("expected unsupported key type error, got", err)
}
}
func TestRsaPrivateExcessPrimes(t *testing.T) {
brokenRsaPrivateKey := rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: rsaTestKey.N,
E: rsaTestKey.E,
},
D: rsaTestKey.D,
Primes: []*big.Int{
rsaTestKey.Primes[0],
rsaTestKey.Primes[1],
big.NewInt(3),
},
}
_, err := fromRsaPrivateKey(&brokenRsaPrivateKey)
if err != ErrUnsupportedKeyType {
t.Error("expected unsupported key type error, got", err)
}
}
func TestRoundtripEcPublic(t *testing.T) {
for i, ecTestKey := range []*ecdsa.PrivateKey{ecTestKey256, ecTestKey384, ecTestKey521} {
jwk, err := fromEcPublicKey(&ecTestKey.PublicKey)
ec2, err := jwk.ecPublicKey()
if err != nil {
t.Error("problem converting ECDSA private -> JWK", i, err)
}
if !reflect.DeepEqual(ec2.Curve, ecTestKey.Curve) {
t.Error("ECDSA private curve mismatch", i)
}
if ec2.X.Cmp(ecTestKey.X) != 0 {
t.Error("ECDSA X mismatch", i)
}
if ec2.Y.Cmp(ecTestKey.Y) != 0 {
t.Error("ECDSA Y mismatch", i)
}
}
}
func TestRoundtripEcPrivate(t *testing.T) {
for i, ecTestKey := range []*ecdsa.PrivateKey{ecTestKey256, ecTestKey384, ecTestKey521} {
jwk, err := fromEcPrivateKey(ecTestKey)
ec2, err := jwk.ecPrivateKey()
if err != nil {
t.Error("problem converting ECDSA private -> JWK", i, err)
}
if !reflect.DeepEqual(ec2.Curve, ecTestKey.Curve) {
t.Error("ECDSA private curve mismatch", i)
}
if ec2.X.Cmp(ecTestKey.X) != 0 {
t.Error("ECDSA X mismatch", i)
}
if ec2.Y.Cmp(ecTestKey.Y) != 0 {
t.Error("ECDSA Y mismatch", i)
}
if ec2.D.Cmp(ecTestKey.D) != 0 {
t.Error("ECDSA D mismatch", i)
}
}
}
func TestMarshalUnmarshalJWK(t *testing.T) {
kid := "DEADBEEF"
for i, key := range []interface{}{ecTestKey256, ecTestKey384, ecTestKey521, rsaTestKey} {
for _, use := range []string{"", "sig", "enc"} {
jwk := JsonWebKey{Key: key, KeyID: kid, Algorithm: "foo"}
if use != "" {
jwk.Use = use
}
jsonbar, err := jwk.MarshalJSON()
if err != nil {
t.Error("problem marshaling", i, err)
}
var jwk2 JsonWebKey
err = jwk2.UnmarshalJSON(jsonbar)
if err != nil {
t.Error("problem unmarshalling", i, err)
}
jsonbar2, err := jwk2.MarshalJSON()
if err != nil {
t.Error("problem marshaling", i, err)
}
if !bytes.Equal(jsonbar, jsonbar2) {
t.Error("roundtrip should not lose information", i)
}
if jwk2.KeyID != kid {
t.Error("kid did not roundtrip JSON marshalling", i)
}
if jwk2.Algorithm != "foo" {
t.Error("alg did not roundtrip JSON marshalling", i)
}
if jwk2.Use != use {
t.Error("use did not roundtrip JSON marshalling", i)
}
}
}
}
func TestMarshalNonPointer(t *testing.T) {
type EmbedsKey struct {
Key JsonWebKey
}
keyJson := []byte(`{
"e": "AQAB",
"kty": "RSA",
"n": "vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw"
}`)
var parsedKey JsonWebKey
err := json.Unmarshal(keyJson, &parsedKey)
if err != nil {
t.Error(fmt.Sprintf("Error unmarshalling key: %v", err))
return
}
ek := EmbedsKey{
Key: parsedKey,
}
out, err := json.Marshal(ek)
if err != nil {
t.Error(fmt.Sprintf("Error marshalling JSON: %v", err))
return
}
expected := "{\"Key\":{\"kty\":\"RSA\",\"n\":\"vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw\",\"e\":\"AQAB\"}}"
if string(out) != expected {
t.Error("Failed to marshal embedded non-pointer JWK properly:", string(out))
}
}
func TestMarshalUnmarshalInvalid(t *testing.T) {
// Make an invalid curve coordinate by creating a byte array that is one
// byte too large, and setting the first byte to 1 (otherwise it's just zero).
invalidCoord := make([]byte, curveSize(ecTestKey256.Curve)+1)
invalidCoord[0] = 1
keys := []interface{}{
// Empty keys
&rsa.PrivateKey{},
&ecdsa.PrivateKey{},
// Invalid keys
&ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
// Missing values in pub key
Curve: elliptic.P256(),
},
},
&ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
// Invalid curve
Curve: nil,
X: ecTestKey256.X,
Y: ecTestKey256.Y,
},
},
&ecdsa.PrivateKey{
// Valid pub key, but missing priv key values
PublicKey: ecTestKey256.PublicKey,
},
&ecdsa.PrivateKey{
// Invalid pub key, values too large
PublicKey: ecdsa.PublicKey{
Curve: ecTestKey256.Curve,
X: big.NewInt(0).SetBytes(invalidCoord),
Y: big.NewInt(0).SetBytes(invalidCoord),
},
D: ecTestKey256.D,
},
nil,
}
for i, key := range keys {
jwk := JsonWebKey{Key: key}
_, err := jwk.MarshalJSON()
if err == nil {
t.Error("managed to serialize invalid key", i)
}
}
}
func TestWebKeyVectorsInvalid(t *testing.T) {
keys := []string{
// Invalid JSON
"{X",
// Empty key
"{}",
// Invalid RSA keys
`{"kty":"RSA"}`,
`{"kty":"RSA","e":""}`,
`{"kty":"RSA","e":"XXXX"}`,
`{"kty":"RSA","d":"XXXX"}`,
// Invalid EC keys
`{"kty":"EC","crv":"ABC"}`,
`{"kty":"EC","crv":"P-256"}`,
`{"kty":"EC","crv":"P-256","d":"XXX"}`,
`{"kty":"EC","crv":"ABC","d":"dGVzdA","x":"dGVzdA"}`,
`{"kty":"EC","crv":"P-256","d":"dGVzdA","x":"dGVzdA"}`,
}
for _, key := range keys {
var jwk2 JsonWebKey
err := jwk2.UnmarshalJSON([]byte(key))
if err == nil {
t.Error("managed to parse invalid key:", key)
}
}
}
// Test vectors from RFC 7520
var cookbookJWKs = []string{
// EC Public
stripWhitespace(`{
"kty": "EC",
"kid": "bilbo.baggins@hobbiton.example",
"use": "sig",
"crv": "P-521",
"x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9
A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt",
"y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy
SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1"
}`),
// EC Private
stripWhitespace(`{
"kty": "EC",
"kid": "bilbo.baggins@hobbiton.example",
"use": "sig",
"crv": "P-521",
"x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9
A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt",
"y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy
SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1",
"d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zb
KipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt"
}`),
// RSA Public
stripWhitespace(`{
"kty": "RSA",
"kid": "bilbo.baggins@hobbiton.example",
"use": "sig",
"n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT
-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV
wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-
oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde
3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC
LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g
HdrNP5zw",
"e": "AQAB"
}`),
// RSA Private
stripWhitespace(`{"kty":"RSA",
"kid":"juliet@capulet.lit",
"use":"enc",
"n":"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRy
O125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP
8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0
Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0X
OC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1
_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q",
"e":"AQAB",
"d":"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfS
NkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9U
vqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnu
ToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsu
rY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2a
hecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ",
"p":"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHf
QP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8
UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws",
"q":"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6I
edis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYK
rYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s",
"dp":"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3
tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1w
Y52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c",
"dq":"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9
GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBy
mXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots",
"qi":"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqq
abu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0o
Yu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8"}`),
}
// SHA-256 thumbprints of the above keys, hex-encoded
var cookbookJWKThumbprints = []string{
"747ae2dd2003664aeeb21e4753fe7402846170a16bc8df8f23a8cf06d3cbe793",
"747ae2dd2003664aeeb21e4753fe7402846170a16bc8df8f23a8cf06d3cbe793",
"f63838e96077ad1fc01c3f8405774dedc0641f558ebb4b40dccf5f9b6d66a932",
"0fc478f8579325fcee0d4cbc6d9d1ce21730a6e97e435d6008fb379b0ebe47d4",
}
func TestWebKeyVectorsValid(t *testing.T) {
for _, key := range cookbookJWKs {
var jwk2 JsonWebKey
err := jwk2.UnmarshalJSON([]byte(key))
if err != nil {
t.Error("unable to parse valid key:", key, err)
}
}
}
func TestThumbprint(t *testing.T) {
for i, key := range cookbookJWKs {
var jwk2 JsonWebKey
err := jwk2.UnmarshalJSON([]byte(key))
if err != nil {
t.Error("unable to parse valid key:", key, err)
}
tp, err := jwk2.Thumbprint(crypto.SHA256)
if err != nil {
t.Error("unable to compute thumbprint:", key, err)
}
tpHex := hex.EncodeToString(tp)
if cookbookJWKThumbprints[i] != tpHex {
t.Error("incorrect thumbprint:", i, cookbookJWKThumbprints[i], tpHex)
}
}
}
func TestMarshalUnmarshalJWKSet(t *testing.T) {
jwk1 := JsonWebKey{Key: rsaTestKey, KeyID: "ABCDEFG", Algorithm: "foo"}
jwk2 := JsonWebKey{Key: rsaTestKey, KeyID: "GFEDCBA", Algorithm: "foo"}
var set JsonWebKeySet
set.Keys = append(set.Keys, jwk1)
set.Keys = append(set.Keys, jwk2)
jsonbar, err := json.Marshal(&set)
if err != nil {
t.Error("problem marshalling set", err)
}
var set2 JsonWebKeySet
err = json.Unmarshal(jsonbar, &set2)
if err != nil {
t.Error("problem unmarshalling set", err)
}
jsonbar2, err := json.Marshal(&set2)
if err != nil {
t.Error("problem marshalling set", err)
}
if !bytes.Equal(jsonbar, jsonbar2) {
t.Error("roundtrip should not lose information")
}
}
var JWKSetDuplicates = stripWhitespace(`{
"keys": [{
"kty": "RSA",
"kid": "exclude-me",
"use": "sig",
"n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT
-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV
wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-
oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde
3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC
LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g
HdrNP5zw",
"e": "AQAB"
}],
"keys": [{
"kty": "RSA",
"kid": "include-me",
"use": "sig",
"n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT
-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV
wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-
oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde
3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC
LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g
HdrNP5zw",
"e": "AQAB"
}],
"custom": "exclude-me",
"custom": "include-me"
}`)
func TestDuplicateJWKSetMembersIgnored(t *testing.T) {
type CustomSet struct {
JsonWebKeySet
CustomMember string `json:"custom"`
}
data := []byte(JWKSetDuplicates)
var set CustomSet
json.Unmarshal(data, &set)
if len(set.Keys) != 1 {
t.Error("expected only one key in set")
}
if set.Keys[0].KeyID != "include-me" {
t.Errorf("expected key with kid: \"include-me\", got: %s", set.Keys[0].KeyID)
}
if set.CustomMember != "include-me" {
t.Errorf("expected custom member value: \"include-me\", got: %s", set.CustomMember)
}
}
func TestJWKSetKey(t *testing.T) {
jwk1 := JsonWebKey{Key: rsaTestKey, KeyID: "ABCDEFG", Algorithm: "foo"}
jwk2 := JsonWebKey{Key: rsaTestKey, KeyID: "GFEDCBA", Algorithm: "foo"}
var set JsonWebKeySet
set.Keys = append(set.Keys, jwk1)
set.Keys = append(set.Keys, jwk2)
k := set.Key("ABCDEFG")
if len(k) != 1 {
t.Errorf("method should return slice with one key not %d", len(k))
}
if k[0].KeyID != "ABCDEFG" {
t.Error("method should return key with ID ABCDEFG")
}
}

View File

@ -46,8 +46,12 @@ type JsonWebSignature struct {
// Signature represents a single signature over the JWS payload and protected header.
type Signature struct {
Header JoseHeader
// Header fields, such as the signature algorithm
Header JoseHeader
// The actual signature value
Signature []byte
protected *rawHeader
header *rawHeader
original *rawSignatureInfo
@ -102,7 +106,7 @@ func parseSignedFull(input string) (*JsonWebSignature, error) {
// sanitized produces a cleaned-up JWS object from the raw JSON.
func (parsed *rawJsonWebSignature) sanitized() (*JsonWebSignature, error) {
if parsed.Payload == nil {
return nil, fmt.Errorf("square/go-jose: missing payload in JWS message")
return nil, ErrUnprotectedNonce
}
obj := &JsonWebSignature{
@ -121,6 +125,10 @@ func (parsed *rawJsonWebSignature) sanitized() (*JsonWebSignature, error) {
}
}
if parsed.Header != nil && parsed.Header.Nonce != "" {
return nil, ErrUnprotectedNonce
}
signature.header = parsed.Header
signature.Signature = parsed.Signature.bytes()
// Make a fake "original" rawSignatureInfo to store the unprocessed
@ -151,6 +159,11 @@ func (parsed *rawJsonWebSignature) sanitized() (*JsonWebSignature, error) {
}
}
// Check that there is not a nonce in the unprotected header
if sig.Header != nil && sig.Header.Nonce != "" {
return nil, ErrUnprotectedNonce
}
obj.Signatures[i].Signature = sig.Signature.bytes()
// Copy value of sig
@ -238,20 +251,3 @@ func (obj JsonWebSignature) FullSerialize() string {
return string(mustSerializeJSON(raw))
}
// MarshalJSON serializes the JWS to JSON.
func (obj JsonWebSignature) MarshalJSON() (result []byte, err error) {
return []byte(obj.FullSerialize()), nil
}
// UnmarshalJSON parses a JWS from JSON data. (This may also accept a compact
// JWS in a string.)
func (obj *JsonWebSignature) UnmarshalJSON(data []byte) (err error) {
parsedJWS, err := ParseSigned(string(data))
if err != nil {
return err
}
*obj = *parsedJWS
return nil
}

View File

@ -0,0 +1,290 @@
/*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"fmt"
"testing"
)
func TestCompactParseJWS(t *testing.T) {
// Should parse
msg := "eyJhbGciOiJYWVoifQ.cGF5bG9hZA.c2lnbmF0dXJl"
_, err := ParseSigned(msg)
if err != nil {
t.Error("Unable to parse valid message:", err)
}
// Messages that should fail to parse
failures := []string{
// Not enough parts
"eyJhbGciOiJYWVoifQ.cGF5bG9hZA",
// Invalid signature
"eyJhbGciOiJYWVoifQ.cGF5bG9hZA.////",
// Invalid payload
"eyJhbGciOiJYWVoifQ.////.c2lnbmF0dXJl",
// Invalid header
"////.eyJhbGciOiJYWVoifQ.c2lnbmF0dXJl",
// Invalid header
"cGF5bG9hZA.cGF5bG9hZA.c2lnbmF0dXJl",
}
for i := range failures {
_, err = ParseSigned(failures[i])
if err == nil {
t.Error("Able to parse invalid message")
}
}
}
func TestFullParseJWS(t *testing.T) {
// Messages that should succeed to parse
successes := []string{
"{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"e30\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"},{\"protected\":\"e30\",\"signature\":\"CUJD\"}]}",
}
for i := range successes {
_, err := ParseSigned(successes[i])
if err != nil {
t.Error("Unble to parse valid message", err, successes[i])
}
}
// Messages that should fail to parse
failures := []string{
// Empty
"{}",
// Invalid JSON
"{XX",
// Invalid protected header
"{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}",
// Invalid protected header
"{\"payload\":\"CUJD\",\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}",
// Invalid protected header
"{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"###\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}",
// Invalid payload
"{\"payload\":\"###\",\"signatures\":[{\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}",
// Invalid payload
"{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"e30\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"###\"}]}",
}
for i := range failures {
_, err := ParseSigned(failures[i])
if err == nil {
t.Error("Able to parse invalid message", err, failures[i])
}
}
}
func TestRejectUnprotectedJWSNonce(t *testing.T) {
// No need to test compact, since that's always protected
// Flattened JSON
input := `{
"header": { "nonce": "should-cause-an-error" },
"payload": "does-not-matter",
"signature": "does-not-matter"
}`
_, err := ParseSigned(input)
if err == nil {
t.Error("JWS with an unprotected nonce parsed as valid.")
} else if err != ErrUnprotectedNonce {
t.Errorf("Improper error for unprotected nonce: %v", err)
}
// Full JSON
input = `{
"payload": "does-not-matter",
"signatures": [{
"header": { "nonce": "should-cause-an-error" },
"signature": "does-not-matter"
}]
}`
_, err = ParseSigned(input)
if err == nil {
t.Error("JWS with an unprotected nonce parsed as valid.")
} else if err != ErrUnprotectedNonce {
t.Errorf("Improper error for unprotected nonce: %v", err)
}
}
func TestVerifyFlattenedWithIncludedUnprotectedKey(t *testing.T) {
input := `{
"header": {
"alg": "RS256",
"jwk": {
"e": "AQAB",
"kty": "RSA",
"n": "tSwgy3ORGvc7YJI9B2qqkelZRUC6F1S5NwXFvM4w5-M0TsxbFsH5UH6adigV0jzsDJ5imAechcSoOhAh9POceCbPN1sTNwLpNbOLiQQ7RD5mY_pSUHWXNmS9R4NZ3t2fQAzPeW7jOfF0LKuJRGkekx6tXP1uSnNibgpJULNc4208dgBaCHo3mvaE2HV2GmVl1yxwWX5QZZkGQGjNDZYnjFfa2DKVvFs0QbAk21ROm594kAxlRlMMrvqlf24Eq4ERO0ptzpZgm_3j_e4hGRD39gJS7kAzK-j2cacFQ5Qi2Y6wZI2p-FCq_wiYsfEAIkATPBiLKl_6d_Jfcvs_impcXQ"
}
},
"payload": "Zm9vCg",
"signature": "hRt2eYqBd_MyMRNIh8PEIACoFtmBi7BHTLBaAhpSU6zyDAFdEBaX7us4VB9Vo1afOL03Q8iuoRA0AT4akdV_mQTAQ_jhTcVOAeXPr0tB8b8Q11UPQ0tXJYmU4spAW2SapJIvO50ntUaqU05kZd0qw8-noH1Lja-aNnU-tQII4iYVvlTiRJ5g8_CADsvJqOk6FcHuo2mG643TRnhkAxUtazvHyIHeXMxydMMSrpwUwzMtln4ZJYBNx4QGEq6OhpAD_VSp-w8Lq5HOwGQoNs0bPxH1SGrArt67LFQBfjlVr94E1sn26p4vigXm83nJdNhWAMHHE9iV67xN-r29LT-FjA"
}`
jws, err := ParseSigned(input)
if err != nil {
t.Error("Unable to parse valid message.")
}
if len(jws.Signatures) != 1 {
t.Error("Too many or too few signatures.")
}
sig := jws.Signatures[0]
if sig.Header.JsonWebKey == nil {
t.Error("No JWK in signature header.")
}
payload, err := jws.Verify(sig.Header.JsonWebKey)
if err != nil {
t.Error(fmt.Sprintf("Signature did not validate: %v", err))
}
if string(payload) != "foo\n" {
t.Error(fmt.Sprintf("Payload was incorrect: '%s' should have been 'foo\\n'", string(payload)))
}
}
func TestVerifyFlattenedWithPrivateProtected(t *testing.T) {
// The protected field contains a Private Header Parameter name, per
// https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4
// Base64-decoded, it's '{"nonce":"8HIepUNFZUa-exKTrXVf4g"}'
input := `{"header":{"alg":"RS256","jwk":{"kty":"RSA","n":"7ixeydcbxxppzxrBphrW1atUiEZqTpiHDpI-79olav5XxAgWolHmVsJyxzoZXRxmtED8PF9-EICZWBGdSAL9ZTD0hLUCIsPcpdgT_LqNW3Sh2b2caPL2hbMF7vsXvnCGg9varpnHWuYTyRrCLUF9vM7ES-V3VCYTa7LcCSRm56Gg9r19qar43Z9kIKBBxpgt723v2cC4bmLmoAX2s217ou3uCpCXGLOeV_BesG4--Nl3pso1VhCfO85wEWjmW6lbv7Kg4d7Jdkv5DjDZfJ086fkEAYZVYGRpIgAvJBH3d3yKDCrSByUEud1bWuFjQBmMaeYOrVDXO_mbYg5PwUDMhw","e":"AQAB"}},"protected":"eyJub25jZSI6IjhISWVwVU5GWlVhLWV4S1RyWFZmNGcifQ","payload":"eyJjb250YWN0IjpbIm1haWx0bzpmb29AYmFyLmNvbSJdfQ","signature":"AyvVGMgXsQ1zTdXrZxE_gyO63pQgotL1KbI7gv6Wi8I7NRy0iAOkDAkWcTQT9pcCYApJ04lXfEDZfP5i0XgcFUm_6spxi5mFBZU-NemKcvK9dUiAbXvb4hB3GnaZtZiuVnMQUb_ku4DOaFFKbteA6gOYCnED_x7v0kAPHIYrQnvIa-KZ6pTajbV9348zgh9TL7NgGIIsTcMHd-Jatr4z1LQ0ubGa8tS300hoDhVzfoDQaEetYjCo1drR1RmdEN1SIzXdHOHfubjA3ZZRbrF_AJnNKpRRoIwzu1VayOhRmdy1qVSQZq_tENF4VrQFycEL7DhG7JLoXC4T2p1urwMlsw"}`
jws, err := ParseSigned(input)
if err != nil {
t.Error("Unable to parse valid message.")
}
if len(jws.Signatures) != 1 {
t.Error("Too many or too few signatures.")
}
sig := jws.Signatures[0]
if sig.Header.JsonWebKey == nil {
t.Error("No JWK in signature header.")
}
payload, err := jws.Verify(sig.Header.JsonWebKey)
if err != nil {
t.Error(fmt.Sprintf("Signature did not validate: %v", err))
}
expected := "{\"contact\":[\"mailto:foo@bar.com\"]}"
if string(payload) != expected {
t.Error(fmt.Sprintf("Payload was incorrect: '%s' should have been '%s'", string(payload), expected))
}
}
// Test vectors generated with nimbus-jose-jwt
func TestSampleNimbusJWSMessagesRSA(t *testing.T) {
rsaPublicKey, err := LoadPublicKey(fromBase64Bytes(`
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3aLSGwbeX0ZA2Ha+EvELaIFGzO
91+Q15JQc/tdGdCgGW3XAbrh7ZUhDh1XKzbs+UOQxqn3Eq4YOx18IG0WsJSuCaHQIxnDlZ
t/GP8WLwjMC0izlJLm2SyfM/EEoNpmTC3w6MQ2dHK7SZ9Zoq+sKijQd+V7CYdr8zHMpDrd
NKoEcR0HjmvzzdMoUChhkGH5TaNbZyollULTggepaYUKS8QphqdSDMWiSetKG+g6V87lv6
CVYyK1FF6g7Esp5OOj5pNn3/bmF+7V+b7TvK91NCIlURCjE9toRgNoIP4TDnWRn/vvfZ3G
zNrtWmlizqz3r5KdvIs71ahWgMUSD4wfazrwIDAQAB`))
if err != nil {
panic(err)
}
rsaSampleMessages := []string{
"eyJhbGciOiJSUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.YHX849fvekz6wJGeyqnQhFqyHFcUXNJKj3o2w3ddR46YLlsCopUJrlifRU_ZuTWzpYxt5oC--T2eoqMhlCvltSWrE5_1_EumqiMfAYsZULx9E6Jns7q3w7mttonYFSIh7aR3-yg2HMMfTCgoAY1y_AZ4VjXwHDcZ5gu1oZDYgvZF4uXtCmwT6e5YtR1m8abiWPF8BgoTG_BD3KV6ClLj_QQiNFdfdxAMDw7vKVOKG1T7BFtz6cDs2Q3ILS4To5E2IjcVSSYS8mi77EitCrWmrqbK_G3WCdKeUFGnMnyuKXaCDy_7FLpAZ6Z5RomRr5iskXeJZdZqIKcJV8zl4fpsPA",
"eyJhbGciOiJSUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.meyfoOTjAAjXHFYiNlU7EEnsYtbeUYeEglK6BL_cxISEr2YAGLr1Gwnn2HnucTnH6YilyRio7ZC1ohy_ZojzmaljPHqpr8kn1iqNFu9nFE2M16ZPgJi38-PGzppcDNliyzOQO-c7L-eA-v8Gfww5uyRaOJdiWg-hUJmeGBIngPIeLtSVmhJtz8oTeqeNdUOqQv7f7VRCuvagLhW1PcEM91VUS-gS0WEUXoXWZ2lp91No0v1O24izgX3__FKiX_16XhrOfAgJ82F61vjbTIQYwhexHPZyYTlXYt_scNRzFGhSKeGFin4zVdFLOXWJqKWdUd5IrDP5Nya3FSoWbWDXAg",
"eyJhbGciOiJSUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.rQPz0PDh8KyE2AX6JorgI0MLwv-qi1tcWlz6tuZuWQG1hdrlzq5tR1tQg1evYNc_SDDX87DWTSKXT7JEqhKoFixLfZa13IJrOc7FB8r5ZLx7OwOBC4F--OWrvxMA9Y3MTJjPN3FemQePUo-na2vNUZv-YgkcbuOgbO3hTxwQ7j1JGuqy-YutXOFnccdXvntp3t8zYZ4Mg1It_IyL9pzgGqHIEmMV1pCFGHsDa-wStB4ffmdhrADdYZc0q_SvxUdobyC_XzZCz9ENzGIhgwYxyyrqg7kjqUGoKmCLmoSlUFW7goTk9IC5SXdUyLPuESxOWNfHoRClGav230GYjPFQFA",
"eyJhbGciOiJQUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.UTtxjsv_6x4CdlAmZfAW6Lun3byMjJbcwRp_OlPH2W4MZaZar7aql052mIB_ddK45O9VUz2aphYVRvKPZY8WHmvlTUU30bk0z_cDJRYB9eIJVMOiRCYj0oNkz1iEZqsP0YgngxwuUDv4Q4A6aJ0Bo5E_rZo3AnrVHMHUjPp_ZRRSBFs30tQma1qQ0ApK4Gxk0XYCYAcxIv99e78vldVRaGzjEZmQeAVZx4tGcqZP20vG1L84nlhSGnOuZ0FhR8UjRFLXuob6M7EqtMRoqPgRYw47EI3fYBdeSivAg98E5S8R7R1NJc7ef-l03RvfUSY0S3_zBq_4PlHK6A-2kHb__w",
"eyJhbGciOiJSUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.meyfoOTjAAjXHFYiNlU7EEnsYtbeUYeEglK6BL_cxISEr2YAGLr1Gwnn2HnucTnH6YilyRio7ZC1ohy_ZojzmaljPHqpr8kn1iqNFu9nFE2M16ZPgJi38-PGzppcDNliyzOQO-c7L-eA-v8Gfww5uyRaOJdiWg-hUJmeGBIngPIeLtSVmhJtz8oTeqeNdUOqQv7f7VRCuvagLhW1PcEM91VUS-gS0WEUXoXWZ2lp91No0v1O24izgX3__FKiX_16XhrOfAgJ82F61vjbTIQYwhexHPZyYTlXYt_scNRzFGhSKeGFin4zVdFLOXWJqKWdUd5IrDP5Nya3FSoWbWDXAg",
"eyJhbGciOiJSUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.rQPz0PDh8KyE2AX6JorgI0MLwv-qi1tcWlz6tuZuWQG1hdrlzq5tR1tQg1evYNc_SDDX87DWTSKXT7JEqhKoFixLfZa13IJrOc7FB8r5ZLx7OwOBC4F--OWrvxMA9Y3MTJjPN3FemQePUo-na2vNUZv-YgkcbuOgbO3hTxwQ7j1JGuqy-YutXOFnccdXvntp3t8zYZ4Mg1It_IyL9pzgGqHIEmMV1pCFGHsDa-wStB4ffmdhrADdYZc0q_SvxUdobyC_XzZCz9ENzGIhgwYxyyrqg7kjqUGoKmCLmoSlUFW7goTk9IC5SXdUyLPuESxOWNfHoRClGav230GYjPFQFA",
}
for _, msg := range rsaSampleMessages {
obj, err := ParseSigned(msg)
if err != nil {
t.Error("unable to parse message", msg, err)
continue
}
payload, err := obj.Verify(rsaPublicKey)
if err != nil {
t.Error("unable to verify message", msg, err)
continue
}
if string(payload) != "Lorem ipsum dolor sit amet" {
t.Error("payload is not what we expected for msg", msg)
}
}
}
// Test vectors generated with nimbus-jose-jwt
func TestSampleNimbusJWSMessagesEC(t *testing.T) {
ecPublicKeyP256, err := LoadPublicKey(fromBase64Bytes("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIg62jq6FyL1otEj9Up7S35BUrwGF9TVrAzrrY1rHUKZqYIGEg67u/imjgadVcr7y9Q32I0gB8W8FHqbqt696rA=="))
if err != nil {
panic(err)
}
ecPublicKeyP384, err := LoadPublicKey(fromBase64Bytes("MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEPXsVlqCtN2oTY+F+hFZm3M0ldYpb7IeeJM5wYmT0k1RaqzBFDhDMNnYK5Q5x+OyssZrAtHgYDFw02AVJhhng/eHRp7mqmL/vI3wbxJtrLKYldIbBA+9fYBQcKeibjlu5"))
if err != nil {
panic(err)
}
ecPublicKeyP521, err := LoadPublicKey(fromBase64Bytes("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAa2w3MMJ5FWD6tSf68G+Wy5jIhWXOD3IA7pE5IC/myQzo1lWcD8KS57SM6nm4POtPcxyLmDhL7FLuh8DKoIZyvtAAdK8+tOQP7XXRlT2bkvzIuazp05It3TAPu00YzTIpKfDlc19Y1lvf7etrbFqhShD92B+hHmhT4ddrdbPCBDW8hvU="))
if err != nil {
panic(err)
}
ecPublicKeys := []interface{}{ecPublicKeyP256, ecPublicKeyP384, ecPublicKeyP521}
ecSampleMessages := []string{
"eyJhbGciOiJFUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.MEWJVlvGRQyzMEGOYm4rwuiwxrX-6LjnlbaRDAuhwmnBm2Gtn7pRpGXRTMFZUXsSGDz2L1p-Hz1qn8j9bFIBtQ",
"eyJhbGciOiJFUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.nbdjPnJPYQtVNNdBIx8-KbFKplTxrz-hnW5UNhYUY7SBkwHK4NZnqc2Lv4DXoA0aWHq9eiypgOh1kmyPWGEmqKAHUx0xdIEkBoHk3ZsbmhOQuq2jL_wcMUG6nTWNhLrB",
"eyJhbGciOiJFUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.AeYNFC1rwIgQv-5fwd8iRyYzvTaSCYTEICepgu9gRId-IW99kbSVY7yH0MvrQnqI-a0L8zwKWDR35fW5dukPAYRkADp3Y1lzqdShFcEFziUVGo46vqbiSajmKFrjBktJcCsfjKSaLHwxErF-T10YYPCQFHWb2nXJOOI3CZfACYqgO84g",
}
for i, msg := range ecSampleMessages {
obj, err := ParseSigned(msg)
if err != nil {
t.Error("unable to parse message", msg, err)
continue
}
payload, err := obj.Verify(ecPublicKeys[i])
if err != nil {
t.Error("unable to verify message", msg, err)
continue
}
if string(payload) != "Lorem ipsum dolor sit amet" {
t.Error("payload is not what we expected for msg", msg)
}
}
}
// Test vectors generated with nimbus-jose-jwt
func TestSampleNimbusJWSMessagesHMAC(t *testing.T) {
hmacTestKey := fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D")
hmacSampleMessages := []string{
"eyJhbGciOiJIUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.W5tc_EUhxexcvLYEEOckyyvdb__M5DQIVpg6Nmk1XGM",
"eyJhbGciOiJIUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.sBu44lXOJa4Nd10oqOdYH2uz3lxlZ6o32QSGHaoGdPtYTDG5zvSja6N48CXKqdAh",
"eyJhbGciOiJIUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.M0yR4tmipsORIix-BitIbxEPGaxPchDfj8UNOpKuhDEfnb7URjGvCKn4nOlyQ1z9mG1FKbwnqR1hOVAWSzAU_w",
}
for _, msg := range hmacSampleMessages {
obj, err := ParseSigned(msg)
if err != nil {
t.Error("unable to parse message", msg, err)
continue
}
payload, err := obj.Verify(hmacTestKey)
if err != nil {
t.Error("unable to verify message", msg, err)
continue
}
if string(payload) != "Lorem ipsum dolor sit amet" {
t.Error("payload is not what we expected for msg", msg)
}
}
}

View File

@ -17,7 +17,9 @@
package jose
import (
"crypto/elliptic"
"errors"
"fmt"
)
// KeyAlgorithm represents a key management algorithm.
@ -53,6 +55,10 @@ var (
// trying to compact-serialize an object which can't be represented in
// compact form.
ErrNotSupported = errors.New("square/go-jose: compact serialization not supported for object")
// ErrUnprotectedNonce indicates that while parsing a JWS or JWE object, a
// nonce header parameter was included in an unprotected header object.
ErrUnprotectedNonce = errors.New("square/go-jose: Nonce parameter included in unprotected header")
)
// Key management algorithms
@ -84,9 +90,9 @@ const (
RS256 = SignatureAlgorithm("RS256") // RSASSA-PKCS-v1.5 using SHA-256
RS384 = SignatureAlgorithm("RS384") // RSASSA-PKCS-v1.5 using SHA-384
RS512 = SignatureAlgorithm("RS512") // RSASSA-PKCS-v1.5 using SHA-512
ES256 = SignatureAlgorithm("ES256") // RCDSA using P-256 and SHA-256
ES384 = SignatureAlgorithm("ES384") // RCDSA using P-384 and SHA-384
ES512 = SignatureAlgorithm("ES512") // RCDSA using P-521 and SHA-512
ES256 = SignatureAlgorithm("ES256") // ECDSA using P-256 and SHA-256
ES384 = SignatureAlgorithm("ES384") // ECDSA using P-384 and SHA-384
ES512 = SignatureAlgorithm("ES512") // ECDSA using P-521 and SHA-512
PS256 = SignatureAlgorithm("PS256") // RSASSA-PSS using SHA256 and MGF1-SHA256
PS384 = SignatureAlgorithm("PS384") // RSASSA-PSS using SHA384 and MGF1-SHA384
PS512 = SignatureAlgorithm("PS512") // RSASSA-PSS using SHA512 and MGF1-SHA512
@ -128,8 +134,8 @@ type rawHeader struct {
type JoseHeader struct {
KeyID string
JsonWebKey *JsonWebKey
Nonce string
Algorithm string
Nonce string
}
// sanitized produces a cleaned-up header object from the raw JSON.
@ -137,8 +143,8 @@ func (parsed rawHeader) sanitized() JoseHeader {
return JoseHeader{
KeyID: parsed.Kid,
JsonWebKey: parsed.Jwk,
Nonce: parsed.Nonce,
Algorithm: parsed.Alg,
Nonce: parsed.Nonce,
}
}
@ -188,3 +194,31 @@ func (dst *rawHeader) merge(src *rawHeader) {
dst.Nonce = src.Nonce
}
}
// Get JOSE name of curve
func curveName(crv elliptic.Curve) (string, error) {
switch crv {
case elliptic.P256():
return "P-256", nil
case elliptic.P384():
return "P-384", nil
case elliptic.P521():
return "P-521", nil
default:
return "", fmt.Errorf("square/go-jose: unsupported/unknown elliptic curve")
}
}
// Get size of curve in bytes
func curveSize(crv elliptic.Curve) int {
bits := crv.Params().BitSize
div := bits / 8
mod := bits % 8
if mod == 0 {
return div
}
return div + 1
}

View File

@ -22,14 +22,21 @@ import (
"fmt"
)
// NonceSource represents a source of random nonces to go into JWS objects
type NonceSource interface {
Nonce() (string, error)
}
// Signer represents a signer which takes a payload and produces a signed JWS object.
type Signer interface {
Sign(payload []byte, nonce string) (*JsonWebSignature, error)
Sign(payload []byte) (*JsonWebSignature, error)
SetNonceSource(source NonceSource)
}
// MultiSigner represents a signer which supports multiple recipients.
type MultiSigner interface {
Sign(payload []byte, nonce string) (*JsonWebSignature, error)
Sign(payload []byte) (*JsonWebSignature, error)
SetNonceSource(source NonceSource)
AddRecipient(alg SignatureAlgorithm, signingKey interface{}) error
}
@ -42,7 +49,8 @@ type payloadVerifier interface {
}
type genericSigner struct {
recipients []recipientSigInfo
recipients []recipientSigInfo
nonceSource NonceSource
}
type recipientSigInfo struct {
@ -123,7 +131,7 @@ func makeRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipientSig
}
}
func (ctx *genericSigner) Sign(payload []byte, nonce string) (*JsonWebSignature, error) {
func (ctx *genericSigner) Sign(payload []byte) (*JsonWebSignature, error) {
obj := &JsonWebSignature{}
obj.payload = payload
obj.Signatures = make([]Signature, len(ctx.recipients))
@ -138,7 +146,11 @@ func (ctx *genericSigner) Sign(payload []byte, nonce string) (*JsonWebSignature,
protected.Kid = recipient.publicKey.KeyID
}
if nonce != "" {
if ctx.nonceSource != nil {
nonce, err := ctx.nonceSource.Nonce()
if err != nil {
return nil, fmt.Errorf("square/go-jose: Error generating nonce: %v", err)
}
protected.Nonce = nonce
}
@ -160,11 +172,18 @@ func (ctx *genericSigner) Sign(payload []byte, nonce string) (*JsonWebSignature,
return obj, nil
}
// SetNonceSource provides or updates a nonce pool to the first recipients.
// After this method is called, the signer will consume one nonce per
// signature, returning an error it is unable to get a nonce.
func (ctx *genericSigner) SetNonceSource(source NonceSource) {
ctx.nonceSource = source
}
// Verify validates the signature on the object and returns the payload.
func (obj JsonWebSignature) Verify(verificationKey interface{}) ([]byte, JoseHeader, error) {
func (obj JsonWebSignature) Verify(verificationKey interface{}) ([]byte, error) {
verifier, err := newVerifier(verificationKey)
if err != nil {
return nil, JoseHeader{}, err
return nil, err
}
for _, signature := range obj.Signatures {
@ -173,13 +192,14 @@ func (obj JsonWebSignature) Verify(verificationKey interface{}) ([]byte, JoseHea
// Unsupported crit header
continue
}
input := obj.computeAuthData(&signature)
alg := SignatureAlgorithm(headers.Alg)
err := verifier.verifyPayload(input, signature.Signature, alg)
if err == nil {
return obj.payload, headers.sanitized(), nil
return obj.payload, nil
}
}
return nil, JoseHeader{}, ErrCryptoFailure
return nil, ErrCryptoFailure
}

View File

@ -0,0 +1,379 @@
/*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"encoding/json"
"fmt"
"io"
"testing"
)
type staticNonceSource string
func (sns staticNonceSource) Nonce() (string, error) {
return string(sns), nil
}
func RoundtripJWS(sigAlg SignatureAlgorithm, serializer func(*JsonWebSignature) (string, error), corrupter func(*JsonWebSignature), signingKey interface{}, verificationKey interface{}, nonce string) error {
signer, err := NewSigner(sigAlg, signingKey)
if err != nil {
return fmt.Errorf("error on new signer: %s", err)
}
if nonce != "" {
signer.SetNonceSource(staticNonceSource(nonce))
}
input := []byte("Lorem ipsum dolor sit amet")
obj, err := signer.Sign(input)
if err != nil {
return fmt.Errorf("error on sign: %s", err)
}
msg, err := serializer(obj)
if err != nil {
return fmt.Errorf("error on serialize: %s", err)
}
obj, err = ParseSigned(msg)
if err != nil {
return fmt.Errorf("error on parse: %s", err)
}
// (Maybe) mangle the object
corrupter(obj)
output, err := obj.Verify(verificationKey)
if err != nil {
return fmt.Errorf("error on verify: %s", err)
}
// Check that verify works with embedded keys (if present)
for i, sig := range obj.Signatures {
if sig.Header.JsonWebKey != nil {
_, err = obj.Verify(sig.Header.JsonWebKey)
if err != nil {
return fmt.Errorf("error on verify with embedded key %d: %s", i, err)
}
}
// Check that the nonce correctly round-tripped (if present)
if sig.Header.Nonce != nonce {
return fmt.Errorf("Incorrect nonce returned: [%s]", sig.Header.Nonce)
}
}
if bytes.Compare(output, input) != 0 {
return fmt.Errorf("input/output do not match, got '%s', expected '%s'", output, input)
}
return nil
}
func TestRoundtripsJWS(t *testing.T) {
// Test matrix
sigAlgs := []SignatureAlgorithm{RS256, RS384, RS512, PS256, PS384, PS512, HS256, HS384, HS512, ES256, ES384, ES512}
serializers := []func(*JsonWebSignature) (string, error){
func(obj *JsonWebSignature) (string, error) { return obj.CompactSerialize() },
func(obj *JsonWebSignature) (string, error) { return obj.FullSerialize(), nil },
}
corrupter := func(obj *JsonWebSignature) {}
for _, alg := range sigAlgs {
signingKey, verificationKey := GenerateSigningTestKey(alg)
for i, serializer := range serializers {
err := RoundtripJWS(alg, serializer, corrupter, signingKey, verificationKey, "test_nonce")
if err != nil {
t.Error(err, alg, i)
}
}
}
}
func TestRoundtripsJWSCorruptSignature(t *testing.T) {
// Test matrix
sigAlgs := []SignatureAlgorithm{RS256, RS384, RS512, PS256, PS384, PS512, HS256, HS384, HS512, ES256, ES384, ES512}
serializers := []func(*JsonWebSignature) (string, error){
func(obj *JsonWebSignature) (string, error) { return obj.CompactSerialize() },
func(obj *JsonWebSignature) (string, error) { return obj.FullSerialize(), nil },
}
corrupters := []func(*JsonWebSignature){
func(obj *JsonWebSignature) {
// Changes bytes in signature
obj.Signatures[0].Signature[10]++
},
func(obj *JsonWebSignature) {
// Set totally invalid signature
obj.Signatures[0].Signature = []byte("###")
},
}
// Test all different configurations
for _, alg := range sigAlgs {
signingKey, verificationKey := GenerateSigningTestKey(alg)
for i, serializer := range serializers {
for j, corrupter := range corrupters {
err := RoundtripJWS(alg, serializer, corrupter, signingKey, verificationKey, "test_nonce")
if err == nil {
t.Error("failed to detect corrupt signature", err, alg, i, j)
}
}
}
}
}
func TestSignerWithBrokenRand(t *testing.T) {
sigAlgs := []SignatureAlgorithm{RS256, RS384, RS512, PS256, PS384, PS512}
serializer := func(obj *JsonWebSignature) (string, error) { return obj.CompactSerialize() }
corrupter := func(obj *JsonWebSignature) {}
// Break rand reader
readers := []func() io.Reader{
// Totally broken
func() io.Reader { return bytes.NewReader([]byte{}) },
// Not enough bytes
func() io.Reader { return io.LimitReader(rand.Reader, 20) },
}
defer resetRandReader()
for _, alg := range sigAlgs {
signingKey, verificationKey := GenerateSigningTestKey(alg)
for i, getReader := range readers {
randReader = getReader()
err := RoundtripJWS(alg, serializer, corrupter, signingKey, verificationKey, "test_nonce")
if err == nil {
t.Error("signer should fail if rand is broken", alg, i)
}
}
}
}
func TestJWSInvalidKey(t *testing.T) {
signingKey0, verificationKey0 := GenerateSigningTestKey(RS256)
_, verificationKey1 := GenerateSigningTestKey(ES256)
signer, err := NewSigner(RS256, signingKey0)
if err != nil {
panic(err)
}
input := []byte("Lorem ipsum dolor sit amet")
obj, err := signer.Sign(input)
if err != nil {
panic(err)
}
// Must work with correct key
_, err = obj.Verify(verificationKey0)
if err != nil {
t.Error("error on verify", err)
}
// Must not work with incorrect key
_, err = obj.Verify(verificationKey1)
if err == nil {
t.Error("verification should fail with incorrect key")
}
// Must not work with invalid key
_, err = obj.Verify("")
if err == nil {
t.Error("verification should fail with incorrect key")
}
}
func TestMultiRecipientJWS(t *testing.T) {
signer := NewMultiSigner()
sharedKey := []byte{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
}
signer.AddRecipient(RS256, rsaTestKey)
signer.AddRecipient(HS384, sharedKey)
input := []byte("Lorem ipsum dolor sit amet")
obj, err := signer.Sign(input)
if err != nil {
t.Error("error on sign: ", err)
return
}
_, err = obj.CompactSerialize()
if err == nil {
t.Error("message with multiple recipient was compact serialized")
}
msg := obj.FullSerialize()
obj, err = ParseSigned(msg)
if err != nil {
t.Error("error on parse: ", err)
return
}
output, err := obj.Verify(&rsaTestKey.PublicKey)
if err != nil {
t.Error("error on verify: ", err)
return
}
if bytes.Compare(output, input) != 0 {
t.Error("input/output do not match", output, input)
return
}
output, err = obj.Verify(sharedKey)
if err != nil {
t.Error("error on verify: ", err)
return
}
if bytes.Compare(output, input) != 0 {
t.Error("input/output do not match", output, input)
return
}
}
func GenerateSigningTestKey(sigAlg SignatureAlgorithm) (sig, ver interface{}) {
switch sigAlg {
case RS256, RS384, RS512, PS256, PS384, PS512:
sig = rsaTestKey
ver = &rsaTestKey.PublicKey
case HS256, HS384, HS512:
sig, _, _ = randomKeyGenerator{size: 16}.genKey()
ver = sig
case ES256:
key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
sig = key
ver = &key.PublicKey
case ES384:
key, _ := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
sig = key
ver = &key.PublicKey
case ES512:
key, _ := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
sig = key
ver = &key.PublicKey
default:
panic("Must update test case")
}
return
}
func TestInvalidSignerAlg(t *testing.T) {
_, err := NewSigner("XYZ", nil)
if err == nil {
t.Error("should not accept invalid algorithm")
}
_, err = NewSigner("XYZ", []byte{})
if err == nil {
t.Error("should not accept invalid algorithm")
}
}
func TestInvalidJWS(t *testing.T) {
signer, err := NewSigner(PS256, rsaTestKey)
if err != nil {
panic(err)
}
obj, err := signer.Sign([]byte("Lorem ipsum dolor sit amet"))
obj.Signatures[0].header = &rawHeader{
Crit: []string{"TEST"},
}
_, err = obj.Verify(&rsaTestKey.PublicKey)
if err == nil {
t.Error("should not verify message with unknown crit header")
}
// Try without alg header
obj.Signatures[0].protected = &rawHeader{}
obj.Signatures[0].header = &rawHeader{}
_, err = obj.Verify(&rsaTestKey.PublicKey)
if err == nil {
t.Error("should not verify message with missing headers")
}
}
func TestSignerKid(t *testing.T) {
kid := "DEADBEEF"
payload := []byte("Lorem ipsum dolor sit amet")
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Error("problem generating test signing key", err)
}
basejwk := JsonWebKey{Key: key}
jsonbar, err := basejwk.MarshalJSON()
if err != nil {
t.Error("problem marshalling base JWK", err)
}
var jsonmsi map[string]interface{}
err = json.Unmarshal(jsonbar, &jsonmsi)
if err != nil {
t.Error("problem unmarshalling base JWK", err)
}
jsonmsi["kid"] = kid
jsonbar2, err := json.Marshal(jsonmsi)
if err != nil {
t.Error("problem marshalling kided JWK", err)
}
var jwk JsonWebKey
err = jwk.UnmarshalJSON(jsonbar2)
if err != nil {
t.Error("problem unmarshalling kided JWK", err)
}
signer, err := NewSigner(ES256, &jwk)
if err != nil {
t.Error("problem creating signer", err)
}
signed, err := signer.Sign(payload)
serialized := signed.FullSerialize()
parsed, err := ParseSigned(serialized)
if err != nil {
t.Error("problem parsing signed object", err)
}
if parsed.Signatures[0].Header.KeyID != kid {
t.Error("KeyID did not survive trip")
}
}

View File

@ -0,0 +1,131 @@
/*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"bytes"
"crypto/cipher"
"crypto/rand"
"io"
"testing"
)
func TestInvalidSymmetricAlgorithms(t *testing.T) {
_, err := newSymmetricRecipient("XYZ", []byte{})
if err != ErrUnsupportedAlgorithm {
t.Error("should not accept invalid algorithm")
}
enc := &symmetricKeyCipher{}
_, err = enc.encryptKey([]byte{}, "XYZ")
if err != ErrUnsupportedAlgorithm {
t.Error("should not accept invalid algorithm")
}
}
func TestAeadErrors(t *testing.T) {
aead := &aeadContentCipher{
keyBytes: 16,
authtagBytes: 16,
getAead: func(key []byte) (cipher.AEAD, error) {
return nil, ErrCryptoFailure
},
}
parts, err := aead.encrypt([]byte{}, []byte{}, []byte{})
if err != ErrCryptoFailure {
t.Error("should handle aead failure")
}
_, err = aead.decrypt([]byte{}, []byte{}, parts)
if err != ErrCryptoFailure {
t.Error("should handle aead failure")
}
}
func TestInvalidKey(t *testing.T) {
gcm := newAESGCM(16).(*aeadContentCipher)
_, err := gcm.getAead([]byte{})
if err == nil {
t.Error("should not accept invalid key")
}
}
func TestStaticKeyGen(t *testing.T) {
key := make([]byte, 32)
io.ReadFull(rand.Reader, key)
gen := &staticKeyGenerator{key: key}
if gen.keySize() != len(key) {
t.Error("static key generator reports incorrect size")
}
generated, _, err := gen.genKey()
if err != nil {
t.Error("static key generator should always succeed", err)
}
if !bytes.Equal(generated, key) {
t.Error("static key generator returns different data")
}
}
func TestVectorsAESGCM(t *testing.T) {
// Source: http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-29#appendix-A.1
plaintext := []byte{
84, 104, 101, 32, 116, 114, 117, 101, 32, 115, 105, 103, 110, 32,
111, 102, 32, 105, 110, 116, 101, 108, 108, 105, 103, 101, 110, 99,
101, 32, 105, 115, 32, 110, 111, 116, 32, 107, 110, 111, 119, 108,
101, 100, 103, 101, 32, 98, 117, 116, 32, 105, 109, 97, 103, 105,
110, 97, 116, 105, 111, 110, 46}
aad := []byte{
101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 83, 85, 48, 69,
116, 84, 48, 70, 70, 85, 67, 73, 115, 73, 109, 86, 117, 89, 121, 73,
54, 73, 107, 69, 121, 78, 84, 90, 72, 81, 48, 48, 105, 102, 81}
expectedCiphertext := []byte{
229, 236, 166, 241, 53, 191, 115, 196, 174, 43, 73, 109, 39, 122,
233, 96, 140, 206, 120, 52, 51, 237, 48, 11, 190, 219, 186, 80, 111,
104, 50, 142, 47, 167, 59, 61, 181, 127, 196, 21, 40, 82, 242, 32,
123, 143, 168, 226, 73, 216, 176, 144, 138, 247, 106, 60, 16, 205,
160, 109, 64, 63, 192}
expectedAuthtag := []byte{
92, 80, 104, 49, 133, 25, 161, 215, 173, 101, 219, 211, 136, 91, 210, 145}
// Mock random reader
randReader = bytes.NewReader([]byte{
177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154,
212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122,
234, 64, 252, 227, 197, 117, 252, 2, 219, 233, 68, 180, 225, 77, 219})
defer resetRandReader()
enc := newAESGCM(32)
key, _, _ := randomKeyGenerator{size: 32}.genKey()
out, err := enc.encrypt(key, aad, plaintext)
if err != nil {
t.Error("Unable to encrypt:", err)
return
}
if bytes.Compare(out.ciphertext, expectedCiphertext) != 0 {
t.Error("Ciphertext did not match")
}
if bytes.Compare(out.tag, expectedAuthtag) != 0 {
t.Error("Auth tag did not match")
}
}

View File

@ -0,0 +1,225 @@
/*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"encoding/base64"
"encoding/hex"
"math/big"
"regexp"
"testing"
)
// Reset random reader to original value
func resetRandReader() {
randReader = rand.Reader
}
// Build big int from hex-encoded string. Strips whitespace (for testing).
func fromHexInt(base16 string) *big.Int {
re := regexp.MustCompile(`\s+`)
val, ok := new(big.Int).SetString(re.ReplaceAllString(base16, ""), 16)
if !ok {
panic("Invalid test data")
}
return val
}
// Build big int from base64-encoded string. Strips whitespace (for testing).
func fromBase64Int(base64 string) *big.Int {
re := regexp.MustCompile(`\s+`)
val, err := base64URLDecode(re.ReplaceAllString(base64, ""))
if err != nil {
panic("Invalid test data")
}
return new(big.Int).SetBytes(val)
}
// Decode hex-encoded string into byte array. Strips whitespace (for testing).
func fromHexBytes(base16 string) []byte {
re := regexp.MustCompile(`\s+`)
val, err := hex.DecodeString(re.ReplaceAllString(base16, ""))
if err != nil {
panic("Invalid test data")
}
return val
}
// Decode base64-encoded string into byte array. Strips whitespace (for testing).
func fromBase64Bytes(b64 string) []byte {
re := regexp.MustCompile(`\s+`)
val, err := base64.StdEncoding.DecodeString(re.ReplaceAllString(b64, ""))
if err != nil {
panic("Invalid test data")
}
return val
}
// Test vectors below taken from crypto/x509/x509_test.go in the Go std lib.
var pkixPublicKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3VoPN9PKUjKFLMwOge6+
wnDi8sbETGIx2FKXGgqtAKpzmem53kRGEQg8WeqRmp12wgp74TGpkEXsGae7RS1k
enJCnma4fii+noGH7R0qKgHvPrI2Bwa9hzsH8tHxpyM3qrXslOmD45EH9SxIDUBJ
FehNdaPbLP1gFyahKMsdfxFJLUvbUycuZSJ2ZnIgeVxwm4qbSvZInL9Iu4FzuPtg
fINKcbbovy1qq4KvPIrXzhbY3PWDc6btxCf3SE0JdE1MCPThntB62/bLMSQ7xdDR
FF53oIpvxe/SCOymfWq/LW849Ytv3Xwod0+wzAP8STXG4HSELS4UedPYeHJJJYcZ
+QIDAQAB
-----END PUBLIC KEY-----`
var pkcs1PrivateKey = `-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
-----END RSA PRIVATE KEY-----`
var ecdsaSHA256p384CertPem = `
-----BEGIN CERTIFICATE-----
MIICSjCCAdECCQDje/no7mXkVzAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS
BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMDYxMDM0
WhcNMjIwNTE5MDYxMDM0WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg
SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s
YW5nLWRldkBnbWFpbC5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARRuzRNIKRK
jIktEmXanNmrTR/q/FaHXLhWRZ6nHWe26Fw7Rsrbk+VjGy4vfWtNn7xSFKrOu5ze
qxKnmE0h5E480MNgrUiRkaGO2GMJJVmxx20aqkXOk59U8yGA4CghE6MwCgYIKoZI
zj0EAwIDZwAwZAIwBZEN8gvmRmfeP/9C1PRLzODIY4JqWub2PLRT4mv9GU+yw3Gr
PU9A3CHMdEcdw/MEAjBBO1lId8KOCh9UZunsSMfqXiVurpzmhWd6VYZ/32G+M+Mh
3yILeYQzllt/g0rKVRk=
-----END CERTIFICATE-----`
var ecdsaSHA256p384CertDer = fromBase64Bytes(`
MIICSjCCAdECCQDje/no7mXkVzAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS
BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMDYxMDM0
WhcNMjIwNTE5MDYxMDM0WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg
SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s
YW5nLWRldkBnbWFpbC5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARRuzRNIKRK
jIktEmXanNmrTR/q/FaHXLhWRZ6nHWe26Fw7Rsrbk+VjGy4vfWtNn7xSFKrOu5ze
qxKnmE0h5E480MNgrUiRkaGO2GMJJVmxx20aqkXOk59U8yGA4CghE6MwCgYIKoZI
zj0EAwIDZwAwZAIwBZEN8gvmRmfeP/9C1PRLzODIY4JqWub2PLRT4mv9GU+yw3Gr
PU9A3CHMdEcdw/MEAjBBO1lId8KOCh9UZunsSMfqXiVurpzmhWd6VYZ/32G+M+Mh
3yILeYQzllt/g0rKVRk=`)
var pkcs8ECPrivateKey = `
-----BEGIN PRIVATE KEY-----
MIHtAgEAMBAGByqGSM49AgEGBSuBBAAjBIHVMIHSAgEBBEHqkl65VsjYDQWIHfgv
zQLPa0JZBsaJI16mjiH8k6VA4lgfK/KNldlEsY433X7wIzo43u8OpX7Nv7n8pVRH
15XWK6GBiQOBhgAEAfDuikMI4bWsyse7t8iSCmjt9fneW/qStZuIPuVLo7mSJdud
Cs3J/x9wOnnhLv1u+0atnq5HKKdL4ff3itJPlhmSAQzByKQ5LTvB7d6fn95GJVK/
hNuS5qGBpB7qeMXVFoki0/2RZIOway8/fXjmNYwe4v/XB5LLn4hcTvEUGYcF8M9K
-----END PRIVATE KEY-----`
var ecPrivateKey = `
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIBv2rdY9mWGD/UgiuXB0LJcUzgaB6TXq/Ra1jrZKBV3IGSacM5QDFu
N8yrywiQaTDEqn1zVcLwrnqoQux3gWN1jxugBwYFK4EEACOhgYkDgYYABAFJgaM/
2a3+gE6Khm/1PYftqNwAzQ21HSLp27q2lTN+GBFho691ARFRkr9UzlQ8gRnhkTbu
yGfASamlHsYlr3Tv+gFc4BY8SU0q8kzpQ0dOHWFk7dfGFmKwhJrSFIIOeRn/LY03
XsVFctNDsGhobS2JguQrxhGx8Ll7vQCakV/PEmCQJA==
-----END EC PRIVATE KEY-----`
var ecPrivateKeyDer = fromBase64Bytes(`
MIHcAgEBBEIBv2rdY9mWGD/UgiuXB0LJcUzgaB6TXq/Ra1jrZKBV3IGSacM5QDFu
N8yrywiQaTDEqn1zVcLwrnqoQux3gWN1jxugBwYFK4EEACOhgYkDgYYABAFJgaM/
2a3+gE6Khm/1PYftqNwAzQ21HSLp27q2lTN+GBFho691ARFRkr9UzlQ8gRnhkTbu
yGfASamlHsYlr3Tv+gFc4BY8SU0q8kzpQ0dOHWFk7dfGFmKwhJrSFIIOeRn/LY03
XsVFctNDsGhobS2JguQrxhGx8Ll7vQCakV/PEmCQJA==`)
var invalidPemKey = `
-----BEGIN PUBLIC KEY-----
MIHcAgEBBEIBv2rdY9mWGD/UgiuXB0LJcUzgaB6TXq/Ra1jrZKBV3IGSacM5QDFu
XsVFctNDsGhobS2JguQrxhGx8Ll7vQCakV/PEmCQJA==
-----END PUBLIC KEY-----`
func TestLoadPublicKey(t *testing.T) {
pub, err := LoadPublicKey([]byte(pkixPublicKey))
switch pub.(type) {
case *rsa.PublicKey:
default:
t.Error("failed to parse RSA PKIX public key:", err)
}
pub, err = LoadPublicKey([]byte(ecdsaSHA256p384CertPem))
switch pub.(type) {
case *ecdsa.PublicKey:
default:
t.Error("failed to parse ECDSA X.509 cert:", err)
}
pub, err = LoadPublicKey([]byte(ecdsaSHA256p384CertDer))
switch pub.(type) {
case *ecdsa.PublicKey:
default:
t.Error("failed to parse ECDSA X.509 cert:", err)
}
pub, err = LoadPublicKey([]byte("###"))
if err == nil {
t.Error("should not parse invalid key")
}
pub, err = LoadPublicKey([]byte(invalidPemKey))
if err == nil {
t.Error("should not parse invalid key")
}
}
func TestLoadPrivateKey(t *testing.T) {
priv, err := LoadPrivateKey([]byte(pkcs1PrivateKey))
switch priv.(type) {
case *rsa.PrivateKey:
default:
t.Error("failed to parse RSA PKCS1 private key:", err)
}
priv, err = LoadPrivateKey([]byte(pkcs8ECPrivateKey))
if _, ok := priv.(*ecdsa.PrivateKey); !ok {
t.Error("failed to parse EC PKCS8 private key:", err)
}
priv, err = LoadPrivateKey([]byte(ecPrivateKey))
if _, ok := priv.(*ecdsa.PrivateKey); !ok {
t.Error("failed to parse EC private key:", err)
}
priv, err = LoadPrivateKey([]byte(ecPrivateKeyDer))
if _, ok := priv.(*ecdsa.PrivateKey); !ok {
t.Error("failed to parse EC private key:", err)
}
priv, err = LoadPrivateKey([]byte("###"))
if err == nil {
t.Error("should not parse invalid key")
}
priv, err = LoadPrivateKey([]byte(invalidPemKey))
if err == nil {
t.Error("should not parse invalid key")
}
}

View File

@ -1,6 +1,7 @@
language: go
sudo: false
go:
- 1.4
- 1.5rc1
- 1.5
script:
- go test -short -bench=.
- go test -race -v -bench=.

View File

@ -10,9 +10,9 @@ If there is stuff you should know as a DNS programmer there isn't a convenience
function for it. Server side and client side programming is supported, i.e. you
can build servers and resolvers with it.
If you like this, you may also be interested in:
* https://github.com/miekg/unbound -- Go wrapper for the Unbound resolver.
We try to keep the "master" branch as sane as possible and at the bleeding edge
of standards, avoiding breaking changes wherever reasonable. We support the last
two versions of Go, currently: 1.4 and 1.5.
# Goals
@ -40,6 +40,12 @@ A not-so-up-to-date-list-that-may-be-actually-current:
* https://mesosphere.github.io/mesos-dns/
* https://pulse.turbobytes.com/
* https://play.google.com/store/apps/details?id=com.turbobytes.dig
* https://github.com/fcambus/statzone
* https://github.com/benschw/dns-clb-go
* https://github.com/corny/dnscheck for http://public-dns.tk/
* https://namesmith.io
* https://github.com/miekg/unbound
* https://github.com/miekg/exdns
Send pull request if you want to be listed here.
@ -121,6 +127,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository.
* 6605 - ECDSA
* 6725 - IANA Registry Update
* 6742 - ILNP DNS
* 6840 - Clarifications and Implementation Notes for DNS Security
* 6844 - CAA record
* 6891 - EDNS0 update
* 6895 - DNS IANA considerations

View File

@ -42,7 +42,7 @@ type Client struct {
//
// co := &dns.Conn{Conn: c} // c is your net.Conn
// co.WriteMsg(m)
// in, err := co.ReadMsg()
// in, err := co.ReadMsg()
// co.Close()
//
func Exchange(m *Msg, a string) (r *Msg, err error) {
@ -53,8 +53,6 @@ func Exchange(m *Msg, a string) (r *Msg, err error) {
}
defer co.Close()
co.SetReadDeadline(time.Now().Add(dnsTimeout))
co.SetWriteDeadline(time.Now().Add(dnsTimeout))
opt := m.IsEdns0()
// If EDNS0 is used use that for size.
@ -62,9 +60,12 @@ func Exchange(m *Msg, a string) (r *Msg, err error) {
co.UDPSize = opt.UDPSize()
}
co.SetWriteDeadline(time.Now().Add(dnsTimeout))
if err = co.WriteMsg(m); err != nil {
return nil, err
}
co.SetReadDeadline(time.Now().Add(dnsTimeout))
r, err = co.ReadMsg()
if err == nil && r.Id != m.Id {
err = ErrId
@ -171,13 +172,13 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
co.UDPSize = c.UDPSize
}
co.SetReadDeadline(time.Now().Add(c.readTimeout()))
co.SetWriteDeadline(time.Now().Add(c.writeTimeout()))
co.TsigSecret = c.TsigSecret
co.SetWriteDeadline(time.Now().Add(c.writeTimeout()))
if err = co.WriteMsg(m); err != nil {
return nil, 0, err
}
co.SetReadDeadline(time.Now().Add(c.readTimeout()))
r, err = co.ReadMsg()
if err == nil && r.Id != m.Id {
err = ErrId
@ -196,6 +197,12 @@ func (co *Conn) ReadMsg() (*Msg, error) {
m := new(Msg)
if err := m.Unpack(p); err != nil {
// If ErrTruncated was returned, we still want to allow the user to use
// the message, but naively they can just check err if they don't want
// to use a truncated message
if err == ErrTruncated {
return m, err
}
return nil, err
}
if t := m.IsTsig(); t != nil {

View File

@ -1,286 +0,0 @@
package dns
import (
"strconv"
"testing"
"time"
)
func TestClientSync(t *testing.T) {
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to run test server: %v", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeSOA)
c := new(Client)
r, _, err := c.Exchange(m, addrstr)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
if r != nil && r.Rcode != RcodeSuccess {
t.Errorf("failed to get an valid answer\n%v", r)
}
// And now with plain Exchange().
r, err = Exchange(m, addrstr)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
if r == nil || r.Rcode != RcodeSuccess {
t.Errorf("failed to get an valid answer\n%v", r)
}
}
func TestClientSyncBadId(t *testing.T) {
HandleFunc("miek.nl.", HelloServerBadId)
defer HandleRemove("miek.nl.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to run test server: %v", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeSOA)
c := new(Client)
if _, _, err := c.Exchange(m, addrstr); err != ErrId {
t.Errorf("did not find a bad Id")
}
// And now with plain Exchange().
if _, err := Exchange(m, addrstr); err != ErrId {
t.Errorf("did not find a bad Id")
}
}
func TestClientEDNS0(t *testing.T) {
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to run test server: %v", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeDNSKEY)
m.SetEdns0(2048, true)
c := new(Client)
r, _, err := c.Exchange(m, addrstr)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
if r != nil && r.Rcode != RcodeSuccess {
t.Errorf("failed to get an valid answer\n%v", r)
}
}
// Validates the transmission and parsing of local EDNS0 options.
func TestClientEDNS0Local(t *testing.T) {
optStr1 := "1979:0x0707"
optStr2 := strconv.Itoa(EDNS0LOCALSTART) + ":0x0601"
handler := func(w ResponseWriter, req *Msg) {
m := new(Msg)
m.SetReply(req)
m.Extra = make([]RR, 1, 2)
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello local edns"}}
// If the local options are what we expect, then reflect them back.
ec1 := req.Extra[0].(*OPT).Option[0].(*EDNS0_LOCAL).String()
ec2 := req.Extra[0].(*OPT).Option[1].(*EDNS0_LOCAL).String()
if ec1 == optStr1 && ec2 == optStr2 {
m.Extra = append(m.Extra, req.Extra[0])
}
w.WriteMsg(m)
}
HandleFunc("miek.nl.", handler)
defer HandleRemove("miek.nl.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to run test server: %s", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeTXT)
// Add two local edns options to the query.
ec1 := &EDNS0_LOCAL{Code: 1979, Data: []byte{7, 7}}
ec2 := &EDNS0_LOCAL{Code: EDNS0LOCALSTART, Data: []byte{6, 1}}
o := &OPT{Hdr: RR_Header{Name: ".", Rrtype: TypeOPT}, Option: []EDNS0{ec1, ec2}}
m.Extra = append(m.Extra, o)
c := new(Client)
r, _, e := c.Exchange(m, addrstr)
if e != nil {
t.Logf("failed to exchange: %s", e.Error())
t.Fail()
}
if r != nil && r.Rcode != RcodeSuccess {
t.Log("failed to get a valid answer")
t.Fail()
t.Logf("%v\n", r)
}
txt := r.Extra[0].(*TXT).Txt[0]
if txt != "Hello local edns" {
t.Log("Unexpected result for miek.nl", txt, "!= Hello local edns")
t.Fail()
}
// Validate the local options in the reply.
got := r.Extra[1].(*OPT).Option[0].(*EDNS0_LOCAL).String()
if got != optStr1 {
t.Log("failed to get local edns0 answer; got %s, expected %s", got, optStr1)
t.Fail()
t.Logf("%v\n", r)
}
got = r.Extra[1].(*OPT).Option[1].(*EDNS0_LOCAL).String()
if got != optStr2 {
t.Log("failed to get local edns0 answer; got %s, expected %s", got, optStr2)
t.Fail()
t.Logf("%v\n", r)
}
}
func TestSingleSingleInflight(t *testing.T) {
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to run test server: %v", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeDNSKEY)
c := new(Client)
c.SingleInflight = true
nr := 10
ch := make(chan time.Duration)
for i := 0; i < nr; i++ {
go func() {
_, rtt, _ := c.Exchange(m, addrstr)
ch <- rtt
}()
}
i := 0
var first time.Duration
// With inflight *all* rtt are identical, and by doing actual lookups
// the changes that this is a coincidence is small.
Loop:
for {
select {
case rtt := <-ch:
if i == 0 {
first = rtt
} else {
if first != rtt {
t.Errorf("all rtts should be equal. got %d want %d", rtt, first)
}
}
i++
if i == 10 {
break Loop
}
}
}
}
// ExampleUpdateLeaseTSIG shows how to update a lease signed with TSIG.
func ExampleUpdateLeaseTSIG(t *testing.T) {
m := new(Msg)
m.SetUpdate("t.local.ip6.io.")
rr, _ := NewRR("t.local.ip6.io. 30 A 127.0.0.1")
rrs := make([]RR, 1)
rrs[0] = rr
m.Insert(rrs)
leaseRr := new(OPT)
leaseRr.Hdr.Name = "."
leaseRr.Hdr.Rrtype = TypeOPT
e := new(EDNS0_UL)
e.Code = EDNS0UL
e.Lease = 120
leaseRr.Option = append(leaseRr.Option, e)
m.Extra = append(m.Extra, leaseRr)
c := new(Client)
m.SetTsig("polvi.", HmacMD5, 300, time.Now().Unix())
c.TsigSecret = map[string]string{"polvi.": "pRZgBrBvI4NAHZYhxmhs/Q=="}
_, _, err := c.Exchange(m, "127.0.0.1:53")
if err != nil {
t.Error(err)
}
}
func TestClientConn(t *testing.T) {
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
// This uses TCP just to make it slightly different than TestClientSync
s, addrstr, err := RunLocalTCPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to run test server: %v", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeSOA)
cn, err := Dial("tcp", addrstr)
if err != nil {
t.Errorf("failed to dial %s: %v", addrstr, err)
}
err = cn.WriteMsg(m)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
r, err := cn.ReadMsg()
if r == nil || r.Rcode != RcodeSuccess {
t.Errorf("failed to get an valid answer\n%v", r)
}
err = cn.WriteMsg(m)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
h := new(Header)
buf, err := cn.ReadMsgHeader(h)
if buf == nil {
t.Errorf("failed to get an valid answer\n%v", r)
}
if int(h.Bits&0xF) != RcodeSuccess {
t.Errorf("failed to get an valid answer in ReadMsgHeader\n%v", r)
}
if h.Ancount != 0 || h.Qdcount != 1 || h.Nscount != 0 || h.Arcount != 1 {
t.Errorf("expected to have question and additional in response; got something else: %+v", h)
}
if err = r.Unpack(buf); err != nil {
t.Errorf("unable to unpack message fully: %v", err)
}
}

View File

@ -1,55 +0,0 @@
package dns
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
const normal string = `
# Comment
domain somedomain.com
nameserver 10.28.10.2
nameserver 11.28.10.1
`
const missingNewline string = `
domain somedomain.com
nameserver 10.28.10.2
nameserver 11.28.10.1` // <- NOTE: NO newline.
func testConfig(t *testing.T, data string) {
tempDir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("TempDir: %v", err)
}
defer os.RemoveAll(tempDir)
path := filepath.Join(tempDir, "resolv.conf")
if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil {
t.Fatalf("WriteFile: %v", err)
}
cc, err := ClientConfigFromFile(path)
if err != nil {
t.Errorf("error parsing resolv.conf: %v", err)
}
if l := len(cc.Servers); l != 2 {
t.Errorf("incorrect number of nameservers detected: %d", l)
}
if l := len(cc.Search); l != 1 {
t.Errorf("domain directive not parsed correctly: %v", cc.Search)
} else {
if cc.Search[0] != "somedomain.com" {
t.Errorf("domain is unexpected: %v", cc.Search[0])
}
}
}
func TestNameserver(t *testing.T) {
testConfig(t, normal)
}
func TestMissingFinalNewLine(t *testing.T) {
testConfig(t, missingNewline)
}

View File

@ -150,11 +150,14 @@ func (dns *Msg) IsEdns0() *OPT {
return nil
}
// IsDomainName checks if s is a valid domainname, it returns
// the number of labels and true, when a domain name is valid.
// Note that non fully qualified domain name is considered valid, in this case the
// last label is counted in the number of labels.
// When false is returned the number of labels is not defined.
// IsDomainName checks if s is a valid domain name, it returns the number of
// labels and true, when a domain name is valid. Note that non fully qualified
// domain name is considered valid, in this case the last label is counted in
// the number of labels. When false is returned the number of labels is not
// defined. Also note that this function is extremely liberal; almost any
// string is a valid domain name as the DNS is 8 bit protocol. It checks if each
// label fits in 63 characters, but there is no length check for the entire
// string s. I.e. a domain name longer than 255 characters is considered valid.
func IsDomainName(s string) (labels int, ok bool) {
_, labels, err := packDomainName(s, nil, 0, nil, false)
return labels, err == nil

View File

@ -36,9 +36,7 @@ type RR interface {
len() int
}
// DNS resource records.
// There are many types of RRs,
// but they all share the same header.
// RR_Header is the header all DNS resource records share.
type RR_Header struct {
Name string `dns:"cdomain-name"`
Rrtype uint16

View File

@ -1,578 +0,0 @@
package dns
import (
"encoding/hex"
"net"
"testing"
)
func TestPackUnpack(t *testing.T) {
out := new(Msg)
out.Answer = make([]RR, 1)
key := new(DNSKEY)
key = &DNSKEY{Flags: 257, Protocol: 3, Algorithm: RSASHA1}
key.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 3600}
key.PublicKey = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
out.Answer[0] = key
msg, err := out.Pack()
if err != nil {
t.Error("failed to pack msg with DNSKEY")
}
in := new(Msg)
if in.Unpack(msg) != nil {
t.Error("failed to unpack msg with DNSKEY")
}
sig := new(RRSIG)
sig = &RRSIG{TypeCovered: TypeDNSKEY, Algorithm: RSASHA1, Labels: 2,
OrigTtl: 3600, Expiration: 4000, Inception: 4000, KeyTag: 34641, SignerName: "miek.nl.",
Signature: "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"}
sig.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeRRSIG, Class: ClassINET, Ttl: 3600}
out.Answer[0] = sig
msg, err = out.Pack()
if err != nil {
t.Error("failed to pack msg with RRSIG")
}
if in.Unpack(msg) != nil {
t.Error("failed to unpack msg with RRSIG")
}
}
func TestPackUnpack2(t *testing.T) {
m := new(Msg)
m.Extra = make([]RR, 1)
m.Answer = make([]RR, 1)
dom := "miek.nl."
rr := new(A)
rr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0}
rr.A = net.IPv4(127, 0, 0, 1)
x := new(TXT)
x.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
x.Txt = []string{"heelalaollo"}
m.Extra[0] = x
m.Answer[0] = rr
_, err := m.Pack()
if err != nil {
t.Error("Packing failed: ", err)
return
}
}
func TestPackUnpack3(t *testing.T) {
m := new(Msg)
m.Extra = make([]RR, 2)
m.Answer = make([]RR, 1)
dom := "miek.nl."
rr := new(A)
rr.Hdr = RR_Header{Name: dom, Rrtype: TypeA, Class: ClassINET, Ttl: 0}
rr.A = net.IPv4(127, 0, 0, 1)
x1 := new(TXT)
x1.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
x1.Txt = []string{}
x2 := new(TXT)
x2.Hdr = RR_Header{Name: dom, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}
x2.Txt = []string{"heelalaollo"}
m.Extra[0] = x1
m.Extra[1] = x2
m.Answer[0] = rr
b, err := m.Pack()
if err != nil {
t.Error("packing failed: ", err)
return
}
var unpackMsg Msg
err = unpackMsg.Unpack(b)
if err != nil {
t.Error("unpacking failed")
return
}
}
func TestBailiwick(t *testing.T) {
yes := map[string]string{
"miek.nl": "ns.miek.nl",
".": "miek.nl",
}
for parent, child := range yes {
if !IsSubDomain(parent, child) {
t.Errorf("%s should be child of %s", child, parent)
t.Errorf("comparelabels %d", CompareDomainName(parent, child))
t.Errorf("lenlabels %d %d", CountLabel(parent), CountLabel(child))
}
}
no := map[string]string{
"www.miek.nl": "ns.miek.nl",
"m\\.iek.nl": "ns.miek.nl",
"w\\.iek.nl": "w.iek.nl",
"p\\\\.iek.nl": "ns.p.iek.nl", // p\\.iek.nl , literal \ in domain name
"miek.nl": ".",
}
for parent, child := range no {
if IsSubDomain(parent, child) {
t.Errorf("%s should not be child of %s", child, parent)
t.Errorf("comparelabels %d", CompareDomainName(parent, child))
t.Errorf("lenlabels %d %d", CountLabel(parent), CountLabel(child))
}
}
}
func TestPack(t *testing.T) {
rr := []string{"US. 86400 IN NSEC 0-.us. NS SOA RRSIG NSEC DNSKEY TYPE65534"}
m := new(Msg)
var err error
m.Answer = make([]RR, 1)
for _, r := range rr {
m.Answer[0], err = NewRR(r)
if err != nil {
t.Errorf("failed to create RR: %v", err)
continue
}
if _, err := m.Pack(); err != nil {
t.Errorf("packing failed: %v", err)
}
}
x := new(Msg)
ns, _ := NewRR("pool.ntp.org. 390 IN NS a.ntpns.org")
ns.(*NS).Ns = "a.ntpns.org"
x.Ns = append(m.Ns, ns)
x.Ns = append(m.Ns, ns)
x.Ns = append(m.Ns, ns)
// This crashes due to the fact the a.ntpns.org isn't a FQDN
// How to recover() from a remove panic()?
if _, err := x.Pack(); err == nil {
t.Error("packing should fail")
}
x.Answer = make([]RR, 1)
x.Answer[0], err = NewRR(rr[0])
if _, err := x.Pack(); err == nil {
t.Error("packing should fail")
}
x.Question = make([]Question, 1)
x.Question[0] = Question{";sd#edddds鍛↙赏‘℅∥↙xzztsestxssweewwsssstx@s@Z嵌e@cn.pool.ntp.org.", TypeA, ClassINET}
if _, err := x.Pack(); err == nil {
t.Error("packing should fail")
}
}
func TestPackNAPTR(t *testing.T) {
for _, n := range []string{
`apple.com. IN NAPTR 100 50 "se" "SIP+D2U" "" _sip._udp.apple.com.`,
`apple.com. IN NAPTR 90 50 "se" "SIP+D2T" "" _sip._tcp.apple.com.`,
`apple.com. IN NAPTR 50 50 "se" "SIPS+D2T" "" _sips._tcp.apple.com.`,
} {
rr, _ := NewRR(n)
msg := make([]byte, rr.len())
if off, err := PackRR(rr, msg, 0, nil, false); err != nil {
t.Errorf("packing failed: %v", err)
t.Errorf("length %d, need more than %d", rr.len(), off)
} else {
t.Logf("buf size needed: %d", off)
}
}
}
func TestCompressLength(t *testing.T) {
m := new(Msg)
m.SetQuestion("miek.nl", TypeMX)
ul := m.Len()
m.Compress = true
if ul != m.Len() {
t.Fatalf("should be equal")
}
}
// Does the predicted length match final packed length?
func TestMsgCompressLength(t *testing.T) {
makeMsg := func(question string, ans, ns, e []RR) *Msg {
msg := new(Msg)
msg.SetQuestion(Fqdn(question), TypeANY)
msg.Answer = append(msg.Answer, ans...)
msg.Ns = append(msg.Ns, ns...)
msg.Extra = append(msg.Extra, e...)
msg.Compress = true
return msg
}
name1 := "12345678901234567890123456789012345.12345678.123."
rrA, _ := NewRR(name1 + " 3600 IN A 192.0.2.1")
rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
tests := []*Msg{
makeMsg(name1, []RR{rrA}, nil, nil),
makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}
for _, msg := range tests {
predicted := msg.Len()
buf, err := msg.Pack()
if err != nil {
t.Error(err)
}
if predicted < len(buf) {
t.Errorf("predicted compressed length is wrong: predicted %s (len=%d) %d, actual %d",
msg.Question[0].Name, len(msg.Answer), predicted, len(buf))
}
}
}
func TestMsgLength(t *testing.T) {
makeMsg := func(question string, ans, ns, e []RR) *Msg {
msg := new(Msg)
msg.SetQuestion(Fqdn(question), TypeANY)
msg.Answer = append(msg.Answer, ans...)
msg.Ns = append(msg.Ns, ns...)
msg.Extra = append(msg.Extra, e...)
return msg
}
name1 := "12345678901234567890123456789012345.12345678.123."
rrA, _ := NewRR(name1 + " 3600 IN A 192.0.2.1")
rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
tests := []*Msg{
makeMsg(name1, []RR{rrA}, nil, nil),
makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)}
for _, msg := range tests {
predicted := msg.Len()
buf, err := msg.Pack()
if err != nil {
t.Error(err)
}
if predicted < len(buf) {
t.Errorf("predicted length is wrong: predicted %s (len=%d), actual %d",
msg.Question[0].Name, predicted, len(buf))
}
}
}
func TestMsgLength2(t *testing.T) {
// Serialized replies
var testMessages = []string{
// google.com. IN A?
"064e81800001000b0004000506676f6f676c6503636f6d0000010001c00c00010001000000050004adc22986c00c00010001000000050004adc22987c00c00010001000000050004adc22988c00c00010001000000050004adc22989c00c00010001000000050004adc2298ec00c00010001000000050004adc22980c00c00010001000000050004adc22981c00c00010001000000050004adc22982c00c00010001000000050004adc22983c00c00010001000000050004adc22984c00c00010001000000050004adc22985c00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc0d800010001000000050004d8ef200ac0ea00010001000000050004d8ef220ac0fc00010001000000050004d8ef240ac10e00010001000000050004d8ef260a0000290500000000050000",
// amazon.com. IN A? (reply has no EDNS0 record)
// TODO(miek): this one is off-by-one, need to find out why
//"6de1818000010004000a000806616d617a6f6e03636f6d0000010001c00c000100010000000500044815c2d4c00c000100010000000500044815d7e8c00c00010001000000050004b02062a6c00c00010001000000050004cdfbf236c00c000200010000000500140570646e733408756c747261646e73036f726700c00c000200010000000500150570646e733508756c747261646e7304696e666f00c00c000200010000000500160570646e733608756c747261646e7302636f02756b00c00c00020001000000050014036e7331037033310664796e656374036e657400c00c00020001000000050006036e7332c0cfc00c00020001000000050006036e7333c0cfc00c00020001000000050006036e7334c0cfc00c000200010000000500110570646e733108756c747261646e73c0dac00c000200010000000500080570646e7332c127c00c000200010000000500080570646e7333c06ec0cb00010001000000050004d04e461fc0eb00010001000000050004cc0dfa1fc0fd00010001000000050004d04e471fc10f00010001000000050004cc0dfb1fc12100010001000000050004cc4a6c01c121001c000100000005001020010502f3ff00000000000000000001c13e00010001000000050004cc4a6d01c13e001c0001000000050010261000a1101400000000000000000001",
// yahoo.com. IN A?
"fc2d81800001000300070008057961686f6f03636f6d0000010001c00c00010001000000050004628afd6dc00c00010001000000050004628bb718c00c00010001000000050004cebe242dc00c00020001000000050006036e7336c00cc00c00020001000000050006036e7338c00cc00c00020001000000050006036e7331c00cc00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7335c00cc07b0001000100000005000444b48310c08d00010001000000050004448eff10c09f00010001000000050004cb54dd35c0b100010001000000050004628a0b9dc0c30001000100000005000477a0f77cc05700010001000000050004ca2bdfaac06900010001000000050004caa568160000290500000000050000",
// microsoft.com. IN A?
"f4368180000100020005000b096d6963726f736f667403636f6d0000010001c00c0001000100000005000440040b25c00c0001000100000005000441373ac9c00c0002000100000005000e036e7331046d736674036e657400c00c00020001000000050006036e7332c04fc00c00020001000000050006036e7333c04fc00c00020001000000050006036e7334c04fc00c00020001000000050006036e7335c04fc04b000100010000000500044137253ec04b001c00010000000500102a010111200500000000000000010001c0650001000100000005000440043badc065001c00010000000500102a010111200600060000000000010001c07700010001000000050004d5c7b435c077001c00010000000500102a010111202000000000000000010001c08900010001000000050004cf2e4bfec089001c00010000000500102404f800200300000000000000010001c09b000100010000000500044137e28cc09b001c00010000000500102a010111200f000100000000000100010000290500000000050000",
// google.com. IN MX?
"724b8180000100050004000b06676f6f676c6503636f6d00000f0001c00c000f000100000005000c000a056173706d78016cc00cc00c000f0001000000050009001404616c7431c02ac00c000f0001000000050009001e04616c7432c02ac00c000f0001000000050009002804616c7433c02ac00c000f0001000000050009003204616c7434c02ac00c00020001000000050006036e7332c00cc00c00020001000000050006036e7333c00cc00c00020001000000050006036e7334c00cc00c00020001000000050006036e7331c00cc02a00010001000000050004adc2421bc02a001c00010000000500102a00145040080c01000000000000001bc04200010001000000050004adc2461bc05700010001000000050004adc2451bc06c000100010000000500044a7d8f1bc081000100010000000500044a7d191bc0ca00010001000000050004d8ef200ac09400010001000000050004d8ef220ac0a600010001000000050004d8ef240ac0b800010001000000050004d8ef260a0000290500000000050000",
// reddit.com. IN A?
"12b98180000100080000000c0672656464697403636f6d0000020001c00c0002000100000005000f046175733204616b616d036e657400c00c000200010000000500070475736534c02dc00c000200010000000500070475737733c02dc00c000200010000000500070475737735c02dc00c00020001000000050008056173696131c02dc00c00020001000000050008056173696139c02dc00c00020001000000050008056e73312d31c02dc00c0002000100000005000a076e73312d313935c02dc02800010001000000050004c30a242ec04300010001000000050004451f1d39c05600010001000000050004451f3bc7c0690001000100000005000460073240c07c000100010000000500046007fb81c090000100010000000500047c283484c090001c00010000000500102a0226f0006700000000000000000064c0a400010001000000050004c16c5b01c0a4001c000100000005001026001401000200000000000000000001c0b800010001000000050004c16c5bc3c0b8001c0001000000050010260014010002000000000000000000c30000290500000000050000",
}
for i, hexData := range testMessages {
// we won't fail the decoding of the hex
input, _ := hex.DecodeString(hexData)
m := new(Msg)
m.Unpack(input)
//println(m.String())
m.Compress = true
lenComp := m.Len()
b, _ := m.Pack()
pacComp := len(b)
m.Compress = false
lenUnComp := m.Len()
b, _ = m.Pack()
pacUnComp := len(b)
if pacComp+1 != lenComp {
t.Errorf("msg.Len(compressed)=%d actual=%d for test %d", lenComp, pacComp, i)
}
if pacUnComp+1 != lenUnComp {
t.Errorf("msg.Len(uncompressed)=%d actual=%d for test %d", lenUnComp, pacUnComp, i)
}
}
}
func TestMsgLengthCompressionMalformed(t *testing.T) {
// SOA with empty hostmaster, which is illegal
soa := &SOA{Hdr: RR_Header{Name: ".", Rrtype: TypeSOA, Class: ClassINET, Ttl: 12345},
Ns: ".",
Mbox: "",
Serial: 0,
Refresh: 28800,
Retry: 7200,
Expire: 604800,
Minttl: 60}
m := new(Msg)
m.Compress = true
m.Ns = []RR{soa}
m.Len() // Should not crash.
}
func BenchmarkMsgLength(b *testing.B) {
b.StopTimer()
makeMsg := func(question string, ans, ns, e []RR) *Msg {
msg := new(Msg)
msg.SetQuestion(Fqdn(question), TypeANY)
msg.Answer = append(msg.Answer, ans...)
msg.Ns = append(msg.Ns, ns...)
msg.Extra = append(msg.Extra, e...)
msg.Compress = true
return msg
}
name1 := "12345678901234567890123456789012345.12345678.123."
rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
b.StartTimer()
for i := 0; i < b.N; i++ {
msg.Len()
}
}
func BenchmarkMsgLengthPack(b *testing.B) {
makeMsg := func(question string, ans, ns, e []RR) *Msg {
msg := new(Msg)
msg.SetQuestion(Fqdn(question), TypeANY)
msg.Answer = append(msg.Answer, ans...)
msg.Ns = append(msg.Ns, ns...)
msg.Extra = append(msg.Extra, e...)
msg.Compress = true
return msg
}
name1 := "12345678901234567890123456789012345.12345678.123."
rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = msg.Pack()
}
}
func BenchmarkMsgPackBuffer(b *testing.B) {
makeMsg := func(question string, ans, ns, e []RR) *Msg {
msg := new(Msg)
msg.SetQuestion(Fqdn(question), TypeANY)
msg.Answer = append(msg.Answer, ans...)
msg.Ns = append(msg.Ns, ns...)
msg.Extra = append(msg.Extra, e...)
msg.Compress = true
return msg
}
name1 := "12345678901234567890123456789012345.12345678.123."
rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
buf := make([]byte, 512)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = msg.PackBuffer(buf)
}
}
func BenchmarkMsgUnpack(b *testing.B) {
makeMsg := func(question string, ans, ns, e []RR) *Msg {
msg := new(Msg)
msg.SetQuestion(Fqdn(question), TypeANY)
msg.Answer = append(msg.Answer, ans...)
msg.Ns = append(msg.Ns, ns...)
msg.Extra = append(msg.Extra, e...)
msg.Compress = true
return msg
}
name1 := "12345678901234567890123456789012345.12345678.123."
rrMx, _ := NewRR(name1 + " 3600 IN MX 10 " + name1)
msg := makeMsg(name1, []RR{rrMx, rrMx}, nil, nil)
msgBuf, _ := msg.Pack()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = msg.Unpack(msgBuf)
}
}
func BenchmarkPackDomainName(b *testing.B) {
name1 := "12345678901234567890123456789012345.12345678.123."
buf := make([]byte, len(name1)+1)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = PackDomainName(name1, buf, 0, nil, false)
}
}
func BenchmarkUnpackDomainName(b *testing.B) {
name1 := "12345678901234567890123456789012345.12345678.123."
buf := make([]byte, len(name1)+1)
_, _ = PackDomainName(name1, buf, 0, nil, false)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _ = UnpackDomainName(buf, 0)
}
}
func BenchmarkUnpackDomainNameUnprintable(b *testing.B) {
name1 := "\x02\x02\x02\x025\x02\x02\x02\x02.12345678.123."
buf := make([]byte, len(name1)+1)
_, _ = PackDomainName(name1, buf, 0, nil, false)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _, _ = UnpackDomainName(buf, 0)
}
}
func TestToRFC3597(t *testing.T) {
a, _ := NewRR("miek.nl. IN A 10.0.1.1")
x := new(RFC3597)
x.ToRFC3597(a)
if x.String() != `miek.nl. 3600 CLASS1 TYPE1 \# 4 0a000101` {
t.Error("string mismatch")
}
}
func TestNoRdataPack(t *testing.T) {
data := make([]byte, 1024)
for typ, fn := range typeToRR {
r := fn()
*r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 3600}
_, err := PackRR(r, data, 0, nil, false)
if err != nil {
t.Errorf("failed to pack RR with zero rdata: %s: %v", TypeToString[typ], err)
}
}
}
// TODO(miek): fix dns buffer too small errors this throws
func TestNoRdataUnpack(t *testing.T) {
data := make([]byte, 1024)
for typ, fn := range typeToRR {
if typ == TypeSOA || typ == TypeTSIG || typ == TypeWKS {
// SOA, TSIG will not be seen (like this) in dyn. updates?
// WKS is an bug, but...deprecated record.
continue
}
r := fn()
*r.Header() = RR_Header{Name: "miek.nl.", Rrtype: typ, Class: ClassINET, Ttl: 3600}
off, err := PackRR(r, data, 0, nil, false)
if err != nil {
// Should always works, TestNoDataPack should have caught this
t.Errorf("failed to pack RR: %v", err)
continue
}
rr, _, err := UnpackRR(data[:off], 0)
if err != nil {
t.Errorf("failed to unpack RR with zero rdata: %s: %v", TypeToString[typ], err)
}
t.Log(rr)
}
}
func TestRdataOverflow(t *testing.T) {
rr := new(RFC3597)
rr.Hdr.Name = "."
rr.Hdr.Class = ClassINET
rr.Hdr.Rrtype = 65280
rr.Rdata = hex.EncodeToString(make([]byte, 0xFFFF))
buf := make([]byte, 0xFFFF*2)
if _, err := PackRR(rr, buf, 0, nil, false); err != nil {
t.Fatalf("maximum size rrdata pack failed: %v", err)
}
rr.Rdata += "00"
if _, err := PackRR(rr, buf, 0, nil, false); err != ErrRdata {
t.Fatalf("oversize rrdata pack didn't return ErrRdata - instead: %v", err)
}
}
func TestCopy(t *testing.T) {
rr, _ := NewRR("miek.nl. 2311 IN A 127.0.0.1") // Weird TTL to avoid catching TTL
rr1 := Copy(rr)
if rr.String() != rr1.String() {
t.Fatalf("Copy() failed %s != %s", rr.String(), rr1.String())
}
}
func TestMsgCopy(t *testing.T) {
m := new(Msg)
m.SetQuestion("miek.nl.", TypeA)
rr, _ := NewRR("miek.nl. 2311 IN A 127.0.0.1")
m.Answer = []RR{rr}
rr, _ = NewRR("miek.nl. 2311 IN NS 127.0.0.1")
m.Ns = []RR{rr}
m1 := m.Copy()
if m.String() != m1.String() {
t.Fatalf("Msg.Copy() failed %s != %s", m.String(), m1.String())
}
m1.Answer[0], _ = NewRR("somethingelse.nl. 2311 IN A 127.0.0.1")
if m.String() == m1.String() {
t.Fatalf("Msg.Copy() failed; change to copy changed template %s", m.String())
}
rr, _ = NewRR("miek.nl. 2311 IN A 127.0.0.2")
m1.Answer = append(m1.Answer, rr)
if m1.Ns[0].String() == m1.Answer[1].String() {
t.Fatalf("Msg.Copy() failed; append changed underlying array %s", m1.Ns[0].String())
}
}
func BenchmarkCopy(b *testing.B) {
b.ReportAllocs()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeA)
rr, _ := NewRR("miek.nl. 2311 IN A 127.0.0.1")
m.Answer = []RR{rr}
rr, _ = NewRR("miek.nl. 2311 IN NS 127.0.0.1")
m.Ns = []RR{rr}
rr, _ = NewRR("miek.nl. 2311 IN A 127.0.0.1")
m.Extra = []RR{rr}
b.ResetTimer()
for i := 0; i < b.N; i++ {
m.Copy()
}
}
func TestPackIPSECKEY(t *testing.T) {
tests := []string{
"38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 1 2 192.0.2.38 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )",
"38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 0 2 . AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )",
"38.2.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 1 2 192.0.2.3 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )",
"38.1.0.192.in-addr.arpa. 7200 IN IPSECKEY ( 10 3 2 mygateway.example.com. AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )",
"0.d.4.0.3.0.e.f.f.f.3.f.0.1.2.0 7200 IN IPSECKEY ( 10 2 2 2001:0DB8:0:8002::2000:1 AQNRU3mG7TVTO2BkR47usntb102uFJtugbo6BSGvgqt4AQ== )",
}
buf := make([]byte, 1024)
for _, t1 := range tests {
rr, _ := NewRR(t1)
off, err := PackRR(rr, buf, 0, nil, false)
if err != nil {
t.Errorf("failed to pack IPSECKEY %v: %s", err, t1)
continue
}
rr, _, err = UnpackRR(buf[:off], 0)
if err != nil {
t.Errorf("failed to unpack IPSECKEY %v: %s", err, t1)
}
t.Log(rr)
}
}
func TestMsgPackBuffer(t *testing.T) {
var testMessages = []string{
// news.ycombinator.com.in.escapemg.com. IN A, response
"586285830001000000010000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001c0210006000100000e10002c036e7332c02103646e730b67726f6f7665736861726bc02d77ed50e600002a3000000e1000093a8000000e10",
// news.ycombinator.com.in.escapemg.com. IN A, question
"586201000001000000000000046e6577730b79636f6d62696e61746f7203636f6d02696e086573636170656d6703636f6d0000010001",
"398781020001000000000000046e6577730b79636f6d62696e61746f7203636f6d0000010001",
}
for i, hexData := range testMessages {
// we won't fail the decoding of the hex
input, _ := hex.DecodeString(hexData)
m := new(Msg)
if err := m.Unpack(input); err != nil {
t.Errorf("packet %d failed to unpack", i)
continue
}
t.Logf("packet %d %s", i, m.String())
}
}

View File

@ -6,14 +6,14 @@ import (
"crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/md5"
_ "crypto/md5"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
_ "crypto/sha1"
_ "crypto/sha256"
_ "crypto/sha512"
"encoding/asn1"
"encoding/hex"
"hash"
"io"
"math/big"
"sort"
"strings"
@ -42,6 +42,38 @@ const (
PRIVATEOID uint8 = 254
)
// Map for algorithm names.
var AlgorithmToString = map[uint8]string{
RSAMD5: "RSAMD5",
DH: "DH",
DSA: "DSA",
RSASHA1: "RSASHA1",
DSANSEC3SHA1: "DSA-NSEC3-SHA1",
RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1",
RSASHA256: "RSASHA256",
RSASHA512: "RSASHA512",
ECCGOST: "ECC-GOST",
ECDSAP256SHA256: "ECDSAP256SHA256",
ECDSAP384SHA384: "ECDSAP384SHA384",
INDIRECT: "INDIRECT",
PRIVATEDNS: "PRIVATEDNS",
PRIVATEOID: "PRIVATEOID",
}
// Map of algorithm strings.
var StringToAlgorithm = reverseInt8(AlgorithmToString)
// Map of algorithm crypto hashes.
var AlgorithmToHash = map[uint8]crypto.Hash{
RSAMD5: crypto.MD5, // Deprecated in RFC 6725
RSASHA1: crypto.SHA1,
RSASHA1NSEC3SHA1: crypto.SHA1,
RSASHA256: crypto.SHA256,
ECDSAP256SHA256: crypto.SHA256,
ECDSAP384SHA384: crypto.SHA384,
RSASHA512: crypto.SHA512,
}
// DNSSEC hashing algorithm codes.
const (
_ uint8 = iota
@ -52,6 +84,18 @@ const (
SHA512 // Experimental
)
// Map for hash names.
var HashToString = map[uint8]string{
SHA1: "SHA1",
SHA256: "SHA256",
GOST94: "GOST94",
SHA384: "SHA384",
SHA512: "SHA512",
}
// Map of hash strings.
var StringToHash = reverseInt8(HashToString)
// DNSKEY flag values.
const (
SEP = 1
@ -60,7 +104,7 @@ const (
)
// The RRSIG needs to be converted to wireformat with some of
// the rdata (the signature) missing. Use this struct to easy
// the rdata (the signature) missing. Use this struct to ease
// the conversion (and re-use the pack/unpack functions).
type rrsigWireFmt struct {
TypeCovered uint16
@ -168,24 +212,23 @@ func (k *DNSKEY) ToDS(h uint8) *DS {
// digest buffer
digest := append(owner, wire...) // another copy
var hash crypto.Hash
switch h {
case SHA1:
s := sha1.New()
io.WriteString(s, string(digest))
ds.Digest = hex.EncodeToString(s.Sum(nil))
hash = crypto.SHA1
case SHA256:
s := sha256.New()
io.WriteString(s, string(digest))
ds.Digest = hex.EncodeToString(s.Sum(nil))
hash = crypto.SHA256
case SHA384:
s := sha512.New384()
io.WriteString(s, string(digest))
ds.Digest = hex.EncodeToString(s.Sum(nil))
case GOST94:
/* I have no clue */
hash = crypto.SHA384
case SHA512:
hash = crypto.SHA512
default:
return nil
}
s := hash.New()
s.Write(digest)
ds.Digest = hex.EncodeToString(s.Sum(nil))
return ds
}
@ -205,14 +248,13 @@ func (d *DS) ToCDS() *CDS {
return c
}
// Sign signs an RRSet. The signature needs to be filled in with
// the values: Inception, Expiration, KeyTag, SignerName and Algorithm.
// The rest is copied from the RRset. Sign returns true when the signing went OK,
// otherwise false.
// There is no check if RRSet is a proper (RFC 2181) RRSet.
// If OrigTTL is non zero, it is used as-is, otherwise the TTL of the RRset
// is used as the OrigTTL.
func (rr *RRSIG) Sign(k PrivateKey, rrset []RR) error {
// Sign signs an RRSet. The signature needs to be filled in with the values:
// Inception, Expiration, KeyTag, SignerName and Algorithm. The rest is copied
// from the RRset. Sign returns a non-nill error when the signing went OK.
// There is no check if RRSet is a proper (RFC 2181) RRSet. If OrigTTL is non
// zero, it is used as-is, otherwise the TTL of the RRset is used as the
// OrigTTL.
func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
if k == nil {
return ErrPrivKey
}
@ -258,39 +300,66 @@ func (rr *RRSIG) Sign(k PrivateKey, rrset []RR) error {
}
signdata = append(signdata, wire...)
var h hash.Hash
switch rr.Algorithm {
case DSA, DSANSEC3SHA1:
// TODO: this seems bugged, will panic
case RSASHA1, RSASHA1NSEC3SHA1:
h = sha1.New()
case RSASHA256, ECDSAP256SHA256:
h = sha256.New()
case ECDSAP384SHA384:
h = sha512.New384()
case RSASHA512:
h = sha512.New()
case RSAMD5:
fallthrough // Deprecated in RFC 6725
default:
hash, ok := AlgorithmToHash[rr.Algorithm]
if !ok {
return ErrAlg
}
_, err = h.Write(signdata)
if err != nil {
return err
}
sighash := h.Sum(nil)
h := hash.New()
h.Write(signdata)
signature, err := k.Sign(sighash, rr.Algorithm)
signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm)
if err != nil {
return err
}
rr.Signature = toBase64(signature)
return nil
}
func sign(k crypto.Signer, hashed []byte, hash crypto.Hash, alg uint8) ([]byte, error) {
signature, err := k.Sign(rand.Reader, hashed, hash)
if err != nil {
return nil, err
}
switch alg {
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
return signature, nil
case ECDSAP256SHA256, ECDSAP384SHA384:
ecdsaSignature := &struct {
R, S *big.Int
}{}
if _, err := asn1.Unmarshal(signature, ecdsaSignature); err != nil {
return nil, err
}
var intlen int
switch alg {
case ECDSAP256SHA256:
intlen = 32
case ECDSAP384SHA384:
intlen = 48
}
signature := intToBytes(ecdsaSignature.R, intlen)
signature = append(signature, intToBytes(ecdsaSignature.S, intlen)...)
return signature, nil
// There is no defined interface for what a DSA backed crypto.Signer returns
case DSA, DSANSEC3SHA1:
// t := divRoundUp(divRoundUp(p.PublicKey.Y.BitLen(), 8)-64, 8)
// signature := []byte{byte(t)}
// signature = append(signature, intToBytes(r1, 20)...)
// signature = append(signature, intToBytes(s1, 20)...)
// rr.Signature = signature
}
return nil, ErrAlg
}
// Verify validates an RRSet with the signature and key. This is only the
// cryptographic test, the signature validity period must be checked separately.
// This function copies the rdata of some RRs (to lowercase domain names) for the validation to work.
@ -351,8 +420,13 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
sigbuf := rr.sigBuf() // Get the binary signature data
if rr.Algorithm == PRIVATEDNS { // PRIVATEOID
// TODO(mg)
// remove the domain name and assume its our
// TODO(miek)
// remove the domain name and assume its ours?
}
hash, ok := AlgorithmToHash[rr.Algorithm]
if !ok {
return ErrAlg
}
switch rr.Algorithm {
@ -362,52 +436,31 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
if pubkey == nil {
return ErrKey
}
// Setup the hash as defined for this alg.
var h hash.Hash
var ch crypto.Hash
switch rr.Algorithm {
case RSAMD5:
h = md5.New()
ch = crypto.MD5
case RSASHA1, RSASHA1NSEC3SHA1:
h = sha1.New()
ch = crypto.SHA1
case RSASHA256:
h = sha256.New()
ch = crypto.SHA256
case RSASHA512:
h = sha512.New()
ch = crypto.SHA512
}
io.WriteString(h, string(signeddata))
sighash := h.Sum(nil)
return rsa.VerifyPKCS1v15(pubkey, ch, sighash, sigbuf)
h := hash.New()
h.Write(signeddata)
return rsa.VerifyPKCS1v15(pubkey, hash, h.Sum(nil), sigbuf)
case ECDSAP256SHA256, ECDSAP384SHA384:
pubkey := k.publicKeyECDSA()
if pubkey == nil {
return ErrKey
}
var h hash.Hash
switch rr.Algorithm {
case ECDSAP256SHA256:
h = sha256.New()
case ECDSAP384SHA384:
h = sha512.New384()
}
io.WriteString(h, string(signeddata))
sighash := h.Sum(nil)
// Split sigbuf into the r and s coordinates
r := big.NewInt(0)
r.SetBytes(sigbuf[:len(sigbuf)/2])
s := big.NewInt(0)
s.SetBytes(sigbuf[len(sigbuf)/2:])
if ecdsa.Verify(pubkey, sighash, r, s) {
r := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2])
s := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:])
h := hash.New()
h.Write(signeddata)
if ecdsa.Verify(pubkey, h.Sum(nil), r, s) {
return nil
}
return ErrSig
default:
return ErrAlg
}
// Unknown alg
return ErrAlg
}
// ValidityPeriod uses RFC1982 serial arithmetic to calculate
@ -555,6 +608,12 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
// NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
// HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
// SRV, DNAME, A6
//
// RFC 6840 - Clarifications and Implementation Notes for DNS Security (DNSSEC):
// Section 6.2 of [RFC4034] also erroneously lists HINFO as a record
// that needs conversion to lowercase, and twice at that. Since HINFO
// records contain no domain names, they are not subject to case
// conversion.
switch x := r1.(type) {
case *NS:
x.Ns = strings.ToLower(x.Ns)
@ -603,36 +662,3 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
}
return buf, nil
}
// Map for algorithm names.
var AlgorithmToString = map[uint8]string{
RSAMD5: "RSAMD5",
DH: "DH",
DSA: "DSA",
RSASHA1: "RSASHA1",
DSANSEC3SHA1: "DSA-NSEC3-SHA1",
RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1",
RSASHA256: "RSASHA256",
RSASHA512: "RSASHA512",
ECCGOST: "ECC-GOST",
ECDSAP256SHA256: "ECDSAP256SHA256",
ECDSAP384SHA384: "ECDSAP384SHA384",
INDIRECT: "INDIRECT",
PRIVATEDNS: "PRIVATEDNS",
PRIVATEOID: "PRIVATEOID",
}
// Map of algorithm strings.
var StringToAlgorithm = reverseInt8(AlgorithmToString)
// Map for hash names.
var HashToString = map[uint8]string{
SHA1: "SHA1",
SHA256: "SHA256",
GOST94: "GOST94",
SHA384: "SHA384",
SHA512: "SHA512",
}
// Map of hash strings.
var StringToHash = reverseInt8(HashToString)

View File

@ -1,6 +1,7 @@
package dns
import (
"crypto"
"crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
@ -15,7 +16,7 @@ import (
// what kind of DNSKEY will be generated.
// The ECDSA algorithms imply a fixed keysize, in that case
// bits should be set to the size of the algorithm.
func (k *DNSKEY) Generate(bits int) (PrivateKey, error) {
func (k *DNSKEY) Generate(bits int) (crypto.PrivateKey, error) {
switch k.Algorithm {
case DSA, DSANSEC3SHA1:
if bits != 1024 {
@ -52,14 +53,14 @@ func (k *DNSKEY) Generate(bits int) (PrivateKey, error) {
return nil, err
}
k.setPublicKeyDSA(params.Q, params.P, params.G, priv.PublicKey.Y)
return (*DSAPrivateKey)(priv), nil
return priv, nil
case RSAMD5, RSASHA1, RSASHA256, RSASHA512, RSASHA1NSEC3SHA1:
priv, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, err
}
k.setPublicKeyRSA(priv.PublicKey.E, priv.PublicKey.N)
return (*RSAPrivateKey)(priv), nil
return priv, nil
case ECDSAP256SHA256, ECDSAP384SHA384:
var c elliptic.Curve
switch k.Algorithm {
@ -73,7 +74,7 @@ func (k *DNSKEY) Generate(bits int) (PrivateKey, error) {
return nil, err
}
k.setPublicKeyECDSA(priv.PublicKey.X, priv.PublicKey.Y)
return (*ECDSAPrivateKey)(priv), nil
return priv, nil
default:
return nil, ErrAlg
}

View File

@ -1,6 +1,7 @@
package dns
import (
"crypto"
"crypto/dsa"
"crypto/ecdsa"
"crypto/rsa"
@ -12,7 +13,7 @@ import (
// NewPrivateKey returns a PrivateKey by parsing the string s.
// s should be in the same form of the BIND private key files.
func (k *DNSKEY) NewPrivateKey(s string) (PrivateKey, error) {
func (k *DNSKEY) NewPrivateKey(s string) (crypto.PrivateKey, error) {
if s[len(s)-1] != '\n' { // We need a closing newline
return k.ReadPrivateKey(strings.NewReader(s+"\n"), "")
}
@ -23,7 +24,7 @@ func (k *DNSKEY) NewPrivateKey(s string) (PrivateKey, error) {
// only used in error reporting.
// The public key must be known, because some cryptographic algorithms embed
// the public inside the privatekey.
func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (PrivateKey, error) {
func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, error) {
m, e := parseKey(q, file)
if m == nil {
return nil, e
@ -50,7 +51,7 @@ func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (PrivateKey, error) {
return nil, ErrKey
}
priv.PublicKey = *pub
return (*DSAPrivateKey)(priv), e
return priv, e
case RSAMD5:
fallthrough
case RSASHA1:
@ -69,7 +70,7 @@ func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (PrivateKey, error) {
return nil, ErrKey
}
priv.PublicKey = *pub
return (*RSAPrivateKey)(priv), e
return priv, e
case ECCGOST:
return nil, ErrPrivKey
case ECDSAP256SHA256:
@ -84,7 +85,7 @@ func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (PrivateKey, error) {
return nil, ErrKey
}
priv.PublicKey = *pub
return (*ECDSAPrivateKey)(priv), e
return priv, e
default:
return nil, ErrPrivKey
}

View File

@ -4,7 +4,6 @@ import (
"crypto"
"crypto/dsa"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"math/big"
"strconv"
@ -12,133 +11,75 @@ import (
const format = "Private-key-format: v1.3\n"
// PrivateKey ... TODO(miek)
type PrivateKey interface {
Sign([]byte, uint8) ([]byte, error)
String(uint8) string
}
// PrivateKeyString converts a PrivateKey to a string. This string has the same
// format as the private-key-file of BIND9 (Private-key-format: v1.3).
// It needs some info from the key (the algorithm), so its a method of the
// DNSKEY and calls PrivateKey.String(alg).
func (r *DNSKEY) PrivateKeyString(p PrivateKey) string {
return p.String(r.Algorithm)
}
// It needs some info from the key (the algorithm), so its a method of the DNSKEY
// It supports rsa.PrivateKey, ecdsa.PrivateKey and dsa.PrivateKey
func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
algorithm := strconv.Itoa(int(r.Algorithm))
algorithm += " (" + AlgorithmToString[r.Algorithm] + ")"
type RSAPrivateKey rsa.PrivateKey
switch p := p.(type) {
case *rsa.PrivateKey:
modulus := toBase64(p.PublicKey.N.Bytes())
e := big.NewInt(int64(p.PublicKey.E))
publicExponent := toBase64(e.Bytes())
privateExponent := toBase64(p.D.Bytes())
prime1 := toBase64(p.Primes[0].Bytes())
prime2 := toBase64(p.Primes[1].Bytes())
// Calculate Exponent1/2 and Coefficient as per: http://en.wikipedia.org/wiki/RSA#Using_the_Chinese_remainder_algorithm
// and from: http://code.google.com/p/go/issues/detail?id=987
one := big.NewInt(1)
p1 := big.NewInt(0).Sub(p.Primes[0], one)
q1 := big.NewInt(0).Sub(p.Primes[1], one)
exp1 := big.NewInt(0).Mod(p.D, p1)
exp2 := big.NewInt(0).Mod(p.D, q1)
coeff := big.NewInt(0).ModInverse(p.Primes[1], p.Primes[0])
exponent1 := toBase64(exp1.Bytes())
exponent2 := toBase64(exp2.Bytes())
coefficient := toBase64(coeff.Bytes())
return format +
"Algorithm: " + algorithm + "\n" +
"Modulus: " + modulus + "\n" +
"PublicExponent: " + publicExponent + "\n" +
"PrivateExponent: " + privateExponent + "\n" +
"Prime1: " + prime1 + "\n" +
"Prime2: " + prime2 + "\n" +
"Exponent1: " + exponent1 + "\n" +
"Exponent2: " + exponent2 + "\n" +
"Coefficient: " + coefficient + "\n"
case *ecdsa.PrivateKey:
var intlen int
switch r.Algorithm {
case ECDSAP256SHA256:
intlen = 32
case ECDSAP384SHA384:
intlen = 48
}
private := toBase64(intToBytes(p.D, intlen))
return format +
"Algorithm: " + algorithm + "\n" +
"PrivateKey: " + private + "\n"
case *dsa.PrivateKey:
T := divRoundUp(divRoundUp(p.PublicKey.Parameters.G.BitLen(), 8)-64, 8)
prime := toBase64(intToBytes(p.PublicKey.Parameters.P, 64+T*8))
subprime := toBase64(intToBytes(p.PublicKey.Parameters.Q, 20))
base := toBase64(intToBytes(p.PublicKey.Parameters.G, 64+T*8))
priv := toBase64(intToBytes(p.X, 20))
pub := toBase64(intToBytes(p.PublicKey.Y, 64+T*8))
return format +
"Algorithm: " + algorithm + "\n" +
"Prime(p): " + prime + "\n" +
"Subprime(q): " + subprime + "\n" +
"Base(g): " + base + "\n" +
"Private_value(x): " + priv + "\n" +
"Public_value(y): " + pub + "\n"
func (p *RSAPrivateKey) Sign(hashed []byte, alg uint8) ([]byte, error) {
var hash crypto.Hash
switch alg {
case RSASHA1, RSASHA1NSEC3SHA1:
hash = crypto.SHA1
case RSASHA256:
hash = crypto.SHA256
case RSASHA512:
hash = crypto.SHA512
default:
return nil, ErrAlg
return ""
}
return rsa.SignPKCS1v15(nil, (*rsa.PrivateKey)(p), hash, hashed)
}
func (p *RSAPrivateKey) String(alg uint8) string {
algorithm := strconv.Itoa(int(alg)) + " (" + AlgorithmToString[alg] + ")"
modulus := toBase64(p.PublicKey.N.Bytes())
e := big.NewInt(int64(p.PublicKey.E))
publicExponent := toBase64(e.Bytes())
privateExponent := toBase64(p.D.Bytes())
prime1 := toBase64(p.Primes[0].Bytes())
prime2 := toBase64(p.Primes[1].Bytes())
// Calculate Exponent1/2 and Coefficient as per: http://en.wikipedia.org/wiki/RSA#Using_the_Chinese_remainder_algorithm
// and from: http://code.google.com/p/go/issues/detail?id=987
one := big.NewInt(1)
p1 := big.NewInt(0).Sub(p.Primes[0], one)
q1 := big.NewInt(0).Sub(p.Primes[1], one)
exp1 := big.NewInt(0).Mod(p.D, p1)
exp2 := big.NewInt(0).Mod(p.D, q1)
coeff := big.NewInt(0).ModInverse(p.Primes[1], p.Primes[0])
exponent1 := toBase64(exp1.Bytes())
exponent2 := toBase64(exp2.Bytes())
coefficient := toBase64(coeff.Bytes())
return format +
"Algorithm: " + algorithm + "\n" +
"Modulus: " + modulus + "\n" +
"PublicExponent: " + publicExponent + "\n" +
"PrivateExponent: " + privateExponent + "\n" +
"Prime1: " + prime1 + "\n" +
"Prime2: " + prime2 + "\n" +
"Exponent1: " + exponent1 + "\n" +
"Exponent2: " + exponent2 + "\n" +
"Coefficient: " + coefficient + "\n"
}
type ECDSAPrivateKey ecdsa.PrivateKey
func (p *ECDSAPrivateKey) Sign(hashed []byte, alg uint8) ([]byte, error) {
var intlen int
switch alg {
case ECDSAP256SHA256:
intlen = 32
case ECDSAP384SHA384:
intlen = 48
default:
return nil, ErrAlg
}
r1, s1, err := ecdsa.Sign(rand.Reader, (*ecdsa.PrivateKey)(p), hashed)
if err != nil {
return nil, err
}
signature := intToBytes(r1, intlen)
signature = append(signature, intToBytes(s1, intlen)...)
return signature, nil
}
func (p *ECDSAPrivateKey) String(alg uint8) string {
algorithm := strconv.Itoa(int(alg)) + " (" + AlgorithmToString[alg] + ")"
var intlen int
switch alg {
case ECDSAP256SHA256:
intlen = 32
case ECDSAP384SHA384:
intlen = 48
}
private := toBase64(intToBytes(p.D, intlen))
return format +
"Algorithm: " + algorithm + "\n" +
"PrivateKey: " + private + "\n"
}
type DSAPrivateKey dsa.PrivateKey
func (p *DSAPrivateKey) Sign(hashed []byte, alg uint8) ([]byte, error) {
r1, s1, err := dsa.Sign(rand.Reader, (*dsa.PrivateKey)(p), hashed)
if err != nil {
return nil, err
}
t := divRoundUp(divRoundUp(p.PublicKey.Y.BitLen(), 8)-64, 8)
signature := []byte{byte(t)}
signature = append(signature, intToBytes(r1, 20)...)
signature = append(signature, intToBytes(s1, 20)...)
return signature, nil
}
func (p *DSAPrivateKey) String(alg uint8) string {
algorithm := strconv.Itoa(int(alg)) + " (" + AlgorithmToString[alg] + ")"
T := divRoundUp(divRoundUp(p.PublicKey.Parameters.G.BitLen(), 8)-64, 8)
prime := toBase64(intToBytes(p.PublicKey.Parameters.P, 64+T*8))
subprime := toBase64(intToBytes(p.PublicKey.Parameters.Q, 20))
base := toBase64(intToBytes(p.PublicKey.Parameters.G, 64+T*8))
priv := toBase64(intToBytes(p.X, 20))
pub := toBase64(intToBytes(p.PublicKey.Y, 64+T*8))
return format +
"Algorithm: " + algorithm + "\n" +
"Prime(p): " + prime + "\n" +
"Subprime(q): " + subprime + "\n" +
"Base(g): " + base + "\n" +
"Private_value(x): " + priv + "\n" +
"Public_value(y): " + pub + "\n"
}

View File

@ -1,719 +0,0 @@
package dns
import (
"reflect"
"strings"
"testing"
"time"
)
func getKey() *DNSKEY {
key := new(DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
return key
}
func getSoa() *SOA {
soa := new(SOA)
soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
soa.Ns = "open.nlnetlabs.nl."
soa.Mbox = "miekg.atoom.net."
soa.Serial = 1293945905
soa.Refresh = 14400
soa.Retry = 3600
soa.Expire = 604800
soa.Minttl = 86400
return soa
}
func TestGenerateEC(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
key := new(DNSKEY)
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = ECDSAP256SHA256
privkey, _ := key.Generate(256)
t.Log(key.String())
t.Log(key.PrivateKeyString(privkey))
}
func TestGenerateDSA(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
key := new(DNSKEY)
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = DSA
privkey, _ := key.Generate(1024)
t.Log(key.String())
t.Log(key.PrivateKeyString(privkey))
}
func TestGenerateRSA(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
key := new(DNSKEY)
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
privkey, _ := key.Generate(1024)
t.Log(key.String())
t.Log(key.PrivateKeyString(privkey))
}
func TestSecure(t *testing.T) {
soa := getSoa()
sig := new(RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.TypeCovered = TypeSOA
sig.Algorithm = RSASHA256
sig.Labels = 2
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sig.OrigTtl = 14400
sig.KeyTag = 12051
sig.SignerName = "miek.nl."
sig.Signature = "oMCbslaAVIp/8kVtLSms3tDABpcPRUgHLrOR48OOplkYo+8TeEGWwkSwaz/MRo2fB4FxW0qj/hTlIjUGuACSd+b1wKdH5GvzRJc2pFmxtCbm55ygAh4EUL0F6U5cKtGJGSXxxg6UFCQ0doJCmiGFa78LolaUOXImJrk6AFrGa0M="
key := new(DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
// It should validate. Period is checked separately, so this will keep on working
if sig.Verify(key, []RR{soa}) != nil {
t.Error("failure to validate")
}
}
func TestSignature(t *testing.T) {
sig := new(RRSIG)
sig.Hdr.Name = "miek.nl."
sig.Hdr.Class = ClassINET
sig.Hdr.Ttl = 3600
sig.TypeCovered = TypeDNSKEY
sig.Algorithm = RSASHA1
sig.Labels = 2
sig.OrigTtl = 4000
sig.Expiration = 1000 //Thu Jan 1 02:06:40 CET 1970
sig.Inception = 800 //Thu Jan 1 01:13:20 CET 1970
sig.KeyTag = 34641
sig.SignerName = "miek.nl."
sig.Signature = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"
// Should not be valid
if sig.ValidityPeriod(time.Now()) {
t.Error("should not be valid")
}
sig.Inception = 315565800 //Tue Jan 1 10:10:00 CET 1980
sig.Expiration = 4102477800 //Fri Jan 1 10:10:00 CET 2100
if !sig.ValidityPeriod(time.Now()) {
t.Error("should be valid")
}
}
func TestSignVerify(t *testing.T) {
// The record we want to sign
soa := new(SOA)
soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
soa.Ns = "open.nlnetlabs.nl."
soa.Mbox = "miekg.atoom.net."
soa.Serial = 1293945905
soa.Refresh = 14400
soa.Retry = 3600
soa.Expire = 604800
soa.Minttl = 86400
soa1 := new(SOA)
soa1.Hdr = RR_Header{"*.miek.nl.", TypeSOA, ClassINET, 14400, 0}
soa1.Ns = "open.nlnetlabs.nl."
soa1.Mbox = "miekg.atoom.net."
soa1.Serial = 1293945905
soa1.Refresh = 14400
soa1.Retry = 3600
soa1.Expire = 604800
soa1.Minttl = 86400
srv := new(SRV)
srv.Hdr = RR_Header{"srv.miek.nl.", TypeSRV, ClassINET, 14400, 0}
srv.Port = 1000
srv.Weight = 800
srv.Target = "web1.miek.nl."
// With this key
key := new(DNSKEY)
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
privkey, _ := key.Generate(512)
// Fill in the values of the Sig, before signing
sig := new(RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.TypeCovered = soa.Hdr.Rrtype
sig.Labels = uint8(CountLabel(soa.Hdr.Name)) // works for all 3
sig.OrigTtl = soa.Hdr.Ttl
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
sig.SignerName = key.Hdr.Name
sig.Algorithm = RSASHA256
for _, r := range []RR{soa, soa1, srv} {
if sig.Sign(privkey, []RR{r}) != nil {
t.Error("failure to sign the record")
continue
}
if sig.Verify(key, []RR{r}) != nil {
t.Error("failure to validate")
continue
}
t.Logf("validated: %s", r.Header().Name)
}
}
func Test65534(t *testing.T) {
t6 := new(RFC3597)
t6.Hdr = RR_Header{"miek.nl.", 65534, ClassINET, 14400, 0}
t6.Rdata = "505D870001"
key := new(DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
privkey, _ := key.Generate(1024)
sig := new(RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.TypeCovered = t6.Hdr.Rrtype
sig.Labels = uint8(CountLabel(t6.Hdr.Name))
sig.OrigTtl = t6.Hdr.Ttl
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sig.KeyTag = key.KeyTag()
sig.SignerName = key.Hdr.Name
sig.Algorithm = RSASHA256
if err := sig.Sign(privkey, []RR{t6}); err != nil {
t.Error(err)
t.Error("failure to sign the TYPE65534 record")
}
if err := sig.Verify(key, []RR{t6}); err != nil {
t.Error(err)
t.Error("failure to validate")
} else {
t.Logf("validated: %s", t6.Header().Name)
}
}
func TestDnskey(t *testing.T) {
pubkey, err := ReadRR(strings.NewReader(`
miek.nl. IN DNSKEY 256 3 10 AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL ;{id = 5240 (zsk), size = 1024b}
`), "Kmiek.nl.+010+05240.key")
if err != nil {
t.Fatal(err)
}
privStr := `Private-key-format: v1.3
Algorithm: 10 (RSASHA512)
Modulus: m4wK7YV26AeROtdiCXmqLG9wPDVoMOW8vjr/EkpscEAdjXp81RvZvrlzCSjYmz9onFRgltmTl3AINnFh+t9tlW0M9C5zejxBoKFXELv8ljPYAdz2oe+pDWPhWsfvVFYg2VCjpViPM38EakyE5mhk4TDOnUd+w4TeU1hyhZTWyYs=
PublicExponent: AQAB
PrivateExponent: UfCoIQ/Z38l8vB6SSqOI/feGjHEl/fxIPX4euKf0D/32k30fHbSaNFrFOuIFmWMB3LimWVEs6u3dpbB9CQeCVg7hwU5puG7OtuiZJgDAhNeOnxvo5btp4XzPZrJSxR4WNQnwIiYWbl0aFlL1VGgHC/3By89ENZyWaZcMLW4KGWE=
Prime1: yxwC6ogAu8aVcDx2wg1V0b5M5P6jP8qkRFVMxWNTw60Vkn+ECvw6YAZZBHZPaMyRYZLzPgUlyYRd0cjupy4+fQ==
Prime2: xA1bF8M0RTIQ6+A11AoVG6GIR/aPGg5sogRkIZ7ID/sF6g9HMVU/CM2TqVEBJLRPp73cv6ZeC3bcqOCqZhz+pw==
Exponent1: xzkblyZ96bGYxTVZm2/vHMOXswod4KWIyMoOepK6B/ZPcZoIT6omLCgtypWtwHLfqyCz3MK51Nc0G2EGzg8rFQ==
Exponent2: Pu5+mCEb7T5F+kFNZhQadHUklt0JUHbi3hsEvVoHpEGSw3BGDQrtIflDde0/rbWHgDPM4WQY+hscd8UuTXrvLw==
Coefficient: UuRoNqe7YHnKmQzE6iDWKTMIWTuoqqrFAmXPmKQnC+Y+BQzOVEHUo9bXdDnoI9hzXP1gf8zENMYwYLeWpuYlFQ==
`
privkey, err := pubkey.(*DNSKEY).ReadPrivateKey(strings.NewReader(privStr),
"Kmiek.nl.+010+05240.private")
if err != nil {
t.Fatal(err)
}
if pubkey.(*DNSKEY).PublicKey != "AwEAAZuMCu2FdugHkTrXYgl5qixvcDw1aDDlvL46/xJKbHBAHY16fNUb2b65cwko2Js/aJxUYJbZk5dwCDZxYfrfbZVtDPQuc3o8QaChVxC7/JYz2AHc9qHvqQ1j4VrH71RWINlQo6VYjzN/BGpMhOZoZOEwzp1HfsOE3lNYcoWU1smL" {
t.Error("pubkey is not what we've read")
}
if pubkey.(*DNSKEY).PrivateKeyString(privkey) != privStr {
t.Error("privkey is not what we've read")
t.Errorf("%v", pubkey.(*DNSKEY).PrivateKeyString(privkey))
}
}
func TestTag(t *testing.T) {
key := new(DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 3600
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
tag := key.KeyTag()
if tag != 12051 {
t.Errorf("wrong key tag: %d for key %v", tag, key)
}
}
func TestKeyRSA(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
key := new(DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 3600
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
priv, _ := key.Generate(2048)
soa := new(SOA)
soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
soa.Ns = "open.nlnetlabs.nl."
soa.Mbox = "miekg.atoom.net."
soa.Serial = 1293945905
soa.Refresh = 14400
soa.Retry = 3600
soa.Expire = 604800
soa.Minttl = 86400
sig := new(RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.TypeCovered = TypeSOA
sig.Algorithm = RSASHA256
sig.Labels = 2
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sig.OrigTtl = soa.Hdr.Ttl
sig.KeyTag = key.KeyTag()
sig.SignerName = key.Hdr.Name
if err := sig.Sign(priv, []RR{soa}); err != nil {
t.Error("failed to sign")
return
}
if err := sig.Verify(key, []RR{soa}); err != nil {
t.Error("failed to verify")
}
}
func TestKeyToDS(t *testing.T) {
key := new(DNSKEY)
key.Hdr.Name = "miek.nl."
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 3600
key.Flags = 256
key.Protocol = 3
key.Algorithm = RSASHA256
key.PublicKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz"
ds := key.ToDS(SHA1)
if strings.ToUpper(ds.Digest) != "B5121BDB5B8D86D0CC5FFAFBAAABE26C3E20BAC1" {
t.Errorf("wrong DS digest for SHA1\n%v", ds)
}
}
func TestSignRSA(t *testing.T) {
pub := "miek.nl. IN DNSKEY 256 3 5 AwEAAb+8lGNCxJgLS8rYVer6EnHVuIkQDghdjdtewDzU3G5R7PbMbKVRvH2Ma7pQyYceoaqWZQirSj72euPWfPxQnMy9ucCylA+FuH9cSjIcPf4PqJfdupHk9X6EBYjxrCLY4p1/yBwgyBIRJtZtAqM3ceAH2WovEJD6rTtOuHo5AluJ"
priv := `Private-key-format: v1.3
Algorithm: 5 (RSASHA1)
Modulus: v7yUY0LEmAtLythV6voScdW4iRAOCF2N217APNTcblHs9sxspVG8fYxrulDJhx6hqpZlCKtKPvZ649Z8/FCczL25wLKUD4W4f1xKMhw9/g+ol926keT1foQFiPGsItjinX/IHCDIEhEm1m0Cozdx4AfZai8QkPqtO064ejkCW4k=
PublicExponent: AQAB
PrivateExponent: YPwEmwjk5HuiROKU4xzHQ6l1hG8Iiha4cKRG3P5W2b66/EN/GUh07ZSf0UiYB67o257jUDVEgwCuPJz776zfApcCB4oGV+YDyEu7Hp/rL8KcSN0la0k2r9scKwxTp4BTJT23zyBFXsV/1wRDK1A5NxsHPDMYi2SoK63Enm/1ptk=
Prime1: /wjOG+fD0ybNoSRn7nQ79udGeR1b0YhUA5mNjDx/x2fxtIXzygYk0Rhx9QFfDy6LOBvz92gbNQlzCLz3DJt5hw==
Prime2: wHZsJ8OGhkp5p3mrJFZXMDc2mbYusDVTA+t+iRPdS797Tj0pjvU2HN4vTnTj8KBQp6hmnY7dLp9Y1qserySGbw==
Exponent1: N0A7FsSRIg+IAN8YPQqlawoTtG1t1OkJ+nWrurPootScApX6iMvn8fyvw3p2k51rv84efnzpWAYiC8SUaQDNxQ==
Exponent2: SvuYRaGyvo0zemE3oS+WRm2scxR8eiA8WJGeOc+obwOKCcBgeZblXzfdHGcEC1KaOcetOwNW/vwMA46lpLzJNw==
Coefficient: 8+7ZN/JgByqv0NfULiFKTjtyegUcijRuyij7yNxYbCBneDvZGxJwKNi4YYXWx743pcAj4Oi4Oh86gcmxLs+hGw==
Created: 20110302104537
Publish: 20110302104537
Activate: 20110302104537`
xk, _ := NewRR(pub)
k := xk.(*DNSKEY)
p, err := k.NewPrivateKey(priv)
if err != nil {
t.Error(err)
}
switch priv := p.(type) {
case *RSAPrivateKey:
if 65537 != priv.PublicKey.E {
t.Error("exponenent should be 65537")
}
default:
t.Errorf("we should have read an RSA key: %v", priv)
}
if k.KeyTag() != 37350 {
t.Errorf("keytag should be 37350, got %d %v", k.KeyTag(), k)
}
soa := new(SOA)
soa.Hdr = RR_Header{"miek.nl.", TypeSOA, ClassINET, 14400, 0}
soa.Ns = "open.nlnetlabs.nl."
soa.Mbox = "miekg.atoom.net."
soa.Serial = 1293945905
soa.Refresh = 14400
soa.Retry = 3600
soa.Expire = 604800
soa.Minttl = 86400
sig := new(RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sig.KeyTag = k.KeyTag()
sig.SignerName = k.Hdr.Name
sig.Algorithm = k.Algorithm
sig.Sign(p, []RR{soa})
if sig.Signature != "D5zsobpQcmMmYsUMLxCVEtgAdCvTu8V/IEeP4EyLBjqPJmjt96bwM9kqihsccofA5LIJ7DN91qkCORjWSTwNhzCv7bMyr2o5vBZElrlpnRzlvsFIoAZCD9xg6ZY7ZyzUJmU6IcTwG4v3xEYajcpbJJiyaw/RqR90MuRdKPiBzSo=" {
t.Errorf("signature is not correct: %v", sig)
}
}
func TestSignVerifyECDSA(t *testing.T) {
pub := `example.net. 3600 IN DNSKEY 257 3 14 (
xKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1
w/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8
/uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )`
priv := `Private-key-format: v1.2
Algorithm: 14 (ECDSAP384SHA384)
PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
eckey, err := NewRR(pub)
if err != nil {
t.Fatal(err)
}
privkey, err := eckey.(*DNSKEY).NewPrivateKey(priv)
if err != nil {
t.Fatal(err)
}
// TODO: Create separate test for this
ds := eckey.(*DNSKEY).ToDS(SHA384)
if ds.KeyTag != 10771 {
t.Fatal("wrong keytag on DS")
}
if ds.Digest != "72d7b62976ce06438e9c0bf319013cf801f09ecc84b8d7e9495f27e305c6a9b0563a9b5f4d288405c3008a946df983d6" {
t.Fatal("wrong DS Digest")
}
a, _ := NewRR("www.example.net. 3600 IN A 192.0.2.1")
sig := new(RRSIG)
sig.Hdr = RR_Header{"example.net.", TypeRRSIG, ClassINET, 14400, 0}
sig.Expiration, _ = StringToTime("20100909102025")
sig.Inception, _ = StringToTime("20100812102025")
sig.KeyTag = eckey.(*DNSKEY).KeyTag()
sig.SignerName = eckey.(*DNSKEY).Hdr.Name
sig.Algorithm = eckey.(*DNSKEY).Algorithm
if sig.Sign(privkey, []RR{a}) != nil {
t.Fatal("failure to sign the record")
}
if err := sig.Verify(eckey.(*DNSKEY), []RR{a}); err != nil {
t.Fatalf("Failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
eckey.(*DNSKEY).String(),
a.String(),
sig.String(),
eckey.(*DNSKEY).PrivateKeyString(privkey),
err,
)
}
}
func TestSignVerifyECDSA2(t *testing.T) {
srv1, err := NewRR("srv.miek.nl. IN SRV 1000 800 0 web1.miek.nl.")
if err != nil {
t.Fatal(err)
}
srv := srv1.(*SRV)
// With this key
key := new(DNSKEY)
key.Hdr.Rrtype = TypeDNSKEY
key.Hdr.Name = "miek.nl."
key.Hdr.Class = ClassINET
key.Hdr.Ttl = 14400
key.Flags = 256
key.Protocol = 3
key.Algorithm = ECDSAP256SHA256
privkey, err := key.Generate(256)
if err != nil {
t.Fatal("failure to generate key")
}
// Fill in the values of the Sig, before signing
sig := new(RRSIG)
sig.Hdr = RR_Header{"miek.nl.", TypeRRSIG, ClassINET, 14400, 0}
sig.TypeCovered = srv.Hdr.Rrtype
sig.Labels = uint8(CountLabel(srv.Hdr.Name)) // works for all 3
sig.OrigTtl = srv.Hdr.Ttl
sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05"
sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05"
sig.KeyTag = key.KeyTag() // Get the keyfrom the Key
sig.SignerName = key.Hdr.Name
sig.Algorithm = ECDSAP256SHA256
if sig.Sign(privkey, []RR{srv}) != nil {
t.Fatal("failure to sign the record")
}
err = sig.Verify(key, []RR{srv})
if err != nil {
t.Logf("Failure to validate:\n%s\n%s\n%s\n\n%s\n\n%v",
key.String(),
srv.String(),
sig.String(),
key.PrivateKeyString(privkey),
err,
)
}
}
// Here the test vectors from the relevant RFCs are checked.
// rfc6605 6.1
func TestRFC6605P256(t *testing.T) {
exDNSKEY := `example.net. 3600 IN DNSKEY 257 3 13 (
GojIhhXUN/u4v54ZQqGSnyhWJwaubCvTmeexv7bR6edb
krSqQpF64cYbcB7wNcP+e+MAnLr+Wi9xMWyQLc8NAA== )`
exPriv := `Private-key-format: v1.2
Algorithm: 13 (ECDSAP256SHA256)
PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=`
rrDNSKEY, err := NewRR(exDNSKEY)
if err != nil {
t.Fatal(err)
}
priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
if err != nil {
t.Fatal(err)
}
exDS := `example.net. 3600 IN DS 55648 13 2 (
b4c8c1fe2e7477127b27115656ad6256f424625bf5c1
e2770ce6d6e37df61d17 )`
rrDS, err := NewRR(exDS)
if err != nil {
t.Fatal(err)
}
ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA256)
if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
t.Errorf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
}
exA := `www.example.net. 3600 IN A 192.0.2.1`
exRRSIG := `www.example.net. 3600 IN RRSIG A 13 3 3600 (
20100909100439 20100812100439 55648 example.net.
qx6wLYqmh+l9oCKTN6qIc+bw6ya+KJ8oMz0YP107epXA
yGmt+3SNruPFKG7tZoLBLlUzGGus7ZwmwWep666VCw== )`
rrA, err := NewRR(exA)
if err != nil {
t.Fatal(err)
}
rrRRSIG, err := NewRR(exRRSIG)
if err != nil {
t.Fatal(err)
}
if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
t.Errorf("Failure to validate the spec RRSIG: %v", err)
}
ourRRSIG := &RRSIG{
Hdr: RR_Header{
Ttl: rrA.Header().Ttl,
},
KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
}
ourRRSIG.Expiration, _ = StringToTime("20100909100439")
ourRRSIG.Inception, _ = StringToTime("20100812100439")
err = ourRRSIG.Sign(priv, []RR{rrA})
if err != nil {
t.Fatal(err)
}
if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
t.Errorf("Failure to validate our RRSIG: %v", err)
}
// Signatures are randomized
rrRRSIG.(*RRSIG).Signature = ""
ourRRSIG.Signature = ""
if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
}
}
// rfc6605 6.2
func TestRFC6605P384(t *testing.T) {
exDNSKEY := `example.net. 3600 IN DNSKEY 257 3 14 (
xKYaNhWdGOfJ+nPrL8/arkwf2EY3MDJ+SErKivBVSum1
w/egsXvSADtNJhyem5RCOpgQ6K8X1DRSEkrbYQ+OB+v8
/uX45NBwY8rp65F6Glur8I/mlVNgF6W/qTI37m40 )`
exPriv := `Private-key-format: v1.2
Algorithm: 14 (ECDSAP384SHA384)
PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
rrDNSKEY, err := NewRR(exDNSKEY)
if err != nil {
t.Fatal(err)
}
priv, err := rrDNSKEY.(*DNSKEY).NewPrivateKey(exPriv)
if err != nil {
t.Fatal(err)
}
exDS := `example.net. 3600 IN DS 10771 14 4 (
72d7b62976ce06438e9c0bf319013cf801f09ecc84b8
d7e9495f27e305c6a9b0563a9b5f4d288405c3008a94
6df983d6 )`
rrDS, err := NewRR(exDS)
if err != nil {
t.Fatal(err)
}
ourDS := rrDNSKEY.(*DNSKEY).ToDS(SHA384)
if !reflect.DeepEqual(ourDS, rrDS.(*DS)) {
t.Fatalf("DS record differs:\n%v\n%v", ourDS, rrDS.(*DS))
}
exA := `www.example.net. 3600 IN A 192.0.2.1`
exRRSIG := `www.example.net. 3600 IN RRSIG A 14 3 3600 (
20100909102025 20100812102025 10771 example.net.
/L5hDKIvGDyI1fcARX3z65qrmPsVz73QD1Mr5CEqOiLP
95hxQouuroGCeZOvzFaxsT8Glr74hbavRKayJNuydCuz
WTSSPdz7wnqXL5bdcJzusdnI0RSMROxxwGipWcJm )`
rrA, err := NewRR(exA)
if err != nil {
t.Fatal(err)
}
rrRRSIG, err := NewRR(exRRSIG)
if err != nil {
t.Fatal(err)
}
if err = rrRRSIG.(*RRSIG).Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
t.Errorf("Failure to validate the spec RRSIG: %v", err)
}
ourRRSIG := &RRSIG{
Hdr: RR_Header{
Ttl: rrA.Header().Ttl,
},
KeyTag: rrDNSKEY.(*DNSKEY).KeyTag(),
SignerName: rrDNSKEY.(*DNSKEY).Hdr.Name,
Algorithm: rrDNSKEY.(*DNSKEY).Algorithm,
}
ourRRSIG.Expiration, _ = StringToTime("20100909102025")
ourRRSIG.Inception, _ = StringToTime("20100812102025")
err = ourRRSIG.Sign(priv, []RR{rrA})
if err != nil {
t.Fatal(err)
}
if err = ourRRSIG.Verify(rrDNSKEY.(*DNSKEY), []RR{rrA}); err != nil {
t.Errorf("Failure to validate our RRSIG: %v", err)
}
// Signatures are randomized
rrRRSIG.(*RRSIG).Signature = ""
ourRRSIG.Signature = ""
if !reflect.DeepEqual(ourRRSIG, rrRRSIG.(*RRSIG)) {
t.Fatalf("RRSIG record differs:\n%v\n%v", ourRRSIG, rrRRSIG.(*RRSIG))
}
}
func TestInvalidRRSet(t *testing.T) {
goodRecords := make([]RR, 2)
goodRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
goodRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
// Generate key
keyname := "cloudflare.com."
key := &DNSKEY{
Hdr: RR_Header{Name: keyname, Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 0},
Algorithm: ECDSAP256SHA256,
Flags: ZONE,
Protocol: 3,
}
privatekey, err := key.Generate(256)
if err != nil {
t.Fatal(err.Error())
}
// Need to fill in: Inception, Expiration, KeyTag, SignerName and Algorithm
curTime := time.Now()
signature := &RRSIG{
Inception: uint32(curTime.Unix()),
Expiration: uint32(curTime.Add(time.Hour).Unix()),
KeyTag: key.KeyTag(),
SignerName: keyname,
Algorithm: ECDSAP256SHA256,
}
// Inconsistent name between records
badRecords := make([]RR, 2)
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
badRecords[1] = &TXT{Hdr: RR_Header{Name: "nama.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"_o/"}}
if IsRRset(badRecords) {
t.Fatal("Record set with inconsistent names considered valid")
}
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
badRecords[1] = &A{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeA, Class: ClassINET, Ttl: 0}}
if IsRRset(badRecords) {
t.Fatal("Record set with inconsistent record types considered valid")
}
badRecords[0] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
badRecords[1] = &TXT{Hdr: RR_Header{Name: "name.cloudflare.com.", Rrtype: TypeTXT, Class: ClassCHAOS, Ttl: 0}, Txt: []string{"_o/"}}
if IsRRset(badRecords) {
t.Fatal("Record set with inconsistent record class considered valid")
}
// Sign the good record set and then make sure verification fails on the bad record set
if err := signature.Sign(privatekey, goodRecords); err != nil {
t.Fatal("Signing good records failed")
}
if err := signature.Verify(key, badRecords); err != ErrRRset {
t.Fatal("Verification did not return ErrRRset with inconsistent records")
}
}

View File

@ -13,7 +13,8 @@ Resource records are native types. They are not stored in wire format.
Basic usage pattern for creating a new resource record:
r := new(dns.MX)
r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600}
r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX,
Class: dns.ClassINET, Ttl: 3600}
r.Preference = 10
r.Mx = "mx.miek.nl."
@ -57,8 +58,8 @@ server configured on 127.0.0.1 and port 53:
c := new(dns.Client)
in, rtt, err := c.Exchange(m1, "127.0.0.1:53")
Suppressing
multiple outstanding queries (with the same question, type and class) is as easy as setting:
Suppressing multiple outstanding queries (with the same question, type and
class) is as easy as setting:
c.SingleInflight = true
@ -118,7 +119,7 @@ certain resource records or names in a zone to specify if resource records
should be added or removed. The table from RFC 2136 supplemented with the Go
DNS function shows which functions exist to specify the prerequisites.
3.2.4 - Table Of Metavalues Used In Prerequisite Section
3.2.4 - Table Of Metavalues Used In Prerequisite Section
CLASS TYPE RDATA Meaning Function
--------------------------------------------------------------
@ -133,7 +134,7 @@ If you have decided on the prerequisites you can tell what RRs should
be added or deleted. The next table shows the options you have and
what functions to call.
3.4.2.6 - Table Of Metavalues Used In Update Section
3.4.2.6 - Table Of Metavalues Used In Update Section
CLASS TYPE RDATA Meaning Function
---------------------------------------------------------------
@ -183,7 +184,7 @@ Basic use pattern validating and replying to a message that has TSIG set.
dns.HandleFunc(".", handleRequest)
func handleRequest(w dns.ResponseWriter, r *dns.Msg) {
m := new(Msg)
m := new(dns.Msg)
m.SetReply(r)
if r.IsTsig() {
if w.TsigStatus() == nil {

View File

@ -1,3 +0,0 @@
package dns
// Find better solution

View File

@ -30,10 +30,6 @@ type OPT struct {
Option []EDNS0 `dns:"opt"`
}
func (rr *OPT) Header() *RR_Header {
return &rr.Hdr
}
func (rr *OPT) String() string {
s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; "
if rr.Do() {
@ -87,10 +83,6 @@ func (rr *OPT) len() int {
return l
}
func (rr *OPT) copy() RR {
return &OPT{*rr.Hdr.copyHeader(), rr.Option}
}
// return the old value -> delete SetVersion?
// Version returns the EDNS version used. Only zero is defined.
@ -176,7 +168,7 @@ func (e *EDNS0_NSID) Option() uint16 { return EDNS0NSID }
func (e *EDNS0_NSID) unpack(b []byte) error { e.Nsid = hex.EncodeToString(b); return nil }
func (e *EDNS0_NSID) String() string { return string(e.Nsid) }
// The subnet EDNS0 option is used to give the remote nameserver
// EDNS0_SUBNET is the subnet option that is used to give the remote nameserver
// an idea of where the client lives. It can then give back a different
// answer depending on the location or network topology.
// Basic use pattern for creating an subnet option:
@ -291,7 +283,7 @@ func (e *EDNS0_SUBNET) String() (s string) {
return
}
// The UL (Update Lease) EDNS0 (draft RFC) option is used to tell the server to set
// The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set
// an expiration on an update RR. This is helpful for clients that cannot clean
// up after themselves. This is a draft RFC and more information can be found at
// http://files.dns-sd.org/draft-sekar-dns-ul.txt
@ -329,7 +321,7 @@ func (e *EDNS0_UL) unpack(b []byte) error {
return nil
}
// Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
// EDNS0_LLQ stands for Long Lived Queries: http://tools.ietf.org/html/draft-sekar-dns-llq-01
// Implemented for completeness, as the EDNS0 type code is assigned.
type EDNS0_LLQ struct {
Code uint16 // Always EDNS0LLQ
@ -471,7 +463,7 @@ func (e *EDNS0_EXPIRE) unpack(b []byte) error {
return nil
}
// The local EDNS0 option is used for local/experimental purposes. The option
// The EDNS0_LOCAL option is used for local/experimental purposes. The option
// code is recommended to be within the range [EDNS0LOCALSTART, EDNS0LOCALEND]
// (RFC6891), although any unassigned code can actually be used. The content of
// the option is made available in Data, unaltered.

View File

@ -1,48 +0,0 @@
package dns
import "testing"
func TestOPTTtl(t *testing.T) {
e := &OPT{}
e.Hdr.Name = "."
e.Hdr.Rrtype = TypeOPT
if e.Do() {
t.Fail()
}
e.SetDo()
if !e.Do() {
t.Fail()
}
oldTtl := e.Hdr.Ttl
if e.Version() != 0 {
t.Fail()
}
e.SetVersion(42)
if e.Version() != 42 {
t.Fail()
}
e.SetVersion(0)
if e.Hdr.Ttl != oldTtl {
t.Fail()
}
if e.ExtendedRcode() != 0 {
t.Fail()
}
e.SetExtendedRcode(42)
if e.ExtendedRcode() != 42 {
t.Fail()
}
e.SetExtendedRcode(0)
if e.Hdr.Ttl != oldTtl {
t.Fail()
}
}

View File

@ -1,147 +0,0 @@
package dns_test
import (
"errors"
"fmt"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns"
"log"
"net"
)
// Retrieve the MX records for miek.nl.
func ExampleMX() {
config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
c := new(dns.Client)
m := new(dns.Msg)
m.SetQuestion("miek.nl.", dns.TypeMX)
m.RecursionDesired = true
r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
if err != nil {
return
}
if r.Rcode != dns.RcodeSuccess {
return
}
for _, a := range r.Answer {
if mx, ok := a.(*dns.MX); ok {
fmt.Printf("%s\n", mx.String())
}
}
}
// Retrieve the DNSKEY records of a zone and convert them
// to DS records for SHA1, SHA256 and SHA384.
func ExampleDS(zone string) {
config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
c := new(dns.Client)
m := new(dns.Msg)
if zone == "" {
zone = "miek.nl"
}
m.SetQuestion(dns.Fqdn(zone), dns.TypeDNSKEY)
m.SetEdns0(4096, true)
r, _, err := c.Exchange(m, config.Servers[0]+":"+config.Port)
if err != nil {
return
}
if r.Rcode != dns.RcodeSuccess {
return
}
for _, k := range r.Answer {
if key, ok := k.(*dns.DNSKEY); ok {
for _, alg := range []uint8{dns.SHA1, dns.SHA256, dns.SHA384} {
fmt.Printf("%s; %d\n", key.ToDS(alg).String(), key.Flags)
}
}
}
}
const TypeAPAIR = 0x0F99
type APAIR struct {
addr [2]net.IP
}
func NewAPAIR() dns.PrivateRdata { return new(APAIR) }
func (rd *APAIR) String() string { return rd.addr[0].String() + " " + rd.addr[1].String() }
func (rd *APAIR) Parse(txt []string) error {
if len(txt) != 2 {
return errors.New("two addresses required for APAIR")
}
for i, s := range txt {
ip := net.ParseIP(s)
if ip == nil {
return errors.New("invalid IP in APAIR text representation")
}
rd.addr[i] = ip
}
return nil
}
func (rd *APAIR) Pack(buf []byte) (int, error) {
b := append([]byte(rd.addr[0]), []byte(rd.addr[1])...)
n := copy(buf, b)
if n != len(b) {
return n, dns.ErrBuf
}
return n, nil
}
func (rd *APAIR) Unpack(buf []byte) (int, error) {
ln := net.IPv4len * 2
if len(buf) != ln {
return 0, errors.New("invalid length of APAIR rdata")
}
cp := make([]byte, ln)
copy(cp, buf) // clone bytes to use them in IPs
rd.addr[0] = net.IP(cp[:3])
rd.addr[1] = net.IP(cp[4:])
return len(buf), nil
}
func (rd *APAIR) Copy(dest dns.PrivateRdata) error {
cp := make([]byte, rd.Len())
_, err := rd.Pack(cp)
if err != nil {
return err
}
d := dest.(*APAIR)
d.addr[0] = net.IP(cp[:3])
d.addr[1] = net.IP(cp[4:])
return nil
}
func (rd *APAIR) Len() int {
return net.IPv4len * 2
}
func ExamplePrivateHandle() {
dns.PrivateHandle("APAIR", TypeAPAIR, NewAPAIR)
defer dns.PrivateHandleRemove(TypeAPAIR)
rr, err := dns.NewRR("miek.nl. APAIR (1.2.3.4 1.2.3.5)")
if err != nil {
log.Fatal("could not parse APAIR record: ", err)
}
fmt.Println(rr)
// Output: miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5
m := new(dns.Msg)
m.Id = 12345
m.SetQuestion("miek.nl.", TypeAPAIR)
m.Answer = append(m.Answer, rr)
fmt.Println(m)
// ;; opcode: QUERY, status: NOERROR, id: 12345
// ;; flags: rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
//
// ;; QUESTION SECTION:
// ;miek.nl. IN APAIR
//
// ;; ANSWER SECTION:
// miek.nl. 3600 IN APAIR 1.2.3.4 1.2.3.5
}

View File

@ -1,25 +0,0 @@
package dns
import "testing"
func TestFuzzString(t *testing.T) {
testcases := []string{"", " MINFO ", " RP ", " NSEC 0 0", " \" NSEC 0 0\"", " \" MINFO \"",
";a ", ";a<><61><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>",
" NSAP O ", " NSAP N ",
" TYPE4 TYPE6a789a3bc0045c8a5fb42c7d1bd998f5444 IN 9579b47d46817afbd17273e6",
" TYPE45 3 3 4147994 TYPE\\(\\)\\)\\(\\)\\(\\(\\)\\(\\)\\)\\)\\(\\)\\(\\)\\(\\(\\R 948\"\")\\(\\)\\)\\)\\(\\ ",
"$GENERATE 0-3 ${441189,5039418474430,o}",
"$INCLUDE 00 TYPE00000000000n ",
"$INCLUDE PE4 TYPE061463623/727071511 \\(\\)\\$GENERATE 6-462/0",
}
for i, tc := range testcases {
rr, err := NewRR(tc)
if err == nil {
// rr can be nil because we can (for instance) just parse a comment
if rr == nil {
continue
}
t.Fatalf("parsed mailformed RR %d: %s", i, rr.String())
}
}
}

View File

@ -1,18 +0,0 @@
package idn_test
import (
"fmt"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns/idn"
)
func ExampleToPunycode() {
name := "インターネット.テスト"
fmt.Printf("%s -> %s", name, idn.ToPunycode(name))
// Output: インターネット.テスト -> xn--eckucmux0ukc.xn--zckzah
}
func ExampleFromPunycode() {
name := "xn--mgbaja8a1hpac.xn--mgbachtv"
fmt.Printf("%s -> %s", name, idn.FromPunycode(name))
// Output: xn--mgbaja8a1hpac.xn--mgbachtv -> الانترنت.اختبار
}

View File

@ -199,7 +199,6 @@ func needFromPunycode(s string) bool {
return true
}
}
panic("dns: not reached")
}
// encode transforms Unicode input bytes (that represent DNS label) into

View File

@ -1,116 +0,0 @@
package idn
import (
"strings"
"testing"
)
var testcases = [][2]string{
{"", ""},
{"a", "a"},
{"a-b", "a-b"},
{"a-b-c", "a-b-c"},
{"abc", "abc"},
{"я", "xn--41a"},
{"zя", "xn--z-0ub"},
{"яZ", "xn--z-zub"},
{"а-я", "xn----7sb8g"},
{"إختبار", "xn--kgbechtv"},
{"آزمایشی", "xn--hgbk6aj7f53bba"},
{"测试", "xn--0zwm56d"},
{"測試", "xn--g6w251d"},
{"испытание", "xn--80akhbyknj4f"},
{"परीक्षा", "xn--11b5bs3a9aj6g"},
{"δοκιμή", "xn--jxalpdlp"},
{"테스트", "xn--9t4b11yi5a"},
{"טעסט", "xn--deba0ad"},
{"テスト", "xn--zckzah"},
{"பரிட்சை", "xn--hlcj6aya9esc7a"},
{"mamão-com-açúcar", "xn--mamo-com-acar-yeb1e6q"},
{"σ", "xn--4xa"},
}
func TestEncodeDecodePunycode(t *testing.T) {
for _, tst := range testcases {
enc := encode([]byte(tst[0]))
if string(enc) != tst[1] {
t.Errorf("%s encodeded as %s but should be %s", tst[0], enc, tst[1])
}
dec := decode([]byte(tst[1]))
if string(dec) != strings.ToLower(tst[0]) {
t.Errorf("%s decoded as %s but should be %s", tst[1], dec, strings.ToLower(tst[0]))
}
}
}
func TestToFromPunycode(t *testing.T) {
for _, tst := range testcases {
// assert unicode.com == punycode.com
full := ToPunycode(tst[0] + ".com")
if full != tst[1]+".com" {
t.Errorf("invalid result from string conversion to punycode, %s and should be %s.com", full, tst[1])
}
// assert punycode.punycode == unicode.unicode
decoded := FromPunycode(tst[1] + "." + tst[1])
if decoded != strings.ToLower(tst[0]+"."+tst[0]) {
t.Errorf("invalid result from string conversion to punycode, %s and should be %s.%s", decoded, tst[0], tst[0])
}
}
}
func TestEncodeDecodeFinalPeriod(t *testing.T) {
for _, tst := range testcases {
// assert unicode.com. == punycode.com.
full := ToPunycode(tst[0] + ".")
if full != tst[1]+"." {
t.Errorf("invalid result from string conversion to punycode when period added at the end, %#v and should be %#v", full, tst[1]+".")
}
// assert punycode.com. == unicode.com.
decoded := FromPunycode(tst[1] + ".")
if decoded != strings.ToLower(tst[0]+".") {
t.Errorf("invalid result from string conversion to punycode when period added, %#v and should be %#v", decoded, tst[0]+".")
}
full = ToPunycode(tst[0])
if full != tst[1] {
t.Errorf("invalid result from string conversion to punycode when no period added at the end, %#v and should be %#v", full, tst[1]+".")
}
// assert punycode.com. == unicode.com.
decoded = FromPunycode(tst[1])
if decoded != strings.ToLower(tst[0]) {
t.Errorf("invalid result from string conversion to punycode when no period added, %#v and should be %#v", decoded, tst[0]+".")
}
}
}
var invalidACEs = []string{
"xn--*",
"xn--",
"xn---",
"xn--a000000000",
}
func TestInvalidPunycode(t *testing.T) {
for _, d := range invalidACEs {
s := FromPunycode(d)
if s != d {
t.Errorf("Changed invalid name %s to %#v", d, s)
}
}
}
// You can verify the labels that are valid or not comparing to the Verisign
// website: http://mct.verisign-grs.com/
var invalidUnicodes = []string{
"Σ",
"ЯZ",
"Испытание",
}
func TestInvalidUnicodes(t *testing.T) {
for _, d := range invalidUnicodes {
s := ToPunycode(d)
if s != "" {
t.Errorf("Changed invalid name %s to %#v", d, s)
}
}
}

View File

@ -98,12 +98,11 @@ func CountLabel(s string) (labels int) {
return
}
}
panic("dns: not reached")
}
// Split splits a name s into its label indexes.
// www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}.
// The root name (.) returns nil. Also see dns.SplitDomainName.
// The root name (.) returns nil. Also see SplitDomainName.
func Split(s string) []int {
if s == "." {
return nil
@ -119,12 +118,12 @@ func Split(s string) []int {
}
idx = append(idx, off)
}
panic("dns: not reached")
}
// NextLabel returns the index of the start of the next label in the
// string s starting at offset.
// The bool end is true when the end of the string has been reached.
// Also see PrevLabel.
func NextLabel(s string, offset int) (i int, end bool) {
quote := false
for i = offset; i < len(s)-1; i++ {
@ -147,6 +146,7 @@ func NextLabel(s string, offset int) (i int, end bool) {
// PrevLabel returns the index of the label when starting from the right and
// jumping n labels to the left.
// The bool start is true when the start of the string has been overshot.
// Also see NextLabel.
func PrevLabel(s string, n int) (i int, start bool) {
if n == 0 {
return len(s), false

View File

@ -1,199 +0,0 @@
package dns
import (
"testing"
)
func TestCompareDomainName(t *testing.T) {
s1 := "www.miek.nl."
s2 := "miek.nl."
s3 := "www.bla.nl."
s4 := "nl.www.bla."
s5 := "nl"
s6 := "miek.nl"
if CompareDomainName(s1, s2) != 2 {
t.Errorf("%s with %s should be %d", s1, s2, 2)
}
if CompareDomainName(s1, s3) != 1 {
t.Errorf("%s with %s should be %d", s1, s3, 1)
}
if CompareDomainName(s3, s4) != 0 {
t.Errorf("%s with %s should be %d", s3, s4, 0)
}
// Non qualified tests
if CompareDomainName(s1, s5) != 1 {
t.Errorf("%s with %s should be %d", s1, s5, 1)
}
if CompareDomainName(s1, s6) != 2 {
t.Errorf("%s with %s should be %d", s1, s5, 2)
}
if CompareDomainName(s1, ".") != 0 {
t.Errorf("%s with %s should be %d", s1, s5, 0)
}
if CompareDomainName(".", ".") != 0 {
t.Errorf("%s with %s should be %d", ".", ".", 0)
}
}
func TestSplit(t *testing.T) {
splitter := map[string]int{
"www.miek.nl.": 3,
"www.miek.nl": 3,
"www..miek.nl": 4,
`www\.miek.nl.`: 2,
`www\\.miek.nl.`: 3,
".": 0,
"nl.": 1,
"nl": 1,
"com.": 1,
".com.": 2,
}
for s, i := range splitter {
if x := len(Split(s)); x != i {
t.Errorf("labels should be %d, got %d: %s %v", i, x, s, Split(s))
} else {
t.Logf("%s %v", s, Split(s))
}
}
}
func TestSplit2(t *testing.T) {
splitter := map[string][]int{
"www.miek.nl.": []int{0, 4, 9},
"www.miek.nl": []int{0, 4, 9},
"nl": []int{0},
}
for s, i := range splitter {
x := Split(s)
switch len(i) {
case 1:
if x[0] != i[0] {
t.Errorf("labels should be %v, got %v: %s", i, x, s)
}
default:
if x[0] != i[0] || x[1] != i[1] || x[2] != i[2] {
t.Errorf("labels should be %v, got %v: %s", i, x, s)
}
}
}
}
func TestPrevLabel(t *testing.T) {
type prev struct {
string
int
}
prever := map[prev]int{
prev{"www.miek.nl.", 0}: 12,
prev{"www.miek.nl.", 1}: 9,
prev{"www.miek.nl.", 2}: 4,
prev{"www.miek.nl", 0}: 11,
prev{"www.miek.nl", 1}: 9,
prev{"www.miek.nl", 2}: 4,
prev{"www.miek.nl.", 5}: 0,
prev{"www.miek.nl", 5}: 0,
prev{"www.miek.nl.", 3}: 0,
prev{"www.miek.nl", 3}: 0,
}
for s, i := range prever {
x, ok := PrevLabel(s.string, s.int)
if i != x {
t.Errorf("label should be %d, got %d, %t: preving %d, %s", i, x, ok, s.int, s.string)
}
}
}
func TestCountLabel(t *testing.T) {
splitter := map[string]int{
"www.miek.nl.": 3,
"www.miek.nl": 3,
"nl": 1,
".": 0,
}
for s, i := range splitter {
x := CountLabel(s)
if x != i {
t.Errorf("CountLabel should have %d, got %d", i, x)
}
}
}
func TestSplitDomainName(t *testing.T) {
labels := map[string][]string{
"miek.nl": []string{"miek", "nl"},
".": nil,
"www.miek.nl.": []string{"www", "miek", "nl"},
"www.miek.nl": []string{"www", "miek", "nl"},
"www..miek.nl": []string{"www", "", "miek", "nl"},
`www\.miek.nl`: []string{`www\.miek`, "nl"},
`www\\.miek.nl`: []string{`www\\`, "miek", "nl"},
}
domainLoop:
for domain, splits := range labels {
parts := SplitDomainName(domain)
if len(parts) != len(splits) {
t.Errorf("SplitDomainName returned %v for %s, expected %v", parts, domain, splits)
continue domainLoop
}
for i := range parts {
if parts[i] != splits[i] {
t.Errorf("SplitDomainName returned %v for %s, expected %v", parts, domain, splits)
continue domainLoop
}
}
}
}
func TestIsDomainName(t *testing.T) {
type ret struct {
ok bool
lab int
}
names := map[string]*ret{
"..": &ret{false, 1},
"@.": &ret{true, 1},
"www.example.com": &ret{true, 3},
"www.e%ample.com": &ret{true, 3},
"www.example.com.": &ret{true, 3},
"mi\\k.nl.": &ret{true, 2},
"mi\\k.nl": &ret{true, 2},
}
for d, ok := range names {
l, k := IsDomainName(d)
if ok.ok != k || ok.lab != l {
t.Errorf(" got %v %d for %s ", k, l, d)
t.Errorf("have %v %d for %s ", ok.ok, ok.lab, d)
}
}
}
func BenchmarkSplitLabels(b *testing.B) {
for i := 0; i < b.N; i++ {
Split("www.example.com")
}
}
func BenchmarkLenLabels(b *testing.B) {
for i := 0; i < b.N; i++ {
CountLabel("www.example.com")
}
}
func BenchmarkCompareLabels(b *testing.B) {
for i := 0; i < b.N; i++ {
CompareDomainName("www.example.com", "aa.example.com")
}
}
func BenchmarkIsSubDomain(b *testing.B) {
for i := 0; i < b.N; i++ {
IsSubDomain("www.example.com", "aa.example.com")
IsSubDomain("example.com", "aa.example.com")
IsSubDomain("miek.nl", "aa.example.com")
}
}

View File

@ -36,7 +36,8 @@ var (
// ErrFqdn indicates that a domain name does not have a closing dot.
ErrFqdn error = &Error{err: "domain must be fully qualified"}
// ErrId indicates there is a mismatch with the message's ID.
ErrId error = &Error{err: "id mismatch"}
ErrId error = &Error{err: "id mismatch"}
// ErrKeyAlg indicates that the algorithm in the key is not valid.
ErrKeyAlg error = &Error{err: "bad key algorithm"}
ErrKey error = &Error{err: "bad key"}
ErrKeySize error = &Error{err: "bad key size"}
@ -53,6 +54,9 @@ var (
ErrSoa error = &Error{err: "no SOA"}
// ErrTime indicates a timing error in TSIG authentication.
ErrTime error = &Error{err: "bad time"}
// ErrTruncated indicates that we failed to unpack a truncated message.
// We unpacked as much as we had so Msg can still be used, if desired.
ErrTruncated error = &Error{err: "failed to unpack truncated message"}
)
// Id, by default, returns a 16 bits random number to be used as a
@ -88,84 +92,6 @@ type Msg struct {
Extra []RR // Holds the RR(s) of the additional section.
}
// TypeToString is a map of strings for each RR wire type.
var TypeToString = map[uint16]string{
TypeA: "A",
TypeAAAA: "AAAA",
TypeAFSDB: "AFSDB",
TypeANY: "ANY", // Meta RR
TypeATMA: "ATMA",
TypeAXFR: "AXFR", // Meta RR
TypeCAA: "CAA",
TypeCDNSKEY: "CDNSKEY",
TypeCDS: "CDS",
TypeCERT: "CERT",
TypeCNAME: "CNAME",
TypeDHCID: "DHCID",
TypeDLV: "DLV",
TypeDNAME: "DNAME",
TypeDNSKEY: "DNSKEY",
TypeDS: "DS",
TypeEID: "EID",
TypeEUI48: "EUI48",
TypeEUI64: "EUI64",
TypeGID: "GID",
TypeGPOS: "GPOS",
TypeHINFO: "HINFO",
TypeHIP: "HIP",
TypeIPSECKEY: "IPSECKEY",
TypeISDN: "ISDN",
TypeIXFR: "IXFR", // Meta RR
TypeKEY: "KEY",
TypeKX: "KX",
TypeL32: "L32",
TypeL64: "L64",
TypeLOC: "LOC",
TypeLP: "LP",
TypeMB: "MB",
TypeMD: "MD",
TypeMF: "MF",
TypeMG: "MG",
TypeMINFO: "MINFO",
TypeMR: "MR",
TypeMX: "MX",
TypeNAPTR: "NAPTR",
TypeNID: "NID",
TypeNINFO: "NINFO",
TypeNIMLOC: "NIMLOC",
TypeNS: "NS",
TypeNSAPPTR: "NSAP-PTR",
TypeNSEC3: "NSEC3",
TypeNSEC3PARAM: "NSEC3PARAM",
TypeNSEC: "NSEC",
TypeNULL: "NULL",
TypeOPT: "OPT",
TypeOPENPGPKEY: "OPENPGPKEY",
TypePTR: "PTR",
TypeRKEY: "RKEY",
TypeRP: "RP",
TypeRRSIG: "RRSIG",
TypeRT: "RT",
TypeSIG: "SIG",
TypeSOA: "SOA",
TypeSPF: "SPF",
TypeSRV: "SRV",
TypeSSHFP: "SSHFP",
TypeTA: "TA",
TypeTALINK: "TALINK",
TypeTKEY: "TKEY", // Meta RR
TypeTLSA: "TLSA",
TypeTSIG: "TSIG", // Meta RR
TypeTXT: "TXT",
TypePX: "PX",
TypeUID: "UID",
TypeUINFO: "UINFO",
TypeUNSPEC: "UNSPEC",
TypeURI: "URI",
TypeWKS: "WKS",
TypeX25: "X25",
}
// StringToType is the reverse of TypeToString, needed for string parsing.
var StringToType = reverseInt16(TypeToString)
@ -1315,8 +1241,8 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er
continue
}
}
if off == lenmsg {
// zero rdata foo, OK for dyn. updates
if off == lenmsg && int(val.FieldByName("Hdr").FieldByName("Rdlength").Uint()) == 0 {
// zero rdata is ok for dyn updates, but only if rdlength is 0
break
}
s, off, err = UnpackDomainName(msg, off)
@ -1460,7 +1386,7 @@ func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) {
}
end := off + int(h.Rdlength)
// make an rr of that type and re-unpack.
mk, known := typeToRR[h.Rrtype]
mk, known := TypeToRR[h.Rrtype]
if !known {
rr = new(RFC3597)
} else {
@ -1473,6 +1399,32 @@ func UnpackRR(msg []byte, off int) (rr RR, off1 int, err error) {
return rr, off, err
}
// unpackRRslice unpacks msg[off:] into an []RR.
// If we cannot unpack the whole array, then it will return nil
func unpackRRslice(l int, msg []byte, off int) (dst1 []RR, off1 int, err error) {
var r RR
// Optimistically make dst be the length that was sent
dst := make([]RR, 0, l)
for i := 0; i < l; i++ {
off1 := off
r, off, err = UnpackRR(msg, off)
if err != nil {
off = len(msg)
break
}
// If offset does not increase anymore, l is a lie
if off1 == off {
l = i
break
}
dst = append(dst, r)
}
if err != nil && off == len(msg) {
dst = nil
}
return dst, off, err
}
// Reverse a map
func reverseInt8(m map[uint8]string) map[string]uint8 {
n := make(map[string]uint8)
@ -1671,84 +1623,48 @@ func (dns *Msg) Unpack(msg []byte) (err error) {
dns.CheckingDisabled = (dh.Bits & _CD) != 0
dns.Rcode = int(dh.Bits & 0xF)
// Don't pre-alloc these arrays, the incoming lengths are from the network.
dns.Question = make([]Question, 0, 1)
dns.Answer = make([]RR, 0, 10)
dns.Ns = make([]RR, 0, 10)
dns.Extra = make([]RR, 0, 10)
// Optimistically use the count given to us in the header
dns.Question = make([]Question, 0, int(dh.Qdcount))
var q Question
for i := 0; i < int(dh.Qdcount); i++ {
off1 := off
off, err = UnpackStruct(&q, msg, off)
if err != nil {
// Even if Truncated is set, we only will set ErrTruncated if we
// actually got the questions
return err
}
if off1 == off { // Offset does not increase anymore, dh.Qdcount is a lie!
dh.Qdcount = uint16(i)
break
}
dns.Question = append(dns.Question, q)
}
// If we see a TC bit being set we return here, without
// an error, because technically it isn't an error. So return
// without parsing the potentially corrupt packet and hitting an error.
// TODO(miek): this isn't the best strategy!
// Better stragey would be: set boolean indicating truncated message, go forth and parse
// until we hit an error, return the message without the latest parsed rr if this boolean
// is true.
if dns.Truncated {
dns.Answer = nil
dns.Ns = nil
dns.Extra = nil
return nil
}
var r RR
for i := 0; i < int(dh.Ancount); i++ {
off1 := off
r, off, err = UnpackRR(msg, off)
if err != nil {
return err
}
if off1 == off { // Offset does not increase anymore, dh.Ancount is a lie!
dh.Ancount = uint16(i)
break
}
dns.Answer = append(dns.Answer, r)
dns.Answer, off, err = unpackRRslice(int(dh.Ancount), msg, off)
// The header counts might have been wrong so we need to update it
dh.Ancount = uint16(len(dns.Answer))
if err == nil {
dns.Ns, off, err = unpackRRslice(int(dh.Nscount), msg, off)
}
for i := 0; i < int(dh.Nscount); i++ {
off1 := off
r, off, err = UnpackRR(msg, off)
if err != nil {
return err
}
if off1 == off { // Offset does not increase anymore, dh.Nscount is a lie!
dh.Nscount = uint16(i)
break
}
dns.Ns = append(dns.Ns, r)
}
for i := 0; i < int(dh.Arcount); i++ {
off1 := off
r, off, err = UnpackRR(msg, off)
if err != nil {
return err
}
if off1 == off { // Offset does not increase anymore, dh.Arcount is a lie!
dh.Arcount = uint16(i)
break
}
dns.Extra = append(dns.Extra, r)
// The header counts might have been wrong so we need to update it
dh.Nscount = uint16(len(dns.Ns))
if err == nil {
dns.Extra, off, err = unpackRRslice(int(dh.Arcount), msg, off)
}
// The header counts might have been wrong so we need to update it
dh.Arcount = uint16(len(dns.Extra))
if off != len(msg) {
// TODO(miek) make this an error?
// use PackOpt to let people tell how detailed the error reporting should be?
// println("dns: extra bytes in dns packet", off, "<", len(msg))
} else if dns.Truncated {
// Whether we ran into a an error or not, we want to return that it
// was truncated
err = ErrTruncated
}
return nil
return err
}
// Convert a complete message to a string with dig-like output.

View File

@ -1,29 +0,0 @@
package dns
import (
"testing"
)
func TestPackNsec3(t *testing.T) {
nsec3 := HashName("dnsex.nl.", SHA1, 0, "DEAD")
if nsec3 != "ROCCJAE8BJJU7HN6T7NG3TNM8ACRS87J" {
t.Error(nsec3)
}
nsec3 = HashName("a.b.c.example.org.", SHA1, 2, "DEAD")
if nsec3 != "6LQ07OAHBTOOEU2R9ANI2AT70K5O0RCG" {
t.Error(nsec3)
}
}
func TestNsec3(t *testing.T) {
// examples taken from .nl
nsec3, _ := NewRR("39p91242oslggest5e6a7cci4iaeqvnk.nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 39P99DCGG0MDLARTCRMCF6OFLLUL7PR6 NS DS RRSIG")
if !nsec3.(*NSEC3).Cover("snasajsksasasa.nl.") { // 39p94jrinub66hnpem8qdpstrec86pg3
t.Error("39p94jrinub66hnpem8qdpstrec86pg3. should be covered by 39p91242oslggest5e6a7cci4iaeqvnk.nl. - 39P99DCGG0MDLARTCRMCF6OFLLUL7PR6")
}
nsec3, _ = NewRR("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. IN NSEC3 1 1 5 F10E9F7EA83FC8F3 SK4F38CQ0ATIEI8MH3RGD0P5I4II6QAN NS SOA TXT RRSIG DNSKEY NSEC3PARAM")
if !nsec3.(*NSEC3).Match("nl.") { // sk4e8fj94u78smusb40o1n0oltbblu2r.nl.
t.Error("sk4e8fj94u78smusb40o1n0oltbblu2r.nl. should match sk4e8fj94u78smusb40o1n0oltbblu2r.nl.")
}
}

File diff suppressed because it is too large Load Diff

View File

@ -33,7 +33,7 @@ type PrivateRR struct {
func mkPrivateRR(rrtype uint16) *PrivateRR {
// Panics if RR is not an instance of PrivateRR.
rrfunc, ok := typeToRR[rrtype]
rrfunc, ok := TypeToRR[rrtype]
if !ok {
panic(fmt.Sprintf("dns: invalid operation with Private RR type %d", rrtype))
}
@ -43,11 +43,13 @@ func mkPrivateRR(rrtype uint16) *PrivateRR {
case *PrivateRR:
return rr
}
panic(fmt.Sprintf("dns: RR is not a PrivateRR, typeToRR[%d] generator returned %T", rrtype, anyrr))
panic(fmt.Sprintf("dns: RR is not a PrivateRR, TypeToRR[%d] generator returned %T", rrtype, anyrr))
}
// Header return the RR header of r.
func (r *PrivateRR) Header() *RR_Header { return &r.Hdr }
func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() }
func (r *PrivateRR) String() string { return r.Hdr.String() + r.Data.String() }
// Private len and copy parts to satisfy RR interface.
func (r *PrivateRR) len() int { return r.Hdr.len() + r.Data.Len() }
@ -69,7 +71,7 @@ func (r *PrivateRR) copy() RR {
func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata) {
rtypestr = strings.ToUpper(rtypestr)
typeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
TypeToRR[rtype] = func() RR { return &PrivateRR{RR_Header{}, generator()} }
TypeToString[rtype] = rtypestr
StringToType[rtypestr] = rtype
@ -106,7 +108,7 @@ func PrivateHandle(rtypestr string, rtype uint16, generator func() PrivateRdata)
func PrivateHandleRemove(rtype uint16) {
rtypestr, ok := TypeToString[rtype]
if ok {
delete(typeToRR, rtype)
delete(TypeToRR, rtype)
delete(TypeToString, rtype)
delete(typeToparserFunc, rtype)
delete(StringToType, rtypestr)

View File

@ -1,170 +0,0 @@
package dns_test
import (
"strings"
"testing"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns"
)
const TypeISBN uint16 = 0x0F01
// A crazy new RR type :)
type ISBN struct {
x string // rdata with 10 or 13 numbers, dashes or spaces allowed
}
func NewISBN() dns.PrivateRdata { return &ISBN{""} }
func (rd *ISBN) Len() int { return len([]byte(rd.x)) }
func (rd *ISBN) String() string { return rd.x }
func (rd *ISBN) Parse(txt []string) error {
rd.x = strings.TrimSpace(strings.Join(txt, " "))
return nil
}
func (rd *ISBN) Pack(buf []byte) (int, error) {
b := []byte(rd.x)
n := copy(buf, b)
if n != len(b) {
return n, dns.ErrBuf
}
return n, nil
}
func (rd *ISBN) Unpack(buf []byte) (int, error) {
rd.x = string(buf)
return len(buf), nil
}
func (rd *ISBN) Copy(dest dns.PrivateRdata) error {
isbn, ok := dest.(*ISBN)
if !ok {
return dns.ErrRdata
}
isbn.x = rd.x
return nil
}
var testrecord = strings.Join([]string{"example.org.", "3600", "IN", "ISBN", "12-3 456789-0-123"}, "\t")
func TestPrivateText(t *testing.T) {
dns.PrivateHandle("ISBN", TypeISBN, NewISBN)
defer dns.PrivateHandleRemove(TypeISBN)
rr, err := dns.NewRR(testrecord)
if err != nil {
t.Fatal(err)
}
if rr.String() != testrecord {
t.Errorf("record string representation did not match original %#v != %#v", rr.String(), testrecord)
} else {
t.Log(rr.String())
}
}
func TestPrivateByteSlice(t *testing.T) {
dns.PrivateHandle("ISBN", TypeISBN, NewISBN)
defer dns.PrivateHandleRemove(TypeISBN)
rr, err := dns.NewRR(testrecord)
if err != nil {
t.Fatal(err)
}
buf := make([]byte, 100)
off, err := dns.PackRR(rr, buf, 0, nil, false)
if err != nil {
t.Errorf("got error packing ISBN: %v", err)
}
custrr := rr.(*dns.PrivateRR)
if ln := custrr.Data.Len() + len(custrr.Header().Name) + 11; ln != off {
t.Errorf("offset is not matching to length of Private RR: %d!=%d", off, ln)
}
rr1, off1, err := dns.UnpackRR(buf[:off], 0)
if err != nil {
t.Errorf("got error unpacking ISBN: %v", err)
}
if off1 != off {
t.Errorf("Offset after unpacking differs: %d != %d", off1, off)
}
if rr1.String() != testrecord {
t.Errorf("Record string representation did not match original %#v != %#v", rr1.String(), testrecord)
} else {
t.Log(rr1.String())
}
}
const TypeVERSION uint16 = 0x0F02
type VERSION struct {
x string
}
func NewVersion() dns.PrivateRdata { return &VERSION{""} }
func (rd *VERSION) String() string { return rd.x }
func (rd *VERSION) Parse(txt []string) error {
rd.x = strings.TrimSpace(strings.Join(txt, " "))
return nil
}
func (rd *VERSION) Pack(buf []byte) (int, error) {
b := []byte(rd.x)
n := copy(buf, b)
if n != len(b) {
return n, dns.ErrBuf
}
return n, nil
}
func (rd *VERSION) Unpack(buf []byte) (int, error) {
rd.x = string(buf)
return len(buf), nil
}
func (rd *VERSION) Copy(dest dns.PrivateRdata) error {
isbn, ok := dest.(*VERSION)
if !ok {
return dns.ErrRdata
}
isbn.x = rd.x
return nil
}
func (rd *VERSION) Len() int {
return len([]byte(rd.x))
}
var smallzone = `$ORIGIN example.org.
@ SOA sns.dns.icann.org. noc.dns.icann.org. (
2014091518 7200 3600 1209600 3600
)
A 1.2.3.4
ok ISBN 1231-92110-12
go VERSION (
1.3.1 ; comment
)
www ISBN 1231-92110-16
* CNAME @
`
func TestPrivateZoneParser(t *testing.T) {
dns.PrivateHandle("ISBN", TypeISBN, NewISBN)
dns.PrivateHandle("VERSION", TypeVERSION, NewVersion)
defer dns.PrivateHandleRemove(TypeISBN)
defer dns.PrivateHandleRemove(TypeVERSION)
r := strings.NewReader(smallzone)
for x := range dns.ParseZone(r, ".", "") {
if err := x.Error; err != nil {
t.Fatal(err)
}
t.Log(x.RR)
}
}

84
Godeps/_workspace/src/github.com/miekg/dns/sanitize.go generated vendored Normal file
View File

@ -0,0 +1,84 @@
package dns
// Dedup removes identical RRs from rrs. It preserves the original ordering.
// The lowest TTL of any duplicates is used in the remaining one. Dedup modifies
// rrs.
// m is used to store the RRs temporay. If it is nil a new map will be allocated.
func Dedup(rrs []RR, m map[string]RR) []RR {
if m == nil {
m = make(map[string]RR)
}
// Save the keys, so we don't have to call normalizedString twice.
keys := make([]*string, 0, len(rrs))
for _, r := range rrs {
key := normalizedString(r)
keys = append(keys, &key)
if _, ok := m[key]; ok {
// Shortest TTL wins.
if m[key].Header().Ttl > r.Header().Ttl {
m[key].Header().Ttl = r.Header().Ttl
}
continue
}
m[key] = r
}
// If the length of the result map equals the amount of RRs we got,
// it means they were all different. We can then just return the original rrset.
if len(m) == len(rrs) {
return rrs
}
j := 0
for i, r := range rrs {
// If keys[i] lives in the map, we should copy and remove it.
if _, ok := m[*keys[i]]; ok {
delete(m, *keys[i])
rrs[j] = r
j++
}
if len(m) == 0 {
break
}
}
return rrs[:j]
}
// normalizedString returns a normalized string from r. The TTL
// is removed and the domain name is lowercased. We go from this:
// DomainName<TAB>TTL<TAB>CLASS<TAB>TYPE<TAB>RDATA to:
// lowercasename<TAB>CLASS<TAB>TYPE...
func normalizedString(r RR) string {
// A string Go DNS makes has: domainname<TAB>TTL<TAB>...
b := []byte(r.String())
// find the first non-escaped tab, then another, so we capture where the TTL lives.
esc := false
ttlStart, ttlEnd := 0, 0
for i := 0; i < len(b) && ttlEnd == 0; i++ {
switch {
case b[i] == '\\':
esc = !esc
case b[i] == '\t' && !esc:
if ttlStart == 0 {
ttlStart = i
continue
}
if ttlEnd == 0 {
ttlEnd = i
}
case b[i] >= 'A' && b[i] <= 'Z' && !esc:
b[i] += 32
default:
esc = false
}
}
// remove TTL.
copy(b[ttlStart:], b[ttlEnd:])
cut := ttlEnd - ttlStart
return string(b[:len(b)-cut])
}

View File

@ -10,6 +10,9 @@ import (
"time"
)
// Maximum number of TCP queries before we close the socket.
const maxTCPQueries = 128
// Handler is implemented by any value that implements ServeDNS.
type Handler interface {
ServeDNS(w ResponseWriter, r *Msg)
@ -159,9 +162,9 @@ func (mux *ServeMux) HandleRemove(pattern string) {
if pattern == "" {
panic("dns: invalid pattern " + pattern)
}
// don't need a mutex here, because deleting is OK, even if the
// entry is note there.
mux.m.Lock()
delete(mux.z, Fqdn(pattern))
mux.m.Unlock()
}
// ServeDNS dispatches the request to the handler whose
@ -268,27 +271,21 @@ type Server struct {
// DecorateWriter is optional, allows customization of the process that writes raw DNS messages.
DecorateWriter DecorateWriter
// For graceful shutdown.
stopUDP chan bool
stopTCP chan bool
wgUDP sync.WaitGroup
wgTCP sync.WaitGroup
// Graceful shutdown handling
// make start/shutdown not racy
lock sync.Mutex
inFlight sync.WaitGroup
lock sync.RWMutex
started bool
}
// ListenAndServe starts a nameserver on the configured address in *Server.
func (srv *Server) ListenAndServe() error {
srv.lock.Lock()
defer srv.lock.Unlock()
if srv.started {
srv.lock.Unlock()
return &Error{err: "server already started"}
}
defer srv.lock.Unlock()
srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool)
srv.started = true
addr := srv.Addr
if addr == "" {
addr = ":domain"
@ -307,7 +304,11 @@ func (srv *Server) ListenAndServe() error {
return e
}
srv.Listener = l
return srv.serveTCP(l)
srv.started = true
srv.lock.Unlock()
e = srv.serveTCP(l)
srv.lock.Lock() // to satisfy the defer at the top
return e
case "udp", "udp4", "udp6":
a, e := net.ResolveUDPAddr(srv.Net, addr)
if e != nil {
@ -321,7 +322,11 @@ func (srv *Server) ListenAndServe() error {
return e
}
srv.PacketConn = l
return srv.serveUDP(l)
srv.started = true
srv.lock.Unlock()
e = srv.serveUDP(l)
srv.lock.Lock() // to satisfy the defer at the top
return e
}
return &Error{err: "bad network"}
}
@ -330,15 +335,12 @@ func (srv *Server) ListenAndServe() error {
// configured in *Server. Its main use is to start a server from systemd.
func (srv *Server) ActivateAndServe() error {
srv.lock.Lock()
defer srv.lock.Unlock()
if srv.started {
srv.lock.Unlock()
return &Error{err: "server already started"}
}
srv.stopUDP, srv.stopTCP = make(chan bool), make(chan bool)
srv.started = true
pConn := srv.PacketConn
l := srv.Listener
srv.lock.Unlock()
if pConn != nil {
if srv.UDPSize == 0 {
srv.UDPSize = MinMsgSize
@ -347,12 +349,20 @@ func (srv *Server) ActivateAndServe() error {
if e := setUDPSocketOptions(t); e != nil {
return e
}
return srv.serveUDP(t)
srv.started = true
srv.lock.Unlock()
e := srv.serveUDP(t)
srv.lock.Lock() // to satisfy the defer at the top
return e
}
}
if l != nil {
if t, ok := l.(*net.TCPListener); ok {
return srv.serveTCP(t)
srv.started = true
srv.lock.Unlock()
e := srv.serveTCP(t)
srv.lock.Lock() // to satisfy the defer at the top
return e
}
}
return &Error{err: "bad listeners"}
@ -369,36 +379,20 @@ func (srv *Server) Shutdown() error {
return &Error{err: "server not started"}
}
srv.started = false
net, addr := srv.Net, srv.Addr
switch {
case srv.Listener != nil:
a := srv.Listener.Addr()
net, addr = a.Network(), a.String()
case srv.PacketConn != nil:
a := srv.PacketConn.LocalAddr()
net, addr = a.Network(), a.String()
}
srv.lock.Unlock()
fin := make(chan bool)
switch net {
case "tcp", "tcp4", "tcp6":
go func() {
srv.stopTCP <- true
srv.wgTCP.Wait()
fin <- true
}()
case "udp", "udp4", "udp6":
go func() {
srv.stopUDP <- true
srv.wgUDP.Wait()
fin <- true
}()
if srv.PacketConn != nil {
srv.PacketConn.Close()
}
if srv.Listener != nil {
srv.Listener.Close()
}
c := &Client{Net: net}
go c.Exchange(new(Msg), addr) // extra query to help ReadXXX loop to pass
fin := make(chan bool)
go func() {
srv.inFlight.Wait()
fin <- true
}()
select {
case <-time.After(srv.getReadTimeout()):
@ -440,21 +434,24 @@ func (srv *Server) serveTCP(l *net.TCPListener) error {
for {
rw, e := l.AcceptTCP()
if e != nil {
continue
if neterr, ok := e.(net.Error); ok && neterr.Temporary() {
continue
}
return e
}
m, e := reader.ReadTCP(rw, rtimeout)
select {
case <-srv.stopTCP:
srv.lock.RLock()
if !srv.started {
srv.lock.RUnlock()
return nil
default:
}
srv.lock.RUnlock()
if e != nil {
continue
}
srv.wgTCP.Add(1)
srv.inFlight.Add(1)
go srv.serve(rw.RemoteAddr(), handler, m, nil, nil, rw)
}
panic("dns: not reached")
}
// serveUDP starts a UDP listener for the server.
@ -479,22 +476,24 @@ func (srv *Server) serveUDP(l *net.UDPConn) error {
// deadline is not used here
for {
m, s, e := reader.ReadUDP(l, rtimeout)
select {
case <-srv.stopUDP:
srv.lock.RLock()
if !srv.started {
srv.lock.RUnlock()
return nil
default:
}
srv.lock.RUnlock()
if e != nil {
continue
}
srv.wgUDP.Add(1)
srv.inFlight.Add(1)
go srv.serve(s.RemoteAddr(), handler, m, l, s, nil)
}
panic("dns: not reached")
}
// Serve a new connection.
func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *SessionUDP, t *net.TCPConn) {
defer srv.inFlight.Done()
w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s}
if srv.DecorateWriter != nil {
w.writer = srv.DecorateWriter(w)
@ -502,15 +501,7 @@ func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *Ses
w.writer = w
}
q := 0
defer func() {
if u != nil {
srv.wgUDP.Done()
}
if t != nil {
srv.wgTCP.Done()
}
}()
q := 0 // counter for the amount of TCP queries we get
reader := Reader(&defaultReader{srv})
if srv.DecorateReader != nil {
@ -544,6 +535,12 @@ Redo:
h.ServeDNS(w, req) // Writes back to the client
Exit:
// TODO(miek): make this number configurable?
if q > maxTCPQueries { // close socket after this many queries
w.Close()
return
}
if w.hijacked {
return // client calls Close()
}
@ -558,11 +555,6 @@ Exit:
m, e := reader.ReadTCP(w.tcp, idleTimeout)
if e == nil {
q++
// TODO(miek): make this number configurable?
if q > 128 { // close socket after this many queries
w.Close()
return
}
goto Redo
}
w.Close()

View File

@ -1,450 +0,0 @@
package dns
import (
"fmt"
"net"
"runtime"
"sync"
"testing"
)
func HelloServer(w ResponseWriter, req *Msg) {
m := new(Msg)
m.SetReply(req)
m.Extra = make([]RR, 1)
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
w.WriteMsg(m)
}
func HelloServerBadId(w ResponseWriter, req *Msg) {
m := new(Msg)
m.SetReply(req)
m.Id += 1
m.Extra = make([]RR, 1)
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
w.WriteMsg(m)
}
func AnotherHelloServer(w ResponseWriter, req *Msg) {
m := new(Msg)
m.SetReply(req)
m.Extra = make([]RR, 1)
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello example"}}
w.WriteMsg(m)
}
func RunLocalUDPServer(laddr string) (*Server, string, error) {
pc, err := net.ListenPacket("udp", laddr)
if err != nil {
return nil, "", err
}
server := &Server{PacketConn: pc}
waitLock := sync.Mutex{}
waitLock.Lock()
server.NotifyStartedFunc = waitLock.Unlock
go func() {
server.ActivateAndServe()
pc.Close()
}()
waitLock.Lock()
return server, pc.LocalAddr().String(), nil
}
func RunLocalUDPServerUnsafe(laddr string) (*Server, string, error) {
pc, err := net.ListenPacket("udp", laddr)
if err != nil {
return nil, "", err
}
server := &Server{PacketConn: pc, Unsafe: true}
waitLock := sync.Mutex{}
waitLock.Lock()
server.NotifyStartedFunc = waitLock.Unlock
go func() {
server.ActivateAndServe()
pc.Close()
}()
waitLock.Lock()
return server, pc.LocalAddr().String(), nil
}
func RunLocalTCPServer(laddr string) (*Server, string, error) {
l, err := net.Listen("tcp", laddr)
if err != nil {
return nil, "", err
}
server := &Server{Listener: l}
waitLock := sync.Mutex{}
waitLock.Lock()
server.NotifyStartedFunc = waitLock.Unlock
go func() {
server.ActivateAndServe()
l.Close()
}()
waitLock.Lock()
return server, l.Addr().String(), nil
}
func TestServing(t *testing.T) {
HandleFunc("miek.nl.", HelloServer)
HandleFunc("example.com.", AnotherHelloServer)
defer HandleRemove("miek.nl.")
defer HandleRemove("example.com.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to run test server: %v", err)
}
defer s.Shutdown()
c := new(Client)
m := new(Msg)
m.SetQuestion("miek.nl.", TypeTXT)
r, _, err := c.Exchange(m, addrstr)
if err != nil || len(r.Extra) == 0 {
t.Fatal("failed to exchange miek.nl", err)
}
txt := r.Extra[0].(*TXT).Txt[0]
if txt != "Hello world" {
t.Error("Unexpected result for miek.nl", txt, "!= Hello world")
}
m.SetQuestion("example.com.", TypeTXT)
r, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Fatal("failed to exchange example.com", err)
}
txt = r.Extra[0].(*TXT).Txt[0]
if txt != "Hello example" {
t.Error("Unexpected result for example.com", txt, "!= Hello example")
}
// Test Mixes cased as noticed by Ask.
m.SetQuestion("eXaMplE.cOm.", TypeTXT)
r, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Error("failed to exchange eXaMplE.cOm", err)
}
txt = r.Extra[0].(*TXT).Txt[0]
if txt != "Hello example" {
t.Error("Unexpected result for example.com", txt, "!= Hello example")
}
}
func BenchmarkServe(b *testing.B) {
b.StopTimer()
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
a := runtime.GOMAXPROCS(4)
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
b.Fatalf("Unable to run test server: %v", err)
}
defer s.Shutdown()
c := new(Client)
m := new(Msg)
m.SetQuestion("miek.nl", TypeSOA)
b.StartTimer()
for i := 0; i < b.N; i++ {
c.Exchange(m, addrstr)
}
runtime.GOMAXPROCS(a)
}
func benchmarkServe6(b *testing.B) {
b.StopTimer()
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
a := runtime.GOMAXPROCS(4)
s, addrstr, err := RunLocalUDPServer("[::1]:0")
if err != nil {
b.Fatalf("Unable to run test server: %v", err)
}
defer s.Shutdown()
c := new(Client)
m := new(Msg)
m.SetQuestion("miek.nl", TypeSOA)
b.StartTimer()
for i := 0; i < b.N; i++ {
c.Exchange(m, addrstr)
}
runtime.GOMAXPROCS(a)
}
func HelloServerCompress(w ResponseWriter, req *Msg) {
m := new(Msg)
m.SetReply(req)
m.Extra = make([]RR, 1)
m.Extra[0] = &TXT{Hdr: RR_Header{Name: m.Question[0].Name, Rrtype: TypeTXT, Class: ClassINET, Ttl: 0}, Txt: []string{"Hello world"}}
m.Compress = true
w.WriteMsg(m)
}
func BenchmarkServeCompress(b *testing.B) {
b.StopTimer()
HandleFunc("miek.nl.", HelloServerCompress)
defer HandleRemove("miek.nl.")
a := runtime.GOMAXPROCS(4)
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
b.Fatalf("Unable to run test server: %v", err)
}
defer s.Shutdown()
c := new(Client)
m := new(Msg)
m.SetQuestion("miek.nl", TypeSOA)
b.StartTimer()
for i := 0; i < b.N; i++ {
c.Exchange(m, addrstr)
}
runtime.GOMAXPROCS(a)
}
func TestDotAsCatchAllWildcard(t *testing.T) {
mux := NewServeMux()
mux.Handle(".", HandlerFunc(HelloServer))
mux.Handle("example.com.", HandlerFunc(AnotherHelloServer))
handler := mux.match("www.miek.nl.", TypeTXT)
if handler == nil {
t.Error("wildcard match failed")
}
handler = mux.match("www.example.com.", TypeTXT)
if handler == nil {
t.Error("example.com match failed")
}
handler = mux.match("a.www.example.com.", TypeTXT)
if handler == nil {
t.Error("a.www.example.com match failed")
}
handler = mux.match("boe.", TypeTXT)
if handler == nil {
t.Error("boe. match failed")
}
}
func TestCaseFolding(t *testing.T) {
mux := NewServeMux()
mux.Handle("_udp.example.com.", HandlerFunc(HelloServer))
handler := mux.match("_dns._udp.example.com.", TypeSRV)
if handler == nil {
t.Error("case sensitive characters folded")
}
handler = mux.match("_DNS._UDP.EXAMPLE.COM.", TypeSRV)
if handler == nil {
t.Error("case insensitive characters not folded")
}
}
func TestRootServer(t *testing.T) {
mux := NewServeMux()
mux.Handle(".", HandlerFunc(HelloServer))
handler := mux.match(".", TypeNS)
if handler == nil {
t.Error("root match failed")
}
}
type maxRec struct {
max int
sync.RWMutex
}
var M = new(maxRec)
func HelloServerLargeResponse(resp ResponseWriter, req *Msg) {
m := new(Msg)
m.SetReply(req)
m.Authoritative = true
m1 := 0
M.RLock()
m1 = M.max
M.RUnlock()
for i := 0; i < m1; i++ {
aRec := &A{
Hdr: RR_Header{
Name: req.Question[0].Name,
Rrtype: TypeA,
Class: ClassINET,
Ttl: 0,
},
A: net.ParseIP(fmt.Sprintf("127.0.0.%d", i+1)).To4(),
}
m.Answer = append(m.Answer, aRec)
}
resp.WriteMsg(m)
}
func TestServingLargeResponses(t *testing.T) {
HandleFunc("example.", HelloServerLargeResponse)
defer HandleRemove("example.")
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to run test server: %v", err)
}
defer s.Shutdown()
// Create request
m := new(Msg)
m.SetQuestion("web.service.example.", TypeANY)
c := new(Client)
c.Net = "udp"
M.Lock()
M.max = 2
M.Unlock()
_, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
// This must fail
M.Lock()
M.max = 20
M.Unlock()
_, _, err = c.Exchange(m, addrstr)
if err == nil {
t.Error("failed to fail exchange, this should generate packet error")
}
// But this must work again
c.UDPSize = 7000
_, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
}
func TestServingResponse(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
HandleFunc("miek.nl.", HelloServer)
s, addrstr, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to run test server: %v", err)
}
c := new(Client)
m := new(Msg)
m.SetQuestion("miek.nl.", TypeTXT)
m.Response = false
_, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Fatal("failed to exchange", err)
}
m.Response = true
_, _, err = c.Exchange(m, addrstr)
if err == nil {
t.Fatal("exchanged response message")
}
s.Shutdown()
s, addrstr, err = RunLocalUDPServerUnsafe("127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to run test server: %v", err)
}
defer s.Shutdown()
m.Response = true
_, _, err = c.Exchange(m, addrstr)
if err != nil {
t.Fatal("could exchanged response message in Unsafe mode")
}
}
func TestShutdownTCP(t *testing.T) {
s, _, err := RunLocalTCPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to run test server: %v", err)
}
err = s.Shutdown()
if err != nil {
t.Errorf("Could not shutdown test TCP server, %v", err)
}
}
func TestShutdownUDP(t *testing.T) {
s, _, err := RunLocalUDPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to run test server: %v", err)
}
err = s.Shutdown()
if err != nil {
t.Errorf("Could not shutdown test UDP server, %v", err)
}
}
type ExampleFrameLengthWriter struct {
Writer
}
func (e *ExampleFrameLengthWriter) Write(m []byte) (int, error) {
fmt.Println("writing raw DNS message of length", len(m))
return e.Writer.Write(m)
}
func ExampleDecorateWriter() {
// instrument raw DNS message writing
wf := DecorateWriter(func(w Writer) Writer {
return &ExampleFrameLengthWriter{w}
})
// simple UDP server
pc, err := net.ListenPacket("udp", "127.0.0.1:0")
if err != nil {
fmt.Println(err.Error())
return
}
server := &Server{
PacketConn: pc,
DecorateWriter: wf,
}
waitLock := sync.Mutex{}
waitLock.Lock()
server.NotifyStartedFunc = waitLock.Unlock
defer server.Shutdown()
go func() {
server.ActivateAndServe()
pc.Close()
}()
waitLock.Lock()
HandleFunc("miek.nl.", HelloServer)
c := new(Client)
m := new(Msg)
m.SetQuestion("miek.nl.", TypeTXT)
_, _, err = c.Exchange(m, pc.LocalAddr().String())
if err != nil {
fmt.Println("failed to exchange", err.Error())
return
}
// Output: writing raw DNS message of length 56
}

View File

@ -13,7 +13,7 @@ import (
// Sign signs a dns.Msg. It fills the signature with the appropriate data.
// The SIG record should have the SignerName, KeyTag, Algorithm, Inception
// and Expiration set.
func (rr *SIG) Sign(k PrivateKey, m *Msg) ([]byte, error) {
func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {
if k == nil {
return nil, ErrPrivKey
}
@ -41,31 +41,26 @@ func (rr *SIG) Sign(k PrivateKey, m *Msg) ([]byte, error) {
return nil, err
}
buf = buf[:off:cap(buf)]
var hash crypto.Hash
switch rr.Algorithm {
case DSA, RSASHA1:
hash = crypto.SHA1
case RSASHA256, ECDSAP256SHA256:
hash = crypto.SHA256
case ECDSAP384SHA384:
hash = crypto.SHA384
case RSASHA512:
hash = crypto.SHA512
default:
hash, ok := AlgorithmToHash[rr.Algorithm]
if !ok {
return nil, ErrAlg
}
hasher := hash.New()
// Write SIG rdata
hasher.Write(buf[len(mbuf)+1+2+2+4+2:])
// Write message
hasher.Write(buf[:len(mbuf)])
hashed := hasher.Sum(nil)
sig, err := k.Sign(hashed, rr.Algorithm)
signature, err := sign(k, hasher.Sum(nil), hash, rr.Algorithm)
if err != nil {
return nil, err
}
rr.Signature = toBase64(sig)
rr.Signature = toBase64(signature)
sig := string(signature)
buf = append(buf, sig...)
if len(buf) > int(^uint16(0)) {
return nil, ErrBuf

View File

@ -1,88 +0,0 @@
package dns
import (
"testing"
"time"
)
func TestSIG0(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
m := new(Msg)
m.SetQuestion("example.org.", TypeSOA)
for _, alg := range []uint8{DSA, ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256, RSASHA512} {
algstr := AlgorithmToString[alg]
keyrr := new(KEY)
keyrr.Hdr.Name = algstr + "."
keyrr.Hdr.Rrtype = TypeKEY
keyrr.Hdr.Class = ClassINET
keyrr.Algorithm = alg
keysize := 1024
switch alg {
case ECDSAP256SHA256:
keysize = 256
case ECDSAP384SHA384:
keysize = 384
}
pk, err := keyrr.Generate(keysize)
if err != nil {
t.Errorf("Failed to generate key for “%s”: %v", algstr, err)
continue
}
now := uint32(time.Now().Unix())
sigrr := new(SIG)
sigrr.Hdr.Name = "."
sigrr.Hdr.Rrtype = TypeSIG
sigrr.Hdr.Class = ClassANY
sigrr.Algorithm = alg
sigrr.Expiration = now + 300
sigrr.Inception = now - 300
sigrr.KeyTag = keyrr.KeyTag()
sigrr.SignerName = keyrr.Hdr.Name
mb, err := sigrr.Sign(pk, m)
if err != nil {
t.Errorf("Failed to sign message using “%s”: %v", algstr, err)
continue
}
m := new(Msg)
if err := m.Unpack(mb); err != nil {
t.Errorf("Failed to unpack message signed using “%s”: %v", algstr, err)
continue
}
if len(m.Extra) != 1 {
t.Errorf("Missing SIG for message signed using “%s”", algstr)
continue
}
var sigrrwire *SIG
switch rr := m.Extra[0].(type) {
case *SIG:
sigrrwire = rr
default:
t.Errorf("Expected SIG RR, instead: %v", rr)
continue
}
for _, rr := range []*SIG{sigrr, sigrrwire} {
id := "sigrr"
if rr == sigrrwire {
id = "sigrrwire"
}
if err := rr.Verify(keyrr, mb); err != nil {
t.Errorf("Failed to verify “%s” signed SIG(%s): %v", algstr, id, err)
continue
}
}
mb[13]++
if err := sigrr.Verify(keyrr, mb); err == nil {
t.Errorf("Verify succeeded on an altered message using “%s”", algstr)
continue
}
sigrr.Expiration = 2
sigrr.Inception = 1
mb, _ = sigrr.Sign(pk, m)
if err := sigrr.Verify(keyrr, mb); err == nil {
t.Errorf("Verify succeeded on an expired message using “%s”", algstr)
continue
}
}
}

View File

@ -37,10 +37,6 @@ type TSIG struct {
OtherData string `dns:"size-hex"`
}
func (rr *TSIG) Header() *RR_Header {
return &rr.Hdr
}
// TSIG has no official presentation format, but this will suffice.
func (rr *TSIG) String() string {
@ -58,15 +54,6 @@ func (rr *TSIG) String() string {
return s
}
func (rr *TSIG) len() int {
return rr.Hdr.len() + len(rr.Algorithm) + 1 + 6 +
4 + len(rr.MAC)/2 + 1 + 6 + len(rr.OtherData)/2 + 1
}
func (rr *TSIG) copy() RR {
return &TSIG{*rr.Hdr.copyHeader(), rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData}
}
// The following values must be put in wireformat, so that the MAC can be calculated.
// RFC 2845, section 3.4.2. TSIG Variables.
type tsigWireFmt struct {

View File

@ -91,26 +91,24 @@ const (
TypeLP uint16 = 107
TypeEUI48 uint16 = 108
TypeEUI64 uint16 = 109
TypeURI uint16 = 256
TypeCAA uint16 = 257
TypeTKEY uint16 = 249
TypeTSIG uint16 = 250
// valid Question.Qtype only
TypeIXFR uint16 = 251
TypeAXFR uint16 = 252
TypeMAILB uint16 = 253
TypeMAILA uint16 = 254
TypeANY uint16 = 255
TypeURI uint16 = 256
TypeCAA uint16 = 257
TypeTA uint16 = 32768
TypeDLV uint16 = 32769
TypeReserved uint16 = 65535
// valid Question.Qclass
ClassINET = 1
ClassCSNET = 2
ClassCHAOS = 3
@ -118,8 +116,7 @@ const (
ClassNONE = 254
ClassANY = 255
// Msg.rcode
// Message Response Codes.
RcodeSuccess = 0
RcodeFormatError = 1
RcodeServerFailure = 2
@ -140,8 +137,7 @@ const (
RcodeBadAlg = 21
RcodeBadTrunc = 22 // TSIG
// Opcode, there is no 3
// Message Opcodes. There is no 3.
OpcodeQuery = 0
OpcodeIQuery = 1
OpcodeStatus = 2
@ -149,7 +145,7 @@ const (
OpcodeUpdate = 5
)
// The wire format for the DNS packet header.
// Headers is the wire format for the DNS packet header.
type Header struct {
Id uint16
Bits uint16
@ -178,7 +174,7 @@ const (
LOC_ALTITUDEBASE = 100000
)
// RFC 4398, Section 2.1
// Different Certificate Types, see RFC 4398, Section 2.1
const (
CertPKIX = 1 + iota
CertSPKI
@ -192,6 +188,8 @@ const (
CertOID = 254
)
// CertTypeToString converts the Cert Type to its string representation.
// See RFC 4398 and RFC 6944.
var CertTypeToString = map[uint16]string{
CertPKIX: "PKIX",
CertSPKI: "SPKI",
@ -205,8 +203,11 @@ var CertTypeToString = map[uint16]string{
CertOID: "OID",
}
// StringToCertType is the reverseof CertTypeToString.
var StringToCertType = reverseInt16(CertTypeToString)
//go:generate go run types_generate.go
// Question holds a DNS question. There can be multiple questions in the
// question section of a message. Usually there is just one.
type Question struct {
@ -215,6 +216,10 @@ type Question struct {
Qclass uint16
}
func (q *Question) len() int {
return len(q.Name) + 1 + 2 + 2
}
func (q *Question) String() (s string) {
// prefix with ; (as in dig)
s = ";" + sprintName(q.Name) + "\t"
@ -223,30 +228,21 @@ func (q *Question) String() (s string) {
return s
}
func (q *Question) len() int {
l := len(q.Name) + 1
return l + 4
}
// ANY is a wildcard record. See RFC 1035, Section 3.2.3. ANY
// is named "*" there.
type ANY struct {
Hdr RR_Header
// Does not have any rdata
}
func (rr *ANY) Header() *RR_Header { return &rr.Hdr }
func (rr *ANY) copy() RR { return &ANY{*rr.Hdr.copyHeader()} }
func (rr *ANY) String() string { return rr.Hdr.String() }
func (rr *ANY) len() int { return rr.Hdr.len() }
func (rr *ANY) String() string { return rr.Hdr.String() }
type CNAME struct {
Hdr RR_Header
Target string `dns:"cdomain-name"`
}
func (rr *CNAME) Header() *RR_Header { return &rr.Hdr }
func (rr *CNAME) copy() RR { return &CNAME{*rr.Hdr.copyHeader(), sprintName(rr.Target)} }
func (rr *CNAME) String() string { return rr.Hdr.String() + rr.Target }
func (rr *CNAME) len() int { return rr.Hdr.len() + len(rr.Target) + 1 }
func (rr *CNAME) String() string { return rr.Hdr.String() + sprintName(rr.Target) }
type HINFO struct {
Hdr RR_Header
@ -254,33 +250,23 @@ type HINFO struct {
Os string
}
func (rr *HINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *HINFO) copy() RR { return &HINFO{*rr.Hdr.copyHeader(), rr.Cpu, rr.Os} }
func (rr *HINFO) String() string {
return rr.Hdr.String() + sprintTxt([]string{rr.Cpu, rr.Os})
}
func (rr *HINFO) len() int { return rr.Hdr.len() + len(rr.Cpu) + len(rr.Os) }
type MB struct {
Hdr RR_Header
Mb string `dns:"cdomain-name"`
}
func (rr *MB) Header() *RR_Header { return &rr.Hdr }
func (rr *MB) copy() RR { return &MB{*rr.Hdr.copyHeader(), sprintName(rr.Mb)} }
func (rr *MB) String() string { return rr.Hdr.String() + rr.Mb }
func (rr *MB) len() int { return rr.Hdr.len() + len(rr.Mb) + 1 }
func (rr *MB) String() string { return rr.Hdr.String() + sprintName(rr.Mb) }
type MG struct {
Hdr RR_Header
Mg string `dns:"cdomain-name"`
}
func (rr *MG) Header() *RR_Header { return &rr.Hdr }
func (rr *MG) copy() RR { return &MG{*rr.Hdr.copyHeader(), rr.Mg} }
func (rr *MG) len() int { l := len(rr.Mg) + 1; return rr.Hdr.len() + l }
func (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) }
func (rr *MG) String() string { return rr.Hdr.String() + sprintName(rr.Mg) }
type MINFO struct {
Hdr RR_Header
@ -288,28 +274,15 @@ type MINFO struct {
Email string `dns:"cdomain-name"`
}
func (rr *MINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *MINFO) copy() RR { return &MINFO{*rr.Hdr.copyHeader(), rr.Rmail, rr.Email} }
func (rr *MINFO) String() string {
return rr.Hdr.String() + sprintName(rr.Rmail) + " " + sprintName(rr.Email)
}
func (rr *MINFO) len() int {
l := len(rr.Rmail) + 1
n := len(rr.Email) + 1
return rr.Hdr.len() + l + n
}
type MR struct {
Hdr RR_Header
Mr string `dns:"cdomain-name"`
}
func (rr *MR) Header() *RR_Header { return &rr.Hdr }
func (rr *MR) copy() RR { return &MR{*rr.Hdr.copyHeader(), rr.Mr} }
func (rr *MR) len() int { l := len(rr.Mr) + 1; return rr.Hdr.len() + l }
func (rr *MR) String() string {
return rr.Hdr.String() + sprintName(rr.Mr)
}
@ -319,10 +292,6 @@ type MF struct {
Mf string `dns:"cdomain-name"`
}
func (rr *MF) Header() *RR_Header { return &rr.Hdr }
func (rr *MF) copy() RR { return &MF{*rr.Hdr.copyHeader(), rr.Mf} }
func (rr *MF) len() int { return rr.Hdr.len() + len(rr.Mf) + 1 }
func (rr *MF) String() string {
return rr.Hdr.String() + sprintName(rr.Mf)
}
@ -332,10 +301,6 @@ type MD struct {
Md string `dns:"cdomain-name"`
}
func (rr *MD) Header() *RR_Header { return &rr.Hdr }
func (rr *MD) copy() RR { return &MD{*rr.Hdr.copyHeader(), rr.Md} }
func (rr *MD) len() int { return rr.Hdr.len() + len(rr.Md) + 1 }
func (rr *MD) String() string {
return rr.Hdr.String() + sprintName(rr.Md)
}
@ -346,10 +311,6 @@ type MX struct {
Mx string `dns:"cdomain-name"`
}
func (rr *MX) Header() *RR_Header { return &rr.Hdr }
func (rr *MX) copy() RR { return &MX{*rr.Hdr.copyHeader(), rr.Preference, rr.Mx} }
func (rr *MX) len() int { l := len(rr.Mx) + 1; return rr.Hdr.len() + l + 2 }
func (rr *MX) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Mx)
}
@ -360,10 +321,6 @@ type AFSDB struct {
Hostname string `dns:"cdomain-name"`
}
func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr }
func (rr *AFSDB) copy() RR { return &AFSDB{*rr.Hdr.copyHeader(), rr.Subtype, rr.Hostname} }
func (rr *AFSDB) len() int { l := len(rr.Hostname) + 1; return rr.Hdr.len() + l + 2 }
func (rr *AFSDB) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Subtype)) + " " + sprintName(rr.Hostname)
}
@ -373,10 +330,6 @@ type X25 struct {
PSDNAddress string
}
func (rr *X25) Header() *RR_Header { return &rr.Hdr }
func (rr *X25) copy() RR { return &X25{*rr.Hdr.copyHeader(), rr.PSDNAddress} }
func (rr *X25) len() int { return rr.Hdr.len() + len(rr.PSDNAddress) + 1 }
func (rr *X25) String() string {
return rr.Hdr.String() + rr.PSDNAddress
}
@ -387,10 +340,6 @@ type RT struct {
Host string `dns:"cdomain-name"`
}
func (rr *RT) Header() *RR_Header { return &rr.Hdr }
func (rr *RT) copy() RR { return &RT{*rr.Hdr.copyHeader(), rr.Preference, rr.Host} }
func (rr *RT) len() int { l := len(rr.Host) + 1; return rr.Hdr.len() + l + 2 }
func (rr *RT) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Host)
}
@ -400,10 +349,6 @@ type NS struct {
Ns string `dns:"cdomain-name"`
}
func (rr *NS) Header() *RR_Header { return &rr.Hdr }
func (rr *NS) len() int { l := len(rr.Ns) + 1; return rr.Hdr.len() + l }
func (rr *NS) copy() RR { return &NS{*rr.Hdr.copyHeader(), rr.Ns} }
func (rr *NS) String() string {
return rr.Hdr.String() + sprintName(rr.Ns)
}
@ -413,10 +358,6 @@ type PTR struct {
Ptr string `dns:"cdomain-name"`
}
func (rr *PTR) Header() *RR_Header { return &rr.Hdr }
func (rr *PTR) copy() RR { return &PTR{*rr.Hdr.copyHeader(), rr.Ptr} }
func (rr *PTR) len() int { l := len(rr.Ptr) + 1; return rr.Hdr.len() + l }
func (rr *PTR) String() string {
return rr.Hdr.String() + sprintName(rr.Ptr)
}
@ -427,10 +368,6 @@ type RP struct {
Txt string `dns:"domain-name"`
}
func (rr *RP) Header() *RR_Header { return &rr.Hdr }
func (rr *RP) copy() RR { return &RP{*rr.Hdr.copyHeader(), rr.Mbox, rr.Txt} }
func (rr *RP) len() int { return rr.Hdr.len() + len(rr.Mbox) + 1 + len(rr.Txt) + 1 }
func (rr *RP) String() string {
return rr.Hdr.String() + rr.Mbox + " " + sprintTxt([]string{rr.Txt})
}
@ -446,11 +383,6 @@ type SOA struct {
Minttl uint32
}
func (rr *SOA) Header() *RR_Header { return &rr.Hdr }
func (rr *SOA) copy() RR {
return &SOA{*rr.Hdr.copyHeader(), rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl}
}
func (rr *SOA) String() string {
return rr.Hdr.String() + sprintName(rr.Ns) + " " + sprintName(rr.Mbox) +
" " + strconv.FormatInt(int64(rr.Serial), 10) +
@ -460,24 +392,11 @@ func (rr *SOA) String() string {
" " + strconv.FormatInt(int64(rr.Minttl), 10)
}
func (rr *SOA) len() int {
l := len(rr.Ns) + 1
n := len(rr.Mbox) + 1
return rr.Hdr.len() + l + n + 20
}
type TXT struct {
Hdr RR_Header
Txt []string `dns:"txt"`
}
func (rr *TXT) Header() *RR_Header { return &rr.Hdr }
func (rr *TXT) copy() RR {
cp := make([]string, len(rr.Txt), cap(rr.Txt))
copy(cp, rr.Txt)
return &TXT{*rr.Hdr.copyHeader(), cp}
}
func (rr *TXT) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
func sprintName(s string) string {
@ -620,36 +539,13 @@ func nextByte(b []byte, offset int) (byte, int) {
}
}
func (rr *TXT) len() int {
l := rr.Hdr.len()
for _, t := range rr.Txt {
l += len(t) + 1
}
return l
}
type SPF struct {
Hdr RR_Header
Txt []string `dns:"txt"`
}
func (rr *SPF) Header() *RR_Header { return &rr.Hdr }
func (rr *SPF) copy() RR {
cp := make([]string, len(rr.Txt), cap(rr.Txt))
copy(cp, rr.Txt)
return &SPF{*rr.Hdr.copyHeader(), cp}
}
func (rr *SPF) String() string { return rr.Hdr.String() + sprintTxt(rr.Txt) }
func (rr *SPF) len() int {
l := rr.Hdr.len()
for _, t := range rr.Txt {
l += len(t) + 1
}
return l
}
type SRV struct {
Hdr RR_Header
Priority uint16
@ -658,12 +554,6 @@ type SRV struct {
Target string `dns:"domain-name"`
}
func (rr *SRV) Header() *RR_Header { return &rr.Hdr }
func (rr *SRV) len() int { l := len(rr.Target) + 1; return rr.Hdr.len() + l + 6 }
func (rr *SRV) copy() RR {
return &SRV{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Port, rr.Target}
}
func (rr *SRV) String() string {
return rr.Hdr.String() +
strconv.Itoa(int(rr.Priority)) + " " +
@ -681,11 +571,6 @@ type NAPTR struct {
Replacement string `dns:"domain-name"`
}
func (rr *NAPTR) Header() *RR_Header { return &rr.Hdr }
func (rr *NAPTR) copy() RR {
return &NAPTR{*rr.Hdr.copyHeader(), rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement}
}
func (rr *NAPTR) String() string {
return rr.Hdr.String() +
strconv.Itoa(int(rr.Order)) + " " +
@ -696,12 +581,7 @@ func (rr *NAPTR) String() string {
rr.Replacement
}
func (rr *NAPTR) len() int {
return rr.Hdr.len() + 4 + len(rr.Flags) + 1 + len(rr.Service) + 1 +
len(rr.Regexp) + 1 + len(rr.Replacement) + 1
}
// See RFC 4398.
// The CERT resource record, see RFC 4398.
type CERT struct {
Hdr RR_Header
Type uint16
@ -710,11 +590,6 @@ type CERT struct {
Certificate string `dns:"base64"`
}
func (rr *CERT) Header() *RR_Header { return &rr.Hdr }
func (rr *CERT) copy() RR {
return &CERT{*rr.Hdr.copyHeader(), rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate}
}
func (rr *CERT) String() string {
var (
ok bool
@ -732,21 +607,12 @@ func (rr *CERT) String() string {
" " + rr.Certificate
}
func (rr *CERT) len() int {
return rr.Hdr.len() + 5 +
base64.StdEncoding.DecodedLen(len(rr.Certificate))
}
// See RFC 2672.
// The DNAME resource record, see RFC 2672.
type DNAME struct {
Hdr RR_Header
Target string `dns:"domain-name"`
}
func (rr *DNAME) Header() *RR_Header { return &rr.Hdr }
func (rr *DNAME) copy() RR { return &DNAME{*rr.Hdr.copyHeader(), rr.Target} }
func (rr *DNAME) len() int { l := len(rr.Target) + 1; return rr.Hdr.len() + l }
func (rr *DNAME) String() string {
return rr.Hdr.String() + sprintName(rr.Target)
}
@ -756,10 +622,6 @@ type A struct {
A net.IP `dns:"a"`
}
func (rr *A) Header() *RR_Header { return &rr.Hdr }
func (rr *A) copy() RR { return &A{*rr.Hdr.copyHeader(), copyIP(rr.A)} }
func (rr *A) len() int { return rr.Hdr.len() + net.IPv4len }
func (rr *A) String() string {
if rr.A == nil {
return rr.Hdr.String()
@ -772,10 +634,6 @@ type AAAA struct {
AAAA net.IP `dns:"aaaa"`
}
func (rr *AAAA) Header() *RR_Header { return &rr.Hdr }
func (rr *AAAA) copy() RR { return &AAAA{*rr.Hdr.copyHeader(), copyIP(rr.AAAA)} }
func (rr *AAAA) len() int { return rr.Hdr.len() + net.IPv6len }
func (rr *AAAA) String() string {
if rr.AAAA == nil {
return rr.Hdr.String()
@ -790,12 +648,9 @@ type PX struct {
Mapx400 string `dns:"domain-name"`
}
func (rr *PX) Header() *RR_Header { return &rr.Hdr }
func (rr *PX) copy() RR { return &PX{*rr.Hdr.copyHeader(), rr.Preference, rr.Map822, rr.Mapx400} }
func (rr *PX) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Map822) + " " + sprintName(rr.Mapx400)
}
func (rr *PX) len() int { return rr.Hdr.len() + 2 + len(rr.Map822) + 1 + len(rr.Mapx400) + 1 }
type GPOS struct {
Hdr RR_Header
@ -804,11 +659,6 @@ type GPOS struct {
Altitude string
}
func (rr *GPOS) Header() *RR_Header { return &rr.Hdr }
func (rr *GPOS) copy() RR { return &GPOS{*rr.Hdr.copyHeader(), rr.Longitude, rr.Latitude, rr.Altitude} }
func (rr *GPOS) len() int {
return rr.Hdr.len() + len(rr.Longitude) + len(rr.Latitude) + len(rr.Altitude) + 3
}
func (rr *GPOS) String() string {
return rr.Hdr.String() + rr.Longitude + " " + rr.Latitude + " " + rr.Altitude
}
@ -824,12 +674,6 @@ type LOC struct {
Altitude uint32
}
func (rr *LOC) Header() *RR_Header { return &rr.Hdr }
func (rr *LOC) len() int { return rr.Hdr.len() + 4 + 12 }
func (rr *LOC) copy() RR {
return &LOC{*rr.Hdr.copyHeader(), rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude}
}
// cmToM takes a cm value expressed in RFC1876 SIZE mantissa/exponent
// format and returns a string in m (two decimals for the cm)
func cmToM(m, e uint8) string {
@ -913,11 +757,6 @@ type RRSIG struct {
Signature string `dns:"base64"`
}
func (rr *RRSIG) Header() *RR_Header { return &rr.Hdr }
func (rr *RRSIG) copy() RR {
return &RRSIG{*rr.Hdr.copyHeader(), rr.TypeCovered, rr.Algorithm, rr.Labels, rr.OrigTtl, rr.Expiration, rr.Inception, rr.KeyTag, rr.SignerName, rr.Signature}
}
func (rr *RRSIG) String() string {
s := rr.Hdr.String()
s += Type(rr.TypeCovered).String()
@ -932,24 +771,12 @@ func (rr *RRSIG) String() string {
return s
}
func (rr *RRSIG) len() int {
return rr.Hdr.len() + len(rr.SignerName) + 1 +
base64.StdEncoding.DecodedLen(len(rr.Signature)) + 18
}
type NSEC struct {
Hdr RR_Header
NextDomain string `dns:"domain-name"`
TypeBitMap []uint16 `dns:"nsec"`
}
func (rr *NSEC) Header() *RR_Header { return &rr.Hdr }
func (rr *NSEC) copy() RR {
cp := make([]uint16, len(rr.TypeBitMap), cap(rr.TypeBitMap))
copy(cp, rr.TypeBitMap)
return &NSEC{*rr.Hdr.copyHeader(), rr.NextDomain, cp}
}
func (rr *NSEC) String() string {
s := rr.Hdr.String() + sprintName(rr.NextDomain)
for i := 0; i < len(rr.TypeBitMap); i++ {
@ -987,12 +814,6 @@ type DS struct {
Digest string `dns:"hex"`
}
func (rr *DS) Header() *RR_Header { return &rr.Hdr }
func (rr *DS) len() int { return rr.Hdr.len() + 4 + len(rr.Digest)/2 }
func (rr *DS) copy() RR {
return &DS{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
}
func (rr *DS) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) +
" " + strconv.Itoa(int(rr.Algorithm)) +
@ -1006,10 +827,6 @@ type KX struct {
Exchanger string `dns:"domain-name"`
}
func (rr *KX) Header() *RR_Header { return &rr.Hdr }
func (rr *KX) len() int { return rr.Hdr.len() + 2 + len(rr.Exchanger) + 1 }
func (rr *KX) copy() RR { return &KX{*rr.Hdr.copyHeader(), rr.Preference, rr.Exchanger} }
func (rr *KX) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) +
" " + sprintName(rr.Exchanger)
@ -1023,12 +840,6 @@ type TA struct {
Digest string `dns:"hex"`
}
func (rr *TA) Header() *RR_Header { return &rr.Hdr }
func (rr *TA) len() int { return rr.Hdr.len() + 4 + len(rr.Digest)/2 }
func (rr *TA) copy() RR {
return &TA{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
}
func (rr *TA) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.KeyTag)) +
" " + strconv.Itoa(int(rr.Algorithm)) +
@ -1042,10 +853,6 @@ type TALINK struct {
NextName string `dns:"domain-name"`
}
func (rr *TALINK) Header() *RR_Header { return &rr.Hdr }
func (rr *TALINK) copy() RR { return &TALINK{*rr.Hdr.copyHeader(), rr.PreviousName, rr.NextName} }
func (rr *TALINK) len() int { return rr.Hdr.len() + len(rr.PreviousName) + len(rr.NextName) + 2 }
func (rr *TALINK) String() string {
return rr.Hdr.String() +
sprintName(rr.PreviousName) + " " + sprintName(rr.NextName)
@ -1058,12 +865,6 @@ type SSHFP struct {
FingerPrint string `dns:"hex"`
}
func (rr *SSHFP) Header() *RR_Header { return &rr.Hdr }
func (rr *SSHFP) len() int { return rr.Hdr.len() + 2 + len(rr.FingerPrint)/2 }
func (rr *SSHFP) copy() RR {
return &SSHFP{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Type, rr.FingerPrint}
}
func (rr *SSHFP) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Algorithm)) +
" " + strconv.Itoa(int(rr.Type)) +
@ -1084,11 +885,6 @@ type IPSECKEY struct {
PublicKey string `dns:"base64"`
}
func (rr *IPSECKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *IPSECKEY) copy() RR {
return &IPSECKEY{*rr.Hdr.copyHeader(), rr.Precedence, rr.GatewayType, rr.Algorithm, rr.GatewayA, rr.GatewayAAAA, rr.GatewayName, rr.PublicKey}
}
func (rr *IPSECKEY) String() string {
s := rr.Hdr.String() + strconv.Itoa(int(rr.Precedence)) +
" " + strconv.Itoa(int(rr.GatewayType)) +
@ -1142,14 +938,6 @@ type DNSKEY struct {
PublicKey string `dns:"base64"`
}
func (rr *DNSKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *DNSKEY) len() int {
return rr.Hdr.len() + 4 + base64.StdEncoding.DecodedLen(len(rr.PublicKey))
}
func (rr *DNSKEY) copy() RR {
return &DNSKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
}
func (rr *DNSKEY) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) +
" " + strconv.Itoa(int(rr.Protocol)) +
@ -1165,12 +953,6 @@ type RKEY struct {
PublicKey string `dns:"base64"`
}
func (rr *RKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *RKEY) len() int { return rr.Hdr.len() + 4 + base64.StdEncoding.DecodedLen(len(rr.PublicKey)) }
func (rr *RKEY) copy() RR {
return &RKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
}
func (rr *RKEY) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Flags)) +
" " + strconv.Itoa(int(rr.Protocol)) +
@ -1183,10 +965,7 @@ type NSAPPTR struct {
Ptr string `dns:"domain-name"`
}
func (rr *NSAPPTR) Header() *RR_Header { return &rr.Hdr }
func (rr *NSAPPTR) copy() RR { return &NSAPPTR{*rr.Hdr.copyHeader(), rr.Ptr} }
func (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) }
func (rr *NSAPPTR) len() int { return rr.Hdr.len() + len(rr.Ptr) }
func (rr *NSAPPTR) String() string { return rr.Hdr.String() + sprintName(rr.Ptr) }
type NSEC3 struct {
Hdr RR_Header
@ -1200,13 +979,6 @@ type NSEC3 struct {
TypeBitMap []uint16 `dns:"nsec"`
}
func (rr *NSEC3) Header() *RR_Header { return &rr.Hdr }
func (rr *NSEC3) copy() RR {
cp := make([]uint16, len(rr.TypeBitMap), cap(rr.TypeBitMap))
copy(cp, rr.TypeBitMap)
return &NSEC3{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, cp}
}
func (rr *NSEC3) String() string {
s := rr.Hdr.String()
s += strconv.Itoa(int(rr.Hash)) +
@ -1242,12 +1014,6 @@ type NSEC3PARAM struct {
Salt string `dns:"hex"`
}
func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr }
func (rr *NSEC3PARAM) len() int { return rr.Hdr.len() + 2 + 4 + 1 + len(rr.Salt)/2 }
func (rr *NSEC3PARAM) copy() RR {
return &NSEC3PARAM{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt}
}
func (rr *NSEC3PARAM) String() string {
s := rr.Hdr.String()
s += strconv.Itoa(int(rr.Hash)) +
@ -1270,31 +1036,17 @@ type TKEY struct {
OtherData string
}
func (rr *TKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *TKEY) copy() RR {
return &TKEY{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Inception, rr.Expiration, rr.Mode, rr.Error, rr.KeySize, rr.Key, rr.OtherLen, rr.OtherData}
}
func (rr *TKEY) String() string {
// It has no presentation format
return ""
}
func (rr *TKEY) len() int {
return rr.Hdr.len() + len(rr.Algorithm) + 1 + 4 + 4 + 6 +
len(rr.Key) + 2 + len(rr.OtherData)
}
// RFC3597 represents an unknown/generic RR.
type RFC3597 struct {
Hdr RR_Header
Rdata string `dns:"hex"`
}
func (rr *RFC3597) Header() *RR_Header { return &rr.Hdr }
func (rr *RFC3597) copy() RR { return &RFC3597{*rr.Hdr.copyHeader(), rr.Rdata} }
func (rr *RFC3597) len() int { return rr.Hdr.len() + len(rr.Rdata)/2 + 2 }
func (rr *RFC3597) String() string {
// Let's call it a hack
s := rfc3597Header(rr.Hdr)
@ -1320,9 +1072,6 @@ type URI struct {
Target string `dns:"octet"`
}
func (rr *URI) Header() *RR_Header { return &rr.Hdr }
func (rr *URI) copy() RR { return &URI{*rr.Hdr.copyHeader(), rr.Weight, rr.Priority, rr.Target} }
func (rr *URI) len() int { return rr.Hdr.len() + 4 + len(rr.Target) }
func (rr *URI) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Priority)) +
" " + strconv.Itoa(int(rr.Weight)) + " " + sprintTxtOctet(rr.Target)
@ -1333,10 +1082,7 @@ type DHCID struct {
Digest string `dns:"base64"`
}
func (rr *DHCID) Header() *RR_Header { return &rr.Hdr }
func (rr *DHCID) copy() RR { return &DHCID{*rr.Hdr.copyHeader(), rr.Digest} }
func (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest }
func (rr *DHCID) len() int { return rr.Hdr.len() + base64.StdEncoding.DecodedLen(len(rr.Digest)) }
func (rr *DHCID) String() string { return rr.Hdr.String() + rr.Digest }
type TLSA struct {
Hdr RR_Header
@ -1346,13 +1092,6 @@ type TLSA struct {
Certificate string `dns:"hex"`
}
func (rr *TLSA) Header() *RR_Header { return &rr.Hdr }
func (rr *TLSA) len() int { return rr.Hdr.len() + 3 + len(rr.Certificate)/2 }
func (rr *TLSA) copy() RR {
return &TLSA{*rr.Hdr.copyHeader(), rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
}
func (rr *TLSA) String() string {
return rr.Hdr.String() +
strconv.Itoa(int(rr.Usage)) +
@ -1371,13 +1110,6 @@ type HIP struct {
RendezvousServers []string `dns:"domain-name"`
}
func (rr *HIP) Header() *RR_Header { return &rr.Hdr }
func (rr *HIP) copy() RR {
cp := make([]string, len(rr.RendezvousServers), cap(rr.RendezvousServers))
copy(cp, rr.RendezvousServers)
return &HIP{*rr.Hdr.copyHeader(), rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, cp}
}
func (rr *HIP) String() string {
s := rr.Hdr.String() +
strconv.Itoa(int(rr.PublicKeyAlgorithm)) +
@ -1389,38 +1121,13 @@ func (rr *HIP) String() string {
return s
}
func (rr *HIP) len() int {
l := rr.Hdr.len() + 4 +
len(rr.Hit)/2 +
base64.StdEncoding.DecodedLen(len(rr.PublicKey))
for _, d := range rr.RendezvousServers {
l += len(d) + 1
}
return l
}
type NINFO struct {
Hdr RR_Header
ZSData []string `dns:"txt"`
}
func (rr *NINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *NINFO) copy() RR {
cp := make([]string, len(rr.ZSData), cap(rr.ZSData))
copy(cp, rr.ZSData)
return &NINFO{*rr.Hdr.copyHeader(), cp}
}
func (rr *NINFO) String() string { return rr.Hdr.String() + sprintTxt(rr.ZSData) }
func (rr *NINFO) len() int {
l := rr.Hdr.len()
for _, t := range rr.ZSData {
l += len(t) + 1
}
return l
}
type WKS struct {
Hdr RR_Header
Address net.IP `dns:"a"`
@ -1428,13 +1135,9 @@ type WKS struct {
BitMap []uint16 `dns:"wks"`
}
func (rr *WKS) Header() *RR_Header { return &rr.Hdr }
func (rr *WKS) len() int { return rr.Hdr.len() + net.IPv4len + 1 }
func (rr *WKS) copy() RR {
cp := make([]uint16, len(rr.BitMap), cap(rr.BitMap))
copy(cp, rr.BitMap)
return &WKS{*rr.Hdr.copyHeader(), copyIP(rr.Address), rr.Protocol, cp}
func (rr *WKS) len() int {
// TODO: this is missing something...
return rr.Hdr.len() + net.IPv4len + 1
}
func (rr *WKS) String() (s string) {
@ -1456,10 +1159,6 @@ type NID struct {
NodeID uint64
}
func (rr *NID) Header() *RR_Header { return &rr.Hdr }
func (rr *NID) copy() RR { return &NID{*rr.Hdr.copyHeader(), rr.Preference, rr.NodeID} }
func (rr *NID) len() int { return rr.Hdr.len() + 2 + 8 }
func (rr *NID) String() string {
s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
node := fmt.Sprintf("%0.16x", rr.NodeID)
@ -1473,10 +1172,6 @@ type L32 struct {
Locator32 net.IP `dns:"a"`
}
func (rr *L32) Header() *RR_Header { return &rr.Hdr }
func (rr *L32) copy() RR { return &L32{*rr.Hdr.copyHeader(), rr.Preference, copyIP(rr.Locator32)} }
func (rr *L32) len() int { return rr.Hdr.len() + net.IPv4len }
func (rr *L32) String() string {
if rr.Locator32 == nil {
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
@ -1491,10 +1186,6 @@ type L64 struct {
Locator64 uint64
}
func (rr *L64) Header() *RR_Header { return &rr.Hdr }
func (rr *L64) copy() RR { return &L64{*rr.Hdr.copyHeader(), rr.Preference, rr.Locator64} }
func (rr *L64) len() int { return rr.Hdr.len() + 2 + 8 }
func (rr *L64) String() string {
s := rr.Hdr.String() + strconv.Itoa(int(rr.Preference))
node := fmt.Sprintf("%0.16X", rr.Locator64)
@ -1508,10 +1199,6 @@ type LP struct {
Fqdn string `dns:"domain-name"`
}
func (rr *LP) Header() *RR_Header { return &rr.Hdr }
func (rr *LP) copy() RR { return &LP{*rr.Hdr.copyHeader(), rr.Preference, rr.Fqdn} }
func (rr *LP) len() int { return rr.Hdr.len() + 2 + len(rr.Fqdn) + 1 }
func (rr *LP) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Preference)) + " " + sprintName(rr.Fqdn)
}
@ -1521,20 +1208,14 @@ type EUI48 struct {
Address uint64 `dns:"uint48"`
}
func (rr *EUI48) Header() *RR_Header { return &rr.Hdr }
func (rr *EUI48) copy() RR { return &EUI48{*rr.Hdr.copyHeader(), rr.Address} }
func (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) }
func (rr *EUI48) len() int { return rr.Hdr.len() + 6 }
func (rr *EUI48) String() string { return rr.Hdr.String() + euiToString(rr.Address, 48) }
type EUI64 struct {
Hdr RR_Header
Address uint64
}
func (rr *EUI64) Header() *RR_Header { return &rr.Hdr }
func (rr *EUI64) copy() RR { return &EUI64{*rr.Hdr.copyHeader(), rr.Address} }
func (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) }
func (rr *EUI64) len() int { return rr.Hdr.len() + 8 }
func (rr *EUI64) String() string { return rr.Hdr.String() + euiToString(rr.Address, 64) }
type CAA struct {
Hdr RR_Header
@ -1543,9 +1224,6 @@ type CAA struct {
Value string `dns:"octet"`
}
func (rr *CAA) Header() *RR_Header { return &rr.Hdr }
func (rr *CAA) copy() RR { return &CAA{*rr.Hdr.copyHeader(), rr.Flag, rr.Tag, rr.Value} }
func (rr *CAA) len() int { return rr.Hdr.len() + 2 + len(rr.Tag) + len(rr.Value) }
func (rr *CAA) String() string {
return rr.Hdr.String() + strconv.Itoa(int(rr.Flag)) + " " + rr.Tag + " " + sprintTxtOctet(rr.Value)
}
@ -1555,62 +1233,42 @@ type UID struct {
Uid uint32
}
func (rr *UID) Header() *RR_Header { return &rr.Hdr }
func (rr *UID) copy() RR { return &UID{*rr.Hdr.copyHeader(), rr.Uid} }
func (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) }
func (rr *UID) len() int { return rr.Hdr.len() + 4 }
func (rr *UID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Uid), 10) }
type GID struct {
Hdr RR_Header
Gid uint32
}
func (rr *GID) Header() *RR_Header { return &rr.Hdr }
func (rr *GID) copy() RR { return &GID{*rr.Hdr.copyHeader(), rr.Gid} }
func (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) }
func (rr *GID) len() int { return rr.Hdr.len() + 4 }
func (rr *GID) String() string { return rr.Hdr.String() + strconv.FormatInt(int64(rr.Gid), 10) }
type UINFO struct {
Hdr RR_Header
Uinfo string
}
func (rr *UINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *UINFO) copy() RR { return &UINFO{*rr.Hdr.copyHeader(), rr.Uinfo} }
func (rr *UINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Uinfo}) }
func (rr *UINFO) len() int { return rr.Hdr.len() + len(rr.Uinfo) + 1 }
func (rr *UINFO) String() string { return rr.Hdr.String() + sprintTxt([]string{rr.Uinfo}) }
type EID struct {
Hdr RR_Header
Endpoint string `dns:"hex"`
}
func (rr *EID) Header() *RR_Header { return &rr.Hdr }
func (rr *EID) copy() RR { return &EID{*rr.Hdr.copyHeader(), rr.Endpoint} }
func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) }
func (rr *EID) len() int { return rr.Hdr.len() + len(rr.Endpoint)/2 }
func (rr *EID) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Endpoint) }
type NIMLOC struct {
Hdr RR_Header
Locator string `dns:"hex"`
}
func (rr *NIMLOC) Header() *RR_Header { return &rr.Hdr }
func (rr *NIMLOC) copy() RR { return &NIMLOC{*rr.Hdr.copyHeader(), rr.Locator} }
func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) }
func (rr *NIMLOC) len() int { return rr.Hdr.len() + len(rr.Locator)/2 }
func (rr *NIMLOC) String() string { return rr.Hdr.String() + strings.ToUpper(rr.Locator) }
type OPENPGPKEY struct {
Hdr RR_Header
PublicKey string `dns:"base64"`
}
func (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *OPENPGPKEY) copy() RR { return &OPENPGPKEY{*rr.Hdr.copyHeader(), rr.PublicKey} }
func (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey }
func (rr *OPENPGPKEY) len() int {
return rr.Hdr.len() + base64.StdEncoding.DecodedLen(len(rr.PublicKey))
}
func (rr *OPENPGPKEY) String() string { return rr.Hdr.String() + rr.PublicKey }
// TimeToString translates the RRSIG's incep. and expir. times to the
// string representation used when printing the record.
@ -1668,73 +1326,3 @@ func copyIP(ip net.IP) net.IP {
copy(p, ip)
return p
}
// Map of constructors for each RR type.
var typeToRR = map[uint16]func() RR{
TypeA: func() RR { return new(A) },
TypeAAAA: func() RR { return new(AAAA) },
TypeAFSDB: func() RR { return new(AFSDB) },
TypeCAA: func() RR { return new(CAA) },
TypeCDS: func() RR { return new(CDS) },
TypeCERT: func() RR { return new(CERT) },
TypeCNAME: func() RR { return new(CNAME) },
TypeDHCID: func() RR { return new(DHCID) },
TypeDLV: func() RR { return new(DLV) },
TypeDNAME: func() RR { return new(DNAME) },
TypeKEY: func() RR { return new(KEY) },
TypeDNSKEY: func() RR { return new(DNSKEY) },
TypeDS: func() RR { return new(DS) },
TypeEUI48: func() RR { return new(EUI48) },
TypeEUI64: func() RR { return new(EUI64) },
TypeGID: func() RR { return new(GID) },
TypeGPOS: func() RR { return new(GPOS) },
TypeEID: func() RR { return new(EID) },
TypeHINFO: func() RR { return new(HINFO) },
TypeHIP: func() RR { return new(HIP) },
TypeIPSECKEY: func() RR { return new(IPSECKEY) },
TypeKX: func() RR { return new(KX) },
TypeL32: func() RR { return new(L32) },
TypeL64: func() RR { return new(L64) },
TypeLOC: func() RR { return new(LOC) },
TypeLP: func() RR { return new(LP) },
TypeMB: func() RR { return new(MB) },
TypeMD: func() RR { return new(MD) },
TypeMF: func() RR { return new(MF) },
TypeMG: func() RR { return new(MG) },
TypeMINFO: func() RR { return new(MINFO) },
TypeMR: func() RR { return new(MR) },
TypeMX: func() RR { return new(MX) },
TypeNAPTR: func() RR { return new(NAPTR) },
TypeNID: func() RR { return new(NID) },
TypeNINFO: func() RR { return new(NINFO) },
TypeNIMLOC: func() RR { return new(NIMLOC) },
TypeNS: func() RR { return new(NS) },
TypeNSAPPTR: func() RR { return new(NSAPPTR) },
TypeNSEC3: func() RR { return new(NSEC3) },
TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) },
TypeNSEC: func() RR { return new(NSEC) },
TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) },
TypeOPT: func() RR { return new(OPT) },
TypePTR: func() RR { return new(PTR) },
TypeRKEY: func() RR { return new(RKEY) },
TypeRP: func() RR { return new(RP) },
TypePX: func() RR { return new(PX) },
TypeSIG: func() RR { return new(SIG) },
TypeRRSIG: func() RR { return new(RRSIG) },
TypeRT: func() RR { return new(RT) },
TypeSOA: func() RR { return new(SOA) },
TypeSPF: func() RR { return new(SPF) },
TypeSRV: func() RR { return new(SRV) },
TypeSSHFP: func() RR { return new(SSHFP) },
TypeTA: func() RR { return new(TA) },
TypeTALINK: func() RR { return new(TALINK) },
TypeTKEY: func() RR { return new(TKEY) },
TypeTLSA: func() RR { return new(TLSA) },
TypeTSIG: func() RR { return new(TSIG) },
TypeTXT: func() RR { return new(TXT) },
TypeUID: func() RR { return new(UID) },
TypeUINFO: func() RR { return new(UINFO) },
TypeURI: func() RR { return new(URI) },
TypeWKS: func() RR { return new(WKS) },
TypeX25: func() RR { return new(X25) },
}

View File

@ -0,0 +1,266 @@
//+build ignore
// types_generate.go is meant to run with go generate. It will use
// go/{importer,types} to track down all the RR struct types. Then for each type
// it will generate conversion tables (TypeToRR and TypeToString) and banal
// methods (len, Header, copy) based on the struct tags. The generated source is
// written to ztypes.go, and is meant to be checked into git.
package main
import (
"bytes"
"fmt"
"go/format"
"go/importer"
"go/types"
"log"
"os"
"strings"
"text/template"
)
var skipLen = map[string]struct{}{
"NSEC": struct{}{},
"NSEC3": struct{}{},
"OPT": struct{}{},
"WKS": struct{}{},
"IPSECKEY": struct{}{},
}
var packageHdr = `
// *** DO NOT MODIFY ***
// AUTOGENERATED BY go generate
package dns
import (
"encoding/base64"
"net"
)
`
var TypeToRR = template.Must(template.New("TypeToRR").Parse(`
// TypeToRR is a map of constructors for each RR type.
var TypeToRR = map[uint16]func() RR{
{{range .}}{{if ne . "RFC3597"}} Type{{.}}: func() RR { return new({{.}}) },
{{end}}{{end}} }
`))
var typeToString = template.Must(template.New("typeToString").Parse(`
// TypeToString is a map of strings for each RR type.
var TypeToString = map[uint16]string{
{{range .}}{{if ne . "NSAPPTR"}} Type{{.}}: "{{.}}",
{{end}}{{end}} TypeNSAPPTR: "NSAP-PTR",
}
`))
var headerFunc = template.Must(template.New("headerFunc").Parse(`
// Header() functions
{{range .}} func (rr *{{.}}) Header() *RR_Header { return &rr.Hdr }
{{end}}
`))
// getTypeStruct will take a type and the package scope, and return the
// (innermost) struct if the type is considered a RR type (currently defined as
// those structs beginning with a RR_Header, could be redefined as implementing
// the RR interface). The bool return value indicates if embedded structs were
// resolved.
func getTypeStruct(t types.Type, scope *types.Scope) (*types.Struct, bool) {
st, ok := t.Underlying().(*types.Struct)
if !ok {
return nil, false
}
if st.Field(0).Type() == scope.Lookup("RR_Header").Type() {
return st, false
}
if st.Field(0).Anonymous() {
st, _ := getTypeStruct(st.Field(0).Type(), scope)
return st, true
}
return nil, false
}
func main() {
// Import and type-check the package
pkg, err := importer.Default().Import("github.com/miekg/dns")
fatalIfErr(err)
scope := pkg.Scope()
// Collect constants like TypeX
var numberedTypes []string
for _, name := range scope.Names() {
o := scope.Lookup(name)
if o == nil || !o.Exported() {
continue
}
b, ok := o.Type().(*types.Basic)
if !ok || b.Kind() != types.Uint16 {
continue
}
if !strings.HasPrefix(o.Name(), "Type") {
continue
}
name := strings.TrimPrefix(o.Name(), "Type")
if name == "PrivateRR" {
continue
}
numberedTypes = append(numberedTypes, name)
}
// Collect actual types (*X)
var namedTypes []string
for _, name := range scope.Names() {
o := scope.Lookup(name)
if o == nil || !o.Exported() {
continue
}
if st, _ := getTypeStruct(o.Type(), scope); st == nil {
continue
}
if name == "PrivateRR" {
continue
}
// Check if corresponding TypeX exists
if scope.Lookup("Type"+o.Name()) == nil && o.Name() != "RFC3597" {
log.Fatalf("Constant Type%s does not exist.", o.Name())
}
namedTypes = append(namedTypes, o.Name())
}
b := &bytes.Buffer{}
b.WriteString(packageHdr)
// Generate TypeToRR
fatalIfErr(TypeToRR.Execute(b, namedTypes))
// Generate typeToString
fatalIfErr(typeToString.Execute(b, numberedTypes))
// Generate headerFunc
fatalIfErr(headerFunc.Execute(b, namedTypes))
// Generate len()
fmt.Fprint(b, "// len() functions\n")
for _, name := range namedTypes {
if _, ok := skipLen[name]; ok {
continue
}
o := scope.Lookup(name)
st, isEmbedded := getTypeStruct(o.Type(), scope)
if isEmbedded {
continue
}
fmt.Fprintf(b, "func (rr *%s) len() int {\n", name)
fmt.Fprintf(b, "l := rr.Hdr.len()\n")
for i := 1; i < st.NumFields(); i++ {
o := func(s string) { fmt.Fprintf(b, s, st.Field(i).Name()) }
if _, ok := st.Field(i).Type().(*types.Slice); ok {
switch st.Tag(i) {
case `dns:"-"`:
// ignored
case `dns:"cdomain-name"`, `dns:"domain-name"`, `dns:"txt"`:
o("for _, x := range rr.%s { l += len(x) + 1 }\n")
default:
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
}
continue
}
switch st.Tag(i) {
case `dns:"-"`:
// ignored
case `dns:"cdomain-name"`, `dns:"domain-name"`:
o("l += len(rr.%s) + 1\n")
case `dns:"octet"`:
o("l += len(rr.%s)\n")
case `dns:"base64"`:
o("l += base64.StdEncoding.DecodedLen(len(rr.%s))\n")
case `dns:"size-hex"`, `dns:"hex"`:
o("l += len(rr.%s)/2 + 1\n")
case `dns:"a"`:
o("l += net.IPv4len // %s\n")
case `dns:"aaaa"`:
o("l += net.IPv6len // %s\n")
case `dns:"txt"`:
o("for _, t := range rr.%s { l += len(t) + 1 }\n")
case `dns:"uint48"`:
o("l += 6 // %s\n")
case "":
switch st.Field(i).Type().(*types.Basic).Kind() {
case types.Uint8:
o("l += 1 // %s\n")
case types.Uint16:
o("l += 2 // %s\n")
case types.Uint32:
o("l += 4 // %s\n")
case types.Uint64:
o("l += 8 // %s\n")
case types.String:
o("l += len(rr.%s) + 1\n")
default:
log.Fatalln(name, st.Field(i).Name())
}
default:
log.Fatalln(name, st.Field(i).Name(), st.Tag(i))
}
}
fmt.Fprintf(b, "return l }\n")
}
// Generate copy()
fmt.Fprint(b, "// copy() functions\n")
for _, name := range namedTypes {
o := scope.Lookup(name)
st, isEmbedded := getTypeStruct(o.Type(), scope)
if isEmbedded {
continue
}
fmt.Fprintf(b, "func (rr *%s) copy() RR {\n", name)
fields := []string{"*rr.Hdr.copyHeader()"}
for i := 1; i < st.NumFields(); i++ {
f := st.Field(i).Name()
if sl, ok := st.Field(i).Type().(*types.Slice); ok {
t := sl.Underlying().String()
t = strings.TrimPrefix(t, "[]")
t = strings.TrimPrefix(t, "github.com/miekg/dns.")
fmt.Fprintf(b, "%s := make([]%s, len(rr.%s)); copy(%s, rr.%s)\n",
f, t, f, f, f)
fields = append(fields, f)
continue
}
if st.Field(i).Type().String() == "net.IP" {
fields = append(fields, "copyIP(rr."+f+")")
continue
}
fields = append(fields, "rr."+f)
}
fmt.Fprintf(b, "return &%s{%s}\n", name, strings.Join(fields, ","))
fmt.Fprintf(b, "}\n")
}
// gofmt
res, err := format.Source(b.Bytes())
if err != nil {
b.WriteTo(os.Stderr)
log.Fatal(err)
}
// write result
f, err := os.Create("ztypes.go")
fatalIfErr(err)
defer f.Close()
f.Write(res)
}
func fatalIfErr(err error) {
if err != nil {
log.Fatal(err)
}
}

View File

@ -1,42 +0,0 @@
package dns
import (
"testing"
)
func TestCmToM(t *testing.T) {
s := cmToM(0, 0)
if s != "0.00" {
t.Error("0, 0")
}
s = cmToM(1, 0)
if s != "0.01" {
t.Error("1, 0")
}
s = cmToM(3, 1)
if s != "0.30" {
t.Error("3, 1")
}
s = cmToM(4, 2)
if s != "4" {
t.Error("4, 2")
}
s = cmToM(5, 3)
if s != "50" {
t.Error("5, 3")
}
s = cmToM(7, 5)
if s != "7000" {
t.Error("7, 5")
}
s = cmToM(9, 9)
if s != "90000000" {
t.Error("9, 9")
}
}

View File

@ -7,11 +7,14 @@ import (
"syscall"
)
// SessionUDP holds the remote address and the associated
// out-of-band data.
type SessionUDP struct {
raddr *net.UDPAddr
context []byte
}
// RemoteAddr returns the remote network address.
func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr }
// setUDPSocketOptions sets the UDP socket options.

View File

@ -1,84 +0,0 @@
package dns
import (
"bytes"
"testing"
)
func TestDynamicUpdateParsing(t *testing.T) {
prefix := "example.com. IN "
for _, typ := range TypeToString {
if typ == "OPT" || typ == "AXFR" || typ == "IXFR" || typ == "ANY" || typ == "TKEY" ||
typ == "TSIG" || typ == "ISDN" || typ == "UNSPEC" || typ == "NULL" || typ == "ATMA" {
continue
}
r, err := NewRR(prefix + typ)
if err != nil {
t.Errorf("failure to parse: %s %s: %v", prefix, typ, err)
} else {
t.Logf("parsed: %s", r.String())
}
}
}
func TestDynamicUpdateUnpack(t *testing.T) {
// From https://github.com/miekg/dns/issues/150#issuecomment-62296803
// It should be an update message for the zone "example.",
// deleting the A RRset "example." and then adding an A record at "example.".
// class ANY, TYPE A
buf := []byte{171, 68, 40, 0, 0, 1, 0, 0, 0, 2, 0, 0, 7, 101, 120, 97, 109, 112, 108, 101, 0, 0, 6, 0, 1, 192, 12, 0, 1, 0, 255, 0, 0, 0, 0, 0, 0, 192, 12, 0, 1, 0, 1, 0, 0, 0, 0, 0, 4, 127, 0, 0, 1}
msg := new(Msg)
err := msg.Unpack(buf)
if err != nil {
t.Errorf("failed to unpack: %v\n%s", err, msg.String())
}
}
func TestDynamicUpdateZeroRdataUnpack(t *testing.T) {
m := new(Msg)
rr := &RR_Header{Name: ".", Rrtype: 0, Class: 1, Ttl: ^uint32(0), Rdlength: 0}
m.Answer = []RR{rr, rr, rr, rr, rr}
m.Ns = m.Answer
for n, s := range TypeToString {
rr.Rrtype = n
bytes, err := m.Pack()
if err != nil {
t.Errorf("failed to pack %s: %v", s, err)
continue
}
if err := new(Msg).Unpack(bytes); err != nil {
t.Errorf("failed to unpack %s: %v", s, err)
}
}
}
func TestRemoveRRset(t *testing.T) {
// Should add a zero data RR in Class ANY with a TTL of 0
// for each set mentioned in the RRs provided to it.
rr, err := NewRR(". 100 IN A 127.0.0.1")
if err != nil {
t.Fatalf("Error constructing RR: %v", err)
}
m := new(Msg)
m.Ns = []RR{&RR_Header{Name: ".", Rrtype: TypeA, Class: ClassANY, Ttl: 0, Rdlength: 0}}
expectstr := m.String()
expect, err := m.Pack()
if err != nil {
t.Fatalf("Error packing expected msg: %v", err)
}
m.Ns = nil
m.RemoveRRset([]RR{rr})
actual, err := m.Pack()
if err != nil {
t.Fatalf("Error packing actual msg: %v", err)
}
if !bytes.Equal(actual, expect) {
tmp := new(Msg)
if err := tmp.Unpack(actual); err != nil {
t.Fatalf("Error unpacking actual msg: %v", err)
}
t.Errorf("Expected msg:\n%s", expectstr)
t.Errorf("Actual msg:\n%v", tmp)
}
}

View File

@ -23,14 +23,26 @@ type Transfer struct {
// Think we need to away to stop the transfer
// In performs an incoming transfer with the server in a.
// If you would like to set the source IP, or some other attribute
// of a Dialer for a Transfer, you can do so by specifying the attributes
// in the Transfer.Conn:
//
// d := net.Dialer{LocalAddr: transfer_source}
// con, err := d.Dial("tcp", master)
// dnscon := &dns.Conn{Conn:con}
// transfer = &dns.Transfer{Conn: dnscon}
// channel, err := transfer.In(message, master)
//
func (t *Transfer) In(q *Msg, a string) (env chan *Envelope, err error) {
timeout := dnsTimeout
if t.DialTimeout != 0 {
timeout = t.DialTimeout
}
t.Conn, err = DialTimeout("tcp", a, timeout)
if err != nil {
return nil, err
if t.Conn == nil {
t.Conn, err = DialTimeout("tcp", a, timeout)
if err != nil {
return nil, err
}
}
if err := t.WriteMsg(q); err != nil {
return nil, err
@ -91,7 +103,6 @@ func (t *Transfer) inAxfr(id uint16, c chan *Envelope) {
c <- &Envelope{in.Answer, nil}
}
}
panic("dns: not reached")
}
func (t *Transfer) inIxfr(id uint16, c chan *Envelope) {

View File

@ -1,92 +0,0 @@
package dns
import (
"net"
"testing"
"time"
)
func getIP(s string) string {
a, err := net.LookupAddr(s)
if err != nil {
return ""
}
return a[0]
}
// flaky, need to setup local server and test from
// that.
func testClientAXFR(t *testing.T) {
if testing.Short() {
return
}
m := new(Msg)
m.SetAxfr("miek.nl.")
server := getIP("linode.atoom.net")
tr := new(Transfer)
if a, err := tr.In(m, net.JoinHostPort(server, "53")); err != nil {
t.Fatal("failed to setup axfr: ", err)
} else {
for ex := range a {
if ex.Error != nil {
t.Errorf("error %v", ex.Error)
break
}
for _, rr := range ex.RR {
t.Log(rr.String())
}
}
}
}
// fails.
func testClientAXFRMultipleEnvelopes(t *testing.T) {
if testing.Short() {
return
}
m := new(Msg)
m.SetAxfr("nlnetlabs.nl.")
server := getIP("open.nlnetlabs.nl.")
tr := new(Transfer)
if a, err := tr.In(m, net.JoinHostPort(server, "53")); err != nil {
t.Fatalf("Failed to setup axfr %v for server: %v", err, server)
} else {
for ex := range a {
if ex.Error != nil {
t.Errorf("Error %v", ex.Error)
break
}
}
}
}
func testClientTsigAXFR(t *testing.T) {
if testing.Short() {
return
}
m := new(Msg)
m.SetAxfr("example.nl.")
m.SetTsig("axfr.", HmacMD5, 300, time.Now().Unix())
tr := new(Transfer)
tr.TsigSecret = map[string]string{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}
if a, err := tr.In(m, "176.58.119.54:53"); err != nil {
t.Fatal("failed to setup axfr: ", err)
} else {
for ex := range a {
if ex.Error != nil {
t.Errorf("error %v", ex.Error)
break
}
for _, rr := range ex.RR {
t.Log(rr.String())
}
}
}
}

View File

@ -144,8 +144,10 @@ func ReadRR(q io.Reader, filename string) (RR, error) {
//
// for x := range dns.ParseZone(strings.NewReader(z), "", "") {
// if x.Error != nil {
// // Do something with x.RR
// }
// // log.Println(x.Error)
// } else {
// // Do something with x.RR
// }
// }
//
// Comments specified after an RR (and on the same line!) are returned too:

View File

@ -896,22 +896,24 @@ func setLOC(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
if l.length == 0 {
return rr, nil, ""
}
if i, e := strconv.Atoi(l.token); e != nil || l.err {
i, e := strconv.Atoi(l.token)
if e != nil || l.err {
return nil, &ParseError{f, "bad LOC Latitude", l}, ""
} else {
rr.Latitude = 1000 * 60 * 60 * uint32(i)
}
rr.Latitude = 1000 * 60 * 60 * uint32(i)
<-c // zBlank
// Either number, 'N' or 'S'
l = <-c
if rr.Latitude, ok = locCheckNorth(l.token, rr.Latitude); ok {
goto East
}
if i, e := strconv.Atoi(l.token); e != nil || l.err {
i, e = strconv.Atoi(l.token)
if e != nil || l.err {
return nil, &ParseError{f, "bad LOC Latitude minutes", l}, ""
} else {
rr.Latitude += 1000 * 60 * uint32(i)
}
rr.Latitude += 1000 * 60 * uint32(i)
<-c // zBlank
l = <-c
if i, e := strconv.ParseFloat(l.token, 32); e != nil || l.err {
@ -1484,11 +1486,11 @@ func setWKS(h RR_Header, c chan lex, o, f string) (RR, *ParseError, string) {
// Ok
case zString:
if k, err = net.LookupPort(proto, l.token); err != nil {
if i, e := strconv.Atoi(l.token); e != nil { // If a number use that
i, e := strconv.Atoi(l.token) // If a number use that
if e != nil {
return nil, &ParseError{f, "bad WKS BitMap", l}, ""
} else {
rr.BitMap = append(rr.BitMap, uint16(i))
}
rr.BitMap = append(rr.BitMap, uint16(i))
}
rr.BitMap = append(rr.BitMap, uint16(k))
default:

842
Godeps/_workspace/src/github.com/miekg/dns/ztypes.go generated vendored Normal file
View File

@ -0,0 +1,842 @@
// *** DO NOT MODIFY ***
// AUTOGENERATED BY go generate
package dns
import (
"encoding/base64"
"net"
)
// TypeToRR is a map of constructors for each RR type.
var TypeToRR = map[uint16]func() RR{
TypeA: func() RR { return new(A) },
TypeAAAA: func() RR { return new(AAAA) },
TypeAFSDB: func() RR { return new(AFSDB) },
TypeANY: func() RR { return new(ANY) },
TypeCAA: func() RR { return new(CAA) },
TypeCDNSKEY: func() RR { return new(CDNSKEY) },
TypeCDS: func() RR { return new(CDS) },
TypeCERT: func() RR { return new(CERT) },
TypeCNAME: func() RR { return new(CNAME) },
TypeDHCID: func() RR { return new(DHCID) },
TypeDLV: func() RR { return new(DLV) },
TypeDNAME: func() RR { return new(DNAME) },
TypeDNSKEY: func() RR { return new(DNSKEY) },
TypeDS: func() RR { return new(DS) },
TypeEID: func() RR { return new(EID) },
TypeEUI48: func() RR { return new(EUI48) },
TypeEUI64: func() RR { return new(EUI64) },
TypeGID: func() RR { return new(GID) },
TypeGPOS: func() RR { return new(GPOS) },
TypeHINFO: func() RR { return new(HINFO) },
TypeHIP: func() RR { return new(HIP) },
TypeIPSECKEY: func() RR { return new(IPSECKEY) },
TypeKEY: func() RR { return new(KEY) },
TypeKX: func() RR { return new(KX) },
TypeL32: func() RR { return new(L32) },
TypeL64: func() RR { return new(L64) },
TypeLOC: func() RR { return new(LOC) },
TypeLP: func() RR { return new(LP) },
TypeMB: func() RR { return new(MB) },
TypeMD: func() RR { return new(MD) },
TypeMF: func() RR { return new(MF) },
TypeMG: func() RR { return new(MG) },
TypeMINFO: func() RR { return new(MINFO) },
TypeMR: func() RR { return new(MR) },
TypeMX: func() RR { return new(MX) },
TypeNAPTR: func() RR { return new(NAPTR) },
TypeNID: func() RR { return new(NID) },
TypeNIMLOC: func() RR { return new(NIMLOC) },
TypeNINFO: func() RR { return new(NINFO) },
TypeNS: func() RR { return new(NS) },
TypeNSAPPTR: func() RR { return new(NSAPPTR) },
TypeNSEC: func() RR { return new(NSEC) },
TypeNSEC3: func() RR { return new(NSEC3) },
TypeNSEC3PARAM: func() RR { return new(NSEC3PARAM) },
TypeOPENPGPKEY: func() RR { return new(OPENPGPKEY) },
TypeOPT: func() RR { return new(OPT) },
TypePTR: func() RR { return new(PTR) },
TypePX: func() RR { return new(PX) },
TypeRKEY: func() RR { return new(RKEY) },
TypeRP: func() RR { return new(RP) },
TypeRRSIG: func() RR { return new(RRSIG) },
TypeRT: func() RR { return new(RT) },
TypeSIG: func() RR { return new(SIG) },
TypeSOA: func() RR { return new(SOA) },
TypeSPF: func() RR { return new(SPF) },
TypeSRV: func() RR { return new(SRV) },
TypeSSHFP: func() RR { return new(SSHFP) },
TypeTA: func() RR { return new(TA) },
TypeTALINK: func() RR { return new(TALINK) },
TypeTKEY: func() RR { return new(TKEY) },
TypeTLSA: func() RR { return new(TLSA) },
TypeTSIG: func() RR { return new(TSIG) },
TypeTXT: func() RR { return new(TXT) },
TypeUID: func() RR { return new(UID) },
TypeUINFO: func() RR { return new(UINFO) },
TypeURI: func() RR { return new(URI) },
TypeWKS: func() RR { return new(WKS) },
TypeX25: func() RR { return new(X25) },
}
// TypeToString is a map of strings for each RR type.
var TypeToString = map[uint16]string{
TypeA: "A",
TypeAAAA: "AAAA",
TypeAFSDB: "AFSDB",
TypeANY: "ANY",
TypeATMA: "ATMA",
TypeAXFR: "AXFR",
TypeCAA: "CAA",
TypeCDNSKEY: "CDNSKEY",
TypeCDS: "CDS",
TypeCERT: "CERT",
TypeCNAME: "CNAME",
TypeDHCID: "DHCID",
TypeDLV: "DLV",
TypeDNAME: "DNAME",
TypeDNSKEY: "DNSKEY",
TypeDS: "DS",
TypeEID: "EID",
TypeEUI48: "EUI48",
TypeEUI64: "EUI64",
TypeGID: "GID",
TypeGPOS: "GPOS",
TypeHINFO: "HINFO",
TypeHIP: "HIP",
TypeIPSECKEY: "IPSECKEY",
TypeISDN: "ISDN",
TypeIXFR: "IXFR",
TypeKEY: "KEY",
TypeKX: "KX",
TypeL32: "L32",
TypeL64: "L64",
TypeLOC: "LOC",
TypeLP: "LP",
TypeMAILA: "MAILA",
TypeMAILB: "MAILB",
TypeMB: "MB",
TypeMD: "MD",
TypeMF: "MF",
TypeMG: "MG",
TypeMINFO: "MINFO",
TypeMR: "MR",
TypeMX: "MX",
TypeNAPTR: "NAPTR",
TypeNID: "NID",
TypeNIMLOC: "NIMLOC",
TypeNINFO: "NINFO",
TypeNS: "NS",
TypeNSEC: "NSEC",
TypeNSEC3: "NSEC3",
TypeNSEC3PARAM: "NSEC3PARAM",
TypeNULL: "NULL",
TypeNXT: "NXT",
TypeNone: "None",
TypeOPENPGPKEY: "OPENPGPKEY",
TypeOPT: "OPT",
TypePTR: "PTR",
TypePX: "PX",
TypeRKEY: "RKEY",
TypeRP: "RP",
TypeRRSIG: "RRSIG",
TypeRT: "RT",
TypeReserved: "Reserved",
TypeSIG: "SIG",
TypeSOA: "SOA",
TypeSPF: "SPF",
TypeSRV: "SRV",
TypeSSHFP: "SSHFP",
TypeTA: "TA",
TypeTALINK: "TALINK",
TypeTKEY: "TKEY",
TypeTLSA: "TLSA",
TypeTSIG: "TSIG",
TypeTXT: "TXT",
TypeUID: "UID",
TypeUINFO: "UINFO",
TypeUNSPEC: "UNSPEC",
TypeURI: "URI",
TypeWKS: "WKS",
TypeX25: "X25",
TypeNSAPPTR: "NSAP-PTR",
}
// Header() functions
func (rr *A) Header() *RR_Header { return &rr.Hdr }
func (rr *AAAA) Header() *RR_Header { return &rr.Hdr }
func (rr *AFSDB) Header() *RR_Header { return &rr.Hdr }
func (rr *ANY) Header() *RR_Header { return &rr.Hdr }
func (rr *CAA) Header() *RR_Header { return &rr.Hdr }
func (rr *CDNSKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *CDS) Header() *RR_Header { return &rr.Hdr }
func (rr *CERT) Header() *RR_Header { return &rr.Hdr }
func (rr *CNAME) Header() *RR_Header { return &rr.Hdr }
func (rr *DHCID) Header() *RR_Header { return &rr.Hdr }
func (rr *DLV) Header() *RR_Header { return &rr.Hdr }
func (rr *DNAME) Header() *RR_Header { return &rr.Hdr }
func (rr *DNSKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *DS) Header() *RR_Header { return &rr.Hdr }
func (rr *EID) Header() *RR_Header { return &rr.Hdr }
func (rr *EUI48) Header() *RR_Header { return &rr.Hdr }
func (rr *EUI64) Header() *RR_Header { return &rr.Hdr }
func (rr *GID) Header() *RR_Header { return &rr.Hdr }
func (rr *GPOS) Header() *RR_Header { return &rr.Hdr }
func (rr *HINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *HIP) Header() *RR_Header { return &rr.Hdr }
func (rr *IPSECKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *KEY) Header() *RR_Header { return &rr.Hdr }
func (rr *KX) Header() *RR_Header { return &rr.Hdr }
func (rr *L32) Header() *RR_Header { return &rr.Hdr }
func (rr *L64) Header() *RR_Header { return &rr.Hdr }
func (rr *LOC) Header() *RR_Header { return &rr.Hdr }
func (rr *LP) Header() *RR_Header { return &rr.Hdr }
func (rr *MB) Header() *RR_Header { return &rr.Hdr }
func (rr *MD) Header() *RR_Header { return &rr.Hdr }
func (rr *MF) Header() *RR_Header { return &rr.Hdr }
func (rr *MG) Header() *RR_Header { return &rr.Hdr }
func (rr *MINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *MR) Header() *RR_Header { return &rr.Hdr }
func (rr *MX) Header() *RR_Header { return &rr.Hdr }
func (rr *NAPTR) Header() *RR_Header { return &rr.Hdr }
func (rr *NID) Header() *RR_Header { return &rr.Hdr }
func (rr *NIMLOC) Header() *RR_Header { return &rr.Hdr }
func (rr *NINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *NS) Header() *RR_Header { return &rr.Hdr }
func (rr *NSAPPTR) Header() *RR_Header { return &rr.Hdr }
func (rr *NSEC) Header() *RR_Header { return &rr.Hdr }
func (rr *NSEC3) Header() *RR_Header { return &rr.Hdr }
func (rr *NSEC3PARAM) Header() *RR_Header { return &rr.Hdr }
func (rr *OPENPGPKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *OPT) Header() *RR_Header { return &rr.Hdr }
func (rr *PTR) Header() *RR_Header { return &rr.Hdr }
func (rr *PX) Header() *RR_Header { return &rr.Hdr }
func (rr *RFC3597) Header() *RR_Header { return &rr.Hdr }
func (rr *RKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *RP) Header() *RR_Header { return &rr.Hdr }
func (rr *RRSIG) Header() *RR_Header { return &rr.Hdr }
func (rr *RT) Header() *RR_Header { return &rr.Hdr }
func (rr *SIG) Header() *RR_Header { return &rr.Hdr }
func (rr *SOA) Header() *RR_Header { return &rr.Hdr }
func (rr *SPF) Header() *RR_Header { return &rr.Hdr }
func (rr *SRV) Header() *RR_Header { return &rr.Hdr }
func (rr *SSHFP) Header() *RR_Header { return &rr.Hdr }
func (rr *TA) Header() *RR_Header { return &rr.Hdr }
func (rr *TALINK) Header() *RR_Header { return &rr.Hdr }
func (rr *TKEY) Header() *RR_Header { return &rr.Hdr }
func (rr *TLSA) Header() *RR_Header { return &rr.Hdr }
func (rr *TSIG) Header() *RR_Header { return &rr.Hdr }
func (rr *TXT) Header() *RR_Header { return &rr.Hdr }
func (rr *UID) Header() *RR_Header { return &rr.Hdr }
func (rr *UINFO) Header() *RR_Header { return &rr.Hdr }
func (rr *URI) Header() *RR_Header { return &rr.Hdr }
func (rr *WKS) Header() *RR_Header { return &rr.Hdr }
func (rr *X25) Header() *RR_Header { return &rr.Hdr }
// len() functions
func (rr *A) len() int {
l := rr.Hdr.len()
l += net.IPv4len // A
return l
}
func (rr *AAAA) len() int {
l := rr.Hdr.len()
l += net.IPv6len // AAAA
return l
}
func (rr *AFSDB) len() int {
l := rr.Hdr.len()
l += 2 // Subtype
l += len(rr.Hostname) + 1
return l
}
func (rr *ANY) len() int {
l := rr.Hdr.len()
return l
}
func (rr *CAA) len() int {
l := rr.Hdr.len()
l += 1 // Flag
l += len(rr.Tag) + 1
l += len(rr.Value)
return l
}
func (rr *CERT) len() int {
l := rr.Hdr.len()
l += 2 // Type
l += 2 // KeyTag
l += 1 // Algorithm
l += base64.StdEncoding.DecodedLen(len(rr.Certificate))
return l
}
func (rr *CNAME) len() int {
l := rr.Hdr.len()
l += len(rr.Target) + 1
return l
}
func (rr *DHCID) len() int {
l := rr.Hdr.len()
l += base64.StdEncoding.DecodedLen(len(rr.Digest))
return l
}
func (rr *DNAME) len() int {
l := rr.Hdr.len()
l += len(rr.Target) + 1
return l
}
func (rr *DNSKEY) len() int {
l := rr.Hdr.len()
l += 2 // Flags
l += 1 // Protocol
l += 1 // Algorithm
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
return l
}
func (rr *DS) len() int {
l := rr.Hdr.len()
l += 2 // KeyTag
l += 1 // Algorithm
l += 1 // DigestType
l += len(rr.Digest)/2 + 1
return l
}
func (rr *EID) len() int {
l := rr.Hdr.len()
l += len(rr.Endpoint)/2 + 1
return l
}
func (rr *EUI48) len() int {
l := rr.Hdr.len()
l += 6 // Address
return l
}
func (rr *EUI64) len() int {
l := rr.Hdr.len()
l += 8 // Address
return l
}
func (rr *GID) len() int {
l := rr.Hdr.len()
l += 4 // Gid
return l
}
func (rr *GPOS) len() int {
l := rr.Hdr.len()
l += len(rr.Longitude) + 1
l += len(rr.Latitude) + 1
l += len(rr.Altitude) + 1
return l
}
func (rr *HINFO) len() int {
l := rr.Hdr.len()
l += len(rr.Cpu) + 1
l += len(rr.Os) + 1
return l
}
func (rr *HIP) len() int {
l := rr.Hdr.len()
l += 1 // HitLength
l += 1 // PublicKeyAlgorithm
l += 2 // PublicKeyLength
l += len(rr.Hit)/2 + 1
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
for _, x := range rr.RendezvousServers {
l += len(x) + 1
}
return l
}
func (rr *KX) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += len(rr.Exchanger) + 1
return l
}
func (rr *L32) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += net.IPv4len // Locator32
return l
}
func (rr *L64) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += 8 // Locator64
return l
}
func (rr *LOC) len() int {
l := rr.Hdr.len()
l += 1 // Version
l += 1 // Size
l += 1 // HorizPre
l += 1 // VertPre
l += 4 // Latitude
l += 4 // Longitude
l += 4 // Altitude
return l
}
func (rr *LP) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += len(rr.Fqdn) + 1
return l
}
func (rr *MB) len() int {
l := rr.Hdr.len()
l += len(rr.Mb) + 1
return l
}
func (rr *MD) len() int {
l := rr.Hdr.len()
l += len(rr.Md) + 1
return l
}
func (rr *MF) len() int {
l := rr.Hdr.len()
l += len(rr.Mf) + 1
return l
}
func (rr *MG) len() int {
l := rr.Hdr.len()
l += len(rr.Mg) + 1
return l
}
func (rr *MINFO) len() int {
l := rr.Hdr.len()
l += len(rr.Rmail) + 1
l += len(rr.Email) + 1
return l
}
func (rr *MR) len() int {
l := rr.Hdr.len()
l += len(rr.Mr) + 1
return l
}
func (rr *MX) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += len(rr.Mx) + 1
return l
}
func (rr *NAPTR) len() int {
l := rr.Hdr.len()
l += 2 // Order
l += 2 // Preference
l += len(rr.Flags) + 1
l += len(rr.Service) + 1
l += len(rr.Regexp) + 1
l += len(rr.Replacement) + 1
return l
}
func (rr *NID) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += 8 // NodeID
return l
}
func (rr *NIMLOC) len() int {
l := rr.Hdr.len()
l += len(rr.Locator)/2 + 1
return l
}
func (rr *NINFO) len() int {
l := rr.Hdr.len()
for _, x := range rr.ZSData {
l += len(x) + 1
}
return l
}
func (rr *NS) len() int {
l := rr.Hdr.len()
l += len(rr.Ns) + 1
return l
}
func (rr *NSAPPTR) len() int {
l := rr.Hdr.len()
l += len(rr.Ptr) + 1
return l
}
func (rr *NSEC3PARAM) len() int {
l := rr.Hdr.len()
l += 1 // Hash
l += 1 // Flags
l += 2 // Iterations
l += 1 // SaltLength
l += len(rr.Salt)/2 + 1
return l
}
func (rr *OPENPGPKEY) len() int {
l := rr.Hdr.len()
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
return l
}
func (rr *PTR) len() int {
l := rr.Hdr.len()
l += len(rr.Ptr) + 1
return l
}
func (rr *PX) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += len(rr.Map822) + 1
l += len(rr.Mapx400) + 1
return l
}
func (rr *RFC3597) len() int {
l := rr.Hdr.len()
l += len(rr.Rdata)/2 + 1
return l
}
func (rr *RKEY) len() int {
l := rr.Hdr.len()
l += 2 // Flags
l += 1 // Protocol
l += 1 // Algorithm
l += base64.StdEncoding.DecodedLen(len(rr.PublicKey))
return l
}
func (rr *RP) len() int {
l := rr.Hdr.len()
l += len(rr.Mbox) + 1
l += len(rr.Txt) + 1
return l
}
func (rr *RRSIG) len() int {
l := rr.Hdr.len()
l += 2 // TypeCovered
l += 1 // Algorithm
l += 1 // Labels
l += 4 // OrigTtl
l += 4 // Expiration
l += 4 // Inception
l += 2 // KeyTag
l += len(rr.SignerName) + 1
l += base64.StdEncoding.DecodedLen(len(rr.Signature))
return l
}
func (rr *RT) len() int {
l := rr.Hdr.len()
l += 2 // Preference
l += len(rr.Host) + 1
return l
}
func (rr *SOA) len() int {
l := rr.Hdr.len()
l += len(rr.Ns) + 1
l += len(rr.Mbox) + 1
l += 4 // Serial
l += 4 // Refresh
l += 4 // Retry
l += 4 // Expire
l += 4 // Minttl
return l
}
func (rr *SPF) len() int {
l := rr.Hdr.len()
for _, x := range rr.Txt {
l += len(x) + 1
}
return l
}
func (rr *SRV) len() int {
l := rr.Hdr.len()
l += 2 // Priority
l += 2 // Weight
l += 2 // Port
l += len(rr.Target) + 1
return l
}
func (rr *SSHFP) len() int {
l := rr.Hdr.len()
l += 1 // Algorithm
l += 1 // Type
l += len(rr.FingerPrint)/2 + 1
return l
}
func (rr *TA) len() int {
l := rr.Hdr.len()
l += 2 // KeyTag
l += 1 // Algorithm
l += 1 // DigestType
l += len(rr.Digest)/2 + 1
return l
}
func (rr *TALINK) len() int {
l := rr.Hdr.len()
l += len(rr.PreviousName) + 1
l += len(rr.NextName) + 1
return l
}
func (rr *TKEY) len() int {
l := rr.Hdr.len()
l += len(rr.Algorithm) + 1
l += 4 // Inception
l += 4 // Expiration
l += 2 // Mode
l += 2 // Error
l += 2 // KeySize
l += len(rr.Key) + 1
l += 2 // OtherLen
l += len(rr.OtherData) + 1
return l
}
func (rr *TLSA) len() int {
l := rr.Hdr.len()
l += 1 // Usage
l += 1 // Selector
l += 1 // MatchingType
l += len(rr.Certificate)/2 + 1
return l
}
func (rr *TSIG) len() int {
l := rr.Hdr.len()
l += len(rr.Algorithm) + 1
l += 6 // TimeSigned
l += 2 // Fudge
l += 2 // MACSize
l += len(rr.MAC)/2 + 1
l += 2 // OrigId
l += 2 // Error
l += 2 // OtherLen
l += len(rr.OtherData)/2 + 1
return l
}
func (rr *TXT) len() int {
l := rr.Hdr.len()
for _, x := range rr.Txt {
l += len(x) + 1
}
return l
}
func (rr *UID) len() int {
l := rr.Hdr.len()
l += 4 // Uid
return l
}
func (rr *UINFO) len() int {
l := rr.Hdr.len()
l += len(rr.Uinfo) + 1
return l
}
func (rr *URI) len() int {
l := rr.Hdr.len()
l += 2 // Priority
l += 2 // Weight
l += len(rr.Target)
return l
}
func (rr *X25) len() int {
l := rr.Hdr.len()
l += len(rr.PSDNAddress) + 1
return l
}
// copy() functions
func (rr *A) copy() RR {
return &A{*rr.Hdr.copyHeader(), copyIP(rr.A)}
}
func (rr *AAAA) copy() RR {
return &AAAA{*rr.Hdr.copyHeader(), copyIP(rr.AAAA)}
}
func (rr *AFSDB) copy() RR {
return &AFSDB{*rr.Hdr.copyHeader(), rr.Subtype, rr.Hostname}
}
func (rr *ANY) copy() RR {
return &ANY{*rr.Hdr.copyHeader()}
}
func (rr *CAA) copy() RR {
return &CAA{*rr.Hdr.copyHeader(), rr.Flag, rr.Tag, rr.Value}
}
func (rr *CERT) copy() RR {
return &CERT{*rr.Hdr.copyHeader(), rr.Type, rr.KeyTag, rr.Algorithm, rr.Certificate}
}
func (rr *CNAME) copy() RR {
return &CNAME{*rr.Hdr.copyHeader(), rr.Target}
}
func (rr *DHCID) copy() RR {
return &DHCID{*rr.Hdr.copyHeader(), rr.Digest}
}
func (rr *DNAME) copy() RR {
return &DNAME{*rr.Hdr.copyHeader(), rr.Target}
}
func (rr *DNSKEY) copy() RR {
return &DNSKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
}
func (rr *DS) copy() RR {
return &DS{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
}
func (rr *EID) copy() RR {
return &EID{*rr.Hdr.copyHeader(), rr.Endpoint}
}
func (rr *EUI48) copy() RR {
return &EUI48{*rr.Hdr.copyHeader(), rr.Address}
}
func (rr *EUI64) copy() RR {
return &EUI64{*rr.Hdr.copyHeader(), rr.Address}
}
func (rr *GID) copy() RR {
return &GID{*rr.Hdr.copyHeader(), rr.Gid}
}
func (rr *GPOS) copy() RR {
return &GPOS{*rr.Hdr.copyHeader(), rr.Longitude, rr.Latitude, rr.Altitude}
}
func (rr *HINFO) copy() RR {
return &HINFO{*rr.Hdr.copyHeader(), rr.Cpu, rr.Os}
}
func (rr *HIP) copy() RR {
RendezvousServers := make([]string, len(rr.RendezvousServers))
copy(RendezvousServers, rr.RendezvousServers)
return &HIP{*rr.Hdr.copyHeader(), rr.HitLength, rr.PublicKeyAlgorithm, rr.PublicKeyLength, rr.Hit, rr.PublicKey, RendezvousServers}
}
func (rr *IPSECKEY) copy() RR {
return &IPSECKEY{*rr.Hdr.copyHeader(), rr.Precedence, rr.GatewayType, rr.Algorithm, copyIP(rr.GatewayA), copyIP(rr.GatewayAAAA), rr.GatewayName, rr.PublicKey}
}
func (rr *KX) copy() RR {
return &KX{*rr.Hdr.copyHeader(), rr.Preference, rr.Exchanger}
}
func (rr *L32) copy() RR {
return &L32{*rr.Hdr.copyHeader(), rr.Preference, copyIP(rr.Locator32)}
}
func (rr *L64) copy() RR {
return &L64{*rr.Hdr.copyHeader(), rr.Preference, rr.Locator64}
}
func (rr *LOC) copy() RR {
return &LOC{*rr.Hdr.copyHeader(), rr.Version, rr.Size, rr.HorizPre, rr.VertPre, rr.Latitude, rr.Longitude, rr.Altitude}
}
func (rr *LP) copy() RR {
return &LP{*rr.Hdr.copyHeader(), rr.Preference, rr.Fqdn}
}
func (rr *MB) copy() RR {
return &MB{*rr.Hdr.copyHeader(), rr.Mb}
}
func (rr *MD) copy() RR {
return &MD{*rr.Hdr.copyHeader(), rr.Md}
}
func (rr *MF) copy() RR {
return &MF{*rr.Hdr.copyHeader(), rr.Mf}
}
func (rr *MG) copy() RR {
return &MG{*rr.Hdr.copyHeader(), rr.Mg}
}
func (rr *MINFO) copy() RR {
return &MINFO{*rr.Hdr.copyHeader(), rr.Rmail, rr.Email}
}
func (rr *MR) copy() RR {
return &MR{*rr.Hdr.copyHeader(), rr.Mr}
}
func (rr *MX) copy() RR {
return &MX{*rr.Hdr.copyHeader(), rr.Preference, rr.Mx}
}
func (rr *NAPTR) copy() RR {
return &NAPTR{*rr.Hdr.copyHeader(), rr.Order, rr.Preference, rr.Flags, rr.Service, rr.Regexp, rr.Replacement}
}
func (rr *NID) copy() RR {
return &NID{*rr.Hdr.copyHeader(), rr.Preference, rr.NodeID}
}
func (rr *NIMLOC) copy() RR {
return &NIMLOC{*rr.Hdr.copyHeader(), rr.Locator}
}
func (rr *NINFO) copy() RR {
ZSData := make([]string, len(rr.ZSData))
copy(ZSData, rr.ZSData)
return &NINFO{*rr.Hdr.copyHeader(), ZSData}
}
func (rr *NS) copy() RR {
return &NS{*rr.Hdr.copyHeader(), rr.Ns}
}
func (rr *NSAPPTR) copy() RR {
return &NSAPPTR{*rr.Hdr.copyHeader(), rr.Ptr}
}
func (rr *NSEC) copy() RR {
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
copy(TypeBitMap, rr.TypeBitMap)
return &NSEC{*rr.Hdr.copyHeader(), rr.NextDomain, TypeBitMap}
}
func (rr *NSEC3) copy() RR {
TypeBitMap := make([]uint16, len(rr.TypeBitMap))
copy(TypeBitMap, rr.TypeBitMap)
return &NSEC3{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt, rr.HashLength, rr.NextDomain, TypeBitMap}
}
func (rr *NSEC3PARAM) copy() RR {
return &NSEC3PARAM{*rr.Hdr.copyHeader(), rr.Hash, rr.Flags, rr.Iterations, rr.SaltLength, rr.Salt}
}
func (rr *OPENPGPKEY) copy() RR {
return &OPENPGPKEY{*rr.Hdr.copyHeader(), rr.PublicKey}
}
func (rr *OPT) copy() RR {
Option := make([]EDNS0, len(rr.Option))
copy(Option, rr.Option)
return &OPT{*rr.Hdr.copyHeader(), Option}
}
func (rr *PTR) copy() RR {
return &PTR{*rr.Hdr.copyHeader(), rr.Ptr}
}
func (rr *PX) copy() RR {
return &PX{*rr.Hdr.copyHeader(), rr.Preference, rr.Map822, rr.Mapx400}
}
func (rr *RFC3597) copy() RR {
return &RFC3597{*rr.Hdr.copyHeader(), rr.Rdata}
}
func (rr *RKEY) copy() RR {
return &RKEY{*rr.Hdr.copyHeader(), rr.Flags, rr.Protocol, rr.Algorithm, rr.PublicKey}
}
func (rr *RP) copy() RR {
return &RP{*rr.Hdr.copyHeader(), rr.Mbox, rr.Txt}
}
func (rr *RRSIG) copy() RR {
return &RRSIG{*rr.Hdr.copyHeader(), rr.TypeCovered, rr.Algorithm, rr.Labels, rr.OrigTtl, rr.Expiration, rr.Inception, rr.KeyTag, rr.SignerName, rr.Signature}
}
func (rr *RT) copy() RR {
return &RT{*rr.Hdr.copyHeader(), rr.Preference, rr.Host}
}
func (rr *SOA) copy() RR {
return &SOA{*rr.Hdr.copyHeader(), rr.Ns, rr.Mbox, rr.Serial, rr.Refresh, rr.Retry, rr.Expire, rr.Minttl}
}
func (rr *SPF) copy() RR {
Txt := make([]string, len(rr.Txt))
copy(Txt, rr.Txt)
return &SPF{*rr.Hdr.copyHeader(), Txt}
}
func (rr *SRV) copy() RR {
return &SRV{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Port, rr.Target}
}
func (rr *SSHFP) copy() RR {
return &SSHFP{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Type, rr.FingerPrint}
}
func (rr *TA) copy() RR {
return &TA{*rr.Hdr.copyHeader(), rr.KeyTag, rr.Algorithm, rr.DigestType, rr.Digest}
}
func (rr *TALINK) copy() RR {
return &TALINK{*rr.Hdr.copyHeader(), rr.PreviousName, rr.NextName}
}
func (rr *TKEY) copy() RR {
return &TKEY{*rr.Hdr.copyHeader(), rr.Algorithm, rr.Inception, rr.Expiration, rr.Mode, rr.Error, rr.KeySize, rr.Key, rr.OtherLen, rr.OtherData}
}
func (rr *TLSA) copy() RR {
return &TLSA{*rr.Hdr.copyHeader(), rr.Usage, rr.Selector, rr.MatchingType, rr.Certificate}
}
func (rr *TSIG) copy() RR {
return &TSIG{*rr.Hdr.copyHeader(), rr.Algorithm, rr.TimeSigned, rr.Fudge, rr.MACSize, rr.MAC, rr.OrigId, rr.Error, rr.OtherLen, rr.OtherData}
}
func (rr *TXT) copy() RR {
Txt := make([]string, len(rr.Txt))
copy(Txt, rr.Txt)
return &TXT{*rr.Hdr.copyHeader(), Txt}
}
func (rr *UID) copy() RR {
return &UID{*rr.Hdr.copyHeader(), rr.Uid}
}
func (rr *UINFO) copy() RR {
return &UINFO{*rr.Hdr.copyHeader(), rr.Uinfo}
}
func (rr *URI) copy() RR {
return &URI{*rr.Hdr.copyHeader(), rr.Priority, rr.Weight, rr.Target}
}
func (rr *WKS) copy() RR {
BitMap := make([]uint16, len(rr.BitMap))
copy(BitMap, rr.BitMap)
return &WKS{*rr.Hdr.copyHeader(), copyIP(rr.Address), rr.Protocol, BitMap}
}
func (rr *X25) copy() RR {
return &X25{*rr.Hdr.copyHeader(), rr.PSDNAddress}
}

View File

@ -40,6 +40,10 @@ CentOS:
sudo yum install libtool-ltdl-devel MariaDB-server MariaDB-client rabbitmq-server
Arch Linux:
sudo pacman -S libtool mariadb rabbitmq --needed
OS X:
brew install libtool mariadb rabbitmq
@ -49,25 +53,34 @@ or
sudo port install libtool mariadb-server rabbitmq-server
(On OS X, using port, you will have to add `CGO_CFLAGS="-I/opt/local/include" CGO_LDFLAGS="-L/opt/local/lib"` to your environment or `go` invocations.)
Resolve Go-dependencies:
> go get bitbucket.org/liamstask/goose/cmd/goose
> go get github.com/jsha/listenbuddy
> go get github.com/letsencrypt/boulder/ # Ignore errors about no buildable files
> go get -u github.com/golang/lint/golint
Set up a database:
> cd $GOPATH/src/github.com/letsencrypt/boulder
> ./test/create_db.sh
# This starts each Boulder component with test configs. Ctrl-C kills all.
> ./start.py
# Run tests
> go get -u github.com/golang/lint/golint
> ./test.sh
Note: `create_db.sh` it uses the root MariaDB user with the default
**Note**: `create_db.sh` uses the root MariaDB user with the default
password, so if you have disabled that account or changed the password
you may have to adjust the file or recreate the commands.
You can also check out the official client from
https://github.com/letsencrypt/letsencrypt/ and follow the setup
instructions there.
Start each boulder component with test configs (Ctrl-C kills all):
> ./start.py
Run tests:
> ./test.sh
Working with a client:
Check out the official Let's Encrypt client from https://github.com/letsencrypt/letsencrypt/ and follow the setup instructions there.
Component Model
---------------

View File

@ -9,6 +9,7 @@ import (
"crypto/rand"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
@ -219,79 +220,101 @@ type rpcError struct {
// Wraps a error in a rpcError so it can be marshalled to
// JSON.
func wrapError(err error) (rpcError rpcError) {
func wrapError(err error) *rpcError {
if err != nil {
rpcError.Value = err.Error()
wrapped := &rpcError{
Value: err.Error(),
}
switch err.(type) {
case core.InternalServerError:
rpcError.Type = "InternalServerError"
wrapped.Type = "InternalServerError"
case core.NotSupportedError:
rpcError.Type = "NotSupportedError"
wrapped.Type = "NotSupportedError"
case core.MalformedRequestError:
rpcError.Type = "MalformedRequestError"
wrapped.Type = "MalformedRequestError"
case core.UnauthorizedError:
rpcError.Type = "UnauthorizedError"
wrapped.Type = "UnauthorizedError"
case core.NotFoundError:
rpcError.Type = "NotFoundError"
wrapped.Type = "NotFoundError"
case core.SyntaxError:
rpcError.Type = "SyntaxError"
wrapped.Type = "SyntaxError"
case core.SignatureValidationError:
rpcError.Type = "SignatureValidationError"
wrapped.Type = "SignatureValidationError"
case core.CertificateIssuanceError:
rpcError.Type = "CertificateIssuanceError"
wrapped.Type = "CertificateIssuanceError"
case core.NoSuchRegistrationError:
rpcError.Type = "NoSuchRegistrationError"
wrapped.Type = "NoSuchRegistrationError"
case core.TooManyRPCRequestsError:
rpcError.Type = "TooManyRPCRequestsError"
wrapped.Type = "TooManyRPCRequestsError"
case core.RateLimitedError:
rpcError.Type = "RateLimitedError"
wrapped.Type = "RateLimitedError"
case core.ServiceUnavailableError:
rpcError.Type = "ServiceUnavailableError"
wrapped.Type = "ServiceUnavailableError"
}
return wrapped
}
return
return nil
}
// Unwraps a rpcError and returns the correct error type.
func unwrapError(rpcError rpcError) (err error) {
if rpcError.Value != "" {
func unwrapError(rpcError *rpcError) error {
if rpcError != nil {
switch rpcError.Type {
case "InternalServerError":
err = core.InternalServerError(rpcError.Value)
return core.InternalServerError(rpcError.Value)
case "NotSupportedError":
err = core.NotSupportedError(rpcError.Value)
return core.NotSupportedError(rpcError.Value)
case "MalformedRequestError":
err = core.MalformedRequestError(rpcError.Value)
return core.MalformedRequestError(rpcError.Value)
case "UnauthorizedError":
err = core.UnauthorizedError(rpcError.Value)
return core.UnauthorizedError(rpcError.Value)
case "NotFoundError":
err = core.NotFoundError(rpcError.Value)
return core.NotFoundError(rpcError.Value)
case "SyntaxError":
err = core.SyntaxError(rpcError.Value)
return core.SyntaxError(rpcError.Value)
case "SignatureValidationError":
err = core.SignatureValidationError(rpcError.Value)
return core.SignatureValidationError(rpcError.Value)
case "CertificateIssuanceError":
err = core.CertificateIssuanceError(rpcError.Value)
return core.CertificateIssuanceError(rpcError.Value)
case "NoSuchRegistrationError":
err = core.NoSuchRegistrationError(rpcError.Value)
return core.NoSuchRegistrationError(rpcError.Value)
case "TooManyRPCRequestsError":
err = core.TooManyRPCRequestsError(rpcError.Value)
return core.TooManyRPCRequestsError(rpcError.Value)
case "RateLimitedError":
err = core.RateLimitedError(rpcError.Value)
return core.RateLimitedError(rpcError.Value)
case "ServiceUnavailableError":
err = core.ServiceUnavailableError(rpcError.Value)
return core.ServiceUnavailableError(rpcError.Value)
default:
err = errors.New(rpcError.Value)
return errors.New(rpcError.Value)
}
}
return
return nil
}
// rpcResponse is a stuct for wire-representation of response messages
// used by DispatchSync
type rpcResponse struct {
ReturnVal []byte `json:"returnVal,omitempty"`
Error rpcError `json:"error,omitempty"`
ReturnVal []byte `json:"returnVal"`
Error *rpcError `json:"error,omitempty"`
}
// Hack: Some of our RPCs return DER directly. If we log it naively it will
// just be a bunch of numbers. It's easy to detect DER, so we use this function
// before logging to base64-encode anything that looks like DER.
func safeDER(input []byte) string {
if len(input) > 0 && input[0] == 0x30 {
return string(base64.RawStdEncoding.EncodeToString(input))
}
return string(input)
}
// Used for debug logging
func (r rpcResponse) debugString() string {
ret := safeDER(r.ReturnVal)
if r.Error == nil {
return ret
}
return fmt.Sprintf("%s, RPCERR: %s", ret, r.Error)
}
// AmqpChannel sets a AMQP connection up using SSL if configuration is provided
@ -370,10 +393,10 @@ func AmqpChannel(conf cmd.Config) (*amqp.Channel, error) {
func (rpc *AmqpRPCServer) processMessage(msg amqp.Delivery) {
// XXX-JWS: jws.Verify(body)
cb, present := rpc.dispatchTable[msg.Type]
rpc.log.Info(fmt.Sprintf(" [s<][%s][%s] received %s(%s) [%s]", rpc.serverQueue, msg.ReplyTo, msg.Type, core.B64enc(msg.Body), msg.CorrelationId))
rpc.log.Info(fmt.Sprintf(" [s<][%s][%s] received %s(%s) [%s]", rpc.serverQueue, msg.ReplyTo, msg.Type, safeDER(msg.Body), msg.CorrelationId))
if !present {
// AUDIT[ Misrouted Messages ] f523f21f-12d2-4c31-b2eb-ee4b7d96d60e
rpc.log.Audit(fmt.Sprintf(" [s<][%s][%s] Misrouted message: %s - %s - %s", rpc.serverQueue, msg.ReplyTo, msg.Type, core.B64enc(msg.Body), msg.CorrelationId))
rpc.log.Audit(fmt.Sprintf(" [s<][%s][%s] Misrouted message: %s - %s - %s", rpc.serverQueue, msg.ReplyTo, msg.Type, safeDER(msg.Body), msg.CorrelationId))
return
}
var response rpcResponse
@ -386,10 +409,7 @@ func (rpc *AmqpRPCServer) processMessage(msg amqp.Delivery) {
rpc.log.Audit(fmt.Sprintf(" [s>][%s][%s] Error condition marshalling RPC response %s [%s]", rpc.serverQueue, msg.ReplyTo, msg.Type, msg.CorrelationId))
return
}
if response.Error.Value != "" {
rpc.log.Info(fmt.Sprintf(" [s>][%s][%s] %s failed, replying: %s (%s) [%s]", rpc.serverQueue, msg.ReplyTo, msg.Type, response.Error.Value, response.Error.Type, msg.CorrelationId))
}
rpc.log.Debug(fmt.Sprintf(" [s>][%s][%s] replying %s(%s) [%s]", rpc.serverQueue, msg.ReplyTo, msg.Type, core.B64enc(jsonResponse), msg.CorrelationId))
rpc.log.Debug(fmt.Sprintf(" [s>][%s][%s] replying %s: %s [%s]", rpc.serverQueue, msg.ReplyTo, msg.Type, response.debugString(), msg.CorrelationId))
rpc.connection.publish(
msg.ReplyTo,
msg.CorrelationId,
@ -584,7 +604,6 @@ func NewAmqpRPCClient(clientQueuePrefix, serverQueue string, c cmd.Config, stats
continue
}
rpc.log.Debug(fmt.Sprintf(" [c<][%s] response %s(%s) [%s]", clientQueue, msg.Type, core.B64enc(msg.Body), corrID))
responseChan <- msg.Body
rpc.mu.Lock()
delete(rpc.pending, corrID)
@ -618,13 +637,18 @@ func (rpc *AmqpRPCCLient) dispatch(method string, body []byte) (string, chan []b
// At least in some cases, it's important that this channel
// be buffered to avoid deadlock
responseChan := make(chan []byte, 1)
corrID := core.NewToken()
corrIDBytes := make([]byte, 8)
_, err := rand.Read(corrIDBytes)
if err != nil {
panic("randomness failed")
}
corrID := base64.RawURLEncoding.EncodeToString(corrIDBytes)
rpc.mu.Lock()
rpc.pending[corrID] = responseChan
rpc.mu.Unlock()
// Send the request
rpc.log.Debug(fmt.Sprintf(" [c>][%s] requesting %s(%s) [%s]", rpc.clientQueue, method, core.B64enc(body), corrID))
rpc.log.Debug(fmt.Sprintf(" [c>][%s] requesting %s(%s) [%s]", rpc.clientQueue, method, safeDER(body), corrID))
rpc.connection.publish(
rpc.serverQueue,
corrID,
@ -645,6 +669,7 @@ func (rpc *AmqpRPCCLient) DispatchSync(method string, body []byte) (response []b
case jsonResponse := <-responseChan:
var rpcResponse rpcResponse
err = json.Unmarshal(jsonResponse, &rpcResponse)
rpc.log.Debug(fmt.Sprintf(" [c<][%s] response %s: %s [%s]", rpc.clientQueue, method, rpcResponse.debugString(), corrID))
if err != nil {
return
}

View File

@ -2,7 +2,7 @@
"syslog": {
"network": "",
"server": "",
"stdoutlevel": -1
"stdoutlevel": 7
},
"amqp": {

View File

@ -32,6 +32,7 @@ import (
)
const maxRedirect = 10
const whitespaceCutset = "\n\t "
var validationTimeout = time.Second * 5
@ -92,7 +93,7 @@ func verifyValidationJWS(validation *jose.JsonWebSignature, accountKey *jose.Jso
return fmt.Errorf("Validation JWS not signed")
}
payload, _, err := validation.Verify(accountKey)
payload, err := validation.Verify(accountKey)
if err != nil {
return fmt.Errorf("Validation JWS failed to verify: %s", err.Error())
}
@ -411,8 +412,10 @@ func (va *ValidationAuthorityImpl) validateSimpleHTTP(identifier core.AcmeIdenti
return challenge, err
}
payload := strings.TrimRight(string(body), whitespaceCutset)
// Parse and verify JWS
parsedJws, err := jose.ParseSigned(string(body))
parsedJws, err := jose.ParseSigned(payload)
if err != nil {
err = fmt.Errorf("Validation response failed to parse as JWS: %s", err.Error())
va.log.Debug(err.Error())
@ -511,8 +514,10 @@ func (va *ValidationAuthorityImpl) validateHTTP01(identifier core.AcmeIdentifier
return challenge, err
}
payload := strings.TrimRight(string(body), whitespaceCutset)
// Parse body as a key authorization object
serverKeyAuthorization, err := core.NewKeyAuthorizationFromString(string(body))
serverKeyAuthorization, err := core.NewKeyAuthorizationFromString(payload)
if err != nil {
err = fmt.Errorf("Error parsing key authorization file: %s", err.Error())
va.log.Debug(err.Error())

View File

@ -87,7 +87,7 @@ func createValidation(token string, enableTLS bool) string {
"tls": enableTLS,
})
signer, _ := jose.NewSigner(jose.RS256, &TheKey)
obj, _ := signer.Sign(payload, "")
obj, _ := signer.Sign(payload)
return obj.FullSerialize()
}
@ -306,6 +306,7 @@ func TestSimpleHttp(t *testing.T) {
va = NewValidationAuthorityImpl(&PortConfig{HTTPPort: goodPort}, nil, stats, clock.Default())
va.DNSResolver = &mocks.DNSResolver{}
log.Clear()
finChall, err := va.validateSimpleHTTP(ident, chall)
test.AssertEquals(t, finChall.Status, core.StatusValid)
@ -496,7 +497,7 @@ func TestDvsni(t *testing.T) {
"token": chall.Token,
})
signer, _ := jose.NewSigner(jose.RS256, &TheKey)
chall.Validation, _ = signer.Sign(validationPayload, "")
chall.Validation, _ = signer.Sign(validationPayload)
log.Clear()
started := time.Now()
@ -588,9 +589,9 @@ func httpSrv(t *testing.T, token string) *httptest.Server {
t.Logf("HTTPSRV: Path = %s\n", r.URL.Path)
keyAuthz, _ := core.NewKeyAuthorization(currentToken, accountKey)
t.Logf("HTTPSRV: Key Authz = %s\n", keyAuthz.String())
t.Logf("HTTPSRV: Key Authz = '%s%s'\n", keyAuthz.String(), "\\n \\t")
fmt.Fprint(w, keyAuthz.String())
fmt.Fprint(w, keyAuthz.String(), "\n \t")
currentToken = defaultToken
}
})
@ -680,6 +681,7 @@ func TestHttp(t *testing.T) {
va = NewValidationAuthorityImpl(&PortConfig{HTTPPort: goodPort}, nil, stats, clock.Default())
va.DNSResolver = &mocks.DNSResolver{}
log.Clear()
t.Logf("Trying to validate: %+v\n", chall)
finChall, err := va.validateHTTP01(ident, chall)
@ -968,7 +970,7 @@ func createChallenge(challengeType string) core.Challenge {
"token": chall.Token,
})
signer, _ := jose.NewSigner(jose.RS256, &TheKey)
chall.Validation, _ = signer.Sign(validationPayload, "")
chall.Validation, _ = signer.Sign(validationPayload)
return chall
}

View File

@ -427,7 +427,7 @@ func (wfe *WebFrontEndImpl) verifyPOST(logEvent *requestEvent, request *http.Req
return nil, nil, reg, err
}
payload, header, err := parsedJws.Verify(key)
payload, err := parsedJws.Verify(key)
if err != nil {
puberr := core.SignatureValidationError("JWS verification error")
wfe.stats.Inc("WFE.Errors.JWSVerificationFailed", 1, 1.0)
@ -440,13 +440,13 @@ func (wfe *WebFrontEndImpl) verifyPOST(logEvent *requestEvent, request *http.Req
}
// Check that the request has a known anti-replay nonce
// i.e., Nonce is in protected header and
if err != nil || len(header.Nonce) == 0 {
nonce := parsedJws.Signatures[0].Header.Nonce
if err != nil || len(nonce) == 0 {
wfe.stats.Inc("WFE.Errors.JWSMissingNonce", 1, 1.0)
logEvent.AddError("JWS is missing an anti-replay nonce")
err = core.SignatureValidationError("JWS has no anti-replay nonce")
return nil, nil, reg, err
} else if !wfe.nonceService.Valid(header.Nonce) {
} else if !wfe.nonceService.Valid(nonce) {
wfe.stats.Inc("WFE.Errors.JWSInvalidNonce", 1, 1.0)
logEvent.AddError("JWS has an invalid anti-replay nonce")
err = core.SignatureValidationError(fmt.Sprintf("JWS has invalid anti-replay nonce"))

View File

@ -189,9 +189,8 @@ func signRequest(t *testing.T, req string, nonceService *core.NonceService) stri
signer, err := jose.NewSigner("RS256", accountKey)
test.AssertNotError(t, err, "Failed to make signer")
nonce, err := nonceService.Nonce()
test.AssertNotError(t, err, "Failed to make nonce")
result, err := signer.Sign([]byte(req), nonce)
signer.SetNonceSource(nonceService)
result, err := signer.Sign([]byte(req))
test.AssertNotError(t, err, "Failed to sign req")
ret := result.FullSerialize()
return ret
@ -752,20 +751,11 @@ func TestNewRegistration(t *testing.T) {
test.Assert(t, ok, "Couldn't load RSA key")
signer, err := jose.NewSigner("RS256", rsaKey)
test.AssertNotError(t, err, "Failed to make signer")
nonce, err := wfe.nonceService.Nonce()
test.AssertNotError(t, err, "Unable to create nonce")
result, err := signer.Sign([]byte("foo"), nonce)
nonce, err = wfe.nonceService.Nonce()
test.AssertNotError(t, err, "Unable to create nonce")
fooBody, err := signer.Sign([]byte("foo"), nonce)
signer.SetNonceSource(wfe.nonceService)
fooBody, err := signer.Sign([]byte("foo"))
test.AssertNotError(t, err, "Unable to sign")
nonce, err = wfe.nonceService.Nonce()
test.AssertNotError(t, err, "Unable to create nonce")
wrongAgreementBody, err := signer.Sign(
[]byte(`{"resource":"new-reg","contact":["tel:123456789"],"agreement":"https://letsencrypt.org/im-bad"}`),
nonce)
wrongAgreementBody, err := signer.Sign([]byte(`{"resource":"new-reg","contact":["tel:123456789"],"agreement":"https://letsencrypt.org/im-bad"}`))
test.AssertNotError(t, err, "Unable to sign")
type newRegErrorTest struct {
@ -837,9 +827,7 @@ func TestNewRegistration(t *testing.T) {
}
responseWriter := httptest.NewRecorder()
nonce, err = wfe.nonceService.Nonce()
test.AssertNotError(t, err, "Unable to create nonce")
result, err = signer.Sign([]byte(`{"resource":"new-reg","contact":["tel:123456789"],"agreement":"`+agreementURL+`"}`), nonce)
result, err := signer.Sign([]byte(`{"resource":"new-reg","contact":["tel:123456789"],"agreement":"` + agreementURL + `"}`))
wfe.NewRegistration(newRequestEvent(), responseWriter,
makePostRequest(result.FullSerialize()))
@ -868,13 +856,12 @@ func TestNewRegistration(t *testing.T) {
test.Assert(t, ok, "Couldn't load RSA key")
signer, err = jose.NewSigner("RS256", rsaKey)
test.AssertNotError(t, err, "Failed to make signer")
signer.SetNonceSource(wfe.nonceService)
// Reset the body and status code
responseWriter = httptest.NewRecorder()
// POST, Valid JSON, Key already in use
nonce, err = wfe.nonceService.Nonce()
test.AssertNotError(t, err, "Unable to create nonce")
result, err = signer.Sign([]byte(`{"resource":"new-reg","contact":["tel:123456789"],"agreement":"`+agreementURL+`"}`), nonce)
result, err = signer.Sign([]byte(`{"resource":"new-reg","contact":["tel:123456789"],"agreement":"` + agreementURL + `"}`))
wfe.NewRegistration(newRequestEvent(), responseWriter,
makePostRequest(result.FullSerialize()))
@ -941,9 +928,8 @@ func TestRevokeCertificateCertKey(t *testing.T) {
wfe.SA = &mockSANoSuchRegistration{mocks.NewStorageAuthority(fc)}
responseWriter := httptest.NewRecorder()
nonce, err := wfe.nonceService.Nonce()
test.AssertNotError(t, err, "Unable to create nonce")
result, _ := signer.Sign(revokeRequestJSON, nonce)
signer.SetNonceSource(wfe.nonceService)
result, _ := signer.Sign(revokeRequestJSON)
wfe.RevokeCertificate(newRequestEvent(), responseWriter,
makePostRequest(result.FullSerialize()))
test.AssertEquals(t, responseWriter.Code, 200)
@ -965,9 +951,8 @@ func TestRevokeCertificateAccountKey(t *testing.T) {
test.Assert(t, ok, "Couldn't load RSA key")
accountKeySigner, err := jose.NewSigner("RS256", test1Key)
test.AssertNotError(t, err, "Failed to make signer")
nonce, err := wfe.nonceService.Nonce()
test.AssertNotError(t, err, "Unable to create nonce")
result, _ := accountKeySigner.Sign(revokeRequestJSON, nonce)
accountKeySigner.SetNonceSource(wfe.nonceService)
result, _ := accountKeySigner.Sign(revokeRequestJSON)
wfe.RevokeCertificate(newRequestEvent(), responseWriter,
makePostRequest(result.FullSerialize()))
test.AssertEquals(t, responseWriter.Code, 200)
@ -977,8 +962,6 @@ func TestRevokeCertificateAccountKey(t *testing.T) {
// A revocation request signed by an unauthorized key.
func TestRevokeCertificateWrongKey(t *testing.T) {
wfe, _ := setupWFE(t)
nonce, err := wfe.nonceService.Nonce()
test.AssertNotError(t, err, "Unable to create nonce")
responseWriter := httptest.NewRecorder()
test2JWK, err := jose.LoadPrivateKey([]byte(test2KeyPrivatePEM))
test.AssertNotError(t, err, "Failed to load key")
@ -986,12 +969,11 @@ func TestRevokeCertificateWrongKey(t *testing.T) {
test.Assert(t, ok, "Couldn't load RSA key")
accountKeySigner2, err := jose.NewSigner("RS256", test2Key)
test.AssertNotError(t, err, "Failed to make signer")
nonce, err = wfe.nonceService.Nonce()
test.AssertNotError(t, err, "Unable to create nonce")
accountKeySigner2.SetNonceSource(wfe.nonceService)
revokeRequestJSON, err := makeRevokeRequestJSON()
test.AssertNotError(t, err, "Unable to create revoke request")
result, _ := accountKeySigner2.Sign(revokeRequestJSON, nonce)
result, _ := accountKeySigner2.Sign(revokeRequestJSON)
wfe.RevokeCertificate(newRequestEvent(), responseWriter,
makePostRequest(result.FullSerialize()))
test.AssertEquals(t, responseWriter.Code, 403)
@ -1031,9 +1013,8 @@ func TestRevokeCertificateAlreadyRevoked(t *testing.T) {
wfe.stats, _ = statsd.NewNoopClient()
responseWriter := httptest.NewRecorder()
responseWriter.Body.Reset()
nonce, err := wfe.nonceService.Nonce()
test.AssertNotError(t, err, "Unable to create nonce")
result, _ := signer.Sign(revokeRequestJSON, nonce)
signer.SetNonceSource(wfe.nonceService)
result, _ := signer.Sign(revokeRequestJSON)
wfe.RevokeCertificate(newRequestEvent(), responseWriter,
makePostRequest(result.FullSerialize()))
test.AssertEquals(t, responseWriter.Code, 409)
@ -1179,9 +1160,8 @@ func TestRegistration(t *testing.T) {
test.AssertNotError(t, err, "Failed to make signer")
// Test POST valid JSON but key is not registered
nonce, err := wfe.nonceService.Nonce()
test.AssertNotError(t, err, "Unable to create nonce")
result, err := signer.Sign([]byte(`{"resource":"reg","agreement":"`+agreementURL+`"}`), nonce)
signer.SetNonceSource(wfe.nonceService)
result, err := signer.Sign([]byte(`{"resource":"reg","agreement":"` + agreementURL + `"}`))
test.AssertNotError(t, err, "Unable to sign")
wfe.Registration(newRequestEvent(), responseWriter,
makePostRequestWithPath("/2", result.FullSerialize()))
@ -1198,9 +1178,8 @@ func TestRegistration(t *testing.T) {
test.AssertNotError(t, err, "Failed to make signer")
// Test POST valid JSON with registration up in the mock (with incorrect agreement URL)
nonce, err = wfe.nonceService.Nonce()
test.AssertNotError(t, err, "Unable to create nonce")
result, err = signer.Sign([]byte(`{"resource":"reg","agreement":"https://letsencrypt.org/im-bad"}`), nonce)
signer.SetNonceSource(wfe.nonceService)
result, err = signer.Sign([]byte(`{"resource":"reg","agreement":"https://letsencrypt.org/im-bad"}`))
// Test POST valid JSON with registration up in the mock
wfe.Registration(newRequestEvent(), responseWriter,
@ -1211,9 +1190,7 @@ func TestRegistration(t *testing.T) {
responseWriter.Body.Reset()
// Test POST valid JSON with registration up in the mock (with correct agreement URL)
nonce, err = wfe.nonceService.Nonce()
test.AssertNotError(t, err, "Unable to create nonce")
result, err = signer.Sign([]byte(`{"resource":"reg","agreement":"`+agreementURL+`"}`), nonce)
result, err = signer.Sign([]byte(`{"resource":"reg","agreement":"` + agreementURL + `"}`))
test.AssertNotError(t, err, "Couldn't sign")
wfe.Registration(newRequestEvent(), responseWriter,
makePostRequestWithPath("/1", result.FullSerialize()))