|
| 1 | +/* |
| 2 | +Copyright IBM Corp. All Rights Reserved. |
| 3 | +
|
| 4 | +SPDX-License-Identifier: Apache-2.0 |
| 5 | +*/ |
| 6 | + |
| 7 | +package inquire |
| 8 | + |
| 9 | +import ( |
| 10 | + "reflect" |
| 11 | +) |
| 12 | + |
| 13 | +// ComparablePrincipalSets aggregate ComparablePrincipalSets |
| 14 | +type ComparablePrincipalSets []ComparablePrincipalSet |
| 15 | + |
| 16 | +// Merge returns ComparablePrincipalSets that the underlying PrincipalSets consist of |
| 17 | +// PrincipalSets that satisfy the endorsement policies that both ComparablePrincipalSets were derived of. |
| 18 | +// More formally speaking, let EP1 and EP2 be endorsement policies, and |
| 19 | +// P1 and P2 be the principal sets that each principal set p in P1 satisfies EP1, |
| 20 | +// and each principal set p in P2 satisfies EP2. |
| 21 | +// Denote as S1 and S2 the ComparablePrincipalSets derived from EP1 and EP2 respectively. |
| 22 | +// Then, S = Merge(S1, S2) wields ComparablePrincipalSets |
| 23 | +// such that every ComparablePrincipalSet s in S, satisfies both EP1 and EP2. |
| 24 | +func Merge(s1, s2 ComparablePrincipalSets) ComparablePrincipalSets { |
| 25 | + var res ComparablePrincipalSets |
| 26 | + setsIn1ToTheContainingSetsIn2 := computeContainedInMapping(s1, s2) |
| 27 | + setsIn1ThatAreIn2 := s1.OfMapping(setsIn1ToTheContainingSetsIn2, s2) |
| 28 | + // Without loss of generality, remove all principal sets in s1 that |
| 29 | + // are contained by principal sets in s2, in order not to have duplicates |
| 30 | + s1 = s1.ExcludeIndices(setsIn1ToTheContainingSetsIn2) |
| 31 | + setsIn2ToTheContainingSetsIn1 := computeContainedInMapping(s2, s1) |
| 32 | + setsIn2ThatAreIn1 := s2.OfMapping(setsIn2ToTheContainingSetsIn1, s1) |
| 33 | + s2 = s2.ExcludeIndices(setsIn2ToTheContainingSetsIn1) |
| 34 | + |
| 35 | + // In the interim, the result contains sets from either the first of the second |
| 36 | + // set, that also contain some other set(s) in the other set |
| 37 | + res = append(res, setsIn1ThatAreIn2.ToMergedPrincipalSets()...) |
| 38 | + res = append(res, setsIn2ThatAreIn1.ToMergedPrincipalSets()...) |
| 39 | + |
| 40 | + // Now, purge the principal sets from the original groups s1 and s2 |
| 41 | + // that are found to contain sets from the other group. |
| 42 | + // The motivation is to be left with s1 and s2 that only contain sets that aren't |
| 43 | + // contained or contain any set of the other group. |
| 44 | + s1 = s1.ExcludeIndices(setsIn2ToTheContainingSetsIn1.invert()) |
| 45 | + s2 = s2.ExcludeIndices(setsIn1ToTheContainingSetsIn2.invert()) |
| 46 | + |
| 47 | + // We're left with principal sets either in both or in one of the sets |
| 48 | + // that are entirely contained by the other sets, so there is nothing more to be done |
| 49 | + if len(s1) == 0 || len(s2) == 0 { |
| 50 | + return res.Reduce() |
| 51 | + } |
| 52 | + |
| 53 | + // We're only left with sets that are not contained in the other sets, |
| 54 | + // in both sets. Therefore we should combine them to form principal sets |
| 55 | + // that contain both sets. |
| 56 | + combinedPairs := CartesianProduct(s1, s2) |
| 57 | + res = append(res, combinedPairs.ToMergedPrincipalSets()...) |
| 58 | + return res.Reduce() |
| 59 | +} |
| 60 | + |
| 61 | +// CartesianProduct returns a comparablePrincipalSetPairs that is comprised of the combination |
| 62 | +// of every possible pair of ComparablePrincipalSet such that the first element is in s1, |
| 63 | +// and the second element is in s2. |
| 64 | +func CartesianProduct(s1, s2 ComparablePrincipalSets) comparablePrincipalSetPairs { |
| 65 | + var res comparablePrincipalSetPairs |
| 66 | + for _, x := range s1 { |
| 67 | + var set comparablePrincipalSetPairs |
| 68 | + // For every set in the first sets, |
| 69 | + // combine it with every set in the second sets |
| 70 | + for _, y := range s2 { |
| 71 | + set = append(set, comparablePrincipalSetPair{ |
| 72 | + contained: x, |
| 73 | + containing: y, |
| 74 | + }) |
| 75 | + } |
| 76 | + res = append(res, set...) |
| 77 | + } |
| 78 | + return res |
| 79 | +} |
| 80 | + |
| 81 | +// comparablePrincipalSetPair is a tuple of 2 ComparablePrincipalSets |
| 82 | +type comparablePrincipalSetPair struct { |
| 83 | + contained ComparablePrincipalSet |
| 84 | + containing ComparablePrincipalSet |
| 85 | +} |
| 86 | + |
| 87 | +// EnsurePlurality returns a ComparablePrincipalSet such that plurality requirements over |
| 88 | +// the contained ComparablePrincipalSet in the comparablePrincipalSetPair hold |
| 89 | +func (pair comparablePrincipalSetPair) MergeWithPlurality() ComparablePrincipalSet { |
| 90 | + var principalsToAdd []*ComparablePrincipal |
| 91 | + used := make(map[int]struct{}) |
| 92 | + // Iterate over the contained set and for each principal |
| 93 | + for _, principal := range pair.contained { |
| 94 | + var covered bool |
| 95 | + // Search a principal in the containing set to cover the principal in the contained set |
| 96 | + for i, coveringPrincipal := range pair.containing { |
| 97 | + // The principal found shouldn't be used twice |
| 98 | + if _, isUsed := used[i]; isUsed { |
| 99 | + continue |
| 100 | + } |
| 101 | + // All identities that satisfy the found principal, should satisfy the covered principal as well. |
| 102 | + if coveringPrincipal.IsA(principal) { |
| 103 | + used[i] = struct{}{} |
| 104 | + covered = true |
| 105 | + break |
| 106 | + } |
| 107 | + } |
| 108 | + // If we haven't found a cover to the principal, it's because we already used up all the potential candidates |
| 109 | + // among the containing set, so just add it to the principals set to be added later. |
| 110 | + if !covered { |
| 111 | + principalsToAdd = append(principalsToAdd, principal) |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + res := pair.containing.Clone() |
| 116 | + res = append(res, principalsToAdd...) |
| 117 | + return res |
| 118 | +} |
| 119 | + |
| 120 | +// comparablePrincipalSetPairs aggregates []comparablePrincipalSetPairs |
| 121 | +type comparablePrincipalSetPairs []comparablePrincipalSetPair |
| 122 | + |
| 123 | +// ToPrincipalSets converts the comparablePrincipalSetPairs to ComparablePrincipalSets |
| 124 | +// while taking into account plurality of each pair |
| 125 | +func (pairs comparablePrincipalSetPairs) ToMergedPrincipalSets() ComparablePrincipalSets { |
| 126 | + var res ComparablePrincipalSets |
| 127 | + for _, pair := range pairs { |
| 128 | + res = append(res, pair.MergeWithPlurality()) |
| 129 | + } |
| 130 | + return res |
| 131 | +} |
| 132 | + |
| 133 | +// OfMapping returns comparablePrincipalSetPairs comprising only of the indices found in the given keys |
| 134 | +func (cps ComparablePrincipalSets) OfMapping(mapping map[int][]int, sets2 ComparablePrincipalSets) comparablePrincipalSetPairs { |
| 135 | + var res []comparablePrincipalSetPair |
| 136 | + for i, js := range mapping { |
| 137 | + for _, j := range js { |
| 138 | + res = append(res, comparablePrincipalSetPair{ |
| 139 | + contained: cps[i], |
| 140 | + containing: sets2[j], |
| 141 | + }) |
| 142 | + } |
| 143 | + } |
| 144 | + return res |
| 145 | +} |
| 146 | + |
| 147 | +// Reduce returns the ComparablePrincipalSets in a form such that no element contains another element. |
| 148 | +// Every element that contains some other element is omitted from the result. |
| 149 | +func (cps ComparablePrincipalSets) Reduce() ComparablePrincipalSets { |
| 150 | + var res ComparablePrincipalSets |
| 151 | + for i, s1 := range cps { |
| 152 | + var isContaining bool |
| 153 | + for j, s2 := range cps { |
| 154 | + if i == j { |
| 155 | + continue |
| 156 | + } |
| 157 | + if s2.IsSubset(s1) { |
| 158 | + isContaining = true |
| 159 | + } |
| 160 | + } |
| 161 | + if !isContaining { |
| 162 | + res = append(res, s1) |
| 163 | + } |
| 164 | + } |
| 165 | + return res |
| 166 | +} |
| 167 | + |
| 168 | +// ExcludeIndices returns a ComparablePrincipalSets without the given indices found in the keys |
| 169 | +func (cps ComparablePrincipalSets) ExcludeIndices(mapping map[int][]int) ComparablePrincipalSets { |
| 170 | + var res ComparablePrincipalSets |
| 171 | + for i, set := range cps { |
| 172 | + if _, exists := mapping[i]; exists { |
| 173 | + continue |
| 174 | + } |
| 175 | + res = append(res, set) |
| 176 | + } |
| 177 | + return res |
| 178 | +} |
| 179 | + |
| 180 | +// Contains returns whether this ComparablePrincipalSet contains the given ComparablePrincipal. |
| 181 | +// A ComparablePrincipalSet X contains a ComparablePrincipal y if |
| 182 | +// there is a ComparablePrincipal x in X such that x.IsA(y). |
| 183 | +// From here it follows that every signature set that satisfies X, also satisfies y. |
| 184 | +func (cps ComparablePrincipalSet) Contains(s *ComparablePrincipal) bool { |
| 185 | + for _, cp := range cps { |
| 186 | + if cp.IsA(s) { |
| 187 | + return true |
| 188 | + } |
| 189 | + } |
| 190 | + return false |
| 191 | +} |
| 192 | + |
| 193 | +// IsContainedIn returns whether this ComparablePrincipalSet is contained in the given ComparablePrincipalSet. |
| 194 | +// More formally- a ComparablePrincipalSet X is said to be contained in ComparablePrincipalSet Y |
| 195 | +// if for each ComparablePrincipalSet x in X there is a ComparablePrincipalSet y in Y such that y.IsA(x) is true. |
| 196 | +// If a ComparablePrincipalSet X is contained by a ComparablePrincipalSet Y then if a signature set satisfies Y, |
| 197 | +// it also satisfies X, because for each x in X there is a y in Y such that there exists a signature of a corresponding |
| 198 | +// identity such that the identity satisfies y, and therefore satisfies x too. |
| 199 | +func (cps ComparablePrincipalSet) IsContainedIn(set ComparablePrincipalSet) bool { |
| 200 | + for _, cp := range cps { |
| 201 | + if !set.Contains(cp) { |
| 202 | + return false |
| 203 | + } |
| 204 | + } |
| 205 | + return true |
| 206 | +} |
| 207 | + |
| 208 | +// computeContainedInMapping returns a mapping from the indices in the first ComparablePrincipalSets |
| 209 | +// to the indices in the second ComparablePrincipalSets that the corresponding ComparablePrincipalSets in |
| 210 | +// the first ComparablePrincipalSets are contained in the second ComparablePrincipalSets given. |
| 211 | +func computeContainedInMapping(s1, s2 []ComparablePrincipalSet) intMapping { |
| 212 | + mapping := make(map[int][]int) |
| 213 | + for i, ps1 := range s1 { |
| 214 | + for j, ps2 := range s2 { |
| 215 | + if !ps1.IsContainedIn(ps2) { |
| 216 | + continue |
| 217 | + } |
| 218 | + mapping[i] = append(mapping[i], j) |
| 219 | + } |
| 220 | + } |
| 221 | + return mapping |
| 222 | +} |
| 223 | + |
| 224 | +// intMapping maps integers to sets of integers |
| 225 | +type intMapping map[int][]int |
| 226 | + |
| 227 | +func (im intMapping) invert() intMapping { |
| 228 | + res := make(intMapping) |
| 229 | + for i, js := range im { |
| 230 | + for _, j := range js { |
| 231 | + res[j] = append(res[j], i) |
| 232 | + } |
| 233 | + } |
| 234 | + return res |
| 235 | +} |
| 236 | + |
| 237 | +// IsSubset returns whether this ComparablePrincipalSet is a subset of the given ComparablePrincipalSet |
| 238 | +func (cps ComparablePrincipalSet) IsSubset(sets ComparablePrincipalSet) bool { |
| 239 | + for _, p1 := range cps { |
| 240 | + var found bool |
| 241 | + for _, p2 := range sets { |
| 242 | + if reflect.DeepEqual(p1, p2) { |
| 243 | + found = true |
| 244 | + } |
| 245 | + } |
| 246 | + if !found { |
| 247 | + return false |
| 248 | + } |
| 249 | + } |
| 250 | + return true |
| 251 | +} |
0 commit comments