2011-12-16 03:38:14 +11:00
|
|
|
package dns
|
|
|
|
|
|
|
|
import (
|
2012-05-06 01:37:48 +10:00
|
|
|
"crypto/dsa"
|
2011-12-17 03:32:15 +11:00
|
|
|
"crypto/ecdsa"
|
|
|
|
"crypto/rsa"
|
2011-12-16 03:38:14 +11:00
|
|
|
"io"
|
2011-12-17 03:32:15 +11:00
|
|
|
"math/big"
|
|
|
|
"strings"
|
2011-12-16 03:38:14 +11:00
|
|
|
)
|
|
|
|
|
2012-12-10 05:23:25 +11:00
|
|
|
func (k *DNSKEY) NewPrivateKey(s string) (PrivateKey, error) {
|
2012-04-16 04:50:53 +10:00
|
|
|
if s[len(s)-1] != '\n' { // We need a closing newline
|
|
|
|
return k.ReadPrivateKey(strings.NewReader(s+"\n"), "")
|
|
|
|
}
|
|
|
|
return k.ReadPrivateKey(strings.NewReader(s), "")
|
|
|
|
}
|
|
|
|
|
2012-09-03 05:22:24 +10:00
|
|
|
// NewPrivateKey reads a private key from the io.Reader q. The string file is
|
|
|
|
// only used in error reporting.
|
|
|
|
// The public key must be
|
2012-04-16 04:50:53 +10:00
|
|
|
// known, because some cryptographics algorithms embed the public inside the privatekey.
|
2012-12-10 05:23:25 +11:00
|
|
|
func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (PrivateKey, error) {
|
2012-01-22 09:36:54 +11:00
|
|
|
m, e := parseKey(q, file)
|
2011-12-17 03:32:15 +11:00
|
|
|
if m == nil {
|
|
|
|
return nil, e
|
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
2012-04-16 04:50:53 +10:00
|
|
|
// TODO(mg): check if the pubkey matches the private key
|
2011-12-17 03:32:15 +11:00
|
|
|
switch m["algorithm"] {
|
2012-04-17 19:39:58 +10:00
|
|
|
case "3 (DSA)":
|
|
|
|
p, e := readPrivateKeyDSA(m)
|
|
|
|
if e != nil {
|
|
|
|
return nil, e
|
|
|
|
}
|
|
|
|
if !k.setPublicKeyInPrivate(p) {
|
|
|
|
return nil, ErrPrivKey
|
|
|
|
}
|
|
|
|
return p, e
|
2012-04-11 23:13:17 +10:00
|
|
|
case "1 (RSAMD5)":
|
|
|
|
fallthrough
|
|
|
|
case "5 (RSASHA1)":
|
|
|
|
fallthrough
|
2012-04-17 19:39:58 +10:00
|
|
|
case "7 (RSASHA1NSEC3SHA1)":
|
|
|
|
fallthrough
|
2012-04-11 23:13:17 +10:00
|
|
|
case "8 (RSASHA256)":
|
|
|
|
fallthrough
|
|
|
|
case "10 (RSASHA512)":
|
2012-04-16 04:50:53 +10:00
|
|
|
p, e := readPrivateKeyRSA(m)
|
|
|
|
if e != nil {
|
2012-04-16 05:00:17 +10:00
|
|
|
return nil, e
|
|
|
|
}
|
|
|
|
if !k.setPublicKeyInPrivate(p) {
|
|
|
|
return nil, ErrPrivKey
|
2012-04-16 04:50:53 +10:00
|
|
|
}
|
|
|
|
return p, e
|
2012-04-16 16:51:04 +10:00
|
|
|
case "12 (ECC-GOST)":
|
|
|
|
p, e := readPrivateKeyGOST(m)
|
|
|
|
if e != nil {
|
|
|
|
return nil, e
|
|
|
|
}
|
|
|
|
// setPublicKeyInPrivate(p)
|
|
|
|
return p, e
|
2012-04-16 04:50:53 +10:00
|
|
|
case "13 (ECDSAP256SHA256)":
|
|
|
|
fallthrough
|
|
|
|
case "14 (ECDSAP384SHA384)":
|
|
|
|
p, e := readPrivateKeyECDSA(m)
|
|
|
|
if e != nil {
|
2012-04-16 05:00:17 +10:00
|
|
|
return nil, e
|
|
|
|
}
|
|
|
|
if !k.setPublicKeyInPrivate(p) {
|
|
|
|
return nil, ErrPrivKey
|
2012-04-16 04:50:53 +10:00
|
|
|
}
|
|
|
|
return p, e
|
2011-12-17 03:32:15 +11:00
|
|
|
}
|
|
|
|
return nil, ErrPrivKey
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read a private key (file) string and create a public key. Return the private key.
|
|
|
|
func readPrivateKeyRSA(m map[string]string) (PrivateKey, error) {
|
|
|
|
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 := packBase64([]byte(v))
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2012-04-17 19:39:58 +10:00
|
|
|
func readPrivateKeyDSA(m map[string]string) (PrivateKey, error) {
|
|
|
|
p := new(dsa.PrivateKey)
|
|
|
|
p.X = big.NewInt(0)
|
|
|
|
for k, v := range m {
|
|
|
|
switch k {
|
|
|
|
case "private_value(x):":
|
|
|
|
v1, err := packBase64([]byte(v))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
p.X.SetBytes(v1)
|
|
|
|
case "created:", "publish:", "activate:":
|
|
|
|
/* not used in Go (yet) */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
2011-12-17 03:32:15 +11:00
|
|
|
func readPrivateKeyECDSA(m map[string]string) (PrivateKey, error) {
|
|
|
|
p := new(ecdsa.PrivateKey)
|
|
|
|
p.D = big.NewInt(0)
|
|
|
|
// Need to check if we have everything
|
|
|
|
for k, v := range m {
|
|
|
|
switch k {
|
|
|
|
case "privatekey:":
|
|
|
|
v1, err := packBase64([]byte(v))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
p.D.SetBytes(v1)
|
|
|
|
case "created:", "publish:", "activate:":
|
|
|
|
/* not used in Go (yet) */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
2012-04-16 16:51:04 +10:00
|
|
|
func readPrivateKeyGOST(m map[string]string) (PrivateKey, error) {
|
2012-05-06 01:37:48 +10:00
|
|
|
// p := new(ecdsa.PrivateKey)
|
|
|
|
// p.D = big.NewInt(0)
|
2012-04-16 16:51:04 +10:00
|
|
|
// Need to check if we have everything
|
|
|
|
for k, v := range m {
|
|
|
|
switch k {
|
|
|
|
case "gostasn1:":
|
|
|
|
v1, err := packBase64([]byte(v))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
v1 = v1
|
|
|
|
//p.D.SetBytes(v1)
|
|
|
|
case "created:", "publish:", "activate:":
|
|
|
|
/* not used in Go (yet) */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2012-01-17 07:44:49 +11:00
|
|
|
// parseKey reads a private key from r. It returns a map[string]string,
|
2011-12-17 03:32:15 +11:00
|
|
|
// with the key-value pairs, or an error when the file is not correct.
|
2012-01-22 09:36:54 +11:00
|
|
|
func parseKey(r io.Reader, file string) (map[string]string, error) {
|
2012-02-24 05:37:08 +11:00
|
|
|
s := scanInit(r)
|
2011-12-17 00:48:30 +11:00
|
|
|
m := make(map[string]string)
|
2011-12-17 05:34:30 +11:00
|
|
|
c := make(chan lex)
|
2011-12-17 00:48:30 +11:00
|
|
|
k := ""
|
2011-12-16 03:38:14 +11:00
|
|
|
// Start the lexer
|
|
|
|
go klexer(s, c)
|
|
|
|
for l := range c {
|
2011-12-17 00:48:30 +11:00
|
|
|
// It should alternate
|
2011-12-16 03:38:14 +11:00
|
|
|
switch l.value {
|
|
|
|
case _KEY:
|
2011-12-17 00:48:30 +11:00
|
|
|
k = l.token
|
2011-12-16 03:38:14 +11:00
|
|
|
case _VALUE:
|
2011-12-17 03:32:15 +11:00
|
|
|
if k == "" {
|
2012-02-16 09:04:46 +11:00
|
|
|
return nil, &ParseError{file, "no private key seen", l}
|
2011-12-17 03:32:15 +11:00
|
|
|
}
|
2011-12-17 05:42:30 +11:00
|
|
|
//println("Setting", strings.ToLower(k), "to", l.token, "b")
|
2011-12-17 03:32:15 +11:00
|
|
|
m[strings.ToLower(k)] = l.token
|
|
|
|
k = ""
|
2011-12-16 03:38:14 +11:00
|
|
|
}
|
|
|
|
}
|
2011-12-17 00:48:30 +11:00
|
|
|
return m, nil
|
2011-12-16 03:38:14 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
// klexer scans the sourcefile and returns tokens on the channel c.
|
2012-02-22 08:41:00 +11:00
|
|
|
func klexer(s *scan, c chan lex) {
|
2011-12-17 05:34:30 +11:00
|
|
|
var l lex
|
2011-12-16 03:38:14 +11:00
|
|
|
str := "" // Hold the current read text
|
|
|
|
commt := false
|
2011-12-17 00:48:30 +11:00
|
|
|
key := true
|
2012-02-22 08:41:00 +11:00
|
|
|
x, err := s.tokenText()
|
2011-12-16 03:38:14 +11:00
|
|
|
defer close(c)
|
2012-02-22 08:41:00 +11:00
|
|
|
for err == nil {
|
|
|
|
l.column = s.position.Column
|
|
|
|
l.line = s.position.Line
|
|
|
|
switch x {
|
|
|
|
case ':':
|
2011-12-16 03:38:14 +11:00
|
|
|
if commt {
|
|
|
|
break
|
|
|
|
}
|
2011-12-17 05:42:30 +11:00
|
|
|
l.token = str
|
2011-12-17 00:48:30 +11:00
|
|
|
if key {
|
|
|
|
l.value = _KEY
|
|
|
|
c <- l
|
2011-12-17 05:42:30 +11:00
|
|
|
// Next token is a space, eat it
|
2012-02-22 08:41:00 +11:00
|
|
|
s.tokenText()
|
2011-12-17 00:48:30 +11:00
|
|
|
key = false
|
2011-12-17 05:42:30 +11:00
|
|
|
str = ""
|
2011-12-17 00:48:30 +11:00
|
|
|
} else {
|
|
|
|
l.value = _VALUE
|
|
|
|
}
|
2012-02-22 08:41:00 +11:00
|
|
|
case ';':
|
2011-12-16 03:38:14 +11:00
|
|
|
commt = true
|
2012-02-22 08:41:00 +11:00
|
|
|
case '\n':
|
2011-12-16 03:38:14 +11:00
|
|
|
if commt {
|
|
|
|
// Reset a comment
|
|
|
|
commt = false
|
|
|
|
}
|
2011-12-17 05:42:30 +11:00
|
|
|
l.value = _VALUE
|
|
|
|
l.token = str
|
2011-12-17 00:48:30 +11:00
|
|
|
c <- l
|
2011-12-16 03:38:14 +11:00
|
|
|
str = ""
|
|
|
|
commt = false
|
2011-12-17 00:48:30 +11:00
|
|
|
key = true
|
2011-12-16 03:38:14 +11:00
|
|
|
default:
|
|
|
|
if commt {
|
|
|
|
break
|
|
|
|
}
|
2012-02-22 08:41:00 +11:00
|
|
|
str += string(x)
|
2011-12-16 03:38:14 +11:00
|
|
|
}
|
2012-02-24 05:37:08 +11:00
|
|
|
x, err = s.tokenText()
|
2011-12-16 03:38:14 +11:00
|
|
|
}
|
2011-12-19 05:59:01 +11:00
|
|
|
if len(str) > 0 {
|
|
|
|
// Send remainder
|
|
|
|
l.token = str
|
|
|
|
l.value = _VALUE
|
|
|
|
c <- l
|
|
|
|
}
|
2011-12-16 03:38:14 +11:00
|
|
|
}
|