dns/dnssec_keyscan.go

260 lines
5.6 KiB
Go
Raw Normal View History

2011-12-15 16:38:14 +00:00
package dns
import (
"crypto"
2012-05-05 15:37:48 +00:00
"crypto/dsa"
2011-12-16 16:32:15 +00:00
"crypto/ecdsa"
"crypto/rsa"
2011-12-15 16:38:14 +00:00
"io"
2011-12-16 16:32:15 +00:00
"math/big"
"strconv"
2011-12-16 16:32:15 +00:00
"strings"
2011-12-15 16:38:14 +00:00
)
// 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) (crypto.PrivateKey, error) {
if s == "" || s[len(s)-1] != '\n' { // We need a closing newline
return k.ReadPrivateKey(strings.NewReader(s+"\n"), "")
}
return k.ReadPrivateKey(strings.NewReader(s), "")
}
2014-01-26 13:05:48 +00:00
// ReadPrivateKey reads a private key from the io.Reader q. The string file is
2012-09-02 19:22:24 +00:00
// 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) (crypto.PrivateKey, error) {
m, err := parseKey(q, file)
2011-12-16 16:32:15 +00:00
if m == nil {
return nil, err
2011-12-16 16:32:15 +00:00
}
if _, ok := m["private-key-format"]; !ok {
return nil, ErrPrivKey
}
if m["private-key-format"] != "v1.2" && m["private-key-format"] != "v1.3" {
return nil, ErrPrivKey
}
// TODO(mg): check if the pubkey matches the private key
algo, err := strconv.ParseUint(strings.SplitN(m["algorithm"], " ", 2)[0], 10, 8)
if err != nil {
return nil, ErrPrivKey
}
switch uint8(algo) {
case DSA:
priv, err := readPrivateKeyDSA(m)
if err != nil {
return nil, err
2012-04-17 09:39:58 +00:00
}
pub := k.publicKeyDSA()
if pub == nil {
return nil, ErrKey
2012-04-17 09:39:58 +00:00
}
priv.PublicKey = *pub
return priv, nil
case RSAMD5:
2012-04-11 13:13:17 +00:00
fallthrough
case RSASHA1:
2012-04-11 13:13:17 +00:00
fallthrough
case RSASHA1NSEC3SHA1:
2012-04-17 09:39:58 +00:00
fallthrough
case RSASHA256:
2012-04-11 13:13:17 +00:00
fallthrough
case RSASHA512:
priv, err := readPrivateKeyRSA(m)
if err != nil {
return nil, err
2012-04-15 19:00:17 +00:00
}
pub := k.publicKeyRSA()
if pub == nil {
return nil, ErrKey
}
priv.PublicKey = *pub
return priv, nil
case ECCGOST:
return nil, ErrPrivKey
case ECDSAP256SHA256:
fallthrough
case ECDSAP384SHA384:
priv, err := readPrivateKeyECDSA(m)
if err != nil {
return nil, err
2012-04-15 19:00:17 +00:00
}
pub := k.publicKeyECDSA()
if pub == nil {
return nil, ErrKey
}
priv.PublicKey = *pub
return priv, nil
default:
return nil, ErrPrivKey
2011-12-16 16:32:15 +00:00
}
}
// Read a private key (file) string and create a public key. Return the private key.
func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) {
2011-12-16 16:32:15 +00:00
p := new(rsa.PrivateKey)
p.Primes = []*big.Int{nil, nil}
for k, v := range m {
switch k {
case "modulus", "publicexponent", "privateexponent", "prime1", "prime2":
v1, err := fromBase64([]byte(v))
2011-12-16 16:32:15 +00:00
if err != nil {
return nil, err
}
switch k {
case "modulus":
p.PublicKey.N = big.NewInt(0)
p.PublicKey.N.SetBytes(v1)
case "publicexponent":
i := big.NewInt(0)
i.SetBytes(v1)
p.PublicKey.E = int(i.Int64()) // int64 should be large enough
case "privateexponent":
p.D = big.NewInt(0)
p.D.SetBytes(v1)
case "prime1":
p.Primes[0] = big.NewInt(0)
p.Primes[0].SetBytes(v1)
case "prime2":
p.Primes[1] = big.NewInt(0)
p.Primes[1].SetBytes(v1)
}
case "exponent1", "exponent2", "coefficient":
// not used in Go (yet)
case "created", "publish", "activate":
// not used in Go (yet)
}
}
return p, nil
}
func readPrivateKeyDSA(m map[string]string) (*dsa.PrivateKey, error) {
2012-04-17 09:39:58 +00:00
p := new(dsa.PrivateKey)
p.X = big.NewInt(0)
for k, v := range m {
switch k {
case "private_value(x)":
v1, err := fromBase64([]byte(v))
2012-04-17 09:39:58 +00:00
if err != nil {
return nil, err
}
p.X.SetBytes(v1)
case "created", "publish", "activate":
2012-04-17 09:39:58 +00:00
/* not used in Go (yet) */
}
}
return p, nil
}
func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
2011-12-16 16:32:15 +00:00
p := new(ecdsa.PrivateKey)
p.D = big.NewInt(0)
// TODO: validate that the required flags are present
2011-12-16 16:32:15 +00:00
for k, v := range m {
switch k {
case "privatekey":
v1, err := fromBase64([]byte(v))
2011-12-16 16:32:15 +00:00
if err != nil {
return nil, err
}
p.D.SetBytes(v1)
case "created", "publish", "activate":
2011-12-16 16:32:15 +00:00
/* not used in Go (yet) */
}
}
return p, nil
}
// parseKey reads a private key from r. It returns a map[string]string,
2011-12-16 16:32:15 +00:00
// with the key-value pairs, or an error when the file is not correct.
func parseKey(r io.Reader, file string) (map[string]string, error) {
s, cancel := scanInit(r)
2011-12-16 13:48:30 +00:00
m := make(map[string]string)
2011-12-16 18:34:30 +00:00
c := make(chan lex)
2011-12-16 13:48:30 +00:00
k := ""
defer func() {
cancel()
// zlexer can send up to two tokens, the next one and possibly 1 remainders.
// Do a non-blocking read.
_, ok := <-c
_, ok = <-c
if !ok {
// too bad
}
}()
2011-12-15 16:38:14 +00:00
// Start the lexer
go klexer(s, c)
for l := range c {
2011-12-16 13:48:30 +00:00
// It should alternate
2011-12-15 16:38:14 +00:00
switch l.value {
2015-02-19 10:45:59 +00:00
case zKey:
2011-12-16 13:48:30 +00:00
k = l.token
2015-02-19 10:45:59 +00:00
case zValue:
2011-12-16 16:32:15 +00:00
if k == "" {
2012-02-15 22:04:46 +00:00
return nil, &ParseError{file, "no private key seen", l}
2011-12-16 16:32:15 +00:00
}
2011-12-16 18:42:30 +00:00
//println("Setting", strings.ToLower(k), "to", l.token, "b")
2011-12-16 16:32:15 +00:00
m[strings.ToLower(k)] = l.token
k = ""
2011-12-15 16:38:14 +00:00
}
}
2011-12-16 13:48:30 +00:00
return m, nil
2011-12-15 16:38:14 +00:00
}
// klexer scans the sourcefile and returns tokens on the channel c.
func klexer(s *scan, c chan lex) {
2011-12-16 18:34:30 +00:00
var l lex
2011-12-15 16:38:14 +00:00
str := "" // Hold the current read text
commt := false
2011-12-16 13:48:30 +00:00
key := true
x, err := s.tokenText()
2011-12-15 16:38:14 +00:00
defer close(c)
for err == nil {
l.column = s.position.Column
l.line = s.position.Line
switch x {
case ':':
2011-12-15 16:38:14 +00:00
if commt {
break
}
2011-12-16 18:42:30 +00:00
l.token = str
2011-12-16 13:48:30 +00:00
if key {
2015-02-19 10:45:59 +00:00
l.value = zKey
2011-12-16 13:48:30 +00:00
c <- l
2011-12-16 18:42:30 +00:00
// Next token is a space, eat it
s.tokenText()
2011-12-16 13:48:30 +00:00
key = false
2011-12-16 18:42:30 +00:00
str = ""
2011-12-16 13:48:30 +00:00
} else {
2015-02-19 10:45:59 +00:00
l.value = zValue
2011-12-16 13:48:30 +00:00
}
case ';':
2011-12-15 16:38:14 +00:00
commt = true
case '\n':
2011-12-15 16:38:14 +00:00
if commt {
// Reset a comment
commt = false
}
2015-02-19 10:45:59 +00:00
l.value = zValue
2011-12-16 18:42:30 +00:00
l.token = str
2011-12-16 13:48:30 +00:00
c <- l
2011-12-15 16:38:14 +00:00
str = ""
commt = false
2011-12-16 13:48:30 +00:00
key = true
2011-12-15 16:38:14 +00:00
default:
if commt {
break
}
str += string(x)
2011-12-15 16:38:14 +00:00
}
2012-02-23 18:37:08 +00:00
x, err = s.tokenText()
2011-12-15 16:38:14 +00:00
}
2011-12-18 18:59:01 +00:00
if len(str) > 0 {
// Send remainder
l.token = str
2015-02-19 10:45:59 +00:00
l.value = zValue
2011-12-18 18:59:01 +00:00
c <- l
}
2011-12-15 16:38:14 +00:00
}