Commit 12e2d6a6 authored by Koen van Ingen's avatar Koen van Ingen
Browse files

Make irma signature format consistent with irma api server

parent fe00fc40
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 {
// TODO: string comparison needed?
return im.Nonce.String() == request.Nonce.String() &&
im.Context.String() == request.Context.String() &&
im.GetNonce().String() == request.GetNonce().String()
}
// Create an IrmaSignedMessage struct and check if type assertions hold
// bool is false if type assertion failed
func SignedMessageFromSession(session IrmaSession, message interface{}) (*IrmaSignedMessage, bool) {
signature, ok1 := message.(gabi.ProofList)
request, ok2 := session.(*SignatureRequest)
if !ok1 || !ok2 {
return nil, false
}
return &IrmaSignedMessage{
Signature: &signature,
Nonce: request.Nonce,
Context: request.Context,
Message: request.Message,
}, true
}
// 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[:])
}
...@@ -21,7 +21,6 @@ type LogEntry struct { ...@@ -21,7 +21,6 @@ type LogEntry struct {
Received map[irma.CredentialTypeIdentifier][]irma.TranslatedString // In case of issuance session Received map[irma.CredentialTypeIdentifier][]irma.TranslatedString // In case of issuance session
Removed map[irma.CredentialTypeIdentifier][]irma.TranslatedString // In case of credential removal Removed map[irma.CredentialTypeIdentifier][]irma.TranslatedString // In case of credential removal
SignedMessage []byte // In case of signature sessions SignedMessage []byte // In case of signature sessions
SignedMessageType string // In case of signature sessions
response interface{} // Our response (ProofList or IssueCommitmentMessage) response interface{} // Our response (ProofList or IssueCommitmentMessage)
rawResponse json.RawMessage // Unparsed []byte version of response rawResponse json.RawMessage // Unparsed []byte version of response
...@@ -43,7 +42,6 @@ func (session *session) createLogEntry(response interface{}) (*LogEntry, error) ...@@ -43,7 +42,6 @@ func (session *session) createLogEntry(response interface{}) (*LogEntry, error)
switch entry.Type { switch entry.Type {
case irma.ActionSigning: case irma.ActionSigning:
entry.SignedMessage = []byte(session.jwt.(*irma.SignatureRequestorJwt).Request.Request.Message) entry.SignedMessage = []byte(session.jwt.(*irma.SignatureRequestorJwt).Request.Request.Message)
entry.SignedMessageType = session.jwt.(*irma.SignatureRequestorJwt).Request.Request.MessageType
fallthrough fallthrough
case irma.ActionDisclosing: case irma.ActionDisclosing:
if prooflist, ok = response.(gabi.ProofList); !ok { if prooflist, ok = response.(gabi.ProofList); !ok {
...@@ -131,7 +129,6 @@ type jsonLogEntry struct { ...@@ -131,7 +129,6 @@ type jsonLogEntry struct {
Received map[irma.CredentialTypeIdentifier][]irma.TranslatedString `json:",omitempty"` Received map[irma.CredentialTypeIdentifier][]irma.TranslatedString `json:",omitempty"`
Removed map[irma.CredentialTypeIdentifier][]irma.TranslatedString `json:",omitempty"` Removed map[irma.CredentialTypeIdentifier][]irma.TranslatedString `json:",omitempty"`
SignedMessage []byte `json:",omitempty"` SignedMessage []byte `json:",omitempty"`
SignedMessageType string `json:",omitempty"`
Response json.RawMessage Response json.RawMessage
} }
...@@ -157,7 +154,6 @@ func (entry *LogEntry) UnmarshalJSON(bytes []byte) error { ...@@ -157,7 +154,6 @@ func (entry *LogEntry) UnmarshalJSON(bytes []byte) error {
Disclosed: temp.Disclosed, Disclosed: temp.Disclosed,
Received: temp.Received, Received: temp.Received,
SignedMessage: temp.SignedMessage, SignedMessage: temp.SignedMessage,
SignedMessageType: temp.SignedMessageType,
rawResponse: temp.Response, rawResponse: temp.Response,
} }
...@@ -202,7 +198,6 @@ func (entry *LogEntry) MarshalJSON() ([]byte, error) { ...@@ -202,7 +198,6 @@ func (entry *LogEntry) MarshalJSON() ([]byte, error) {
Disclosed: entry.Disclosed, Disclosed: entry.Disclosed,
Received: entry.Received, Received: entry.Received,
SignedMessage: entry.SignedMessage, SignedMessage: entry.SignedMessage,
SignedMessageType: entry.SignedMessageType,
} }
return json.Marshal(temp) return json.Marshal(temp)
......
...@@ -34,15 +34,13 @@ func issue(t *testing.T, ms ManualSessionHandler) { ...@@ -34,15 +34,13 @@ func issue(t *testing.T, ms ManualSessionHandler) {
// Flip one bit in the proof string if invalidate is set to true // Flip one bit in the proof string if invalidate is set to true
var invalidate bool var invalidate bool
func corruptProofString(proof string) string { func corruptAndConvertProofString(proof string) []byte {
if invalidate {
proofBytes := []byte(proof) proofBytes := []byte(proof)
if invalidate {
// 15 because this is somewhere in a bigint in the json string // 42 because this is somewhere in a bigint in the json string
proofBytes[15] ^= 0x01 proofBytes[42] ^= 0x01
return string(proofBytes)
} }
return proof return proofBytes
} }
// Create a ManualSessionHandler for unit tests // Create a ManualSessionHandler for unit tests
...@@ -69,7 +67,7 @@ func createManualSessionHandler(request string, invalidRequest string, t *testin ...@@ -69,7 +67,7 @@ func createManualSessionHandler(request string, invalidRequest string, t *testin
func TestManualSession(t *testing.T) { func TestManualSession(t *testing.T) {
invalidate = false 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) ms := createManualSessionHandler(request, request, t)
client = parseStorage(t) client = parseStorage(t)
...@@ -97,7 +95,7 @@ func TestManualSession(t *testing.T) { ...@@ -97,7 +95,7 @@ func TestManualSession(t *testing.T) {
func TestManualSessionUnsatisfiable(t *testing.T) { func TestManualSessionUnsatisfiable(t *testing.T) {
invalidate = false 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) ms := createManualSessionHandler(request, request, t)
client = parseStorage(t) client = parseStorage(t)
...@@ -115,8 +113,8 @@ func TestManualSessionUnsatisfiable(t *testing.T) { ...@@ -115,8 +113,8 @@ func TestManualSessionUnsatisfiable(t *testing.T) {
func TestManualSessionInvalidNonce(t *testing.T) { func TestManualSessionInvalidNonce(t *testing.T) {
invalidate = false 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\"]}]}"
invalidRequest := "{\"nonce\": 1, \"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\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
ms := createManualSessionHandler(request, invalidRequest, t) ms := createManualSessionHandler(request, invalidRequest, t)
...@@ -129,8 +127,8 @@ func TestManualSessionInvalidNonce(t *testing.T) { ...@@ -129,8 +127,8 @@ func TestManualSessionInvalidNonce(t *testing.T) {
} }
// No errors, obtain proof result from channel // No errors, obtain proof result from channel
if result := <-ms.resultChannel; 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.INVALID_CRYPTO) t.Logf("Invalid proof result: %v Expected: %v", result.ProofStatus, irma.UNMATCHED_REQUEST)
t.Fail() t.Fail()
} }
test.ClearTestStorage(t) test.ClearTestStorage(t)
...@@ -140,8 +138,8 @@ func TestManualSessionInvalidNonce(t *testing.T) { ...@@ -140,8 +138,8 @@ func TestManualSessionInvalidNonce(t *testing.T) {
func TestManualSessionInvalidRequest(t *testing.T) { func TestManualSessionInvalidRequest(t *testing.T) {
invalidate = false 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\"]}]}"
invalidRequest := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.university\"]}]}" 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) ms := createManualSessionHandler(request, invalidRequest, t)
...@@ -177,8 +175,8 @@ func TestManualSessionInvalidRequest(t *testing.T) { ...@@ -177,8 +175,8 @@ func TestManualSessionInvalidRequest(t *testing.T) {
func TestManualSessionInvalidAttributeValue(t *testing.T) { func TestManualSessionInvalidAttributeValue(t *testing.T) {
invalidate = false 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\"}}]}" 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\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":{\"irma-demo.RU.studentCard.studentID\": \"123\"}}]}" 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) ms := createManualSessionHandler(request, invalidRequest, t)
...@@ -206,7 +204,7 @@ func TestManualSessionInvalidAttributeValue(t *testing.T) { ...@@ -206,7 +204,7 @@ func TestManualSessionInvalidAttributeValue(t *testing.T) {
func TestManualKeyShareSession(t *testing.T) { func TestManualKeyShareSession(t *testing.T) {
invalidate = false 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) ms := createManualSessionHandler(request, request, t)
...@@ -239,7 +237,7 @@ func TestManualSessionMultiProof(t *testing.T) { ...@@ -239,7 +237,7 @@ func TestManualSessionMultiProof(t *testing.T) {
} }
// Request to sign with both BSN and StudentID // 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) ms := createManualSessionHandler(request, request, t)
...@@ -270,7 +268,7 @@ func TestManualSessionMultiProof(t *testing.T) { ...@@ -270,7 +268,7 @@ func TestManualSessionMultiProof(t *testing.T) {
func TestManualSessionInvalidProof(t *testing.T) { func TestManualSessionInvalidProof(t *testing.T) {
invalidate = true 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) ms := createManualSessionHandler(request, request, t)
client = parseStorage(t) client = parseStorage(t)
...@@ -293,10 +291,20 @@ func (sh *ManualSessionHandler) Success(irmaAction irma.Action, result string) { ...@@ -293,10 +291,20 @@ func (sh *ManualSessionHandler) Success(irmaAction irma.Action, result string) {
switch irmaAction { switch irmaAction {
case irma.ActionSigning: case irma.ActionSigning:
// Make proof corrupt if we want to test invalid proofs // 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() { go func() {
sh.resultChannel <- irma.VerifySig(client.Configuration, result, sh.sigVerifyRequest) sh.resultChannel <- irma.VerifySig(client.Configuration, irmaSignedMessage, sh.sigVerifyRequest)
}() }()
} }
sh.errorChannel <- nil sh.errorChannel <- nil
......
...@@ -485,10 +485,32 @@ func (session *session) sendResponse(message interface{}) { ...@@ -485,10 +485,32 @@ func (session *session) sendResponse(message interface{}) {
var err error var err error
var messageJson []byte var messageJson []byte
if session.IsInteractive() {
switch session.Action { switch session.Action {
case irma.ActionSigning: case irma.ActionSigning:
fallthrough irmaSignature, ok := irma.SignedMessageFromSession(session.irmaSession, message)
if !ok {
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, irmaSignature); 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 // TODO: also for non-interactive sessions?
}
case irma.ActionDisclosing: case irma.ActionDisclosing:
var response disclosureResponse var response disclosureResponse
if err = session.transport.Post("proofs", &response, message); err != nil { if err = session.transport.Post("proofs", &response, message); err != nil {
...@@ -512,13 +534,6 @@ func (session *session) sendResponse(message interface{}) { ...@@ -512,13 +534,6 @@ func (session *session) sendResponse(message interface{}) {
} }
log, _ = session.createLogEntry(message) // TODO err log, _ = session.createLogEntry(message) // TODO err
} }
} else {
messageJson, err = json.Marshal(message)
if err != nil {
session.fail(&irma.SessionError{ErrorType: irma.ErrorSerialization, Err: err})
return
}
}
_ = session.client.addLogEntry(log) // TODO err _ = session.client.addLogEntry(log) // TODO err
if session.Action == irma.ActionIssuing { if session.Action == irma.ActionIssuing {
......
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"github.com/privacybydesign/irmago" "github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/internal/test" "github.com/privacybydesign/irmago/internal/test"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"math/big"
) )
type TestHandler struct { type TestHandler struct {
...@@ -98,8 +99,11 @@ func getDisclosureJwt(name string, id irma.AttributeTypeIdentifier) interface{} ...@@ -98,8 +99,11 @@ func getDisclosureJwt(name string, id irma.AttributeTypeIdentifier) interface{}
func getSigningJwt(name string, id irma.AttributeTypeIdentifier) interface{} { func getSigningJwt(name string, id irma.AttributeTypeIdentifier) interface{} {
return irma.NewSignatureRequestorJwt(name, &irma.SignatureRequest{ return irma.NewSignatureRequestorJwt(name, &irma.SignatureRequest{
Message: "test", Message: "test",
MessageType: "STRING",
DisclosureRequest: irma.DisclosureRequest{ DisclosureRequest: irma.DisclosureRequest{
SessionRequest: irma.SessionRequest{
Nonce: big.NewInt(1),
Context: big.NewInt(1),
},
Content: irma.AttributeDisjunctionList([]*irma.AttributeDisjunction{{ Content: irma.AttributeDisjunctionList([]*irma.AttributeDisjunction{{
Label: "foo", Label: "foo",
Attributes: []irma.AttributeTypeIdentifier{id}, Attributes: []irma.AttributeTypeIdentifier{id},
......
...@@ -238,3 +238,81 @@ func TestServiceProvider(t *testing.T) { ...@@ -238,3 +238,81 @@ func TestServiceProvider(t *testing.T) {
require.NotNil(t, spjwt.Request.Request.Content.Find(NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID"))) 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\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
sigRequestJSON := []byte(request)
sigRequest := &SignatureRequest{}
json.Unmarshal(sigRequestJSON, sigRequest)
// 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 verify against unmatched request (i.e. different nonce, context or message)
unmatched := "{\"nonce\": 42, \"context\": 1337, \"message\":\"I owe you NOTHING\",\"messageType\":\"STRING\",\"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].GetAttributeValue(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\",\"messageType\":\"STRING\",\"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\",\"messageType\":\"STRING\",\"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)
}
package irma package irma
import ( import (
"crypto/sha256"
"encoding/asn1"
"fmt" "fmt"
"log"
"math/big" "math/big"
"strconv" "strconv"
"time" "time"
...@@ -16,7 +13,7 @@ import ( ...@@ -16,7 +13,7 @@ import (
type SessionRequest struct { type SessionRequest struct {
Context *big.Int `json:"context"` Context *big.Int `json:"context"`
Nonce *big.Int `json:"nonce"` Nonce *big.Int `json:"nonce"`
Candidates [][]*AttributeIdentifier Candidates [][]*AttributeIdentifier `json:"-"`
Choice *DisclosureChoice `json:"-"` Choice *DisclosureChoice `json:"-"`
Ids *IrmaIdentifierSet `json:"-"` Ids *IrmaIdentifierSet `json:"-"`
...@@ -58,7 +55,6 @@ type DisclosureRequest struct { ...@@ -58,7 +55,6 @@ type DisclosureRequest struct {
type SignatureRequest struct { type SignatureRequest struct {
DisclosureRequest DisclosureRequest
Message string `json:"message"` Message string `json:"message"`
MessageType string `json:"messageType"`
} }
// An IssuanceRequest is a request to issue certain credentials, // An IssuanceRequest is a request to issue certain credentials,
...@@ -187,7 +183,7 @@ func (cr *CredentialRequest) AttributeList(conf *Configuration, metadataVersion ...@@ -187,7 +183,7 @@ func (cr *CredentialRequest) AttributeList(conf *Configuration, metadataVersion
attrs[i+1].Add(attrs[i+1], big.NewInt(1)) // attr += 1 attrs[i+1].Add(attrs[i+1], big.NewInt(1)) // attr += 1
} }
} else { } else {
if (attrtype.Optional != "true") { if attrtype.Optional != "true" {
return nil, errors.New("Required attribute not provided") return nil, errors.New("Required attribute not provided")
} }
} }
...@@ -291,15 +287,7 @@ func (dr *DisclosureRequest) SetNonce(nonce *big.Int) { dr.Nonce = nonce } ...@@ -291,15 +287,7 @@ func (dr *DisclosureRequest) SetNonce(nonce *big.Int) { dr.Nonce = nonce }
// GetNonce returns the nonce of this signature session // GetNonce returns the nonce of this signature session
// (with the message already hashed into it). // (with the message already hashed into it).
func (sr *SignatureRequest) GetNonce() *big.Int { func (sr *SignatureRequest) GetNonce() *big.Int {
hashbytes := sha256.Sum256([]byte(sr.Message)) return ASN1ConvertSignatureNonce(sr.Message, sr.Nonce)
hashint := new(big.Int).SetBytes(hashbytes[:])
// TODO the 2 should be abstracted away
asn1bytes, err := asn1.Marshal([]interface{}{big.NewInt(2), sr.Nonce, hashint})
if err != nil {
log.Print(err) // TODO? does this happen?
}
asn1hash := sha256.Sum256(asn1bytes)
return new(big.Int).SetBytes(asn1hash[:])
} }
// Check if Timestamp is before other Timestamp. Used for checking expiry of attributes // Check if Timestamp is before other Timestamp. Used for checking expiry of attributes
......
...@@ -16,7 +16,7 @@ const ( ...@@ -16,7 +16,7 @@ const (
VALID = ProofStatus("VALID") VALID = ProofStatus("VALID")
EXPIRED = ProofStatus("EXPIRED") EXPIRED = ProofStatus("EXPIRED")
INVALID_CRYPTO = ProofStatus("INVALID_CRYPTO") INVALID_CRYPTO = ProofStatus("INVALID_CRYPTO")
INVALID_SYNTAX = ProofStatus("INVALID_SYNTAX") UNMATCHED_REQUEST = ProofStatus("UNMATCHED_REQUEST")
MISSING_ATTRIBUTES = ProofStatus("MISSING_ATTRIBUTES") MISSING_ATTRIBUTES = ProofStatus("MISSING_ATTRIBUTES")
) )
...@@ -280,23 +280,18 @@ func verify(configuration *Configuration, proofList gabi.ProofList, context *big ...@@ -280,23 +280,18 @@ func verify(configuration *Configuration, proofList gabi.ProofList, context *big
} }
// Verify a signature proof and check if the attributes match the attributes in the original request // Verify a signature proof and check if the attributes match the attributes in the original request
func VerifySig(configuration *Configuration, proofString string, sigRequest *SignatureRequest) *SignatureProofResult { func VerifySig(configuration *Configuration, irmaSignature *IrmaSignedMessage, sigRequest *SignatureRequest) *SignatureProofResult {
// First, check if nonce and context of the signature match those of the signature request
// First, unmarshal proof and check if all the attributes in the proofstring match the signature request if !irmaSignature.MatchesNonceAndContext(sigRequest) {
var proofList gabi.ProofList
proofBytes := []byte(proofString)
err := proofList.UnmarshalJSON(proofBytes)