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 }