github.com/mhutchinson/trillian@v1.2.1/trees/trees.go (about)

     1  // Copyright 2017 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package trees contains utility method for retrieving trees and acquiring objects (hashers,
    16  // signers) associated with them.
    17  package trees
    18  
    19  import (
    20  	"context"
    21  	"crypto"
    22  	"fmt"
    23  
    24  	"github.com/golang/protobuf/ptypes"
    25  	"github.com/google/trillian"
    26  	"github.com/google/trillian/crypto/keys"
    27  	"github.com/google/trillian/crypto/sigpb"
    28  	"github.com/google/trillian/storage"
    29  	"go.opencensus.io/trace"
    30  	"google.golang.org/grpc/codes"
    31  	"google.golang.org/grpc/status"
    32  
    33  	tcrypto "github.com/google/trillian/crypto"
    34  )
    35  
    36  const traceSpanRoot = "github.com/google/trillian/trees"
    37  
    38  type treeKey struct{}
    39  
    40  type accessRule struct {
    41  	// Tree states are accepted if there is a 'true' value for them in this map.
    42  	okStates map[trillian.TreeState]bool
    43  	// Allows the error code to be specified for specific rejected states.
    44  	rejectCodes map[trillian.TreeState]codes.Code
    45  	// Tree types are accepted if there is a 'true' value for them in this map.
    46  	okTypes map[trillian.TreeType]bool
    47  }
    48  
    49  // These rules define the permissible combinations of tree state and type
    50  // for each operation type.
    51  var rules = map[OpType]accessRule{
    52  	Unknown: {},
    53  	Admin: {
    54  		okStates: map[trillian.TreeState]bool{
    55  			trillian.TreeState_UNKNOWN_TREE_STATE: true,
    56  			trillian.TreeState_ACTIVE:             true,
    57  			trillian.TreeState_DRAINING:           true,
    58  			trillian.TreeState_FROZEN:             true,
    59  		},
    60  		okTypes: map[trillian.TreeType]bool{
    61  			trillian.TreeType_LOG:            true,
    62  			trillian.TreeType_MAP:            true,
    63  			trillian.TreeType_PREORDERED_LOG: true,
    64  		},
    65  	},
    66  	Query: {
    67  		okStates: map[trillian.TreeState]bool{
    68  			// Have to allow queries on unknown state so storage can get a chance
    69  			// to return ErrTreeNeedsInit.
    70  			trillian.TreeState_UNKNOWN_TREE_STATE: true,
    71  			trillian.TreeState_ACTIVE:             true,
    72  			trillian.TreeState_DRAINING:           true,
    73  			trillian.TreeState_FROZEN:             true,
    74  		},
    75  		okTypes: map[trillian.TreeType]bool{
    76  			trillian.TreeType_LOG:            true,
    77  			trillian.TreeType_MAP:            true,
    78  			trillian.TreeType_PREORDERED_LOG: true,
    79  		},
    80  	},
    81  	QueueLog: {
    82  		okStates: map[trillian.TreeState]bool{
    83  			trillian.TreeState_ACTIVE: true,
    84  		},
    85  		rejectCodes: map[trillian.TreeState]codes.Code{
    86  			trillian.TreeState_DRAINING: codes.PermissionDenied,
    87  			trillian.TreeState_FROZEN:   codes.PermissionDenied,
    88  		},
    89  		okTypes: map[trillian.TreeType]bool{
    90  			trillian.TreeType_LOG:            true,
    91  			trillian.TreeType_PREORDERED_LOG: true,
    92  		},
    93  	},
    94  	SequenceLog: {
    95  		okStates: map[trillian.TreeState]bool{
    96  			trillian.TreeState_ACTIVE:   true,
    97  			trillian.TreeState_DRAINING: true,
    98  		},
    99  		okTypes: map[trillian.TreeType]bool{
   100  			trillian.TreeType_LOG:            true,
   101  			trillian.TreeType_PREORDERED_LOG: true,
   102  		},
   103  		rejectCodes: map[trillian.TreeState]codes.Code{
   104  			trillian.TreeState_FROZEN: codes.PermissionDenied,
   105  		},
   106  	},
   107  	UpdateMap: {
   108  		okStates: map[trillian.TreeState]bool{
   109  			trillian.TreeState_ACTIVE: true,
   110  		},
   111  		okTypes: map[trillian.TreeType]bool{
   112  			trillian.TreeType_MAP: true,
   113  		},
   114  	},
   115  }
   116  
   117  // NewContext returns a ctx with the given tree.
   118  func NewContext(ctx context.Context, tree *trillian.Tree) context.Context {
   119  	return context.WithValue(ctx, treeKey{}, tree)
   120  }
   121  
   122  // FromContext returns the tree within ctx if present, together with an indication of whether a
   123  // tree was present.
   124  func FromContext(ctx context.Context) (*trillian.Tree, bool) {
   125  	tree, ok := ctx.Value(treeKey{}).(*trillian.Tree)
   126  	return tree, ok && tree != nil
   127  }
   128  
   129  func validate(o GetOpts, tree *trillian.Tree) error {
   130  	// Do the special case checks first
   131  	if len(o.TreeTypes) > 0 && !o.TreeTypes[tree.TreeType] {
   132  		return status.Errorf(codes.InvalidArgument, "operation not allowed for %s-type trees (wanted one of %v)", tree.TreeType, o.TreeTypes)
   133  	}
   134  
   135  	// Reject any operation types we don't know about.
   136  	rule, ok := rules[o.Operation]
   137  	if !ok {
   138  		return status.Errorf(codes.Internal, "invalid operation type in GetOpts: %v", o)
   139  	}
   140  
   141  	// Apply the rule, ensure it allows the tree type and state that we have.
   142  	if !rule.okTypes[tree.TreeType] || !rule.okStates[tree.TreeState] {
   143  		// If we have a status code to use it takes precedence, otherwise it's
   144  		// a generic InvalidArgument code.
   145  		code, ok := rule.rejectCodes[tree.TreeState]
   146  		if !ok {
   147  			code = codes.InvalidArgument
   148  		}
   149  		return status.Errorf(code, "operation: %v not allowed for tree type: %v state: %v", o.Operation, tree.TreeType, tree.TreeState)
   150  	}
   151  
   152  	return nil
   153  }
   154  
   155  // GetTree returns the specified tree, either from the ctx (if present) or read from storage.
   156  // The tree will be validated according to GetOpts before returned. Tree state is also considered
   157  // (for example, deleted tree will return NotFound errors).
   158  func GetTree(ctx context.Context, s storage.AdminStorage, treeID int64, opts GetOpts) (*trillian.Tree, error) {
   159  	ctx, span := spanFor(ctx, "GetTree")
   160  	defer span.End()
   161  	tree, ok := FromContext(ctx)
   162  	if !ok || tree.TreeId != treeID {
   163  		var err error
   164  		tree, err = storage.GetTree(ctx, s, treeID)
   165  		if err != nil {
   166  			return nil, err
   167  		}
   168  	}
   169  
   170  	if err := validate(opts, tree); err != nil {
   171  		return nil, err
   172  	}
   173  	if tree.Deleted {
   174  		return nil, status.Errorf(codes.NotFound, "tree %v not found", tree.TreeId)
   175  	}
   176  
   177  	return tree, nil
   178  }
   179  
   180  // Hash returns the crypto.Hash configured by the tree.
   181  func Hash(tree *trillian.Tree) (crypto.Hash, error) {
   182  	switch tree.HashAlgorithm {
   183  	case sigpb.DigitallySigned_SHA256:
   184  		return crypto.SHA256, nil
   185  	}
   186  	// There's no nil-like value for crypto.Hash, something has to be returned.
   187  	return crypto.SHA256, fmt.Errorf("unexpected hash algorithm: %s", tree.HashAlgorithm)
   188  }
   189  
   190  // Signer returns a Trillian crypto.Signer configured by the tree.
   191  func Signer(ctx context.Context, tree *trillian.Tree) (*tcrypto.Signer, error) {
   192  	if tree.SignatureAlgorithm == sigpb.DigitallySigned_ANONYMOUS {
   193  		return nil, fmt.Errorf("signature algorithm not supported: %s", tree.SignatureAlgorithm)
   194  	}
   195  
   196  	hash, err := Hash(tree)
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  
   201  	var keyProto ptypes.DynamicAny
   202  	if err := ptypes.UnmarshalAny(tree.PrivateKey, &keyProto); err != nil {
   203  		return nil, fmt.Errorf("failed to unmarshal tree.PrivateKey: %v", err)
   204  	}
   205  
   206  	signer, err := keys.NewSigner(ctx, keyProto.Message)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	if tcrypto.SignatureAlgorithm(signer.Public()) != tree.SignatureAlgorithm {
   212  		return nil, fmt.Errorf("%s signature not supported by signer of type %T", tree.SignatureAlgorithm, signer)
   213  	}
   214  
   215  	return tcrypto.NewSigner(tree.GetTreeId(), signer, hash), nil
   216  }
   217  
   218  func spanFor(ctx context.Context, name string) (context.Context, *trace.Span) {
   219  	return trace.StartSpan(ctx, fmt.Sprintf("%s.%s", traceSpanRoot, name))
   220  }