| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package security |
| |
| import ( |
| "bytes" |
| "fmt" |
| "reflect" |
| "sort" |
| "strings" |
| "sync" |
| "time" |
| |
| "v.io/v23/context" |
| "v.io/v23/verror" |
| ) |
| |
| var ( |
| errEmptyChain = verror.Register(pkgPath+".errEmptyChain", verror.NoRetry, "empty certificate chain in blessings") |
| errMisconfiguredRoots = verror.Register(pkgPath+".errMisconfiguredRoots", verror.NoRetry, "recognized root certificates not configured") |
| errMultiplePublicKeys = verror.Register(pkgPath+".errMultiplePublicKeys", verror.NoRetry, "invalid blessings: two certificate chains that bind to different public keys") |
| errInvalidUnion = verror.Register(pkgPath+".errInvalidUnion", verror.NoRetry, "cannot create union of blessings bound to different public keys") |
| errNeedRoots = verror.Register(pkgPath+".errNeedRoots", verror.NoRetry, "{1:}{2:}principal does not have any BlessingRoots{:_}") |
| errCantAddRoot = verror.Register(pkgPath+".errCantAddRoot", verror.NoRetry, "{1:}{2:}failed to Add root: {3} for pattern: {4} to this principal's roots: {5}{:_}") |
| ) |
| |
| // Blessings encapsulates all cryptographic operations required to |
| // prove that a set of (human-readable) blessing names are bound |
| // to a principal in a specific call. |
| // |
| // Blessings objects are meant to be presented to other principals to |
| // authenticate and authorize actions. The functions 'LocalBlessingNames', |
| // 'SigningBlessingNames' and 'RemoteBlessingNames' defined in this package |
| // can be used to uncover the blessing names encapsulated in these objects. |
| // |
| // Blessings objects are immutable and multiple goroutines may invoke methods |
| // on them simultaneously. |
| // |
| // See also: https://github.com/vanadium/docs/blob/master/glossary.md#blessing |
| type Blessings struct { |
| chains [][]Certificate |
| publicKey PublicKey |
| digests [][]byte // digests[i] is the digest of chains[i] |
| uniqueID []byte |
| } |
| |
| // PublicKey returns the public key of the principal to which |
| // blessings obtained from this object are bound. |
| // |
| // Can return nil if b is the zero value. |
| func (b Blessings) PublicKey() PublicKey { return b.publicKey } |
| |
| // ThirdPartyCaveats returns the set of third-party restrictions on the |
| // scope of the blessings (i.e., the subset of Caveats for which |
| // ThirdPartyDetails will be non-nil). |
| func (b Blessings) ThirdPartyCaveats() []Caveat { |
| var ret []Caveat |
| for _, chain := range b.chains { |
| for _, cert := range chain { |
| for _, cav := range cert.Caveats { |
| if tp := cav.ThirdPartyDetails(); tp != nil { |
| ret = append(ret, cav) |
| } |
| } |
| } |
| } |
| return ret |
| } |
| |
| // IsZero returns true if b represents the zero value of blessings (an empty |
| // set). |
| func (b Blessings) IsZero() bool { |
| // b.publicKey == nil <=> len(b.chains) == 0 |
| return b.publicKey == nil |
| } |
| |
| // Equivalent returns true if 'b' and 'blessings' can be used interchangeably, |
| // i.e., 'b' will be authorized wherever 'blessings' is and vice-versa. |
| func (b Blessings) Equivalent(blessings Blessings) bool { |
| return bytes.Equal(b.UniqueID(), blessings.UniqueID()) |
| } |
| |
| // UniqueID returns an identifier of the set of blessings. Two blessings |
| // objects with the same UniqueID are interchangeable for any authorization |
| // decisions. |
| // |
| // The converse is not guaranteed at this time. Two interchangeable blessings |
| // objects may in theory have different UniqueIDs. |
| func (b Blessings) UniqueID() []byte { return b.uniqueID } |
| |
| func (b *Blessings) init() { |
| if len(b.digests) == 0 { |
| return |
| } |
| tohash := make([]byte, 0, len(b.digests[0])*len(b.digests)) |
| for _, d := range b.digests { |
| tohash = append(tohash, d...) |
| } |
| b.uniqueID = b.publicKey.hash().sum(tohash) |
| } |
| |
| // CouldHaveNames returns true iff the blessings 'b' encapsulates the provided |
| // set of blessing names. |
| // |
| // This check ignores all caveats on the blessing name and the recognition status |
| // of its blessing root. |
| func (b Blessings) CouldHaveNames(names []string) bool { |
| hasNames := make(map[string]bool) |
| for _, chain := range b.chains { |
| hasNames[claimedName(chain)] = true |
| } |
| for _, n := range names { |
| if !hasNames[n] { |
| return false |
| } |
| } |
| return true |
| } |
| |
| // Expiry returns the time at which b will no longer be valid, or the zero |
| // value of time.Time if the blessing does not expire. |
| func (b Blessings) Expiry() time.Time { |
| var min time.Time |
| for _, chain := range b.chains { |
| for _, cert := range chain { |
| for _, cav := range cert.Caveats { |
| if t := expiryTime(cav); !t.IsZero() && (min.IsZero() || t.Before(min)) { |
| min = t |
| } |
| } |
| } |
| } |
| return min |
| } |
| |
| func (b Blessings) publicKeyDER() []byte { |
| chain := b.chains[0] |
| return chain[len(chain)-1].PublicKey |
| } |
| |
| func (b Blessings) String() string { |
| blessings := make([]string, len(b.chains)) |
| for chainidx, chain := range b.chains { |
| onechain := make([]string, len(chain)) |
| for certidx, cert := range chain { |
| onechain[certidx] = cert.Extension |
| } |
| blessings[chainidx] = fmt.Sprintf("%v", strings.Join(onechain, ChainSeparator)) |
| } |
| return strings.Join(blessings, ",") |
| } |
| |
| // We keep a pool of buffers for claimedName, this saves a lot of allocations. |
| var claimedPool = sync.Pool{New: func() interface{} { |
| return &bytes.Buffer{} |
| }} |
| |
| // claimedName returns the blessing name that the certificate chain claims to |
| // have (i.e., ignoring any caveats or recognition of the root public key as an |
| // authority on the namespace). |
| func claimedName(chain []Certificate) string { |
| buf := claimedPool.Get().(*bytes.Buffer) |
| buf.Reset() |
| buf.WriteString(chain[0].Extension) |
| for i := 1; i < len(chain); i++ { |
| buf.WriteString(ChainSeparator) |
| buf.WriteString(chain[i].Extension) |
| } |
| ret := buf.String() |
| claimedPool.Put(buf) |
| return ret |
| } |
| |
| func nameForPrincipal(pubkey []byte, roots BlessingRoots, chain []Certificate) string { |
| // Verify the chain belongs to this principal |
| if !bytes.Equal(chain[len(chain)-1].PublicKey, pubkey) { |
| return "" |
| } |
| // Verify that the root of the chain is recognized as an authority on |
| // blessing. |
| blessing := claimedName(chain) |
| if err := roots.Recognized(chain[0].PublicKey, blessing); err != nil { |
| return "" |
| } |
| return blessing |
| } |
| |
| // chainCaveats returns the union of the set of caveats in the certificates present |
| // in 'chain'. |
| func chainCaveats(chain []Certificate) []Caveat { |
| var cavs []Caveat |
| for _, c := range chain { |
| cavs = append(cavs, c.Caveats...) |
| } |
| return cavs |
| } |
| |
| func defaultCaveatValidation(ctx *context.T, call Call, chains [][]Caveat) []error { |
| results := make([]error, len(chains)) |
| for i, chain := range chains { |
| for _, cav := range chain { |
| if err := cav.Validate(ctx, call); err != nil { |
| results[i] = err |
| break |
| } |
| } |
| } |
| return results |
| } |
| |
| // TODO(ashankar): Get rid of this function? It allows users to mess |
| // with the integrity of 'b'. |
| func MarshalBlessings(b Blessings) WireBlessings { |
| return WireBlessings{b.chains} |
| } |
| |
| func wireBlessingsToNative(wire WireBlessings, native *Blessings) error { |
| if len(wire.CertificateChains) == 0 { |
| return nil |
| } |
| certchains := wire.CertificateChains |
| if len(certchains) == 0 || len(certchains[0]) == 0 { |
| return verror.New(errEmptyChain, nil) |
| } |
| // if this is a nameless blessing |
| if isNamelessChains(certchains) { |
| cert := certchains[0][0] |
| pk, err := UnmarshalPublicKey(cert.PublicKey) |
| if err != nil { |
| return err |
| } |
| digest, _ := cert.chainedDigests(pk.hash(), nil) |
| *native = Blessings{ |
| chains: [][]Certificate{{cert}}, |
| publicKey: pk, |
| digests: [][]byte{digest}, |
| } |
| native.init() |
| return nil |
| } |
| // Public keys should match for all chains. |
| marshaledkey := certchains[0][len(certchains[0])-1].PublicKey |
| digests := make([][]byte, len(certchains)) |
| |
| var key PublicKey |
| var err error |
| if key, digests[0], err = validateCertificateChain(certchains[0]); err != nil { |
| return err |
| } |
| for i := 1; i < len(certchains); i++ { |
| chain := certchains[i] |
| if len(chain) == 0 { |
| return verror.New(errEmptyChain, nil) |
| } |
| cert := chain[len(chain)-1] |
| if !bytes.Equal(marshaledkey, cert.PublicKey) { |
| return verror.New(errMultiplePublicKeys, nil) |
| } |
| if _, digests[i], err = validateCertificateChain(chain); err != nil { |
| return err |
| } |
| } |
| native.chains = certchains |
| native.publicKey = key |
| native.digests = digests |
| native.init() |
| return nil |
| } |
| |
| func wireBlessingsFromNative(wire *WireBlessings, native Blessings) error { |
| wire.CertificateChains = native.chains |
| return nil |
| } |
| |
| // UnionOfBlessings returns a Blessings object that carries the union of the |
| // provided blessings. |
| // |
| // All provided Blessings must have the same PublicKey. |
| // |
| // UnionOfBlessings with no arguments returns (nil, nil). |
| func UnionOfBlessings(blessings ...Blessings) (Blessings, error) { |
| for len(blessings) > 0 && blessings[0].IsZero() { |
| blessings = blessings[1:] |
| } |
| switch len(blessings) { |
| case 0: |
| return Blessings{}, nil |
| case 1: |
| return blessings[0], nil |
| } |
| key0 := blessings[0].publicKeyDER() |
| onlyNameless := blessings[0].isNamelessBlessing() |
| var ret Blessings |
| for idx, b := range blessings { |
| if b.IsZero() || b.isNamelessBlessing() { |
| continue |
| } |
| onlyNameless = false |
| if idx > 0 && !bytes.Equal(key0, b.publicKeyDER()) { |
| return Blessings{}, verror.New(errInvalidUnion, nil) |
| } |
| ret.chains = append(ret.chains, b.chains...) |
| ret.digests = append(ret.digests, b.digests...) |
| } |
| if onlyNameless { |
| return blessings[0], nil |
| } |
| var err error |
| if ret.publicKey, err = UnmarshalPublicKey(key0); err != nil { |
| return Blessings{}, err |
| } |
| // For pretty printing, sort the certificate chains so that there is a consistent |
| // ordering, irrespective of the ordering of arguments to UnionOfBlessings. |
| sort.Stable(blessingsSorter(ret)) |
| ret.init() |
| return ret, nil |
| } |
| |
| type blessingsSorter Blessings |
| |
| func (b blessingsSorter) Len() int { return len(b.chains) } |
| func (b blessingsSorter) Swap(i, j int) { |
| b.chains[i], b.chains[j] = b.chains[j], b.chains[i] |
| b.digests[i], b.digests[j] = b.digests[j], b.digests[i] |
| } |
| func (b blessingsSorter) Less(i, j int) bool { |
| ci := b.chains[i] |
| cj := b.chains[j] |
| if len(ci) < len(cj) { |
| return true |
| } |
| if len(ci) > len(cj) { |
| return false |
| } |
| // Equal size chains, order by the names in the certificates. |
| N := len(ci) |
| for idx := 0; idx < N; idx++ { |
| ie := ci[idx].Extension |
| je := cj[idx].Extension |
| if ie < je { |
| return true |
| } |
| if ie > je { |
| return false |
| } |
| } |
| return false |
| } |
| |
| func (i RejectedBlessing) String() string { |
| return fmt.Sprintf("{%q: %v}", i.Blessing, i.Err) |
| } |
| |
| // DefaultBlessingPatterns returns the BlessingsPatterns of the Default Blessings |
| // of the provided Principal. |
| func DefaultBlessingPatterns(p Principal) (patterns []BlessingPattern) { |
| for _, b := range BlessingNames(p, p.BlessingStore().Default()) { |
| patterns = append(patterns, BlessingPattern(b)) |
| } |
| return |
| } |
| |
| var ( |
| caveatValidationMu sync.RWMutex |
| caveatValidation = defaultCaveatValidation |
| caveatValidationOverridden = false |
| ) |
| |
| func overrideCaveatValidation(fn func(ctx *context.T, call Call, sets [][]Caveat) []error) { |
| caveatValidationMu.Lock() |
| if caveatValidationOverridden { |
| panic("security: OverrideCaveatValidation may only be called once") |
| } |
| caveatValidationOverridden = true |
| caveatValidation = fn |
| caveatValidationMu.Unlock() |
| } |
| |
| func setCaveatValidationForTest(fn func(ctx *context.T, call Call, sets [][]Caveat) []error) { |
| // For tests we skip the panic on multiple calls, so that we can easily revert |
| // to the default validator. |
| caveatValidationMu.Lock() |
| caveatValidation = fn |
| caveatValidationMu.Unlock() |
| } |
| |
| func getCaveatValidation() func(ctx *context.T, call Call, sets [][]Caveat) []error { |
| caveatValidationMu.RLock() |
| fn := caveatValidation |
| caveatValidationMu.RUnlock() |
| return fn |
| } |
| |
| func isSigningBlessingCaveat(cav Caveat) bool { |
| switch cav.Id { |
| case ExpiryCaveat.Id: |
| return true |
| case ConstCaveat.Id: |
| return true |
| } |
| // TODO(ataly): Figure out what revocation caveats can be supported |
| // for signing blessings. Our current revocation caveats are third-party |
| // caveats and are unsuitable for signing for the following reasons: |
| // - They violate the requirement that the caveat should be universally |
| // understandable and validatable. This is because validating the |
| // caveat requires reaching out to a third-party caveat discharger |
| // which may be reachable from some services and unreachable from others |
| // (perhaps due to network partitions). |
| // - Given a third-party caveat, there is no way to tell whether it is revocation |
| // caveat. |
| return false |
| } |
| |
| func validateCaveatsForSigning(ctx *context.T, call Call, chain []Certificate) error { |
| for _, cav := range chainCaveats(chain) { |
| if !isSigningBlessingCaveat(cav) { |
| return NewErrInvalidSigningBlessingCaveat(nil, cav.Id) |
| } |
| if err := cav.Validate(ctx, call); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| // RootBlessings returns the blessings of the roots of b. |
| // In other words, RootBlessings returns the blessings of the identity |
| // providers encapsulated in b. |
| // |
| // In particular: |
| // AddToRoots(p, b) |
| // is equivalent to: |
| // for _, root := range RootBlessings(b) { |
| // AddToRoots(p, root) |
| // } |
| // Why would you use the latter? Only to share roots with another process, |
| // without revealing your complete blessings and using fewer bytes. |
| func RootBlessings(b Blessings) []Blessings { |
| if b.isNamelessBlessing() { |
| return nil |
| } |
| ret := make([]Blessings, len(b.chains)) |
| for i, chain := range b.chains { |
| // One option is to convert to WireBlessings and back. |
| // But that involves a lot of crypto verification that has |
| // already been done, so why bother. |
| if len(chain) == 0 { |
| continue |
| } |
| var ( |
| cert = chain[0] |
| digest, _ = cert.chainedDigests(cert.Signature.Hash, nil) |
| ptr = &ret[i] |
| ) |
| ptr.chains = [][]Certificate{[]Certificate{cert}} |
| ptr.digests = [][]byte{digest} |
| // The public key must parse because there is no way to create |
| // b without a valid certificate chain. |
| ptr.publicKey, _ = UnmarshalPublicKey(cert.PublicKey) |
| ptr.init() |
| } |
| if len(ret) == 0 { |
| return nil |
| } |
| return ret |
| } |
| |
| // SigningBlessings returns a blessings object that encapsulates the subset of |
| // names of the provided blessings object that can be used to sign data at rest. |
| // |
| // The names of the returned blessings object can be obtained using the |
| // 'SigningNames' function. |
| func SigningBlessings(blessings Blessings) Blessings { |
| ret := Blessings{} |
| if blessings.IsZero() || blessings.isNamelessBlessing() { |
| return ret |
| } |
| for _, chain := range blessings.chains { |
| suitableForSigning := true |
| for _, cav := range chainCaveats(chain) { |
| if !isSigningBlessingCaveat(cav) { |
| suitableForSigning = false |
| break |
| } |
| } |
| if suitableForSigning { |
| ret.chains = append(ret.chains, chain) |
| } |
| } |
| if len(ret.chains) > 0 { |
| ret.publicKey = blessings.publicKey |
| } |
| return ret |
| } |
| |
| // SigningBlessingNames returns the validated set of human-readable blessing |
| // names encapsulated in the provided signing blessings object, as determined |
| // by the provided principal. |
| // |
| // This function also returns the RejectedBlessings for each blessing name that |
| // cannot be validated. |
| // TODO(ataly): While the principal is encapsulated inside context.T, we can't |
| // extract it due to an import cycle issue. Therefore at the moment we have the |
| // principal separately passed to this function. We should clean this up. |
| func SigningBlessingNames(ctx *context.T, p Principal, blessings Blessings) ([]string, []RejectedBlessing) { |
| if ctx == nil || p == nil { |
| return nil, nil |
| } |
| if blessings.IsZero() || blessings.isNamelessBlessing() { |
| return nil, nil |
| } |
| var ( |
| validatedNames []string |
| rejected []RejectedBlessing |
| call = NewCall(&CallParams{LocalPrincipal: p, RemoteBlessings: blessings}) |
| ) |
| for _, chain := range blessings.chains { |
| name := claimedName(chain) |
| if err := p.Roots().Recognized(chain[0].PublicKey, name); err != nil { |
| rejected = append(rejected, RejectedBlessing{name, err}) |
| continue |
| } |
| if err := validateCaveatsForSigning(ctx, call, chain); err != nil { |
| rejected = append(rejected, RejectedBlessing{name, err}) |
| continue |
| } |
| validatedNames = append(validatedNames, name) |
| } |
| return validatedNames, rejected |
| } |
| |
| // RemoteBlessingNames returns the validated set of human-readable blessing names |
| // encapsulated in the blessings object presented by the remote end of a call. |
| // |
| // The blessing names are guaranteed to: |
| // |
| // (1) Satisfy all the caveats associated with them, in the context of the call. |
| // (2) Be rooted in call.LocalPrincipal.Roots. |
| // |
| // Caveats are considered satisfied for the 'call' if the CaveatValidator implementation |
| // can be found in the address space of the caller and Validate returns nil. |
| // |
| // RemoteBlessingNames also returns the RejectedBlessings for each blessing name that cannot |
| // be validated. |
| func RemoteBlessingNames(ctx *context.T, call Call) ([]string, []RejectedBlessing) { |
| if ctx == nil || call == nil { |
| return nil, nil |
| } |
| b := call.RemoteBlessings() |
| if b.IsZero() || b.isNamelessBlessing() { |
| return nil, nil |
| } |
| var ( |
| validatedNames []string |
| rejected []RejectedBlessing |
| pendingNames []string |
| pendingCaveatSets [][]Caveat |
| rootsErr error |
| ) |
| if p := call.LocalPrincipal(); p == nil || p.Roots() == nil { |
| // These conditions should not be possible: Here only for |
| // unittests where the Call object is intentionally limited. |
| rootsErr = verror.New(errMisconfiguredRoots, ctx) |
| } |
| for _, chain := range b.chains { |
| name := claimedName(chain) |
| err := rootsErr |
| if err == nil { |
| err = call.LocalPrincipal().Roots().Recognized(chain[0].PublicKey, name) |
| } |
| if err != nil { |
| rejected = append(rejected, RejectedBlessing{name, err}) |
| continue |
| } |
| cavs := chainCaveats(chain) |
| if len(cavs) == 0 { |
| validatedNames = append(validatedNames, name) // No caveats to validate, add it to blessingNames. |
| } else { |
| pendingNames = append(pendingNames, name) |
| pendingCaveatSets = append(pendingCaveatSets, cavs) |
| } |
| } |
| if len(pendingCaveatSets) == 0 { |
| return validatedNames, rejected |
| } |
| |
| validationResults := getCaveatValidation()(ctx, call, pendingCaveatSets) |
| if g, w := len(validationResults), len(pendingNames); g != w { |
| panic(fmt.Sprintf("Got wrong number of validation results. Got %d, expected %d.", g, w)) |
| } |
| |
| for i, resultErr := range validationResults { |
| if resultErr == nil { |
| validatedNames = append(validatedNames, pendingNames[i]) |
| } else { |
| rejected = append(rejected, RejectedBlessing{pendingNames[i], resultErr}) |
| } |
| } |
| return validatedNames, rejected |
| } |
| |
| // LocalBlessingNames returns the set of human-readable blessing names |
| // encapsulated in the blessings object presented by the local end of the call. |
| // |
| // This is just a convenience function over: |
| // BlessingNames(call.LocalPrincipal(), call.LocalBlessings()) |
| func LocalBlessingNames(ctx *context.T, call Call) []string { |
| if ctx == nil || call == nil { |
| return nil |
| } |
| return BlessingNames(call.LocalPrincipal(), call.LocalBlessings()) |
| } |
| |
| // BlessingNames returns the set of human-readable blessing names encapsulated |
| // in blessings. |
| // |
| // The returned names are guaranteed to be rooted in principal.Roots, though |
| // caveats may not be validated. |
| // |
| // The blessings must be bound to principal. There is intentionally no API to |
| // obtain blessing names bound to other principals by ignoring caveats. This |
| // is to prevent accidental authorization based on potentially invalid names |
| // (since caveats are not validated). |
| func BlessingNames(principal Principal, blessings Blessings) []string { |
| pKey, err := principal.PublicKey().MarshalBinary() |
| if err != nil { |
| return nil |
| } |
| chains := blessings.chains |
| if len(chains) == 0 { |
| return nil |
| } |
| // Blessings objects contain multiple certificate chains all bound to |
| // the same public key, so checking that any one of them is bound to |
| // pKey is sufficient. |
| if !bytes.Equal(chains[0][len(chains[0])-1].PublicKey, pKey) { |
| return nil |
| } |
| var ret []string |
| for _, chain := range blessings.chains { |
| name := claimedName(chain) |
| if err := principal.Roots().Recognized(chain[0].PublicKey, name); err == nil { |
| ret = append(ret, name) |
| } |
| } |
| return ret |
| } |
| |
| // NamelessBlessing returns a blessings object that has no names, only the |
| // provided public key. |
| func NamelessBlessing(pk PublicKey) (Blessings, error) { |
| pkbytes, err := pk.MarshalBinary() |
| if err != nil { |
| return Blessings{}, err |
| } |
| cert := Certificate{PublicKey: pkbytes} |
| digest, _ := cert.chainedDigests(pk.hash(), nil) |
| b := Blessings{ |
| chains: [][]Certificate{{cert}}, |
| publicKey: pk, |
| digests: [][]byte{digest}, |
| } |
| b.init() |
| return b, nil |
| } |
| |
| func (b Blessings) isNamelessBlessing() bool { |
| return b.publicKey != nil && isNamelessChains(b.chains) |
| } |
| |
| func isNamelessChains(chains [][]Certificate) bool { |
| var emptyCert Certificate |
| if len(chains) != 1 || len(chains[0]) != 1 { |
| return false |
| } |
| cert := chains[0][0] |
| cert.PublicKey = nil |
| return reflect.DeepEqual(cert, emptyCert) |
| } |
| |
| // AddToRoots marks the root principals of all blessing chains represented by |
| // 'blessings' as an authority on blessing chains beginning at that root name |
| // in p.BlessingRoots(). |
| // |
| // For example, if blessings represents the blessing chains |
| // ["alice:friend:spouse", "charlie:family:daughter"] then AddToRoots(blessing) |
| // will mark the root public key of the chain "alice:friend:bob" as the |
| // authority on all blessings that match the pattern "alice", and root public |
| // key of the chain "charlie:family:daughter" as an authority on all blessings |
| // that match the pattern "charlie". |
| // |
| // This is a convenience function over extracting the names and public keys of |
| // the roots of blessings and invoking p.Roots().Add(...). |
| func AddToRoots(p Principal, blessings Blessings) error { |
| if p.Roots() == nil { |
| return verror.New(errNeedRoots, nil) |
| } |
| if blessings.isNamelessBlessing() { |
| return nil |
| } |
| chains := blessings.chains |
| for _, chain := range chains { |
| pattern := BlessingPattern(chain[0].Extension) |
| root := chain[0].PublicKey |
| if err := p.Roots().Add(root, pattern); err != nil { |
| return verror.New(errCantAddRoot, nil, root, pattern, err) |
| } |
| } |
| return nil |
| } |