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",
|
||||
"Rev": "ab4ba80203ffa5bfd742e6891bd28bfbf43a9453"
|
||||
"Rev": "82786136d505f582d0f898a2e80c9f6b97b1402c"
|
||||
},
|
||||
{
|
||||
"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
|
||||
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),
|
||||
however in attempting to add delegations I found I was making such
|
||||
significant changes that I could not maintain backwards compatibility
|
||||
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
|
||||
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 {
|
||||
ID() string
|
||||
Cipher() string
|
||||
Algorithm() KeyAlgorithm
|
||||
Public() []byte
|
||||
Private() []byte
|
||||
}
|
||||
|
|
@ -21,14 +21,14 @@ type KeyPair struct {
|
|||
}
|
||||
|
||||
type TUFKey struct {
|
||||
id string `json:"-"`
|
||||
Type string `json:"keytype"`
|
||||
Value KeyPair `json:"keyval"`
|
||||
id string `json:"-"`
|
||||
Type KeyAlgorithm `json:"keytype"`
|
||||
Value KeyPair `json:"keyval"`
|
||||
}
|
||||
|
||||
func NewTUFKey(cipher string, public, private []byte) *TUFKey {
|
||||
func NewTUFKey(algorithm KeyAlgorithm, public, private []byte) *TUFKey {
|
||||
return &TUFKey{
|
||||
Type: cipher,
|
||||
Type: algorithm,
|
||||
Value: KeyPair{
|
||||
Public: public,
|
||||
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
|
||||
}
|
||||
|
||||
func (k *TUFKey) ID() string {
|
||||
if k.id == "" {
|
||||
pubK := NewTUFKey(k.Cipher(), k.Public(), nil)
|
||||
pubK := NewTUFKey(k.Algorithm(), k.Public(), nil)
|
||||
data, err := cjson.Marshal(&pubK)
|
||||
if err != nil {
|
||||
logrus.Error("Error generating key ID:", err)
|
||||
|
|
@ -65,10 +65,10 @@ func (k PublicKey) Private() []byte {
|
|||
return nil
|
||||
}
|
||||
|
||||
func NewPublicKey(cipher string, public []byte) *PublicKey {
|
||||
func NewPublicKey(algorithm KeyAlgorithm, public []byte) *PublicKey {
|
||||
return &PublicKey{
|
||||
TUFKey{
|
||||
Type: cipher,
|
||||
Type: algorithm,
|
||||
Value: KeyPair{
|
||||
Public: public,
|
||||
Private: nil,
|
||||
|
|
@ -87,10 +87,10 @@ type PrivateKey struct {
|
|||
TUFKey
|
||||
}
|
||||
|
||||
func NewPrivateKey(cipher string, public, private []byte) *PrivateKey {
|
||||
func NewPrivateKey(algorithm KeyAlgorithm, public, private []byte) *PrivateKey {
|
||||
return &PrivateKey{
|
||||
TUFKey{
|
||||
Type: cipher,
|
||||
Type: algorithm,
|
||||
Value: KeyPair{
|
||||
Public: []byte(public),
|
||||
Private: []byte(private),
|
||||
|
|
|
|||
|
|
@ -8,21 +8,37 @@ import (
|
|||
"hash"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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 (
|
||||
defaultHashAlgorithm = "sha256"
|
||||
EDDSASignature = "eddsa"
|
||||
RSAPSSSignature = "rsapss"
|
||||
ECDSASignature = "ecdsa"
|
||||
RSAKey = "rsa"
|
||||
RSAx509Key = "rsa-x509"
|
||||
ECDSAKey = "ecdsa"
|
||||
ECDSAx509Key = "ecdsa-x509"
|
||||
PyCryptoSignature = "pycrypto-pkcs#1 pss"
|
||||
|
||||
EDDSASignature SigAlgorithm = "eddsa"
|
||||
RSAPSSSignature SigAlgorithm = "rsapss"
|
||||
ECDSASignature SigAlgorithm = "ecdsa"
|
||||
PyCryptoSignature SigAlgorithm = "pycrypto-pkcs#1 pss"
|
||||
|
||||
ED25519Key KeyAlgorithm = "ed25519"
|
||||
RSAKey KeyAlgorithm = "rsa"
|
||||
RSAx509Key KeyAlgorithm = "rsa-x509"
|
||||
ECDSAKey KeyAlgorithm = "ecdsa"
|
||||
ECDSAx509Key KeyAlgorithm = "ecdsa-x509"
|
||||
)
|
||||
|
||||
var TUFTypes = map[string]string{
|
||||
|
|
@ -63,9 +79,9 @@ type Signed struct {
|
|||
}
|
||||
|
||||
type Signature struct {
|
||||
KeyID string `json:"keyid"`
|
||||
Method string `json:"method"`
|
||||
Signature HexBytes `json:"sig"`
|
||||
KeyID string `json:"keyid"`
|
||||
Method SigAlgorithm `json:"method"`
|
||||
Signature HexBytes `json:"sig"`
|
||||
}
|
||||
|
||||
type Files map[string]FileMeta
|
||||
|
|
@ -144,3 +160,16 @@ func DefaultExpires(role string) time.Time {
|
|||
}
|
||||
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 (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
. "gopkg.in/check.v1"
|
||||
)
|
||||
|
|
@ -43,3 +44,14 @@ func (TypesSuite) TestGenerateFileMetaExplicit(c *C) {
|
|||
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 (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
|
||||
"github.com/agl/ed25519"
|
||||
"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)
|
||||
signatures = append(signatures, data.Signature{
|
||||
KeyID: kID,
|
||||
Method: "ED25519",
|
||||
Method: data.EDDSASignature,
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
public := data.NewPublicKey("ED25519", pub[:])
|
||||
private := data.NewPrivateKey("ED25519", pub[:], priv[:])
|
||||
public := data.NewPublicKey(data.ED25519Key, pub[:])
|
||||
private := data.NewPrivateKey(data.ED25519Key, pub[:], priv[:])
|
||||
trust.addKey(private)
|
||||
return public, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,9 @@ type SigningService interface {
|
|||
type KeyService interface {
|
||||
// Create issues a new key pair and is responsible for loading
|
||||
// the private key into the appropriate signing service.
|
||||
Create(role string) (*data.PublicKey, error)
|
||||
|
||||
// PublicKeys return the PublicKey instances for the given KeyIDs
|
||||
// PublicKeys(keyIDs ...string) (map[string]*data.PublicKey, error)
|
||||
// The role isn't currently used for anything, but it's here to support
|
||||
// future features
|
||||
Create(role string, algorithm data.KeyAlgorithm) (*data.PublicKey, error)
|
||||
}
|
||||
|
||||
// CryptoService defines a unified Signing and Key Service as this
|
||||
|
|
|
|||
|
|
@ -5,19 +5,9 @@ import (
|
|||
"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
|
||||
// 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))
|
||||
signatures := make([]data.Signature, 0, len(s.Signatures)+1)
|
||||
keyIDMemb := make(map[string]struct{})
|
||||
|
|
@ -34,7 +24,7 @@ func (signer *Signer) Sign(s *data.Signed, keys ...*data.PublicKey) error {
|
|||
}
|
||||
signatures = append(signatures, sig)
|
||||
}
|
||||
newSigs, err := signer.service.Sign(keyIDs, s.Signed)
|
||||
newSigs, err := service.Sign(keyIDs, s.Signed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -42,12 +32,3 @@ func (signer *Signer) Sign(s *data.Signed, keys ...*data.PublicKey) error {
|
|||
s.Signatures = append(signatures, newSigs...)
|
||||
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
|
||||
}
|
||||
|
||||
func (mts *MockCryptoService) Create(_ string) (*data.PublicKey, error) {
|
||||
func (mts *MockCryptoService) Create(_ string, _ data.KeyAlgorithm) (*data.PublicKey, error) {
|
||||
return &mts.testKey, nil
|
||||
}
|
||||
|
||||
|
|
@ -42,16 +42,14 @@ var _ CryptoService = &MockCryptoService{}
|
|||
func TestBasicSign(t *testing.T) {
|
||||
testKey, _ := pem.Decode([]byte(testKeyPEM1))
|
||||
k := data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
||||
signer := Signer{&MockCryptoService{
|
||||
testKey: *k,
|
||||
}}
|
||||
key, err := signer.Create("root")
|
||||
mockCryptoService := &MockCryptoService{testKey: *k}
|
||||
key, err := mockCryptoService.Create("root", data.ED25519Key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testData := data.Signed{}
|
||||
|
||||
signer.Sign(&testData, key)
|
||||
Sign(mockCryptoService, &testData, key)
|
||||
|
||||
if len(testData.Signatures) != 1 {
|
||||
t.Fatalf("Incorrect number of signatures: %d", len(testData.Signatures))
|
||||
|
|
@ -69,13 +67,11 @@ func TestBasicSign(t *testing.T) {
|
|||
func TestReSign(t *testing.T) {
|
||||
testKey, _ := pem.Decode([]byte(testKeyPEM1))
|
||||
k := data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
||||
signer := Signer{&MockCryptoService{
|
||||
testKey: *k,
|
||||
}}
|
||||
mockCryptoService := &MockCryptoService{testKey: *k}
|
||||
testData := data.Signed{}
|
||||
|
||||
signer.Sign(&testData, k)
|
||||
signer.Sign(&testData, k)
|
||||
Sign(mockCryptoService, &testData, k)
|
||||
Sign(mockCryptoService, &testData, k)
|
||||
|
||||
if len(testData.Signatures) != 1 {
|
||||
t.Fatalf("Incorrect number of signatures: %d", len(testData.Signatures))
|
||||
|
|
@ -88,16 +84,16 @@ func TestReSign(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMultiSign(t *testing.T) {
|
||||
signer := Signer{&MockCryptoService{}}
|
||||
mockCryptoService := &MockCryptoService{}
|
||||
testData := data.Signed{}
|
||||
|
||||
testKey, _ := pem.Decode([]byte(testKeyPEM1))
|
||||
key := data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
||||
signer.Sign(&testData, key)
|
||||
Sign(mockCryptoService, &testData, key)
|
||||
|
||||
testKey, _ = pem.Decode([]byte(testKeyPEM2))
|
||||
key = data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
||||
signer.Sign(&testData, key)
|
||||
Sign(mockCryptoService, &testData, key)
|
||||
|
||||
if len(testData.Signatures) != 2 {
|
||||
t.Fatalf("Incorrect number of signatures: %d", len(testData.Signatures))
|
||||
|
|
@ -115,11 +111,9 @@ func TestMultiSign(t *testing.T) {
|
|||
func TestCreate(t *testing.T) {
|
||||
testKey, _ := pem.Decode([]byte(testKeyPEM1))
|
||||
k := data.NewPublicKey(data.RSAKey, testKey.Bytes)
|
||||
signer := Signer{&MockCryptoService{
|
||||
testKey: *k,
|
||||
}}
|
||||
mockCryptoService := &MockCryptoService{testKey: *k}
|
||||
|
||||
key, err := signer.Create("root")
|
||||
key, err := mockCryptoService.Create("root", data.ED25519Key)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import (
|
|||
// Verifiers serves as a map of all verifiers available on the system and
|
||||
// can be injected into a verificationService. For testing and configuration
|
||||
// purposes, it will not be used by default.
|
||||
var Verifiers = map[string]Verifier{
|
||||
var Verifiers = map[data.SigAlgorithm]Verifier{
|
||||
data.RSAPSSSignature: RSAPSSVerifier{},
|
||||
data.PyCryptoSignature: RSAPyCryptoVerifier{},
|
||||
data.ECDSASignature: ECDSAVerifier{},
|
||||
|
|
@ -27,8 +27,8 @@ var Verifiers = map[string]Verifier{
|
|||
|
||||
// RegisterVerifier provides a convenience function for init() functions
|
||||
// to register additional verifiers or replace existing ones.
|
||||
func RegisterVerifier(name string, v Verifier) {
|
||||
curr, ok := Verifiers[name]
|
||||
func RegisterVerifier(algorithm data.SigAlgorithm, v Verifier) {
|
||||
curr, ok := Verifiers[algorithm]
|
||||
if ok {
|
||||
typOld := reflect.TypeOf(curr)
|
||||
typNew := reflect.TypeOf(v)
|
||||
|
|
@ -38,9 +38,9 @@ func RegisterVerifier(name string, v Verifier) {
|
|||
typNew.PkgPath(), typNew.Name(),
|
||||
)
|
||||
} else {
|
||||
logrus.Debug("adding verifier for: ", name)
|
||||
logrus.Debug("adding verifier for: ", algorithm)
|
||||
}
|
||||
Verifiers[name] = v
|
||||
Verifiers[algorithm] = v
|
||||
}
|
||||
|
||||
type Ed25519Verifier struct{}
|
||||
|
|
@ -83,10 +83,10 @@ type RSAPSSVerifier struct{}
|
|||
|
||||
// Verify does the actual check.
|
||||
func (v RSAPSSVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
|
||||
cipher := key.Cipher()
|
||||
algorithm := key.Algorithm()
|
||||
var pubKey crypto.PublicKey
|
||||
|
||||
switch cipher {
|
||||
switch algorithm {
|
||||
case data.RSAx509Key:
|
||||
pemCert, _ := pem.Decode([]byte(key.Public()))
|
||||
if pemCert == nil {
|
||||
|
|
@ -107,7 +107,7 @@ func (v RSAPSSVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
|
|||
return ErrInvalid
|
||||
}
|
||||
default:
|
||||
logrus.Infof("invalid key type for RSAPSS verifier: %s", cipher)
|
||||
logrus.Infof("invalid key type for RSAPSS verifier: %s", algorithm)
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
|
|
@ -145,10 +145,10 @@ type ECDSAVerifier struct{}
|
|||
|
||||
// Verify does the actual check.
|
||||
func (v ECDSAVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
|
||||
cipher := key.Cipher()
|
||||
algorithm := key.Algorithm()
|
||||
var pubKey crypto.PublicKey
|
||||
|
||||
switch cipher {
|
||||
switch algorithm {
|
||||
case data.ECDSAx509Key:
|
||||
pemCert, _ := pem.Decode([]byte(key.Public()))
|
||||
if pemCert == nil {
|
||||
|
|
@ -170,7 +170,7 @@ func (v ECDSAVerifier) Verify(key data.Key, sig []byte, msg []byte) error {
|
|||
return ErrInvalid
|
||||
}
|
||||
default:
|
||||
logrus.Infof("invalid key type for ECDSA verifier: %s", cipher)
|
||||
logrus.Infof("invalid key type for ECDSA verifier: %s", algorithm)
|
||||
return ErrInvalid
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import (
|
|||
)
|
||||
|
||||
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=="}}`
|
||||
|
|
@ -248,8 +248,8 @@ func TestECDSAVerifierWithInvalidSignature(t *testing.T) {
|
|||
}
|
||||
|
||||
func rsaSign(privKey *data.PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
|
||||
if privKey.Cipher() != data.RSAKey {
|
||||
return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher())
|
||||
if privKey.Algorithm() != data.RSAKey {
|
||||
return nil, fmt.Errorf("private key type not supported: %s", privKey.Algorithm())
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if privKey.Cipher() != data.ECDSAKey {
|
||||
return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher())
|
||||
if privKey.Algorithm() != data.ECDSAKey {
|
||||
return nil, fmt.Errorf("private key type not supported: %s", privKey.Algorithm())
|
||||
}
|
||||
|
||||
// Create an ecdsa.PrivateKey out of the private key bytes
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package signed
|
|||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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{})
|
||||
for _, sig := range s.Signatures {
|
||||
// make method lookup consistent with case uniformity.
|
||||
method := strings.ToLower(sig.Method)
|
||||
// method lookup is consistent due to Unmarshal JSON doing lower case for us.
|
||||
method := sig.Method
|
||||
verifier, ok := Verifiers[method]
|
||||
if !ok {
|
||||
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)
|
||||
continue
|
||||
}
|
||||
// make method lookup consistent with case uniformity.
|
||||
method := strings.ToLower(sig.Method)
|
||||
// method lookup is consistent due to Unmarshal JSON doing lower case for us.
|
||||
method := sig.Method
|
||||
verifier, ok := Verifiers[method]
|
||||
if !ok {
|
||||
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) {
|
||||
trust := NewEd25519()
|
||||
signer := NewSigner(trust)
|
||||
type test struct {
|
||||
name string
|
||||
keys []*data.PublicKey
|
||||
|
|
@ -78,8 +77,8 @@ func (VerifySuite) Test(c *C) {
|
|||
{
|
||||
name: "more than enough signatures",
|
||||
mut: func(t *test) {
|
||||
k, _ := signer.Create("root")
|
||||
signer.Sign(t.s, k)
|
||||
k, _ := trust.Create("root", data.ED25519Key)
|
||||
Sign(trust, t.s, k)
|
||||
t.keys = append(t.keys, k)
|
||||
t.roles["root"].KeyIDs = append(t.roles["root"].KeyIDs, k.ID())
|
||||
},
|
||||
|
|
@ -95,15 +94,15 @@ func (VerifySuite) Test(c *C) {
|
|||
{
|
||||
name: "unknown key",
|
||||
mut: func(t *test) {
|
||||
k, _ := signer.Create("root")
|
||||
signer.Sign(t.s, k)
|
||||
k, _ := trust.Create("root", data.ED25519Key)
|
||||
Sign(trust, t.s, k)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unknown key below threshold",
|
||||
mut: func(t *test) {
|
||||
k, _ := signer.Create("root")
|
||||
signer.Sign(t.s, k)
|
||||
k, _ := trust.Create("root", data.ED25519Key)
|
||||
Sign(trust, t.s, k)
|
||||
t.roles["root"].Threshold = 2
|
||||
},
|
||||
err: ErrRoleThreshold,
|
||||
|
|
@ -111,16 +110,16 @@ func (VerifySuite) Test(c *C) {
|
|||
{
|
||||
name: "unknown keys in db",
|
||||
mut: func(t *test) {
|
||||
k, _ := signer.Create("root")
|
||||
signer.Sign(t.s, k)
|
||||
k, _ := trust.Create("root", data.ED25519Key)
|
||||
Sign(trust, t.s, k)
|
||||
t.keys = append(t.keys, k)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unknown keys in db below threshold",
|
||||
mut: func(t *test) {
|
||||
k, _ := signer.Create("root")
|
||||
signer.Sign(t.s, k)
|
||||
k, _ := trust.Create("root", data.ED25519Key)
|
||||
Sign(trust, t.s, k)
|
||||
t.keys = append(t.keys, k)
|
||||
t.roles["root"].Threshold = 2
|
||||
},
|
||||
|
|
@ -157,13 +156,13 @@ func (VerifySuite) Test(c *C) {
|
|||
t.typ = data.TUFTypes[t.role]
|
||||
}
|
||||
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")}
|
||||
|
||||
b, err := cjson.Marshal(meta)
|
||||
c.Assert(err, IsNil)
|
||||
s := &data.Signed{Signed: b}
|
||||
signer.Sign(s, k)
|
||||
Sign(trust, s, k)
|
||||
t.s = s
|
||||
t.keys = []*data.PublicKey{k}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/tent/canonical-json-go"
|
||||
|
|
@ -55,7 +54,7 @@ func TestGetMeta(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
method := strings.ToLower(p.Signatures[0].Method)
|
||||
method := p.Signatures[0].Method
|
||||
err = signed.Verifiers[method].Verify(k, sigBytes, msg)
|
||||
if err != nil {
|
||||
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-----"
|
||||
testStr := "The quick brown fox jumps over the lazy dog."
|
||||
sigHex := "4e05ee9e435653549ac4eddbc43e1a6868636e8ea6dbec2564435afcb0de47e0824cddbd88776ddb20728c53ecc90b5d543d5c37575fda8bd0317025fc07de62ee8084b1a75203b1a23d1ef4ac285da3d1fc63317d5b2cf1aafa3e522acedd366ccd5fe4a7f02a42922237426ca3dc154c57408638b9bfaf0d0213855d4e9ee621db204151bcb13d4dbb18f930ec601469c992c84b14e9e0b6f91ac9517bb3b749dd117e1cbac2e4acb0e549f44558a2005898a226d5b6c8b9291d7abae0d9e0a16858b89662a085f74a202deb867acab792bdbd2c36731217caea8b17bd210c29b890472f11e5afdd1dd7b69004db070e04201778f2c49f5758643881403d45a58d08f51b5c63910c6185892f0b590f191d760b669eff2464456f130239bba94acf54a0cb98f6939ff84ae26a37f9b890be259d9b5d636f6eb367b53e895227d7d79a3a88afd6d28c198ee80f6527437c5fbf63accb81709925c4e03d1c9eaee86f58e4bd1c669d6af042dbd412de0d13b98b1111e2fadbe34b45de52125e9a"
|
||||
k := data.NewPublicKey("RSA", []byte(pubPem))
|
||||
k := data.NewPublicKey(data.RSAKey, []byte(pubPem))
|
||||
|
||||
sigBytes, err := hex.DecodeString(sigHex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
v := signed.RSAPSSVerifier{}
|
||||
v := signed.RSAPyCryptoVerifier{}
|
||||
err = v.Verify(k, sigBytes, []byte(testStr))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -88,11 +87,11 @@ func TestPyNaCled25519Compat(t *testing.T) {
|
|||
sigHex := "166e7013e48f26dccb4e68fe4cf558d1cd3af902f8395534336a7f8b4c56588694aa3ac671767246298a59d5ef4224f02c854f41bfcfe70241db4be1546d6a00"
|
||||
|
||||
pub, _ := hex.DecodeString(pubHex)
|
||||
k := data.NewPublicKey("ED25519", pub)
|
||||
k := data.NewPublicKey(data.ED25519Key, pub)
|
||||
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,21 +50,21 @@ func (err *ErrNotLoaded) Error() string {
|
|||
// fetching raw JSON and using the Set* functions to populate
|
||||
// the TufRepo instance.
|
||||
type TufRepo struct {
|
||||
Root *data.SignedRoot
|
||||
Targets map[string]*data.SignedTargets
|
||||
Snapshot *data.SignedSnapshot
|
||||
Timestamp *data.SignedTimestamp
|
||||
keysDB *keys.KeyDB
|
||||
signer *signed.Signer
|
||||
Root *data.SignedRoot
|
||||
Targets map[string]*data.SignedTargets
|
||||
Snapshot *data.SignedSnapshot
|
||||
Timestamp *data.SignedTimestamp
|
||||
keysDB *keys.KeyDB
|
||||
cryptoService signed.CryptoService
|
||||
}
|
||||
|
||||
// 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.
|
||||
func NewTufRepo(keysDB *keys.KeyDB, signer *signed.Signer) *TufRepo {
|
||||
func NewTufRepo(keysDB *keys.KeyDB, cryptoService signed.CryptoService) *TufRepo {
|
||||
repo := &TufRepo{
|
||||
Targets: make(map[string]*data.SignedTargets),
|
||||
keysDB: keysDB,
|
||||
signer: signer,
|
||||
Targets: make(map[string]*data.SignedTargets),
|
||||
keysDB: keysDB,
|
||||
cryptoService: cryptoService,
|
||||
}
|
||||
return repo
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ func (tr *TufRepo) AddBaseKeys(role string, keys ...data.Key) error {
|
|||
return &ErrNotLoaded{role: "root"}
|
||||
}
|
||||
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.keysDB.AddKey(key)
|
||||
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{}
|
||||
}
|
||||
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()) {
|
||||
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.
|
||||
key := tr.keysDB.GetKey(kid)
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
|
@ -449,7 +449,7 @@ func (tr *TufRepo) UpdateTimestamp(s *data.Signed) error {
|
|||
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...")
|
||||
if tr.Root.Dirty {
|
||||
tr.Root.Signed.Version++
|
||||
|
|
@ -459,7 +459,7 @@ func (tr *TufRepo) SignRoot(expires time.Time, signer *signed.Signer) (*data.Sig
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signed, err = tr.sign(signed, *root, signer)
|
||||
signed, err = tr.sign(signed, *root, cryptoService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -467,7 +467,7 @@ func (tr *TufRepo) SignRoot(expires time.Time, signer *signed.Signer) (*data.Sig
|
|||
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)
|
||||
if tr.Targets[role].Dirty {
|
||||
tr.Targets[role].Signed.Version++
|
||||
|
|
@ -477,7 +477,7 @@ func (tr *TufRepo) SignTargets(role string, expires time.Time, signer *signed.Si
|
|||
return nil, err
|
||||
}
|
||||
targets := tr.keysDB.GetRole(role)
|
||||
signed, err = tr.sign(signed, *targets, signer)
|
||||
signed, err = tr.sign(signed, *targets, cryptoService)
|
||||
if err != nil {
|
||||
logrus.Debug("errored signing ", role)
|
||||
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...")
|
||||
if tr.Root.Dirty {
|
||||
signedRoot, err := tr.SignRoot(data.DefaultExpires("root"), signer)
|
||||
signedRoot, err := tr.SignRoot(data.DefaultExpires("root"), cryptoService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -511,7 +511,7 @@ func (tr *TufRepo) SignSnapshot(expires time.Time, signer *signed.Signer) (*data
|
|||
if !targets.Dirty {
|
||||
continue
|
||||
}
|
||||
signedTargets, err := tr.SignTargets(role, data.DefaultExpires("targets"), signer)
|
||||
signedTargets, err := tr.SignTargets(role, data.DefaultExpires("targets"), cryptoService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -528,7 +528,7 @@ func (tr *TufRepo) SignSnapshot(expires time.Time, signer *signed.Signer) (*data
|
|||
return nil, err
|
||||
}
|
||||
snapshot := tr.keysDB.GetRole(data.ValidRoles["snapshot"])
|
||||
signed, err = tr.sign(signed, *snapshot, signer)
|
||||
signed, err = tr.sign(signed, *snapshot, cryptoService)
|
||||
if err != nil {
|
||||
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")
|
||||
if tr.Snapshot.Dirty {
|
||||
signedSnapshot, err := tr.SignSnapshot(data.DefaultExpires("snapshot"), signer)
|
||||
signedSnapshot, err := tr.SignSnapshot(data.DefaultExpires("snapshot"), cryptoService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -562,7 +562,7 @@ func (tr *TufRepo) SignTimestamp(expires time.Time, signer *signed.Signer) (*dat
|
|||
return nil, err
|
||||
}
|
||||
timestamp := tr.keysDB.GetRole(data.ValidRoles["timestamp"])
|
||||
signed, err = tr.sign(signed, *timestamp, signer)
|
||||
signed, err = tr.sign(signed, *timestamp, cryptoService)
|
||||
if err != nil {
|
||||
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))
|
||||
for _, kid := range role.KeyIDs {
|
||||
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 {
|
||||
return nil, keys.ErrInvalidKey
|
||||
}
|
||||
if signer != nil {
|
||||
err := signer.Sign(signed, ks...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
err := tr.signer.Sign(signed, ks...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cryptoService == nil {
|
||||
cryptoService = tr.cryptoService
|
||||
}
|
||||
return signed, nil
|
||||
err := signed.Sign(cryptoService, signedData, ks...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signedData, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,21 +13,20 @@ import (
|
|||
"github.com/endophage/gotuf/signed"
|
||||
)
|
||||
|
||||
func initRepo(t *testing.T, signer *signed.Signer, keyDB *keys.KeyDB) *TufRepo {
|
||||
|
||||
rootKey, err := signer.Create("root")
|
||||
func initRepo(t *testing.T, cryptoService signed.CryptoService, keyDB *keys.KeyDB) *TufRepo {
|
||||
rootKey, err := cryptoService.Create("root", data.ED25519Key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
targetsKey, err := signer.Create("targets")
|
||||
targetsKey, err := cryptoService.Create("targets", data.ED25519Key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
snapshotKey, err := signer.Create("snapshot")
|
||||
snapshotKey, err := cryptoService.Create("snapshot", data.ED25519Key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
timestampKey, err := signer.Create("timestamp")
|
||||
timestampKey, err := cryptoService.Create("timestamp", data.ED25519Key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -71,7 +70,7 @@ func initRepo(t *testing.T, signer *signed.Signer, keyDB *keys.KeyDB) *TufRepo {
|
|||
keyDB.AddRole(snapshotRole)
|
||||
keyDB.AddRole(timestampRole)
|
||||
|
||||
repo := NewTufRepo(keyDB, signer)
|
||||
repo := NewTufRepo(keyDB, cryptoService)
|
||||
err = repo.InitRepo(false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -124,19 +123,17 @@ func writeRepo(t *testing.T, dir string, repo *TufRepo) {
|
|||
|
||||
func TestInitRepo(t *testing.T) {
|
||||
ed25519 := signed.NewEd25519()
|
||||
signer := signed.NewSigner(ed25519)
|
||||
keyDB := keys.NewDB()
|
||||
repo := initRepo(t, signer, keyDB)
|
||||
repo := initRepo(t, ed25519, keyDB)
|
||||
writeRepo(t, "/tmp/tufrepo", repo)
|
||||
}
|
||||
|
||||
func TestUpdateDelegations(t *testing.T) {
|
||||
ed25519 := signed.NewEd25519()
|
||||
signer := signed.NewSigner(ed25519)
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -150,7 +147,7 @@ func TestUpdateDelegations(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testDeepKey, err := signer.Create("targets/test/deep")
|
||||
testDeepKey, err := ed25519.Create("targets/test/deep", data.ED25519Key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,51 +15,52 @@ import (
|
|||
"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
|
||||
passphrase string
|
||||
keyStore *trustmanager.KeyFileStore
|
||||
}
|
||||
|
||||
// RSACryptoService implements Sign and Create, holding a specific GUN and keystore to
|
||||
// operate on
|
||||
type RSACryptoService struct {
|
||||
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}}
|
||||
// NewCryptoService returns an instance of CryptoService
|
||||
func NewCryptoService(gun string, keyStore *trustmanager.KeyFileStore, passphrase string) *CryptoService {
|
||||
return &CryptoService{gun: gun, keyStore: keyStore, passphrase: passphrase}
|
||||
}
|
||||
|
||||
// Create is used to generate keys for targets, snapshots and timestamps
|
||||
func (ccs *RSACryptoService) Create(role string) (*data.PublicKey, error) {
|
||||
privKey, err := trustmanager.GenerateRSAKey(rand.Reader, rsaKeySize)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate RSA key: %v", err)
|
||||
func (ccs *CryptoService) Create(role string, algorithm data.KeyAlgorithm) (*data.PublicKey, error) {
|
||||
var privKey *data.PrivateKey
|
||||
var err error
|
||||
|
||||
switch algorithm {
|
||||
case data.RSAKey:
|
||||
privKey, err = trustmanager.GenerateRSAKey(rand.Reader, rsaKeySize)
|
||||
if err != nil {
|
||||
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
|
||||
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 RSA key for role: %s and 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 *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
|
||||
hash := crypto.SHA256
|
||||
hashed := sha256.Sum256(payload)
|
||||
|
|
@ -72,11 +73,8 @@ func (ccs *RSACryptoService) Sign(keyIDs []string, payload []byte) ([]data.Signa
|
|||
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
|
||||
// Read PrivateKey from file and decrypt it if there is a passphrase.
|
||||
if ccs.passphrase != "" {
|
||||
// This is a root key
|
||||
privKey, err = ccs.keyStore.GetDecryptedKey(keyName, ccs.passphrase)
|
||||
} else {
|
||||
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
|
||||
// the root keys. Continuing here is safe because we
|
||||
// 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
|
||||
}
|
||||
|
||||
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 the rsaSign method got called with a non RSA private key,
|
||||
// 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)
|
||||
logrus.Debugf("ignoring error attempting to %s sign with keyID: %s, %v", algorithm, keyid, err)
|
||||
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
|
||||
signatures = append(signatures, data.Signature{
|
||||
KeyID: keyid,
|
||||
Method: data.RSAPSSSignature,
|
||||
Method: sigAlgorithm,
|
||||
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) {
|
||||
if privKey.Cipher() != data.RSAKey {
|
||||
return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher())
|
||||
if privKey.Algorithm() != data.RSAKey {
|
||||
return nil, fmt.Errorf("private key type not supported: %s", privKey.Algorithm())
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if privKey.Cipher() != data.ECDSAKey {
|
||||
return nil, fmt.Errorf("private key type not supported: %s", privKey.Cipher())
|
||||
if privKey.Algorithm() != data.ECDSAKey {
|
||||
return nil, fmt.Errorf("private key type not supported: %s", privKey.Algorithm())
|
||||
}
|
||||
|
||||
// Create an ecdsa.PrivateKey out of the private key bytes
|
||||
|
|
|
|||
130
client/client.go
130
client/client.go
|
|
@ -55,11 +55,12 @@ const (
|
|||
/// that doesn't exist.
|
||||
var ErrRepositoryNotExist = errors.New("repository does not exist")
|
||||
|
||||
// UnlockedSigner encapsulates a private key and a signer that uses that private key,
|
||||
// providing convinience methods for generation of certificates.
|
||||
type UnlockedSigner struct {
|
||||
privKey *data.PrivateKey
|
||||
signer *signed.Signer
|
||||
// UnlockedCryptoService encapsulates a private key and a cryptoservice that
|
||||
// uses that private key, providing convinience methods for generation of
|
||||
// certificates.
|
||||
type UnlockedCryptoService struct {
|
||||
privKey *data.PrivateKey
|
||||
cryptoService signed.CryptoService
|
||||
}
|
||||
|
||||
// NotaryRepository stores all the information needed to operate on a notary
|
||||
|
|
@ -72,11 +73,10 @@ type NotaryRepository struct {
|
|||
caStore trustmanager.X509Store
|
||||
certificateStore trustmanager.X509Store
|
||||
fileStore store.MetadataStore
|
||||
signer *signed.Signer
|
||||
cryptoService signed.CryptoService
|
||||
tufRepo *tuf.TufRepo
|
||||
privKeyStore *trustmanager.KeyFileStore
|
||||
rootKeyStore *trustmanager.KeyFileStore
|
||||
rootSigner *UnlockedSigner
|
||||
roundTrip http.RoundTripper
|
||||
}
|
||||
|
||||
|
|
@ -115,17 +115,16 @@ func NewNotaryRepository(baseDir, gun, baseURL string, rt http.RoundTripper) (*N
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(diogo): This hardcodes snapshots and targets to using EC. Change it.
|
||||
signer := signed.NewSigner(NewECDSACryptoService(gun, privKeyStore, ""))
|
||||
cryptoService := NewCryptoService(gun, privKeyStore, "")
|
||||
|
||||
nRepo := &NotaryRepository{
|
||||
gun: gun,
|
||||
baseDir: baseDir,
|
||||
baseURL: baseURL,
|
||||
tufRepoPath: filepath.Join(baseDir, tufDir, gun),
|
||||
signer: signer,
|
||||
privKeyStore: privKeyStore,
|
||||
roundTrip: rt,
|
||||
gun: gun,
|
||||
baseDir: baseDir,
|
||||
baseURL: baseURL,
|
||||
tufRepoPath: filepath.Join(baseDir, tufDir, gun),
|
||||
cryptoService: cryptoService,
|
||||
privKeyStore: privKeyStore,
|
||||
roundTrip: rt,
|
||||
}
|
||||
|
||||
if err := nRepo.loadKeys(trustDir, rootKeysDir); err != nil {
|
||||
|
|
@ -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
|
||||
// TUF repository.
|
||||
func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
|
||||
rootCert, err := uSigner.GenerateCertificate(r.gun)
|
||||
func (r *NotaryRepository) Initialize(uCryptoService *UnlockedCryptoService) error {
|
||||
rootCert, err := uCryptoService.GenerateCertificate(r.gun)
|
||||
if err != nil {
|
||||
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
|
||||
// as ECDSAx509 to allow the gotuf verifiers to correctly decode the
|
||||
// key on verification of signatures.
|
||||
var cipherType string
|
||||
cipher := uSigner.privKey.Cipher()
|
||||
switch cipher {
|
||||
var algorithmType data.KeyAlgorithm
|
||||
algorithm := uCryptoService.privKey.Algorithm()
|
||||
switch algorithm {
|
||||
case data.RSAKey:
|
||||
cipherType = data.RSAx509Key
|
||||
algorithmType = data.RSAx509Key
|
||||
case data.ECDSAKey:
|
||||
cipherType = data.ECDSAx509Key
|
||||
algorithmType = data.ECDSAx509Key
|
||||
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
|
||||
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
|
||||
// is associated with. This is used to be able to retrieve the root private key
|
||||
// associated with a particular certificate
|
||||
logrus.Debugf("Linking %s to %s.", rootKey.ID(), uSigner.ID())
|
||||
err = r.rootKeyStore.Link(uSigner.ID(), rootKey.ID())
|
||||
logrus.Debugf("Linking %s to %s.", rootKey.ID(), uCryptoService.ID())
|
||||
err = r.rootKeyStore.Link(uCryptoService.ID(), rootKey.ID())
|
||||
if err != nil {
|
||||
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
|
||||
timestampKey := data.NewPublicKey(parsedKey.Cipher(), parsedKey.Public())
|
||||
logrus.Debugf("got remote %s timestamp key with keyID: %s", parsedKey.Cipher(), timestampKey.ID())
|
||||
timestampKey := data.NewPublicKey(parsedKey.Algorithm(), parsedKey.Public())
|
||||
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.
|
||||
targetsKey, err := r.signer.Create("targets")
|
||||
targetsKey, err := r.cryptoService.Create("targets", data.ECDSAKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
snapshotKey, err := r.signer.Create("snapshot")
|
||||
snapshotKey, err := r.cryptoService.Create("snapshot", data.ECDSAKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -236,7 +236,7 @@ func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
|
|||
return err
|
||||
}
|
||||
|
||||
r.tufRepo = tuf.NewTufRepo(kdb, r.signer)
|
||||
r.tufRepo = tuf.NewTufRepo(kdb, r.cryptoService)
|
||||
|
||||
r.fileStore, err = store.NewFilesystemStore(
|
||||
r.tufRepoPath,
|
||||
|
|
@ -252,7 +252,7 @@ func (r *NotaryRepository) Initialize(uSigner *UnlockedSigner) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := r.saveMetadata(uSigner.signer); err != nil {
|
||||
if err := r.saveMetadata(uCryptoService.cryptoService); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -389,11 +389,11 @@ func (r *NotaryRepository) Publish(getPass passwordRetriever) error {
|
|||
return err
|
||||
}
|
||||
rootKeyID := r.tufRepo.Root.Signed.Roles["root"].KeyIDs[0]
|
||||
rootSigner, err := r.GetRootSigner(rootKeyID, passphrase)
|
||||
rootCryptoService, err := r.GetRootCryptoService(rootKeyID, passphrase)
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
@ -459,7 +459,7 @@ func (r *NotaryRepository) bootstrapRepo() error {
|
|||
}
|
||||
|
||||
kdb := keys.NewDB()
|
||||
tufRepo := tuf.NewTufRepo(kdb, r.signer)
|
||||
tufRepo := tuf.NewTufRepo(kdb, r.cryptoService)
|
||||
|
||||
logrus.Debugf("Loading trusted collection.")
|
||||
rootJSON, err := fileStore.GetMeta("root", 0)
|
||||
|
|
@ -499,8 +499,8 @@ func (r *NotaryRepository) bootstrapRepo() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *NotaryRepository) saveMetadata(rootSigner *signed.Signer) error {
|
||||
signedRoot, err := r.tufRepo.SignRoot(data.DefaultExpires("root"), rootSigner)
|
||||
func (r *NotaryRepository) saveMetadata(rootCryptoService signed.CryptoService) error {
|
||||
signedRoot, err := r.tufRepo.SignRoot(data.DefaultExpires("root"), rootCryptoService)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -632,7 +632,7 @@ func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) {
|
|||
}
|
||||
|
||||
kdb := keys.NewDB()
|
||||
r.tufRepo = tuf.NewTufRepo(kdb, r.signer)
|
||||
r.tufRepo = tuf.NewTufRepo(kdb, r.cryptoService)
|
||||
|
||||
err = r.tufRepo.SetRoot(root)
|
||||
if err != nil {
|
||||
|
|
@ -653,11 +653,15 @@ func (r *NotaryRepository) ListRootKeys() []string {
|
|||
}
|
||||
|
||||
// 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) {
|
||||
var err error
|
||||
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:
|
||||
privKey, err = trustmanager.GenerateRSAKey(rand.Reader, rsaRootKeySize)
|
||||
case data.ECDSAKey:
|
||||
|
|
@ -676,28 +680,18 @@ func (r *NotaryRepository) GenRootKey(algorithm, passphrase string) (string, err
|
|||
return privKey.ID(), nil
|
||||
}
|
||||
|
||||
// GetRootSigner retreives a root key that includes the ID and a signer
|
||||
func (r *NotaryRepository) GetRootSigner(rootKeyID, passphrase string) (*UnlockedSigner, error) {
|
||||
// GetRootCryptoService retreives a root key and a cryptoservice to use with it
|
||||
func (r *NotaryRepository) GetRootCryptoService(rootKeyID, passphrase string) (*UnlockedCryptoService, error) {
|
||||
privKey, err := r.rootKeyStore.GetDecryptedKey(rootKeyID, passphrase)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get decrypted root key with keyID: %s, %v", rootKeyID, err)
|
||||
}
|
||||
|
||||
var signer *signed.Signer
|
||||
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)
|
||||
}
|
||||
cryptoService := NewCryptoService("", r.rootKeyStore, passphrase)
|
||||
|
||||
return &UnlockedSigner{
|
||||
privKey: privKey,
|
||||
signer: signer}, nil
|
||||
return &UnlockedCryptoService{
|
||||
privKey: privKey,
|
||||
cryptoService: cryptoService}, nil
|
||||
}
|
||||
|
||||
func (r *NotaryRepository) loadKeys(trustDir, rootKeysDir string) error {
|
||||
|
|
@ -738,35 +732,35 @@ func (r *NotaryRepository) loadKeys(trustDir, rootKeysDir string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ID gets a consistent ID based on the PrivateKey bytes and cipher type
|
||||
func (uk *UnlockedSigner) ID() string {
|
||||
return uk.PublicKey().ID()
|
||||
// ID gets a consistent ID based on the PrivateKey bytes and algorithm type
|
||||
func (ucs *UnlockedCryptoService) ID() string {
|
||||
return ucs.PublicKey().ID()
|
||||
}
|
||||
|
||||
// PublicKey Returns the public key associated with the Private Key within the Signer
|
||||
func (uk *UnlockedSigner) PublicKey() *data.PublicKey {
|
||||
return data.PublicKeyFromPrivate(*uk.privKey)
|
||||
// PublicKey Returns the public key associated with the private key
|
||||
func (ucs *UnlockedCryptoService) PublicKey() *data.PublicKey {
|
||||
return data.PublicKeyFromPrivate(*ucs.privKey)
|
||||
}
|
||||
|
||||
// GenerateCertificate generates an X509 Certificate from a template, given a GUN
|
||||
func (uk *UnlockedSigner) GenerateCertificate(gun string) (*x509.Certificate, error) {
|
||||
cipher := uk.privKey.Cipher()
|
||||
func (ucs *UnlockedCryptoService) GenerateCertificate(gun string) (*x509.Certificate, error) {
|
||||
algorithm := ucs.privKey.Algorithm()
|
||||
var publicKey crypto.PublicKey
|
||||
var privateKey crypto.PrivateKey
|
||||
var err error
|
||||
switch cipher {
|
||||
switch algorithm {
|
||||
case data.RSAKey:
|
||||
var rsaPrivateKey *rsa.PrivateKey
|
||||
rsaPrivateKey, err = x509.ParsePKCS1PrivateKey(uk.privKey.Private())
|
||||
rsaPrivateKey, err = x509.ParsePKCS1PrivateKey(ucs.privKey.Private())
|
||||
privateKey = rsaPrivateKey
|
||||
publicKey = rsaPrivateKey.Public()
|
||||
case data.ECDSAKey:
|
||||
var ecdsaPrivateKey *ecdsa.PrivateKey
|
||||
ecdsaPrivateKey, err = x509.ParseECPrivateKey(uk.privKey.Private())
|
||||
ecdsaPrivateKey, err = x509.ParseECPrivateKey(ucs.privKey.Private())
|
||||
privateKey = ecdsaPrivateKey
|
||||
publicKey = ecdsaPrivateKey.Public()
|
||||
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 {
|
||||
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.
|
||||
// We test this with both an RSA and ECDSA root key
|
||||
func TestInitRepo(t *testing.T) {
|
||||
testInitRepo(t, data.RSAKey)
|
||||
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"
|
||||
// Temporary directory where test files will be created
|
||||
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)
|
||||
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)
|
||||
|
||||
rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase")
|
||||
rootCryptoService, err := repo.GetRootCryptoService(rootKeyID, "passphrase")
|
||||
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)
|
||||
|
||||
// Inspect contents of the temporary directory
|
||||
|
|
@ -98,7 +100,7 @@ func testInitRepo(t *testing.T, rootType string) {
|
|||
// Look for keys in root_keys
|
||||
// There should be a file named after the key ID of the root key we
|
||||
// passed in.
|
||||
rootKeyFilename := rootSigner.ID() + ".key"
|
||||
rootKeyFilename := rootCryptoService.ID() + ".key"
|
||||
_, err = os.Stat(filepath.Join(tempBaseDir, "private", "root_keys", rootKeyFilename))
|
||||
assert.NoError(t, err, "missing root key")
|
||||
|
||||
|
|
@ -184,11 +186,13 @@ type tufChange struct {
|
|||
// internal HTTP server.
|
||||
// We test this with both an RSA and ECDSA root key
|
||||
func TestAddListTarget(t *testing.T) {
|
||||
testAddListTarget(t, data.RSAKey)
|
||||
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
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
|
@ -203,13 +207,13 @@ func testAddListTarget(t *testing.T, rootType string) {
|
|||
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
|
||||
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)
|
||||
|
||||
rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase")
|
||||
rootCryptoService, err := repo.GetRootCryptoService(rootKeyID, "passphrase")
|
||||
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)
|
||||
|
||||
// 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
|
||||
// key is a valid x509 certificate.
|
||||
func TestValidateRootKey(t *testing.T) {
|
||||
testValidateRootKey(t, data.RSAKey)
|
||||
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
|
||||
tempBaseDir, err := ioutil.TempDir("", "notary-test-")
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
|
@ -386,13 +392,13 @@ func testValidateRootKey(t *testing.T, rootType string) {
|
|||
repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport)
|
||||
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)
|
||||
|
||||
rootSigner, err := repo.GetRootSigner(rootKeyID, "passphrase")
|
||||
rootCryptoService, err := repo.GetRootCryptoService(rootKeyID, "passphrase")
|
||||
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)
|
||||
|
||||
rootJSONFile := filepath.Join(tempBaseDir, "tuf", gun, "metadata", "root.json")
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import (
|
|||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/term"
|
||||
notaryclient "github.com/docker/notary/client"
|
||||
"github.com/endophage/gotuf/data"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
|
@ -120,7 +119,7 @@ func tufInit(cmd *cobra.Command, args []string) {
|
|||
if err != nil {
|
||||
fatalf(err.Error())
|
||||
}
|
||||
rootKeyID, err = nRepo.GenRootKey(data.ECDSAKey, passphrase)
|
||||
rootKeyID, err = nRepo.GenRootKey("ECDSA", passphrase)
|
||||
if err != nil {
|
||||
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 {
|
||||
fatalf(err.Error())
|
||||
}
|
||||
|
||||
nRepo.Initialize(rootSigner)
|
||||
nRepo.Initialize(rootCryptoService)
|
||||
if err != nil {
|
||||
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"),
|
||||
}
|
||||
}
|
||||
sign := ctx.Value("signer")
|
||||
signer, ok := sign.(*signed.Signer)
|
||||
cryptoServiceVal := ctx.Value("cryptoService")
|
||||
cryptoService, ok := cryptoServiceVal.(signed.CryptoService)
|
||||
if !ok {
|
||||
return &errors.HTTPError{
|
||||
HTTPStatus: http.StatusInternalServerError,
|
||||
Code: 9999,
|
||||
Err: fmt.Errorf("Signer not configured"),
|
||||
Err: fmt.Errorf("CryptoService not configured"),
|
||||
}
|
||||
}
|
||||
|
||||
vars := mux.Vars(r)
|
||||
gun := vars["imageName"]
|
||||
|
||||
out, err := timestamp.GetOrCreateTimestamp(gun, store, signer)
|
||||
out, err := timestamp.GetOrCreateTimestamp(gun, store, cryptoService)
|
||||
if err != nil {
|
||||
if _, ok := err.(*storage.ErrNoKey); ok {
|
||||
return &errors.HTTPError{
|
||||
|
|
@ -224,7 +224,7 @@ func GetTimestampKeyHandler(ctx context.Context, w http.ResponseWriter, r *http.
|
|||
vars := mux.Vars(r)
|
||||
gun := vars["imageName"]
|
||||
|
||||
key, err := timestamp.GetOrCreateTimestampKey(gun, store, crypto)
|
||||
key, err := timestamp.GetOrCreateTimestampKey(gun, store, crypto, data.ECDSAKey)
|
||||
if err != nil {
|
||||
return &errors.HTTPError{
|
||||
HTTPStatus: http.StatusInternalServerError,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package storage
|
|||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/endophage/gotuf/data"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
|
|
@ -91,10 +92,11 @@ func (db *MySQLStorage) Delete(gun string) error {
|
|||
}
|
||||
|
||||
// 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`=?;"
|
||||
row := db.QueryRow(stmt, gun)
|
||||
|
||||
var cipher string
|
||||
err = row.Scan(&cipher, &public)
|
||||
if err == sql.ErrNoRows {
|
||||
return "", nil, ErrNoKey{gun: gun}
|
||||
|
|
@ -102,13 +104,13 @@ func (db *MySQLStorage) GetTimestampKey(gun string) (cipher string, public []byt
|
|||
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
|
||||
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 (?,?,?);"
|
||||
_, err := db.Exec(stmt, gun, cipher, public)
|
||||
_, err := db.Exec(stmt, gun, string(algorithm), public)
|
||||
if err, ok := err.(*mysql.MySQLError); ok {
|
||||
if err.Number == 1022 { // duplicate key error
|
||||
return &ErrTimestampKeyExists{gun: gun}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package storage
|
||||
|
||||
import "github.com/endophage/gotuf/data"
|
||||
|
||||
// MetaStore holds the methods that are used for a Metadata Store
|
||||
type MetaStore interface {
|
||||
UpdateCurrent(gun, role string, version int, data []byte) error
|
||||
GetCurrent(gun, tufRole string) (data []byte, err error)
|
||||
Delete(gun string) error
|
||||
GetTimestampKey(gun string) (cipher string, public []byte, err error)
|
||||
SetTimestampKey(gun, cipher string, public []byte) error
|
||||
GetTimestampKey(gun string) (algorithm data.KeyAlgorithm, public []byte, err error)
|
||||
SetTimestampKey(gun string, algorithm data.KeyAlgorithm, public []byte) error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/endophage/gotuf/data"
|
||||
)
|
||||
|
||||
type key struct {
|
||||
cipher string
|
||||
public []byte
|
||||
algorithm data.KeyAlgorithm
|
||||
public []byte
|
||||
}
|
||||
|
||||
type ver struct {
|
||||
|
|
@ -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
|
||||
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
|
||||
// wasn't observed
|
||||
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 k.cipher, k.public, nil
|
||||
return k.algorithm, k.public, nil
|
||||
}
|
||||
|
||||
// SetTimestampKey sets a Timestamp key under a gun
|
||||
func (st *MemStorage) SetTimestampKey(gun, cipher string, public []byte) error {
|
||||
k := &key{cipher: cipher, public: public}
|
||||
func (st *MemStorage) SetTimestampKey(gun string, algorithm data.KeyAlgorithm, public []byte) error {
|
||||
k := &key{algorithm: algorithm, public: public}
|
||||
st.lock.Lock()
|
||||
defer st.lock.Unlock()
|
||||
if _, ok := st.tsKeys[gun]; ok {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ func TestGetTimestampKey(t *testing.T) {
|
|||
|
||||
c, k, err := s.GetTimestampKey("gun")
|
||||
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")
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ func TestSetTimestampKey(t *testing.T) {
|
|||
assert.IsType(t, &ErrTimestampKeyExists{}, err, "Expected err to be ErrTimestampKeyExists")
|
||||
|
||||
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")
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,28 +17,28 @@ import (
|
|||
// 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
|
||||
// receives a conflict when writing.
|
||||
func GetOrCreateTimestampKey(gun string, store storage.MetaStore, crypto signed.CryptoService) (*data.TUFKey, error) {
|
||||
cipher, public, err := store.GetTimestampKey(gun)
|
||||
func GetOrCreateTimestampKey(gun string, store storage.MetaStore, crypto signed.CryptoService, fallBackAlgorithm data.KeyAlgorithm) (*data.TUFKey, error) {
|
||||
keyAlgorithm, public, err := store.GetTimestampKey(gun)
|
||||
if err == nil {
|
||||
return data.NewTUFKey(cipher, public, nil), nil
|
||||
return data.NewTUFKey(keyAlgorithm, public, nil), nil
|
||||
}
|
||||
|
||||
if _, ok := err.(*storage.ErrNoKey); ok {
|
||||
key, err := crypto.Create("timestamp")
|
||||
key, err := crypto.Create("timestamp", fallBackAlgorithm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = store.SetTimestampKey(gun, key.Cipher(), key.Public())
|
||||
err = store.SetTimestampKey(gun, key.Algorithm(), key.Public())
|
||||
if err == nil {
|
||||
return &key.TUFKey, nil
|
||||
}
|
||||
|
||||
if _, ok := err.(*storage.ErrTimestampKeyExists); ok {
|
||||
cipher, public, err = store.GetTimestampKey(gun)
|
||||
keyAlgorithm, public, err = store.GetTimestampKey(gun)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data.NewTUFKey(cipher, public, nil), nil
|
||||
return data.NewTUFKey(keyAlgorithm, public, nil), nil
|
||||
}
|
||||
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
|
||||
// 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.
|
||||
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")
|
||||
if err != nil {
|
||||
if _, ok := err.(*storage.ErrNotFound); !ok {
|
||||
|
|
@ -69,7 +69,7 @@ func GetOrCreateTimestamp(gun string, store storage.MetaStore, signer *signed.Si
|
|||
return d, nil
|
||||
}
|
||||
}
|
||||
sgnd, version, err := createTimestamp(gun, ts, store, signer)
|
||||
sgnd, version, err := createTimestamp(gun, ts, store, cryptoService)
|
||||
if err != nil {
|
||||
logrus.Error("Failed to create a new timestamp")
|
||||
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
|
||||
// version number one higher than prev. The store is used to lookup the current
|
||||
// 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) {
|
||||
cipher, public, err := store.GetTimestampKey(gun)
|
||||
func createTimestamp(gun string, prev *data.SignedTimestamp, store storage.MetaStore, cryptoService signed.CryptoService) (*data.Signed, int, error) {
|
||||
algorithm, public, err := store.GetTimestampKey(gun)
|
||||
if err != nil {
|
||||
// owner of gun must have generated a timestamp key otherwise
|
||||
// we won't proceed with generating everything.
|
||||
return nil, 0, err
|
||||
}
|
||||
key := data.NewPublicKey(cipher, public)
|
||||
key := data.NewPublicKey(algorithm, public)
|
||||
snapshot, err := store.GetCurrent(gun, "snapshot")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
|
|
@ -128,7 +128,7 @@ func createTimestamp(gun string, prev *data.SignedTimestamp, store storage.MetaS
|
|||
Signatures: ts.Signatures,
|
||||
Signed: sgndTs,
|
||||
}
|
||||
err = signer.Sign(out, key)
|
||||
err = signed.Sign(cryptoService, out, key)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,11 +32,11 @@ func TestTimestampExpired(t *testing.T) {
|
|||
func TestGetTimestampKey(t *testing.T) {
|
||||
store := storage.NewMemStorage()
|
||||
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.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")
|
||||
|
||||
|
|
@ -48,16 +48,15 @@ func TestGetTimestampKey(t *testing.T) {
|
|||
func TestGetTimestamp(t *testing.T) {
|
||||
store := storage.NewMemStorage()
|
||||
crypto := signed.NewEd25519()
|
||||
signer := signed.NewSigner(crypto)
|
||||
|
||||
snapshot := &data.SignedSnapshot{}
|
||||
snapJSON, _ := json.Marshal(snapshot)
|
||||
|
||||
store.UpdateCurrent("gun", "snapshot", 0, snapJSON)
|
||||
// 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")
|
||||
|
||||
_, err = GetOrCreateTimestamp("gun", store, signer)
|
||||
_, err = GetOrCreateTimestamp("gun", store, crypto)
|
||||
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{
|
||||
KeyID: sig.KeyID.ID,
|
||||
Method: sig.Algorithm.Algorithm,
|
||||
Method: data.SigAlgorithm(sig.Algorithm.Algorithm),
|
||||
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
|
||||
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{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//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
|
||||
}
|
||||
|
||||
|
|
@ -82,7 +83,7 @@ func (trust *RufusSigner) PublicKeys(keyIDs ...string) (map[string]*data.PublicK
|
|||
return nil, err
|
||||
}
|
||||
publicKeys[public.KeyID.ID] =
|
||||
data.NewPublicKey(public.Algorithm.Algorithm, public.PublicKey)
|
||||
data.NewPublicKey(data.KeyAlgorithm(public.Algorithm.Algorithm), public.PublicKey)
|
||||
}
|
||||
return publicKeys, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ func fingerprintCert(cert *x509.Certificate) (CertID, error) {
|
|||
block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
|
||||
pemdata := pem.EncodeToMemory(&block)
|
||||
|
||||
keyType := ""
|
||||
var keyType data.KeyAlgorithm
|
||||
switch cert.PublicKeyAlgorithm {
|
||||
case x509.RSA:
|
||||
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
|
||||
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
|
||||
rsaPubBytes, err := x509.MarshalPKIXPublicKey(&rsaPrivKey.PublicKey)
|
||||
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
|
||||
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
|
||||
ecdsaPubBytes, err := x509.MarshalPKIXPublicKey(&ecdsaPrivKey.PublicKey)
|
||||
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
|
||||
func KeyToPEM(privKey *data.PrivateKey) ([]byte, error) {
|
||||
var pemType string
|
||||
cipher := privKey.Cipher()
|
||||
algorithm := privKey.Algorithm()
|
||||
|
||||
switch cipher {
|
||||
switch algorithm {
|
||||
case data.RSAKey:
|
||||
pemType = "RSA PRIVATE KEY"
|
||||
case data.ECDSAKey:
|
||||
pemType = "EC PRIVATE KEY"
|
||||
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
|
||||
|
|
@ -298,15 +298,15 @@ func KeyToPEM(privKey *data.PrivateKey) ([]byte, error) {
|
|||
// and a passphrase
|
||||
func EncryptPrivateKey(key *data.PrivateKey, passphrase string) ([]byte, error) {
|
||||
var blockType string
|
||||
cipher := key.Cipher()
|
||||
algorithm := key.Algorithm()
|
||||
|
||||
switch cipher {
|
||||
switch algorithm {
|
||||
case data.RSAKey:
|
||||
blockType = "RSA PRIVATE KEY"
|
||||
case data.ECDSAKey:
|
||||
blockType = "EC PRIVATE KEY"
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -48,9 +48,6 @@ func (root *rootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
vars := mux.Vars(r)
|
||||
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, "http.request", r)
|
||||
|
|
|
|||
Loading…
Reference in New Issue