Unverified Commit f884e022 authored by Sietse Ringers's avatar Sietse Ringers Committed by GitHub
Browse files

Merge pull request #12 from koen92/fix-signature-format

Fix signature format
parents fe00fc40 4472df6b
......@@ -112,8 +112,15 @@ func (al *AttributeList) Strings() []TranslatedString {
}
func (al *AttributeList) decode(i int) *string {
bi := new(big.Int).Set(al.Ints[i+1])
if al.MetadataAttribute.Version() >= 3 {
attr := al.Ints[i+1]
metadataVersion := al.MetadataAttribute.Version()
return decodeAttribute(attr, metadataVersion)
}
// Decode attribute value into string according to metadataVersion
func decodeAttribute(attr *big.Int, metadataVersion byte) *string {
bi := new(big.Int).Set(attr)
if metadataVersion >= 3 {
if bi.Bit(0) == 0 { // attribute does not exist
return nil
}
......@@ -451,6 +458,27 @@ func (disjunction *AttributeDisjunction) MarshalJSON() ([]byte, error) {
return json.Marshal(temp)
}
// Since we have a custom MarshalJSON for AttributeDisjunction, we also need one for every struct that extends AttributeDisjunction...
func (disclosedAttributeDisjunction *DisclosedAttributeDisjunction) MarshalJSON() ([]byte, error) {
temp := struct {
Label string `json:"label"`
Attributes []AttributeTypeIdentifier `json:"attributes"`
DisclosedValue string `json:"disclosedValue"`
DisclosedId AttributeTypeIdentifier `json:"disclosedId"`
ProofStatus AttributeProofStatus `json:"proofStatus"`
}{
Label: disclosedAttributeDisjunction.Label,
Attributes: disclosedAttributeDisjunction.Attributes,
DisclosedValue: disclosedAttributeDisjunction.DisclosedValue,
DisclosedId: disclosedAttributeDisjunction.DisclosedId,
ProofStatus: disclosedAttributeDisjunction.ProofStatus,
}
return json.Marshal(temp)
}
// UnmarshalJSON unmarshals an attribute disjunction from JSON.
func (disjunction *AttributeDisjunction) UnmarshalJSON(bytes []byte) error {
if disjunction.Values == nil {
......
package irma
import (
"crypto/sha256"
"encoding/asn1"
"github.com/mhe/gabi"
"log"
"math/big"
)
// IrmaSignedMessage is a message signed with an attribute-based signature
// The 'realnonce' will be calculated as: SigRequest.GetNonce() = ASN1(sha256(message), sha256(nonce))
type IrmaSignedMessage struct {
Signature *gabi.ProofList `json:"signature"`
Nonce *big.Int `json:"nonce"`
Context *big.Int `json:"context"`
Message string `json:"message"`
}
func (im *IrmaSignedMessage) GetNonce() *big.Int {
return ASN1ConvertSignatureNonce(im.Message, im.Nonce)
}
func (im *IrmaSignedMessage) MatchesNonceAndContext(request *SignatureRequest) bool {
return im.Nonce.Cmp(request.Nonce) == 0 &&
im.Context.Cmp(request.Context) == 0 &&
im.GetNonce().Cmp(request.GetNonce()) == 0
}
// Convert a Nonce to a nonce of a signature session
// (with the message already hashed into it).
func ASN1ConvertSignatureNonce(message string, nonce *big.Int) *big.Int {
hashbytes := sha256.Sum256([]byte(message))
hashint := new(big.Int).SetBytes(hashbytes[:])
// TODO the 2 should be abstracted away
asn1bytes, err := asn1.Marshal([]interface{}{big.NewInt(2), nonce, hashint})
if err != nil {
log.Print(err) // TODO? does this happen?
}
asn1hash := sha256.Sum256(asn1bytes)
return new(big.Int).SetBytes(asn1hash[:])
}
......@@ -17,11 +17,10 @@ type LogEntry struct {
SessionInfo *irma.SessionInfo // Message that started the session
// Session type-specific info
Disclosed map[irma.CredentialTypeIdentifier]map[int]irma.TranslatedString // Any session type
Received map[irma.CredentialTypeIdentifier][]irma.TranslatedString // In case of issuance session
Removed map[irma.CredentialTypeIdentifier][]irma.TranslatedString // In case of credential removal
SignedMessage []byte // In case of signature sessions
SignedMessageType string // In case of signature sessions
Disclosed map[irma.CredentialTypeIdentifier]map[int]irma.TranslatedString // Any session type
Received map[irma.CredentialTypeIdentifier][]irma.TranslatedString // In case of issuance session
Removed map[irma.CredentialTypeIdentifier][]irma.TranslatedString // In case of credential removal
SignedMessage []byte // In case of signature sessions
response interface{} // Our response (ProofList or IssueCommitmentMessage)
rawResponse json.RawMessage // Unparsed []byte version of response
......@@ -42,8 +41,15 @@ func (session *session) createLogEntry(response interface{}) (*LogEntry, error)
var ok bool
switch entry.Type {
case irma.ActionSigning:
entry.SignedMessage = []byte(session.jwt.(*irma.SignatureRequestorJwt).Request.Request.Message)
entry.SignedMessageType = session.jwt.(*irma.SignatureRequestorJwt).Request.Request.MessageType
if session.IsInteractive() {
entry.SignedMessage = []byte(session.jwt.(*irma.SignatureRequestorJwt).Request.Request.Message)
} else {
request, ok := session.irmaSession.(*irma.SignatureRequest)
if !ok {
return nil, errors.New("Session does not contain a valid Signature Request")
}
entry.SignedMessage = []byte(request.Message)
}
fallthrough
case irma.ActionDisclosing:
if prooflist, ok = response.(gabi.ProofList); !ok {
......@@ -127,11 +133,10 @@ type jsonLogEntry struct {
Time irma.Timestamp
SessionInfo *logSessionInfo
Disclosed map[irma.CredentialTypeIdentifier]map[int]irma.TranslatedString `json:",omitempty"`
Received map[irma.CredentialTypeIdentifier][]irma.TranslatedString `json:",omitempty"`
Removed map[irma.CredentialTypeIdentifier][]irma.TranslatedString `json:",omitempty"`
SignedMessage []byte `json:",omitempty"`
SignedMessageType string `json:",omitempty"`
Disclosed map[irma.CredentialTypeIdentifier]map[int]irma.TranslatedString `json:",omitempty"`
Received map[irma.CredentialTypeIdentifier][]irma.TranslatedString `json:",omitempty"`
Removed map[irma.CredentialTypeIdentifier][]irma.TranslatedString `json:",omitempty"`
SignedMessage []byte `json:",omitempty"`
Response json.RawMessage
}
......@@ -153,12 +158,11 @@ func (entry *LogEntry) UnmarshalJSON(bytes []byte) error {
Context: temp.SessionInfo.Context,
Keys: make(map[irma.IssuerIdentifier]int),
},
Removed: temp.Removed,
Disclosed: temp.Disclosed,
Received: temp.Received,
SignedMessage: temp.SignedMessage,
SignedMessageType: temp.SignedMessageType,
rawResponse: temp.Response,
Removed: temp.Removed,
Disclosed: temp.Disclosed,
Received: temp.Received,
SignedMessage: temp.SignedMessage,
rawResponse: temp.Response,
}
// TODO remove on protocol upgrade
......@@ -194,15 +198,14 @@ func (entry *LogEntry) MarshalJSON() ([]byte, error) {
}
}
temp := &jsonLogEntry{
Type: entry.Type,
Time: entry.Time,
Response: entry.rawResponse,
SessionInfo: si,
Removed: entry.Removed,
Disclosed: entry.Disclosed,
Received: entry.Received,
SignedMessage: entry.SignedMessage,
SignedMessageType: entry.SignedMessageType,
Type: entry.Type,
Time: entry.Time,
Response: entry.rawResponse,
SessionInfo: si,
Removed: entry.Removed,
Disclosed: entry.Disclosed,
Received: entry.Received,
SignedMessage: entry.SignedMessage,
}
return json.Marshal(temp)
......
......@@ -34,15 +34,13 @@ func issue(t *testing.T, ms ManualSessionHandler) {
// Flip one bit in the proof string if invalidate is set to true
var invalidate bool
func corruptProofString(proof string) string {
func corruptAndConvertProofString(proof string) []byte {
proofBytes := []byte(proof)
if invalidate {
proofBytes := []byte(proof)
// 15 because this is somewhere in a bigint in the json string
proofBytes[15] ^= 0x01
return string(proofBytes)
// 42 because this is somewhere in a bigint in the json string
proofBytes[42] ^= 0x01
}
return proof
return proofBytes
}
// Create a ManualSessionHandler for unit tests
......@@ -69,7 +67,7 @@ func createManualSessionHandler(request string, invalidRequest string, t *testin
func TestManualSession(t *testing.T) {
invalidate = false
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
ms := createManualSessionHandler(request, request, t)
client = parseStorage(t)
......@@ -97,7 +95,7 @@ func TestManualSession(t *testing.T) {
func TestManualSessionUnsatisfiable(t *testing.T) {
invalidate = false
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":{\"irma-demo.RU.studentCard.studentID\": \"123\"}}]}"
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":{\"irma-demo.RU.studentCard.studentID\": \"123\"}}]}"
ms := createManualSessionHandler(request, request, t)
client = parseStorage(t)
......@@ -115,8 +113,8 @@ func TestManualSessionUnsatisfiable(t *testing.T) {
func TestManualSessionInvalidNonce(t *testing.T) {
invalidate = false
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
invalidRequest := "{\"nonce\": 1, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
invalidRequest := "{\"nonce\": 1, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
ms := createManualSessionHandler(request, invalidRequest, t)
......@@ -129,8 +127,8 @@ func TestManualSessionInvalidNonce(t *testing.T) {
}
// No errors, obtain proof result from channel
if result := <-ms.resultChannel; result.ProofStatus != irma.INVALID_CRYPTO {
t.Logf("Invalid proof result: %v Expected: %v", result.ProofStatus, irma.INVALID_CRYPTO)
if result := <-ms.resultChannel; result.ProofStatus != irma.UNMATCHED_REQUEST {
t.Logf("Invalid proof result: %v Expected: %v", result.ProofStatus, irma.UNMATCHED_REQUEST)
t.Fail()
}
test.ClearTestStorage(t)
......@@ -140,8 +138,8 @@ func TestManualSessionInvalidNonce(t *testing.T) {
func TestManualSessionInvalidRequest(t *testing.T) {
invalidate = false
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
invalidRequest := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.university\"]}]}"
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
invalidRequest := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.university\"]}]}"
ms := createManualSessionHandler(request, invalidRequest, t)
......@@ -177,8 +175,8 @@ func TestManualSessionInvalidRequest(t *testing.T) {
func TestManualSessionInvalidAttributeValue(t *testing.T) {
invalidate = false
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":{\"irma-demo.RU.studentCard.studentID\": \"456\"}}]}"
invalidRequest := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":{\"irma-demo.RU.studentCard.studentID\": \"123\"}}]}"
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":{\"irma-demo.RU.studentCard.studentID\": \"456\"}}]}"
invalidRequest := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":{\"irma-demo.RU.studentCard.studentID\": \"123\"}}]}"
ms := createManualSessionHandler(request, invalidRequest, t)
......@@ -206,7 +204,7 @@ func TestManualSessionInvalidAttributeValue(t *testing.T) {
func TestManualKeyShareSession(t *testing.T) {
invalidate = false
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"test.test.mijnirma.email\"]}]}"
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"test.test.mijnirma.email\"]}]}"
ms := createManualSessionHandler(request, request, t)
......@@ -239,7 +237,7 @@ func TestManualSessionMultiProof(t *testing.T) {
}
// Request to sign with both BSN and StudentID
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]},{\"label\":\"BSN\",\"attributes\":[\"irma-demo.MijnOverheid.root.BSN\"]}]}"
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]},{\"label\":\"BSN\",\"attributes\":[\"irma-demo.MijnOverheid.root.BSN\"]}]}"
ms := createManualSessionHandler(request, request, t)
......@@ -270,7 +268,7 @@ func TestManualSessionMultiProof(t *testing.T) {
func TestManualSessionInvalidProof(t *testing.T) {
invalidate = true
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
ms := createManualSessionHandler(request, request, t)
client = parseStorage(t)
......@@ -293,10 +291,20 @@ func (sh *ManualSessionHandler) Success(irmaAction irma.Action, result string) {
switch irmaAction {
case irma.ActionSigning:
// Make proof corrupt if we want to test invalid proofs
result = corruptProofString(result)
resultBytes := corruptAndConvertProofString(result)
irmaSignedMessage := &irma.IrmaSignedMessage{}
json.Unmarshal(resultBytes, irmaSignedMessage)
if err := json.Unmarshal(resultBytes, irmaSignedMessage); err != nil {
sh.errorChannel <- &irma.SessionError{
Err: err,
ErrorType: irma.ErrorSerialization,
}
return
}
go func() {
sh.resultChannel <- irma.VerifySig(client.Configuration, result, sh.sigVerifyRequest)
sh.resultChannel <- irma.VerifySig(client.Configuration, irmaSignedMessage, sh.sigVerifyRequest)
}()
}
sh.errorChannel <- nil
......
......@@ -485,13 +485,29 @@ func (session *session) sendResponse(message interface{}) {
var err error
var messageJson []byte
if session.IsInteractive() {
switch session.Action {
case irma.ActionSigning:
fallthrough
case irma.ActionDisclosing:
switch session.Action {
case irma.ActionSigning:
request, ok := session.irmaSession.(*irma.SignatureRequest)
if !ok {
session.fail(&irma.SessionError{ErrorType: irma.ErrorSerialization, Info: "Type assertion failed"})
return
}
irmaSignature, err := request.SignatureFromMessage(message)
if err != nil {
session.fail(&irma.SessionError{ErrorType: irma.ErrorSerialization, Info: "Type assertion failed"})
return
}
messageJson, err = json.Marshal(irmaSignature)
if err != nil {
session.fail(&irma.SessionError{ErrorType: irma.ErrorSerialization, Err: err})
return
}
if session.IsInteractive() {
var response disclosureResponse
if err = session.transport.Post("proofs", &response, message); err != nil {
if err = session.transport.Post("proofs", &response, irmaSignature); err != nil {
session.fail(err.(*irma.SessionError))
return
}
......@@ -499,25 +515,30 @@ func (session *session) sendResponse(message interface{}) {
session.fail(&irma.SessionError{ErrorType: irma.ErrorRejected, Info: string(response)})
return
}
log, _ = session.createLogEntry(message.(gabi.ProofList)) // TODO err
case irma.ActionIssuing:
response := []*gabi.IssueSignatureMessage{}
if err = session.transport.Post("commitments", &response, message); err != nil {
session.fail(err.(*irma.SessionError))
return
}
if err = session.client.ConstructCredentials(response, session.irmaSession.(*irma.IssuanceRequest)); err != nil {
session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err})
return
}
log, _ = session.createLogEntry(message) // TODO err
}
} else {
messageJson, err = json.Marshal(message)
if err != nil {
session.fail(&irma.SessionError{ErrorType: irma.ErrorSerialization, Err: err})
log, _ = session.createLogEntry(message.(gabi.ProofList)) // TODO err
case irma.ActionDisclosing:
var response disclosureResponse
if err = session.transport.Post("proofs", &response, message); err != nil {
session.fail(err.(*irma.SessionError))
return
}
if response != "VALID" {
session.fail(&irma.SessionError{ErrorType: irma.ErrorRejected, Info: string(response)})
return
}
log, _ = session.createLogEntry(message.(gabi.ProofList)) // TODO err
case irma.ActionIssuing:
response := []*gabi.IssueSignatureMessage{}
if err = session.transport.Post("commitments", &response, message); err != nil {
session.fail(err.(*irma.SessionError))
return
}
if err = session.client.ConstructCredentials(response, session.irmaSession.(*irma.IssuanceRequest)); err != nil {
session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err})
return
}
log, _ = session.createLogEntry(message) // TODO err
}
_ = session.client.addLogEntry(log) // TODO err
......
......@@ -12,6 +12,7 @@ import (
"github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/internal/test"
"github.com/stretchr/testify/require"
"math/big"
)
type TestHandler struct {
......@@ -97,9 +98,12 @@ func getDisclosureJwt(name string, id irma.AttributeTypeIdentifier) interface{}
func getSigningJwt(name string, id irma.AttributeTypeIdentifier) interface{} {
return irma.NewSignatureRequestorJwt(name, &irma.SignatureRequest{
Message: "test",
MessageType: "STRING",
Message: "test",
DisclosureRequest: irma.DisclosureRequest{
SessionRequest: irma.SessionRequest{
Nonce: big.NewInt(1),
Context: big.NewInt(1),
},
Content: irma.AttributeDisjunctionList([]*irma.AttributeDisjunction{{
Label: "foo",
Attributes: []irma.AttributeTypeIdentifier{id},
......
......@@ -238,3 +238,114 @@ func TestServiceProvider(t *testing.T) {
require.NotNil(t, spjwt.Request.Request.Content.Find(NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")))
}
func TestVerifyValidSig(t *testing.T) {
conf := parseConfiguration(t)
irmaSignedMessageJson := "{\"signature\":[{\"c\":84387860940227163820403495286748244564707922600034683359608691396081567025602,\"A\":88281712394778800408977859704145725624282144718487192171772162550525428429519190981888278801087212465321690290335409063053117882098498400332781065742081358484833309192327120556339332157874905818122935827879129997343067882402015244634759393479031207772945330229833048906663580440236616733112577470729616366786,\"e_response\":158765301676865980664519775970491722252026400039481431972586202039855979595012153817023420279082580759592345985626616023615379719752399328,\"v_response\":6611746513363446317846147623316898858155094502709878009639760817201381076603429289631603604529060691249377615651154531801036452874566979268891286567129727685535821475399052346372076099618720496054457827506328803409424296248607363948476756029304344829069350093632120223961428827899945063479494984026332706037701056800933468297084225080081776744374335801370875205735636759162890211232669349095889736548891226015515520674239004969135762927189899345062330063667418982393995289342139999902051131072263853724059860710122540669055502508808347469655730875919155588858804817423089007580489779129833698002045070596906945040,\"a_responses\":{\"0\":4434835936588491146877061202237807779146602258202436880339162159399867035387373999563459639269288401746730567517727346492632152446746295360674508158204107496002345160420756725205,\"2\":9977462643736893130880681297952480237124511902476432783965737452034254055110952583256642665857196544092176573351041403473080012083097371376185951351204344524454609167069034294004,\"3\":1904659714829479350823098920128825893793063661618141776764549847325998719856920007188109803030455877752450027088898741652270026015791752527030236440389575193675041497254880209617,\"5\":3936452247676614466878886279006122732122146262864401884641740995040119769042022356436514614808831282997701569758564908168629905499336984204831548844404122251651655734975885548663},\"a_disclosed\":{\"1\":49043497911096929607726931703203423024551950578089278988,\"4\":3421494}}],\"nonce\":42,\"context\":1337,\"message\":\"I owe you everything\"}"
irmaSignedMessage := &IrmaSignedMessage{}
json.Unmarshal([]byte(irmaSignedMessageJson), irmaSignedMessage)
request := "{\"nonce\": 42, \"context\": 1337, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
sigRequestJSON := []byte(request)
sigRequest := &SignatureRequest{}
json.Unmarshal(sigRequestJSON, sigRequest)
// Test marshalling of 'string' fields:
require.Equal(t, sigRequest.Nonce, big.NewInt(42))
require.Equal(t, sigRequest.Context, big.NewInt(1337))
// Test if we can verify it with the original request
sigProofResult := VerifySig(conf, irmaSignedMessage, sigRequest)
require.Equal(t, sigProofResult.ProofStatus, VALID)
attributeList := sigProofResult.ToAttributeResultList()
require.Len(t, attributeList, 1)
require.Equal(t, attributeList[0].AttributeProofStatus, PRESENT)
require.Equal(t, attributeList[0].AttributeValue, "456")
// Test if we can verify it with a request that contains strings instead of ints for nonce and context
stringRequest := "{\"nonce\": \"42\", \"context\": \"1337\", \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
stringSigRequestJSON := []byte(stringRequest)
stringSigRequest := &SignatureRequest{}
json.Unmarshal(stringSigRequestJSON, stringSigRequest)
// Test marshalling of 'string' fields:
require.Equal(t, stringSigRequest.Nonce, big.NewInt(42))
require.Equal(t, stringSigRequest.Context, big.NewInt(1337))
// Test if we can verify it with the original request
stringSigProofResult := VerifySig(conf, irmaSignedMessage, sigRequest)
require.Equal(t, stringSigProofResult.ProofStatus, VALID)
stringAttributeList := sigProofResult.ToAttributeResultList()
require.Len(t, stringAttributeList, 1)
require.Equal(t, stringAttributeList[0].AttributeProofStatus, PRESENT)
require.Equal(t, stringAttributeList[0].AttributeValue, "456")
// Test verify against unmatched request (i.e. different nonce, context or message)
unmatched := "{\"nonce\": 42, \"context\": 1337, \"message\":\"I owe you NOTHING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
unmatchedSigRequestJSON := []byte(unmatched)
unmatchedSigRequest := &SignatureRequest{}
json.Unmarshal(unmatchedSigRequestJSON, unmatchedSigRequest)
unmatchedResult := VerifySig(conf, irmaSignedMessage, unmatchedSigRequest)
require.Equal(t, unmatchedResult.ProofStatus, UNMATCHED_REQUEST)
// Test if we can also verify it without using the original request
proofStatus, disclosed := VerifySigWithoutRequest(conf, irmaSignedMessage)
require.Equal(t, proofStatus, VALID)
require.Len(t, disclosed, 1)
require.Equal(t, *disclosed[0].Attributes[NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")], "456")
}
func TestVerifyInValidSig(t *testing.T) {
conf := parseConfiguration(t)
// Same json as valid case, but starts with c: 74.. instead of c: 84..
irmaSignedMessageJson := "{\"signature\":[{\"c\":74387860940227163820403495286748244564707922600034683359608691396081567025602,\"A\":88281712394778800408977859704145725624282144718487192171772162550525428429519190981888278801087212465321690290335409063053117882098498400332781065742081358484833309192327120556339332157874905818122935827879129997343067882402015244634759393479031207772945330229833048906663580440236616733112577470729616366786,\"e_response\":158765301676865980664519775970491722252026400039481431972586202039855979595012153817023420279082580759592345985626616023615379719752399328,\"v_response\":6611746513363446317846147623316898858155094502709878009639760817201381076603429289631603604529060691249377615651154531801036452874566979268891286567129727685535821475399052346372076099618720496054457827506328803409424296248607363948476756029304344829069350093632120223961428827899945063479494984026332706037701056800933468297084225080081776744374335801370875205735636759162890211232669349095889736548891226015515520674239004969135762927189899345062330063667418982393995289342139999902051131072263853724059860710122540669055502508808347469655730875919155588858804817423089007580489779129833698002045070596906945040,\"a_responses\":{\"0\":4434835936588491146877061202237807779146602258202436880339162159399867035387373999563459639269288401746730567517727346492632152446746295360674508158204107496002345160420756725205,\"2\":9977462643736893130880681297952480237124511902476432783965737452034254055110952583256642665857196544092176573351041403473080012083097371376185951351204344524454609167069034294004,\"3\":1904659714829479350823098920128825893793063661618141776764549847325998719856920007188109803030455877752450027088898741652270026015791752527030236440389575193675041497254880209617,\"5\":3936452247676614466878886279006122732122146262864401884641740995040119769042022356436514614808831282997701569758564908168629905499336984204831548844404122251651655734975885548663},\"a_disclosed\":{\"1\":49043497911096929607726931703203423024551950578089278988,\"4\":3421494}}],\"nonce\":42,\"context\":1337,\"message\":\"I owe you everything\"}"
irmaSignedMessage := &IrmaSignedMessage{}
json.Unmarshal([]byte(irmaSignedMessageJson), irmaSignedMessage)
request := "{\"nonce\": 42, \"context\": 1337, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
sigRequestJSON := []byte(request)
sigRequest := &SignatureRequest{}
json.Unmarshal(sigRequestJSON, sigRequest)
sigProofResult := VerifySig(conf, irmaSignedMessage, sigRequest)
require.Equal(t, sigProofResult.ProofStatus, INVALID_CRYPTO)
proofStatus, disclosed := VerifySigWithoutRequest(conf, irmaSignedMessage)
require.Equal(t, proofStatus, INVALID_CRYPTO)
require.Nil(t, disclosed)
}
func TestVerifyInValidNonce(t *testing.T) {
conf := parseConfiguration(t)
// Same json as valid case, has invalid nonce
irmaSignedMessageJson := "{\"signature\":[{\"c\":84387860940227163820403495286748244564707922600034683359608691396081567025602,\"A\":88281712394778800408977859704145725624282144718487192171772162550525428429519190981888278801087212465321690290335409063053117882098498400332781065742081358484833309192327120556339332157874905818122935827879129997343067882402015244634759393479031207772945330229833048906663580440236616733112577470729616366786,\"e_response\":158765301676865980664519775970491722252026400039481431972586202039855979595012153817023420279082580759592345985626616023615379719752399328,\"v_response\":6611746513363446317846147623316898858155094502709878009639760817201381076603429289631603604529060691249377615651154531801036452874566979268891286567129727685535821475399052346372076099618720496054457827506328803409424296248607363948476756029304344829069350093632120223961428827899945063479494984026332706037701056800933468297084225080081776744374335801370875205735636759162890211232669349095889736548891226015515520674239004969135762927189899345062330063667418982393995289342139999902051131072263853724059860710122540669055502508808347469655730875919155588858804817423089007580489779129833698002045070596906945040,\"a_responses\":{\"0\":4434835936588491146877061202237807779146602258202436880339162159399867035387373999563459639269288401746730567517727346492632152446746295360674508158204107496002345160420756725205,\"2\":9977462643736893130880681297952480237124511902476432783965737452034254055110952583256642665857196544092176573351041403473080012083097371376185951351204344524454609167069034294004,\"3\":1904659714829479350823098920128825893793063661618141776764549847325998719856920007188109803030455877752450027088898741652270026015791752527030236440389575193675041497254880209617,\"5\":3936452247676614466878886279006122732122146262864401884641740995040119769042022356436514614808831282997701569758564908168629905499336984204831548844404122251651655734975885548663},\"a_disclosed\":{\"1\":49043497911096929607726931703203423024551950578089278988,\"4\":3421494}}],\"nonce\":4242,\"context\":1337,\"message\":\"I owe you everything\"}"
irmaSignedMessage := &IrmaSignedMessage{}
json.Unmarshal([]byte(irmaSignedMessageJson), irmaSignedMessage)
// Original request also has the same invalid nonce (otherwise we would get unmatched_request)
request := "{\"nonce\": 4242, \"context\": 1337, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
sigRequestJSON := []byte(request)
sigRequest := &SignatureRequest{}
json.Unmarshal(sigRequestJSON, sigRequest)
sigProofResult := VerifySig(conf, irmaSignedMessage, sigRequest)
require.Equal(t, sigProofResult.ProofStatus, INVALID_CRYPTO)
proofStatus, disclosed := VerifySigWithoutRequest(conf, irmaSignedMessage)
require.Equal(t, proofStatus, INVALID_CRYPTO)
require.Nil(t, disclosed)
}
// Test attribute decoding with both old and new metadata versions
func TestAttributeDecoding(t *testing.T) {
expected := "male"
newAttribute, _ := new(big.Int).SetString("3670202571", 10)
newString := decodeAttribute(newAttribute, 3)
require.Equal(t, *newString, expected)
oldAttribute, _ := new(big.Int).SetString("1835101285", 10)
oldString := decodeAttribute(oldAttribute, 2)
require.Equal(t, *oldString, expected)
}
package irma
import (
"crypto/sha256"
"encoding/asn1"
"fmt"
"log"
"math/big"
"strconv"
"time"
"encoding/json"
"github.com/go-errors/errors"
"github.com/mhe/gabi"
)
// SessionRequest contains the context and nonce for an IRMA session.
type SessionRequest struct {
Context *big.Int `json:"context"`
Nonce *big.Int `json:"nonce"`
Candidates [][]*AttributeIdentifier
Context *big.Int `json:"context"`
Nonce *big.Int `json:"nonce"`
Candidates [][]*AttributeIdentifier `json:"-"`
Choice *DisclosureChoice `json:"-"`
Ids *IrmaIdentifierSet `json:"-"`
......@@ -57,8 +56,7 @@ type DisclosureRequest struct {
// A SignatureRequest is a a request to sign a message with certain attributes.
type SignatureRequest struct {
DisclosureRequest
Message string `json:"message"`
MessageType string `json:"messageType"`
Message string `json:"message"`
}
// An IssuanceRequest is a request to issue certain credentials,
......@@ -187,7 +185,7 @@ func (cr *CredentialRequest) AttributeList(conf *Configuration, metadataVersion
attrs[i+1].Add(attrs[i+1], big.NewInt(1)) // attr += 1
}
} else {
if (attrtype.Optional != "true") {
if attrtype.Optional != "true" {
return nil, errors.New("Required attribute not provided")
}
}
......@@ -291,15 +289,64 @@ func (dr *DisclosureRequest) SetNonce(nonce *big.Int) { dr.Nonce = nonce }
// GetNonce returns the nonce of this signature session
// (with the message already hashed into it).
func (sr *SignatureRequest) GetNonce() *big.Int {
hashbytes := sha256.Sum256([]byte(sr.Message))
hashint := new(big.Int).SetBytes(hashbytes[:])
// TODO the 2 should be abstracted away
asn1bytes, err := asn1.Marshal([]interface{}{big.NewInt(2), sr.Nonce, hashint})