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 }