identifiers.go 9.08 KB
Newer Older
1
package irma
2

3
import (
4
	"database/sql/driver" // only imported to refer to the driver.Value type
5 6
	"fmt"
	"strings"
7

8
	"github.com/fxamacker/cbor"
9 10
	"github.com/go-errors/errors"
	"github.com/jinzhu/gorm"
11
)
12

Sietse Ringers's avatar
Sietse Ringers committed
13
type metaObjectIdentifier string
14

15 16
func (oi *metaObjectIdentifier) UnmarshalCBOR(data []byte) error {
	return cbor.Unmarshal(data, (*string)(oi))
17 18
}

19 20
func (oi metaObjectIdentifier) MarshalCBOR() (data []byte, err error) {
	return cbor.Marshal(string(oi), cbor.EncOptions{})
21 22
}

23 24
// SchemeManagerIdentifier identifies a scheme manager. Equal to its ID. For example "irma-demo".
type SchemeManagerIdentifier struct {
Sietse Ringers's avatar
Sietse Ringers committed
25
	metaObjectIdentifier
26 27
}

Sietse Ringers's avatar
Sietse Ringers committed
28
// IssuerIdentifier identifies an issuer. For example "irma-demo.RU".
29
type IssuerIdentifier struct {
Sietse Ringers's avatar
Sietse Ringers committed
30
	metaObjectIdentifier
31 32
}

33 34
// CredentialTypeIdentifier identifies a credentialtype. For example "irma-demo.RU.studentCard".
type CredentialTypeIdentifier struct {
Sietse Ringers's avatar
Sietse Ringers committed
35
	metaObjectIdentifier
36 37
}

38 39
// AttributeTypeIdentifier identifies an attribute. For example "irma-demo.RU.studentCard.studentID".
type AttributeTypeIdentifier struct {
Sietse Ringers's avatar
Sietse Ringers committed
40
	metaObjectIdentifier
41 42
}

Sietse Ringers's avatar
Sietse Ringers committed
43
// CredentialIdentifier identifies a credential instance.
44
type CredentialIdentifier struct {
45 46
	Type CredentialTypeIdentifier
	Hash string
47 48
}

Sietse Ringers's avatar
Sietse Ringers committed
49
// AttributeIdentifier identifies an attribute instance.
50
type AttributeIdentifier struct {
51 52
	Type           AttributeTypeIdentifier
	CredentialHash string
53 54
}

55
// IrmaIdentifierSet contains a set (ensured by using map[...]struct{}) of all scheme managers,
Leon's avatar
Leon committed
56
// all issuers, all credential types, all public keys and all attribute types that are involved in an IRMA session.
57 58 59 60
type IrmaIdentifierSet struct {
	SchemeManagers  map[SchemeManagerIdentifier]struct{}
	Issuers         map[IssuerIdentifier]struct{}
	CredentialTypes map[CredentialTypeIdentifier]struct{}
61
	PublicKeys      map[IssuerIdentifier][]uint
Leon's avatar
Leon committed
62
	AttributeTypes  map[AttributeTypeIdentifier]struct{}
63 64
}

65 66 67 68 69
func newIrmaIdentifierSet() *IrmaIdentifierSet {
	return &IrmaIdentifierSet{
		SchemeManagers:  map[SchemeManagerIdentifier]struct{}{},
		Issuers:         map[IssuerIdentifier]struct{}{},
		CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
70
		PublicKeys:      map[IssuerIdentifier][]uint{},
Leon's avatar
Leon committed
71
		AttributeTypes:  map[AttributeTypeIdentifier]struct{}{},
72 73 74
	}
}

Sietse Ringers's avatar
Sietse Ringers committed
75
// Parent returns the parent object of this identifier.
Sietse Ringers's avatar
Sietse Ringers committed
76
func (oi metaObjectIdentifier) Parent() string {
77
	str := string(oi)
Sietse Ringers's avatar
Sietse Ringers committed
78 79 80 81 82
	if i := strings.LastIndex(str, "."); i != -1 {
		return str[:i]
	} else {
		return str
	}
83 84
}

Sietse Ringers's avatar
Sietse Ringers committed
85
// Name returns the last part of this identifier.
Sietse Ringers's avatar
Sietse Ringers committed
86
func (oi metaObjectIdentifier) Name() string {
87
	str := string(oi)
Sietse Ringers's avatar
Sietse Ringers committed
88
	return str[strings.LastIndex(str, ".")+1:]
89 90
}

Sietse Ringers's avatar
Sietse Ringers committed
91
// String returns this identifier as a string.
Sietse Ringers's avatar
Sietse Ringers committed
92
func (oi metaObjectIdentifier) String() string {
93 94 95
	return string(oi)
}

96 97 98 99
func (oi metaObjectIdentifier) Empty() bool {
	return len(oi) == 0
}

Sietse Ringers's avatar
Sietse Ringers committed
100 101 102 103 104 105 106 107 108
func (oi metaObjectIdentifier) Root() string {
	str := string(oi)
	if i := strings.Index(str, "."); i != -1 {
		return str[:i]
	} else {
		return str
	}
}

109 110
// NewSchemeManagerIdentifier converts the specified identifier to a SchemeManagerIdentifier.
func NewSchemeManagerIdentifier(id string) SchemeManagerIdentifier {
Sietse Ringers's avatar
Sietse Ringers committed
111
	return SchemeManagerIdentifier{metaObjectIdentifier(id)}
112 113 114 115
}

// NewIssuerIdentifier converts the specified identifier to a IssuerIdentifier.
func NewIssuerIdentifier(id string) IssuerIdentifier {
Sietse Ringers's avatar
Sietse Ringers committed
116
	return IssuerIdentifier{metaObjectIdentifier(id)}
117 118
}

119 120
// NewCredentialTypeIdentifier converts the specified identifier to a CredentialTypeIdentifier.
func NewCredentialTypeIdentifier(id string) CredentialTypeIdentifier {
Sietse Ringers's avatar
Sietse Ringers committed
121
	return CredentialTypeIdentifier{metaObjectIdentifier(id)}
122 123
}

124 125
// NewAttributeTypeIdentifier converts the specified identifier to a AttributeTypeIdentifier.
func NewAttributeTypeIdentifier(id string) AttributeTypeIdentifier {
Sietse Ringers's avatar
Sietse Ringers committed
126
	return AttributeTypeIdentifier{metaObjectIdentifier(id)}
127 128 129 130 131 132 133 134
}

// SchemeManagerIdentifier returns the scheme manager identifer of the issuer.
func (id IssuerIdentifier) SchemeManagerIdentifier() SchemeManagerIdentifier {
	return NewSchemeManagerIdentifier(id.Parent())
}

// IssuerIdentifier returns the IssuerIdentifier of the credential identifier.
135
func (id CredentialTypeIdentifier) IssuerIdentifier() IssuerIdentifier {
136 137 138
	return NewIssuerIdentifier(id.Parent())
}

139
// CredentialTypeIdentifier returns the CredentialTypeIdentifier of the attribute identifier.
140
func (id AttributeTypeIdentifier) CredentialTypeIdentifier() CredentialTypeIdentifier {
141 142 143
	if id.IsCredential() {
		return NewCredentialTypeIdentifier(id.String())
	}
144
	return NewCredentialTypeIdentifier(id.Parent())
145
}
146

Sietse Ringers's avatar
Sietse Ringers committed
147 148
// IsCredential returns true if this attribute refers to its containing credential
// (i.e., it consists of only 3 parts).
149 150 151
func (id AttributeTypeIdentifier) IsCredential() bool {
	return strings.Count(id.String(), ".") == 2
}
152

Sietse Ringers's avatar
Sietse Ringers committed
153
// CredentialIdentifier returns the credential identifier of this attribute.
154
func (ai *AttributeIdentifier) CredentialIdentifier() CredentialIdentifier {
155
	return CredentialIdentifier{Type: ai.Type.CredentialTypeIdentifier(), Hash: ai.CredentialHash}
156
}
157

158 159 160
// MarshalText implements encoding.TextMarshaler.
func (id SchemeManagerIdentifier) MarshalText() ([]byte, error) {
	return []byte(id.String()), nil
161 162
}

163 164 165 166 167 168
// UnmarshalText implements encoding.TextUnmarshaler.
func (id *SchemeManagerIdentifier) UnmarshalText(text []byte) error {
	*id = NewSchemeManagerIdentifier(string(text))
	return nil
}

Sietse Ringers's avatar
Sietse Ringers committed
169 170 171 172 173
// MarshalText implements encoding.TextMarshaler.
func (id IssuerIdentifier) MarshalText() ([]byte, error) {
	return []byte(id.String()), nil
}

Tomas's avatar
Tomas committed
174 175 176 177 178
// UnmarshalText implements encoding.TextUnmarshaler.
func (id *IssuerIdentifier) UnmarshalText(text []byte) error {
	*id = NewIssuerIdentifier(string(text))
	return nil
}
179

180 181 182 183 184 185 186 187
// MarshalText implements encoding.TextMarshaler.
func (id CredentialTypeIdentifier) MarshalText() ([]byte, error) {
	return []byte(id.String()), nil
}

// UnmarshalText implements encoding.TextUnmarshaler.
func (id *CredentialTypeIdentifier) UnmarshalText(text []byte) error {
	*id = NewCredentialTypeIdentifier(string(text))
188 189
	return nil
}
Sietse Ringers's avatar
Sietse Ringers committed
190

191 192 193 194 195 196 197 198
// MarshalText implements encoding.TextMarshaler.
func (id AttributeTypeIdentifier) MarshalText() ([]byte, error) {
	return []byte(id.String()), nil
}

// UnmarshalText implements encoding.TextUnmarshaler.
func (id *AttributeTypeIdentifier) UnmarshalText(text []byte) error {
	*id = NewAttributeTypeIdentifier(string(text))
Sietse Ringers's avatar
Sietse Ringers committed
199 200
	return nil
}
201

202 203 204 205 206 207 208 209 210 211
func (set *IrmaIdentifierSet) join(other *IrmaIdentifierSet) {
	for scheme := range other.SchemeManagers {
		set.SchemeManagers[scheme] = struct{}{}
	}
	for issuer := range other.Issuers {
		set.Issuers[issuer] = struct{}{}
	}
	for ct := range other.CredentialTypes {
		set.CredentialTypes[ct] = struct{}{}
	}
Leon's avatar
Leon committed
212 213 214
	for at := range other.AttributeTypes {
		set.AttributeTypes[at] = struct{}{}
	}
215 216
	for issuer := range other.PublicKeys {
		if len(set.PublicKeys[issuer]) == 0 {
217
			set.PublicKeys[issuer] = make([]uint, 0, len(other.PublicKeys[issuer]))
218 219 220 221 222
		}
		set.PublicKeys[issuer] = append(set.PublicKeys[issuer], other.PublicKeys[issuer]...)
	}
}

223
func (set *IrmaIdentifierSet) Distributed(conf *Configuration) bool {
224
	for id := range set.SchemeManagers {
225
		if conf.SchemeManagers[id].Distributed() {
226 227 228 229 230
			return true
		}
	}
	return false
}
231

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
func (set *IrmaIdentifierSet) allSchemes() map[SchemeManagerIdentifier]struct{} {
	schemes := make(map[SchemeManagerIdentifier]struct{})
	for s := range set.SchemeManagers {
		schemes[s] = struct{}{}
	}
	for i := range set.Issuers {
		schemes[i.SchemeManagerIdentifier()] = struct{}{}
	}
	for i := range set.PublicKeys {
		if len(set.PublicKeys[i]) > 0 {
			schemes[i.SchemeManagerIdentifier()] = struct{}{}
		}
	}
	for c := range set.CredentialTypes {
		schemes[c.IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{}
	}
Leon's avatar
Leon committed
248 249 250
	for a := range set.AttributeTypes {
		schemes[a.CredentialTypeIdentifier().IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{}
	}
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
	return schemes
}

func (set *IrmaIdentifierSet) String() string {
	var builder strings.Builder
	for s := range set.SchemeManagers {
		builder.WriteString(s.String() + ", ")
	}
	for i := range set.Issuers {
		builder.WriteString(i.String() + ", ")
	}
	for i, keys := range set.PublicKeys {
		for _, k := range keys {
			builder.WriteString(fmt.Sprintf("%s-%d", i.String(), k))
		}
	}
	for c := range set.CredentialTypes {
		builder.WriteString(c.String() + ", ")
	}
Leon's avatar
Leon committed
270 271 272
	for a := range set.AttributeTypes {
		builder.WriteString(a.String() + ", ")
	}
273 274 275 276 277 278 279
	s := builder.String()
	if len(s) > 0 { // strip trailing comma
		s = s[:len(s)-2]
	}
	return s
}

280
func (set *IrmaIdentifierSet) Empty() bool {
Leon's avatar
Leon committed
281
	return len(set.SchemeManagers) == 0 && len(set.Issuers) == 0 && len(set.CredentialTypes) == 0 && len(set.PublicKeys) == 0 && len(set.AttributeTypes) == 0
282
}
283 284 285 286 287 288

func (oi metaObjectIdentifier) Value() (driver.Value, error) {
	return oi.String(), nil
}

func (oi *metaObjectIdentifier) Scan(src interface{}) error {
289 290 291 292 293 294 295
	switch s := src.(type) {
	case string:
		*oi = metaObjectIdentifier(s)
		return nil
	case []byte:
		*oi = metaObjectIdentifier(s)
		return nil
296
	}
297
	return errors.New("cannot convert source: not a string or []byte")
298 299 300
}

func (metaObjectIdentifier) GormDataType(dialect gorm.Dialect) string {
301 302
	switch dialect.GetName() {
	case "postgres":
303
		return "text"
304
	case "mysql":
305
		return "varchar(255)"
306 307
	default:
		return ""
308 309
	}
}