-
Notifications
You must be signed in to change notification settings - Fork 111
Expand file tree
/
Copy pathsig.go
More file actions
142 lines (120 loc) · 4.66 KB
/
Copy pathsig.go
File metadata and controls
142 lines (120 loc) · 4.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package boolpolicy
import (
"encoding/asn1"
"github.com/LFDT-Panurus/panurus/token"
"github.com/LFDT-Panurus/panurus/token/driver"
"github.com/hyperledger-labs/fabric-smart-client/pkg/utils/errors"
)
// PolicySignature is the on-wire signature envelope for a policy identity.
// It carries one slot per component identity; slots for parties that did not
// sign are left nil/empty. This allows OR policies to be satisfied by a
// strict subset of the component signers.
//
// Wire format: ASN.1 SEQUENCE OF OCTET STRING (mirrors MultiSignature).
type PolicySignature struct {
Signatures [][]byte
}
// Bytes serialises the PolicySignature to ASN.1 DER.
func (s *PolicySignature) Bytes() ([]byte, error) {
return asn1.Marshal(*s)
}
// FromBytes deserialises raw ASN.1 DER into the receiver.
func (s *PolicySignature) FromBytes(raw []byte) error {
_, err := asn1.Unmarshal(raw, s)
return err
}
// JoinSignatures builds a PolicySignature from a map of per-identity
// signatures. Identities not present in sigmas receive a nil entry,
// which is valid as long as the policy does not require them.
// The order of the entries matches the order of identities.
func JoinSignatures(identities []token.Identity, sigmas map[string][]byte) ([]byte, error) {
signatures := make([][]byte, len(identities))
for k, id := range identities {
if sig, ok := sigmas[id.UniqueID()]; ok {
signatures[k] = sig
}
// absent entry stays nil — valid for OR branches
}
return (&PolicySignature{Signatures: signatures}).Bytes()
}
// PolicyVerifier verifies a PolicySignature against a parsed policy AST.
// It implements driver.Verifier.
//
// Verification walks the policy AST:
// - RefNode{i}: sigs[i] must be non-empty and Verifiers[i].Verify must succeed.
// - AndNode: both sub-trees must verify successfully.
// - OrNode: at least one sub-tree must verify successfully.
//
// This means a valid PolicySignature need only carry signatures for the
// identities actually required by the satisfied policy branch.
type PolicyVerifier struct {
// Policy is the parsed boolean AST, produced by Parse.
Policy Node
// Verifiers is indexed by $N; each entry verifies the corresponding
// component identity's individual signature.
Verifiers []driver.Verifier
}
// Verify implements driver.Verifier.
// sigBytes must be a PolicySignature ASN.1 DER blob produced by JoinSignatures.
func (v *PolicyVerifier) Verify(msg, sigBytes []byte) error {
sig := &PolicySignature{}
if err := sig.FromBytes(sigBytes); err != nil {
return errors.Wrap(err, "failed to unmarshal policy signature")
}
if len(sig.Signatures) != len(v.Verifiers) {
return errors.Errorf("policy signature has [%d] slots, expected [%d]",
len(sig.Signatures), len(v.Verifiers))
}
// memo caches each index's verification result for the duration of this
// Verify call: a $N reference is checked against the same (msg, sigs[N])
// no matter how many times it appears in the policy, so the potentially
// expensive Verifiers[N].Verify is invoked at most once per index.
memo := make([]refResult, len(sig.Signatures))
if !v.evalNode(v.Policy, msg, sig.Signatures, memo) {
return errors.New("policy not satisfied")
}
return nil
}
// refResult is the memoised outcome of verifying a single component index.
type refResult int8
const (
refUnknown refResult = iota // not yet verified in this Verify call
refPass // verification succeeded
refFail // verification failed (absent slot or bad signature)
)
// evalNode recursively evaluates the policy AST against the provided signatures.
// memo is indexed by component index ($N) and caches per-index verification
// results across the whole traversal; it must have one entry per signature slot.
func (v *PolicyVerifier) evalNode(node Node, msg []byte, sigs [][]byte, memo []refResult) bool {
switch n := node.(type) {
case *RefNode:
return v.evalRef(n.Index, msg, sigs, memo)
case *AndNode:
return v.evalNode(n.Left, msg, sigs, memo) && v.evalNode(n.Right, msg, sigs, memo)
case *OrNode:
return v.evalNode(n.Left, msg, sigs, memo) || v.evalNode(n.Right, msg, sigs, memo)
default:
return false
}
}
// evalRef verifies the component identity at index i, reusing a previously
// computed result for the same index when one is available.
func (v *PolicyVerifier) evalRef(i int, msg []byte, sigs [][]byte, memo []refResult) bool {
if i < 0 || i >= len(sigs) || len(sigs[i]) == 0 {
return false
}
if memo[i] != refUnknown {
return memo[i] == refPass
}
ok := v.Verifiers[i].Verify(msg, sigs[i]) == nil
if ok {
memo[i] = refPass
} else {
memo[i] = refFail
}
return ok
}