Commit 3a6e8e88 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Use types for manager, issuer, credential and attribute identifiers

parent 0c761ca8
......@@ -45,6 +45,20 @@ type CredentialType struct {
XMLName xml.Name `xml:"IssueSpecification"`
}
// ContainsAttribute tests whether the specified attribute is contained in this
// credentialtype.
func (ct *CredentialType) ContainsAttribute(ai AttributeIdentifier) bool {
if ai.CredentialIdentifier().String() != ct.Identifier().String() {
return false
}
for _, desc := range ct.Attributes {
if desc.ID == ai.Name() {
return true
}
}
return false
}
// AttributeDescription is a description of an attribute within a credential type.
type AttributeDescription struct {
ID string `xml:"id,attr"`
......@@ -92,18 +106,23 @@ func (ts *TranslatedString) Translation(lang string) string {
}
// Identifier returns the identifier of the specified credential type.
func (cd *CredentialType) Identifier() string {
return cd.SchemeManagerID + "." + cd.IssuerID + "." + cd.ID
func (ct *CredentialType) Identifier() CredentialIdentifier {
return NewCredentialIdentifier(ct.SchemeManagerID + "." + ct.IssuerID + "." + ct.ID)
}
// IssuerIdentifier returns the issuer identifier of the specified credential type.
func (cd *CredentialType) IssuerIdentifier() string {
return cd.SchemeManagerID + "." + cd.IssuerID
func (ct *CredentialType) IssuerIdentifier() IssuerIdentifier {
return NewIssuerIdentifier(ct.SchemeManagerID + "." + ct.IssuerID)
}
// Identifier returns the identifier of the specified issuer description.
func (id *Issuer) Identifier() string {
return id.SchemeManagerID + "." + id.ID
func (id *Issuer) Identifier() IssuerIdentifier {
return NewIssuerIdentifier(id.SchemeManagerID + "." + id.ID)
}
// Identifier returns the identifier of the specified scheme manager.
func (sm *SchemeManager) Identifier() SchemeManagerIdentifier {
return NewSchemeManagerIdentifier(sm.ID)
}
// CurrentPublicKey returns the latest known public key of the issuer identified by this instance.
......
package irmago
import "strings"
type objectIdentifier string
// SchemeManagerIdentifier identifies a scheme manager. Equal to its ID. For example "irma-demo".
type SchemeManagerIdentifier struct {
objectIdentifier
}
// IssuerIdentifier identifies an inssuer. For example "irma-demo.RU".
type IssuerIdentifier struct {
objectIdentifier
}
// CredentialIdentifier identifies a credentialtype. For example "irma-demo.RU.studentCard".
type CredentialIdentifier struct {
objectIdentifier
}
// AttributeIdentifier identifies an attribute. For example "irma-demo.RU.studentCard.studentID".
type AttributeIdentifier struct {
objectIdentifier
}
func (oi objectIdentifier) Parent() string {
str := string(oi)
return str[:strings.LastIndex(str, "/")]
}
func (oi objectIdentifier) Name() string {
str := string(oi)
return str[strings.LastIndex(str, "/")+1:]
}
func (oi objectIdentifier) String() string {
return string(oi)
}
// NewSchemeManagerIdentifier converts the specified identifier to a SchemeManagerIdentifier.
func NewSchemeManagerIdentifier(id string) SchemeManagerIdentifier {
return SchemeManagerIdentifier{objectIdentifier(id)}
}
// NewIssuerIdentifier converts the specified identifier to a IssuerIdentifier.
func NewIssuerIdentifier(id string) IssuerIdentifier {
return IssuerIdentifier{objectIdentifier(id)}
}
// NewCredentialIdentifier converts the specified identifier to a CredentialIdentifier.
func NewCredentialIdentifier(id string) CredentialIdentifier {
return CredentialIdentifier{objectIdentifier(id)}
}
// NewAttributeIdentifier converts the specified identifier to a AttributeIdentifier.
func NewAttributeIdentifier(id string) AttributeIdentifier {
return AttributeIdentifier{objectIdentifier(id)}
}
// 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.
func (id CredentialIdentifier) IssuerIdentifier() IssuerIdentifier {
return NewIssuerIdentifier(id.Parent())
}
// CredentialIdentifier returns the CredentialIdentifier of the attribute identifier.
func (id AttributeIdentifier) CredentialIdentifier() CredentialIdentifier {
return NewCredentialIdentifier(id.Parent())
}
......@@ -7,6 +7,7 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
......@@ -39,7 +40,7 @@ func parseStorage(t *testing.T) {
func teardown(t *testing.T) {
MetaStore = newConfigurationStore()
Manager = newCredentialManager()
require.NoError(t, os.RemoveAll("testdata/storage/test"))
assert.NoError(t, os.RemoveAll("testdata/storage/test"))
}
// A convenience function for initializing big integers from known correct (10
......@@ -50,16 +51,16 @@ func s2big(s string) (r *big.Int) {
}
func parseAndroidStorage(t *testing.T) {
require.NoError(t, Manager.ParseAndroidStorage(), "ParseAndroidStorage() failed")
assert.NoError(t, Manager.ParseAndroidStorage(), "ParseAndroidStorage() failed")
}
func verifyStoreIsUnmarshaled(t *testing.T) {
cred, err := Manager.Credential("irma-demo.RU.studentCard", 0)
require.NoError(t, err, "could not fetch credential")
require.NotNil(t, cred, "Credential should exist")
require.NotNil(t, cred.Attributes[0], "Metadata attribute of irma-demo.RU.studentCard should not be nil")
cred, err := Manager.Credential(NewCredentialIdentifier("irma-demo.RU.studentCard"), 0)
assert.NoError(t, err, "could not fetch credential")
assert.NotNil(t, cred, "Credential should exist")
assert.NotNil(t, cred.Attributes[0], "Metadata attribute of irma-demo.RU.studentCard should not be nil")
require.True(t,
assert.True(t,
cred.Signature.Verify(cred.PublicKey(), cred.Attributes),
"Credential should be valid",
)
......@@ -90,30 +91,30 @@ func TestUnmarshaling(t *testing.T) {
func TestParseStore(t *testing.T) {
parseMetaStore(t)
require.NotNil(t, MetaStore.Issuers["irma-demo.RU"].CurrentPublicKey().N, "irma-demo.RU public key has no modulus")
require.Equal(t,
assert.NotNil(t, MetaStore.Issuers[NewIssuerIdentifier("irma-demo.RU")].CurrentPublicKey().N, "irma-demo.RU public key has no modulus")
assert.Equal(t,
"Irma Demo",
MetaStore.SchemeManagers["irma-demo"].Name.Translation("en"),
MetaStore.SchemeManagers[NewSchemeManagerIdentifier("irma-demo")].Name.Translation("en"),
"irma-demo scheme manager has unexpected name")
require.Equal(t,
assert.Equal(t,
"Radboud Universiteit Nijmegen",
MetaStore.Issuers["irma-demo.RU"].Name.Translation("en"),
MetaStore.Issuers[NewIssuerIdentifier("irma-demo.RU")].Name.Translation("en"),
"irma-demo.RU issuer has unexpected name")
require.Equal(t,
assert.Equal(t,
"Student Card",
MetaStore.Credentials["irma-demo.RU.studentCard"].ShortName.Translation("en"),
MetaStore.Credentials[NewCredentialIdentifier("irma-demo.RU.studentCard")].ShortName.Translation("en"),
"irma-demo.RU.studentCard has unexpected name")
require.Equal(t,
assert.Equal(t,
"studentID",
MetaStore.Credentials["irma-demo.RU.studentCard"].Attributes[2].ID,
MetaStore.Credentials[NewCredentialIdentifier("irma-demo.RU.studentCard")].Attributes[2].ID,
"irma-demo.RU.studentCard.studentID has unexpected name")
// Hash algorithm pseudocode:
// Base64(SHA256("irma-demo.RU.studentCard")[0:16])
require.Contains(t, MetaStore.reverseHashes, "1stqlPad5edpfS1Na1U+DA==",
assert.Contains(t, MetaStore.reverseHashes, "1stqlPad5edpfS1Na1U+DA==",
"irma-demo.RU.studentCard had improper hash")
require.Contains(t, MetaStore.reverseHashes, "CLjnADMBYlFcuGOT7Z0xRg==",
assert.Contains(t, MetaStore.reverseHashes, "CLjnADMBYlFcuGOT7Z0xRg==",
"irma-demo.MijnOverheid.root had improper hash")
teardown(t)
......@@ -140,17 +141,17 @@ func TestMetadataCompatibility(t *testing.T) {
// An actual metadata attribute of an IRMA credential extracted from the IRMA app
attr := MetadataFromInt(s2big("49043481832371145193140299771658227036446546573739245068"))
require.NotNil(t, attr.CredentialType(), "attr.CredentialType() should not be nil")
assert.NotNil(t, attr.CredentialType(), "attr.CredentialType() should not be nil")
require.Equal(t,
"irma-demo.RU.studentCard",
assert.Equal(t,
NewCredentialIdentifier("irma-demo.RU.studentCard"),
attr.CredentialType().Identifier(),
"Metadata credential type was not irma-demo.RU.studentCard",
)
require.Equal(t, byte(0x02), attr.Version(), "Unexpected metadata version")
require.Equal(t, time.Unix(1499904000, 0), attr.SigningDate(), "Unexpected signing date")
require.Equal(t, time.Unix(1516233600, 0), attr.Expiry(), "Unexpected expiry date")
require.Equal(t, 2, attr.KeyCounter(), "Unexpected key counter")
assert.Equal(t, byte(0x02), attr.Version(), "Unexpected metadata version")
assert.Equal(t, time.Unix(1499904000, 0), attr.SigningDate(), "Unexpected signing date")
assert.Equal(t, time.Unix(1516233600, 0), attr.Expiry(), "Unexpected expiry date")
assert.Equal(t, 2, attr.KeyCounter(), "Unexpected key counter")
teardown(t)
}
......@@ -18,13 +18,13 @@ var Manager = newCredentialManager()
type CredentialManager struct {
secretkey *big.Int
storagePath string
attributes map[string][]*AttributeList
credentials map[string]map[int]*Credential
attributes map[CredentialIdentifier][]*AttributeList
credentials map[CredentialIdentifier]map[int]*Credential
}
func newCredentialManager() *CredentialManager {
return &CredentialManager{
credentials: make(map[string]map[int]*Credential),
credentials: make(map[CredentialIdentifier]map[int]*Credential),
}
}
......@@ -50,7 +50,7 @@ func (cm *CredentialManager) Init(path string) (err error) {
}
// attrs returns cm.attributes[id], initializing it to an empty slice if neccesary
func (cm *CredentialManager) attrs(id string) []*AttributeList {
func (cm *CredentialManager) attrs(id CredentialIdentifier) []*AttributeList {
list, exists := cm.attributes[id]
if !exists {
list = make([]*AttributeList, 0, 1)
......@@ -60,7 +60,7 @@ func (cm *CredentialManager) attrs(id string) []*AttributeList {
}
// creds returns cm.credentials[id], initializing it to an empty map if neccesary
func (cm *CredentialManager) creds(id string) map[int]*Credential {
func (cm *CredentialManager) creds(id CredentialIdentifier) map[int]*Credential {
list, exists := cm.credentials[id]
if !exists {
list = make(map[int]*Credential)
......@@ -70,7 +70,7 @@ func (cm *CredentialManager) creds(id string) map[int]*Credential {
}
// Attributes returns the attribute list of the requested credential, or nil if we do not have it.
func (cm *CredentialManager) Attributes(id string, counter int) (attributes *AttributeList) {
func (cm *CredentialManager) Attributes(id CredentialIdentifier, counter int) (attributes *AttributeList) {
list := cm.attrs(id)
if len(list) <= counter {
return
......@@ -79,7 +79,7 @@ func (cm *CredentialManager) Attributes(id string, counter int) (attributes *Att
}
// Credential returns the requested credential, or nil if we do not have it.
func (cm *CredentialManager) Credential(id string, counter int) (cred *Credential, err error) {
func (cm *CredentialManager) Credential(id CredentialIdentifier, counter int) (cred *Credential, err error) {
// If the requested credential is not in credential map, we check if its attributes were
// deserialized during Init(). If so, there should be a corresponding signature file,
// so we read that, construct the credential, and add it to the credential map
......
......@@ -106,15 +106,21 @@ func (cm *CredentialManager) storeSignature(cred *Credential, counter int) (err
}
// TODO existence check
filename := cm.signatureFilename(cred.CredentialType().Identifier(), counter)
filename := cm.signatureFilename(cred.CredentialType().Identifier().String(), counter)
err = ioutil.WriteFile(filename, credbytes, 0600)
return
}
func (cm *CredentialManager) storeAttributes() (err error) {
attrbytes, err := json.Marshal(cm.attributes)
// Unfortunately, the type of cm.attributes (map[CredentialIdentifier][]*AttributeList)
// cannot be passed directly to json.Marshal(), so we copy it into a temp list.
temp := make(map[string][]*AttributeList)
for credid, list := range cm.attributes {
temp[credid.String()] = list
}
attrbytes, err := json.Marshal(temp)
if err != nil {
return
return err
}
// TODO existence check
......@@ -122,8 +128,8 @@ func (cm *CredentialManager) storeAttributes() (err error) {
return
}
func (cm *CredentialManager) loadSignature(id string, counter int) (signature *gabi.CLSignature, err error) {
path := cm.signatureFilename(id, counter)
func (cm *CredentialManager) loadSignature(id CredentialIdentifier, counter int) (signature *gabi.CLSignature, err error) {
path := cm.signatureFilename(id.String(), counter)
exists, err := pathExists(path)
if err != nil || !exists {
return
......@@ -147,21 +153,22 @@ func (cm *CredentialManager) loadSecretKey() (*big.Int, error) {
return nil, err
}
return new(big.Int).SetBytes(bytes), nil
} else {
sk, err := cm.generateSecretKey()
if err != nil {
return nil, err
}
err = cm.storeSecretKey(sk)
if err != nil {
return nil, err
}
return sk, nil
}
sk, err := cm.generateSecretKey()
if err != nil {
return nil, err
}
err = cm.storeSecretKey(sk)
if err != nil {
return nil, err
}
return sk, nil
}
func (cm *CredentialManager) loadAttributes() (list map[string][]*AttributeList, err error) {
list = make(map[string][]*AttributeList)
func (cm *CredentialManager) loadAttributes() (list map[CredentialIdentifier][]*AttributeList, err error) {
list = make(map[CredentialIdentifier][]*AttributeList)
temp := make(map[string][]*AttributeList)
exists, err := pathExists(cm.path(attributesFile))
if err != nil || !exists {
......@@ -172,6 +179,13 @@ func (cm *CredentialManager) loadAttributes() (list map[string][]*AttributeList,
if err != nil {
return nil, err
}
return list, json.Unmarshal(bytes, &list)
err = json.Unmarshal(bytes, &temp)
if err != nil {
return nil, err
}
for credid, attrs := range temp {
list[NewCredentialIdentifier(credid)] = attrs
}
return list, nil
}
......@@ -18,28 +18,28 @@ var MetaStore = newConfigurationStore()
// ConfigurationStore keeps track of scheme managers, issuers, credential types and public keys.
// Use the global MetaStore instance.
type ConfigurationStore struct {
SchemeManagers map[string]*SchemeManager
Issuers map[string]*Issuer
Credentials map[string]*CredentialType
PublicKeys map[string][]*gabi.PublicKey
SchemeManagers map[SchemeManagerIdentifier]*SchemeManager
Issuers map[IssuerIdentifier]*Issuer
Credentials map[CredentialIdentifier]*CredentialType
PublicKeys map[IssuerIdentifier][]*gabi.PublicKey
reverseHashes map[string]string
reverseHashes map[string]CredentialIdentifier
initialized bool
}
func newConfigurationStore() (store *ConfigurationStore) {
store = &ConfigurationStore{
SchemeManagers: make(map[string]*SchemeManager),
Issuers: make(map[string]*Issuer),
Credentials: make(map[string]*CredentialType),
PublicKeys: make(map[string][]*gabi.PublicKey),
reverseHashes: make(map[string]string),
SchemeManagers: make(map[SchemeManagerIdentifier]*SchemeManager),
Issuers: make(map[IssuerIdentifier]*Issuer),
Credentials: make(map[CredentialIdentifier]*CredentialType),
PublicKeys: make(map[IssuerIdentifier][]*gabi.PublicKey),
reverseHashes: make(map[string]CredentialIdentifier),
}
return
}
// PublicKey returns the specified public key, or nil if not present in the ConfigurationStore.
func (store *ConfigurationStore) PublicKey(id string, counter int) *gabi.PublicKey {
func (store *ConfigurationStore) PublicKey(id IssuerIdentifier, counter int) *gabi.PublicKey {
if list, ok := MetaStore.PublicKeys[id]; ok {
if len(list) > counter {
return list[counter]
......@@ -48,8 +48,8 @@ func (store *ConfigurationStore) PublicKey(id string, counter int) *gabi.PublicK
return nil
}
func (store *ConfigurationStore) addReverseHash(credid string) {
hash := sha256.Sum256([]byte(credid))
func (store *ConfigurationStore) addReverseHash(credid CredentialIdentifier) {
hash := sha256.Sum256([]byte(credid.String()))
store.reverseHashes[base64.StdEncoding.EncodeToString(hash[:16])] = credid
}
......@@ -60,6 +60,7 @@ func (store *ConfigurationStore) hashToCredentialType(hash []byte) *CredentialTy
return nil
}
// IsInitialized indicates whether this instance has successfully been initialized.
func (store *ConfigurationStore) IsInitialized() bool {
return store.initialized
}
......@@ -74,7 +75,7 @@ func (store *ConfigurationStore) ParseFolder(path string) error {
return err
}
if exists {
MetaStore.SchemeManagers[manager.ID] = manager
MetaStore.SchemeManagers[manager.Identifier()] = manager
return store.parseIssuerFolders(dir)
}
return nil
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment