Merge pull request #30 from docker/client-integration-tests

Adding integration tests for notary client.

Signed-off-by: David Lawrence <david.lawrence@docker.com>

Signed-off-by: Ying Li <cyli@users.noreply.github.com> (github: endophage)
This commit is contained in:
Ying Li 2015-11-07 21:05:49 -08:00 committed by David Lawrence
commit 4698ad69f2
9 changed files with 689 additions and 45 deletions

View File

@ -2,7 +2,6 @@ package main
import (
"crypto/x509"
"fmt"
"math"
"os"
"path/filepath"
@ -92,14 +91,14 @@ func certRemove(cmd *cobra.Command, args []string) {
}
// List all the keys about to be removed
fmt.Printf("The following certificates will be removed:\n\n")
cmd.Printf("The following certificates will be removed:\n\n")
for _, cert := range certsToRemove {
// This error can't occur because we're getting certs off of an
// x509 store that indexes by ID.
certID, _ := trustmanager.FingerprintCert(cert)
fmt.Printf("%s - %s\n", cert.Subject.CommonName, certID)
cmd.Printf("%s - %s\n", cert.Subject.CommonName, certID)
}
fmt.Println("\nAre you sure you want to remove these certificates? (yes/no)")
cmd.Println("\nAre you sure you want to remove these certificates? (yes/no)")
// Ask for confirmation before removing certificates, unless -y is provided
if !certRemoveYes {
@ -136,20 +135,20 @@ func certList(cmd *cobra.Command, args []string) {
fatalf("failed to create a new truststore manager with directory: %s", trustDir)
}
fmt.Println("")
fmt.Println("# Trusted Certificates:")
cmd.Println("")
cmd.Println("# Trusted Certificates:")
trustedCerts := keyStoreManager.TrustedCertificateStore().GetCertificates()
for _, c := range trustedCerts {
printCert(c)
printCert(cmd, c)
}
}
func printCert(cert *x509.Certificate) {
func printCert(cmd *cobra.Command, cert *x509.Certificate) {
timeDifference := cert.NotAfter.Sub(time.Now())
certID, err := trustmanager.FingerprintCert(cert)
if err != nil {
fatalf("could not fingerprint certificate: %v", err)
}
fmt.Printf("%s %s (expires in: %v days)\n", cert.Subject.CommonName, certID, math.Floor(timeDifference.Hours()/24))
cmd.Printf("%s %s (expires in: %v days)\n", cert.Subject.CommonName, certID, math.Floor(timeDifference.Hours()/24))
}

View File

@ -0,0 +1,34 @@
// +build !pkcs11
package main
import (
"testing"
"github.com/docker/notary/passphrase"
)
func rootOnHardware() bool {
return false
}
// Per-test set up that returns a cleanup function. This set up changes the
// passphrase retriever to always produce a constant passphrase
func setUp(t *testing.T) func() {
oldRetriever := retriever
var fake = func(k, a string, c bool, n int) (string, bool, error) {
return testPassphrase, false, nil
}
retriever = fake
getRetriever = func() passphrase.Retriever { return fake }
return func() {
retriever = oldRetriever
getRetriever = getPassphraseRetriever
}
}
// no-op
func verifyRootKeyOnHardware(t *testing.T, rootKeyID string) {}

View File

@ -0,0 +1,63 @@
// +build pkcs11
package main
import (
"testing"
"github.com/docker/notary/passphrase"
"github.com/docker/notary/signer/api"
"github.com/docker/notary/tuf/data"
"github.com/stretchr/testify/assert"
)
var rootOnHardware = api.YubikeyAccessible
// Per-test set up that returns a cleanup function. This set up:
// - changes the passphrase retriever to always produce a constant passphrase
// - disables touch on yubikeys
// - deletes all keys on the yubikey
func setUp(t *testing.T) func() {
oldRetriever := retriever
var fake = func(k, a string, c bool, n int) (string, bool, error) {
if k == "Yubikey" {
return oldRetriever(k, a, c, n)
}
return testPassphrase, false, nil
}
retriever = fake
getRetriever = func() passphrase.Retriever { return fake }
api.SetYubikeyKeyMode(api.KeymodeNone)
// //we're just removing keys here, so nil is fine
s, err := api.NewYubiKeyStore(nil, retriever)
assert.NoError(t, err)
for k := range s.ListKeys() {
err := s.RemoveKey(k)
assert.NoError(t, err)
}
return func() {
retriever = oldRetriever
getRetriever = getPassphraseRetriever
api.SetYubikeyKeyMode(api.KeymodeTouch | api.KeymodePinOnce)
}
}
// ensures that the root is actually on the yubikey - this makes sure the
// commands are hooked up to interact with the yubikey, rather than right files
// on disk
func verifyRootKeyOnHardware(t *testing.T, rootKeyID string) {
// do not bother verifying if there is no yubikey available
if api.YubikeyAccessible() {
// //we're just getting keys here, so nil is fine
s, err := api.NewYubiKeyStore(nil, retriever)
assert.NoError(t, err)
privKey, role, err := s.GetKey(rootKeyID)
assert.NoError(t, err)
assert.NotNil(t, privKey)
assert.Equal(t, data.CanonicalRootRole, role)
}
}

View File

@ -0,0 +1,513 @@
// Actually start up a notary server and run through basic TUF and key
// interactions via the client.
// Note - if using Yubikey, retrieving pins/touch doesn't seem to work right
// when running in the midst of all tests.
package main
import (
"bytes"
"crypto/rand"
"io/ioutil"
"net/http/httptest"
"os"
"path/filepath"
"sort"
"strings"
"testing"
"github.com/Sirupsen/logrus"
ctxu "github.com/docker/distribution/context"
"github.com/docker/notary/cryptoservice"
"github.com/docker/notary/server"
"github.com/docker/notary/server/storage"
"github.com/docker/notary/trustmanager"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"golang.org/x/net/context"
)
var cmd = &cobra.Command{}
var testPassphrase = "passphrase"
// run a command and return the output as a string
func runCommand(t *testing.T, tempDir string, args ...string) (string, error) {
b := new(bytes.Buffer)
cmd.SetArgs(append([]string{"-c", "/tmp/ignore.json", "-d", tempDir}, args...))
cmd.SetOutput(b)
t.Logf("Running `notary %s`", strings.Join(args, " "))
retErr := cmd.Execute()
output, err := ioutil.ReadAll(b)
assert.NoError(t, err)
return string(output), retErr
}
// makes a testing notary-server
func setupServer() *httptest.Server {
// Set up server
ctx := context.WithValue(
context.Background(), "metaStore", storage.NewMemStorage())
// Do not pass one of the const KeyAlgorithms here as the value! Passing a
// string is in itself good test that we are handling it correctly as we
// will be receiving a string from the configuration.
ctx = context.WithValue(ctx, "keyAlgorithm", "ecdsa")
// Eat the logs instead of spewing them out
var b bytes.Buffer
l := logrus.New()
l.Out = &b
ctx = ctxu.WithLogger(ctx, logrus.NewEntry(l))
cryptoService := cryptoservice.NewCryptoService(
"", trustmanager.NewKeyMemoryStore(retriever))
return httptest.NewServer(server.RootHandler(nil, ctx, cryptoService))
}
// Initializes a repo, adds a target, publishes the target, lists the target,
// verifies the target, and then removes the target.
func TestClientTufInteraction(t *testing.T) {
// -- setup --
cleanup := setUp(t)
defer cleanup()
tempDir, err := ioutil.TempDir("/tmp", "repo")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)
server := setupServer()
defer server.Close()
tempFile, err := ioutil.TempFile("/tmp", "targetfile")
assert.NoError(t, err)
tempFile.Close()
defer os.Remove(tempFile.Name())
var (
output string
target = "sdgkadga"
)
// -- tests --
// init repo
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun")
assert.NoError(t, err)
// add a target
_, err = runCommand(t, tempDir, "add", "gun", target, tempFile.Name())
assert.NoError(t, err)
// check status - see target
output, err = runCommand(t, tempDir, "status", "gun")
assert.NoError(t, err)
assert.True(t, strings.Contains(output, target))
// publish repo
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
assert.NoError(t, err)
// check status - no targets
output, err = runCommand(t, tempDir, "status", "gun")
assert.NoError(t, err)
assert.False(t, strings.Contains(string(output), target))
// list repo - see target
output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun")
assert.NoError(t, err)
assert.True(t, strings.Contains(string(output), target))
// verify repo - empty file
output, err = runCommand(t, tempDir, "verify", "gun", target)
assert.NoError(t, err)
// remove target
_, err = runCommand(t, tempDir, "remove", "gun", target)
assert.NoError(t, err)
// publish repo
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
assert.NoError(t, err)
// list repo - don't see target
output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun")
assert.NoError(t, err)
assert.False(t, strings.Contains(string(output), target))
}
// Splits a string into lines, and returns any lines that are not empty (
// striped of whitespace)
func splitLines(chunk string) []string {
splitted := strings.Split(strings.TrimSpace(chunk), "\n")
var results []string
for _, line := range splitted {
line := strings.TrimSpace(line)
if line != "" {
results = append(results, line)
}
}
return results
}
// List keys, parses the output, and returns the keys as an array of root key
// IDs and an array of signing key IDs
func GetKeys(t *testing.T, tempDir string) ([]string, []string) {
output, err := runCommand(t, tempDir, "key", "list")
assert.NoError(t, err)
parts := strings.Split(output, "# Signing keys:")
assert.Len(t, parts, 2)
fixed := make([][]string, 2)
for i, part := range parts {
fixed[i] = splitLines(
strings.TrimPrefix(strings.TrimSpace(part), "# Root keys:"))
sort.Strings(fixed[i])
}
return fixed[0], fixed[1]
}
// List keys, parses the output, and asserts something about the number of root
// keys and number of signing keys, as well as returning them.
func assertNumKeys(t *testing.T, tempDir string, numRoot, numSigning int) (
[]string, []string) {
root, signing := GetKeys(t, tempDir)
assert.Len(t, root, numRoot)
assert.Len(t, signing, numSigning)
for _, rootKeyID := range root {
// it should always be present on disk
_, err := os.Stat(filepath.Join(
tempDir, "private", "root_keys", rootKeyID+"_root.key"))
assert.NoError(t, err)
// this function is declared is in the build-tagged setup files
verifyRootKeyOnHardware(t, rootKeyID)
}
return root, signing
}
// Adds the given target to the gun, publishes it, and lists it to ensure that
// it appears. Returns the listing output.
func assertSuccessfullyPublish(
t *testing.T, tempDir, url, gun, target, fname string) string {
_, err := runCommand(t, tempDir, "add", gun, target, fname)
assert.NoError(t, err)
_, err = runCommand(t, tempDir, "-s", url, "publish", gun)
assert.NoError(t, err)
output, err := runCommand(t, tempDir, "-s", url, "list", gun)
assert.NoError(t, err)
assert.True(t, strings.Contains(string(output), target))
return output
}
// Tests root key generation and key rotation
func TestClientKeyGenerationRotation(t *testing.T) {
// -- setup --
cleanup := setUp(t)
defer cleanup()
tempDir, err := ioutil.TempDir("/tmp", "repo")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)
tempfiles := make([]string, 2)
for i := 0; i < 2; i++ {
tempFile, err := ioutil.TempFile("/tmp", "targetfile")
assert.NoError(t, err)
tempFile.Close()
tempfiles[i] = tempFile.Name()
defer os.Remove(tempFile.Name())
}
server := setupServer()
defer server.Close()
var target = "sdgkadga"
// -- tests --
// starts out with no keys
assertNumKeys(t, tempDir, 0, 0)
// generate root key produces a single root key and no other keys
_, err = runCommand(t, tempDir, "key", "generate", "ecdsa")
assert.NoError(t, err)
assertNumKeys(t, tempDir, 1, 0)
// initialize a repo, should have signing keys and no new root key
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun")
assert.NoError(t, err)
origRoot, origSign := assertNumKeys(t, tempDir, 1, 2)
// publish using the original keys
assertSuccessfullyPublish(t, tempDir, server.URL, "gun", target, tempfiles[0])
// rotate the signing keys
_, err = runCommand(t, tempDir, "key", "rotate", "gun")
assert.NoError(t, err)
root, sign := assertNumKeys(t, tempDir, 1, 4)
assert.Equal(t, origRoot[0], root[0])
// there should be the new keys and the old keys
for _, origKey := range origSign {
found := false
for _, key := range sign {
if key == origKey {
found = true
}
}
assert.True(t, found, "Old key not found in list of old and new keys")
}
// publish the key rotation
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
assert.NoError(t, err)
root, sign = assertNumKeys(t, tempDir, 1, 2)
assert.Equal(t, origRoot[0], root[0])
// just do a cursory rotation check that the keys aren't equal anymore
for _, origKey := range origSign {
for _, key := range sign {
assert.NotEqual(
t, key, origKey, "One of the signing keys was not removed")
}
}
// publish using the new keys
output := assertSuccessfullyPublish(
t, tempDir, server.URL, "gun", target+"2", tempfiles[1])
// assert that the previous target is sitll there
assert.True(t, strings.Contains(string(output), target))
}
// Tests import/export root+signing keys - repo with imported keys should be
// able to publish successfully
func TestClientKeyImportExportRootAndSigning(t *testing.T) {
// -- setup --
cleanup := setUp(t)
defer cleanup()
dirs := make([]string, 3)
for i := 0; i < 3; i++ {
tempDir, err := ioutil.TempDir("/tmp", "repo")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)
dirs[i] = tempDir
}
tempfiles := make([]string, 2)
for i := 0; i < 2; i++ {
tempFile, err := ioutil.TempFile("/tmp", "tempfile")
assert.NoError(t, err)
tempFile.Close()
tempfiles[i] = tempFile.Name()
defer os.Remove(tempFile.Name())
}
server := setupServer()
defer server.Close()
var (
target = "sdgkadga"
err error
)
// create two repos and publish a target
for _, gun := range []string{"gun1", "gun2"} {
_, err = runCommand(t, dirs[0], "-s", server.URL, "init", gun)
assert.NoError(t, err)
assertSuccessfullyPublish(
t, dirs[0], server.URL, gun, target, tempfiles[0])
}
assertNumKeys(t, dirs[0], 1, 4)
// -- tests --
zipfile := tempfiles[0] + ".zip"
defer os.Remove(zipfile)
// export then import all keys
_, err = runCommand(t, dirs[0], "key", "export", zipfile)
assert.NoError(t, err)
_, err = runCommand(t, dirs[1], "key", "import", zipfile)
assert.NoError(t, err)
assertNumKeys(t, dirs[1], 1, 4) // all keys should be there
// can list and publish to both repos using imported keys
for _, gun := range []string{"gun1", "gun2"} {
output, err := runCommand(t, dirs[1], "-s", server.URL, "list", gun)
assert.NoError(t, err)
assert.True(t, strings.Contains(string(output), target))
assertSuccessfullyPublish(
t, dirs[1], server.URL, gun, target+"2", tempfiles[1])
}
// export then import keys for one gun
_, err = runCommand(t, dirs[0], "key", "export", zipfile, "-g", "gun1")
assert.NoError(t, err)
_, err = runCommand(t, dirs[2], "key", "import", zipfile)
assert.NoError(t, err)
// this function is declared is in the build-tagged setup files
if rootOnHardware() {
// hardware root is still present, but two signing keys moved - we
// can't call assertNumKeys, because in this case it will ONLY be on
// hardware and not on disk
root, signing := GetKeys(t, dirs[2])
assert.Len(t, root, 1)
verifyRootKeyOnHardware(t, root[0])
assert.Len(t, signing, 2)
} else {
// only 2 signing keys should be there, and no root key
assertNumKeys(t, dirs[2], 0, 2)
}
}
// Generate a root key and export the root key only. Return the key ID
// exported.
func exportRoot(t *testing.T, exportTo string) string {
tempDir, err := ioutil.TempDir("/tmp", "repo")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)
// generate root key produces a single root key and no other keys
_, err = runCommand(t, tempDir, "key", "generate", "ecdsa")
assert.NoError(t, err)
oldRoot, _ := assertNumKeys(t, tempDir, 1, 0)
// export does not require a password
oldRetriever := retriever
retriever = nil
defer func() { // but import will, later
retriever = oldRetriever
}()
_, err = runCommand(
t, tempDir, "key", "export-root", oldRoot[0], exportTo)
assert.NoError(t, err)
return oldRoot[0]
}
// Tests import/export root key only
func TestClientKeyImportExportRootOnly(t *testing.T) {
// -- setup --
cleanup := setUp(t)
defer cleanup()
tempDir, err := ioutil.TempDir("/tmp", "repo")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)
server := setupServer()
defer server.Close()
var (
target = "sdgkadga"
rootKeyID string
)
tempFile, err := ioutil.TempFile("/tmp", "pemfile")
assert.NoError(t, err)
// close later, because we might need to write to it
defer os.Remove(tempFile.Name())
// -- tests --
if rootOnHardware() {
t.Log("Cannot export a key from hardware. Will generate one to import.")
privKey, err := trustmanager.GenerateECDSAKey(rand.Reader)
assert.NoError(t, err)
pemBytes, err := trustmanager.EncryptPrivateKey(privKey, testPassphrase)
assert.NoError(t, err)
nBytes, err := tempFile.Write(pemBytes)
assert.NoError(t, err)
tempFile.Close()
assert.Equal(t, len(pemBytes), nBytes)
rootKeyID = privKey.ID()
} else {
tempFile.Close()
rootKeyID = exportRoot(t, tempFile.Name())
}
// import the key
_, err = runCommand(t, tempDir, "key", "import-root", tempFile.Name())
assert.NoError(t, err)
newRoot, _ := assertNumKeys(t, tempDir, 1, 0)
assert.Equal(t, rootKeyID, newRoot[0])
// Just to make sure, init a repo and publish
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun")
assert.NoError(t, err)
assertNumKeys(t, tempDir, 1, 2)
assertSuccessfullyPublish(
t, tempDir, server.URL, "gun", target, tempFile.Name())
}
func assertNumCerts(t *testing.T, tempDir string, expectedNum int) []string {
output, err := runCommand(t, tempDir, "cert", "list")
assert.NoError(t, err)
certs := splitLines(
strings.TrimPrefix(strings.TrimSpace(output), "# Trusted Certificates:"))
assert.Len(t, certs, expectedNum)
return certs
}
// TestClientCertInteraction
func TestClientCertInteraction(t *testing.T) {
// -- setup --
cleanup := setUp(t)
defer cleanup()
tempDir, err := ioutil.TempDir("/tmp", "repo")
assert.NoError(t, err)
defer os.RemoveAll(tempDir)
server := setupServer()
defer server.Close()
// -- tests --
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun1")
assert.NoError(t, err)
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun2")
assert.NoError(t, err)
certs := assertNumCerts(t, tempDir, 2)
// remove certs for one gun
_, err = runCommand(t, tempDir, "cert", "remove", "-g", "gun1", "-y")
assert.NoError(t, err)
certs = assertNumCerts(t, tempDir, 1)
// remove a single cert
certID := strings.TrimSpace(strings.Split(certs[0], " ")[1])
// passing an empty gun here because the string for the previous gun has
// has already been stored (a drawback of running these commands without)
// shelling out
_, err = runCommand(t, tempDir, "cert", "remove", certID, "-y", "-g", "")
assert.NoError(t, err)
assertNumCerts(t, tempDir, 0)
}
func TestMain(m *testing.M) {
if testing.Short() {
// skip
os.Exit(0)
}
setupCommand(cmd)
os.Exit(m.Run())
}

View File

@ -2,7 +2,6 @@ package main
import (
"archive/zip"
"fmt"
"os"
"path/filepath"
"sort"
@ -11,7 +10,6 @@ import (
"github.com/docker/notary"
notaryclient "github.com/docker/notary/client"
"github.com/docker/notary/cryptoservice"
"github.com/docker/notary/passphrase"
"github.com/docker/notary/signer/api"
"github.com/docker/notary/trustmanager"
@ -116,16 +114,16 @@ func keysList(cmd *cobra.Command, args []string) {
// Get a map of all the keys/roles
keysMap := cs.ListAllKeys()
fmt.Println("")
fmt.Println("# Root keys: ")
cmd.Println("")
cmd.Println("# Root keys: ")
for k, v := range keysMap {
if v == "root" {
fmt.Println(k)
cmd.Println(k)
}
}
fmt.Println("")
fmt.Println("# Signing keys: ")
cmd.Println("")
cmd.Println("# Signing keys: ")
// Get a list of all the keys
var sortedKeys []string
@ -138,7 +136,7 @@ func keysList(cmd *cobra.Command, args []string) {
// Print a sorted list of the key/role
for _, k := range sortedKeys {
if keysMap[k] != "root" {
printKey(k, keysMap[k])
printKey(cmd, k, keysMap[k])
}
}
}
@ -180,7 +178,7 @@ func keysGenerateRootKey(cmd *cobra.Command, args []string) {
fatalf("failed to create a new root key: %v", err)
}
fmt.Printf("Generated new %s root key with keyID: %s\n", algorithm, pubKey.ID())
cmd.Printf("Generated new %s root key with keyID: %s\n", algorithm, pubKey.ID())
}
// keysExport exports a collection of keys to a ZIP file
@ -208,7 +206,7 @@ func keysExport(cmd *cobra.Command, args []string) {
// Must use a different passphrase retriever to avoid caching the
// unlocking passphrase and reusing that.
exportRetriever := passphrase.PromptRetriever()
exportRetriever := getRetriever()
if keysExportGUN != "" {
err = cs.ExportKeysByGUN(exportFile, keysExportGUN, exportRetriever)
} else {
@ -253,7 +251,7 @@ func keysExportRoot(cmd *cobra.Command, args []string) {
if keysExportRootChangePassphrase {
// Must use a different passphrase retriever to avoid caching the
// unlocking passphrase and reusing that.
exportRetriever := passphrase.PromptRetriever()
exportRetriever := getRetriever()
err = cs.ExportRootKeyReencrypt(exportFile, keyID, exportRetriever)
} else {
err = cs.ExportRootKey(exportFile, keyID)
@ -334,10 +332,10 @@ func keysImportRoot(cmd *cobra.Command, args []string) {
}
}
func printKey(keyPath, alias string) {
func printKey(cmd *cobra.Command, keyPath, alias string) {
keyID := filepath.Base(keyPath)
gun := filepath.Dir(keyPath)
fmt.Printf("%s - %s - %s\n", gun, alias, keyID)
cmd.Printf("%s - %s - %s\n", gun, alias, keyID)
}
func keysRotate(cmd *cobra.Command, args []string) {

View File

@ -30,6 +30,7 @@ var (
configFileName = "config"
configFileExt = "json"
retriever passphrase.Retriever
getRetriever = getPassphraseRetriever
mainViper = viper.New()
)
@ -85,13 +86,7 @@ func parseConfig() {
}
}
func main() {
var notaryCmd = &cobra.Command{
Use: "notary",
Short: "notary allows the creation of trusted collections.",
Long: "notary allows the creation and management of collections of signed targets, allowing the signing and validation of arbitrary content.",
}
func setupCommand(notaryCmd *cobra.Command) {
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number of notary",
@ -124,7 +119,15 @@ func main() {
cmdTufLookup.Flags().StringVarP(&remoteTrustServer, "server", "s", "", "Remote trust server location")
notaryCmd.AddCommand(cmdVerify)
cmdVerify.Flags().StringVarP(&remoteTrustServer, "server", "s", "", "Remote trust server location")
}
func main() {
var notaryCmd = &cobra.Command{
Use: "notary",
Short: "notary allows the creation of trusted collections.",
Long: "notary allows the creation and management of collections of signed targets, allowing the signing and validation of arbitrary content.",
}
setupCommand(notaryCmd)
notaryCmd.Execute()
}

View File

@ -107,7 +107,7 @@ func tufAdd(cmd *cobra.Command, args []string) {
if err != nil {
fatalf(err.Error())
}
fmt.Printf("Addition of %s to %s staged for next publish.\n", targetName, gun)
cmd.Printf("Addition of %s to %s staged for next publish.\n", targetName, gun)
}
func tufInit(cmd *cobra.Command, args []string) {
@ -128,7 +128,7 @@ func tufInit(cmd *cobra.Command, args []string) {
var rootKeyID string
if len(rootKeyList) < 1 {
fmt.Println("No root keys found. Generating a new root key...")
cmd.Println("No root keys found. Generating a new root key...")
rootPublicKey, err := nRepo.CryptoService.Create(data.CanonicalRootRole, data.ECDSAKey)
rootKeyID = rootPublicKey.ID()
if err != nil {
@ -138,7 +138,7 @@ func tufInit(cmd *cobra.Command, args []string) {
// Choses the first root key available, which is initialization specific
// but should return the HW one first.
rootKeyID = rootKeyList[0]
fmt.Printf("Root key found, using: %s\n", rootKeyID)
cmd.Printf("Root key found, using: %s\n", rootKeyID)
}
err = nRepo.Initialize(rootKeyID)
@ -168,7 +168,7 @@ func tufList(cmd *cobra.Command, args []string) {
// Print all the available targets
for _, t := range targetList {
fmt.Printf("%s %x %d\n", t.Name, t.Hashes["sha256"], t.Length)
cmd.Printf("%s %x %d\n", t.Name, t.Hashes["sha256"], t.Length)
}
}
@ -191,7 +191,7 @@ func tufLookup(cmd *cobra.Command, args []string) {
fatalf(err.Error())
}
fmt.Println(target.Name, fmt.Sprintf("sha256:%x", target.Hashes["sha256"]), target.Length)
cmd.Println(target.Name, fmt.Sprintf("sha256:%x", target.Hashes["sha256"]), target.Length)
}
func tufStatus(cmd *cobra.Command, args []string) {
@ -214,15 +214,15 @@ func tufStatus(cmd *cobra.Command, args []string) {
}
if len(cl.List()) == 0 {
fmt.Printf("No unpublished changes for %s\n", gun)
cmd.Printf("No unpublished changes for %s\n", gun)
return
}
fmt.Printf("Unpublished changes for %s:\n\n", gun)
fmt.Printf("%-10s%-10s%-12s%s\n", "action", "scope", "type", "path")
fmt.Println("----------------------------------------------------")
cmd.Printf("Unpublished changes for %s:\n\n", gun)
cmd.Printf("%-10s%-10s%-12s%s\n", "action", "scope", "type", "path")
cmd.Println("----------------------------------------------------")
for _, ch := range cl.List() {
fmt.Printf("%-10s%-10s%-12s%s\n", ch.Action(), ch.Scope(), ch.Type(), ch.Path())
cmd.Printf("%-10s%-10s%-12s%s\n", ch.Action(), ch.Scope(), ch.Type(), ch.Path())
}
}
@ -235,7 +235,7 @@ func tufPublish(cmd *cobra.Command, args []string) {
gun := args[0]
parseConfig()
fmt.Println("Pushing changes to ", gun, ".")
cmd.Println("Pushing changes to", gun)
nRepo, err := notaryclient.NewNotaryRepository(trustDir, gun, getRemoteTrustServer(), getTransport(gun, false), retriever)
if err != nil {
@ -268,7 +268,7 @@ func tufRemove(cmd *cobra.Command, args []string) {
fatalf(err.Error())
}
fmt.Printf("Removal of %s from %s staged for next publish.\n", targetName, gun)
cmd.Printf("Removal of %s from %s staged for next publish.\n", targetName, gun)
}
func verify(cmd *cobra.Command, args []string) {

View File

@ -25,8 +25,29 @@ const (
USER_PIN = "123456"
SO_USER_PIN = "010203040506070801020304050607080102030405060708"
numSlots = 4 // number of slots in the yubikey
KeymodeNone = 0
KeymodeTouch = 1 // touch enabled
KeymodePinOnce = 2 // require pin entry once
KeymodePinAlways = 4 // require pin entry all the time
)
// what key mode to use when generating keys
var yubikeyKeymode = KeymodeTouch | KeymodePinOnce
// SetYubikeyKeyMode - sets the mode when generating yubikey keys.
// This is to be used for testing. It does nothing if not building with tag
// pkcs11.
func SetYubikeyKeyMode(keyMode int) error {
// technically 7 (1 | 2 | 4) is valid, but KeymodePinOnce +
// KeymdoePinAlways don't really make sense together
if keyMode < 0 || keyMode > 5 {
return errors.New("Invalid key mode")
}
yubikeyKeymode = keyMode
return nil
}
// Hardcoded yubikey PKCS11 ID
var YUBIKEY_ROOT_KEY_ID = []byte{2}
@ -153,10 +174,7 @@ func addECDSAKey(
pkcs11.NewAttribute(pkcs11.CKA_ID, pkcs11KeyID),
pkcs11.NewAttribute(pkcs11.CKA_EC_PARAMS, []byte{0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07}),
pkcs11.NewAttribute(pkcs11.CKA_VALUE, ecdsaPrivKeyD),
// 1 is touch enabled
// 2 is pin once
// 4 is pin always
pkcs11.NewAttribute(pkcs11.CKA_VENDOR_DEFINED, 3),
pkcs11.NewAttribute(pkcs11.CKA_VENDOR_DEFINED, yubikeyKeymode),
}
_, err = ctx.CreateObject(session, certTemplate)
@ -641,6 +659,16 @@ func SetupHSMEnv(libraryPath string) (*pkcs11.Ctx, pkcs11.SessionHandle, error)
return p, session, nil
}
// YubikeyEnabled returns true if a Yubikey can be accessed
func YubikeyAccessible() bool {
ctx, session, err := SetupHSMEnv(pkcs11Lib)
if err != nil {
return false
}
defer cleanup(ctx, session)
return true
}
func login(ctx *pkcs11.Ctx, session pkcs11.SessionHandle, passRetriever passphrase.Retriever, userFlag uint, defaultPassw string) error {
// try default password
err := ctx.Login(session, userFlag, defaultPassw)

View File

@ -146,6 +146,12 @@ func (tr *Repo) RemoveBaseKeys(role string, keyIDs ...string) error {
// remove keys no longer in use by any roles
for k := range toDelete {
delete(tr.Root.Signed.Keys, k)
// remove the signing key from the cryptoservice if it
// isn't a root key. Root keys must be kept for rotation
// signing
if role != data.CanonicalRootRole {
tr.cryptoService.RemoveKey(k)
}
}
tr.Root.Dirty = true
return nil