Commit b28be0df authored by Ayke van Laethem's avatar Ayke van Laethem
Browse files

Add support for empty vs non-existing attributes

This also required adding support for calculating the metadata version
based on the protocol version, and sending the current protocol version
to the server as a hint.
parent 1a5b4d62
......@@ -19,8 +19,6 @@ const (
)
var (
metadataVersion = []byte{0x02}
versionField = metadataField{1, 0}
signingDateField = metadataField{3, 1}
validityField = metadataField{2, 4}
......@@ -120,13 +118,13 @@ func MetadataFromInt(i *big.Int, conf *Configuration) *MetadataAttribute {
}
// NewMetadataAttribute constructs a new instance containing the default values:
// 0x02 as versionField
// provided version as versionField
// now as signing date
// 0 as keycounter
// ValidityDefault (half a year) as default validity.
func NewMetadataAttribute() *MetadataAttribute {
func NewMetadataAttribute(version byte) *MetadataAttribute {
val := MetadataAttribute{new(big.Int), nil, nil}
val.setField(versionField, metadataVersion)
val.setField(versionField, []byte{version})
val.setSigningDate()
val.setKeyCounter(0)
val.setDefaultValidityDuration()
......
......@@ -31,7 +31,14 @@ func NewCredentialInfo(ints []*big.Int, conf *Configuration) *CredentialInfo {
attrs := make([]TranslatedString, len(credtype.Attributes))
for i := range credtype.Attributes {
val := string(ints[i+1].Bytes())
bi := ints[i+1]
if meta.Version() >= 3 { // has optional attributes
if bi.Bit(0) == 0 { // attribute does not exist
continue
}
bi = bi.Rsh(bi, 1)
}
val := string(bi.Bytes())
attrs[i] = TranslatedString(map[string]string{"en": val, "nl": val})
}
......
......@@ -594,7 +594,7 @@ func (client *Client) ConstructCredentials(msg []*gabi.IssueSignatureMessage, re
// we save none of them to fail the session cleanly
gabicreds := []*gabi.Credential{}
for i, sig := range msg {
attrs, err := request.Credentials[i].AttributeList(client.Configuration)
attrs, err := request.Credentials[i].AttributeList(client.Configuration, getMetadataVersion(request.GetVersion()))
if err != nil {
return err
}
......
......@@ -54,7 +54,7 @@ func (session *session) createLogEntry(response interface{}) (*LogEntry, error)
entry.Received = map[irma.CredentialTypeIdentifier][]irma.TranslatedString{}
}
for _, req := range session.jwt.(*irma.IdentityProviderJwt).Request.Request.Credentials {
list, err := req.AttributeList(session.client.Configuration)
list, err := req.AttributeList(session.client.Configuration, getMetadataVersion(session.Version))
if err != nil {
continue // TODO?
}
......
......@@ -50,10 +50,19 @@ type SessionDismisser interface {
Dismiss()
}
// getMetadataVersion maps a chosen protocol version to a metadata version that
// the server will use.
func getMetadataVersion(v *irma.ProtocolVersion) byte {
if v.Below(2, 3) {
return 0x02 // no support for optional attributes
}
return 0x03 // current version
}
type session struct {
Action irma.Action
Handler Handler
Version irma.Version
Version *irma.ProtocolVersion
choice *irma.DisclosureChoice
client *Client
......@@ -73,24 +82,24 @@ var _ keyshareSessionHandler = (*session)(nil)
// Supported protocol versions. Minor version numbers should be reverse sorted.
var supportedVersions = map[int][]int{
2: {2, 1},
2: {3, 2, 1},
}
func calcVersion(qr *irma.Qr) (string, error) {
func calcVersion(qr *irma.Qr) (*irma.ProtocolVersion, error) {
// Parse range supported by server
var minmajor, minminor, maxmajor, maxminor int
var err error
if minmajor, err = strconv.Atoi(string(qr.ProtocolVersion[0])); err != nil {
return "", err
return nil, err
}
if minminor, err = strconv.Atoi(string(qr.ProtocolVersion[2])); err != nil {
return "", err
return nil, err
}
if maxmajor, err = strconv.Atoi(string(qr.ProtocolMaxVersion[0])); err != nil {
return "", err
return nil, err
}
if maxminor, err = strconv.Atoi(string(qr.ProtocolMaxVersion[2])); err != nil {
return "", err
return nil, err
}
// Iterate supportedVersions in reverse sorted order (i.e. biggest major number first)
......@@ -104,11 +113,11 @@ func calcVersion(qr *irma.Qr) (string, error) {
aboveMinimum := major > minmajor || (major == minmajor && minor >= minminor)
underMaximum := major < maxmajor || (major == maxmajor && minor <= maxminor)
if aboveMinimum && underMaximum {
return fmt.Sprintf("%d.%d", major, minor), nil
return irma.NewVersion(major, minor), nil
}
}
}
return "", fmt.Errorf("No supported protocol version between %s and %s", qr.ProtocolVersion, qr.ProtocolMaxVersion)
return nil, fmt.Errorf("No supported protocol version between %s and %s", qr.ProtocolVersion, qr.ProtocolMaxVersion)
}
func (session *session) IsInteractive() bool {
......@@ -220,7 +229,7 @@ func (client *Client) NewManualSession(sigrequestJSONString string, handler Hand
Action: irma.ActionSigning, // TODO hardcoded for now
Handler: handler,
client: client,
Version: irma.Version("2"), // TODO hardcoded for now
Version: irma.NewVersion(2, 0), // TODO hardcoded for now
irmaSession: sigrequest,
}
......@@ -268,7 +277,8 @@ func (client *Client) NewSession(qr *irma.Qr, handler Handler) SessionDismisser
session.fail(&irma.SessionError{ErrorType: irma.ErrorProtocolVersionNotSupported, Err: err})
return nil
}
session.Version = irma.Version(version)
session.Version = version
session.transport.SetHeader("X-IRMA-ProtocolVersion", version.String())
// Check if the action is one of the supported types
switch session.Action {
......@@ -315,6 +325,7 @@ func (session *session) start() {
session.irmaSession = session.jwt.IrmaSession()
session.irmaSession.SetContext(session.info.Context)
session.irmaSession.SetNonce(session.info.Nonce)
session.irmaSession.SetVersion(session.Version)
if session.Action == irma.ActionIssuing {
ir := session.irmaSession.(*irma.IssuanceRequest)
// Store which public keys the server will use
......@@ -330,7 +341,7 @@ func (session *session) start() {
if session.Action == irma.ActionIssuing {
ir := session.irmaSession.(*irma.IssuanceRequest)
for _, credreq := range ir.Credentials {
info, err := credreq.Info(session.client.Configuration)
info, err := credreq.Info(session.client.Configuration, getMetadataVersion(session.Version))
if err != nil {
session.fail(&irma.SessionError{ErrorType: irma.ErrorUnknownCredentialType, Err: err})
return
......
......@@ -137,8 +137,8 @@ func getIssuanceRequest(defaultValidity bool) *irma.IssuanceRequest {
}
}
func getNameIssuanceRequest(prefix bool) *irma.IssuanceRequest {
expiry := irma.Timestamp(irma.NewMetadataAttribute().Expiry())
func getNameIssuanceRequest() *irma.IssuanceRequest {
expiry := irma.Timestamp(irma.NewMetadataAttribute(0).Expiry())
credid := irma.NewCredentialTypeIdentifier("irma-demo.MijnOverheid.fullName")
req := &irma.IssuanceRequest{
......@@ -150,16 +150,11 @@ func getNameIssuanceRequest(prefix bool) *irma.IssuanceRequest {
"firstnames": "Johan Pieter",
"firstname": "Johan",
"familyname": "Stuivezand",
"prefix": "",
},
},
},
}
if (prefix) {
req.Credentials[0].Attributes["prefix"] = "van"
}
return req
}
......@@ -218,12 +213,22 @@ func TestDefaultCredentialValidity(t *testing.T) {
}
func TestIssuanceOptionalEmptyAttributes(t *testing.T) {
jwt := irma.NewIdentityProviderJwt("testip", getNameIssuanceRequest(false))
req := getNameIssuanceRequest()
jwt := irma.NewIdentityProviderJwt("testip", req)
sessionHelper(t, jwt, "issue", nil)
}
func TestIssuanceOptionalZeroLengthAttributes(t *testing.T) {
req := getNameIssuanceRequest()
req.Credentials[0].Attributes["prefix"] = ""
jwt := irma.NewIdentityProviderJwt("testip", req)
sessionHelper(t, jwt, "issue", nil)
}
func TestIssuanceOptionalSetAttributes(t *testing.T) {
jwt := irma.NewIdentityProviderJwt("testip", getNameIssuanceRequest(true))
req := getNameIssuanceRequest()
req.Credentials[0].Attributes["prefix"] = "van"
jwt := irma.NewIdentityProviderJwt("testip", req)
sessionHelper(t, jwt, "issue", nil)
}
......@@ -297,7 +302,7 @@ func TestKeyshareEnrollmentAndSessions(t *testing.T) {
enrollKeyshareServer(t, client)
id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
expiry := irma.Timestamp(irma.NewMetadataAttribute().Expiry())
expiry := irma.Timestamp(irma.NewMetadataAttribute(0).Expiry())
credid := irma.NewCredentialTypeIdentifier("test.test.mijnirma")
jwt := getCombinedJwt("testip", id)
jwt.(*irma.IdentityProviderJwt).Request.Request.Credentials = append(
......@@ -340,7 +345,7 @@ func TestKeyshareSessions(t *testing.T) {
client := parseStorage(t)
id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
expiry := irma.Timestamp(irma.NewMetadataAttribute().Expiry())
expiry := irma.Timestamp(irma.NewMetadataAttribute(0).Expiry())
credid := irma.NewCredentialTypeIdentifier("test.test.mijnirma")
jwt := getCombinedJwt("testip", id)
jwt.(*irma.IdentityProviderJwt).Request.Request.Credentials = append(
......
......@@ -166,7 +166,7 @@ func TestAttributeDisjunctionMarshaling(t *testing.T) {
}
func TestMetadataAttribute(t *testing.T) {
metadata := NewMetadataAttribute()
metadata := NewMetadataAttribute(0x02)
if metadata.Version() != 0x02 {
t.Errorf("Unexpected metadata version: %d", metadata.Version())
}
......
......@@ -16,8 +16,27 @@ import (
// Status encodes the status of an IRMA session (e.g., connected).
type Status string
// Version encodes the IRMA protocol version of an IRMA session.
type Version string
// ProtocolVersion encodes the IRMA protocol version of an IRMA session.
type ProtocolVersion struct {
major int
minor int
}
func NewVersion(major, minor int) *ProtocolVersion {
return &ProtocolVersion{major, minor}
}
func (v *ProtocolVersion) String() string {
return fmt.Sprintf("%d.%d", v.major, v.minor)
}
// Returns true if v is below the given version.
func (v *ProtocolVersion) Below(major, minor int) bool {
if v.major < major {
return true
}
return v.major == major && v.minor < minor
}
// Action encodes the session type of an IRMA session (e.g., disclosing).
type Action string
......
......@@ -20,6 +20,8 @@ type SessionRequest struct {
Choice *DisclosureChoice `json:"-"`
Ids *IrmaIdentifierSet `json:"-"`
version *ProtocolVersion
}
func (sr *SessionRequest) SetCandidates(candidates [][]*AttributeIdentifier) {
......@@ -36,6 +38,16 @@ func (sr *SessionRequest) SetDisclosureChoice(choice *DisclosureChoice) {
sr.Choice = choice
}
// ...
func (sr *SessionRequest) SetVersion(v *ProtocolVersion) {
sr.version = v
}
// ...
func (sr *SessionRequest) GetVersion() *ProtocolVersion {
return sr.version
}
// A DisclosureRequest is a request to disclose certain attributes.
type DisclosureRequest struct {
SessionRequest
......@@ -113,6 +125,7 @@ type IrmaSession interface {
SetNonce(*big.Int)
GetContext() *big.Int
SetContext(*big.Int)
SetVersion(*ProtocolVersion)
ToDisclose() AttributeDisjunctionList
DisclosureChoice() *DisclosureChoice
SetDisclosureChoice(choice *DisclosureChoice)
......@@ -123,8 +136,8 @@ type IrmaSession interface {
// Timestamp is a time.Time that marshals to Unix timestamps.
type Timestamp time.Time
func (cr *CredentialRequest) Info(conf *Configuration) (*CredentialInfo, error) {
list, err := cr.AttributeList(conf)
func (cr *CredentialRequest) Info(conf *Configuration, metadataVersion byte) (*CredentialInfo, error) {
list, err := cr.AttributeList(conf, metadataVersion)
if err != nil {
return nil, err
}
......@@ -132,8 +145,8 @@ func (cr *CredentialRequest) Info(conf *Configuration) (*CredentialInfo, error)
}
// AttributeList returns the list of attributes from this credential request.
func (cr *CredentialRequest) AttributeList(conf *Configuration) (*AttributeList, error) {
meta := NewMetadataAttribute()
func (cr *CredentialRequest) AttributeList(conf *Configuration, metadataVersion byte) (*AttributeList, error) {
meta := NewMetadataAttribute(metadataVersion)
meta.setKeyCounter(cr.KeyCounter)
meta.setCredentialTypeIdentifier(cr.CredentialTypeID.String())
meta.setSigningDate()
......@@ -167,7 +180,12 @@ func (cr *CredentialRequest) AttributeList(conf *Configuration) (*AttributeList,
for i, attrtype := range credtype.Attributes {
attrs[i+1] = new(big.Int)
if str, present := cr.Attributes[attrtype.ID]; present {
// Set attribute to str << 1 + 1
attrs[i+1].SetBytes([]byte(str))
if meta.Version() >= 0x03 {
attrs[i+1].Lsh(attrs[i+1], 1) // attr <<= 1
attrs[i+1].Add(attrs[i+1], big.NewInt(1)) // attr += 1
}
} else {
if (attrtype.Optional != "true") {
return nil, errors.New("Required attribute not provided")
......
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