mcuboot/imgtool/imgtool.go

348 lines
6.9 KiB
Go

// The image tool.
//
// A standalone tool to manipulate images.
package main
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"math/big"
"os"
"strings"
"text/template"
log "github.com/Sirupsen/logrus"
"github.com/spf13/cobra"
)
var keyFile string
var keyType KeyGenerator
func main() {
root := &cobra.Command{
Use: "imgtool command args ...",
Short: "Manipulate boot images",
Run: func(cmd *cobra.Command, args []string) {
cmd.Usage()
log.Fatal("Invalid usage")
},
}
fl := root.PersistentFlags()
fl.StringVarP(&keyFile, "key", "k", "root_ec.pem", "Keyfile to use")
keygen := &cobra.Command{
Use: "keygen",
Short: "Generate an ECDSA P-256 private key",
Run: doKeyGen,
}
fl = keygen.Flags()
fl.VarP(&keyType, "key-type", "t", "Type of key to generate")
root.AddCommand(keygen)
getpub := &cobra.Command{
Use: "getpub",
Short: "Extract the public key as C code",
Run: doGetPub,
}
root.AddCommand(getpub)
root.AddCommand(setupSign())
if err := root.Execute(); err != nil {
log.Fatal(err)
}
}
func doKeyGen(cmd *cobra.Command, args []string) {
if keyType.generate == nil {
cmd.Usage()
log.Fatal("Must specify key type with --key-type")
}
if len(args) != 0 {
cmd.Usage()
log.Fatal("Expecting no arguments to keygen")
}
priv509, err := keyType.generate()
if err != nil {
log.Fatal(err)
}
fd, err := os.OpenFile(keyFile, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600)
if err != nil {
log.Fatal(err)
}
defer fd.Close()
block := pem.Block{
Type: keyType.pemType,
Bytes: priv509,
}
err = pem.Encode(fd, &block)
if err != nil {
log.Fatal(err)
}
}
var keyGens map[string]*KeyGenerator
type KeyGenerator struct {
name string
description string
pemType string
generate func() ([]byte, error)
}
func (g *KeyGenerator) Set(text string) error {
kg, ok := keyGens[text]
if !ok {
return errors.New("Unsupported key type")
}
*g = *kg
return nil
}
func (g *KeyGenerator) String() string {
return g.name
}
func (g *KeyGenerator) Type() string {
return "keytype"
}
func init() {
keyGens = make(map[string]*KeyGenerator)
kg := &KeyGenerator{
name: "ecdsa-p256",
description: "ECDSA with SHA256 and the NIST P-256 curve",
pemType: "EC PRIVATE KEY",
generate: genEcdsaP256,
}
keyGens[kg.name] = kg
kg = &KeyGenerator{
name: "ecdsa-p224",
description: "ECDSA with SHA256 and the NIST P-224 curve",
pemType: "EC PRIVATE KEY",
generate: genEcdsaP224,
}
keyGens[kg.name] = kg
kg = &KeyGenerator{
name: "rsa-2048",
description: "RSA 2048",
pemType: "RSA PRIVATE KEY",
generate: genRSA2048,
}
keyGens[kg.name] = kg
}
func genEcdsaP224() ([]byte, error) {
priv, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
if err != nil {
return nil, err
}
return x509.MarshalECPrivateKey(priv)
}
func genEcdsaP256() ([]byte, error) {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
return x509.MarshalECPrivateKey(priv)
}
func genRSA2048() ([]byte, error) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
return x509.MarshalPKCS1PrivateKey(priv), nil
}
func doGetPub(cmd *cobra.Command, args []string) {
data, err := ioutil.ReadFile(keyFile)
if err != nil {
log.Fatal(err)
}
block, data := pem.Decode(data)
// openssl will sometimes generate this extra parameters
// fields at the top (although it is included in the private
// key as well). If we see this, just read the next block.
if block.Type == "EC PARAMETERS" {
block, data = pem.Decode(data)
}
// fmt.Printf("type=%q, headers=%v, data=\n%s", block.Type, block.Headers, hex.Dump(block.Bytes))
if block.Type == "EC PRIVATE KEY" {
dumpECPub(block)
} else if block.Type == "RSA PRIVATE KEY" {
dumpRSAPub(block)
} else {
log.Fatal("Only supports ECDSA and RSA keys")
}
}
func dumpECPub(block *pem.Block) {
privateKey, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
log.Fatal(err)
}
// fmt.Printf("priv: %+v\n", privateKey)
// Dump out the public key as a nice structure.
// fmt.Printf("x = %x\n", privateKey.X.Bytes())
// fmt.Printf("y = %x\n", privateKey.Y.Bytes())
// tdata := make(map[string]string)
// tdata["x"] = formatCData(privateKey.X.Bytes(), 2)
// tdata["y"] = formatCData(privateKey.Y.Bytes(), 2)
// err = ecKeyTemplate.Execute(os.Stdout, tdata)
// if err != nil {
// log.Fatal(err)
// }
// The public key needs the algorithm and curve parameters.
var curve []int
switch privateKey.Params().Name {
case "P-224":
curve = []int{1, 3, 132, 0, 33}
case "P-256":
curve = []int{1, 2, 840, 10045, 3, 1, 7}
default:
log.Fatal("Key uses unsupported curve: %q", privateKey.Params().Name)
}
// The public key is encoded uncompressed, as a concatenation
// of the bytes.
var bbuf bytes.Buffer
bbuf.WriteByte(0x04)
bbuf.Write(privateKey.X.Bytes())
bbuf.Write(privateKey.Y.Bytes())
pkeyBytes := bbuf.Bytes()
pkey := EcPublicKey{
Algorithm: AlgorithmId{
Algorithm: []int{1, 2, 840, 10045, 2, 1},
Curve: curve,
},
PubKey: asn1.BitString{
Bytes: pkeyBytes,
BitLength: len(pkeyBytes) * 8,
},
}
asnBytes, err := asn1.Marshal(pkey)
if err != nil {
log.Fatal(err)
}
// fmt.Print(hex.Dump(asnBytes))
fmt.Printf(`/* Autogenerated, do not edit */
const unsigned char ec_pub_key[] = {
%s };
const unsigned int ec_pub_key_len = %d;
`,
formatCData(asnBytes, 1), len(asnBytes))
}
func dumpRSAPub(block *pem.Block) {
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
log.Fatal(err)
}
pubKey := RSAPublicKey{
N: privateKey.N,
E: privateKey.E,
}
asnBytes, err := asn1.Marshal(pubKey)
if err != nil {
log.Fatal(err)
}
fmt.Printf(`/* Autogenerated, do not edit */
const unsigned char rsa_pub_key[] = {
%s };
const unsigned int ec_pub_key_len = %d;
`,
formatCData(asnBytes, 1), len(asnBytes))
}
// ecPublicKey represents an ASN.1 Elliptic Curve Public Key structure
type EcPublicKey struct {
Algorithm AlgorithmId
PubKey asn1.BitString
}
type RSAPublicKey struct {
N *big.Int
E int
}
type AlgorithmId struct {
Algorithm asn1.ObjectIdentifier
Curve asn1.ObjectIdentifier
}
// Format a byte slice as 'C' data, with the given indentation on
// subsequent lines.
func formatCData(data []byte, indent int) string {
buf := new(bytes.Buffer)
indText := strings.Repeat("\t", indent)
for i, b := range data {
if i%8 == 0 {
if i > 0 {
fmt.Fprintf(buf, "\n%s", indText)
}
} else {
fmt.Fprintf(buf, " ")
}
fmt.Fprintf(buf, "0x%02x,", b)
}
return buf.String()
}
var ecKeyTemplate = template.Must(template.New("eckey").Parse(`
/* Autogenerated, do not edit. */
#include <stdint.h>
struct ec_key {
uint8_t x[32];
uint8_t y[32];
};
struct ec_key ec_pub_key = {
.x = { {{.x}} },
.y = { {{.y}} },
};
`))