123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- package dns
- import (
- "bufio"
- "crypto"
- "crypto/ecdsa"
- "crypto/ed25519"
- "crypto/rsa"
- "io"
- "math/big"
- "strconv"
- "strings"
- )
- // 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), "")
- }
- // ReadPrivateKey reads a private key from the io.Reader q. The string file is
- // 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)
- if m == nil {
- return nil, err
- }
- 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 RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
- priv, err := readPrivateKeyRSA(m)
- if err != nil {
- return nil, err
- }
- pub := k.publicKeyRSA()
- if pub == nil {
- return nil, ErrKey
- }
- priv.PublicKey = *pub
- return priv, nil
- case ECDSAP256SHA256, ECDSAP384SHA384:
- priv, err := readPrivateKeyECDSA(m)
- if err != nil {
- return nil, err
- }
- pub := k.publicKeyECDSA()
- if pub == nil {
- return nil, ErrKey
- }
- priv.PublicKey = *pub
- return priv, nil
- case ED25519:
- return readPrivateKeyED25519(m)
- default:
- return nil, ErrAlg
- }
- }
- // Read a private key (file) string and create a public key. Return the private key.
- func readPrivateKeyRSA(m map[string]string) (*rsa.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 := fromBase64([]byte(v))
- if err != nil {
- return nil, err
- }
- switch k {
- case "modulus":
- p.PublicKey.N = new(big.Int).SetBytes(v1)
- case "publicexponent":
- i := new(big.Int).SetBytes(v1)
- p.PublicKey.E = int(i.Int64()) // int64 should be large enough
- case "privateexponent":
- p.D = new(big.Int).SetBytes(v1)
- case "prime1":
- p.Primes[0] = new(big.Int).SetBytes(v1)
- case "prime2":
- p.Primes[1] = new(big.Int).SetBytes(v1)
- }
- case "exponent1", "exponent2", "coefficient":
- // not used in Go (yet)
- case "created", "publish", "activate":
- // not used in Go (yet)
- }
- }
- return p, nil
- }
- func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
- p := new(ecdsa.PrivateKey)
- p.D = new(big.Int)
- // TODO: validate that the required flags are present
- for k, v := range m {
- switch k {
- case "privatekey":
- v1, err := fromBase64([]byte(v))
- if err != nil {
- return nil, err
- }
- p.D.SetBytes(v1)
- case "created", "publish", "activate":
- /* not used in Go (yet) */
- }
- }
- return p, nil
- }
- func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) {
- var p ed25519.PrivateKey
- // TODO: validate that the required flags are present
- for k, v := range m {
- switch k {
- case "privatekey":
- p1, err := fromBase64([]byte(v))
- if err != nil {
- return nil, err
- }
- if len(p1) != ed25519.SeedSize {
- return nil, ErrPrivKey
- }
- p = ed25519.NewKeyFromSeed(p1)
- case "created", "publish", "activate":
- /* not used in Go (yet) */
- }
- }
- return p, nil
- }
- // parseKey reads a private key from r. It returns a map[string]string,
- // 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) {
- m := make(map[string]string)
- var k string
- c := newKLexer(r)
- for l, ok := c.Next(); ok; l, ok = c.Next() {
- // It should alternate
- switch l.value {
- case zKey:
- k = l.token
- case zValue:
- if k == "" {
- return nil, &ParseError{file, "no private key seen", l}
- }
- m[strings.ToLower(k)] = l.token
- k = ""
- }
- }
- // Surface any read errors from r.
- if err := c.Err(); err != nil {
- return nil, &ParseError{file: file, err: err.Error()}
- }
- return m, nil
- }
- type klexer struct {
- br io.ByteReader
- readErr error
- line int
- column int
- key bool
- eol bool // end-of-line
- }
- func newKLexer(r io.Reader) *klexer {
- br, ok := r.(io.ByteReader)
- if !ok {
- br = bufio.NewReaderSize(r, 1024)
- }
- return &klexer{
- br: br,
- line: 1,
- key: true,
- }
- }
- func (kl *klexer) Err() error {
- if kl.readErr == io.EOF {
- return nil
- }
- return kl.readErr
- }
- // readByte returns the next byte from the input
- func (kl *klexer) readByte() (byte, bool) {
- if kl.readErr != nil {
- return 0, false
- }
- c, err := kl.br.ReadByte()
- if err != nil {
- kl.readErr = err
- return 0, false
- }
- // delay the newline handling until the next token is delivered,
- // fixes off-by-one errors when reporting a parse error.
- if kl.eol {
- kl.line++
- kl.column = 0
- kl.eol = false
- }
- if c == '\n' {
- kl.eol = true
- } else {
- kl.column++
- }
- return c, true
- }
- func (kl *klexer) Next() (lex, bool) {
- var (
- l lex
- str strings.Builder
- commt bool
- )
- for x, ok := kl.readByte(); ok; x, ok = kl.readByte() {
- l.line, l.column = kl.line, kl.column
- switch x {
- case ':':
- if commt || !kl.key {
- break
- }
- kl.key = false
- // Next token is a space, eat it
- kl.readByte()
- l.value = zKey
- l.token = str.String()
- return l, true
- case ';':
- commt = true
- case '\n':
- if commt {
- // Reset a comment
- commt = false
- }
- if kl.key && str.Len() == 0 {
- // ignore empty lines
- break
- }
- kl.key = true
- l.value = zValue
- l.token = str.String()
- return l, true
- default:
- if commt {
- break
- }
- str.WriteByte(x)
- }
- }
- if kl.readErr != nil && kl.readErr != io.EOF {
- // Don't return any tokens after a read error occurs.
- return lex{value: zEOF}, false
- }
- if str.Len() > 0 {
- // Send remainder
- l.value = zValue
- l.token = str.String()
- return l, true
- }
- return lex{value: zEOF}, false
- }
|