...

Source file src/github.com/miekg/dns/dnssec_keyscan.go

Documentation: github.com/miekg/dns

     1  package dns
     2  
     3  import (
     4  	"bufio"
     5  	"crypto"
     6  	"crypto/ecdsa"
     7  	"crypto/ed25519"
     8  	"crypto/rsa"
     9  	"io"
    10  	"math/big"
    11  	"strconv"
    12  	"strings"
    13  )
    14  
    15  // NewPrivateKey returns a PrivateKey by parsing the string s.
    16  // s should be in the same form of the BIND private key files.
    17  func (k *DNSKEY) NewPrivateKey(s string) (crypto.PrivateKey, error) {
    18  	if s == "" || s[len(s)-1] != '\n' { // We need a closing newline
    19  		return k.ReadPrivateKey(strings.NewReader(s+"\n"), "")
    20  	}
    21  	return k.ReadPrivateKey(strings.NewReader(s), "")
    22  }
    23  
    24  // ReadPrivateKey reads a private key from the io.Reader q. The string file is
    25  // only used in error reporting.
    26  // The public key must be known, because some cryptographic algorithms embed
    27  // the public inside the privatekey.
    28  func (k *DNSKEY) ReadPrivateKey(q io.Reader, file string) (crypto.PrivateKey, error) {
    29  	m, err := parseKey(q, file)
    30  	if m == nil {
    31  		return nil, err
    32  	}
    33  	if _, ok := m["private-key-format"]; !ok {
    34  		return nil, ErrPrivKey
    35  	}
    36  	if m["private-key-format"] != "v1.2" && m["private-key-format"] != "v1.3" {
    37  		return nil, ErrPrivKey
    38  	}
    39  	// TODO(mg): check if the pubkey matches the private key
    40  	algoStr, _, _ := strings.Cut(m["algorithm"], " ")
    41  	algo, err := strconv.ParseUint(algoStr, 10, 8)
    42  	if err != nil {
    43  		return nil, ErrPrivKey
    44  	}
    45  	switch uint8(algo) {
    46  	case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
    47  		priv, err := readPrivateKeyRSA(m)
    48  		if err != nil {
    49  			return nil, err
    50  		}
    51  		pub := k.publicKeyRSA()
    52  		if pub == nil {
    53  			return nil, ErrKey
    54  		}
    55  		priv.PublicKey = *pub
    56  		return priv, nil
    57  	case ECDSAP256SHA256, ECDSAP384SHA384:
    58  		priv, err := readPrivateKeyECDSA(m)
    59  		if err != nil {
    60  			return nil, err
    61  		}
    62  		pub := k.publicKeyECDSA()
    63  		if pub == nil {
    64  			return nil, ErrKey
    65  		}
    66  		priv.PublicKey = *pub
    67  		return priv, nil
    68  	case ED25519:
    69  		return readPrivateKeyED25519(m)
    70  	default:
    71  		return nil, ErrAlg
    72  	}
    73  }
    74  
    75  // Read a private key (file) string and create a public key. Return the private key.
    76  func readPrivateKeyRSA(m map[string]string) (*rsa.PrivateKey, error) {
    77  	p := new(rsa.PrivateKey)
    78  	p.Primes = []*big.Int{nil, nil}
    79  	for k, v := range m {
    80  		switch k {
    81  		case "modulus", "publicexponent", "privateexponent", "prime1", "prime2":
    82  			v1, err := fromBase64([]byte(v))
    83  			if err != nil {
    84  				return nil, err
    85  			}
    86  			switch k {
    87  			case "modulus":
    88  				p.PublicKey.N = new(big.Int).SetBytes(v1)
    89  			case "publicexponent":
    90  				i := new(big.Int).SetBytes(v1)
    91  				p.PublicKey.E = int(i.Int64()) // int64 should be large enough
    92  			case "privateexponent":
    93  				p.D = new(big.Int).SetBytes(v1)
    94  			case "prime1":
    95  				p.Primes[0] = new(big.Int).SetBytes(v1)
    96  			case "prime2":
    97  				p.Primes[1] = new(big.Int).SetBytes(v1)
    98  			}
    99  		case "exponent1", "exponent2", "coefficient":
   100  			// not used in Go (yet)
   101  		case "created", "publish", "activate":
   102  			// not used in Go (yet)
   103  		}
   104  	}
   105  	return p, nil
   106  }
   107  
   108  func readPrivateKeyECDSA(m map[string]string) (*ecdsa.PrivateKey, error) {
   109  	p := new(ecdsa.PrivateKey)
   110  	p.D = new(big.Int)
   111  	// TODO: validate that the required flags are present
   112  	for k, v := range m {
   113  		switch k {
   114  		case "privatekey":
   115  			v1, err := fromBase64([]byte(v))
   116  			if err != nil {
   117  				return nil, err
   118  			}
   119  			p.D.SetBytes(v1)
   120  		case "created", "publish", "activate":
   121  			/* not used in Go (yet) */
   122  		}
   123  	}
   124  	return p, nil
   125  }
   126  
   127  func readPrivateKeyED25519(m map[string]string) (ed25519.PrivateKey, error) {
   128  	var p ed25519.PrivateKey
   129  	// TODO: validate that the required flags are present
   130  	for k, v := range m {
   131  		switch k {
   132  		case "privatekey":
   133  			p1, err := fromBase64([]byte(v))
   134  			if err != nil {
   135  				return nil, err
   136  			}
   137  			if len(p1) != ed25519.SeedSize {
   138  				return nil, ErrPrivKey
   139  			}
   140  			p = ed25519.NewKeyFromSeed(p1)
   141  		case "created", "publish", "activate":
   142  			/* not used in Go (yet) */
   143  		}
   144  	}
   145  	return p, nil
   146  }
   147  
   148  // parseKey reads a private key from r. It returns a map[string]string,
   149  // with the key-value pairs, or an error when the file is not correct.
   150  func parseKey(r io.Reader, file string) (map[string]string, error) {
   151  	m := make(map[string]string)
   152  	var k string
   153  
   154  	c := newKLexer(r)
   155  
   156  	for l, ok := c.Next(); ok; l, ok = c.Next() {
   157  		// It should alternate
   158  		switch l.value {
   159  		case zKey:
   160  			k = l.token
   161  		case zValue:
   162  			if k == "" {
   163  				return nil, &ParseError{file, "no private key seen", l}
   164  			}
   165  
   166  			m[strings.ToLower(k)] = l.token
   167  			k = ""
   168  		}
   169  	}
   170  
   171  	// Surface any read errors from r.
   172  	if err := c.Err(); err != nil {
   173  		return nil, &ParseError{file: file, err: err.Error()}
   174  	}
   175  
   176  	return m, nil
   177  }
   178  
   179  type klexer struct {
   180  	br io.ByteReader
   181  
   182  	readErr error
   183  
   184  	line   int
   185  	column int
   186  
   187  	key bool
   188  
   189  	eol bool // end-of-line
   190  }
   191  
   192  func newKLexer(r io.Reader) *klexer {
   193  	br, ok := r.(io.ByteReader)
   194  	if !ok {
   195  		br = bufio.NewReaderSize(r, 1024)
   196  	}
   197  
   198  	return &klexer{
   199  		br: br,
   200  
   201  		line: 1,
   202  
   203  		key: true,
   204  	}
   205  }
   206  
   207  func (kl *klexer) Err() error {
   208  	if kl.readErr == io.EOF {
   209  		return nil
   210  	}
   211  
   212  	return kl.readErr
   213  }
   214  
   215  // readByte returns the next byte from the input
   216  func (kl *klexer) readByte() (byte, bool) {
   217  	if kl.readErr != nil {
   218  		return 0, false
   219  	}
   220  
   221  	c, err := kl.br.ReadByte()
   222  	if err != nil {
   223  		kl.readErr = err
   224  		return 0, false
   225  	}
   226  
   227  	// delay the newline handling until the next token is delivered,
   228  	// fixes off-by-one errors when reporting a parse error.
   229  	if kl.eol {
   230  		kl.line++
   231  		kl.column = 0
   232  		kl.eol = false
   233  	}
   234  
   235  	if c == '\n' {
   236  		kl.eol = true
   237  	} else {
   238  		kl.column++
   239  	}
   240  
   241  	return c, true
   242  }
   243  
   244  func (kl *klexer) Next() (lex, bool) {
   245  	var (
   246  		l lex
   247  
   248  		str strings.Builder
   249  
   250  		commt bool
   251  	)
   252  
   253  	for x, ok := kl.readByte(); ok; x, ok = kl.readByte() {
   254  		l.line, l.column = kl.line, kl.column
   255  
   256  		switch x {
   257  		case ':':
   258  			if commt || !kl.key {
   259  				break
   260  			}
   261  
   262  			kl.key = false
   263  
   264  			// Next token is a space, eat it
   265  			kl.readByte()
   266  
   267  			l.value = zKey
   268  			l.token = str.String()
   269  			return l, true
   270  		case ';':
   271  			commt = true
   272  		case '\n':
   273  			if commt {
   274  				// Reset a comment
   275  				commt = false
   276  			}
   277  
   278  			if kl.key && str.Len() == 0 {
   279  				// ignore empty lines
   280  				break
   281  			}
   282  
   283  			kl.key = true
   284  
   285  			l.value = zValue
   286  			l.token = str.String()
   287  			return l, true
   288  		default:
   289  			if commt {
   290  				break
   291  			}
   292  
   293  			str.WriteByte(x)
   294  		}
   295  	}
   296  
   297  	if kl.readErr != nil && kl.readErr != io.EOF {
   298  		// Don't return any tokens after a read error occurs.
   299  		return lex{value: zEOF}, false
   300  	}
   301  
   302  	if str.Len() > 0 {
   303  		// Send remainder
   304  		l.value = zValue
   305  		l.token = str.String()
   306  		return l, true
   307  	}
   308  
   309  	return lex{value: zEOF}, false
   310  }
   311  

View as plain text