This closes #10.

Merge remote-tracking branch 'd3zd3z/imgtool'

* d3zd3z/imgtool:
  imgtool: Add support for RSA keys
  imgtool: Add support for P-224
  imgtool: Create simple signing tool
diff --git a/imgtool/README.rst b/imgtool/README.rst
new file mode 100644
index 0000000..eb4ab1a
--- /dev/null
+++ b/imgtool/README.rst
@@ -0,0 +1,108 @@
+Image Manipulation Tool
+#######################
+
+Introduction
+============
+
+The *mcuboot* bootloader began as the bootloader for the Mynewt_
+project.  This project's *newt* tool, in addition to configuration and
+compilation, also contains code to manipulate image headers and apply
+signatures.
+
+.. _Mynewt: https://mynewt.apache.org/
+
+In order to use *mcuboot* with other OSes (starting with Zephyr), we
+need a tool that doesn't depend on being run inside of a *Mynewt*
+project.  This ``imgtool`` command is a small program that uses the
+*newt* tool's library as a small image manipulation tool
+
+Building It
+===========
+
+The *newt* tool is written in Go_, and as such, this imgtool is also.
+In order to build from source, you will need the Go toolchain
+installed.  On most distributions, this can be accomplished by
+installing a ``go`` or ``golang`` package.  The tool is driven by a
+program ``go``, and::
+
+    $ go version
+
+.. _Go: https://golang.org/
+
+should print out the version installed.
+
+Go prefers a fairly specific source tree layout, and it is easiest to
+work with if you keep this layout.  To do this, you will need a
+directory to hold your go source.  Once you've created this, the
+environment variable ``GOPATH`` needs to be set to point to this
+directory.  Once this is done, you can get this code, and its
+necessary dependencies by running::
+
+    $ go get github.com/runtimeco/mcuboot/imgtool
+
+and the package can be built with::
+
+    $ go install github.com/runtimeco/mcuboot/imgtool
+
+This will place the build tool in ``$GOPATH/bin/imgtool``.  Feel free
+to either place this directory in your path, or move the image to
+somewhere in your path.
+
+Usage
+=====
+
+Run ``imgtool`` either with no arguments, or with ``help`` to get more
+help.  The tool is capable of generating the signing keys needed for
+the bootloader, extracting the public key to embed in the image, and
+signing images.
+
+Generating keys
+---------------
+
+Keys are generated with the ``keygen`` subcommand.  The ``-k`` option
+allows you to specify a different file for the resulting key.  The key
+is stored in *PEM* format.
+
+You must specify a ``--key-type`` parameter.  Current valid entries
+are:
+
+.. list-table:: Key types
+   :header-rows: 1
+
+   * - Parameter
+     - Description
+   * - ecdsa-p224
+     - ECDSA with SHA256 and NIST P-224 (secp224r1)
+   * - ecdsa-p256
+     - ECDSA with SHA256 and NIST P-256 (secp256r1)
+
+Extracting the public key
+-------------------------
+
+In order to use a key other than the key distributed in the Zephyr
+directory, it needs to be extracted from the private key.  The
+``getpub`` subcommand does this.  The ``-k`` option can be used to
+specify a keyfile.  The program will then output the public key to
+stdout.  This should be written to a ``.cc`` file and included in the
+build.  For Zephyr builds, this is in the file ``boot/zephyr/keys.c``
+in the main tree.
+
+Signing images
+--------------
+
+In order for the bootloader to be able to upgrade an image, it must
+have a header prefixed to it, and a signature placed at the end.  The
+``sign`` subcommand can perform these operations.
+
+In order to use Zephyr images, you must set CONFIG_TEXT_SECTION_OFFSET
+in the main Zephyr program you want to build.  The exact value depends
+on the particular target.  The FRDM-K64F, 0x200 should be used.
+
+The ``sign`` command needs to be told this header offset as well, with
+the ``--header-size 0x200`` argument.
+
+The ``sign`` command is also able to mark images for upgrade.  This is
+done with the ``--pad``, and ``--align`` arguments.  The pad should be
+set to the size of the image slot (in bytes), and the alignment needs
+to be set to the write alignment of the flash device.  For FRDM-K64F,
+the alignment is 8.
diff --git a/imgtool/imgtool.go b/imgtool/imgtool.go
new file mode 100644
index 0000000..b880244
--- /dev/null
+++ b/imgtool/imgtool.go
@@ -0,0 +1,347 @@
+// 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}} },
+};
+`))
diff --git a/imgtool/sign.go b/imgtool/sign.go
new file mode 100644
index 0000000..b53279e
--- /dev/null
+++ b/imgtool/sign.go
@@ -0,0 +1,147 @@
+package main
+
+import (
+	"errors"
+	"os"
+	"strconv"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/spf13/cobra"
+
+	"mynewt.apache.org/newt/newt/image"
+)
+
+var version string
+var headerSize uint
+var signAlign alignment = 1
+var padTo int64
+
+func setupSign() *cobra.Command {
+	sign := &cobra.Command{
+		Use:   "sign infile outfile",
+		Short: "Sign image with key",
+		Run:   doSign,
+	}
+
+	fl := sign.Flags()
+	fl.StringVarP(&version, "version", "v", "1.0", "Version to stamp image with")
+	fl.UintVarP(&headerSize, "header-size", "H", 0, "Header size to use")
+	fl.Var(&signAlign, "align", "Alignment of flash device")
+	fl.Int64Var(&padTo, "pad", 0, "Pad image to this many bytes, adding trailer magic")
+
+	return sign
+}
+
+type alignment uint
+
+func (a alignment) String() string {
+	return strconv.FormatUint(uint64(a), 10)
+}
+
+func (a alignment) Type() string {
+	return "alignment"
+}
+
+func (a *alignment) Set(text string) error {
+	value, err := strconv.ParseUint(text, 10, 8)
+	if err != nil {
+		return err
+	}
+
+	switch value {
+	case 1, 2, 4, 8:
+		*a = alignment(value)
+		return nil
+	default:
+		return errors.New("Invalid alignment, must be one of 1,2,4,8")
+	}
+}
+
+func doSign(cmd *cobra.Command, args []string) {
+	if len(args) != 2 {
+		cmd.Usage()
+		log.Fatal("Expecting infile and outfile arguments")
+	}
+
+	img, err := image.NewImage(args[0], args[1])
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	err = img.SetVersion(version)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	err = img.SetSigningKey(keyFile, 0)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// For Zephyr, we want to both skip and use the header size.
+	img.SrcSkip = headerSize
+	img.HeaderSize = headerSize
+
+	err = img.Generate(nil)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if padTo > 0 {
+		err = padImage(args[1])
+		if err != nil {
+			log.Fatal(err)
+		}
+	}
+}
+
+// Pad the image to a given position, and write the image trailer.
+func padImage(name string) error {
+	f, err := os.OpenFile(name, os.O_RDWR, 0)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+
+	info, err := f.Stat()
+	if err != nil {
+		return err
+	}
+
+	var trailerSize int64
+	switch signAlign {
+	case 1:
+		trailerSize = 402
+	case 2:
+		trailerSize = 788
+	case 4:
+		trailerSize = 1560
+	case 8:
+		trailerSize = 3104
+	default:
+		panic("Invalid alignment")
+	}
+
+	if info.Size() > padTo-trailerSize {
+		return errors.New("Image is too large for specified padding")
+	}
+
+	_, err = f.WriteAt(bootMagic, padTo-trailerSize)
+	if err != nil {
+		return err
+	}
+
+	err = f.Truncate(padTo)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+var bootMagic = []byte{
+	0x77, 0xc2, 0x95, 0xf3,
+	0x60, 0xd2, 0xef, 0x7f,
+	0x35, 0x52, 0x50, 0x0f,
+	0x2c, 0xb6, 0x79, 0x80,
+}