Refactor DNSSEC to use crypto.{PrivateKey,Signer}

This will allow RRSIG.Sign to use generic crypto.Signer implementations.

This is a interface breaking change, even if the required changes are most
likely just type asserions from crypto.PrivateKey to the underlying type or
crypto.Signer.
This commit is contained in:
Filippo Valsorda 2015-03-26 15:34:52 -07:00
parent c50a9fc91d
commit 034c247229
8 changed files with 234 additions and 271 deletions

237
dnssec.go
View File

@ -6,14 +6,14 @@ import (
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/md5" _ "crypto/md5"
"crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/sha1" _ "crypto/sha1"
"crypto/sha256" _ "crypto/sha256"
"crypto/sha512" _ "crypto/sha512"
"encoding/asn1"
"encoding/hex" "encoding/hex"
"hash"
"io"
"math/big" "math/big"
"sort" "sort"
"strings" "strings"
@ -42,6 +42,38 @@ const (
PRIVATEOID uint8 = 254 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. // DNSSEC hashing algorithm codes.
const ( const (
_ uint8 = iota _ uint8 = iota
@ -52,6 +84,18 @@ const (
SHA512 // Experimental 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. // DNSKEY flag values.
const ( const (
SEP = 1 SEP = 1
@ -168,24 +212,23 @@ func (k *DNSKEY) ToDS(h uint8) *DS {
// digest buffer // digest buffer
digest := append(owner, wire...) // another copy digest := append(owner, wire...) // another copy
var hash crypto.Hash
switch h { switch h {
case SHA1: case SHA1:
s := sha1.New() hash = crypto.SHA1
io.WriteString(s, string(digest))
ds.Digest = hex.EncodeToString(s.Sum(nil))
case SHA256: case SHA256:
s := sha256.New() hash = crypto.SHA256
io.WriteString(s, string(digest))
ds.Digest = hex.EncodeToString(s.Sum(nil))
case SHA384: case SHA384:
s := sha512.New384() hash = crypto.SHA384
io.WriteString(s, string(digest)) case SHA512:
ds.Digest = hex.EncodeToString(s.Sum(nil)) hash = crypto.SHA512
case GOST94:
/* I have no clue */
default: default:
return nil return nil
} }
s := hash.New()
s.Write(digest)
ds.Digest = hex.EncodeToString(s.Sum(nil))
return ds return ds
} }
@ -212,7 +255,7 @@ func (d *DS) ToCDS() *CDS {
// There is no check if RRSet is a proper (RFC 2181) RRSet. // 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 // If OrigTTL is non zero, it is used as-is, otherwise the TTL of the RRset
// is used as the OrigTTL. // is used as the OrigTTL.
func (rr *RRSIG) Sign(k PrivateKey, rrset []RR) error { func (rr *RRSIG) Sign(k crypto.Signer, rrset []RR) error {
if k == nil { if k == nil {
return ErrPrivKey return ErrPrivKey
} }
@ -258,39 +301,66 @@ func (rr *RRSIG) Sign(k PrivateKey, rrset []RR) error {
} }
signdata = append(signdata, wire...) signdata = append(signdata, wire...)
var h hash.Hash hash, ok := AlgorithmToHash[rr.Algorithm]
switch rr.Algorithm { if !ok {
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:
return ErrAlg return ErrAlg
} }
_, err = h.Write(signdata) h := hash.New()
if err != nil { h.Write(signdata)
return err
}
sighash := h.Sum(nil)
signature, err := k.Sign(sighash, rr.Algorithm) signature, err := sign(k, h.Sum(nil), hash, rr.Algorithm)
if err != nil { if err != nil {
return err return err
} }
rr.Signature = toBase64(signature) rr.Signature = toBase64(signature)
return nil 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 // Verify validates an RRSet with the signature and key. This is only the
// cryptographic test, the signature validity period must be checked separately. // 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. // This function copies the rdata of some RRs (to lowercase domain names) for the validation to work.
@ -355,6 +425,11 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
// remove the domain name and assume its our // remove the domain name and assume its our
} }
hash, ok := AlgorithmToHash[rr.Algorithm]
if !ok {
return ErrAlg
}
switch rr.Algorithm { switch rr.Algorithm {
case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512, RSAMD5: case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512, RSAMD5:
// TODO(mg): this can be done quicker, ie. cache the pubkey data somewhere?? // TODO(mg): this can be done quicker, ie. cache the pubkey data somewhere??
@ -362,52 +437,31 @@ func (rr *RRSIG) Verify(k *DNSKEY, rrset []RR) error {
if pubkey == nil { if pubkey == nil {
return ErrKey return ErrKey
} }
// Setup the hash as defined for this alg.
var h hash.Hash h := hash.New()
var ch crypto.Hash h.Write(signeddata)
switch rr.Algorithm { return rsa.VerifyPKCS1v15(pubkey, hash, h.Sum(nil), sigbuf)
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)
case ECDSAP256SHA256, ECDSAP384SHA384: case ECDSAP256SHA256, ECDSAP384SHA384:
pubkey := k.publicKeyECDSA() pubkey := k.publicKeyECDSA()
if pubkey == nil { if pubkey == nil {
return ErrKey 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 // Split sigbuf into the r and s coordinates
r := big.NewInt(0) r := new(big.Int).SetBytes(sigbuf[:len(sigbuf)/2])
r.SetBytes(sigbuf[:len(sigbuf)/2]) s := new(big.Int).SetBytes(sigbuf[len(sigbuf)/2:])
s := big.NewInt(0)
s.SetBytes(sigbuf[len(sigbuf)/2:]) h := hash.New()
if ecdsa.Verify(pubkey, sighash, r, s) { h.Write(signeddata)
if ecdsa.Verify(pubkey, h.Sum(nil), r, s) {
return nil return nil
} }
return ErrSig return ErrSig
default:
return ErrAlg
} }
// Unknown alg
return ErrAlg
} }
// ValidityPeriod uses RFC1982 serial arithmetic to calculate // ValidityPeriod uses RFC1982 serial arithmetic to calculate
@ -603,36 +657,3 @@ func rawSignatureData(rrset []RR, s *RRSIG) (buf []byte, err error) {
} }
return buf, nil 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 package dns
import ( import (
"crypto"
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
@ -15,7 +16,7 @@ import (
// what kind of DNSKEY will be generated. // what kind of DNSKEY will be generated.
// The ECDSA algorithms imply a fixed keysize, in that case // The ECDSA algorithms imply a fixed keysize, in that case
// bits should be set to the size of the algorithm. // 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 { switch k.Algorithm {
case DSA, DSANSEC3SHA1: case DSA, DSANSEC3SHA1:
if bits != 1024 { if bits != 1024 {
@ -52,14 +53,14 @@ func (k *DNSKEY) Generate(bits int) (PrivateKey, error) {
return nil, err return nil, err
} }
k.setPublicKeyDSA(params.Q, params.P, params.G, priv.PublicKey.Y) k.setPublicKeyDSA(params.Q, params.P, params.G, priv.PublicKey.Y)
return (*DSAPrivateKey)(priv), nil return priv, nil
case RSAMD5, RSASHA1, RSASHA256, RSASHA512, RSASHA1NSEC3SHA1: case RSAMD5, RSASHA1, RSASHA256, RSASHA512, RSASHA1NSEC3SHA1:
priv, err := rsa.GenerateKey(rand.Reader, bits) priv, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil { if err != nil {
return nil, err return nil, err
} }
k.setPublicKeyRSA(priv.PublicKey.E, priv.PublicKey.N) k.setPublicKeyRSA(priv.PublicKey.E, priv.PublicKey.N)
return (*RSAPrivateKey)(priv), nil return priv, nil
case ECDSAP256SHA256, ECDSAP384SHA384: case ECDSAP256SHA256, ECDSAP384SHA384:
var c elliptic.Curve var c elliptic.Curve
switch k.Algorithm { switch k.Algorithm {
@ -73,7 +74,7 @@ func (k *DNSKEY) Generate(bits int) (PrivateKey, error) {
return nil, err return nil, err
} }
k.setPublicKeyECDSA(priv.PublicKey.X, priv.PublicKey.Y) k.setPublicKeyECDSA(priv.PublicKey.X, priv.PublicKey.Y)
return (*ECDSAPrivateKey)(priv), nil return priv, nil
default: default:
return nil, ErrAlg return nil, ErrAlg
} }

View File

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

View File

@ -4,7 +4,6 @@ import (
"crypto" "crypto"
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand"
"crypto/rsa" "crypto/rsa"
"math/big" "math/big"
"strconv" "strconv"
@ -12,133 +11,75 @@ import (
const format = "Private-key-format: v1.3\n" 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 // 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). // 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 // It needs some info from the key (the algorithm), so its a method of the DNSKEY
// DNSKEY and calls PrivateKey.String(alg). // It supports rsa.PrivateKey, ecdsa.PrivateKey and dsa.PrivateKey
func (r *DNSKEY) PrivateKeyString(p PrivateKey) string { func (r *DNSKEY) PrivateKeyString(p crypto.PrivateKey) string {
return p.String(r.Algorithm) 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: 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,6 +1,9 @@
package dns package dns
import ( import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
@ -192,7 +195,7 @@ func TestSignVerify(t *testing.T) {
sig.Algorithm = RSASHA256 sig.Algorithm = RSASHA256
for _, r := range []RR{soa, soa1, srv} { for _, r := range []RR{soa, soa1, srv} {
if sig.Sign(privkey, []RR{r}) != nil { if sig.Sign(privkey.(*rsa.PrivateKey), []RR{r}) != nil {
t.Error("failure to sign the record") t.Error("failure to sign the record")
continue continue
} }
@ -228,7 +231,7 @@ func Test65534(t *testing.T) {
sig.KeyTag = key.KeyTag() sig.KeyTag = key.KeyTag()
sig.SignerName = key.Hdr.Name sig.SignerName = key.Hdr.Name
sig.Algorithm = RSASHA256 sig.Algorithm = RSASHA256
if err := sig.Sign(privkey, []RR{t6}); err != nil { if err := sig.Sign(privkey.(*rsa.PrivateKey), []RR{t6}); err != nil {
t.Error(err) t.Error(err)
t.Error("failure to sign the TYPE65534 record") t.Error("failure to sign the TYPE65534 record")
} }
@ -324,7 +327,7 @@ func TestKeyRSA(t *testing.T) {
sig.KeyTag = key.KeyTag() sig.KeyTag = key.KeyTag()
sig.SignerName = key.Hdr.Name sig.SignerName = key.Hdr.Name
if err := sig.Sign(priv, []RR{soa}); err != nil { if err := sig.Sign(priv.(*rsa.PrivateKey), []RR{soa}); err != nil {
t.Error("failed to sign") t.Error("failed to sign")
return return
} }
@ -374,7 +377,7 @@ Activate: 20110302104537`
t.Error(err) t.Error(err)
} }
switch priv := p.(type) { switch priv := p.(type) {
case *RSAPrivateKey: case *rsa.PrivateKey:
if 65537 != priv.PublicKey.E { if 65537 != priv.PublicKey.E {
t.Error("exponenent should be 65537") t.Error("exponenent should be 65537")
} }
@ -403,7 +406,7 @@ Activate: 20110302104537`
sig.SignerName = k.Hdr.Name sig.SignerName = k.Hdr.Name
sig.Algorithm = k.Algorithm sig.Algorithm = k.Algorithm
sig.Sign(p, []RR{soa}) sig.Sign(p.(*rsa.PrivateKey), []RR{soa})
if sig.Signature != "D5zsobpQcmMmYsUMLxCVEtgAdCvTu8V/IEeP4EyLBjqPJmjt96bwM9kqihsccofA5LIJ7DN91qkCORjWSTwNhzCv7bMyr2o5vBZElrlpnRzlvsFIoAZCD9xg6ZY7ZyzUJmU6IcTwG4v3xEYajcpbJJiyaw/RqR90MuRdKPiBzSo=" { if sig.Signature != "D5zsobpQcmMmYsUMLxCVEtgAdCvTu8V/IEeP4EyLBjqPJmjt96bwM9kqihsccofA5LIJ7DN91qkCORjWSTwNhzCv7bMyr2o5vBZElrlpnRzlvsFIoAZCD9xg6ZY7ZyzUJmU6IcTwG4v3xEYajcpbJJiyaw/RqR90MuRdKPiBzSo=" {
t.Errorf("signature is not correct: %v", sig) t.Errorf("signature is not correct: %v", sig)
} }
@ -443,7 +446,7 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
sig.SignerName = eckey.(*DNSKEY).Hdr.Name sig.SignerName = eckey.(*DNSKEY).Hdr.Name
sig.Algorithm = eckey.(*DNSKEY).Algorithm sig.Algorithm = eckey.(*DNSKEY).Algorithm
if sig.Sign(privkey, []RR{a}) != nil { if sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{a}) != nil {
t.Fatal("failure to sign the record") t.Fatal("failure to sign the record")
} }
@ -491,7 +494,7 @@ func TestSignVerifyECDSA2(t *testing.T) {
sig.SignerName = key.Hdr.Name sig.SignerName = key.Hdr.Name
sig.Algorithm = ECDSAP256SHA256 sig.Algorithm = ECDSAP256SHA256
if sig.Sign(privkey, []RR{srv}) != nil { if sig.Sign(privkey.(*ecdsa.PrivateKey), []RR{srv}) != nil {
t.Fatal("failure to sign the record") t.Fatal("failure to sign the record")
} }
@ -564,7 +567,7 @@ PrivateKey: GU6SnQ/Ou+xC5RumuIUIuJZteXT2z0O/ok1s38Et6mQ=`
} }
ourRRSIG.Expiration, _ = StringToTime("20100909100439") ourRRSIG.Expiration, _ = StringToTime("20100909100439")
ourRRSIG.Inception, _ = StringToTime("20100812100439") ourRRSIG.Inception, _ = StringToTime("20100812100439")
err = ourRRSIG.Sign(priv, []RR{rrA}) err = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -640,7 +643,7 @@ PrivateKey: WURgWHCcYIYUPWgeLmiPY2DJJk02vgrmTfitxgqcL4vwW7BOrbawVmVe0d9V94SR`
} }
ourRRSIG.Expiration, _ = StringToTime("20100909102025") ourRRSIG.Expiration, _ = StringToTime("20100909102025")
ourRRSIG.Inception, _ = StringToTime("20100812102025") ourRRSIG.Inception, _ = StringToTime("20100812102025")
err = ourRRSIG.Sign(priv, []RR{rrA}) err = ourRRSIG.Sign(priv.(*ecdsa.PrivateKey), []RR{rrA})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -709,7 +712,7 @@ func TestInvalidRRSet(t *testing.T) {
} }
// Sign the good record set and then make sure verification fails on the bad record set // Sign the good record set and then make sure verification fails on the bad record set
if err := signature.Sign(privatekey, goodRecords); err != nil { if err := signature.Sign(privatekey.(crypto.Signer), goodRecords); err != nil {
t.Fatal("Signing good records failed") t.Fatal("Signing good records failed")
} }

View File

@ -1237,8 +1237,8 @@ func TestNewPrivateKey(t *testing.T) {
} }
switch newPrivKey := newPrivKey.(type) { switch newPrivKey := newPrivKey.(type) {
case *RSAPrivateKey: case *rsa.PrivateKey:
(*rsa.PrivateKey)(newPrivKey).Precompute() newPrivKey.Precompute()
} }
if !reflect.DeepEqual(privkey, newPrivKey) { if !reflect.DeepEqual(privkey, newPrivKey) {

25
sig0.go
View File

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

View File

@ -1,6 +1,7 @@
package dns package dns
import ( import (
"crypto"
"testing" "testing"
"time" "time"
) )
@ -11,7 +12,7 @@ func TestSIG0(t *testing.T) {
} }
m := new(Msg) m := new(Msg)
m.SetQuestion("example.org.", TypeSOA) m.SetQuestion("example.org.", TypeSOA)
for _, alg := range []uint8{DSA, ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256, RSASHA512} { for _, alg := range []uint8{ECDSAP256SHA256, ECDSAP384SHA384, RSASHA1, RSASHA256, RSASHA512} {
algstr := AlgorithmToString[alg] algstr := AlgorithmToString[alg]
keyrr := new(KEY) keyrr := new(KEY)
keyrr.Hdr.Name = algstr + "." keyrr.Hdr.Name = algstr + "."
@ -40,7 +41,7 @@ func TestSIG0(t *testing.T) {
sigrr.Inception = now - 300 sigrr.Inception = now - 300
sigrr.KeyTag = keyrr.KeyTag() sigrr.KeyTag = keyrr.KeyTag()
sigrr.SignerName = keyrr.Hdr.Name sigrr.SignerName = keyrr.Hdr.Name
mb, err := sigrr.Sign(pk, m) mb, err := sigrr.Sign(pk.(crypto.Signer), m)
if err != nil { if err != nil {
t.Errorf("Failed to sign message using “%s”: %v", algstr, err) t.Errorf("Failed to sign message using “%s”: %v", algstr, err)
continue continue
@ -79,7 +80,7 @@ func TestSIG0(t *testing.T) {
} }
sigrr.Expiration = 2 sigrr.Expiration = 2
sigrr.Inception = 1 sigrr.Inception = 1
mb, _ = sigrr.Sign(pk, m) mb, _ = sigrr.Sign(pk.(crypto.Signer), m)
if err := sigrr.Verify(keyrr, mb); err == nil { if err := sigrr.Verify(keyrr, mb); err == nil {
t.Errorf("Verify succeeded on an expired message using “%s”", algstr) t.Errorf("Verify succeeded on an expired message using “%s”", algstr)
continue continue