// CreateKey creates a new key inside the cryptoservice for the given role and gun, // returning the public key. If the role is a root role, create an x509 key. func CreateKey(cs signed.CryptoService, gun, role, keyAlgorithm string) (data.PublicKey, error) { key, err := cs.Create(role, gun, keyAlgorithm) if err != nil { return nil, err } if role == data.CanonicalRootRole { start := time.Now().AddDate(0, 0, -1) privKey, _, err := cs.GetPrivateKey(key.ID()) if err != nil { return nil, err } cert, err := cryptoservice.GenerateCertificate( privKey, gun, start, start.AddDate(1, 0, 0), ) if err != nil { return nil, err } // Keep the x509 key type consistent with the key's algorithm switch keyAlgorithm { case data.RSAKey: key = data.NewRSAx509PublicKey(trustmanager.CertToPEM(cert)) case data.ECDSAKey: key = data.NewECDSAx509PublicKey(trustmanager.CertToPEM(cert)) default: // This should be impossible because of the Create() call above, but just in case return nil, fmt.Errorf("invalid key algorithm type") } } return key, nil }
func testValidateSuccessfulRootRotation(t *testing.T, keyAlg, rootKeyType string) { // The gun to test gun := "docker.com/notary" tempBaseDir, keyStoreManager, certs := filestoreWithTwoCerts(t, gun, keyAlg) defer os.RemoveAll(tempBaseDir) origRootCert := certs[0] replRootCert := certs[1] // Add the old root cert part of trustedCertificates keyStoreManager.AddTrustedCert(origRootCert) // We need the PEM representation of the replacement key to put it into the TUF data origRootPEMCert := trustmanager.CertToPEM(origRootCert) replRootPEMCert := trustmanager.CertToPEM(replRootCert) // Tuf key with PEM-encoded x509 certificate origRootKey := data.NewPublicKey(rootKeyType, origRootPEMCert) replRootKey := data.NewPublicKey(rootKeyType, replRootPEMCert) rootRole, err := data.NewRole("root", 1, []string{replRootKey.ID()}, nil, nil) assert.NoError(t, err) testRoot, err := data.NewRoot( map[string]data.PublicKey{replRootKey.ID(): replRootKey}, map[string]*data.RootRole{"root": &rootRole.RootRole}, false, ) assert.NoError(t, err, "Failed to create new root") signedTestRoot, err := testRoot.ToSigned() assert.NoError(t, err) cs := cryptoservice.NewCryptoService(gun, keyStoreManager.KeyStore) err = signed.Sign(cs, signedTestRoot, replRootKey) assert.NoError(t, err) err = signed.Sign(cs, signedTestRoot, origRootKey) assert.NoError(t, err) // // This call to ValidateRoot will succeed since we are using a valid PEM // encoded certificate, and have no other certificates for this CN // err = keyStoreManager.ValidateRoot(signedTestRoot, gun) assert.NoError(t, err) // Finally, validate the only trusted certificate that exists is the new one certs = keyStoreManager.trustedCertificateStore.GetCertificates() assert.Len(t, certs, 1) assert.Equal(t, certs[0], replRootCert) }
func testValidateRootRotationMissingNewSig(t *testing.T, keyAlg, rootKeyType string) { gun := "docker.com/notary" tempBaseDir, certStore, cryptoService, certificates := filestoreWithTwoCerts( t, gun, keyAlg) defer os.RemoveAll(tempBaseDir) origRootCert := certificates[0] replRootCert := certificates[1] // Add the old root cert part of trustedCertificates certStore.AddCert(origRootCert) // We need the PEM representation of the replacement key to put it into the TUF data origRootPEMCert := trustmanager.CertToPEM(origRootCert) replRootPEMCert := trustmanager.CertToPEM(replRootCert) // Tuf key with PEM-encoded x509 certificate origRootKey := data.NewPublicKey(rootKeyType, origRootPEMCert) replRootKey := data.NewPublicKey(rootKeyType, replRootPEMCert) rootRole, err := data.NewRole(data.CanonicalRootRole, 1, []string{replRootKey.ID()}, nil) assert.NoError(t, err) testRoot, err := data.NewRoot( map[string]data.PublicKey{replRootKey.ID(): replRootKey}, map[string]*data.RootRole{data.CanonicalRootRole: &rootRole.RootRole}, false, ) assert.NoError(t, err, "Failed to create new root") signedTestRoot, err := testRoot.ToSigned() assert.NoError(t, err) // We only sign with the old key, and not with the new one err = signed.Sign(cryptoService, signedTestRoot, origRootKey) assert.NoError(t, err) // This call to ValidateRoot will succeed since we are using a valid PEM // encoded certificate, and have no other certificates for this CN err = ValidateRoot(certStore, signedTestRoot, gun) assert.Error(t, err, "insuficient signatures on root") // Finally, validate the only trusted certificate that exists is still // the old one certificates = certStore.GetCertificates() assert.Len(t, certificates, 1) assert.Equal(t, certificates[0], origRootCert) }
// generates a multiple-certificate file with both RSA and ECDSA certs and // returns the filename so that cleanup can be deferred. func generateMultiCert(t *testing.T) string { tempFile, err := ioutil.TempFile("/tmp", "cert-test") defer tempFile.Close() assert.NoError(t, err) rsaKey, err := rsa.GenerateKey(rand.Reader, 2048) assert.NoError(t, err) ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) assert.NoError(t, err) template, err := trustmanager.NewCertificate("gun") assert.NoError(t, err) for _, key := range []crypto.Signer{rsaKey, ecKey} { derBytes, err := x509.CreateCertificate( rand.Reader, template, template, key.Public(), key) assert.NoError(t, err) cert, err := x509.ParseCertificate(derBytes) assert.NoError(t, err) pemBytes := trustmanager.CertToPEM(cert) nBytes, err := tempFile.Write(pemBytes) assert.NoError(t, err) assert.Equal(t, nBytes, len(pemBytes)) } return tempFile.Name() }
func TestAddInvalidShortPubkeyCert(t *testing.T) { // Cleanup after test defer os.RemoveAll(testTrustDir) // Setup certificate tempFile, err := ioutil.TempFile("/tmp", "pemfile") require.NoError(t, err) cert, _, err := generateShortRSAKeyTestCert() _, err = tempFile.Write(trustmanager.CertToPEM(cert)) require.NoError(t, err) tempFile.Close() defer os.Remove(tempFile.Name()) // Setup commander commander := setup() // Should error due to short RSA key err = commander.delegationAdd(commander.GetCommand(), []string{"gun", "targets/delegation", tempFile.Name(), "--paths", "path"}) require.Error(t, err) }
func TestAddInvalidDelegationName(t *testing.T) { // Cleanup after test defer os.RemoveAll(testTrustDir) // Setup certificate tempFile, err := ioutil.TempFile("/tmp", "pemfile") require.NoError(t, err) cert, _, err := generateValidTestCert() _, err = tempFile.Write(trustmanager.CertToPEM(cert)) require.NoError(t, err) tempFile.Close() defer os.Remove(tempFile.Name()) // Setup commander commander := setup() // Should error due to invalid delegation name (should be prefixed by "targets/") err = commander.delegationAdd(commander.GetCommand(), []string{"gun", "INVALID_NAME", tempFile.Name()}) require.Error(t, err) }
func createKey(cs signed.CryptoService, gun, role string) (data.PublicKey, error) { key, err := cs.Create(role, data.ECDSAKey) if err != nil { return nil, err } if role == data.CanonicalRootRole { start := time.Now().AddDate(0, 0, -1) privKey, _, err := cs.GetPrivateKey(key.ID()) if err != nil { return nil, err } cert, err := cryptoservice.GenerateCertificate( privKey, gun, start, start.AddDate(1, 0, 0), ) if err != nil { return nil, err } key = data.NewECDSAx509PublicKey(trustmanager.CertToPEM(cert)) } return key, nil }
// generates a multiple-certificate file with both RSA and ECDSA certs and // returns the filename so that cleanup can be deferred. func generateMultiCert(t *testing.T) string { tempFile, err := ioutil.TempFile("/tmp", "cert-test") defer tempFile.Close() assert.NoError(t, err) rsaKey, err := rsa.GenerateKey(rand.Reader, 2048) assert.NoError(t, err) ecKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) assert.NoError(t, err) for _, key := range []crypto.Signer{rsaKey, ecKey} { cert, err := cryptoservice.GenerateTestingCertificate(key, "gun") assert.NoError(t, err) pemBytes := trustmanager.CertToPEM(cert) nBytes, err := tempFile.Write(pemBytes) assert.NoError(t, err) assert.Equal(t, nBytes, len(pemBytes)) } return tempFile.Name() }
// Initialize creates a new repository by using rootKey as the root Key for the // TUF repository. func (r *NotaryRepository) Initialize(rootKeyID string, serverManagedRoles ...string) error { privKey, _, err := r.CryptoService.GetPrivateKey(rootKeyID) if err != nil { return err } // currently we only support server managing timestamps and snapshots, and // nothing else - timestamps are always managed by the server, and implicit // (do not have to be passed in as part of `serverManagedRoles`, so that // the API of Initialize doens't change). var serverManagesSnapshot bool locallyManagedKeys := []string{ data.CanonicalTargetsRole, data.CanonicalSnapshotRole, // root is also locally managed, but that should have been created // already } remotelyManagedKeys := []string{data.CanonicalTimestampRole} for _, role := range serverManagedRoles { switch role { case data.CanonicalTimestampRole: continue // timestamp is already in the right place case data.CanonicalSnapshotRole: // because we put Snapshot last locallyManagedKeys = []string{data.CanonicalTargetsRole} remotelyManagedKeys = append( remotelyManagedKeys, data.CanonicalSnapshotRole) serverManagesSnapshot = true default: return ErrInvalidRemoteRole{Role: role} } } // Hard-coded policy: the generated certificate expires in 10 years. startTime := time.Now() rootCert, err := cryptoservice.GenerateCertificate( privKey, r.gun, startTime, startTime.AddDate(10, 0, 0)) if err != nil { return err } r.CertManager.AddTrustedCert(rootCert) // The root key gets stored in the TUF metadata X509 encoded, linking // the tuf root.json to our X509 PKI. // 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 rootKey data.PublicKey switch privKey.Algorithm() { case data.RSAKey: rootKey = data.NewRSAx509PublicKey(trustmanager.CertToPEM(rootCert)) case data.ECDSAKey: rootKey = data.NewECDSAx509PublicKey(trustmanager.CertToPEM(rootCert)) default: return fmt.Errorf("invalid format for root key: %s", privKey.Algorithm()) } kdb := keys.NewDB() err = addKeyForRole(kdb, data.CanonicalRootRole, rootKey) if err != nil { return err } // we want to create all the local keys first so we don't have to // make unnecessary network calls for _, role := range locallyManagedKeys { // This is currently hardcoding the keys to ECDSA. key, err := r.CryptoService.Create(role, data.ECDSAKey) if err != nil { return err } if err := addKeyForRole(kdb, role, key); err != nil { return err } } for _, role := range remotelyManagedKeys { // This key is generated by the remote server. key, err := getRemoteKey(r.baseURL, r.gun, role, r.roundTrip) if err != nil { return err } logrus.Debugf("got remote %s %s key with keyID: %s", role, key.Algorithm(), key.ID()) if err := addKeyForRole(kdb, role, key); err != nil { return err } } r.tufRepo = tuf.NewRepo(kdb, r.CryptoService) err = r.tufRepo.InitRoot(false) if err != nil { logrus.Debug("Error on InitRoot: ", err.Error()) return err } _, err = r.tufRepo.InitTargets(data.CanonicalTargetsRole) if err != nil { logrus.Debug("Error on InitTargets: ", err.Error()) return err } err = r.tufRepo.InitSnapshot() if err != nil { logrus.Debug("Error on InitSnapshot: ", err.Error()) return err } return r.saveMetadata(serverManagesSnapshot) }
// Initialize creates a new repository by using rootKey as the root Key for the // TUF repository. func (r *NotaryRepository) Initialize(uCryptoService *cryptoservice.UnlockedCryptoService) error { rootCert, err := uCryptoService.GenerateCertificate(r.gun) if err != nil { return err } r.KeyStoreManager.AddTrustedCert(rootCert) // The root key gets stored in the TUF metadata X509 encoded, linking // the tuf root.json to our X509 PKI. // 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 algorithmType data.KeyAlgorithm algorithm := uCryptoService.PrivKey.Algorithm() switch algorithm { case data.RSAKey: algorithmType = data.RSAx509Key case data.ECDSAKey: algorithmType = data.ECDSAx509Key default: return fmt.Errorf("invalid format for root key: %s", algorithm) } // Generate a x509Key using the rootCert as the public key 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(), uCryptoService.ID()) err = r.KeyStoreManager.RootKeyStore().Link(uCryptoService.ID()+"_root", rootKey.ID()+"_root") if err != nil { return err } // All the timestamp keys are generated by the remote server. remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip) rawTSKey, err := remote.GetKey("timestamp") if err != nil { return err } parsedKey := &data.TUFKey{} err = json.Unmarshal(rawTSKey, parsedKey) if err != nil { return err } // Turn the JSON timestamp key from the remote server into a TUFKey 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.cryptoService.Create("targets", data.ECDSAKey) if err != nil { return err } snapshotKey, err := r.cryptoService.Create("snapshot", data.ECDSAKey) if err != nil { return err } kdb := keys.NewDB() kdb.AddKey(rootKey) kdb.AddKey(targetsKey) kdb.AddKey(snapshotKey) kdb.AddKey(timestampKey) err = initRoles(kdb, rootKey, targetsKey, snapshotKey, timestampKey) if err != nil { return err } r.tufRepo = tuf.NewTufRepo(kdb, r.cryptoService) err = r.tufRepo.InitRoot(false) if err != nil { logrus.Debug("Error on InitRoot: ", err.Error()) switch err.(type) { case tuferrors.ErrInsufficientSignatures, trustmanager.ErrPasswordInvalid: default: return err } } err = r.tufRepo.InitTargets() if err != nil { logrus.Debug("Error on InitTargets: ", err.Error()) return err } err = r.tufRepo.InitSnapshot() if err != nil { logrus.Debug("Error on InitSnapshot: ", err.Error()) return err } return r.saveMetadata(uCryptoService.CryptoService) }
func testValidateRootRotationMissingNewSig(t *testing.T, keyAlg data.KeyAlgorithm, rootKeyType data.KeyAlgorithm) { // Temporary directory where test files will be created tempBaseDir, err := ioutil.TempDir("", "notary-test-") defer os.RemoveAll(tempBaseDir) assert.NoError(t, err, "failed to create a temporary directory: %s", err) // The gun to test gun := "docker.com/notary" // Create a FileStoreManager keyStoreManager, err := NewKeyStoreManager(tempBaseDir, passphraseRetriever) assert.NoError(t, err) origRootKeyID, err := keyStoreManager.GenRootKey(keyAlg.String()) assert.NoError(t, err) replRootKeyID, err := keyStoreManager.GenRootKey(keyAlg.String()) assert.NoError(t, err) origUnlockedCryptoService, err := keyStoreManager.GetRootCryptoService(origRootKeyID) assert.NoError(t, err) replUnlockedCryptoService, err := keyStoreManager.GetRootCryptoService(replRootKeyID) assert.NoError(t, err) // Generating the certificate automatically adds it to the trusted store origRootCert, err := origUnlockedCryptoService.GenerateCertificate(gun) assert.NoError(t, err) // Add the old root cert part of trustedCertificates keyStoreManager.AddTrustedCert(origRootCert) assert.NoError(t, err) // Generate a certificate for our replacement root key replRootCert, err := replUnlockedCryptoService.GenerateCertificate(gun) assert.NoError(t, err) // We need the PEM representation of the replacement key to put it into the TUF data origRootPEMCert := trustmanager.CertToPEM(origRootCert) replRootPEMCert := trustmanager.CertToPEM(replRootCert) // Tuf key with PEM-encoded x509 certificate origRootKey := data.NewPublicKey(rootKeyType, origRootPEMCert) replRootKey := data.NewPublicKey(rootKeyType, replRootPEMCert) // Link both certificates to the original public keys err = keyStoreManager.RootKeyStore().Link(origRootKeyID+"_root", origRootKey.ID()+"_root") assert.NoError(t, err) err = keyStoreManager.RootKeyStore().Link(replRootKeyID+"_root", replRootKey.ID()+"_root") assert.NoError(t, err) rootRole, err := data.NewRole("root", 1, []string{replRootKey.ID()}, nil, nil) assert.NoError(t, err) testRoot, err := data.NewRoot( map[string]data.PublicKey{replRootKey.ID(): replRootKey}, map[string]*data.RootRole{"root": &rootRole.RootRole}, false, ) assert.NoError(t, err, "Failed to create new root") signedTestRoot, err := testRoot.ToSigned() assert.NoError(t, err) // We only sign with the old key, and not with the new one err = signed.Sign(replUnlockedCryptoService.CryptoService, signedTestRoot, origRootKey) assert.NoError(t, err) // // This call to ValidateRoot will succeed since we are using a valid PEM // encoded certificate, and have no other certificates for this CN // err = keyStoreManager.ValidateRoot(signedTestRoot, gun) assert.Error(t, err, "insuficient signatures on root") // Finally, validate the only trusted certificate that exists is still // the old one certs := keyStoreManager.trustedCertificateStore.GetCertificates() assert.Len(t, certs, 1) assert.Equal(t, certs[0], origRootCert) }
// Initialize creates a new repository by using rootKey as the root Key for the // TUF repository. func (r *NotaryRepository) Initialize(rootKeyID string) error { privKey, _, err := r.CryptoService.GetPrivateKey(rootKeyID) if err != nil { return err } rootCert, err := cryptoservice.GenerateCertificate(privKey, r.gun) if err != nil { return err } r.KeyStoreManager.AddTrustedCert(rootCert) // The root key gets stored in the TUF metadata X509 encoded, linking // the tuf root.json to our X509 PKI. // 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 rootKey data.PublicKey switch privKey.Algorithm() { case data.RSAKey: rootKey = data.NewRSAx509PublicKey(trustmanager.CertToPEM(rootCert)) case data.ECDSAKey: rootKey = data.NewECDSAx509PublicKey(trustmanager.CertToPEM(rootCert)) default: return fmt.Errorf("invalid format for root key: %s", privKey.Algorithm()) } // All the timestamp keys are generated by the remote server. remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip) if err != nil { return err } rawTSKey, err := remote.GetKey("timestamp") if err != nil { return err } timestampKey, err := data.UnmarshalPublicKey(rawTSKey) if err != nil { return err } logrus.Debugf("got remote %s timestamp key with keyID: %s", timestampKey.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.CryptoService.Create("targets", data.ECDSAKey) if err != nil { return err } snapshotKey, err := r.CryptoService.Create("snapshot", data.ECDSAKey) if err != nil { return err } kdb := keys.NewDB() kdb.AddKey(rootKey) kdb.AddKey(targetsKey) kdb.AddKey(snapshotKey) kdb.AddKey(timestampKey) err = initRoles(kdb, rootKey, targetsKey, snapshotKey, timestampKey) if err != nil { return err } r.tufRepo = tuf.NewRepo(kdb, r.CryptoService) err = r.tufRepo.InitRoot(false) if err != nil { logrus.Debug("Error on InitRoot: ", err.Error()) switch err.(type) { case tuferrors.ErrInsufficientSignatures, trustmanager.ErrPasswordInvalid: default: return err } } err = r.tufRepo.InitTargets() if err != nil { logrus.Debug("Error on InitTargets: ", err.Error()) return err } err = r.tufRepo.InitSnapshot() if err != nil { logrus.Debug("Error on InitSnapshot: ", err.Error()) return err } return r.saveMetadata() }
// Initialize repo and test publishing targets with delegation roles func TestClientDelegationsPublishing(t *testing.T) { setUp(t) tempDir := tempDirWithConfig(t, "{}") defer os.RemoveAll(tempDir) server := setupServer() defer server.Close() // Setup certificate for delegation role tempFile, err := ioutil.TempFile("", "pemfile") assert.NoError(t, err) privKey, err := trustmanager.GenerateRSAKey(rand.Reader, 2048) assert.NoError(t, err) privKeyBytesNoRole, err := trustmanager.KeyToPEM(privKey, "") assert.NoError(t, err) privKeyBytesWithRole, err := trustmanager.KeyToPEM(privKey, "user") assert.NoError(t, err) startTime := time.Now() endTime := startTime.AddDate(10, 0, 0) cert, err := cryptoservice.GenerateCertificate(privKey, "gun", startTime, endTime) assert.NoError(t, err) _, err = tempFile.Write(trustmanager.CertToPEM(cert)) assert.NoError(t, err) tempFile.Close() defer os.Remove(tempFile.Name()) rawPubBytes, _ := ioutil.ReadFile(tempFile.Name()) parsedPubKey, _ := trustmanager.ParsePEMPublicKey(rawPubBytes) canonicalKeyID, err := utils.CanonicalKeyID(parsedPubKey) assert.NoError(t, err) // Set up targets for publishing tempTargetFile, err := ioutil.TempFile("", "targetfile") assert.NoError(t, err) tempTargetFile.Close() defer os.Remove(tempTargetFile.Name()) var target = "sdgkadga" var output string // init repo _, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - none yet output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, "No delegations present in this repository.") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // validate that we have all keys, including snapshot assertNumKeys(t, tempDir, 1, 2, true) // rotate the snapshot key to server output, err = runCommand(t, tempDir, "-s", server.URL, "key", "rotate", "gun", "-r", "--key-type", "snapshot") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // validate that we lost the snapshot signing key _, signingKeyIDs := assertNumKeys(t, tempDir, 1, 1, true) targetKeyID := signingKeyIDs[0] // add new valid delegation with single new cert output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/releases", tempFile.Name(), "--paths", "\"\"") assert.NoError(t, err) assert.Contains(t, output, "Addition of delegation role") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see our one delegation output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.NotContains(t, output, "No delegations present in this repository.") // remove the targets key to demonstrate that delegates don't need this key keyDir := filepath.Join(tempDir, "private", "tuf_keys") assert.NoError(t, os.Remove(filepath.Join(keyDir, "gun", targetKeyID+".key"))) // Note that we need to use the canonical key ID, followed by the base of the role here err = ioutil.WriteFile(filepath.Join(keyDir, canonicalKeyID+"_releases.key"), privKeyBytesNoRole, 0700) assert.NoError(t, err) // add a target using the delegation -- will only add to targets/releases _, err = runCommand(t, tempDir, "add", "gun", target, tempTargetFile.Name(), "--roles", "targets/releases") assert.NoError(t, err) // list targets for targets/releases - we should see no targets until we publish output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "No targets") output, err = runCommand(t, tempDir, "-s", server.URL, "status", "gun") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list targets for targets/releases - we should see our target! output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "targets/releases") // remove the target for this role only _, err = runCommand(t, tempDir, "remove", "gun", target, "--roles", "targets/releases") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list targets for targets/releases - we should see no targets output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "No targets present") // Try adding a target with a different key style - private/tuf_keys/canonicalKeyID.key with "user" set as the "role" PEM header // First remove the old key and add the new style assert.NoError(t, os.Remove(filepath.Join(keyDir, canonicalKeyID+"_releases.key"))) err = ioutil.WriteFile(filepath.Join(keyDir, canonicalKeyID+".key"), privKeyBytesWithRole, 0700) assert.NoError(t, err) // add a target using the delegation -- will only add to targets/releases _, err = runCommand(t, tempDir, "add", "gun", target, tempTargetFile.Name(), "--roles", "targets/releases") assert.NoError(t, err) // list targets for targets/releases - we should see no targets until we publish output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "No targets") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list targets for targets/releases - we should see our target! output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "targets/releases") // remove the target for this role only _, err = runCommand(t, tempDir, "remove", "gun", target, "--roles", "targets/releases") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // Now remove this key, and make a new file to import the delegation's key from assert.NoError(t, os.Remove(filepath.Join(keyDir, canonicalKeyID+".key"))) tempPrivFile, err := ioutil.TempFile("/tmp", "privfile") assert.NoError(t, err) defer os.Remove(tempPrivFile.Name()) // Write the private key to a file so we can import it _, err = tempPrivFile.Write(privKeyBytesNoRole) assert.NoError(t, err) tempPrivFile.Close() // Import the private key, associating it with our delegation role _, err = runCommand(t, tempDir, "key", "import", tempPrivFile.Name(), "--role", "targets/releases") assert.NoError(t, err) // add a target using the delegation -- will only add to targets/releases _, err = runCommand(t, tempDir, "add", "gun", target, tempTargetFile.Name(), "--roles", "targets/releases") assert.NoError(t, err) // list targets for targets/releases - we should see no targets until we publish output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "No targets") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list targets for targets/releases - we should see our target! output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun", "--roles", "targets/releases") assert.NoError(t, err) assert.Contains(t, output, "targets/releases") }
// Initialize repo and test delegations commands by adding, listing, and removing delegations func TestClientDelegationsInteraction(t *testing.T) { setUp(t) tempDir := tempDirWithConfig(t, "{}") defer os.RemoveAll(tempDir) server := setupServer() defer server.Close() // Setup certificate tempFile, err := ioutil.TempFile("", "pemfile") assert.NoError(t, err) privKey, err := trustmanager.GenerateECDSAKey(rand.Reader) startTime := time.Now() endTime := startTime.AddDate(10, 0, 0) cert, err := cryptoservice.GenerateCertificate(privKey, "gun", startTime, endTime) assert.NoError(t, err) _, err = tempFile.Write(trustmanager.CertToPEM(cert)) assert.NoError(t, err) tempFile.Close() defer os.Remove(tempFile.Name()) rawPubBytes, _ := ioutil.ReadFile(tempFile.Name()) parsedPubKey, _ := trustmanager.ParsePEMPublicKey(rawPubBytes) keyID, err := utils.CanonicalKeyID(parsedPubKey) assert.NoError(t, err) var output string // -- tests -- // init repo _, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - none yet output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, "No delegations present in this repository.") // add new valid delegation with single new cert, and no path output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/delegation", tempFile.Name()) assert.NoError(t, err) assert.Contains(t, output, "Addition of delegation role") assert.NotContains(t, output, "path") // check status - see delegation output, err = runCommand(t, tempDir, "status", "gun") assert.NoError(t, err) assert.Contains(t, output, "Unpublished changes for gun") // list delegations - none yet because still unpublished output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, "No delegations present in this repository.") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // check status - no changelist output, err = runCommand(t, tempDir, "status", "gun") assert.NoError(t, err) assert.Contains(t, output, "No unpublished changes for gun") // list delegations - we should see our added delegation, with no paths output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, "targets/delegation") assert.Contains(t, output, keyID) assert.NotContains(t, output, "\"\"") // add all paths to this delegation output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/delegation", "--all-paths") assert.NoError(t, err) assert.Contains(t, output, "Addition of delegation role") assert.Contains(t, output, "\"\"") assert.Contains(t, output, "<all paths>") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see our added delegation, with no paths output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, "targets/delegation") assert.Contains(t, output, "\"\"") assert.Contains(t, output, "<all paths>") // Setup another certificate tempFile2, err := ioutil.TempFile("", "pemfile2") assert.NoError(t, err) privKey, err = trustmanager.GenerateECDSAKey(rand.Reader) startTime = time.Now() endTime = startTime.AddDate(10, 0, 0) cert, err = cryptoservice.GenerateCertificate(privKey, "gun", startTime, endTime) assert.NoError(t, err) _, err = tempFile2.Write(trustmanager.CertToPEM(cert)) assert.NoError(t, err) assert.NoError(t, err) tempFile2.Close() defer os.Remove(tempFile2.Name()) rawPubBytes2, _ := ioutil.ReadFile(tempFile2.Name()) parsedPubKey2, _ := trustmanager.ParsePEMPublicKey(rawPubBytes2) keyID2, err := utils.CanonicalKeyID(parsedPubKey2) assert.NoError(t, err) // add to the delegation by specifying the same role, this time add a scoped path output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/delegation", tempFile2.Name(), "--paths", "path") assert.NoError(t, err) assert.Contains(t, output, "Addition of delegation role") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see two keys output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, ",") assert.Contains(t, output, "path") assert.Contains(t, output, keyID) assert.Contains(t, output, keyID2) // remove the delegation's first key output, err = runCommand(t, tempDir, "delegation", "remove", "gun", "targets/delegation", keyID) assert.NoError(t, err) assert.Contains(t, output, "Removal of delegation role") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see the delegation but with only the second key output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.NotContains(t, output, keyID) assert.Contains(t, output, keyID2) // remove the delegation's second key output, err = runCommand(t, tempDir, "delegation", "remove", "gun", "targets/delegation", keyID2) assert.NoError(t, err) assert.Contains(t, output, "Removal of delegation role") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see no delegations output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, "No delegations present in this repository.") // add delegation with multiple certs and multiple paths output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/delegation", tempFile.Name(), tempFile2.Name(), "--paths", "path1,path2") assert.NoError(t, err) assert.Contains(t, output, "Addition of delegation role") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see two keys output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, ",") assert.Contains(t, output, "path1,path2") assert.Contains(t, output, keyID) assert.Contains(t, output, keyID2) // add delegation with multiple certs and multiple paths output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/delegation", "--paths", "path3") assert.NoError(t, err) assert.Contains(t, output, "Addition of delegation role") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see two keys output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, ",") assert.Contains(t, output, "path1,path2,path3") // just remove two paths from this delegation output, err = runCommand(t, tempDir, "delegation", "remove", "gun", "targets/delegation", "--paths", "path2,path3") assert.NoError(t, err) assert.Contains(t, output, "Removal of delegation role") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see the same two keys, and only path1 output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, ",") assert.Contains(t, output, "path1") assert.NotContains(t, output, "path2") assert.NotContains(t, output, "path3") assert.Contains(t, output, keyID) assert.Contains(t, output, keyID2) // remove the remaining path, should not remove the delegation entirely output, err = runCommand(t, tempDir, "delegation", "remove", "gun", "targets/delegation", "--paths", "path1") assert.NoError(t, err) assert.Contains(t, output, "Removal of delegation role") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see the same two keys, and no paths output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, ",") assert.NotContains(t, output, "path1") assert.NotContains(t, output, "path2") assert.NotContains(t, output, "path3") assert.Contains(t, output, keyID) assert.Contains(t, output, keyID2) // Add a bunch of individual paths so we can test a delegation remove --all-paths output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/delegation", "--paths", "abcdef,123456") assert.NoError(t, err) // Add more individual paths so we can test a delegation remove --all-paths output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/delegation", "--paths", "banana/split,apple/crumble/pie,orange.peel,kiwi") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see all of our paths output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, "abcdef") assert.Contains(t, output, "123456") assert.Contains(t, output, "banana/split") assert.Contains(t, output, "apple/crumble/pie") assert.Contains(t, output, "orange.peel") assert.Contains(t, output, "kiwi") // Try adding "", and check that adding it with other paths clears out the others output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/delegation", "--paths", "\"\",grapefruit,pomegranate") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see all of our old paths, and "" output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, "abcdef") assert.Contains(t, output, "123456") assert.Contains(t, output, "banana/split") assert.Contains(t, output, "apple/crumble/pie") assert.Contains(t, output, "orange.peel") assert.Contains(t, output, "kiwi") assert.Contains(t, output, "\"\"") assert.NotContains(t, output, "grapefruit") assert.NotContains(t, output, "pomegranate") // Try removing just "" output, err = runCommand(t, tempDir, "delegation", "remove", "gun", "targets/delegation", "--paths", "\"\"") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see all of our old paths without "" output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, "abcdef") assert.Contains(t, output, "123456") assert.Contains(t, output, "banana/split") assert.Contains(t, output, "apple/crumble/pie") assert.Contains(t, output, "orange.peel") assert.Contains(t, output, "kiwi") assert.NotContains(t, output, "\"\"") // Remove --all-paths to clear out all paths from this delegation output, err = runCommand(t, tempDir, "delegation", "remove", "gun", "targets/delegation", "--all-paths") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see all of our paths output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.NotContains(t, output, "abcdef") assert.NotContains(t, output, "123456") assert.NotContains(t, output, "banana/split") assert.NotContains(t, output, "apple/crumble/pie") assert.NotContains(t, output, "orange.peel") assert.NotContains(t, output, "kiwi") // Check that we ignore other --paths if we pass in --all-paths on an add output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/delegation", "--all-paths", "--paths", "grapefruit,pomegranate") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should only see "", and not the other paths specified output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, "\"\"") assert.NotContains(t, output, "grapefruit") assert.NotContains(t, output, "pomegranate") // Add those extra paths we ignored to set up the next test output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/delegation", "--paths", "grapefruit,pomegranate") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // Check that we ignore other --paths if we pass in --all-paths on a remove output, err = runCommand(t, tempDir, "delegation", "remove", "gun", "targets/delegation", "--all-paths", "--paths", "pomegranate") assert.NoError(t, err) // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see no paths output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.NotContains(t, output, "\"\"") assert.NotContains(t, output, "grapefruit") assert.NotContains(t, output, "pomegranate") // remove by force to delete the delegation entirely output, err = runCommand(t, tempDir, "delegation", "remove", "gun", "targets/delegation", "-y") assert.NoError(t, err) assert.Contains(t, output, "Forced removal (including all keys and paths) of delegation role") // publish repo _, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun") assert.NoError(t, err) // list delegations - we should see no delegations output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun") assert.NoError(t, err) assert.Contains(t, output, "No delegations present in this repository.") }
// 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) if err != nil { return err } r.certificateStore.AddCert(rootCert) rootKey := data.NewPublicKey("RSA", trustmanager.CertToPEM(rootCert)) err = r.rootKeyStore.Link(uSigner.ID(), rootKey.ID()) if err != nil { return err } remote, err := getRemoteStore(r.baseURL, r.Gun) rawTSKey, err := remote.GetKey("timestamp") if err != nil { return err } parsedKey := &data.TUFKey{} err = json.Unmarshal(rawTSKey, parsedKey) if err != nil { return err } timestampKey := data.NewPublicKey(parsedKey.Cipher(), parsedKey.Public()) targetsKey, err := r.signer.Create("targets") if err != nil { return err } snapshotKey, err := r.signer.Create("snapshot") if err != nil { return err } kdb := keys.NewDB() kdb.AddKey(rootKey) kdb.AddKey(targetsKey) kdb.AddKey(snapshotKey) kdb.AddKey(timestampKey) rootRole, err := data.NewRole("root", 1, []string{rootKey.ID()}, nil, nil) if err != nil { return err } targetsRole, err := data.NewRole("targets", 1, []string{targetsKey.ID()}, nil, nil) if err != nil { return err } snapshotRole, err := data.NewRole("snapshot", 1, []string{snapshotKey.ID()}, nil, nil) if err != nil { return err } timestampRole, err := data.NewRole("timestamp", 1, []string{timestampKey.ID()}, nil, nil) if err != nil { return err } if err := kdb.AddRole(rootRole); err != nil { return err } if err := kdb.AddRole(targetsRole); err != nil { return err } if err := kdb.AddRole(snapshotRole); err != nil { return err } if err := kdb.AddRole(timestampRole); err != nil { return err } r.tufRepo = tuf.NewTufRepo(kdb, r.signer) r.fileStore, err = store.NewFilesystemStore( r.tufRepoPath, "metadata", "json", "targets", ) if err != nil { return err } if err := r.tufRepo.InitRepo(false); err != nil { return err } if err := r.saveMetadata(uSigner.signer); err != nil { return err } // Creates an empty snapshot return r.snapshot() }