mirror of https://github.com/docker/docs.git
Merge pull request #47 from docker/cryptoservice-refactor
Refactor crypto service
This commit is contained in:
commit
935b9a9366
|
|
@ -52,7 +52,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/endophage/gotuf",
|
"ImportPath": "github.com/endophage/gotuf",
|
||||||
"Rev": "ab4ba80203ffa5bfd742e6891bd28bfbf43a9453"
|
"Rev": "82786136d505f582d0f898a2e80c9f6b97b1402c"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/go-sql-driver/mysql",
|
"ImportPath": "github.com/go-sql-driver/mysql",
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,36 @@
|
||||||
|
# GOTUF
|
||||||
|
|
||||||
This is still a work in progress but will shortly be a fully compliant
|
This is still a work in progress but will shortly be a fully compliant
|
||||||
Go implementation of [The Update Framework (TUF)](http://theupdateframework.com/).
|
Go implementation of [The Update Framework (TUF)](http://theupdateframework.com/).
|
||||||
|
|
||||||
|
## Where's the CLI
|
||||||
|
|
||||||
|
This repository provides a library only. The [Notary project](https://github.com/docker/notary)
|
||||||
|
from Docker should be considered the official CLI to be used with this implementation of TUF.
|
||||||
|
|
||||||
|
## TODOs:
|
||||||
|
|
||||||
|
- [X] Add Targets to existing repo
|
||||||
|
- [X] Sign metadata files
|
||||||
|
- [X] Refactor TufRepo to take care of signing ~~and verification~~
|
||||||
|
- [ ] Ensure consistent capitalization in naming (TUF\_\_\_ vs Tuf\_\_\_)
|
||||||
|
- [ ] Make caching of metadata files smarter - PR #5
|
||||||
|
- [ ] ~~Add configuration for CLI commands. Order of configuration priority from most to least: flags, config file, defaults~~ Notary should be the official CLI
|
||||||
|
- [X] Reasses organization of data types. Possibly consolidate a few things into the data package but break up package into a few more distinct files
|
||||||
|
- [ ] Comprehensive test cases
|
||||||
|
- [ ] Delete files no longer in use
|
||||||
|
- [ ] Fix up errors. Some have to be instantiated, others don't, the inconsistency is annoying.
|
||||||
|
- [X] Bump version numbers in meta files (could probably be done better)
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
This implementation was originally forked from [flynn/go-tuf](https://github.com/flynn/go-tuf),
|
This implementation was originally forked from [flynn/go-tuf](https://github.com/flynn/go-tuf),
|
||||||
however in attempting to add delegations I found I was making such
|
however in attempting to add delegations I found I was making such
|
||||||
significant changes that I could not maintain backwards compatibility
|
significant changes that I could not maintain backwards compatibility
|
||||||
without the code becoming overly convoluted.
|
without the code becoming overly convoluted.
|
||||||
|
|
||||||
|
Some features such as pluggable verifiers have alreayd been merged upstream to flynn/go-tuf
|
||||||
|
and we are in discussion with [titanous](https://github.com/titanous) about working to merge the 2 implementations.
|
||||||
|
|
||||||
This implementation retains the same 3 Clause BSD license present on
|
This implementation retains the same 3 Clause BSD license present on
|
||||||
the original flynn implementation.
|
the original flynn implementation.
|
||||||
|
|
||||||
TODOs:
|
|
||||||
|
|
||||||
- [X] Add Targets to existing repo
|
|
||||||
- [X] Sign metadata files
|
|
||||||
- [X] Refactor TufRepo to take care of signing ~~and verification~~
|
|
||||||
- [ ] Ensure consistent capitalization in naming (TUF\_\_\_ vs Tuf\_\_\_)
|
|
||||||
- [ ] Make caching of metadata files smarter
|
|
||||||
- [ ] ~~Add configuration for CLI commands. Order of configuration priority from most to least: flags, config file, defaults~~ Notary should be the official CLI
|
|
||||||
- [ ] Reasses organization of data types. Possibly consolidate a few things into the data package but break up package into a few more distinct files
|
|
||||||
- [ ] Comprehensive test cases
|
|
||||||
- [ ] Delete files no longer in use
|
|
||||||
- [ ] Fix up errors. Some have to be instantiated, others don't, the inconsistency is annoying.
|
|
||||||
- [X] Bump version numbers in meta files (could probably be done better)
|
|
||||||
|
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
"github.com/endophage/gotuf/keys"
|
|
||||||
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/endophage/gotuf/client"
|
|
||||||
"github.com/endophage/gotuf/data"
|
|
||||||
"github.com/endophage/gotuf/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
var commandDownload = cli.Command{
|
|
||||||
Name: "download",
|
|
||||||
Usage: "provide the path to a target you wish to download.",
|
|
||||||
Action: download,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
commandDownload.Flags = []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "root, r",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The local file path to the current, or immediately previous, root.json for the TUF repo.",
|
|
||||||
EnvVar: "TUF_ROOT",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "host, h",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The scheme and hostname of the TUF repository, e.g. http://example.com/.",
|
|
||||||
EnvVar: "TUF_HOST",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "meta, m",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The path prefix for TUF metadata files.",
|
|
||||||
EnvVar: "TUF_META_PREFIX",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "ext, e",
|
|
||||||
Value: "json",
|
|
||||||
Usage: "The file extension for TUF metadata files.",
|
|
||||||
EnvVar: "TUF_META_EXT",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "targets, t",
|
|
||||||
Value: "",
|
|
||||||
Usage: "The path prefix for target files.",
|
|
||||||
EnvVar: "TUF_TARGETS_PREFIX",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func download(ctx *cli.Context) {
|
|
||||||
if len(ctx.Args()) < 1 {
|
|
||||||
fmt.Println("At least one target name must be provided.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var root []byte
|
|
||||||
r := &data.Signed{}
|
|
||||||
err := json.Unmarshal(root, r)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Could not read initial root.json")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
kdb := keys.NewDB()
|
|
||||||
repo := tuf.NewTufRepo(kdb, nil)
|
|
||||||
repo.SetRoot(r)
|
|
||||||
remote, err := store.NewHTTPStore(
|
|
||||||
ctx.String("host"),
|
|
||||||
ctx.String("meta"),
|
|
||||||
ctx.String("ext"),
|
|
||||||
ctx.String("targets"),
|
|
||||||
)
|
|
||||||
cached := store.NewFileCacheStore(remote, "/tmp/tuf")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
client := client.NewClient(repo, cached, kdb)
|
|
||||||
|
|
||||||
err = client.Update()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
filename := filepath.Base(ctx.Args()[0])
|
|
||||||
f, err := os.OpenFile(filename, os.O_TRUNC|os.O_CREATE|os.O_RDWR, 0644)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
m := client.TargetMeta(ctx.Args()[0])
|
|
||||||
if m == nil {
|
|
||||||
fmt.Println("Requested package not found.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = client.DownloadTarget(f, ctx.Args()[0], m)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Println("Requested pacakge downloaded.")
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
app := cli.NewApp()
|
|
||||||
app.Name = "tufc"
|
|
||||||
app.Usage = "tuf download <package name>"
|
|
||||||
|
|
||||||
app.Flags = []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "config",
|
|
||||||
Value: "config.json",
|
|
||||||
Usage: "Set the path to a json configuration file for the TUF repo you want to interact with.",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Commands = []cli.Command{
|
|
||||||
commandDownload,
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Run(os.Args)
|
|
||||||
}
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/endophage/gotuf/signed"
|
|
||||||
"github.com/endophage/gotuf/store"
|
|
||||||
"github.com/endophage/gotuf/testutils"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.SetFlags(0)
|
|
||||||
|
|
||||||
usage := `usage: tuftools [-h|--help] <command> [<args>...]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h, --help
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
help Show usage for a specific command
|
|
||||||
meta Generate metadata from the given file path
|
|
||||||
|
|
||||||
See "tuf help <command>" for more information on a specific command
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", true)
|
|
||||||
cmd := args.String["<command>"]
|
|
||||||
cmdArgs := args.All["<args>"].([]string)
|
|
||||||
|
|
||||||
if cmd == "help" {
|
|
||||||
if len(cmdArgs) == 0 { // `tuf help`
|
|
||||||
fmt.Println(usage)
|
|
||||||
return
|
|
||||||
} else { // `tuf help <command>`
|
|
||||||
cmd = cmdArgs[0]
|
|
||||||
cmdArgs = []string{"--help"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := runCommand(cmd, cmdArgs); err != nil {
|
|
||||||
log.Fatalln("ERROR:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type cmdFunc func(*docopt.Args, *tuf.Repo) error
|
|
||||||
|
|
||||||
type command struct {
|
|
||||||
usage string
|
|
||||||
f cmdFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
var commands = make(map[string]*command)
|
|
||||||
|
|
||||||
func register(name string, f cmdFunc, usage string) {
|
|
||||||
commands[name] = &command{usage: usage, f: f}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runCommand(name string, args []string) error {
|
|
||||||
argv := make([]string, 1, 1+len(args))
|
|
||||||
argv[0] = name
|
|
||||||
argv = append(argv, args...)
|
|
||||||
|
|
||||||
cmd, ok := commands[name]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("%s is not a tuf command. See 'tuf help'", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedArgs, err := docopt.Parse(cmd.usage, argv, true, "", true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
db := testutils.GetSqliteDB()
|
|
||||||
local := store.DBStore(db, "")
|
|
||||||
signer := signed.Ed25519{}
|
|
||||||
repo, err := tuf.NewRepo(&signer, local, "sha256")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return cmd.f(parsedArgs, repo)
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/endophage/gotuf/data"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("meta", cmdMeta, `
|
|
||||||
usage: tuftools meta [<path>...]
|
|
||||||
|
|
||||||
Generate sample metadata for file(s) given by path.
|
|
||||||
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdMeta(args *docopt.Args, repo *tuf.Repo) error {
|
|
||||||
paths := args.All["<path>"].([]string)
|
|
||||||
for _, file := range paths {
|
|
||||||
reader, _ := os.Open(file)
|
|
||||||
meta, _ := data.NewFileMeta(reader, "sha256")
|
|
||||||
jsonBytes, err := json.Marshal(meta)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
filename := fmt.Sprintf("%s.meta.json", file)
|
|
||||||
err = ioutil.WriteFile(filename, jsonBytes, 0644)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
# go-tuf client CLI
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
```
|
|
||||||
go get github.com/flynn/go-tuf/cmd/tuf-client
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
The CLI provides three commands:
|
|
||||||
|
|
||||||
* `tuf-client init` - initialize a local file store using root keys (e.g. from
|
|
||||||
the output of `tuf root-keys`)
|
|
||||||
* `tuf-client list` - list available targets and their file sizes
|
|
||||||
* `tuf-client get` - get a target file and write to STDOUT
|
|
||||||
|
|
||||||
All commands require the base URL of the TUF repository as the first non-flag
|
|
||||||
argument, and accept an optional `--store` flag which is the path to the local
|
|
||||||
storage.
|
|
||||||
|
|
||||||
Run `tuf-client help` from the command line to get more detailed usage
|
|
||||||
information.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
```
|
|
||||||
# init
|
|
||||||
$ tuf-client init https://example.com/path/to/repo
|
|
||||||
|
|
||||||
# init with a custom store path
|
|
||||||
$ tuf-client init --store /tmp/tuf.db https://example.com/path/to/repo
|
|
||||||
|
|
||||||
# list available targets
|
|
||||||
$ tuf-client list https://example.com/path/to/repo
|
|
||||||
PATH SIZE
|
|
||||||
/foo.txt 1.6KB
|
|
||||||
/bar.txt 336B
|
|
||||||
/baz.txt 1.5KB
|
|
||||||
|
|
||||||
# get a target
|
|
||||||
$ tuf-client get https://example.com/path/to/repo /foo.txt
|
|
||||||
the contents of foo.txt
|
|
||||||
|
|
||||||
# the prefixed / is optional
|
|
||||||
$ tuf-client get https://example.com/path/to/repo foo.txt
|
|
||||||
the contents of foo.txt
|
|
||||||
```
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
tuf "github.com/endophage/gotuf/client"
|
|
||||||
"github.com/endophage/gotuf/utils"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("get", cmdGet, `
|
|
||||||
usage: tuf-client get [-s|--store=<path>] <url> <target>
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-s <path> The path to the local file store [default: tuf.db]
|
|
||||||
|
|
||||||
Get a target from the repository.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
type tmpFile struct {
|
|
||||||
*os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tmpFile) Delete() error {
|
|
||||||
t.Close()
|
|
||||||
return os.Remove(t.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdGet(args *docopt.Args, client *tuf.Client) error {
|
|
||||||
if _, err := client.Update(); err != nil && !tuf.IsLatestSnapshot(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
target := utils.NormalizeTarget(args.String["<target>"])
|
|
||||||
file, err := ioutil.TempFile("", "gotuf")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tmp := tmpFile{file}
|
|
||||||
if err := client.Download(target, &tmp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer tmp.Delete()
|
|
||||||
if _, err := tmp.Seek(0, os.SEEK_SET); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = io.Copy(os.Stdout, file)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
tuf "github.com/endophage/gotuf/client"
|
|
||||||
"github.com/endophage/gotuf/data"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("init", cmdInit, `
|
|
||||||
usage: tuf-client init [-s|--store=<path>] <url> [<root-keys-file>]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-s <path> The path to the local file store [default: tuf.db]
|
|
||||||
|
|
||||||
Initialize the local file store with root keys.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdInit(args *docopt.Args, client *tuf.Client) error {
|
|
||||||
file := args.String["<root-keys-file>"]
|
|
||||||
var in io.Reader
|
|
||||||
if file == "" || file == "-" {
|
|
||||||
in = os.Stdin
|
|
||||||
} else {
|
|
||||||
var err error
|
|
||||||
in, err = os.Open(file)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var rootKeys []*data.Key
|
|
||||||
if err := json.NewDecoder(in).Decode(&rootKeys); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return client.Init(rootKeys, len(rootKeys))
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"text/tabwriter"
|
|
||||||
|
|
||||||
"github.com/dustin/go-humanize"
|
|
||||||
tuf "github.com/endophage/gotuf/client"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("list", cmdList, `
|
|
||||||
usage: tuf-client list [-s|--store=<path>] <url>
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-s <path> The path to the local file store [default: tuf.db]
|
|
||||||
|
|
||||||
List available target files.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdList(args *docopt.Args, client *tuf.Client) error {
|
|
||||||
if _, err := client.Update(); err != nil && !tuf.IsLatestSnapshot(err) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
targets, err := client.Targets()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
w := tabwriter.NewWriter(os.Stdout, 1, 2, 2, ' ', 0)
|
|
||||||
defer w.Flush()
|
|
||||||
fmt.Fprintln(w, "PATH\tSIZE")
|
|
||||||
for path, meta := range targets {
|
|
||||||
fmt.Fprintf(w, "%s\t%s\n", path, humanize.Bytes(uint64(meta.Length)))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/endophage/gotuf/client"
|
|
||||||
"github.com/endophage/gotuf/store"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.SetFlags(0)
|
|
||||||
|
|
||||||
usage := `usage: tuf-client [-h|--help] <command> [<args>...]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h, --help
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
help Show usage for a specific command
|
|
||||||
init Initialize with root keys
|
|
||||||
list List available target files
|
|
||||||
get Get a target file
|
|
||||||
|
|
||||||
See "tuf-client help <command>" for more information on a specific command.
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", true)
|
|
||||||
cmd := args.String["<command>"]
|
|
||||||
cmdArgs := args.All["<args>"].([]string)
|
|
||||||
|
|
||||||
if cmd == "help" {
|
|
||||||
if len(cmdArgs) == 0 { // `tuf-client help`
|
|
||||||
fmt.Println(usage)
|
|
||||||
return
|
|
||||||
} else { // `tuf-client help <command>`
|
|
||||||
cmd = cmdArgs[0]
|
|
||||||
cmdArgs = []string{"--help"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := runCommand(cmd, cmdArgs); err != nil {
|
|
||||||
log.Fatalln("ERROR:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type cmdFunc func(*docopt.Args, *client.Client) error
|
|
||||||
|
|
||||||
type command struct {
|
|
||||||
usage string
|
|
||||||
f cmdFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
var commands = make(map[string]*command)
|
|
||||||
|
|
||||||
func register(name string, f cmdFunc, usage string) {
|
|
||||||
commands[name] = &command{usage: usage, f: f}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runCommand(name string, args []string) error {
|
|
||||||
argv := make([]string, 1, 1+len(args))
|
|
||||||
argv[0] = name
|
|
||||||
argv = append(argv, args...)
|
|
||||||
|
|
||||||
cmd, ok := commands[name]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("%s is not a tuf-client command. See 'tuf-client help'", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedArgs, err := docopt.Parse(cmd.usage, argv, true, "", true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := tufClient(parsedArgs)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return cmd.f(parsedArgs, client)
|
|
||||||
}
|
|
||||||
|
|
||||||
func tufClient(args *docopt.Args) (*client.Client, error) {
|
|
||||||
storePath, ok := args.String["--store"]
|
|
||||||
if !ok {
|
|
||||||
storePath = args.String["-s"]
|
|
||||||
}
|
|
||||||
local := store.FileSystemStore(storePath, nil)
|
|
||||||
remote, err := client.HTTPRemoteStore(args.String["<url>"], nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return client.NewClient(local, remote), nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
// "encoding/json"
|
|
||||||
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("add", cmdAdd, `
|
|
||||||
usage: tuf add [--expires=<days>] [--custom=<data>] [<path>...]
|
|
||||||
|
|
||||||
Add target file(s).
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--expires=<days> Set the targets manifest to expire <days> days from now.
|
|
||||||
--custom=<data> Set custom JSON data for the target(s).
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdAdd(args *docopt.Args, repo *tuf.Repo) error {
|
|
||||||
// var custom json.RawMessage
|
|
||||||
// if c := args.String["--custom"]; c != "" {
|
|
||||||
// custom = json.RawMessage(c)
|
|
||||||
// }
|
|
||||||
paths := args.All["<path>"].([]string)
|
|
||||||
if arg := args.String["--expires"]; arg != "" {
|
|
||||||
expires, err := parseExpires(arg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return repo.AddTargetsWithExpires(nil, expires, paths...)
|
|
||||||
}
|
|
||||||
return repo.AddTargets(nil, paths...)
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("clean", cmdClean, `
|
|
||||||
usage: tuf clean
|
|
||||||
|
|
||||||
Remove all staged manifests.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdClean(args *docopt.Args, repo *tuf.Repo) error {
|
|
||||||
return repo.Clean()
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("commit", cmdCommit, `
|
|
||||||
usage: tuf commit
|
|
||||||
|
|
||||||
Commit staged files to the repository.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdCommit(args *docopt.Args, repo *tuf.Repo) error {
|
|
||||||
return repo.Commit()
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("gen-key", cmdGenKey, `
|
|
||||||
usage: tuf gen-key [--expires=<days>] <role>
|
|
||||||
|
|
||||||
Generate a new signing key for the given role.
|
|
||||||
|
|
||||||
The key will be serialized to JSON and written to the "keys" directory with
|
|
||||||
filename pattern "ROLE-KEYID.json". The root manifest will also be staged
|
|
||||||
with the addition of the key's ID to the role's list of key IDs.
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--expires=<days> Set the root manifest to expire <days> days from now.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdGenKey(args *docopt.Args, repo *tuf.Repo) error {
|
|
||||||
role := args.String["<role>"]
|
|
||||||
var id string
|
|
||||||
var err error
|
|
||||||
if arg := args.String["--expires"]; arg != "" {
|
|
||||||
expires, err := parseExpires(arg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
id, err = repo.GenKeyWithExpires(role, expires)
|
|
||||||
} else {
|
|
||||||
id, err = repo.GenKey(role)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println("Generated", role, "key with ID", id)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("init", cmdInit, `
|
|
||||||
usage: tuf init [--consistent-snapshot=false]
|
|
||||||
|
|
||||||
Initialize a new repository.
|
|
||||||
|
|
||||||
This is only required if the repository should not generate consistent
|
|
||||||
snapshots (i.e. by passing "--consistent-snapshot=false"). If consistent
|
|
||||||
snapshots should be generated, the repository will be implicitly
|
|
||||||
initialized to do so when generating keys.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdInit(args *docopt.Args, repo *tuf.Repo) error {
|
|
||||||
return repo.Init(args.String["--consistent-snapshot"] != "false")
|
|
||||||
}
|
|
||||||
|
|
@ -1,167 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/term"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/endophage/gotuf/signed"
|
|
||||||
"github.com/endophage/gotuf/store"
|
|
||||||
"github.com/endophage/gotuf/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.SetFlags(0)
|
|
||||||
|
|
||||||
usage := `usage: tuf [-h|--help] [-d|--dir=<dir>] [--insecure-plaintext] <command> [<args>...]
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-h, --help
|
|
||||||
-d <dir> The path to the repository (defaults to the current working directory)
|
|
||||||
--insecure-plaintext Don't encrypt signing keys
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
help Show usage for a specific command
|
|
||||||
gen-key Generate a new signing key for a specific manifest
|
|
||||||
revoke-key Revoke a signing key
|
|
||||||
add Add target file(s)
|
|
||||||
remove Remove a target file
|
|
||||||
snapshot Update the snapshot manifest
|
|
||||||
timestamp Update the timestamp manifest
|
|
||||||
sign Sign a manifest
|
|
||||||
commit Commit staged files to the repository
|
|
||||||
regenerate Recreate the targets manifest
|
|
||||||
clean Remove all staged manifests
|
|
||||||
root-keys Output a JSON serialized array of root keys to STDOUT
|
|
||||||
|
|
||||||
See "tuf help <command>" for more information on a specific command
|
|
||||||
`
|
|
||||||
|
|
||||||
args, _ := docopt.Parse(usage, nil, true, "", true)
|
|
||||||
cmd := args.String["<command>"]
|
|
||||||
cmdArgs := args.All["<args>"].([]string)
|
|
||||||
|
|
||||||
if cmd == "help" {
|
|
||||||
if len(cmdArgs) == 0 { // `tuf help`
|
|
||||||
fmt.Println(usage)
|
|
||||||
return
|
|
||||||
} else { // `tuf help <command>`
|
|
||||||
cmd = cmdArgs[0]
|
|
||||||
cmdArgs = []string{"--help"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, ok := args.String["-d"]
|
|
||||||
if !ok {
|
|
||||||
dir = args.String["--dir"]
|
|
||||||
}
|
|
||||||
if dir == "" {
|
|
||||||
var err error
|
|
||||||
dir, err = os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := runCommand(cmd, cmdArgs, dir, args.Bool["--insecure-plaintext"]); err != nil {
|
|
||||||
log.Fatalln("ERROR:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type cmdFunc func(*docopt.Args, *tuf.Repo) error
|
|
||||||
|
|
||||||
type command struct {
|
|
||||||
usage string
|
|
||||||
f cmdFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
var commands = make(map[string]*command)
|
|
||||||
|
|
||||||
func register(name string, f cmdFunc, usage string) {
|
|
||||||
commands[name] = &command{usage: usage, f: f}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runCommand(name string, args []string, dir string, insecure bool) error {
|
|
||||||
argv := make([]string, 1, 1+len(args))
|
|
||||||
argv[0] = name
|
|
||||||
argv = append(argv, args...)
|
|
||||||
|
|
||||||
cmd, ok := commands[name]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("%s is not a tuf command. See 'tuf help'", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedArgs, err := docopt.Parse(cmd.usage, argv, true, "", true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var p utils.PassphraseFunc
|
|
||||||
if !insecure {
|
|
||||||
p = getPassphrase
|
|
||||||
}
|
|
||||||
signer := signed.Ed25519{}
|
|
||||||
repo, err := tuf.NewRepo(&signer, store.FileSystemStore(dir, p), "sha256")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return cmd.f(parsedArgs, repo)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseExpires(arg string) (time.Time, error) {
|
|
||||||
days, err := strconv.Atoi(arg)
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, fmt.Errorf("failed to parse --expires arg: %s", err)
|
|
||||||
}
|
|
||||||
return time.Now().AddDate(0, 0, days).UTC(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPassphrase(role string, confirm bool) ([]byte, error) {
|
|
||||||
if pass := os.Getenv(fmt.Sprintf("TUF_%s_PASSPHRASE", strings.ToUpper(role))); pass != "" {
|
|
||||||
return []byte(pass), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
state, err := term.SaveState(0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
term.DisableEcho(0, state)
|
|
||||||
defer term.RestoreTerminal(0, state)
|
|
||||||
|
|
||||||
stdin := bufio.NewReader(os.Stdin)
|
|
||||||
|
|
||||||
fmt.Printf("Enter %s keys passphrase: ", role)
|
|
||||||
passphrase, err := stdin.ReadBytes('\n')
|
|
||||||
fmt.Println()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
passphrase = passphrase[0 : len(passphrase)-1]
|
|
||||||
|
|
||||||
if !confirm {
|
|
||||||
return passphrase, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Repeat %s keys passphrase: ", role)
|
|
||||||
confirmation, err := stdin.ReadBytes('\n')
|
|
||||||
fmt.Println()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
confirmation = confirmation[0 : len(confirmation)-1]
|
|
||||||
|
|
||||||
if !bytes.Equal(passphrase, confirmation) {
|
|
||||||
return nil, errors.New("The entered passphrases do not match")
|
|
||||||
}
|
|
||||||
return passphrase, nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("regenerate", cmdRegenerate, `
|
|
||||||
usage: tuf regenerate [--consistent-snapshot=false]
|
|
||||||
|
|
||||||
Recreate the targets manifest.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdRegenerate(args *docopt.Args, repo *tuf.Repo) error {
|
|
||||||
// TODO: implement this
|
|
||||||
log.Println("not implemented")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("remove", cmdRemove, `
|
|
||||||
usage: tuf remove [--expires=<days>] [--all] [<path>...]
|
|
||||||
|
|
||||||
Remove target file(s).
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--all Remove all target files.
|
|
||||||
--expires=<days> Set the targets manifest to expire <days> days from now.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdRemove(args *docopt.Args, repo *tuf.Repo) error {
|
|
||||||
paths := args.All["<path>"].([]string)
|
|
||||||
if len(paths) == 0 && !args.Bool["--all"] {
|
|
||||||
return errors.New("either specify some paths or set the --all flag to remove all targets")
|
|
||||||
}
|
|
||||||
if arg := args.String["--expires"]; arg != "" {
|
|
||||||
expires, err := parseExpires(arg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return repo.RemoveTargetsWithExpires(expires, paths...)
|
|
||||||
}
|
|
||||||
return repo.RemoveTargets(paths)
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("revoke-key", cmdRevokeKey, `
|
|
||||||
usage: tuf revoke-key [--expires=<days>] <role> <id>
|
|
||||||
|
|
||||||
Revoke a signing key
|
|
||||||
|
|
||||||
The key will be removed from the root manifest, but the key will remain in the
|
|
||||||
"keys" directory if present.
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--expires=<days> Set the root manifest to expire <days> days from now.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdRevokeKey(args *docopt.Args, repo *tuf.Repo) error {
|
|
||||||
if arg := args.String["--expires"]; arg != "" {
|
|
||||||
expires, err := parseExpires(arg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return repo.RevokeKeyWithExpires(args.String["<role>"], args.String["<id>"], expires)
|
|
||||||
}
|
|
||||||
return repo.RevokeKey(args.String["<role>"], args.String["<id>"])
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("root-keys", cmdRootKeys, `
|
|
||||||
usage: tuf root-keys
|
|
||||||
|
|
||||||
Outputs a JSON serialized array of root keys to STDOUT.
|
|
||||||
|
|
||||||
The resulting JSON should be distributed to clients for performing initial updates.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdRootKeys(args *docopt.Args, repo *tuf.Repo) error {
|
|
||||||
keys, err := repo.RootKeys()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return json.NewEncoder(os.Stdout).Encode(keys)
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("sign", cmdSign, `
|
|
||||||
usage: tuf sign <manifest>
|
|
||||||
|
|
||||||
Sign a manifest.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdSign(args *docopt.Args, repo *tuf.Repo) error {
|
|
||||||
return repo.Sign(args.String["<manifest>"])
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("snapshot", cmdSnapshot, `
|
|
||||||
usage: tuf snapshot [--expires=<days>] [--compression=<format>]
|
|
||||||
|
|
||||||
Update the snapshot manifest.
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--expires=<days> Set the snapshot manifest to expire <days> days from now.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdSnapshot(args *docopt.Args, repo *tuf.Repo) error {
|
|
||||||
// TODO: parse --compression
|
|
||||||
if arg := args.String["--expires"]; arg != "" {
|
|
||||||
expires, err := parseExpires(arg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return repo.SnapshotWithExpires(tuf.CompressionTypeNone, expires)
|
|
||||||
}
|
|
||||||
return repo.Snapshot(tuf.CompressionTypeNone)
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/endophage/gotuf"
|
|
||||||
"github.com/flynn/go-docopt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
register("timestamp", cmdTimestamp, `
|
|
||||||
usage: tuf timestamp [--expires=<days>]
|
|
||||||
|
|
||||||
Update the timestamp manifest.
|
|
||||||
|
|
||||||
Options:
|
|
||||||
--expires=<days> Set the timestamp manifest to expire <days> days from now.
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdTimestamp(args *docopt.Args, repo *tuf.Repo) error {
|
|
||||||
if arg := args.String["--expires"]; arg != "" {
|
|
||||||
expires, err := parseExpires(arg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return repo.TimestampWithExpires(expires)
|
|
||||||
}
|
|
||||||
return repo.Timestamp()
|
|
||||||
}
|
|
||||||
|
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
type Key interface {
|
type Key interface {
|
||||||
ID() string
|
ID() string
|
||||||
Cipher() string
|
Algorithm() KeyAlgorithm
|
||||||
Public() []byte
|
Public() []byte
|
||||||
Private() []byte
|
Private() []byte
|
||||||
}
|
}
|
||||||
|
|
@ -22,13 +22,13 @@ type KeyPair struct {
|
||||||
|
|
||||||
type TUFKey struct {
|
type TUFKey struct {
|
||||||
id string `json:"-"`
|
id string `json:"-"`
|
||||||
Type string `json:"keytype"`
|
Type KeyAlgorithm `json:"keytype"`
|
||||||
Value KeyPair `json:"keyval"`
|
Value KeyPair `json:"keyval"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTUFKey(cipher string, public, private []byte) *TUFKey {
|
func NewTUFKey(algorithm KeyAlgorithm, public, private []byte) *TUFKey {
|
||||||
return &TUFKey{
|
return &TUFKey{
|
||||||
Type: cipher,
|
Type: algorithm,
|
||||||
Value: KeyPair{
|
Value: KeyPair{
|
||||||
Public: public,
|
Public: public,
|
||||||
Private: private,
|
Private: private,
|
||||||
|
|
@ -36,13 +36,13 @@ func NewTUFKey(cipher string, public, private []byte) *TUFKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k TUFKey) Cipher() string {
|
func (k TUFKey) Algorithm() KeyAlgorithm {
|
||||||
return k.Type
|
return k.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *TUFKey) ID() string {
|
func (k *TUFKey) ID() string {
|
||||||
if k.id == "" {
|
if k.id == "" {
|
||||||
pubK := NewTUFKey(k.Cipher(), k.Public(), nil)
|
pubK := NewTUFKey(k.Algorithm(), k.Public(), nil)
|
||||||
data, err := cjson.Marshal(&pubK)
|
data, err := cjson.Marshal(&pubK)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Error("Error generating key ID:", err)
|
logrus.Error("Error generating key ID:", err)
|
||||||
|
|
@ -65,10 +65,10 @@ func (k PublicKey) Private() []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPublicKey(cipher string, public []byte) *PublicKey {
|
func NewPublicKey(algorithm KeyAlgorithm, public []byte) *PublicKey {
|
||||||
return &PublicKey{
|
return &PublicKey{
|
||||||
TUFKey{
|
TUFKey{
|
||||||
Type: cipher,
|
Type: algorithm,
|
||||||
Value: KeyPair{
|
Value: KeyPair{
|
||||||
Public: public,
|
Public: public,
|
||||||
Private: nil,
|
Private: nil,
|
||||||
|
|
@ -87,10 +87,10 @@ type PrivateKey struct {
|
||||||
TUFKey
|
TUFKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPrivateKey(cipher string, public, private []byte) *PrivateKey {
|
func NewPrivateKey(algorithm KeyAlgorithm, public, private []byte) *PrivateKey {
|
||||||
return &PrivateKey{
|
return &PrivateKey{
|
||||||
TUFKey{
|
TUFKey{
|
||||||
Type: cipher,
|
Type: algorithm,
|
||||||
Value: KeyPair{
|
Value: KeyPair{
|
||||||
Public: []byte(public),
|
Public: []byte(public),
|
||||||
Private: []byte(private),
|
Private: []byte(private),
|
||||||
|
|
|
||||||
|
|
@ -8,21 +8,37 @@ import (
|
||||||
"hash"
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type KeyAlgorithm string
|
||||||
|
|
||||||
|
func (k KeyAlgorithm) String() string {
|
||||||
|
return string(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SigAlgorithm string
|
||||||
|
|
||||||
|
func (k SigAlgorithm) String() string {
|
||||||
|
return string(k)
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultHashAlgorithm = "sha256"
|
defaultHashAlgorithm = "sha256"
|
||||||
EDDSASignature = "eddsa"
|
|
||||||
RSAPSSSignature = "rsapss"
|
EDDSASignature SigAlgorithm = "eddsa"
|
||||||
ECDSASignature = "ecdsa"
|
RSAPSSSignature SigAlgorithm = "rsapss"
|
||||||
RSAKey = "rsa"
|
ECDSASignature SigAlgorithm = "ecdsa"
|
||||||
RSAx509Key = "rsa-x509"
|
PyCryptoSignature SigAlgorithm = "pycrypto-pkcs#1 pss"
|
||||||
ECDSAKey = "ecdsa"
|
|
||||||
ECDSAx509Key = "ecdsa-x509"
|
ED25519Key KeyAlgorithm = "ed25519"
|
||||||
PyCryptoSignature = "pycrypto-pkcs#1 pss"
|
RSAKey KeyAlgorithm = "rsa"
|
||||||
|
RSAx509Key KeyAlgorithm = "rsa-x509"
|
||||||
|
ECDSAKey KeyAlgorithm = "ecdsa"
|
||||||
|
ECDSAx509Key KeyAlgorithm = "ecdsa-x509"
|
||||||
)
|
)
|
||||||
|
|
||||||
var TUFTypes = map[string]string{
|
var TUFTypes = map[string]string{
|
||||||
|
|
@ -64,7 +80,7 @@ type Signed struct {
|
||||||
|
|
||||||
type Signature struct {
|
type Signature struct {
|
||||||
KeyID string `json:"keyid"`
|
KeyID string `json:"keyid"`
|
||||||
Method string `json:"method"`
|
Method SigAlgorithm `json:"method"`
|
||||||
Signature HexBytes `json:"sig"`
|
Signature HexBytes `json:"sig"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,3 +160,16 @@ func DefaultExpires(role string) time.Time {
|
||||||
}
|
}
|
||||||
return t.UTC().Round(time.Second)
|
return t.UTC().Round(time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type unmarshalledSignature Signature
|
||||||
|
|
||||||
|
func (s *Signature) UnmarshalJSON(data []byte) error {
|
||||||
|
uSignature := unmarshalledSignature{}
|
||||||
|
err := json.Unmarshal(data, &uSignature)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
uSignature.Method = SigAlgorithm(strings.ToLower(string(uSignature.Method)))
|
||||||
|
*s = Signature(uSignature)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
. "gopkg.in/check.v1"
|
. "gopkg.in/check.v1"
|
||||||
)
|
)
|
||||||
|
|
@ -43,3 +44,14 @@ func (TypesSuite) TestGenerateFileMetaExplicit(c *C) {
|
||||||
c.Assert(hash.String(), DeepEquals, val)
|
c.Assert(hash.String(), DeepEquals, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (TypesSuite) TestSignatureUnmarshalJSON(c *C) {
|
||||||
|
signatureJSON := `{"keyid":"97e8e1b51b6e7cf8720a56b5334bd8692ac5b28233c590b89fab0b0cd93eeedc","method":"RSA","sig":"2230cba525e4f5f8fc744f234221ca9a92924da4cc5faf69a778848882fcf7a20dbb57296add87f600891f2569a9c36706314c240f9361c60fd36f5a915a0e9712fc437b761e8f480868d7a4444724daa0d29a2669c0edbd4046046649a506b3d711d0aa5e70cb9d09dec7381e7de27a3168e77731e08f6ed56fcce2478855e837816fb69aff53412477748cd198dce783850080d37aeb929ad0f81460ebd31e61b772b6c7aa56977c787d4281fa45dbdefbb38d449eb5bccb2702964a52c78811545939712c8280dee0b23b2fa9fbbdd6a0c42476689ace655eba0745b4a21ba108bcd03ad00fdefff416dc74e08486a0538f8fd24989e1b9fc89e675141b7c"}`
|
||||||
|
|
||||||
|
var sig Signature
|
||||||
|
err := json.Unmarshal([]byte(signatureJSON), &sig)
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
|
// Check that the method string is lowercased
|
||||||
|
c.Assert(sig.Method.String(), Equals, "rsa")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package signed
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/agl/ed25519"
|
"github.com/agl/ed25519"
|
||||||
"github.com/endophage/gotuf/data"
|
"github.com/endophage/gotuf/data"
|
||||||
|
|
@ -37,7 +38,7 @@ func (trust *Ed25519) Sign(keyIDs []string, toSign []byte) ([]data.Signature, er
|
||||||
sig := ed25519.Sign(&priv, toSign)
|
sig := ed25519.Sign(&priv, toSign)
|
||||||
signatures = append(signatures, data.Signature{
|
signatures = append(signatures, data.Signature{
|
||||||
KeyID: kID,
|
KeyID: kID,
|
||||||
Method: "ED25519",
|
Method: data.EDDSASignature,
|
||||||
Signature: sig[:],
|
Signature: sig[:],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -45,13 +46,17 @@ func (trust *Ed25519) Sign(keyIDs []string, toSign []byte) ([]data.Signature, er
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (trust *Ed25519) Create(role string) (*data.PublicKey, error) {
|
func (trust *Ed25519) Create(role string, algorithm data.KeyAlgorithm) (*data.PublicKey, error) {
|
||||||
|
if algorithm != data.ED25519Key {
|
||||||
|
return nil, errors.New("only ED25519 supported by this cryptoservice")
|
||||||
|
}
|
||||||
|
|
||||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
public := data.NewPublicKey("ED25519", pub[:])
|
public := data.NewPublicKey(data.ED25519Key, pub[:])
|
||||||
private := data.NewPrivateKey("ED25519", pub[:], priv[:])
|
private := data.NewPrivateKey(data.ED25519Key, pub[:], priv[:])
|
||||||
trust.addKey(private)
|
trust.addKey(private)
|
||||||
return public, nil
|
return public, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,9 @@ type SigningService interface {
|
||||||
type KeyService interface {
|
type KeyService interface {
|
||||||
// Create issues a new key pair and is responsible for loading
|
// Create issues a new key pair and is responsible for loading
|
||||||
// the private key into the appropriate signing service.
|
// the private key into the appropriate signing service.
|
||||||
Create(role string) (*data.PublicKey, error)
|
// The role isn't currently used for anything, but it's here to support
|
||||||
|
// future features
|
||||||
// PublicKeys return the PublicKey instances for the given KeyIDs
|
Create(role string, algorithm data.KeyAlgorithm) (*data.PublicKey, error)
|
||||||
// PublicKeys(keyIDs ...string) (map[string]*data.PublicKey, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CryptoService defines a unified Signing and Key Service as this
|
// CryptoService defines a unified Signing and Key Service as this
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,9 @@ import (
|
||||||
"github.com/endophage/gotuf/data"
|
"github.com/endophage/gotuf/data"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Signer encapsulates a signing service with some convenience methods to
|
|
||||||
// interface between TUF keys and the generic service interface
|
|
||||||
type Signer struct {
|
|
||||||
service CryptoService
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSigner(service CryptoService) *Signer {
|
|
||||||
return &Signer{service}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign takes a data.Signed and a key, calculated and adds the signature
|
// Sign takes a data.Signed and a key, calculated and adds the signature
|
||||||
// to the data.Signed
|
// to the data.Signed
|
||||||
func (signer *Signer) Sign(s *data.Signed, keys ...*data.PublicKey) error {
|
func Sign(service CryptoService, s *data.Signed, keys ...*data.PublicKey) error {
|
||||||
logrus.Debugf("sign called with %d keys", len(keys))
|
logrus.Debugf("sign called with %d keys", len(keys))
|
||||||
signatures := make([]data.Signature, 0, len(s.Signatures)+1)
|
signatures := make([]data.Signature, 0, len(s.Signatures)+1)
|
||||||
keyIDMemb := make(map[string]struct{})
|
keyIDMemb := make(map[string]struct{})
|
||||||
|
|
@ -34,7 +24,7 @@ func (signer *Signer) Sign(s *data.Signed, keys ...*data.PublicKey) error {
|
||||||
}
|
}
|
||||||
signatures = append(signatures, sig)
|
signatures = append(signatures, sig)
|
||||||
}
|
}
|
||||||
newSigs, err := signer.service.Sign(keyIDs, s.Signed)
|
newSigs, err := service.Sign(keyIDs, s.Signed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -42,12 +32,3 @@ func (signer *Signer) Sign(s *data.Signed, keys ...*data.PublicKey) error {
|
||||||
s.Signatures = append(signatures, newSigs...)
|
s.Signatures = append(signatures, newSigs...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (signer *Signer) Create(role string) (*data.PublicKey, error) {
|
|
||||||
key, err := signer.service.Create(role)
|
|
||||||
return key, err
|
|
||||||
}
|
|
||||||
|
|
||||||
//func (signer *Signer) PublicKeys(keyIDs ...string) (map[string]*data.PublicKey, error) {
|
|
||||||
// return signer.service.PublicKeys(keyIDs...)
|
|
||||||
//}
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ func (mts *MockCryptoService) Sign(keyIDs []string, _ []byte) ([]data.Signature,
|
||||||
return sigs, nil
|
return sigs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mts *MockCryptoService) Create(_ string) (*data.PublicKey, error) {
|
func (mts *MockCryptoService) Create(_ string, _ data.KeyAlgorithm) (*data.PublicKey, error) {
|
||||||
return &mts.testKey, nil
|
return &mts.testKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,16 +42,14 @@ var _ CryptoService = &MockCryptoService{}
|
||||||
func TestBasicSign(t *testing.T) {
|
func TestBasicSign(t *testing.T) {
|
||||||
testKey, _ := pem.Decode([]byte(testKeyPEM1))
|
testKey, _ := pem.Decode([]byte(testKeyPEM1))
|
||||||
k := data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
k := data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
||||||
signer := Signer{&MockCryptoService{
|
mockCryptoService := &MockCryptoService{testKey: *k}
|
||||||
testKey: *k,
|
key, err := mockCryptoService.Create("root", data.ED25519Key)
|
||||||
}}
|
|
||||||
key, err := signer.Create("root")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
testData := data.Signed{}
|
testData := data.Signed{}
|
||||||
|
|
||||||
signer.Sign(&testData, key)
|
Sign(mockCryptoService, &testData, key)
|
||||||
|
|
||||||
if len(testData.Signatures) != 1 {
|
if len(testData.Signatures) != 1 {
|
||||||
t.Fatalf("Incorrect number of signatures: %d", len(testData.Signatures))
|
t.Fatalf("Incorrect number of signatures: %d", len(testData.Signatures))
|
||||||
|
|
@ -69,13 +67,11 @@ func TestBasicSign(t *testing.T) {
|
||||||
func TestReSign(t *testing.T) {
|
func TestReSign(t *testing.T) {
|
||||||
testKey, _ := pem.Decode([]byte(testKeyPEM1))
|
testKey, _ := pem.Decode([]byte(testKeyPEM1))
|
||||||
k := data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
k := data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
||||||
signer := Signer{&MockCryptoService{
|
mockCryptoService := &MockCryptoService{testKey: *k}
|
||||||
testKey: *k,
|
|
||||||
}}
|
|
||||||
testData := data.Signed{}
|
testData := data.Signed{}
|
||||||
|
|
||||||
signer.Sign(&testData, k)
|
Sign(mockCryptoService, &testData, k)
|
||||||
signer.Sign(&testData, k)
|
Sign(mockCryptoService, &testData, k)
|
||||||
|
|
||||||
if len(testData.Signatures) != 1 {
|
if len(testData.Signatures) != 1 {
|
||||||
t.Fatalf("Incorrect number of signatures: %d", len(testData.Signatures))
|
t.Fatalf("Incorrect number of signatures: %d", len(testData.Signatures))
|
||||||
|
|
@ -88,16 +84,16 @@ func TestReSign(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMultiSign(t *testing.T) {
|
func TestMultiSign(t *testing.T) {
|
||||||
signer := Signer{&MockCryptoService{}}
|
mockCryptoService := &MockCryptoService{}
|
||||||
testData := data.Signed{}
|
testData := data.Signed{}
|
||||||
|
|
||||||
testKey, _ := pem.Decode([]byte(testKeyPEM1))
|
testKey, _ := pem.Decode([]byte(testKeyPEM1))
|
||||||
key := data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
key := data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
||||||
signer.Sign(&testData, key)
|
Sign(mockCryptoService, &testData, key)
|
||||||
|
|
||||||
testKey, _ = pem.Decode([]byte(testKeyPEM2))
|
testKey, _ = pem.Decode([]byte(testKeyPEM2))
|
||||||
key = data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
key = data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
||||||
signer.Sign(&testData, key)
|
Sign(mockCryptoService, &testData, key)
|
||||||
|
|
||||||
if len(testData.Signatures) != 2 {
|
if len(testData.Signatures) != 2 {
|
||||||
t.Fatalf("Incorrect number of signatures: %d", len(testData.Signatures))
|
t.Fatalf("Incorrect number of signatures: %d", len(testData.Signatures))
|
||||||
|
|
@ -115,11 +111,9 @@ func TestMultiSign(t *testing.T) {
|
||||||
func TestCreate(t *testing.T) {
|
func TestCreate(t *testing.T) {
|
||||||
testKey, _ := pem.Decode([]byte(testKeyPEM1))
|
testKey, _ := pem.Decode([]byte(testKeyPEM1))
|
||||||
k := data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
k := data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
||||||
signer := Signer{&MockCryptoService{
|
mockCryptoService := &MockCryptoService{testKey: *k}
|
||||||
testKey: *k,
|
|
||||||
}}
|
|
||||||
|
|
||||||
key, err := signer.Create("root")
|
key, err := mockCryptoService.Create("root", data.ED25519Key)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import (
|
||||||
// Verifiers serves as a map of all verifiers available on the system and
|
// Verifiers serves as a map of all verifiers available on the system and
|
||||||
// can be injected into a verificationService. For testing and configuration
|
// can be injected into a verificationService. For testing and configuration
|
||||||
// purposes, it will not be used by default.
|
// purposes, it will not be used by default.
|
||||||
var Verifiers = map[string]Verifier{
|
var Verifiers = map[data.SigAlgorithm]Verifier{
|
||||||
data.RSAPSSSignature: RSAPSSVerifier{},
|
data.RSAPSSSignature: RSAPSSVerifier{},
|
||||||
data.PyCryptoSignature: RSAPyCryptoVerifier{},
|
data.PyCryptoSignature: RSAPyCryptoVerifier{},
|
||||||
data.ECDSASignature: ECDSAVerifier{},
|
data.ECDSASignature: ECDSAVerifier{},
|
||||||
|
|
@ -27,8 +27,8 @@ var Verifiers = map[string]Verifier{
|
||||||
|
|
||||||
// RegisterVerifier provides a convenience function for init() functions
|
// RegisterVerifier provides a convenience function for init() functions
|
||||||
// to register additional verifiers or replace existing ones.
|
// to register additional verifiers or replace existing ones.
|
||||||
func RegisterVerifier(name string, v Verifier) {
|
func RegisterVerifier(algorithm data.SigAlgorithm, v Verifier) {
|
||||||
curr, ok := Verifiers[name]
|
curr, ok := Verifiers[algorithm]
|
||||||
if ok {
|
if ok {
|
||||||
typOld := reflect.TypeOf(curr)
|
typOld := reflect.TypeOf(curr)
|
||||||
typNew := reflect.TypeOf(v)
|
typNew := reflect.TypeOf(v)
|
||||||
|
|
@ -38,9 +38,9 @@ func RegisterVerifier(name string, v Verifier) {
|
||||||
typNew.PkgPath(), typNew.Name(),
|
typNew.PkgPath(), typNew.Name(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
logrus.Debug("adding verifier for: ", name)
|
logrus.Debug("adding verifier for: ", algorithm)
|
||||||
}
|
}
|
||||||
Verifiers[name] = v
|
Verifiers[algorithm] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
type Ed25519Verifier struct{}
|
type Ed25519Verifier struct{}
|
||||||
|
|
@ -83,10 +83,10 @@ type RSAPSSVerifier struct{}
|
||||||
|
|
||||||
// Verify does the actual check.
|
// Verify does the actual check.
|
||||||
func (v RSAPSSVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
|
func (v RSAPSSVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
|
||||||
cipher := key.Cipher()
|
algorithm := key.Algorithm()
|
||||||
var pubKey crypto.PublicKey
|
var pubKey crypto.PublicKey
|
||||||
|
|
||||||
switch cipher {
|
switch algorithm {
|
||||||
case data.RSAx509Key:
|
case data.RSAx509Key:
|
||||||
pemCert, _ := pem.Decode([]byte(key.Public()))
|
pemCert, _ := pem.Decode([]byte(key.Public()))
|
||||||
if pemCert == nil {
|
if pemCert == nil {
|
||||||
|
|
@ -107,7 +107,7 @@ func (v RSAPSSVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
|
||||||
return ErrInvalid
|
return ErrInvalid
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
logrus.Infof("invalid key type for RSAPSS verifier: %s", cipher)
|
logrus.Infof("invalid key type for RSAPSS verifier: %s", algorithm)
|
||||||
return ErrInvalid
|
return ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,10 +145,10 @@ type ECDSAVerifier struct{}
|
||||||
|
|
||||||
// Verify does the actual check.
|
// Verify does the actual check.
|
||||||
func (v ECDSAVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
|
func (v ECDSAVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
|
||||||
cipher := key.Cipher()
|
algorithm := key.Algorithm()
|
||||||
var pubKey crypto.PublicKey
|
var pubKey crypto.PublicKey
|
||||||
|
|
||||||
switch cipher {
|
switch algorithm {
|
||||||
case data.ECDSAx509Key:
|
case data.ECDSAx509Key:
|
||||||
pemCert, _ := pem.Decode([]byte(key.Public()))
|
pemCert, _ := pem.Decode([]byte(key.Public()))
|
||||||
if pemCert == nil {
|
if pemCert == nil {
|
||||||
|
|
@ -170,7 +170,7 @@ func (v ECDSAVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
|
||||||
return ErrInvalid
|
return ErrInvalid
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
logrus.Infof("invalid key type for ECDSA verifier: %s", cipher)
|
logrus.Infof("invalid key type for ECDSA verifier: %s", algorithm)
|
||||||
return ErrInvalid
|
return ErrInvalid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type KeyTemplate struct {
|
type KeyTemplate struct {
|
||||||
KeyType string
|
KeyType data.KeyAlgorithm
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseRSAKey = `{"keytype":"{{.KeyType}}","keyval":{"public":"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyyvBtTg2xzYS+MTTIBqSpI4V78tt8Yzqi7Jki/Z6NqjiDvcnbgcTqNR2t6B2W5NjGdp/hSaT2jyHM+kdmEGaPxg/zIuHbL3NIp4e0qwovWiEgACPIaELdn8O/kt5swsSKl1KMvLCH1sM86qMibNMAZ/hXOwd90TcHXCgZ91wHEAmsdjDC3dB0TT+FBgOac8RM01Y196QrZoOaDMTWh0EQfw7YbXAElhFVDFxBzDdYWbcIHSIogXQmq0CP+zaL/1WgcZZIClt2M6WCaxxF1S34wNn45gCvVZiZQ/iKWHerSr/2dGQeGo+7ezMSutRzvJ+01fInD86RS/CEtBCFZ1VyQIDAQAB","private":"MIIEpAIBAAKCAQEAyyvBtTg2xzYS+MTTIBqSpI4V78tt8Yzqi7Jki/Z6NqjiDvcnbgcTqNR2t6B2W5NjGdp/hSaT2jyHM+kdmEGaPxg/zIuHbL3NIp4e0qwovWiEgACPIaELdn8O/kt5swsSKl1KMvLCH1sM86qMibNMAZ/hXOwd90TcHXCgZ91wHEAmsdjDC3dB0TT+FBgOac8RM01Y196QrZoOaDMTWh0EQfw7YbXAElhFVDFxBzDdYWbcIHSIogXQmq0CP+zaL/1WgcZZIClt2M6WCaxxF1S34wNn45gCvVZiZQ/iKWHerSr/2dGQeGo+7ezMSutRzvJ+01fInD86RS/CEtBCFZ1VyQIDAQABAoIBAHar8FFxrE1gAGTeUpOF8fG8LIQMRwO4U6eVY7V9GpWiv6gOJTHXYFxU/aL0Ty3eQRxwy9tyVRo8EJz5pRex+e6ws1M+jLOviYqW4VocxQ8dZYd+zBvQfWmRfah7XXJ/HPUx2I05zrmR7VbGX6Bu4g5w3KnyIO61gfyQNKF2bm2Q3yblfupx3URvX0bl180R/+QN2Aslr4zxULFE6b+qJqBydrztq+AAP3WmskRxGa6irFnKxkspJqUpQN1mFselj6iQrzAcwkRPoCw0RwCCMq1/OOYvQtgxTJcO4zDVlbw54PvnxPZtcCWw7fO8oZ2Fvo2SDo75CDOATOGaT4Y9iqECgYEAzWZSpFbN9ZHmvq1lJQg//jFAyjsXRNn/nSvyLQILXltz6EHatImnXo3v+SivG91tfzBI1GfDvGUGaJpvKHoomB+qmhd8KIQhO5MBdAKZMf9fZqZofOPTD9xRXECCwdi+XqHBmL+l1OWz+O9Bh+Qobs2as/hQVgHaoXhQpE0NkTcCgYEA/Tjf6JBGl1+WxQDoGZDJrXoejzG9OFW19RjMdmPrg3t4fnbDtqTpZtCzXxPTCSeMrvplKbqAqZglWyq227ksKw4p7O6YfyhdtvC58oJmivlLr6sFaTsER7mDcYce8sQpqm+XQ8IPbnOk0Z1l6g56euTwTnew49uy25M6U1xL0P8CgYEAxEXv2Kw+OVhHV5PX4BBHHj6we88FiDyMfwM8cvfOJ0datekf9X7ImZkmZEAVPJpWBMD+B0J0jzU2b4SLjfFVkzBHVOH2Ob0xCH2MWPAWtekin7OKizUlPbW5ZV8b0+Kq30DQ/4a7D3rEhK8UPqeuX1tHZox1MAqrgbq3zJj4yvcCgYEAktYPKPm4pYCdmgFrlZ+bA0iEPf7Wvbsd91F5BtHsOOM5PQQ7e0bnvWIaEXEad/2CG9lBHlBy2WVLjDEZthILpa/h6e11ao8KwNGY0iKBuebT17rxOVMqqTjPGt8CuD2994IcEgOPFTpkAdUmyvG4XlkxbB8F6St17NPUB5DGuhsCgYA//Lfytk0FflXEeRQ16LT1YXgV7pcR2jsha4+4O5pxSFw/kTsOfJaYHg8StmROoyFnyE3sg76dCgLn0LENRCe5BvDhJnp5bMpQldG3XwcAxH8FGFNY4LtV/2ZKnJhxcONkfmzQPOmTyedOzrKQ+bNURsqLukCypP7/by6afBY4dA=="}}`
|
const baseRSAKey = `{"keytype":"{{.KeyType}}","keyval":{"public":"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyyvBtTg2xzYS+MTTIBqSpI4V78tt8Yzqi7Jki/Z6NqjiDvcnbgcTqNR2t6B2W5NjGdp/hSaT2jyHM+kdmEGaPxg/zIuHbL3NIp4e0qwovWiEgACPIaELdn8O/kt5swsSKl1KMvLCH1sM86qMibNMAZ/hXOwd90TcHXCgZ91wHEAmsdjDC3dB0TT+FBgOac8RM01Y196QrZoOaDMTWh0EQfw7YbXAElhFVDFxBzDdYWbcIHSIogXQmq0CP+zaL/1WgcZZIClt2M6WCaxxF1S34wNn45gCvVZiZQ/iKWHerSr/2dGQeGo+7ezMSutRzvJ+01fInD86RS/CEtBCFZ1VyQIDAQAB","private":"MIIEpAIBAAKCAQEAyyvBtTg2xzYS+MTTIBqSpI4V78tt8Yzqi7Jki/Z6NqjiDvcnbgcTqNR2t6B2W5NjGdp/hSaT2jyHM+kdmEGaPxg/zIuHbL3NIp4e0qwovWiEgACPIaELdn8O/kt5swsSKl1KMvLCH1sM86qMibNMAZ/hXOwd90TcHXCgZ91wHEAmsdjDC3dB0TT+FBgOac8RM01Y196QrZoOaDMTWh0EQfw7YbXAElhFVDFxBzDdYWbcIHSIogXQmq0CP+zaL/1WgcZZIClt2M6WCaxxF1S34wNn45gCvVZiZQ/iKWHerSr/2dGQeGo+7ezMSutRzvJ+01fInD86RS/CEtBCFZ1VyQIDAQABAoIBAHar8FFxrE1gAGTeUpOF8fG8LIQMRwO4U6eVY7V9GpWiv6gOJTHXYFxU/aL0Ty3eQRxwy9tyVRo8EJz5pRex+e6ws1M+jLOviYqW4VocxQ8dZYd+zBvQfWmRfah7XXJ/HPUx2I05zrmR7VbGX6Bu4g5w3KnyIO61gfyQNKF2bm2Q3yblfupx3URvX0bl180R/+QN2Aslr4zxULFE6b+qJqBydrztq+AAP3WmskRxGa6irFnKxkspJqUpQN1mFselj6iQrzAcwkRPoCw0RwCCMq1/OOYvQtgxTJcO4zDVlbw54PvnxPZtcCWw7fO8oZ2Fvo2SDo75CDOATOGaT4Y9iqECgYEAzWZSpFbN9ZHmvq1lJQg//jFAyjsXRNn/nSvyLQILXltz6EHatImnXo3v+SivG91tfzBI1GfDvGUGaJpvKHoomB+qmhd8KIQhO5MBdAKZMf9fZqZofOPTD9xRXECCwdi+XqHBmL+l1OWz+O9Bh+Qobs2as/hQVgHaoXhQpE0NkTcCgYEA/Tjf6JBGl1+WxQDoGZDJrXoejzG9OFW19RjMdmPrg3t4fnbDtqTpZtCzXxPTCSeMrvplKbqAqZglWyq227ksKw4p7O6YfyhdtvC58oJmivlLr6sFaTsER7mDcYce8sQpqm+XQ8IPbnOk0Z1l6g56euTwTnew49uy25M6U1xL0P8CgYEAxEXv2Kw+OVhHV5PX4BBHHj6we88FiDyMfwM8cvfOJ0datekf9X7ImZkmZEAVPJpWBMD+B0J0jzU2b4SLjfFVkzBHVOH2Ob0xCH2MWPAWtekin7OKizUlPbW5ZV8b0+Kq30DQ/4a7D3rEhK8UPqeuX1tHZox1MAqrgbq3zJj4yvcCgYEAktYPKPm4pYCdmgFrlZ+bA0iEPf7Wvbsd91F5BtHsOOM5PQQ7e0bnvWIaEXEad/2CG9lBHlBy2WVLjDEZthILpa/h6e11ao8KwNGY0iKBuebT17rxOVMqqTjPGt8CuD2994IcEgOPFTpkAdUmyvG4XlkxbB8F6St17NPUB5DGuhsCgYA//Lfytk0FflXEeRQ16LT1YXgV7pcR2jsha4+4O5pxSFw/kTsOfJaYHg8StmROoyFnyE3sg76dCgLn0LENRCe5BvDhJnp5bMpQldG3XwcAxH8FGFNY4LtV/2ZKnJhxcONkfmzQPOmTyedOzrKQ+bNURsqLukCypP7/by6afBY4dA=="}}`
|
||||||
|
|
@ -248,8 +248,8 @@ func TestECDSAVerifierWithInvalidSignature(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
|
func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
|
||||||
if privKey.Cipher() != data.RSAKey {
|
if privKey.Algorithm() != data.RSAKey {
|
||||||
return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher())
|
return nil, fmt.Errorf("private key type not supported: %s", privKey.Algorithm())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an rsa.PrivateKey out of the private key bytes
|
// Create an rsa.PrivateKey out of the private key bytes
|
||||||
|
|
@ -268,8 +268,8 @@ func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte,
|
||||||
}
|
}
|
||||||
|
|
||||||
func ecdsaSign(privKey *data.PrivateKey, hashed []byte) ([]byte, error) {
|
func ecdsaSign(privKey *data.PrivateKey, hashed []byte) ([]byte, error) {
|
||||||
if privKey.Cipher() != data.ECDSAKey {
|
if privKey.Algorithm() != data.ECDSAKey {
|
||||||
return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher())
|
return nil, fmt.Errorf("private key type not supported: %s", privKey.Algorithm())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an ecdsa.PrivateKey out of the private key bytes
|
// Create an ecdsa.PrivateKey out of the private key bytes
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package signed
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
|
@ -45,8 +44,8 @@ func VerifyRoot(s *data.Signed, minVersion int, keys map[string]*data.PublicKey,
|
||||||
|
|
||||||
valid := make(map[string]struct{})
|
valid := make(map[string]struct{})
|
||||||
for _, sig := range s.Signatures {
|
for _, sig := range s.Signatures {
|
||||||
// make method lookup consistent with case uniformity.
|
// method lookup is consistent due to Unmarshal JSON doing lower case for us.
|
||||||
method := strings.ToLower(sig.Method)
|
method := sig.Method
|
||||||
verifier, ok := Verifiers[method]
|
verifier, ok := Verifiers[method]
|
||||||
if !ok {
|
if !ok {
|
||||||
logrus.Debugf("continuing b/c signing method is not supported for verify root: %s\n", sig.Method)
|
logrus.Debugf("continuing b/c signing method is not supported for verify root: %s\n", sig.Method)
|
||||||
|
|
@ -133,8 +132,8 @@ func VerifySignatures(s *data.Signed, role string, db *keys.KeyDB) error {
|
||||||
logrus.Debugf("continuing b/c keyid lookup was nil: %s\n", sig.KeyID)
|
logrus.Debugf("continuing b/c keyid lookup was nil: %s\n", sig.KeyID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// make method lookup consistent with case uniformity.
|
// method lookup is consistent due to Unmarshal JSON doing lower case for us.
|
||||||
method := strings.ToLower(sig.Method)
|
method := sig.Method
|
||||||
verifier, ok := Verifiers[method]
|
verifier, ok := Verifiers[method]
|
||||||
if !ok {
|
if !ok {
|
||||||
logrus.Debugf("continuing b/c signing method is not supported: %s\n", sig.Method)
|
logrus.Debugf("continuing b/c signing method is not supported: %s\n", sig.Method)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ var _ = Suite(&VerifySuite{})
|
||||||
|
|
||||||
func (VerifySuite) Test(c *C) {
|
func (VerifySuite) Test(c *C) {
|
||||||
trust := NewEd25519()
|
trust := NewEd25519()
|
||||||
signer := NewSigner(trust)
|
|
||||||
type test struct {
|
type test struct {
|
||||||
name string
|
name string
|
||||||
keys []*data.PublicKey
|
keys []*data.PublicKey
|
||||||
|
|
@ -78,8 +77,8 @@ func (VerifySuite) Test(c *C) {
|
||||||
{
|
{
|
||||||
name: "more than enough signatures",
|
name: "more than enough signatures",
|
||||||
mut: func(t *test) {
|
mut: func(t *test) {
|
||||||
k, _ := signer.Create("root")
|
k, _ := trust.Create("root", data.ED25519Key)
|
||||||
signer.Sign(t.s, k)
|
Sign(trust, t.s, k)
|
||||||
t.keys = append(t.keys, k)
|
t.keys = append(t.keys, k)
|
||||||
t.roles["root"].KeyIDs = append(t.roles["root"].KeyIDs, k.ID())
|
t.roles["root"].KeyIDs = append(t.roles["root"].KeyIDs, k.ID())
|
||||||
},
|
},
|
||||||
|
|
@ -95,15 +94,15 @@ func (VerifySuite) Test(c *C) {
|
||||||
{
|
{
|
||||||
name: "unknown key",
|
name: "unknown key",
|
||||||
mut: func(t *test) {
|
mut: func(t *test) {
|
||||||
k, _ := signer.Create("root")
|
k, _ := trust.Create("root", data.ED25519Key)
|
||||||
signer.Sign(t.s, k)
|
Sign(trust, t.s, k)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "unknown key below threshold",
|
name: "unknown key below threshold",
|
||||||
mut: func(t *test) {
|
mut: func(t *test) {
|
||||||
k, _ := signer.Create("root")
|
k, _ := trust.Create("root", data.ED25519Key)
|
||||||
signer.Sign(t.s, k)
|
Sign(trust, t.s, k)
|
||||||
t.roles["root"].Threshold = 2
|
t.roles["root"].Threshold = 2
|
||||||
},
|
},
|
||||||
err: ErrRoleThreshold,
|
err: ErrRoleThreshold,
|
||||||
|
|
@ -111,16 +110,16 @@ func (VerifySuite) Test(c *C) {
|
||||||
{
|
{
|
||||||
name: "unknown keys in db",
|
name: "unknown keys in db",
|
||||||
mut: func(t *test) {
|
mut: func(t *test) {
|
||||||
k, _ := signer.Create("root")
|
k, _ := trust.Create("root", data.ED25519Key)
|
||||||
signer.Sign(t.s, k)
|
Sign(trust, t.s, k)
|
||||||
t.keys = append(t.keys, k)
|
t.keys = append(t.keys, k)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "unknown keys in db below threshold",
|
name: "unknown keys in db below threshold",
|
||||||
mut: func(t *test) {
|
mut: func(t *test) {
|
||||||
k, _ := signer.Create("root")
|
k, _ := trust.Create("root", data.ED25519Key)
|
||||||
signer.Sign(t.s, k)
|
Sign(trust, t.s, k)
|
||||||
t.keys = append(t.keys, k)
|
t.keys = append(t.keys, k)
|
||||||
t.roles["root"].Threshold = 2
|
t.roles["root"].Threshold = 2
|
||||||
},
|
},
|
||||||
|
|
@ -157,13 +156,13 @@ func (VerifySuite) Test(c *C) {
|
||||||
t.typ = data.TUFTypes[t.role]
|
t.typ = data.TUFTypes[t.role]
|
||||||
}
|
}
|
||||||
if t.keys == nil && t.s == nil {
|
if t.keys == nil && t.s == nil {
|
||||||
k, _ := signer.Create("root")
|
k, _ := trust.Create("root", data.ED25519Key)
|
||||||
meta := &signedMeta{Type: t.typ, Version: t.ver, Expires: t.exp.Format("2006-01-02 15:04:05 MST")}
|
meta := &signedMeta{Type: t.typ, Version: t.ver, Expires: t.exp.Format("2006-01-02 15:04:05 MST")}
|
||||||
|
|
||||||
b, err := cjson.Marshal(meta)
|
b, err := cjson.Marshal(meta)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
s := &data.Signed{Signed: b}
|
s := &data.Signed{Signed: b}
|
||||||
signer.Sign(s, k)
|
Sign(trust, s, k)
|
||||||
t.s = s
|
t.s = s
|
||||||
t.keys = []*data.PublicKey{k}
|
t.keys = []*data.PublicKey{k}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tent/canonical-json-go"
|
"github.com/tent/canonical-json-go"
|
||||||
|
|
@ -55,7 +54,7 @@ func TestGetMeta(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
method := strings.ToLower(p.Signatures[0].Method)
|
method := p.Signatures[0].Method
|
||||||
err = signed.Verifiers[method].Verify(k, sigBytes, msg)
|
err = signed.Verifiers[method].Verify(k, sigBytes, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -68,13 +67,13 @@ func TestPyCryptoRSAPSSCompat(t *testing.T) {
|
||||||
//privPem := "-----BEGIN RSA PRIVATE KEY-----\nMIIG4wIBAAKCAYEAnKuXZeefa2LmgxaL5NsMzKOHNe+x/nL6ik+lDBCTV6OdcwAh\nHQS+PONGhrChIUVR6Vth3hUCrreLzPO73Oo5VSCuRJ53UronENl6lsa5mFKP8StY\nLvIDITNvkoT3j52BJIjyNUK9UKY9As2TNqDfBEPIRp28ev/NViwGOEkBu2UAbwCI\ndnDXm8JQErCZA0Ydm7PKGgjLbFsFGrVzqXHK6pdzJXlhr9yap3UpgQ/iO9JtoEYB\n2EXsnSrPc9JRjR30bNHHtnVql3fvinXrAEwq3xmN4p+R4VGzfdQN+8Kl/IPjqWB5\n35twhFYEG/B7Ze8IwbygBjK3co/KnOPqMUrMBI8ztvPiogz+MvXb8WvarZ6TMTh8\nifZI96r7zzqyzjR1hJulEy3IsMGvz8XS2J0X7sXoaqszEtXdq5ef5zKVxkiyIQZc\nbPgmpHLq4MgfdryuVVc/RPASoRIXG4lKaTJj1ANMFPxDQpHudCLxwCzjCb+sVa20\nHBRPTnzo8LSZkI6jAgMBAAECggGAdzyI7z/HLt2IfoAsXDLynNRgVYZluzgawiU3\ngeUjnnGhpSKWERXJC2IWDPBk0YOGgcnQxErNTdfXiFZ/xfRlSgqjVwob2lRe4w4B\npLr+CZXcgznv1VrPUvdolOSp3R2Mahfn7u0qVDUQ/g8jWVI6KW7FACmQhzQkPM8o\ntLGrpcmK+PA465uaHKtYccEB02ILqrK8v++tknv7eIZczrsSKlS1h/HHjSaidYxP\n2DAUiF7wnChrwwQEvuEUHhwVgQcoDMBoow0zwHdbFiFO2ZT54H2oiJWLhpR/x6RK\ngM1seqoPH2sYErPJACMcYsMtF4Tx7b5c4WSj3vDCGb+jeqnNS6nFC3aMnv75mUS2\nYDPU1heJFd8pNHVf0RDejLZZUiJSnXf3vpOxt9Xv2+4He0jeMfLV7zX0mO2Ni3MJ\nx6PiVy4xerHImOuuHzSla5crOq2ECiAxd1wEOFDRD2LRHzfhpk1ghiA5xA1qwc7Z\neRnkVfoy6PPZ4lZakZTm0p8YCQURAoHBAMUIC/7vnayLae7POmgy+np/ty7iMfyd\nV1eO6LTO21KAaGGlhaY26WD/5LcG2FUgc5jKKahprGrmiNLzLUeQPckJmuijSEVM\nl/4DlRvCo867l7fLaVqYzsQBBdeGIFNiT+FBOd8atff87ZBEfH/rXbDi7METD/VR\n4TdblnCsKYAXEJUdkw3IK7SUGERiQZIwKXrH/Map4ibDrljJ71iCgEureU0DBwcg\nwLftmjGMISoLscdRxeubX5uf/yxtHBJeRwKBwQDLjzHhb4gNGdBHUl4hZPAGCq1V\nLX/GpfoOVObW64Lud+tI6N9GNua5/vWduL7MWWOzDTMZysganhKwsJCY5SqAA9p0\nb6ohusf9i1nUnOa2F2j+weuYPXrTYm+ZrESBBdaEJPuj3R5YHVujrBA9Xe0kVOe3\nne151A+0xJOI3tX9CttIaQAsXR7cMDinkDITw6i7X4olRMPCSixHLW97cDsVDRGt\necO1d4dP3OGscN+vKCoL6tDKDotzWHYPwjH47sUCgcEAoVI8WCiipbKkMnaTsNsE\ngKXvO0DSgq3k5HjLCbdQldUzIbgfnH7bSKNcBYtiNxjR7OihgRW8qO5GWsnmafCs\n1dy6a/2835id3cnbHRaZflvUFhVDFn2E1bCsstFLyFn3Y0w/cO9yzC/X5sZcVXRF\nit3R0Selakv3JZckru4XMJwx5JWJYMBjIIAc+miknWg3niL+UT6pPun65xG3mXWI\nS+yC7c4rw+dKQ44UMLs2MDHRBoxqi8T0W/x9NkfDszpjAoHAclH7S4ZdvC3RIR0L\nLGoJuvroGbwx1JiGdOINuooNwGuswge2zTIsJi0gN/H3hcB2E6rIFiYid4BrMrwW\nmSeq1LZVS6siu0qw4p4OVy+/CmjfWKQD8j4k6u6PipiK6IMk1JYIlSCr2AS04JjT\njgNgGVVtxVt2cUM9huIXkXjEaRZdzK7boA60NCkIyGJdHWh3LLQdW4zg/A64C0lj\nIMoJBGuQkAKgfRuh7KI6Q6Qom7BM3OCFXdUJUEBQHc2MTyeZAoHAJdBQGBn1RFZ+\nn75AnbTMZJ6Twp2fVjzWUz/+rnXFlo87ynA18MR2BzaDST4Bvda29UBFGb32Mux9\nOHukqLgIE5jDuqWjy4B5eCoxZf/OvwlgXkX9+gprGR3axn/PZBFPbFB4ZmjbWLzn\nbocn7FJCXf+Cm0cMmv1jIIxej19MUU/duq9iq4RkHY2LG+KrSEQIUVmImCftXdN3\n/qNP5JetY0eH6C+KRc8JqDB0nvbqZNOgYXOfYXo/5Gk8XIHTFihm\n-----END RSA PRIVATE KEY-----"
|
//privPem := "-----BEGIN RSA PRIVATE KEY-----\nMIIG4wIBAAKCAYEAnKuXZeefa2LmgxaL5NsMzKOHNe+x/nL6ik+lDBCTV6OdcwAh\nHQS+PONGhrChIUVR6Vth3hUCrreLzPO73Oo5VSCuRJ53UronENl6lsa5mFKP8StY\nLvIDITNvkoT3j52BJIjyNUK9UKY9As2TNqDfBEPIRp28ev/NViwGOEkBu2UAbwCI\ndnDXm8JQErCZA0Ydm7PKGgjLbFsFGrVzqXHK6pdzJXlhr9yap3UpgQ/iO9JtoEYB\n2EXsnSrPc9JRjR30bNHHtnVql3fvinXrAEwq3xmN4p+R4VGzfdQN+8Kl/IPjqWB5\n35twhFYEG/B7Ze8IwbygBjK3co/KnOPqMUrMBI8ztvPiogz+MvXb8WvarZ6TMTh8\nifZI96r7zzqyzjR1hJulEy3IsMGvz8XS2J0X7sXoaqszEtXdq5ef5zKVxkiyIQZc\nbPgmpHLq4MgfdryuVVc/RPASoRIXG4lKaTJj1ANMFPxDQpHudCLxwCzjCb+sVa20\nHBRPTnzo8LSZkI6jAgMBAAECggGAdzyI7z/HLt2IfoAsXDLynNRgVYZluzgawiU3\ngeUjnnGhpSKWERXJC2IWDPBk0YOGgcnQxErNTdfXiFZ/xfRlSgqjVwob2lRe4w4B\npLr+CZXcgznv1VrPUvdolOSp3R2Mahfn7u0qVDUQ/g8jWVI6KW7FACmQhzQkPM8o\ntLGrpcmK+PA465uaHKtYccEB02ILqrK8v++tknv7eIZczrsSKlS1h/HHjSaidYxP\n2DAUiF7wnChrwwQEvuEUHhwVgQcoDMBoow0zwHdbFiFO2ZT54H2oiJWLhpR/x6RK\ngM1seqoPH2sYErPJACMcYsMtF4Tx7b5c4WSj3vDCGb+jeqnNS6nFC3aMnv75mUS2\nYDPU1heJFd8pNHVf0RDejLZZUiJSnXf3vpOxt9Xv2+4He0jeMfLV7zX0mO2Ni3MJ\nx6PiVy4xerHImOuuHzSla5crOq2ECiAxd1wEOFDRD2LRHzfhpk1ghiA5xA1qwc7Z\neRnkVfoy6PPZ4lZakZTm0p8YCQURAoHBAMUIC/7vnayLae7POmgy+np/ty7iMfyd\nV1eO6LTO21KAaGGlhaY26WD/5LcG2FUgc5jKKahprGrmiNLzLUeQPckJmuijSEVM\nl/4DlRvCo867l7fLaVqYzsQBBdeGIFNiT+FBOd8atff87ZBEfH/rXbDi7METD/VR\n4TdblnCsKYAXEJUdkw3IK7SUGERiQZIwKXrH/Map4ibDrljJ71iCgEureU0DBwcg\nwLftmjGMISoLscdRxeubX5uf/yxtHBJeRwKBwQDLjzHhb4gNGdBHUl4hZPAGCq1V\nLX/GpfoOVObW64Lud+tI6N9GNua5/vWduL7MWWOzDTMZysganhKwsJCY5SqAA9p0\nb6ohusf9i1nUnOa2F2j+weuYPXrTYm+ZrESBBdaEJPuj3R5YHVujrBA9Xe0kVOe3\nne151A+0xJOI3tX9CttIaQAsXR7cMDinkDITw6i7X4olRMPCSixHLW97cDsVDRGt\necO1d4dP3OGscN+vKCoL6tDKDotzWHYPwjH47sUCgcEAoVI8WCiipbKkMnaTsNsE\ngKXvO0DSgq3k5HjLCbdQldUzIbgfnH7bSKNcBYtiNxjR7OihgRW8qO5GWsnmafCs\n1dy6a/2835id3cnbHRaZflvUFhVDFn2E1bCsstFLyFn3Y0w/cO9yzC/X5sZcVXRF\nit3R0Selakv3JZckru4XMJwx5JWJYMBjIIAc+miknWg3niL+UT6pPun65xG3mXWI\nS+yC7c4rw+dKQ44UMLs2MDHRBoxqi8T0W/x9NkfDszpjAoHAclH7S4ZdvC3RIR0L\nLGoJuvroGbwx1JiGdOINuooNwGuswge2zTIsJi0gN/H3hcB2E6rIFiYid4BrMrwW\nmSeq1LZVS6siu0qw4p4OVy+/CmjfWKQD8j4k6u6PipiK6IMk1JYIlSCr2AS04JjT\njgNgGVVtxVt2cUM9huIXkXjEaRZdzK7boA60NCkIyGJdHWh3LLQdW4zg/A64C0lj\nIMoJBGuQkAKgfRuh7KI6Q6Qom7BM3OCFXdUJUEBQHc2MTyeZAoHAJdBQGBn1RFZ+\nn75AnbTMZJ6Twp2fVjzWUz/+rnXFlo87ynA18MR2BzaDST4Bvda29UBFGb32Mux9\nOHukqLgIE5jDuqWjy4B5eCoxZf/OvwlgXkX9+gprGR3axn/PZBFPbFB4ZmjbWLzn\nbocn7FJCXf+Cm0cMmv1jIIxej19MUU/duq9iq4RkHY2LG+KrSEQIUVmImCftXdN3\n/qNP5JetY0eH6C+KRc8JqDB0nvbqZNOgYXOfYXo/5Gk8XIHTFihm\n-----END RSA PRIVATE KEY-----"
|
||||||
testStr := "The quick brown fox jumps over the lazy dog."
|
testStr := "The quick brown fox jumps over the lazy dog."
|
||||||
sigHex := "4e05ee9e435653549ac4eddbc43e1a6868636e8ea6dbec2564435afcb0de47e0824cddbd88776ddb20728c53ecc90b5d543d5c37575fda8bd0317025fc07de62ee8084b1a75203b1a23d1ef4ac285da3d1fc63317d5b2cf1aafa3e522acedd366ccd5fe4a7f02a42922237426ca3dc154c57408638b9bfaf0d0213855d4e9ee621db204151bcb13d4dbb18f930ec601469c992c84b14e9e0b6f91ac9517bb3b749dd117e1cbac2e4acb0e549f44558a2005898a226d5b6c8b9291d7abae0d9e0a16858b89662a085f74a202deb867acab792bdbd2c36731217caea8b17bd210c29b890472f11e5afdd1dd7b69004db070e04201778f2c49f5758643881403d45a58d08f51b5c63910c6185892f0b590f191d760b669eff2464456f130239bba94acf54a0cb98f6939ff84ae26a37f9b890be259d9b5d636f6eb367b53e895227d7d79a3a88afd6d28c198ee80f6527437c5fbf63accb81709925c4e03d1c9eaee86f58e4bd1c669d6af042dbd412de0d13b98b1111e2fadbe34b45de52125e9a"
|
sigHex := "4e05ee9e435653549ac4eddbc43e1a6868636e8ea6dbec2564435afcb0de47e0824cddbd88776ddb20728c53ecc90b5d543d5c37575fda8bd0317025fc07de62ee8084b1a75203b1a23d1ef4ac285da3d1fc63317d5b2cf1aafa3e522acedd366ccd5fe4a7f02a42922237426ca3dc154c57408638b9bfaf0d0213855d4e9ee621db204151bcb13d4dbb18f930ec601469c992c84b14e9e0b6f91ac9517bb3b749dd117e1cbac2e4acb0e549f44558a2005898a226d5b6c8b9291d7abae0d9e0a16858b89662a085f74a202deb867acab792bdbd2c36731217caea8b17bd210c29b890472f11e5afdd1dd7b69004db070e04201778f2c49f5758643881403d45a58d08f51b5c63910c6185892f0b590f191d760b669eff2464456f130239bba94acf54a0cb98f6939ff84ae26a37f9b890be259d9b5d636f6eb367b53e895227d7d79a3a88afd6d28c198ee80f6527437c5fbf63accb81709925c4e03d1c9eaee86f58e4bd1c669d6af042dbd412de0d13b98b1111e2fadbe34b45de52125e9a"
|
||||||
k := data.NewPublicKey("RSA", []byte(pubPem))
|
k := data.NewPublicKey(data.RSAKey, []byte(pubPem))
|
||||||
|
|
||||||
sigBytes, err := hex.DecodeString(sigHex)
|
sigBytes, err := hex.DecodeString(sigHex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
v := signed.RSAPSSVerifier{}
|
v := signed.RSAPyCryptoVerifier{}
|
||||||
err = v.Verify(k, sigBytes, []byte(testStr))
|
err = v.Verify(k, sigBytes, []byte(testStr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -88,11 +87,11 @@ func TestPyNaCled25519Compat(t *testing.T) {
|
||||||
sigHex := "166e7013e48f26dccb4e68fe4cf558d1cd3af902f8395534336a7f8b4c56588694aa3ac671767246298a59d5ef4224f02c854f41bfcfe70241db4be1546d6a00"
|
sigHex := "166e7013e48f26dccb4e68fe4cf558d1cd3af902f8395534336a7f8b4c56588694aa3ac671767246298a59d5ef4224f02c854f41bfcfe70241db4be1546d6a00"
|
||||||
|
|
||||||
pub, _ := hex.DecodeString(pubHex)
|
pub, _ := hex.DecodeString(pubHex)
|
||||||
k := data.NewPublicKey("ED25519", pub)
|
k := data.NewPublicKey(data.ED25519Key, pub)
|
||||||
|
|
||||||
sigBytes, _ := hex.DecodeString(sigHex)
|
sigBytes, _ := hex.DecodeString(sigHex)
|
||||||
|
|
||||||
err := signed.Verifiers["ED25519"].Verify(k, sigBytes, []byte(testStr))
|
err := signed.Verifiers[data.EDDSASignature].Verify(k, sigBytes, []byte(testStr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,16 +55,16 @@ type TufRepo struct {
|
||||||
Snapshot *data.SignedSnapshot
|
Snapshot *data.SignedSnapshot
|
||||||
Timestamp *data.SignedTimestamp
|
Timestamp *data.SignedTimestamp
|
||||||
keysDB *keys.KeyDB
|
keysDB *keys.KeyDB
|
||||||
signer *signed.Signer
|
cryptoService signed.CryptoService
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTufRepo initializes a TufRepo instance with a keysDB and a signer.
|
// NewTufRepo initializes a TufRepo instance with a keysDB and a signer.
|
||||||
// If the TufRepo will only be used for reading, the signer should be nil.
|
// If the TufRepo will only be used for reading, the signer should be nil.
|
||||||
func NewTufRepo(keysDB *keys.KeyDB, signer *signed.Signer) *TufRepo {
|
func NewTufRepo(keysDB *keys.KeyDB, cryptoService signed.CryptoService) *TufRepo {
|
||||||
repo := &TufRepo{
|
repo := &TufRepo{
|
||||||
Targets: make(map[string]*data.SignedTargets),
|
Targets: make(map[string]*data.SignedTargets),
|
||||||
keysDB: keysDB,
|
keysDB: keysDB,
|
||||||
signer: signer,
|
cryptoService: cryptoService,
|
||||||
}
|
}
|
||||||
return repo
|
return repo
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +75,7 @@ func (tr *TufRepo) AddBaseKeys(role string, keys ...data.Key) error {
|
||||||
return &ErrNotLoaded{role: "root"}
|
return &ErrNotLoaded{role: "root"}
|
||||||
}
|
}
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
key := data.NewPublicKey(k.Cipher(), k.Public())
|
key := data.NewPublicKey(k.Algorithm(), k.Public())
|
||||||
tr.Root.Signed.Keys[key.ID()] = key
|
tr.Root.Signed.Keys[key.ID()] = key
|
||||||
tr.keysDB.AddKey(key)
|
tr.keysDB.AddKey(key)
|
||||||
tr.Root.Signed.Roles[role].KeyIDs = append(tr.Root.Signed.Roles[role].KeyIDs, key.ID())
|
tr.Root.Signed.Roles[role].KeyIDs = append(tr.Root.Signed.Roles[role].KeyIDs, key.ID())
|
||||||
|
|
@ -142,7 +142,7 @@ func (tr *TufRepo) UpdateDelegations(role *data.Role, keys []data.Key, before st
|
||||||
return errors.ErrInvalidRole{}
|
return errors.ErrInvalidRole{}
|
||||||
}
|
}
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
key := data.NewPublicKey(k.Cipher(), k.Public())
|
key := data.NewPublicKey(k.Algorithm(), k.Public())
|
||||||
if !utils.StrSliceContains(role.KeyIDs, key.ID()) {
|
if !utils.StrSliceContains(role.KeyIDs, key.ID()) {
|
||||||
role.KeyIDs = append(role.KeyIDs, key.ID())
|
role.KeyIDs = append(role.KeyIDs, key.ID())
|
||||||
}
|
}
|
||||||
|
|
@ -203,7 +203,7 @@ func (tr *TufRepo) InitRoot(consistent bool) error {
|
||||||
// checked by KeyDB when role was added.
|
// checked by KeyDB when role was added.
|
||||||
key := tr.keysDB.GetKey(kid)
|
key := tr.keysDB.GetKey(kid)
|
||||||
// Create new key object to doubly ensure private key is excluded
|
// Create new key object to doubly ensure private key is excluded
|
||||||
k := data.NewPublicKey(key.Cipher(), key.Public())
|
k := data.NewPublicKey(key.Algorithm(), key.Public())
|
||||||
rootKeys[kid] = k
|
rootKeys[kid] = k
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -449,7 +449,7 @@ func (tr *TufRepo) UpdateTimestamp(s *data.Signed) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *TufRepo) SignRoot(expires time.Time, signer *signed.Signer) (*data.Signed, error) {
|
func (tr *TufRepo) SignRoot(expires time.Time, cryptoService signed.CryptoService) (*data.Signed, error) {
|
||||||
logrus.Debug("signing root...")
|
logrus.Debug("signing root...")
|
||||||
if tr.Root.Dirty {
|
if tr.Root.Dirty {
|
||||||
tr.Root.Signed.Version++
|
tr.Root.Signed.Version++
|
||||||
|
|
@ -459,7 +459,7 @@ func (tr *TufRepo) SignRoot(expires time.Time, signer *signed.Signer) (*data.Sig
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
signed, err = tr.sign(signed, *root, signer)
|
signed, err = tr.sign(signed, *root, cryptoService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -467,7 +467,7 @@ func (tr *TufRepo) SignRoot(expires time.Time, signer *signed.Signer) (*data.Sig
|
||||||
return signed, nil
|
return signed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *TufRepo) SignTargets(role string, expires time.Time, signer *signed.Signer) (*data.Signed, error) {
|
func (tr *TufRepo) SignTargets(role string, expires time.Time, cryptoService signed.CryptoService) (*data.Signed, error) {
|
||||||
logrus.Debugf("sign targets called for role %s", role)
|
logrus.Debugf("sign targets called for role %s", role)
|
||||||
if tr.Targets[role].Dirty {
|
if tr.Targets[role].Dirty {
|
||||||
tr.Targets[role].Signed.Version++
|
tr.Targets[role].Signed.Version++
|
||||||
|
|
@ -477,7 +477,7 @@ func (tr *TufRepo) SignTargets(role string, expires time.Time, signer *signed.Si
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
targets := tr.keysDB.GetRole(role)
|
targets := tr.keysDB.GetRole(role)
|
||||||
signed, err = tr.sign(signed, *targets, signer)
|
signed, err = tr.sign(signed, *targets, cryptoService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Debug("errored signing ", role)
|
logrus.Debug("errored signing ", role)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -494,10 +494,10 @@ func (tr *TufRepo) SignTargets(role string, expires time.Time, signer *signed.Si
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *TufRepo) SignSnapshot(expires time.Time, signer *signed.Signer) (*data.Signed, error) {
|
func (tr *TufRepo) SignSnapshot(expires time.Time, cryptoService signed.CryptoService) (*data.Signed, error) {
|
||||||
logrus.Debug("signing snapshot...")
|
logrus.Debug("signing snapshot...")
|
||||||
if tr.Root.Dirty {
|
if tr.Root.Dirty {
|
||||||
signedRoot, err := tr.SignRoot(data.DefaultExpires("root"), signer)
|
signedRoot, err := tr.SignRoot(data.DefaultExpires("root"), cryptoService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -511,7 +511,7 @@ func (tr *TufRepo) SignSnapshot(expires time.Time, signer *signed.Signer) (*data
|
||||||
if !targets.Dirty {
|
if !targets.Dirty {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
signedTargets, err := tr.SignTargets(role, data.DefaultExpires("targets"), signer)
|
signedTargets, err := tr.SignTargets(role, data.DefaultExpires("targets"), cryptoService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -528,7 +528,7 @@ func (tr *TufRepo) SignSnapshot(expires time.Time, signer *signed.Signer) (*data
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
snapshot := tr.keysDB.GetRole(data.ValidRoles["snapshot"])
|
snapshot := tr.keysDB.GetRole(data.ValidRoles["snapshot"])
|
||||||
signed, err = tr.sign(signed, *snapshot, signer)
|
signed, err = tr.sign(signed, *snapshot, cryptoService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -543,10 +543,10 @@ func (tr *TufRepo) SignSnapshot(expires time.Time, signer *signed.Signer) (*data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *TufRepo) SignTimestamp(expires time.Time, signer *signed.Signer) (*data.Signed, error) {
|
func (tr *TufRepo) SignTimestamp(expires time.Time, cryptoService signed.CryptoService) (*data.Signed, error) {
|
||||||
logrus.Debug("SignTimestamp")
|
logrus.Debug("SignTimestamp")
|
||||||
if tr.Snapshot.Dirty {
|
if tr.Snapshot.Dirty {
|
||||||
signedSnapshot, err := tr.SignSnapshot(data.DefaultExpires("snapshot"), signer)
|
signedSnapshot, err := tr.SignSnapshot(data.DefaultExpires("snapshot"), cryptoService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -562,7 +562,7 @@ func (tr *TufRepo) SignTimestamp(expires time.Time, signer *signed.Signer) (*dat
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
timestamp := tr.keysDB.GetRole(data.ValidRoles["timestamp"])
|
timestamp := tr.keysDB.GetRole(data.ValidRoles["timestamp"])
|
||||||
signed, err = tr.sign(signed, *timestamp, signer)
|
signed, err = tr.sign(signed, *timestamp, cryptoService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -578,7 +578,7 @@ func (tr *TufRepo) SignTimestamp(expires time.Time, signer *signed.Signer) (*dat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr TufRepo) sign(signed *data.Signed, role data.Role, signer *signed.Signer) (*data.Signed, error) {
|
func (tr TufRepo) sign(signedData *data.Signed, role data.Role, cryptoService signed.CryptoService) (*data.Signed, error) {
|
||||||
ks := make([]*data.PublicKey, 0, len(role.KeyIDs))
|
ks := make([]*data.PublicKey, 0, len(role.KeyIDs))
|
||||||
for _, kid := range role.KeyIDs {
|
for _, kid := range role.KeyIDs {
|
||||||
k := tr.keysDB.GetKey(kid)
|
k := tr.keysDB.GetKey(kid)
|
||||||
|
|
@ -590,16 +590,12 @@ func (tr TufRepo) sign(signed *data.Signed, role data.Role, signer *signed.Signe
|
||||||
if len(ks) < 1 {
|
if len(ks) < 1 {
|
||||||
return nil, keys.ErrInvalidKey
|
return nil, keys.ErrInvalidKey
|
||||||
}
|
}
|
||||||
if signer != nil {
|
if cryptoService == nil {
|
||||||
err := signer.Sign(signed, ks...)
|
cryptoService = tr.cryptoService
|
||||||
|
}
|
||||||
|
err := signed.Sign(cryptoService, signedData, ks...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
return signedData, nil
|
||||||
err := tr.signer.Sign(signed, ks...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return signed, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,21 +13,20 @@ import (
|
||||||
"github.com/endophage/gotuf/signed"
|
"github.com/endophage/gotuf/signed"
|
||||||
)
|
)
|
||||||
|
|
||||||
func initRepo(t *testing.T, signer *signed.Signer, keyDB *keys.KeyDB) *TufRepo {
|
func initRepo(t *testing.T, cryptoService signed.CryptoService, keyDB *keys.KeyDB) *TufRepo {
|
||||||
|
rootKey, err := cryptoService.Create("root", data.ED25519Key)
|
||||||
rootKey, err := signer.Create("root")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
targetsKey, err := signer.Create("targets")
|
targetsKey, err := cryptoService.Create("targets", data.ED25519Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
snapshotKey, err := signer.Create("snapshot")
|
snapshotKey, err := cryptoService.Create("snapshot", data.ED25519Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
timestampKey, err := signer.Create("timestamp")
|
timestampKey, err := cryptoService.Create("timestamp", data.ED25519Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -71,7 +70,7 @@ func initRepo(t *testing.T, signer *signed.Signer, keyDB *keys.KeyDB) *TufRepo {
|
||||||
keyDB.AddRole(snapshotRole)
|
keyDB.AddRole(snapshotRole)
|
||||||
keyDB.AddRole(timestampRole)
|
keyDB.AddRole(timestampRole)
|
||||||
|
|
||||||
repo := NewTufRepo(keyDB, signer)
|
repo := NewTufRepo(keyDB, cryptoService)
|
||||||
err = repo.InitRepo(false)
|
err = repo.InitRepo(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -124,19 +123,17 @@ func writeRepo(t *testing.T, dir string, repo *TufRepo) {
|
||||||
|
|
||||||
func TestInitRepo(t *testing.T) {
|
func TestInitRepo(t *testing.T) {
|
||||||
ed25519 := signed.NewEd25519()
|
ed25519 := signed.NewEd25519()
|
||||||
signer := signed.NewSigner(ed25519)
|
|
||||||
keyDB := keys.NewDB()
|
keyDB := keys.NewDB()
|
||||||
repo := initRepo(t, signer, keyDB)
|
repo := initRepo(t, ed25519, keyDB)
|
||||||
writeRepo(t, "/tmp/tufrepo", repo)
|
writeRepo(t, "/tmp/tufrepo", repo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateDelegations(t *testing.T) {
|
func TestUpdateDelegations(t *testing.T) {
|
||||||
ed25519 := signed.NewEd25519()
|
ed25519 := signed.NewEd25519()
|
||||||
signer := signed.NewSigner(ed25519)
|
|
||||||
keyDB := keys.NewDB()
|
keyDB := keys.NewDB()
|
||||||
repo := initRepo(t, signer, keyDB)
|
repo := initRepo(t, ed25519, keyDB)
|
||||||
|
|
||||||
testKey, err := signer.Create("targets/test")
|
testKey, err := ed25519.Create("targets/test", data.ED25519Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +147,7 @@ func TestUpdateDelegations(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
testDeepKey, err := signer.Create("targets/test/deep")
|
testDeepKey, err := ed25519.Create("targets/test/deep", data.ED25519Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,51 +15,52 @@ import (
|
||||||
"github.com/endophage/gotuf/data"
|
"github.com/endophage/gotuf/data"
|
||||||
)
|
)
|
||||||
|
|
||||||
type genericCryptoService struct {
|
// CryptoService implements Sign and Create, holding a specific GUN and keystore to
|
||||||
|
// operate on
|
||||||
|
type CryptoService struct {
|
||||||
gun string
|
gun string
|
||||||
passphrase string
|
passphrase string
|
||||||
keyStore *trustmanager.KeyFileStore
|
keyStore *trustmanager.KeyFileStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// RSACryptoService implements Sign and Create, holding a specific GUN and keystore to
|
// NewCryptoService returns an instance of CryptoService
|
||||||
// operate on
|
func NewCryptoService(gun string, keyStore *trustmanager.KeyFileStore, passphrase string) *CryptoService {
|
||||||
type RSACryptoService struct {
|
return &CryptoService{gun: gun, keyStore: keyStore, passphrase: passphrase}
|
||||||
genericCryptoService
|
|
||||||
}
|
|
||||||
|
|
||||||
// ECDSACryptoService implements Sign and Create, holding a specific GUN and keystore to
|
|
||||||
// operate on
|
|
||||||
type ECDSACryptoService struct {
|
|
||||||
genericCryptoService
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRSACryptoService returns an instance of CryptoService
|
|
||||||
func NewRSACryptoService(gun string, keyStore *trustmanager.KeyFileStore, passphrase string) *RSACryptoService {
|
|
||||||
return &RSACryptoService{genericCryptoService{gun: gun, keyStore: keyStore, passphrase: passphrase}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create is used to generate keys for targets, snapshots and timestamps
|
// Create is used to generate keys for targets, snapshots and timestamps
|
||||||
func (ccs *RSACryptoService) Create(role string) (*data.PublicKey, error) {
|
func (ccs *CryptoService) Create(role string, algorithm data.KeyAlgorithm) (*data.PublicKey, error) {
|
||||||
privKey, err := trustmanager.GenerateRSAKey(rand.Reader, rsaKeySize)
|
var privKey *data.PrivateKey
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch algorithm {
|
||||||
|
case data.RSAKey:
|
||||||
|
privKey, err = trustmanager.GenerateRSAKey(rand.Reader, rsaKeySize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to generate RSA key: %v", err)
|
return nil, fmt.Errorf("failed to generate RSA key: %v", err)
|
||||||
}
|
}
|
||||||
|
case data.ECDSAKey:
|
||||||
|
privKey, err = trustmanager.GenerateECDSAKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to generate EC key: %v", err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("private key type not supported for key generation: %s", algorithm)
|
||||||
|
}
|
||||||
|
logrus.Debugf("generated new %s key for role: %s and keyID: %s", algorithm, role, privKey.ID())
|
||||||
|
|
||||||
// Store the private key into our keystore with the name being: /GUN/ID.key
|
// Store the private key into our keystore with the name being: /GUN/ID.key
|
||||||
err = ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), privKey)
|
err = ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), privKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to add key to filestore: %v", err)
|
return nil, fmt.Errorf("failed to add key to filestore: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("generated new RSA key for role: %s and keyID: %s", role, privKey.ID())
|
|
||||||
|
|
||||||
return data.PublicKeyFromPrivate(*privKey), nil
|
return data.PublicKeyFromPrivate(*privKey), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign returns the signatures for the payload with a set of keyIDs. It ignores
|
// Sign returns the signatures for the payload with a set of keyIDs. It ignores
|
||||||
// errors to sign and expects the called to validate if the number of returned
|
// errors to sign and expects the called to validate if the number of returned
|
||||||
// signatures is adequate.
|
// signatures is adequate.
|
||||||
func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) {
|
func (ccs *CryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) {
|
||||||
// Create hasher and hash data
|
// Create hasher and hash data
|
||||||
hash := crypto.SHA256
|
hash := crypto.SHA256
|
||||||
hashed := sha256.Sum256(payload)
|
hashed := sha256.Sum256(payload)
|
||||||
|
|
@ -72,11 +73,8 @@ func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signa
|
||||||
var privKey *data.PrivateKey
|
var privKey *data.PrivateKey
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Read PrivateKey from file.
|
// Read PrivateKey from file and decrypt it if there is a passphrase.
|
||||||
// TODO(diogo): This assumes both that only root keys are encrypted and
|
|
||||||
// that encrypted keys are always X509 encoded
|
|
||||||
if ccs.passphrase != "" {
|
if ccs.passphrase != "" {
|
||||||
// This is a root key
|
|
||||||
privKey, err = ccs.keyStore.GetDecryptedKey(keyName, ccs.passphrase)
|
privKey, err = ccs.keyStore.GetDecryptedKey(keyName, ccs.passphrase)
|
||||||
} else {
|
} else {
|
||||||
privKey, err = ccs.keyStore.GetKey(keyName)
|
privKey, err = ccs.keyStore.GetKey(keyName)
|
||||||
|
|
@ -86,27 +84,33 @@ func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signa
|
||||||
// InitRepo gets a signer that doesn't have access to
|
// InitRepo gets a signer that doesn't have access to
|
||||||
// the root keys. Continuing here is safe because we
|
// the root keys. Continuing here is safe because we
|
||||||
// end up not returning any signatures.
|
// end up not returning any signatures.
|
||||||
logrus.Debugf("ignoring error attempting to retrieve RSA key ID: %s, %v", keyid, err)
|
logrus.Debugf("ignoring error attempting to retrieve key ID: %s, %v", keyid, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
sig, err := rsaSign(privKey, hash, hashed[:])
|
algorithm := privKey.Algorithm()
|
||||||
|
var sigAlgorithm data.SigAlgorithm
|
||||||
|
var sig []byte
|
||||||
|
|
||||||
|
switch algorithm {
|
||||||
|
case data.RSAKey:
|
||||||
|
sig, err = rsaSign(privKey, hash, hashed[:])
|
||||||
|
sigAlgorithm = data.RSAPSSSignature
|
||||||
|
case data.ECDSAKey:
|
||||||
|
sig, err = ecdsaSign(privKey, hashed[:])
|
||||||
|
sigAlgorithm = data.ECDSASignature
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If the rsaSign method got called with a non RSA private key,
|
logrus.Debugf("ignoring error attempting to %s sign with keyID: %s, %v", algorithm, keyid, err)
|
||||||
// we ignore this call.
|
|
||||||
// This might happen when root is ECDSA, targets and snapshots RSA, and
|
|
||||||
// gotuf still attempts to sign root with this cryptoserver
|
|
||||||
// return nil, err
|
|
||||||
logrus.Debugf("ignoring error attempting to RSA sign with keyID: %s, %v", keyid, err)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("appending RSA signature with Key ID: %s", privKey.ID())
|
logrus.Debugf("appending %s signature with Key ID: %s", algorithm, keyid)
|
||||||
|
|
||||||
// Append signatures to result array
|
// Append signatures to result array
|
||||||
signatures = append(signatures, data.Signature{
|
signatures = append(signatures, data.Signature{
|
||||||
KeyID: keyid,
|
KeyID: keyid,
|
||||||
Method: data.RSAPSSSignature,
|
Method: sigAlgorithm,
|
||||||
Signature: sig[:],
|
Signature: sig[:],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -115,8 +119,8 @@ func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signa
|
||||||
}
|
}
|
||||||
|
|
||||||
func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
|
func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
|
||||||
if privKey.Cipher() != data.RSAKey {
|
if privKey.Algorithm() != data.RSAKey {
|
||||||
return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher())
|
return nil, fmt.Errorf("private key type not supported: %s", privKey.Algorithm())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an rsa.PrivateKey out of the private key bytes
|
// Create an rsa.PrivateKey out of the private key bytes
|
||||||
|
|
@ -134,94 +138,9 @@ func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte,
|
||||||
return sig, nil
|
return sig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewECDSACryptoService returns an instance of CryptoService
|
|
||||||
func NewECDSACryptoService(gun string, keyStore *trustmanager.KeyFileStore, passphrase string) *ECDSACryptoService {
|
|
||||||
return &ECDSACryptoService{genericCryptoService{gun: gun, keyStore: keyStore, passphrase: passphrase}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create is used to generate keys for targets, snapshots and timestamps
|
|
||||||
func (ccs *ECDSACryptoService) Create(role string) (*data.PublicKey, error) {
|
|
||||||
privKey, err := trustmanager.GenerateECDSAKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to generate EC key: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the private key into our keystore with the name being: /GUN/ID.key
|
|
||||||
err = ccs.keyStore.AddKey(filepath.Join(ccs.gun, privKey.ID()), privKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to add key to filestore: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Debugf("generated new ECDSA key for role %s with keyID: %s", role, privKey.ID())
|
|
||||||
|
|
||||||
return data.PublicKeyFromPrivate(*privKey), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign returns the signatures for the payload with a set of keyIDs. It ignores
|
|
||||||
// errors to sign and expects the called to validate if the number of returned
|
|
||||||
// signatures is adequate.
|
|
||||||
func (ccs *ECDSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signature, error) {
|
|
||||||
// Create hasher and hash data
|
|
||||||
hashed := sha256.Sum256(payload)
|
|
||||||
|
|
||||||
signatures := make([]data.Signature, 0, len(keyIDs))
|
|
||||||
for _, keyid := range keyIDs {
|
|
||||||
// ccs.gun will be empty if this is the root key
|
|
||||||
keyName := filepath.Join(ccs.gun, keyid)
|
|
||||||
|
|
||||||
var privKey *data.PrivateKey
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// Read PrivateKey from file
|
|
||||||
// TODO(diogo): This assumes both that only root keys are encrypted and
|
|
||||||
// that encrypted keys are always X509 encoded
|
|
||||||
if ccs.passphrase != "" {
|
|
||||||
// This is a root key
|
|
||||||
privKey, err = ccs.keyStore.GetDecryptedKey(keyName, ccs.passphrase)
|
|
||||||
} else {
|
|
||||||
privKey, err = ccs.keyStore.GetKey(keyName)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// Note that GetDecryptedKey always fails on InitRepo.
|
|
||||||
// InitRepo gets a signer that doesn't have access to
|
|
||||||
// the root keys. Continuing here is safe because we
|
|
||||||
// end up not returning any signatures.
|
|
||||||
// TODO(diogo): figure out if there are any specific error types to
|
|
||||||
// check. We're swallowing all errors.
|
|
||||||
logrus.Debugf("Ignoring error attempting to retrieve ECDSA key ID: %s, %v", keyid, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("ERROR: ", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := ecdsaSign(privKey, hashed[:])
|
|
||||||
if err != nil {
|
|
||||||
// If the ecdsaSign method got called with a non ECDSA private key,
|
|
||||||
// we ignore this call.
|
|
||||||
// This might happen when root is RSA, targets and snapshots ECDSA, and
|
|
||||||
// gotuf still attempts to sign root with this cryptoserver
|
|
||||||
// return nil, err
|
|
||||||
logrus.Debugf("ignoring error attempting to ECDSA sign with keyID: %s, %v", privKey.ID(), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Debugf("appending ECDSA signature with Key ID: %s", privKey.ID())
|
|
||||||
|
|
||||||
// Append signatures to result array
|
|
||||||
signatures = append(signatures, data.Signature{
|
|
||||||
KeyID: keyid,
|
|
||||||
Method: data.ECDSASignature,
|
|
||||||
Signature: sig[:],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return signatures, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ecdsaSign(privKey *data.PrivateKey, hashed []byte) ([]byte, error) {
|
func ecdsaSign(privKey *data.PrivateKey, hashed []byte) ([]byte, error) {
|
||||||
if privKey.Cipher() != data.ECDSAKey {
|
if privKey.Algorithm() != data.ECDSAKey {
|
||||||
return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher())
|
return nil, fmt.Errorf("private key type not supported: %s", privKey.Algorithm())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an ecdsa.PrivateKey out of the private key bytes
|
// Create an ecdsa.PrivateKey out of the private key bytes
|
||||||
|
|
|
||||||
114
client/client.go
114
client/client.go
|
|
@ -55,11 +55,12 @@ const (
|
||||||
/// that doesn't exist.
|
/// that doesn't exist.
|
||||||
var ErrRepositoryNotExist = errors.New("repository does not exist")
|
var ErrRepositoryNotExist = errors.New("repository does not exist")
|
||||||
|
|
||||||
// UnlockedSigner encapsulates a private key and a signer that uses that private key,
|
// UnlockedCryptoService encapsulates a private key and a cryptoservice that
|
||||||
// providing convinience methods for generation of certificates.
|
// uses that private key, providing convinience methods for generation of
|
||||||
type UnlockedSigner struct {
|
// certificates.
|
||||||
|
type UnlockedCryptoService struct {
|
||||||
privKey *data.PrivateKey
|
privKey *data.PrivateKey
|
||||||
signer *signed.Signer
|
cryptoService signed.CryptoService
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotaryRepository stores all the information needed to operate on a notary
|
// NotaryRepository stores all the information needed to operate on a notary
|
||||||
|
|
@ -72,11 +73,10 @@ type NotaryRepository struct {
|
||||||
caStore trustmanager.X509Store
|
caStore trustmanager.X509Store
|
||||||
certificateStore trustmanager.X509Store
|
certificateStore trustmanager.X509Store
|
||||||
fileStore store.MetadataStore
|
fileStore store.MetadataStore
|
||||||
signer *signed.Signer
|
cryptoService signed.CryptoService
|
||||||
tufRepo *tuf.TufRepo
|
tufRepo *tuf.TufRepo
|
||||||
privKeyStore *trustmanager.KeyFileStore
|
privKeyStore *trustmanager.KeyFileStore
|
||||||
rootKeyStore *trustmanager.KeyFileStore
|
rootKeyStore *trustmanager.KeyFileStore
|
||||||
rootSigner *UnlockedSigner
|
|
||||||
roundTrip http.RoundTripper
|
roundTrip http.RoundTripper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,15 +115,14 @@ func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper) (*N
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(diogo): This hardcodes snapshots and targets to using EC. Change it.
|
cryptoService := NewCryptoService(gun, privKeyStore, "")
|
||||||
signer := signed.NewSigner(NewECDSACryptoService(gun, privKeyStore, ""))
|
|
||||||
|
|
||||||
nRepo := &NotaryRepository{
|
nRepo := &NotaryRepository{
|
||||||
gun: gun,
|
gun: gun,
|
||||||
baseDir: baseDir,
|
baseDir: baseDir,
|
||||||
baseURL: baseURL,
|
baseURL: baseURL,
|
||||||
tufRepoPath: filepath.Join(baseDir, tufDir, gun),
|
tufRepoPath: filepath.Join(baseDir, tufDir, gun),
|
||||||
signer: signer,
|
cryptoService: cryptoService,
|
||||||
privKeyStore: privKeyStore,
|
privKeyStore: privKeyStore,
|
||||||
roundTrip: rt,
|
roundTrip: rt,
|
||||||
}
|
}
|
||||||
|
|
@ -137,8 +136,8 @@ func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper) (*N
|
||||||
|
|
||||||
// Initialize creates a new repository by using rootKey as the root Key for the
|
// Initialize creates a new repository by using rootKey as the root Key for the
|
||||||
// TUF repository.
|
// TUF repository.
|
||||||
func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
|
func (r *NotaryRepository) Initialize(uCryptoService *UnlockedCryptoService) error {
|
||||||
rootCert, err := uSigner.GenerateCertificate(r.gun)
|
rootCert, err := uCryptoService.GenerateCertificate(r.gun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -149,25 +148,25 @@ func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
|
||||||
// If the key is RSA, we store it as type RSAx509, if it is ECDSA we store it
|
// If the key is RSA, we store it as type RSAx509, if it is ECDSA we store it
|
||||||
// as ECDSAx509 to allow the gotuf verifiers to correctly decode the
|
// as ECDSAx509 to allow the gotuf verifiers to correctly decode the
|
||||||
// key on verification of signatures.
|
// key on verification of signatures.
|
||||||
var cipherType string
|
var algorithmType data.KeyAlgorithm
|
||||||
cipher := uSigner.privKey.Cipher()
|
algorithm := uCryptoService.privKey.Algorithm()
|
||||||
switch cipher {
|
switch algorithm {
|
||||||
case data.RSAKey:
|
case data.RSAKey:
|
||||||
cipherType = data.RSAx509Key
|
algorithmType = data.RSAx509Key
|
||||||
case data.ECDSAKey:
|
case data.ECDSAKey:
|
||||||
cipherType = data.ECDSAx509Key
|
algorithmType = data.ECDSAx509Key
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid format for root key: %s", cipher)
|
return fmt.Errorf("invalid format for root key: %s", algorithm)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a x509Key using the rootCert as the public key
|
// Generate a x509Key using the rootCert as the public key
|
||||||
rootKey := data.NewPublicKey(cipherType, trustmanager.CertToPEM(rootCert))
|
rootKey := data.NewPublicKey(algorithmType, trustmanager.CertToPEM(rootCert))
|
||||||
|
|
||||||
// Creates a symlink between the certificate ID and the real public key it
|
// Creates a symlink between the certificate ID and the real public key it
|
||||||
// is associated with. This is used to be able to retrieve the root private key
|
// is associated with. This is used to be able to retrieve the root private key
|
||||||
// associated with a particular certificate
|
// associated with a particular certificate
|
||||||
logrus.Debugf("Linking %s to %s.", rootKey.ID(), uSigner.ID())
|
logrus.Debugf("Linking %s to %s.", rootKey.ID(), uCryptoService.ID())
|
||||||
err = r.rootKeyStore.Link(uSigner.ID(), rootKey.ID())
|
err = r.rootKeyStore.Link(uCryptoService.ID(), rootKey.ID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -186,15 +185,16 @@ func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn the JSON timestamp key from the remote server into a TUFKey
|
// Turn the JSON timestamp key from the remote server into a TUFKey
|
||||||
timestampKey := data.NewPublicKey(parsedKey.Cipher(), parsedKey.Public())
|
timestampKey := data.NewPublicKey(parsedKey.Algorithm(), parsedKey.Public())
|
||||||
logrus.Debugf("got remote %s timestamp key with keyID: %s", parsedKey.Cipher(), timestampKey.ID())
|
logrus.Debugf("got remote %s timestamp key with keyID: %s", parsedKey.Algorithm(), timestampKey.ID())
|
||||||
|
|
||||||
|
// This is currently hardcoding the targets and snapshots keys to ECDSA
|
||||||
// Targets and snapshot keys are always generated locally.
|
// Targets and snapshot keys are always generated locally.
|
||||||
targetsKey, err := r.signer.Create("targets")
|
targetsKey, err := r.cryptoService.Create("targets", data.ECDSAKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
snapshotKey, err := r.signer.Create("snapshot")
|
snapshotKey, err := r.cryptoService.Create("snapshot", data.ECDSAKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +236,7 @@ func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
r.tufRepo = tuf.NewTufRepo(kdb, r.signer)
|
r.tufRepo = tuf.NewTufRepo(kdb, r.cryptoService)
|
||||||
|
|
||||||
r.fileStore, err = store.NewFilesystemStore(
|
r.fileStore, err = store.NewFilesystemStore(
|
||||||
r.tufRepoPath,
|
r.tufRepoPath,
|
||||||
|
|
@ -252,7 +252,7 @@ func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.saveMetadata(uSigner.signer); err != nil {
|
if err := r.saveMetadata(uCryptoService.cryptoService); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -389,11 +389,11 @@ func (r *NotaryRepository) Publish(getPass passwordRetriever) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rootKeyID := r.tufRepo.Root.Signed.Roles["root"].KeyIDs[0]
|
rootKeyID := r.tufRepo.Root.Signed.Roles["root"].KeyIDs[0]
|
||||||
rootSigner, err := r.GetRootSigner(rootKeyID, passphrase)
|
rootCryptoService, err := r.GetRootCryptoService(rootKeyID, passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
root, err = r.tufRepo.SignRoot(data.DefaultExpires("root"), rootSigner.signer)
|
root, err = r.tufRepo.SignRoot(data.DefaultExpires("root"), rootCryptoService.cryptoService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -459,7 +459,7 @@ func (r *NotaryRepository) bootstrapRepo() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
kdb := keys.NewDB()
|
kdb := keys.NewDB()
|
||||||
tufRepo := tuf.NewTufRepo(kdb, r.signer)
|
tufRepo := tuf.NewTufRepo(kdb, r.cryptoService)
|
||||||
|
|
||||||
logrus.Debugf("Loading trusted collection.")
|
logrus.Debugf("Loading trusted collection.")
|
||||||
rootJSON, err := fileStore.GetMeta("root", 0)
|
rootJSON, err := fileStore.GetMeta("root", 0)
|
||||||
|
|
@ -499,8 +499,8 @@ func (r *NotaryRepository) bootstrapRepo() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *NotaryRepository) saveMetadata(rootSigner *signed.Signer) error {
|
func (r *NotaryRepository) saveMetadata(rootCryptoService signed.CryptoService) error {
|
||||||
signedRoot, err := r.tufRepo.SignRoot(data.DefaultExpires("root"), rootSigner)
|
signedRoot, err := r.tufRepo.SignRoot(data.DefaultExpires("root"), rootCryptoService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -632,7 +632,7 @@ func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
kdb := keys.NewDB()
|
kdb := keys.NewDB()
|
||||||
r.tufRepo = tuf.NewTufRepo(kdb, r.signer)
|
r.tufRepo = tuf.NewTufRepo(kdb, r.cryptoService)
|
||||||
|
|
||||||
err = r.tufRepo.SetRoot(root)
|
err = r.tufRepo.SetRoot(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -653,11 +653,15 @@ func (r *NotaryRepository) ListRootKeys() []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenRootKey generates a new root key protected by a given passphrase
|
// GenRootKey generates a new root key protected by a given passphrase
|
||||||
|
// TODO(diogo): show not create keys manually, should use a cryptoservice instead
|
||||||
func (r *NotaryRepository) GenRootKey(algorithm, passphrase string) (string, error) {
|
func (r *NotaryRepository) GenRootKey(algorithm, passphrase string) (string, error) {
|
||||||
var err error
|
var err error
|
||||||
var privKey *data.PrivateKey
|
var privKey *data.PrivateKey
|
||||||
|
|
||||||
switch strings.ToLower(algorithm) {
|
// We don't want external API callers to rely on internal TUF data types, so
|
||||||
|
// the API here should continue to receive a string algorithm, and ensure
|
||||||
|
// that it is downcased
|
||||||
|
switch data.KeyAlgorithm(strings.ToLower(algorithm)) {
|
||||||
case data.RSAKey:
|
case data.RSAKey:
|
||||||
privKey, err = trustmanager.GenerateRSAKey(rand.Reader, rsaRootKeySize)
|
privKey, err = trustmanager.GenerateRSAKey(rand.Reader, rsaRootKeySize)
|
||||||
case data.ECDSAKey:
|
case data.ECDSAKey:
|
||||||
|
|
@ -676,28 +680,18 @@ func (r *NotaryRepository) GenRootKey(algorithm, passphrase string) (string, err
|
||||||
return privKey.ID(), nil
|
return privKey.ID(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRootSigner retreives a root key that includes the ID and a signer
|
// GetRootCryptoService retreives a root key and a cryptoservice to use with it
|
||||||
func (r *NotaryRepository) GetRootSigner(rootKeyID, passphrase string) (*UnlockedSigner, error) {
|
func (r *NotaryRepository) GetRootCryptoService(rootKeyID, passphrase string) (*UnlockedCryptoService, error) {
|
||||||
privKey, err := r.rootKeyStore.GetDecryptedKey(rootKeyID, passphrase)
|
privKey, err := r.rootKeyStore.GetDecryptedKey(rootKeyID, passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not get decrypted root key with keyID: %s, %v", rootKeyID, err)
|
return nil, fmt.Errorf("could not get decrypted root key with keyID: %s, %v", rootKeyID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var signer *signed.Signer
|
cryptoService := NewCryptoService("", r.rootKeyStore, passphrase)
|
||||||
cipher := privKey.Cipher()
|
|
||||||
// Passing an empty GUN because root keys aren't associated with a GUN.
|
|
||||||
switch strings.ToLower(cipher) {
|
|
||||||
case data.RSAKey:
|
|
||||||
signer = signed.NewSigner(NewRSACryptoService("", r.rootKeyStore, passphrase))
|
|
||||||
case data.ECDSAKey:
|
|
||||||
signer = signed.NewSigner(NewECDSACryptoService("", r.rootKeyStore, passphrase))
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &UnlockedSigner{
|
return &UnlockedCryptoService{
|
||||||
privKey: privKey,
|
privKey: privKey,
|
||||||
signer: signer}, nil
|
cryptoService: cryptoService}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *NotaryRepository) loadKeys(trustDir, rootKeysDir string) error {
|
func (r *NotaryRepository) loadKeys(trustDir, rootKeysDir string) error {
|
||||||
|
|
@ -738,35 +732,35 @@ func (r *NotaryRepository) loadKeys(trustDir, rootKeysDir string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID gets a consistent ID based on the PrivateKey bytes and cipher type
|
// ID gets a consistent ID based on the PrivateKey bytes and algorithm type
|
||||||
func (uk *UnlockedSigner) ID() string {
|
func (ucs *UnlockedCryptoService) ID() string {
|
||||||
return uk.PublicKey().ID()
|
return ucs.PublicKey().ID()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PublicKey Returns the public key associated with the Private Key within the Signer
|
// PublicKey Returns the public key associated with the private key
|
||||||
func (uk *UnlockedSigner) PublicKey() *data.PublicKey {
|
func (ucs *UnlockedCryptoService) PublicKey() *data.PublicKey {
|
||||||
return data.PublicKeyFromPrivate(*uk.privKey)
|
return data.PublicKeyFromPrivate(*ucs.privKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateCertificate generates an X509 Certificate from a template, given a GUN
|
// GenerateCertificate generates an X509 Certificate from a template, given a GUN
|
||||||
func (uk *UnlockedSigner) GenerateCertificate(gun string) (*x509.Certificate, error) {
|
func (ucs *UnlockedCryptoService) GenerateCertificate(gun string) (*x509.Certificate, error) {
|
||||||
cipher := uk.privKey.Cipher()
|
algorithm := ucs.privKey.Algorithm()
|
||||||
var publicKey crypto.PublicKey
|
var publicKey crypto.PublicKey
|
||||||
var privateKey crypto.PrivateKey
|
var privateKey crypto.PrivateKey
|
||||||
var err error
|
var err error
|
||||||
switch cipher {
|
switch algorithm {
|
||||||
case data.RSAKey:
|
case data.RSAKey:
|
||||||
var rsaPrivateKey *rsa.PrivateKey
|
var rsaPrivateKey *rsa.PrivateKey
|
||||||
rsaPrivateKey, err = x509.ParsePKCS1PrivateKey(uk.privKey.Private())
|
rsaPrivateKey, err = x509.ParsePKCS1PrivateKey(ucs.privKey.Private())
|
||||||
privateKey = rsaPrivateKey
|
privateKey = rsaPrivateKey
|
||||||
publicKey = rsaPrivateKey.Public()
|
publicKey = rsaPrivateKey.Public()
|
||||||
case data.ECDSAKey:
|
case data.ECDSAKey:
|
||||||
var ecdsaPrivateKey *ecdsa.PrivateKey
|
var ecdsaPrivateKey *ecdsa.PrivateKey
|
||||||
ecdsaPrivateKey, err = x509.ParseECPrivateKey(uk.privKey.Private())
|
ecdsaPrivateKey, err = x509.ParseECPrivateKey(ucs.privKey.Private())
|
||||||
privateKey = ecdsaPrivateKey
|
privateKey = ecdsaPrivateKey
|
||||||
publicKey = ecdsaPrivateKey.Public()
|
publicKey = ecdsaPrivateKey.Public()
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher)
|
return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", algorithm)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse root key: %s (%v)", gun, err)
|
return nil, fmt.Errorf("failed to parse root key: %s (%v)", gun, err)
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,13 @@ func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) {
|
||||||
// sure the repository looks correct on disk.
|
// sure the repository looks correct on disk.
|
||||||
// We test this with both an RSA and ECDSA root key
|
// We test this with both an RSA and ECDSA root key
|
||||||
func TestInitRepo(t *testing.T) {
|
func TestInitRepo(t *testing.T) {
|
||||||
testInitRepo(t, data.RSAKey)
|
|
||||||
testInitRepo(t, data.ECDSAKey)
|
testInitRepo(t, data.ECDSAKey)
|
||||||
|
if !testing.Short() {
|
||||||
|
testInitRepo(t, data.RSAKey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInitRepo(t *testing.T, rootType string) {
|
func testInitRepo(t *testing.T, rootType data.KeyAlgorithm) {
|
||||||
gun := "docker.com/notary"
|
gun := "docker.com/notary"
|
||||||
// Temporary directory where test files will be created
|
// Temporary directory where test files will be created
|
||||||
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||||
|
|
@ -61,13 +63,13 @@ func testInitRepo(t *testing.T, rootType string) {
|
||||||
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
|
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
|
||||||
assert.NoError(t, err, "error creating repo: %s", err)
|
assert.NoError(t, err, "error creating repo: %s", err)
|
||||||
|
|
||||||
rootKeyID, err := repo.GenRootKey(rootType, "passphrase")
|
rootKeyID, err := repo.GenRootKey(rootType.String(), "passphrase")
|
||||||
assert.NoError(t, err, "error generating root key: %s", err)
|
assert.NoError(t, err, "error generating root key: %s", err)
|
||||||
|
|
||||||
rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase")
|
rootCryptoService, err := repo.GetRootCryptoService(rootKeyID, "passphrase")
|
||||||
assert.NoError(t, err, "error retrieving root key: %s", err)
|
assert.NoError(t, err, "error retrieving root key: %s", err)
|
||||||
|
|
||||||
err = repo.Initialize(rootSigner)
|
err = repo.Initialize(rootCryptoService)
|
||||||
assert.NoError(t, err, "error creating repository: %s", err)
|
assert.NoError(t, err, "error creating repository: %s", err)
|
||||||
|
|
||||||
// Inspect contents of the temporary directory
|
// Inspect contents of the temporary directory
|
||||||
|
|
@ -98,7 +100,7 @@ func testInitRepo(t *testing.T, rootType string) {
|
||||||
// Look for keys in root_keys
|
// Look for keys in root_keys
|
||||||
// There should be a file named after the key ID of the root key we
|
// There should be a file named after the key ID of the root key we
|
||||||
// passed in.
|
// passed in.
|
||||||
rootKeyFilename := rootSigner.ID() + ".key"
|
rootKeyFilename := rootCryptoService.ID() + ".key"
|
||||||
_, err = os.Stat(filepath.Join(tempBaseDir, "private", "root_keys", rootKeyFilename))
|
_, err = os.Stat(filepath.Join(tempBaseDir, "private", "root_keys", rootKeyFilename))
|
||||||
assert.NoError(t, err, "missing root key")
|
assert.NoError(t, err, "missing root key")
|
||||||
|
|
||||||
|
|
@ -184,11 +186,13 @@ type tufChange struct {
|
||||||
// internal HTTP server.
|
// internal HTTP server.
|
||||||
// We test this with both an RSA and ECDSA root key
|
// We test this with both an RSA and ECDSA root key
|
||||||
func TestAddListTarget(t *testing.T) {
|
func TestAddListTarget(t *testing.T) {
|
||||||
testAddListTarget(t, data.RSAKey)
|
|
||||||
testAddListTarget(t, data.ECDSAKey)
|
testAddListTarget(t, data.ECDSAKey)
|
||||||
|
if !testing.Short() {
|
||||||
|
testInitRepo(t, data.RSAKey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAddListTarget(t *testing.T, rootType string) {
|
func testAddListTarget(t *testing.T, rootType data.KeyAlgorithm) {
|
||||||
// Temporary directory where test files will be created
|
// Temporary directory where test files will be created
|
||||||
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||||
defer os.RemoveAll(tempBaseDir)
|
defer os.RemoveAll(tempBaseDir)
|
||||||
|
|
@ -203,13 +207,13 @@ func testAddListTarget(t *testing.T, rootType string) {
|
||||||
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
|
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
|
||||||
assert.NoError(t, err, "error creating repository: %s", err)
|
assert.NoError(t, err, "error creating repository: %s", err)
|
||||||
|
|
||||||
rootKeyID, err := repo.GenRootKey(rootType, "passphrase")
|
rootKeyID, err := repo.GenRootKey(rootType.String(), "passphrase")
|
||||||
assert.NoError(t, err, "error generating root key: %s", err)
|
assert.NoError(t, err, "error generating root key: %s", err)
|
||||||
|
|
||||||
rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase")
|
rootCryptoService, err := repo.GetRootCryptoService(rootKeyID, "passphrase")
|
||||||
assert.NoError(t, err, "error retreiving root key: %s", err)
|
assert.NoError(t, err, "error retreiving root key: %s", err)
|
||||||
|
|
||||||
err = repo.Initialize(rootSigner)
|
err = repo.Initialize(rootCryptoService)
|
||||||
assert.NoError(t, err, "error creating repository: %s", err)
|
assert.NoError(t, err, "error creating repository: %s", err)
|
||||||
|
|
||||||
// Add fixtures/ca.cert as a target. There's no particular reason
|
// Add fixtures/ca.cert as a target. There's no particular reason
|
||||||
|
|
@ -367,11 +371,13 @@ func testAddListTarget(t *testing.T, rootType string) {
|
||||||
// TestValidateRootKey verifies that the public data in root.json for the root
|
// TestValidateRootKey verifies that the public data in root.json for the root
|
||||||
// key is a valid x509 certificate.
|
// key is a valid x509 certificate.
|
||||||
func TestValidateRootKey(t *testing.T) {
|
func TestValidateRootKey(t *testing.T) {
|
||||||
testValidateRootKey(t, data.RSAKey)
|
|
||||||
testValidateRootKey(t, data.ECDSAKey)
|
testValidateRootKey(t, data.ECDSAKey)
|
||||||
|
if !testing.Short() {
|
||||||
|
testInitRepo(t, data.RSAKey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testValidateRootKey(t *testing.T, rootType string) {
|
func testValidateRootKey(t *testing.T, rootType data.KeyAlgorithm) {
|
||||||
// Temporary directory where test files will be created
|
// Temporary directory where test files will be created
|
||||||
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||||
defer os.RemoveAll(tempBaseDir)
|
defer os.RemoveAll(tempBaseDir)
|
||||||
|
|
@ -386,13 +392,13 @@ func testValidateRootKey(t *testing.T, rootType string) {
|
||||||
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
|
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
|
||||||
assert.NoError(t, err, "error creating repository: %s", err)
|
assert.NoError(t, err, "error creating repository: %s", err)
|
||||||
|
|
||||||
rootKeyID, err := repo.GenRootKey(rootType, "passphrase")
|
rootKeyID, err := repo.GenRootKey(rootType.String(), "passphrase")
|
||||||
assert.NoError(t, err, "error generating root key: %s", err)
|
assert.NoError(t, err, "error generating root key: %s", err)
|
||||||
|
|
||||||
rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase")
|
rootCryptoService, err := repo.GetRootCryptoService(rootKeyID, "passphrase")
|
||||||
assert.NoError(t, err, "error retreiving root key: %s", err)
|
assert.NoError(t, err, "error retreiving root key: %s", err)
|
||||||
|
|
||||||
err = repo.Initialize(rootSigner)
|
err = repo.Initialize(rootCryptoService)
|
||||||
assert.NoError(t, err, "error creating repository: %s", err)
|
assert.NoError(t, err, "error creating repository: %s", err)
|
||||||
|
|
||||||
rootJSONFile := filepath.Join(tempBaseDir, "tuf", gun, "metadata", "root.json")
|
rootJSONFile := filepath.Join(tempBaseDir, "tuf", gun, "metadata", "root.json")
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/pkg/term"
|
"github.com/docker/docker/pkg/term"
|
||||||
notaryclient "github.com/docker/notary/client"
|
notaryclient "github.com/docker/notary/client"
|
||||||
"github.com/endophage/gotuf/data"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
@ -120,7 +119,7 @@ func tufInit(cmd *cobra.Command, args []string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf(err.Error())
|
fatalf(err.Error())
|
||||||
}
|
}
|
||||||
rootKeyID, err = nRepo.GenRootKey(data.ECDSAKey, passphrase)
|
rootKeyID, err = nRepo.GenRootKey("ECDSA", passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf(err.Error())
|
fatalf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -134,12 +133,12 @@ func tufInit(cmd *cobra.Command, args []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootSigner, err := nRepo.GetRootSigner(rootKeyID, passphrase)
|
rootCryptoService, err := nRepo.GetRootCryptoService(rootKeyID, passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf(err.Error())
|
fatalf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
nRepo.Initialize(rootSigner)
|
nRepo.Initialize(rootCryptoService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf(err.Error())
|
fatalf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -165,20 +165,20 @@ func GetTimestampHandler(ctx context.Context, w http.ResponseWriter, r *http.Req
|
||||||
Err: fmt.Errorf("Version store not configured"),
|
Err: fmt.Errorf("Version store not configured"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sign := ctx.Value("signer")
|
cryptoServiceVal := ctx.Value("cryptoService")
|
||||||
signer, ok := sign.(*signed.Signer)
|
cryptoService, ok := cryptoServiceVal.(signed.CryptoService)
|
||||||
if !ok {
|
if !ok {
|
||||||
return &errors.HTTPError{
|
return &errors.HTTPError{
|
||||||
HTTPStatus: http.StatusInternalServerError,
|
HTTPStatus: http.StatusInternalServerError,
|
||||||
Code: 9999,
|
Code: 9999,
|
||||||
Err: fmt.Errorf("Signer not configured"),
|
Err: fmt.Errorf("CryptoService not configured"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
gun := vars["imageName"]
|
gun := vars["imageName"]
|
||||||
|
|
||||||
out, err := timestamp.GetOrCreateTimestamp(gun, store, signer)
|
out, err := timestamp.GetOrCreateTimestamp(gun, store, cryptoService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(*storage.ErrNoKey); ok {
|
if _, ok := err.(*storage.ErrNoKey); ok {
|
||||||
return &errors.HTTPError{
|
return &errors.HTTPError{
|
||||||
|
|
@ -224,7 +224,7 @@ func GetTimestampKeyHandler(ctx context.Context, w http.ResponseWriter, r *http.
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
gun := vars["imageName"]
|
gun := vars["imageName"]
|
||||||
|
|
||||||
key, err := timestamp.GetOrCreateTimestampKey(gun, store, crypto)
|
key, err := timestamp.GetOrCreateTimestampKey(gun, store, crypto, data.ECDSAKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &errors.HTTPError{
|
return &errors.HTTPError{
|
||||||
HTTPStatus: http.StatusInternalServerError,
|
HTTPStatus: http.StatusInternalServerError,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package storage
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/endophage/gotuf/data"
|
||||||
"github.com/go-sql-driver/mysql"
|
"github.com/go-sql-driver/mysql"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -91,10 +92,11 @@ func (db *MySQLStorage) Delete(gun string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTimestampKey returns the timestamps Public Key data
|
// GetTimestampKey returns the timestamps Public Key data
|
||||||
func (db *MySQLStorage) GetTimestampKey(gun string) (cipher string, public []byte, err error) {
|
func (db *MySQLStorage) GetTimestampKey(gun string) (algorithm data.KeyAlgorithm, public []byte, err error) {
|
||||||
stmt := "SELECT `cipher`, `public` FROM `timestamp_keys` WHERE `gun`=?;"
|
stmt := "SELECT `cipher`, `public` FROM `timestamp_keys` WHERE `gun`=?;"
|
||||||
row := db.QueryRow(stmt, gun)
|
row := db.QueryRow(stmt, gun)
|
||||||
|
|
||||||
|
var cipher string
|
||||||
err = row.Scan(&cipher, &public)
|
err = row.Scan(&cipher, &public)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return "", nil, ErrNoKey{gun: gun}
|
return "", nil, ErrNoKey{gun: gun}
|
||||||
|
|
@ -102,13 +104,13 @@ func (db *MySQLStorage) GetTimestampKey(gun string) (cipher string, public []byt
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return cipher, public, err
|
return data.KeyAlgorithm(cipher), public, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTimestampKey attempts to write a TimeStamp key and returns an error if it already exists
|
// SetTimestampKey attempts to write a TimeStamp key and returns an error if it already exists
|
||||||
func (db *MySQLStorage) SetTimestampKey(gun, cipher string, public []byte) error {
|
func (db *MySQLStorage) SetTimestampKey(gun string, algorithm data.KeyAlgorithm, public []byte) error {
|
||||||
stmt := "INSERT INTO `timestamp_keys` (`gun`, `cipher`, `public`) VALUES (?,?,?);"
|
stmt := "INSERT INTO `timestamp_keys` (`gun`, `cipher`, `public`) VALUES (?,?,?);"
|
||||||
_, err := db.Exec(stmt, gun, cipher, public)
|
_, err := db.Exec(stmt, gun, string(algorithm), public)
|
||||||
if err, ok := err.(*mysql.MySQLError); ok {
|
if err, ok := err.(*mysql.MySQLError); ok {
|
||||||
if err.Number == 1022 { // duplicate key error
|
if err.Number == 1022 { // duplicate key error
|
||||||
return &ErrTimestampKeyExists{gun: gun}
|
return &ErrTimestampKeyExists{gun: gun}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
|
import "github.com/endophage/gotuf/data"
|
||||||
|
|
||||||
// MetaStore holds the methods that are used for a Metadata Store
|
// MetaStore holds the methods that are used for a Metadata Store
|
||||||
type MetaStore interface {
|
type MetaStore interface {
|
||||||
UpdateCurrent(gun, role string, version int, data []byte) error
|
UpdateCurrent(gun, role string, version int, data []byte) error
|
||||||
GetCurrent(gun, tufRole string) (data []byte, err error)
|
GetCurrent(gun, tufRole string) (data []byte, err error)
|
||||||
Delete(gun string) error
|
Delete(gun string) error
|
||||||
GetTimestampKey(gun string) (cipher string, public []byte, err error)
|
GetTimestampKey(gun string) (algorithm data.KeyAlgorithm, public []byte, err error)
|
||||||
SetTimestampKey(gun, cipher string, public []byte) error
|
SetTimestampKey(gun string, algorithm data.KeyAlgorithm, public []byte) error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,12 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/endophage/gotuf/data"
|
||||||
)
|
)
|
||||||
|
|
||||||
type key struct {
|
type key struct {
|
||||||
cipher string
|
algorithm data.KeyAlgorithm
|
||||||
public []byte
|
public []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +75,7 @@ func (st *MemStorage) Delete(gun string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTimestampKey returns the public key material of the timestamp key of a given gun
|
// GetTimestampKey returns the public key material of the timestamp key of a given gun
|
||||||
func (st *MemStorage) GetTimestampKey(gun string) (cipher string, public []byte, err error) {
|
func (st *MemStorage) GetTimestampKey(gun string) (algorithm data.KeyAlgorithm, public []byte, err error) {
|
||||||
// no need for lock. It's ok to return nil if an update
|
// no need for lock. It's ok to return nil if an update
|
||||||
// wasn't observed
|
// wasn't observed
|
||||||
k, ok := st.tsKeys[gun]
|
k, ok := st.tsKeys[gun]
|
||||||
|
|
@ -81,12 +83,12 @@ func (st *MemStorage) GetTimestampKey(gun string) (cipher string, public []byte,
|
||||||
return "", nil, &ErrNoKey{gun: gun}
|
return "", nil, &ErrNoKey{gun: gun}
|
||||||
}
|
}
|
||||||
|
|
||||||
return k.cipher, k.public, nil
|
return k.algorithm, k.public, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTimestampKey sets a Timestamp key under a gun
|
// SetTimestampKey sets a Timestamp key under a gun
|
||||||
func (st *MemStorage) SetTimestampKey(gun, cipher string, public []byte) error {
|
func (st *MemStorage) SetTimestampKey(gun string, algorithm data.KeyAlgorithm, public []byte) error {
|
||||||
k := &key{cipher: cipher, public: public}
|
k := &key{algorithm: algorithm, public: public}
|
||||||
st.lock.Lock()
|
st.lock.Lock()
|
||||||
defer st.lock.Unlock()
|
defer st.lock.Unlock()
|
||||||
if _, ok := st.tsKeys[gun]; ok {
|
if _, ok := st.tsKeys[gun]; ok {
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ func TestGetTimestampKey(t *testing.T) {
|
||||||
|
|
||||||
c, k, err := s.GetTimestampKey("gun")
|
c, k, err := s.GetTimestampKey("gun")
|
||||||
assert.Nil(t, err, "Expected error to be nil")
|
assert.Nil(t, err, "Expected error to be nil")
|
||||||
assert.Equal(t, data.RSAKey, c, "Expected cipher rsa, received %s", c)
|
assert.Equal(t, data.RSAKey, c, "Expected algorithm rsa, received %s", c)
|
||||||
assert.Equal(t, []byte("test"), k, "Key data was wrong")
|
assert.Equal(t, []byte("test"), k, "Key data was wrong")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ func TestSetTimestampKey(t *testing.T) {
|
||||||
assert.IsType(t, &ErrTimestampKeyExists{}, err, "Expected err to be ErrTimestampKeyExists")
|
assert.IsType(t, &ErrTimestampKeyExists{}, err, "Expected err to be ErrTimestampKeyExists")
|
||||||
|
|
||||||
k := s.tsKeys["gun"]
|
k := s.tsKeys["gun"]
|
||||||
assert.Equal(t, data.RSAKey, k.cipher, "Expected cipher to be rsa, received %s", k.cipher)
|
assert.Equal(t, data.RSAKey, k.algorithm, "Expected algorithm to be rsa, received %s", k.algorithm)
|
||||||
assert.Equal(t, []byte("test"), k.public, "Public key did not match expected")
|
assert.Equal(t, []byte("test"), k.public, "Public key did not match expected")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,28 +17,28 @@ import (
|
||||||
// found. It attempts to handle the race condition that may occur if 2 servers try to
|
// found. It attempts to handle the race condition that may occur if 2 servers try to
|
||||||
// create the key at the same time by simply querying the store a second time if it
|
// create the key at the same time by simply querying the store a second time if it
|
||||||
// receives a conflict when writing.
|
// receives a conflict when writing.
|
||||||
func GetOrCreateTimestampKey(gun string, store storage.MetaStore, crypto signed.CryptoService) (*data.TUFKey, error) {
|
func GetOrCreateTimestampKey(gun string, store storage.MetaStore, crypto signed.CryptoService, fallBackAlgorithm data.KeyAlgorithm) (*data.TUFKey, error) {
|
||||||
cipher, public, err := store.GetTimestampKey(gun)
|
keyAlgorithm, public, err := store.GetTimestampKey(gun)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return data.NewTUFKey(cipher, public, nil), nil
|
return data.NewTUFKey(keyAlgorithm, public, nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := err.(*storage.ErrNoKey); ok {
|
if _, ok := err.(*storage.ErrNoKey); ok {
|
||||||
key, err := crypto.Create("timestamp")
|
key, err := crypto.Create("timestamp", fallBackAlgorithm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = store.SetTimestampKey(gun, key.Cipher(), key.Public())
|
err = store.SetTimestampKey(gun, key.Algorithm(), key.Public())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return &key.TUFKey, nil
|
return &key.TUFKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := err.(*storage.ErrTimestampKeyExists); ok {
|
if _, ok := err.(*storage.ErrTimestampKeyExists); ok {
|
||||||
cipher, public, err = store.GetTimestampKey(gun)
|
keyAlgorithm, public, err = store.GetTimestampKey(gun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return data.NewTUFKey(cipher, public, nil), nil
|
return data.NewTUFKey(keyAlgorithm, public, nil), nil
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -48,7 +48,7 @@ func GetOrCreateTimestampKey(gun string, store storage.MetaStore, crypto signed.
|
||||||
// GetOrCreateTimestamp returns the current timestamp for the gun. This may mean
|
// GetOrCreateTimestamp returns the current timestamp for the gun. This may mean
|
||||||
// a new timestamp is generated either because none exists, or because the current
|
// a new timestamp is generated either because none exists, or because the current
|
||||||
// one has expired. Once generated, the timestamp is saved in the store.
|
// one has expired. Once generated, the timestamp is saved in the store.
|
||||||
func GetOrCreateTimestamp(gun string, store storage.MetaStore, signer *signed.Signer) ([]byte, error) {
|
func GetOrCreateTimestamp(gun string, store storage.MetaStore, cryptoService signed.CryptoService) ([]byte, error) {
|
||||||
d, err := store.GetCurrent(gun, "timestamp")
|
d, err := store.GetCurrent(gun, "timestamp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(*storage.ErrNotFound); !ok {
|
if _, ok := err.(*storage.ErrNotFound); !ok {
|
||||||
|
|
@ -69,7 +69,7 @@ func GetOrCreateTimestamp(gun string, store storage.MetaStore, signer *signed.Si
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sgnd, version, err := createTimestamp(gun, ts, store, signer)
|
sgnd, version, err := createTimestamp(gun, ts, store, cryptoService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Error("Failed to create a new timestamp")
|
logrus.Error("Failed to create a new timestamp")
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -95,14 +95,14 @@ func timestampExpired(ts *data.SignedTimestamp) bool {
|
||||||
// is assumed this is the immediately previous one, and the new one will have a
|
// is assumed this is the immediately previous one, and the new one will have a
|
||||||
// version number one higher than prev. The store is used to lookup the current
|
// version number one higher than prev. The store is used to lookup the current
|
||||||
// snapshot, this function does not save the newly generated timestamp.
|
// snapshot, this function does not save the newly generated timestamp.
|
||||||
func createTimestamp(gun string, prev *data.SignedTimestamp, store storage.MetaStore, signer *signed.Signer) (*data.Signed, int, error) {
|
func createTimestamp(gun string, prev *data.SignedTimestamp, store storage.MetaStore, cryptoService signed.CryptoService) (*data.Signed, int, error) {
|
||||||
cipher, public, err := store.GetTimestampKey(gun)
|
algorithm, public, err := store.GetTimestampKey(gun)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// owner of gun must have generated a timestamp key otherwise
|
// owner of gun must have generated a timestamp key otherwise
|
||||||
// we won't proceed with generating everything.
|
// we won't proceed with generating everything.
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
key := data.NewPublicKey(cipher, public)
|
key := data.NewPublicKey(algorithm, public)
|
||||||
snapshot, err := store.GetCurrent(gun, "snapshot")
|
snapshot, err := store.GetCurrent(gun, "snapshot")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
|
|
@ -128,7 +128,7 @@ func createTimestamp(gun string, prev *data.SignedTimestamp, store storage.MetaS
|
||||||
Signatures: ts.Signatures,
|
Signatures: ts.Signatures,
|
||||||
Signed: sgndTs,
|
Signed: sgndTs,
|
||||||
}
|
}
|
||||||
err = signer.Sign(out, key)
|
err = signed.Sign(cryptoService, out, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,11 +32,11 @@ func TestTimestampExpired(t *testing.T) {
|
||||||
func TestGetTimestampKey(t *testing.T) {
|
func TestGetTimestampKey(t *testing.T) {
|
||||||
store := storage.NewMemStorage()
|
store := storage.NewMemStorage()
|
||||||
crypto := signed.NewEd25519()
|
crypto := signed.NewEd25519()
|
||||||
k, err := GetOrCreateTimestampKey("gun", store, crypto)
|
k, err := GetOrCreateTimestampKey("gun", store, crypto, data.ED25519Key)
|
||||||
assert.Nil(t, err, "Expected nil error")
|
assert.Nil(t, err, "Expected nil error")
|
||||||
assert.NotNil(t, k, "Key should not be nil")
|
assert.NotNil(t, k, "Key should not be nil")
|
||||||
|
|
||||||
k2, err := GetOrCreateTimestampKey("gun", store, crypto)
|
k2, err := GetOrCreateTimestampKey("gun", store, crypto, data.ED25519Key)
|
||||||
|
|
||||||
assert.Nil(t, err, "Expected nil error")
|
assert.Nil(t, err, "Expected nil error")
|
||||||
|
|
||||||
|
|
@ -48,16 +48,15 @@ func TestGetTimestampKey(t *testing.T) {
|
||||||
func TestGetTimestamp(t *testing.T) {
|
func TestGetTimestamp(t *testing.T) {
|
||||||
store := storage.NewMemStorage()
|
store := storage.NewMemStorage()
|
||||||
crypto := signed.NewEd25519()
|
crypto := signed.NewEd25519()
|
||||||
signer := signed.NewSigner(crypto)
|
|
||||||
|
|
||||||
snapshot := &data.SignedSnapshot{}
|
snapshot := &data.SignedSnapshot{}
|
||||||
snapJSON, _ := json.Marshal(snapshot)
|
snapJSON, _ := json.Marshal(snapshot)
|
||||||
|
|
||||||
store.UpdateCurrent("gun", "snapshot", 0, snapJSON)
|
store.UpdateCurrent("gun", "snapshot", 0, snapJSON)
|
||||||
// create a key to be used by GetTimestamp
|
// create a key to be used by GetTimestamp
|
||||||
_, err := GetOrCreateTimestampKey("gun", store, crypto)
|
_, err := GetOrCreateTimestampKey("gun", store, crypto, data.ED25519Key)
|
||||||
assert.Nil(t, err, "GetTimestampKey errored")
|
assert.Nil(t, err, "GetTimestampKey errored")
|
||||||
|
|
||||||
_, err = GetOrCreateTimestamp("gun", store, signer)
|
_, err = GetOrCreateTimestamp("gun", store, crypto)
|
||||||
assert.Nil(t, err, "GetTimestamp errored")
|
assert.Nil(t, err, "GetTimestamp errored")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ func (trust *RufusSigner) Sign(keyIDs []string, toSign []byte) ([]data.Signature
|
||||||
}
|
}
|
||||||
signatures = append(signatures, data.Signature{
|
signatures = append(signatures, data.Signature{
|
||||||
KeyID: sig.KeyID.ID,
|
KeyID: sig.KeyID.ID,
|
||||||
Method: sig.Algorithm.Algorithm,
|
Method: data.SigAlgorithm(sig.Algorithm.Algorithm),
|
||||||
Signature: sig.Content,
|
Signature: sig.Content,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -62,13 +62,14 @@ func (trust *RufusSigner) Sign(keyIDs []string, toSign []byte) ([]data.Signature
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a remote key and returns the PublicKey associated with the remote private key
|
// Create creates a remote key and returns the PublicKey associated with the remote private key
|
||||||
func (trust *RufusSigner) Create(role string) (*data.PublicKey, error) {
|
// TODO(diogo): Ignoring algorithm for now until rufus supports it
|
||||||
|
func (trust *RufusSigner) Create(role string, _ data.KeyAlgorithm) (*data.PublicKey, error) {
|
||||||
publicKey, err := trust.kmClient.CreateKey(context.Background(), &pb.Void{})
|
publicKey, err := trust.kmClient.CreateKey(context.Background(), &pb.Void{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
//TODO(mccauley): Update API to return algorithm and/or take it as a param
|
//TODO(mccauley): Update API to return algorithm and/or take it as a param
|
||||||
public := data.NewPublicKey(publicKey.Algorithm.Algorithm, publicKey.PublicKey)
|
public := data.NewPublicKey(data.KeyAlgorithm(publicKey.Algorithm.Algorithm), publicKey.PublicKey)
|
||||||
return public, nil
|
return public, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,7 +83,7 @@ func (trust *RufusSigner) PublicKeys(keyIDs ...string) (map[string]*data.PublicK
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
publicKeys[public.KeyID.ID] =
|
publicKeys[public.KeyID.ID] =
|
||||||
data.NewPublicKey(public.Algorithm.Algorithm, public.PublicKey)
|
data.NewPublicKey(data.KeyAlgorithm(public.Algorithm.Algorithm), public.PublicKey)
|
||||||
}
|
}
|
||||||
return publicKeys, nil
|
return publicKeys, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ func fingerprintCert(cert *x509.Certificate) (CertID, error) {
|
||||||
block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
|
block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
|
||||||
pemdata := pem.EncodeToMemory(&block)
|
pemdata := pem.EncodeToMemory(&block)
|
||||||
|
|
||||||
keyType := ""
|
var keyType data.KeyAlgorithm
|
||||||
switch cert.PublicKeyAlgorithm {
|
switch cert.PublicKeyAlgorithm {
|
||||||
case x509.RSA:
|
case x509.RSA:
|
||||||
keyType = data.RSAx509Key
|
keyType = data.RSAx509Key
|
||||||
|
|
@ -228,7 +228,7 @@ func GenerateRSAKey(random io.Reader, bits int) (*data.PrivateKey, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RSAToPrivateKey converts an rsa.Private key to a TUF data.PrivateKey type
|
// RSAToPrivateKey converts an rsa.Private key to a TUF data.PrivateKey type
|
||||||
func RSAToPrivateKey(rsaPrivKey *rsa.PrivateKey, keyType string) (*data.PrivateKey, error) {
|
func RSAToPrivateKey(rsaPrivKey *rsa.PrivateKey, keyType data.KeyAlgorithm) (*data.PrivateKey, error) {
|
||||||
// Get a DER-encoded representation of the PublicKey
|
// Get a DER-encoded representation of the PublicKey
|
||||||
rsaPubBytes, err := x509.MarshalPKIXPublicKey(&rsaPrivKey.PublicKey)
|
rsaPubBytes, err := x509.MarshalPKIXPublicKey(&rsaPrivKey.PublicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -261,7 +261,7 @@ func GenerateECDSAKey(random io.Reader) (*data.PrivateKey, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ECDSAToPrivateKey converts an rsa.Private key to a TUF data.PrivateKey type
|
// ECDSAToPrivateKey converts an rsa.Private key to a TUF data.PrivateKey type
|
||||||
func ECDSAToPrivateKey(ecdsaPrivKey *ecdsa.PrivateKey, keyType string) (*data.PrivateKey, error) {
|
func ECDSAToPrivateKey(ecdsaPrivKey *ecdsa.PrivateKey, keyType data.KeyAlgorithm) (*data.PrivateKey, error) {
|
||||||
// Get a DER-encoded representation of the PublicKey
|
// Get a DER-encoded representation of the PublicKey
|
||||||
ecdsaPubBytes, err := x509.MarshalPKIXPublicKey(&ecdsaPrivKey.PublicKey)
|
ecdsaPubBytes, err := x509.MarshalPKIXPublicKey(&ecdsaPrivKey.PublicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -280,15 +280,15 @@ func ECDSAToPrivateKey(ecdsaPrivKey *ecdsa.PrivateKey, keyType string) (*data.Pr
|
||||||
// KeyToPEM returns a PEM encoded key from a Private Key
|
// KeyToPEM returns a PEM encoded key from a Private Key
|
||||||
func KeyToPEM(privKey *data.PrivateKey) ([]byte, error) {
|
func KeyToPEM(privKey *data.PrivateKey) ([]byte, error) {
|
||||||
var pemType string
|
var pemType string
|
||||||
cipher := privKey.Cipher()
|
algorithm := privKey.Algorithm()
|
||||||
|
|
||||||
switch cipher {
|
switch algorithm {
|
||||||
case data.RSAKey:
|
case data.RSAKey:
|
||||||
pemType = "RSA PRIVATE KEY"
|
pemType = "RSA PRIVATE KEY"
|
||||||
case data.ECDSAKey:
|
case data.ECDSAKey:
|
||||||
pemType = "EC PRIVATE KEY"
|
pemType = "EC PRIVATE KEY"
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher)
|
return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", algorithm)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pem.EncodeToMemory(&pem.Block{Type: pemType, Bytes: privKey.Private()}), nil
|
return pem.EncodeToMemory(&pem.Block{Type: pemType, Bytes: privKey.Private()}), nil
|
||||||
|
|
@ -298,15 +298,15 @@ func KeyToPEM(privKey *data.PrivateKey) ([]byte, error) {
|
||||||
// and a passphrase
|
// and a passphrase
|
||||||
func EncryptPrivateKey(key *data.PrivateKey, passphrase string) ([]byte, error) {
|
func EncryptPrivateKey(key *data.PrivateKey, passphrase string) ([]byte, error) {
|
||||||
var blockType string
|
var blockType string
|
||||||
cipher := key.Cipher()
|
algorithm := key.Algorithm()
|
||||||
|
|
||||||
switch cipher {
|
switch algorithm {
|
||||||
case data.RSAKey:
|
case data.RSAKey:
|
||||||
blockType = "RSA PRIVATE KEY"
|
blockType = "RSA PRIVATE KEY"
|
||||||
case data.ECDSAKey:
|
case data.ECDSAKey:
|
||||||
blockType = "EC PRIVATE KEY"
|
blockType = "EC PRIVATE KEY"
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", cipher)
|
return nil, fmt.Errorf("only RSA or ECDSA keys are currently supported. Found: %s", algorithm)
|
||||||
}
|
}
|
||||||
|
|
||||||
password := []byte(passphrase)
|
password := []byte(passphrase)
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,6 @@ func (root *rootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
ctx := context.WithValue(root.context, "repo", vars["imageName"])
|
ctx := context.WithValue(root.context, "repo", vars["imageName"])
|
||||||
|
|
||||||
// endophage: I don't guarantee that a signer will always be stateless but a CryptoService
|
|
||||||
// is expected to be. Create a new Signer for each request.
|
|
||||||
ctx = context.WithValue(ctx, "signer", signed.NewSigner(root.trust))
|
|
||||||
ctx = context.WithValue(ctx, "cryptoService", root.trust)
|
ctx = context.WithValue(ctx, "cryptoService", root.trust)
|
||||||
|
|
||||||
ctx = context.WithValue(ctx, "http.request", r)
|
ctx = context.WithValue(ctx, "http.request", r)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue