예제 #1
0
// VerifyCanSign returns nil if the role exists and we have at least one
// signing key for the role, false otherwise.  This does not check that we have
// enough signing keys to meet the threshold, since we want to support the use
// case of multiple signers for a role.  It returns an error if the role doesn't
// exist or if there are no signing keys.
func (tr *Repo) VerifyCanSign(roleName string) error {
	var (
		role data.BaseRole
		err  error
	)
	// we only need the BaseRole part of a delegation because we're just
	// checking KeyIDs
	if data.IsDelegation(roleName) {
		r, err := tr.GetDelegationRole(roleName)
		if err != nil {
			return err
		}
		role = r.BaseRole
	} else {
		role, err = tr.GetBaseRole(roleName)
	}
	if err != nil {
		return data.ErrInvalidRole{Role: roleName, Reason: "does not exist"}
	}

	for keyID, k := range role.Keys {
		check := []string{keyID}
		if canonicalID, err := utils.CanonicalKeyID(k); err == nil {
			check = append(check, canonicalID)
		}
		for _, id := range check {
			p, _, err := tr.cryptoService.GetPrivateKey(id)
			if err == nil && p != nil {
				return nil
			}
		}
	}
	return signed.ErrNoKeys{KeyIDs: role.ListKeyIDs()}
}
예제 #2
0
파일: helpers.go 프로젝트: mbentley/notary
func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error {
	switch c.Action() {
	case changelist.ActionCreate:
		td := changelist.TUFDelegation{}
		err := json.Unmarshal(c.Content(), &td)
		if err != nil {
			return err
		}

		// Try to create brand new role or update one
		// First add the keys, then the paths.  We can only add keys and paths in this scenario
		err = repo.UpdateDelegationKeys(c.Scope(), td.AddKeys, []string{}, td.NewThreshold)
		if err != nil {
			return err
		}
		return repo.UpdateDelegationPaths(c.Scope(), td.AddPaths, []string{}, false)
	case changelist.ActionUpdate:
		td := changelist.TUFDelegation{}
		err := json.Unmarshal(c.Content(), &td)
		if err != nil {
			return err
		}
		delgRole, err := repo.GetDelegationRole(c.Scope())
		if err != nil {
			return err
		}

		// We need to translate the keys from canonical ID to TUF ID for compatibility
		canonicalToTUFID := make(map[string]string)
		for tufID, pubKey := range delgRole.Keys {
			canonicalID, err := utils.CanonicalKeyID(pubKey)
			if err != nil {
				return err
			}
			canonicalToTUFID[canonicalID] = tufID
		}

		removeTUFKeyIDs := []string{}
		for _, canonID := range td.RemoveKeys {
			removeTUFKeyIDs = append(removeTUFKeyIDs, canonicalToTUFID[canonID])
		}

		// If we specify the only keys left delete the role, else just delete specified keys
		if strings.Join(delgRole.ListKeyIDs(), ";") == strings.Join(removeTUFKeyIDs, ";") && len(td.AddKeys) == 0 {
			return repo.DeleteDelegation(c.Scope())
		}
		err = repo.UpdateDelegationKeys(c.Scope(), td.AddKeys, removeTUFKeyIDs, td.NewThreshold)
		if err != nil {
			return err
		}
		return repo.UpdateDelegationPaths(c.Scope(), td.AddPaths, td.RemovePaths, td.ClearAllPaths)
	case changelist.ActionDelete:
		return repo.DeleteDelegation(c.Scope())
	default:
		return fmt.Errorf("unsupported action against delegations: %s", c.Action())
	}

}
예제 #3
0
파일: sign.go 프로젝트: rogaha/notary
// Sign takes a data.Signed and a key, calculated and adds the signature
// to the data.Signed
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{})
	keyIDs := make(
		[]idPair,
		0,
		len(keys),
	)

	for _, key := range keys {
		keyID, err := utils.CanonicalKeyID(key)
		if err != nil {
			continue
		}
		keyIDMemb[key.ID()] = struct{}{}
		keyIDs = append(keyIDs, idPair{
			scopedKeyID:    key.ID(),
			canonicalKeyID: keyID,
		})
	}

	// we need to ask the signer to sign with the canonical key ID
	// (ID of the TUF key's public key bytes only), but
	// we need to translate back to the scoped key ID (the hash of the TUF key
	// with the full PEM bytes) before giving the
	// signature back to TUF.
	for _, pair := range keyIDs {
		newSigs, err := service.Sign([]string{pair.canonicalKeyID}, s.Signed)
		if err != nil {
			return err
		}
		// we only asked to sign with 1 key ID, so there will either be 1
		// or zero signatures
		if len(newSigs) == 1 {
			newSig := newSigs[0]
			newSig.KeyID = pair.scopedKeyID
			signatures = append(signatures, newSig)
		}
	}
	if len(signatures) < 1 {
		return errors.ErrInsufficientSignatures{
			Name: fmt.Sprintf("Cryptoservice failed to produce any signatures for keys with IDs: %v", keyIDs),
			Err:  nil,
		}
	}
	for _, sig := range s.Signatures {
		if _, ok := keyIDMemb[sig.KeyID]; ok {
			continue
		}
		signatures = append(signatures, sig)
	}
	s.Signatures = signatures
	return nil
}
예제 #4
0
func translateDelegationsToCanonicalIDs(delegationInfo data.Delegations) ([]*data.Role, error) {
	canonicalDelegations := make([]*data.Role, len(delegationInfo.Roles))
	copy(canonicalDelegations, delegationInfo.Roles)
	delegationKeys := delegationInfo.Keys
	for i, delegation := range canonicalDelegations {
		canonicalKeyIDs := []string{}
		for _, keyID := range delegation.KeyIDs {
			pubKey, ok := delegationKeys[keyID]
			if !ok {
				return nil, fmt.Errorf("Could not translate canonical key IDs for %s", delegation.Name)
			}
			canonicalKeyID, err := utils.CanonicalKeyID(pubKey)
			if err != nil {
				return nil, fmt.Errorf("Could not translate canonical key IDs for %s: %v", delegation.Name, err)
			}
			canonicalKeyIDs = append(canonicalKeyIDs, canonicalKeyID)
		}
		canonicalDelegations[i].KeyIDs = canonicalKeyIDs
	}
	return canonicalDelegations, nil
}
예제 #5
0
파일: tuf.go 프로젝트: useidel/notary
// VerifyCanSign returns nil if the role exists and we have at least one
// signing key for the role, false otherwise.  This does not check that we have
// enough signing keys to meet the threshold, since we want to support the use
// case of multiple signers for a role.  It returns an error if the role doesn't
// exist or if there are no signing keys.
func (tr *Repo) VerifyCanSign(roleName string) error {
	role := tr.keysDB.GetRole(roleName)
	if role == nil {
		return data.ErrInvalidRole{Role: roleName, Reason: "does not exist"}
	}

	for _, keyID := range role.KeyIDs {
		k := tr.keysDB.GetKey(keyID)
		canonicalID, err := utils.CanonicalKeyID(k)
		check := []string{keyID}
		if err == nil {
			check = append(check, canonicalID)
		}
		for _, id := range check {
			p, _, err := tr.cryptoService.GetPrivateKey(id)
			if err == nil && p != nil {
				return nil
			}
		}
	}
	return signed.ErrNoKeys{KeyIDs: role.KeyIDs}
}
예제 #6
0
파일: delegations.go 프로젝트: Mic92/docker
func translateDelegationsToCanonicalIDs(delegationInfo data.Delegations) ([]data.Role, error) {
	canonicalDelegations := make([]data.Role, len(delegationInfo.Roles))
	// Do a copy by value to ensure local delegation metadata is untouched
	for idx, origRole := range delegationInfo.Roles {
		canonicalDelegations[idx] = *origRole
	}
	delegationKeys := delegationInfo.Keys
	for i, delegation := range canonicalDelegations {
		canonicalKeyIDs := []string{}
		for _, keyID := range delegation.KeyIDs {
			pubKey, ok := delegationKeys[keyID]
			if !ok {
				return []data.Role{}, fmt.Errorf("Could not translate canonical key IDs for %s", delegation.Name)
			}
			canonicalKeyID, err := utils.CanonicalKeyID(pubKey)
			if err != nil {
				return []data.Role{}, fmt.Errorf("Could not translate canonical key IDs for %s: %v", delegation.Name, err)
			}
			canonicalKeyIDs = append(canonicalKeyIDs, canonicalKeyID)
		}
		canonicalDelegations[i].KeyIDs = canonicalKeyIDs
	}
	return canonicalDelegations, nil
}
예제 #7
0
파일: delegations.go 프로젝트: cyli/notary
// delegationAdd creates a new delegation by adding a public key from a certificate to a specific role in a GUN
func (d *delegationCommander) delegationAdd(cmd *cobra.Command, args []string) error {
	// We must have at least the gun and role name, and at least one key or path (or the --all-paths flag) to add
	if len(args) < 2 || len(args) < 3 && d.paths == nil && !d.allPaths {
		cmd.Usage()
		return fmt.Errorf("must specify the Global Unique Name and the role of the delegation along with the public key certificate paths and/or a list of paths to add")
	}

	config, err := d.configGetter()
	if err != nil {
		return err
	}

	gun := args[0]
	role := args[1]

	pubKeys := []data.PublicKey{}
	if len(args) > 2 {
		pubKeyPaths := args[2:]
		for _, pubKeyPath := range pubKeyPaths {
			// Read public key bytes from PEM file
			pubKeyBytes, err := ioutil.ReadFile(pubKeyPath)
			if err != nil {
				return fmt.Errorf("unable to read public key from file: %s", pubKeyPath)
			}

			// Parse PEM bytes into type PublicKey
			pubKey, err := trustmanager.ParsePEMPublicKey(pubKeyBytes)
			if err != nil {
				return fmt.Errorf("unable to parse valid public key certificate from PEM file %s: %v", pubKeyPath, err)
			}
			pubKeys = append(pubKeys, pubKey)
		}
	}

	for _, path := range d.paths {
		if path == "" {
			d.allPaths = true
			break
		}
	}

	// If the user passes --all-paths (or gave the "" path in --paths), give the "" path
	if d.allPaths {
		d.paths = []string{""}
	}

	trustPin, err := getTrustPinning(config)
	if err != nil {
		return err
	}

	// no online operations are performed by add so the transport argument
	// should be nil
	nRepo, err := notaryclient.NewNotaryRepository(
		config.GetString("trust_dir"), gun, getRemoteTrustServer(config), nil, d.retriever, trustPin)
	if err != nil {
		return err
	}

	// Add the delegation to the repository
	err = nRepo.AddDelegation(role, pubKeys, d.paths)
	if err != nil {
		return fmt.Errorf("failed to create delegation: %v", err)
	}

	// Make keyID slice for better CLI print
	pubKeyIDs := []string{}
	for _, pubKey := range pubKeys {
		pubKeyID, err := utils.CanonicalKeyID(pubKey)
		if err != nil {
			return err
		}
		pubKeyIDs = append(pubKeyIDs, pubKeyID)
	}

	cmd.Println("")
	addingItems := ""
	if len(pubKeyIDs) > 0 {
		addingItems = addingItems + fmt.Sprintf("with keys %s, ", pubKeyIDs)
	}
	if d.paths != nil || d.allPaths {
		addingItems = addingItems + fmt.Sprintf("with paths [%s], ", prettyPrintPaths(d.paths))
	}
	cmd.Printf(
		"Addition of delegation role %s %sto repository \"%s\" staged for next publish.\n",
		role, addingItems, gun)
	cmd.Println("")
	return nil
}
예제 #8
0
파일: sign.go 프로젝트: useidel/notary
// Sign takes a data.Signed and a key, calculated and adds the signature
// to the data.Signed
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)
	signingKeyIDs := make(map[string]struct{})
	ids := make([]string, 0, len(keys))

	privKeys := make(map[string]data.PrivateKey)

	// Get all the private key objects related to the public keys
	for _, key := range keys {
		canonicalID, err := utils.CanonicalKeyID(key)
		ids = append(ids, canonicalID)
		if err != nil {
			continue
		}
		k, _, err := service.GetPrivateKey(canonicalID)
		if err != nil {
			continue
		}
		privKeys[key.ID()] = k
	}

	// Check to ensure we have at least one signing key
	if len(privKeys) == 0 {
		return ErrNoKeys{KeyIDs: ids}
	}

	// Do signing and generate list of signatures
	for keyID, pk := range privKeys {
		sig, err := pk.Sign(rand.Reader, s.Signed, nil)
		if err != nil {
			logrus.Debugf("Failed to sign with key: %s. Reason: %v", keyID, err)
			continue
		}
		signingKeyIDs[keyID] = struct{}{}
		signatures = append(signatures, data.Signature{
			KeyID:     keyID,
			Method:    pk.SignatureAlgorithm(),
			Signature: sig[:],
		})
	}

	// Check we produced at least on signature
	if len(signatures) < 1 {
		return ErrInsufficientSignatures{
			Name: fmt.Sprintf(
				"cryptoservice failed to produce any signatures for keys with IDs: %v",
				ids),
		}
	}

	for _, sig := range s.Signatures {
		if _, ok := signingKeyIDs[sig.KeyID]; ok {
			// key is in the set of key IDs for which a signature has been created
			continue
		}
		signatures = append(signatures, sig)
	}
	s.Signatures = signatures
	return nil
}
예제 #9
0
func changeTargetsDelegation(repo *tuf.Repo, c changelist.Change) error {
	switch c.Action() {
	case changelist.ActionCreate:
		td := changelist.TufDelegation{}
		err := json.Unmarshal(c.Content(), &td)
		if err != nil {
			return err
		}
		r, _, err := repo.GetDelegation(c.Scope())
		if _, ok := err.(data.ErrNoSuchRole); err != nil && !ok {
			// error that wasn't ErrNoSuchRole
			return err
		}
		if err == nil {
			// role existed, attempt to merge paths and keys
			if err := r.AddPaths(td.AddPaths); err != nil {
				return err
			}
			return repo.UpdateDelegations(r, td.AddKeys)
		}
		// create brand new role
		r, err = td.ToNewRole(c.Scope())
		if err != nil {
			return err
		}
		return repo.UpdateDelegations(r, td.AddKeys)
	case changelist.ActionUpdate:
		td := changelist.TufDelegation{}
		err := json.Unmarshal(c.Content(), &td)
		if err != nil {
			return err
		}
		r, keys, err := repo.GetDelegation(c.Scope())
		if err != nil {
			return err
		}

		// We need to translate the keys from canonical ID to TUF ID for compatibility
		canonicalToTUFID := make(map[string]string)
		for tufID, pubKey := range keys {
			canonicalID, err := utils.CanonicalKeyID(pubKey)
			if err != nil {
				return err
			}
			canonicalToTUFID[canonicalID] = tufID
		}

		removeTUFKeyIDs := []string{}
		for _, canonID := range td.RemoveKeys {
			removeTUFKeyIDs = append(removeTUFKeyIDs, canonicalToTUFID[canonID])
		}

		// If we specify the only keys left delete the role, else just delete specified keys
		if strings.Join(r.KeyIDs, ";") == strings.Join(removeTUFKeyIDs, ";") && len(td.AddKeys) == 0 {
			r := data.Role{Name: c.Scope()}
			return repo.DeleteDelegation(r)
		}
		// if we aren't deleting and the role exists, merge
		if err := r.AddPaths(td.AddPaths); err != nil {
			return err
		}

		// Clear all paths if we're given the flag, else remove specified paths
		if td.ClearAllPaths {
			r.RemovePaths(r.Paths)
		} else {
			r.RemovePaths(td.RemovePaths)
		}
		r.RemoveKeys(removeTUFKeyIDs)
		return repo.UpdateDelegations(r, td.AddKeys)
	case changelist.ActionDelete:
		r := data.Role{Name: c.Scope()}
		return repo.DeleteDelegation(r)
	default:
		return fmt.Errorf("unsupported action against delegations: %s", c.Action())
	}

}
예제 #10
0
파일: tuf.go 프로젝트: Mic92/docker
// PurgeDelegationKeys removes the provided canonical key IDs from all delegations
// present in the subtree rooted at role. The role argument must be provided in a wildcard
// format, i.e. targets/* would remove the key from all delegations in the repo
func (tr *Repo) PurgeDelegationKeys(role string, removeKeys []string) error {
	if !data.IsWildDelegation(role) {
		return data.ErrInvalidRole{
			Role:   role,
			Reason: "only wildcard roles can be used in a purge",
		}
	}

	removeIDs := make(map[string]struct{})
	for _, id := range removeKeys {
		removeIDs[id] = struct{}{}
	}

	start := path.Dir(role)
	tufIDToCanon := make(map[string]string)

	purgeKeys := func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} {
		var (
			deleteCandidates []string
			err              error
		)
		for id, key := range tgt.Signed.Delegations.Keys {
			var (
				canonID string
				ok      bool
			)
			if canonID, ok = tufIDToCanon[id]; !ok {
				canonID, err = utils.CanonicalKeyID(key)
				if err != nil {
					return err
				}
				tufIDToCanon[id] = canonID
			}
			if _, ok := removeIDs[canonID]; ok {
				deleteCandidates = append(deleteCandidates, id)
			}
		}
		if len(deleteCandidates) == 0 {
			// none of the interesting keys were present. We're done with this role
			return nil
		}
		// now we know there are changes, check if we'll be able to sign them in
		if err := tr.VerifyCanSign(validRole.Name); err != nil {
			logrus.Warnf(
				"role %s contains keys being purged but you do not have the necessary keys present to sign it; keys will not be purged from %s or its immediate children",
				validRole.Name,
				validRole.Name,
			)
			return nil
		}
		// we know we can sign in the changes, delete the keys
		for _, id := range deleteCandidates {
			delete(tgt.Signed.Delegations.Keys, id)
		}
		// delete candidate keys from all roles.
		for _, role := range tgt.Signed.Delegations.Roles {
			role.RemoveKeys(deleteCandidates)
			if len(role.KeyIDs) < role.Threshold {
				logrus.Warnf("role %s has fewer keys than its threshold of %d; it will not be usable until keys are added to it", role.Name, role.Threshold)
			}
		}
		tgt.Dirty = true
		return nil
	}
	return tr.WalkTargets("", start, purgeKeys)
}
예제 #11
0
// 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")
}
예제 #12
0
// 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.")
}
예제 #13
0
파일: sign.go 프로젝트: Mic92/docker
// Sign takes a data.Signed and a cryptoservice containing private keys,
// calculates and adds at least minSignature signatures using signingKeys the
// data.Signed.  It will also clean up any signatures that are not in produced
// by either a signingKey or an otherWhitelistedKey.
// Note that in most cases, otherWhitelistedKeys should probably be null. They
// are for keys you don't want to sign with, but you also don't want to remove
// existing signatures by those keys.  For instance, if you want to call Sign
// multiple times with different sets of signing keys without undoing removing
// signatures produced by the previous call to Sign.
func Sign(service CryptoService, s *data.Signed, signingKeys []data.PublicKey,
	minSignatures int, otherWhitelistedKeys []data.PublicKey) error {

	logrus.Debugf("sign called with %d/%d required keys", minSignatures, len(signingKeys))
	signatures := make([]data.Signature, 0, len(s.Signatures)+1)
	signingKeyIDs := make(map[string]struct{})
	tufIDs := make(map[string]data.PublicKey)

	privKeys := make(map[string]data.PrivateKey)

	// Get all the private key objects related to the public keys
	missingKeyIDs := []string{}
	for _, key := range signingKeys {
		canonicalID, err := utils.CanonicalKeyID(key)
		tufIDs[key.ID()] = key
		if err != nil {
			return err
		}
		k, _, err := service.GetPrivateKey(canonicalID)
		if err != nil {
			if _, ok := err.(trustmanager.ErrKeyNotFound); ok {
				missingKeyIDs = append(missingKeyIDs, canonicalID)
				continue
			}
			return err
		}
		privKeys[key.ID()] = k
	}

	// include the list of otherWhitelistedKeys
	for _, key := range otherWhitelistedKeys {
		if _, ok := tufIDs[key.ID()]; !ok {
			tufIDs[key.ID()] = key
		}
	}

	// Check to ensure we have enough signing keys
	if len(privKeys) < minSignatures {
		return ErrInsufficientSignatures{FoundKeys: len(privKeys),
			NeededKeys: minSignatures, MissingKeyIDs: missingKeyIDs}
	}

	emptyStruct := struct{}{}
	// Do signing and generate list of signatures
	for keyID, pk := range privKeys {
		sig, err := pk.Sign(rand.Reader, *s.Signed, nil)
		if err != nil {
			logrus.Debugf("Failed to sign with key: %s. Reason: %v", keyID, err)
			return err
		}
		signingKeyIDs[keyID] = emptyStruct
		signatures = append(signatures, data.Signature{
			KeyID:     keyID,
			Method:    pk.SignatureAlgorithm(),
			Signature: sig[:],
		})
	}

	for _, sig := range s.Signatures {
		if _, ok := signingKeyIDs[sig.KeyID]; ok {
			// key is in the set of key IDs for which a signature has been created
			continue
		}
		var (
			k  data.PublicKey
			ok bool
		)
		if k, ok = tufIDs[sig.KeyID]; !ok {
			// key is no longer a valid signing key
			continue
		}
		if err := VerifySignature(*s.Signed, &sig, k); err != nil {
			// signature is no longer valid
			continue
		}
		// keep any signatures that still represent valid keys and are
		// themselves valid
		signatures = append(signatures, sig)
	}
	s.Signatures = signatures
	return nil
}
예제 #14
0
// delegationAdd creates a new delegation by adding a public key from a certificate to a specific role in a GUN
func (d *delegationCommander) delegationAdd(cmd *cobra.Command, args []string) error {
	// We must have at least the gun and role name, and at least one key or path (or the --all-paths flag) to add
	if len(args) < 2 || len(args) < 3 && d.paths == nil && !d.allPaths {
		cmd.Usage()
		return fmt.Errorf("must specify the Global Unique Name and the role of the delegation along with the public key certificate paths and/or a list of paths to add")
	}

	config, err := d.configGetter()
	if err != nil {
		return err
	}

	gun := args[0]
	role := args[1]

	pubKeys, err := ingestPublicKeys(args)
	if err != nil {
		return err
	}

	checkAllPaths(d)

	trustPin, err := getTrustPinning(config)
	if err != nil {
		return err
	}

	// no online operations are performed by add so the transport argument
	// should be nil
	nRepo, err := notaryclient.NewFileCachedNotaryRepository(
		config.GetString("trust_dir"), gun, getRemoteTrustServer(config), nil, d.retriever, trustPin)
	if err != nil {
		return err
	}

	// Add the delegation to the repository
	err = nRepo.AddDelegation(role, pubKeys, d.paths)
	if err != nil {
		return fmt.Errorf("failed to create delegation: %v", err)
	}

	// Make keyID slice for better CLI print
	pubKeyIDs := []string{}
	for _, pubKey := range pubKeys {
		pubKeyID, err := utils.CanonicalKeyID(pubKey)
		if err != nil {
			return err
		}
		pubKeyIDs = append(pubKeyIDs, pubKeyID)
	}

	cmd.Println("")
	addingItems := ""
	if len(pubKeyIDs) > 0 {
		addingItems = addingItems + fmt.Sprintf("with keys %s, ", pubKeyIDs)
	}
	if d.paths != nil || d.allPaths {
		addingItems = addingItems + fmt.Sprintf(
			"with paths [%s], ",
			strings.Join(prettyPaths(d.paths), "\n"),
		)
	}
	cmd.Printf(
		"Addition of delegation role %s %sto repository \"%s\" staged for next publish.\n",
		role, addingItems, gun)
	cmd.Println("")

	return maybeAutoPublish(cmd, d.autoPublish, gun, config, d.retriever)
}