github.com/bartle-stripe/trillian@v1.2.1/storage/tree_validation.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 storage
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  
    21  	"github.com/golang/protobuf/proto"
    22  	"github.com/golang/protobuf/ptypes"
    23  	"github.com/google/trillian"
    24  	"github.com/google/trillian/crypto/keys"
    25  	"github.com/google/trillian/crypto/keys/der"
    26  	"github.com/google/trillian/crypto/sigpb"
    27  	"google.golang.org/grpc/codes"
    28  	"google.golang.org/grpc/status"
    29  )
    30  
    31  const (
    32  	maxDisplayNameLength = 20
    33  	maxDescriptionLength = 200
    34  )
    35  
    36  // ValidateTreeForCreation returns nil if tree is valid for insertion, error
    37  // otherwise.
    38  // See the documentation on trillian.Tree for reference on which values are
    39  // valid.
    40  func ValidateTreeForCreation(ctx context.Context, tree *trillian.Tree) error {
    41  	switch {
    42  	case tree == nil:
    43  		return status.Error(codes.InvalidArgument, "a tree is required")
    44  	case tree.TreeState != trillian.TreeState_ACTIVE:
    45  		return status.Errorf(codes.InvalidArgument, "invalid tree_state: %s", tree.TreeState)
    46  	case tree.TreeType == trillian.TreeType_UNKNOWN_TREE_TYPE:
    47  		return status.Errorf(codes.InvalidArgument, "invalid tree_type: %s", tree.TreeType)
    48  	case tree.HashStrategy == trillian.HashStrategy_UNKNOWN_HASH_STRATEGY:
    49  		return status.Errorf(codes.InvalidArgument, "invalid hash_strategy: %s", tree.HashStrategy)
    50  	case tree.HashAlgorithm == sigpb.DigitallySigned_NONE:
    51  		return status.Errorf(codes.InvalidArgument, "invalid hash_algorithm: %s", tree.HashAlgorithm)
    52  	case tree.SignatureAlgorithm == sigpb.DigitallySigned_ANONYMOUS:
    53  		return status.Errorf(codes.InvalidArgument, "invalid signature_algorithm: %s", tree.SignatureAlgorithm)
    54  	case tree.PrivateKey == nil:
    55  		return status.Error(codes.InvalidArgument, "a private_key is required")
    56  	case tree.PublicKey == nil:
    57  		return status.Error(codes.InvalidArgument, "a public_key is required")
    58  	case tree.Deleted:
    59  		return status.Errorf(codes.InvalidArgument, "invalid deleted: %v", tree.Deleted)
    60  	case tree.DeleteTime != nil:
    61  		return status.Errorf(codes.InvalidArgument, "invalid delete_time: %+v (must be nil)", tree.DeleteTime)
    62  	}
    63  
    64  	return validateMutableTreeFields(ctx, tree)
    65  }
    66  
    67  // validateTreeTypeUpdate returns nil iff oldTree.TreeType can be updated to
    68  // newTree.TreeType. The tree type is changeable only if the Tree is and
    69  // remains in the FROZEN state.
    70  // At the moment only PREORDERED_LOG->LOG type transition is permitted.
    71  func validateTreeTypeUpdate(oldTree, newTree *trillian.Tree) error {
    72  	const prefix = "can't change tree_type"
    73  
    74  	const wantState = trillian.TreeState_FROZEN
    75  	if oldState := oldTree.TreeState; oldState != wantState {
    76  		return status.Errorf(codes.InvalidArgument, "%s: tree_state=%v, want %v", prefix, oldState, wantState)
    77  	} else if newTree.TreeState != wantState {
    78  		return status.Errorf(codes.InvalidArgument, "%s: tree_state should stay %v", prefix, wantState)
    79  	}
    80  
    81  	if oldTree.TreeType != trillian.TreeType_PREORDERED_LOG || newTree.TreeType != trillian.TreeType_LOG {
    82  		return status.Errorf(codes.InvalidArgument, "%s: %v->%v", prefix, oldTree.TreeType, newTree.TreeType)
    83  	}
    84  	return nil
    85  }
    86  
    87  // ValidateTreeForUpdate returns nil if newTree is valid for update, error
    88  // otherwise.
    89  // The newTree is compared to the storedTree to determine if readonly fields
    90  // have been changed. It's assumed that storage-generated fields, such as
    91  // update_time, have not yet changed when this method is called.
    92  // See the documentation on trillian.Tree for reference on which fields may be
    93  // changed and what is considered valid for each of them.
    94  func ValidateTreeForUpdate(ctx context.Context, storedTree, newTree *trillian.Tree) error {
    95  	// Check that readonly fields didn't change
    96  	switch {
    97  	case storedTree.TreeId != newTree.TreeId:
    98  		return status.Error(codes.InvalidArgument, "readonly field changed: tree_id")
    99  	case storedTree.TreeType != newTree.TreeType:
   100  		if err := validateTreeTypeUpdate(storedTree, newTree); err != nil {
   101  			return err
   102  		}
   103  	case storedTree.HashStrategy != newTree.HashStrategy:
   104  		return status.Error(codes.InvalidArgument, "readonly field changed: hash_strategy")
   105  	case storedTree.HashAlgorithm != newTree.HashAlgorithm:
   106  		return status.Error(codes.InvalidArgument, "readonly field changed: hash_algorithm")
   107  	case storedTree.SignatureAlgorithm != newTree.SignatureAlgorithm:
   108  		return status.Error(codes.InvalidArgument, "readonly field changed: signature_algorithm")
   109  	case !proto.Equal(storedTree.CreateTime, newTree.CreateTime):
   110  		return status.Error(codes.InvalidArgument, "readonly field changed: create_time")
   111  	case !proto.Equal(storedTree.UpdateTime, newTree.UpdateTime):
   112  		return status.Error(codes.InvalidArgument, "readonly field changed: update_time")
   113  	case !proto.Equal(storedTree.PublicKey, newTree.PublicKey):
   114  		return status.Error(codes.InvalidArgument, "readonly field changed: public_key")
   115  	case storedTree.Deleted != newTree.Deleted:
   116  		return status.Error(codes.InvalidArgument, "readonly field changed: deleted")
   117  	case !proto.Equal(storedTree.DeleteTime, newTree.DeleteTime):
   118  		return status.Error(codes.InvalidArgument, "readonly field changed: delete_time")
   119  	}
   120  	return validateMutableTreeFields(ctx, newTree)
   121  }
   122  
   123  func validateMutableTreeFields(ctx context.Context, tree *trillian.Tree) error {
   124  	switch {
   125  	case tree.TreeState == trillian.TreeState_UNKNOWN_TREE_STATE:
   126  		return status.Errorf(codes.InvalidArgument, "invalid tree_state: %v", tree.TreeState)
   127  	case len(tree.DisplayName) > maxDisplayNameLength:
   128  		return status.Errorf(codes.InvalidArgument, "display_name too big, max length is %v: %v", maxDisplayNameLength, tree.DisplayName)
   129  	case len(tree.Description) > maxDescriptionLength:
   130  		return status.Errorf(codes.InvalidArgument, "description too big, max length is %v: %v", maxDescriptionLength, tree.Description)
   131  	}
   132  	if duration, err := ptypes.Duration(tree.MaxRootDuration); err != nil {
   133  		return status.Errorf(codes.InvalidArgument, "max_root_duration malformed: %v", tree.MaxRootDuration)
   134  	} else if duration < 0 {
   135  		return status.Errorf(codes.InvalidArgument, "max_root_duration negative: %v", tree.MaxRootDuration)
   136  	}
   137  
   138  	// Implementations may vary, so let's assume storage_settings is mutable.
   139  	// Other than checking that it's a valid Any there isn't much to do at this layer, though.
   140  	if tree.StorageSettings != nil {
   141  		var settings ptypes.DynamicAny
   142  		if err := ptypes.UnmarshalAny(tree.StorageSettings, &settings); err != nil {
   143  			return status.Errorf(codes.InvalidArgument, "invalid storage_settings: %v", err)
   144  		}
   145  	}
   146  
   147  	var privateKeyProto ptypes.DynamicAny
   148  	if err := ptypes.UnmarshalAny(tree.PrivateKey, &privateKeyProto); err != nil {
   149  		return status.Errorf(codes.InvalidArgument, "invalid private_key: %v", err)
   150  	}
   151  
   152  	// Check that the private key can be obtained and matches the public key.
   153  	privateKey, err := keys.NewSigner(ctx, privateKeyProto.Message)
   154  	if err != nil {
   155  		return status.Errorf(codes.InvalidArgument, "invalid private_key: %v", err)
   156  	}
   157  	publicKeyDER, err := der.MarshalPublicKey(privateKey.Public())
   158  	if err != nil {
   159  		return status.Errorf(codes.InvalidArgument, "invalid private_key: %v", err)
   160  	}
   161  	if !bytes.Equal(publicKeyDER, tree.PublicKey.GetDer()) {
   162  		return status.Errorf(codes.InvalidArgument, "private_key and public_key are not a matching pair")
   163  	}
   164  
   165  	return nil
   166  }