Commit d5720003 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Include new message in disclosure proofs for disclosure choices

parent 2e8a4bd7
......@@ -188,10 +188,9 @@ func (th *ManualTestHandler) RequestSchemeManagerPermission(manager *irma.Scheme
th.Failure(&irma.SessionError{Err: errors.New("Unexpected session type")})
}
func (th *ManualTestHandler) RequestVerificationPermission(request irma.DisclosureRequest, verifierName string, ph irmaclient.PermissionHandler) {
var attributes []*irma.AttributeIdentifier
var choice irma.DisclosureChoice
for _, cand := range request.Candidates {
attributes = append(attributes, cand[0])
choice.Attributes = append(choice.Attributes, cand[0])
}
c := irma.DisclosureChoice{attributes}
ph(true, &c)
ph(true, &choice)
}
......@@ -14,11 +14,12 @@ import (
// SignedMessage is a message signed with an attribute-based signature
// The 'realnonce' will be calculated as: SigRequest.GetNonce() = ASN1(nonce, SHA256(message), timestampSignature)
type SignedMessage struct {
Signature gabi.ProofList `json:"signature"`
Nonce *big.Int `json:"nonce"`
Context *big.Int `json:"context"`
Message string `json:"message"`
Timestamp *atum.Timestamp `json:"timestamp"`
Signature gabi.ProofList `json:"signature"`
Indices DisclosedAttributeIndices `json:"indices"`
Nonce *big.Int `json:"nonce"`
Context *big.Int `json:"context"`
Message string `json:"message"`
Timestamp *atum.Timestamp `json:"timestamp"`
}
func (sm *SignedMessage) GetNonce() *big.Int {
......@@ -31,6 +32,13 @@ func (sm *SignedMessage) MatchesNonceAndContext(request *SignatureRequest) bool
sm.GetNonce().Cmp(request.GetNonce()) == 0
}
func (sm *SignedMessage) Disclosure() *Disclosure {
return &Disclosure{
Proofs: sm.Signature,
Indices: sm.Indices,
}
}
// ASN1ConvertSignatureNonce computes the nonce that is used in the creation of the attribute-based signature:
// nonce = SHA256(serverNonce, SHA256(message), timestampSignature)
// where serverNonce is the nonce sent by the signature requestor.
......
......@@ -496,13 +496,16 @@ func (client *Client) CheckSatisfiability(
return candidates, missing
}
func (client *Client) groupCredentials(choice *irma.DisclosureChoice) (map[irma.CredentialIdentifier][]int, error) {
func (client *Client) groupCredentials(choice *irma.DisclosureChoice, disjunctions irma.AttributeDisjunctionList) (
map[irma.CredentialIdentifier][]int, irma.DisclosedAttributeIndices, error,
) {
grouped := make(map[irma.CredentialIdentifier][]int)
if choice == nil || choice.Attributes == nil {
return grouped, nil
return grouped, irma.DisclosedAttributeIndices{}, nil
}
for _, attribute := range choice.Attributes {
attributeIndices := make(irma.DisclosedAttributeIndices, len(choice.Attributes))
for i, attribute := range choice.Attributes {
identifier := attribute.Type
ici := attribute.CredentialIdentifier()
......@@ -519,7 +522,11 @@ func (client *Client) groupCredentials(choice *irma.DisclosureChoice) (map[irma.
}
index, err := client.Configuration.CredentialTypes[identifier.CredentialTypeIdentifier()].IndexOf(identifier)
if err != nil {
return nil, err
return nil, nil, err
}
attributeIndices[i] = []*irma.DisclosedAttributeIndex{
{AttributeIndex: index + 2, Identifier: ici},
}
// These indices will be used in the []*big.Int at gabi.credential.Attributes,
......@@ -527,23 +534,26 @@ func (client *Client) groupCredentials(choice *irma.DisclosureChoice) (map[irma.
grouped[ici] = append(grouped[ici], index+2)
}
return grouped, nil
return grouped, attributeIndices, nil
}
// ProofBuilders constructs a list of proof builders for the specified attribute choice.
func (client *Client) ProofBuilders(choice *irma.DisclosureChoice, request irma.SessionRequest, issig bool) (gabi.ProofBuilderList, error) {
todisclose, err := client.groupCredentials(choice)
func (client *Client) ProofBuilders(choice *irma.DisclosureChoice, request irma.SessionRequest, issig bool,
) (gabi.ProofBuilderList, irma.DisclosedAttributeIndices, error) {
todisclose, disjunctionChoices, err := client.groupCredentials(choice, request.ToDisclose())
if err != nil {
return nil, err
return nil, nil, err
}
builders := gabi.ProofBuilderList([]gabi.ProofBuilder{})
for id, list := range todisclose {
cred, err := client.credentialByID(id)
for i, choice := range disjunctionChoices {
cred, err := client.credentialByID(choice[0].Identifier)
if err != nil {
return nil, err
return nil, nil, err
}
builders = append(builders, cred.Credential.CreateDisclosureProofBuilder(list))
choice[0].CredentialIndex = i
builders = append(builders, cred.Credential.CreateDisclosureProofBuilder(todisclose[choice[0].Identifier]))
}
if issig {
......@@ -559,21 +569,24 @@ func (client *Client) ProofBuilders(choice *irma.DisclosureChoice, request irma.
r := request.(*irma.SignatureRequest)
r.Timestamp, err = irma.GetTimestamp(r.Message, sigs, disclosed)
if err != nil {
return nil, err
return nil, nil, err
}
}
return builders, nil
return builders, disjunctionChoices, nil
}
// Proofs computes disclosure proofs containing the attributes specified by choice.
func (client *Client) Proofs(choice *irma.DisclosureChoice, request irma.SessionRequest, issig bool) (gabi.ProofList, error) {
builders, err := client.ProofBuilders(choice, request, issig)
func (client *Client) Proofs(choice *irma.DisclosureChoice, request irma.SessionRequest, issig bool) (*irma.Disclosure, error) {
builders, choices, err := client.ProofBuilders(choice, request, issig)
if err != nil {
return nil, err
}
return builders.BuildProofList(request.GetContext(), request.GetNonce(), issig), nil
return &irma.Disclosure{
Proofs: builders.BuildProofList(request.GetContext(), request.GetNonce(), issig),
Indices: choices,
}, nil
}
// generateIssuerProofNonce generates a nonce which the issuer must use in its gabi.ProofS.
......@@ -584,40 +597,47 @@ func generateIssuerProofNonce() (*big.Int, error) {
// IssuanceProofBuilders constructs a list of proof builders in the issuance protocol
// for the future credentials as well as possibly any disclosed attributes, and generates
// a nonce against which the issuer's proof of knowledge must verify.
func (client *Client) IssuanceProofBuilders(request *irma.IssuanceRequest) (gabi.ProofBuilderList, *big.Int, error) {
func (client *Client) IssuanceProofBuilders(request *irma.IssuanceRequest,
) (gabi.ProofBuilderList, irma.DisclosedAttributeIndices, *big.Int, error) {
issuerProofNonce, err := generateIssuerProofNonce()
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
builders := gabi.ProofBuilderList([]gabi.ProofBuilder{})
for _, futurecred := range request.Credentials {
var pk *gabi.PublicKey
pk, err = client.Configuration.PublicKey(futurecred.CredentialTypeID.IssuerIdentifier(), futurecred.KeyCounter)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
credBuilder := gabi.NewCredentialBuilder(
pk, request.GetContext(), client.secretkey.Key, issuerProofNonce)
builders = append(builders, credBuilder)
}
disclosures, err := client.ProofBuilders(request.Choice, request, false)
disclosures, choices, err := client.ProofBuilders(request.Choice, request, false)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
builders = append(disclosures, builders...)
return builders, issuerProofNonce, nil
return builders, choices, issuerProofNonce, nil
}
// IssueCommitments computes issuance commitments, along with disclosure proofs specified by choice,
// and also returns the credential builders which will become the new credentials upon combination with the issuer's signature.
func (client *Client) IssueCommitments(request *irma.IssuanceRequest) (*gabi.IssueCommitmentMessage, gabi.ProofBuilderList, error) {
builders, issuerProofNonce, err := client.IssuanceProofBuilders(request)
func (client *Client) IssueCommitments(request *irma.IssuanceRequest,
) (*irma.IssueCommitmentMessage, gabi.ProofBuilderList, error) {
builders, choices, issuerProofNonce, err := client.IssuanceProofBuilders(request)
if err != nil {
return nil, nil, err
}
list := builders.BuildProofList(request.GetContext(), request.GetNonce(), false)
return &gabi.IssueCommitmentMessage{Proofs: list, Nonce2: issuerProofNonce}, builders, nil
return &irma.IssueCommitmentMessage{
IssueCommitmentMessage: &gabi.IssueCommitmentMessage{
Proofs: builders.BuildProofList(request.GetContext(), request.GetNonce(), false),
Nonce2: issuerProofNonce,
},
Indices: choices,
}, builders, nil
}
// ConstructCredentials constructs and saves new credentials using the specified issuance signature messages
......
......@@ -55,10 +55,11 @@ type session struct {
Version *irma.ProtocolVersion
ServerName string
choice *irma.DisclosureChoice
client *Client
request irma.SessionRequest
done bool
choice *irma.DisclosureChoice
attrIndices irma.DisclosedAttributeIndices
client *Client
request irma.SessionRequest
done bool
// State for issuance protocol
issuerProofNonce *big.Int
......@@ -281,7 +282,7 @@ func (session *session) doSession(proceed bool) {
session.sendResponse(message)
} else {
var err error
session.builders, session.issuerProofNonce, err = session.getBuilders()
session.builders, session.attrIndices, session.issuerProofNonce, err = session.getBuilders()
if err != nil {
session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err})
}
......@@ -411,21 +412,22 @@ func (session *session) managerSession() {
// getBuilders computes the builders for disclosure proofs or secretkey-knowledge proof (in case of disclosure/signing
// and issuing respectively).
func (session *session) getBuilders() (gabi.ProofBuilderList, *big.Int, error) {
func (session *session) getBuilders() (gabi.ProofBuilderList, irma.DisclosedAttributeIndices, *big.Int, error) {
var builders gabi.ProofBuilderList
var err error
var issuerProofNonce *big.Int
var choices irma.DisclosedAttributeIndices
switch session.Action {
case irma.ActionSigning:
builders, err = session.client.ProofBuilders(session.choice, session.request, true)
builders, choices, err = session.client.ProofBuilders(session.choice, session.request, true)
case irma.ActionDisclosing:
builders, err = session.client.ProofBuilders(session.choice, session.request, false)
builders, choices, err = session.client.ProofBuilders(session.choice, session.request, false)
case irma.ActionIssuing:
builders, issuerProofNonce, err = session.client.IssuanceProofBuilders(session.request.(*irma.IssuanceRequest))
builders, choices, issuerProofNonce, err = session.client.IssuanceProofBuilders(session.request.(*irma.IssuanceRequest))
}
return builders, issuerProofNonce, err
return builders, choices, issuerProofNonce, err
}
// getProofs computes the disclosure proofs or secretkey-knowledge proof (in case of disclosure/signing
......@@ -591,7 +593,20 @@ func (session *session) Dismiss() {
// Keyshare session handler methods
func (session *session) KeyshareDone(message interface{}) {
session.sendResponse(message)
switch session.Action {
case irma.ActionSigning:
fallthrough
case irma.ActionDisclosing:
session.sendResponse(&irma.Disclosure{
Proofs: message.(gabi.ProofList),
Indices: session.attrIndices,
})
case irma.ActionIssuing:
session.sendResponse(&irma.IssueCommitmentMessage{
IssueCommitmentMessage: message.(*gabi.IssueCommitmentMessage),
Indices: session.attrIndices,
})
}
}
func (session *session) KeyshareCancelled() {
......
......@@ -12,6 +12,7 @@ import (
"fmt"
"github.com/go-errors/errors"
"github.com/mhe/gabi"
)
// Status encodes the status of an IRMA session (e.g., connected).
......@@ -240,6 +241,35 @@ func (e *SessionError) Stack() string {
return ""
}
type Disclosure struct {
Proofs gabi.ProofList `json:"proofs"`
Indices DisclosedAttributeIndices `json:"indices"`
}
// DisclosedAttributeIndices contains, for each conjunction of an attribute disclosure request,
// a list of attribute indices, pointing to where the disclosed attributes for that conjunction
// can be found within a gabi.ProofList.
type DisclosedAttributeIndices [][]*DisclosedAttributeIndex
// DisclosedAttributeIndex points to a specific attribute in a gabi.ProofList.
type DisclosedAttributeIndex struct {
CredentialIndex int `json:"cred"`
AttributeIndex int `json:"attr"`
Identifier CredentialIdentifier `json:"-"` // credential from which this attribute was disclosed
}
type IssueCommitmentMessage struct {
*gabi.IssueCommitmentMessage
Indices DisclosedAttributeIndices `json:"indices"`
}
func (i *IssueCommitmentMessage) Disclosure() *Disclosure {
return &Disclosure{
Proofs: i.Proofs,
Indices: i.Indices,
}
}
func JwtDecode(jwt string, body interface{}) error {
jwtparts := strings.Split(jwt, ".")
if jwtparts == nil || len(jwtparts) < 2 {
......
......@@ -8,7 +8,6 @@ import (
"github.com/bwesterb/go-atum"
"github.com/go-errors/errors"
"github.com/mhe/gabi"
"github.com/mhe/gabi/big"
"github.com/privacybydesign/irmago/internal/fs"
)
......@@ -362,14 +361,14 @@ func (sr *SignatureRequest) GetNonce() *big.Int {
}
func (sr *SignatureRequest) SignatureFromMessage(message interface{}) (*SignedMessage, error) {
signature, ok := message.(gabi.ProofList)
signature, ok := message.(*Disclosure)
if !ok {
return nil, errors.Errorf("Type assertion failed")
}
return &SignedMessage{
Signature: signature,
Signature: signature.Proofs,
Nonce: sr.Nonce,
Context: sr.Context,
Message: sr.Message,
......
......@@ -119,7 +119,7 @@ func (pl ProofList) Expired(configuration *Configuration, t *time.Time) bool {
// with the disjunction list in the disjunction list. If any of the given disjunctions is not matched by one
// of the disclosed attributes, then the corresponding item in the returned slice has status AttributeProofStatusMissing.
// The first return parameter of this function indicates whether or not all disjunctions (if present) are satisfied.
func (pl ProofList) DisclosedAttributes(configuration *Configuration, disjunctions AttributeDisjunctionList) (bool, []*DisclosedAttribute, error) {
func (d *Disclosure) DisclosedAttributes(configuration *Configuration, disjunctions AttributeDisjunctionList) (bool, []*DisclosedAttribute, error) {
var list []*DisclosedAttribute
list = make([]*DisclosedAttribute, len(disjunctions))
for i := range list {
......@@ -135,7 +135,7 @@ func (pl ProofList) DisclosedAttributes(configuration *Configuration, disjunctio
// we append these to list just before returning
extraAttrs := map[AttributeTypeIdentifier]*DisclosedAttribute{}
for _, proof := range pl {
for _, proof := range d.Proofs {
proofd, ok := proof.(*gabi.ProofD)
if !ok {
continue
......@@ -198,7 +198,7 @@ func (pl ProofList) DisclosedAttributes(configuration *Configuration, disjunctio
return len(disjunctions) == 0 || disjunctions.satisfied(), list, nil
}
func (pl ProofList) VerifyAgainstDisjunctions(
func (d *Disclosure) VerifyAgainstDisjunctions(
configuration *Configuration,
required AttributeDisjunctionList,
context, nonce *big.Int,
......@@ -206,13 +206,13 @@ func (pl ProofList) VerifyAgainstDisjunctions(
issig bool,
) ([]*DisclosedAttribute, ProofStatus, error) {
// Cryptographically verify the IRMA disclosure proofs in the signature
valid, err := pl.VerifyProofs(configuration, context, nonce, publickeys, issig)
valid, err := ProofList(d.Proofs).VerifyProofs(configuration, context, nonce, publickeys, issig)
if !valid || err != nil {
return nil, ProofStatusInvalid, err
}
// Next extract the contained attributes from the proofs, and match them to the signature request if present
allmatched, list, err := pl.DisclosedAttributes(configuration, required)
allmatched, list, err := d.DisclosedAttributes(configuration, required)
if err != nil {
return nil, ProofStatusInvalid, err
}
......@@ -225,14 +225,14 @@ func (pl ProofList) VerifyAgainstDisjunctions(
return list, ProofStatusValid, nil
}
func (pl ProofList) Verify(configuration *Configuration, request *DisclosureRequest) ([]*DisclosedAttribute, ProofStatus, error) {
list, status, err := pl.VerifyAgainstDisjunctions(configuration, request.Content, request.Context, request.Nonce, nil, false)
func (d *Disclosure) Verify(configuration *Configuration, request *DisclosureRequest) ([]*DisclosedAttribute, ProofStatus, error) {
list, status, err := d.VerifyAgainstDisjunctions(configuration, request.Content, request.Context, request.Nonce, nil, false)
if err != nil {
return list, status, err
}
now := time.Now()
if expired := pl.Expired(configuration, &now); expired {
if expired := ProofList(d.Proofs).Expired(configuration, &now); expired {
return list, ProofStatusExpired, nil
}
......@@ -272,12 +272,11 @@ func (sm *SignedMessage) Verify(configuration *Configuration, request *Signature
}
// Now, cryptographically verify the IRMA disclosure proofs in the signature
pl := ProofList(sm.Signature)
var required AttributeDisjunctionList
if request != nil {
required = request.Content
}
result, status, err := pl.VerifyAgainstDisjunctions(configuration, required, sm.Context, sm.GetNonce(), nil, true)
result, status, err := sm.Disclosure().VerifyAgainstDisjunctions(configuration, required, sm.Context, sm.GetNonce(), nil, true)
if status != ProofStatusValid || err != nil {
return result, status, err
}
......@@ -287,7 +286,7 @@ func (sm *SignedMessage) Verify(configuration *Configuration, request *Signature
if sm.Timestamp != nil {
t = time.Unix(sm.Timestamp.Time, 0)
}
if expired := pl.Expired(configuration, &t); expired {
if expired := ProofList(sm.Signature).Expired(configuration, &t); expired {
// The ABS contains attributes that were expired at the time of creation of the ABS.
return result, ProofStatusExpired, 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