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[:])
}
......@@ -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
......@@ -43,7 +42,6 @@ func (session *session) createLogEntry(response interface{}) (*LogEntry, error)
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
fallthrough
case irma.ActionDisclosing:
if prooflist, ok = response.(gabi.ProofList); !ok {
......@@ -127,11 +125,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 +150,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 +190,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,23 @@ 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:
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, message); err != nil {
if err = session.transport.Post("proofs", &response, irmaSignature); err != nil {
session.fail(err.(*irma.SessionError))
return
}
......@@ -499,25 +509,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
log, _ = session.createLogEntry(message.(gabi.ProofList)) // TODO err // TODO: also for non-interactive sessions?
}
} else {
messageJson, err = json.Marshal(message)
if err != nil {
session.fail(&irma.SessionError{ErrorType: irma.ErrorSerialization, Err: 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,81 @@ 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\",\"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
import (
"crypto/sha256"
"encoding/asn1"
"fmt"
"log"
"math/big"
"strconv"
"time"
......@@ -14,9 +11,9 @@ import (
// 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 +54,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 +183,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 +287,7 @@ 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})
if err != nil {
log.Print(err) // TODO? does this happen?
}
asn1hash := sha256.Sum256(asn1bytes)
return new(big.Int).SetBytes(asn1hash[:])
return ASN1ConvertSignatureNonce(sr.Message, sr.Nonce)
}
// Check if Timestamp is before other Timestamp. Used for checking expiry of attributes
......
......@@ -16,7 +16,7 @@ const (
VALID = ProofStatus("VALID")
EXPIRED = ProofStatus("EXPIRED")
INVALID_CRYPTO = ProofStatus("INVALID_CRYPTO")
INVALID_SYNTAX = ProofStatus("INVALID_SYNTAX")
UNMATCHED_REQUEST = ProofStatus("UNMATCHED_REQUEST")
MISSING_ATTRIBUTES = ProofStatus("MISSING_ATTRIBUTES")
)
......@@ -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
func VerifySig(configuration *Configuration, proofString string, sigRequest *SignatureRequest) *SignatureProofResult {
// First, unmarshal proof and check if all the attributes in the proofstring match the signature request
var proofList gabi.ProofList
proofBytes := []byte(proofString)
err := proofList.UnmarshalJSON(proofBytes)
if err != nil {
func VerifySig(configuration *Configuration, irmaSignature *IrmaSignedMessage, sigRequest *SignatureRequest) *SignatureProofResult {
// First, check if nonce and context of the signature match those of the signature request
if !irmaSignature.MatchesNonceAndContext(sigRequest) {
return &SignatureProofResult{
ProofResult: &ProofResult{
ProofStatus: INVALID_SYNTAX,
ProofStatus: UNMATCHED_REQUEST,
},
}
}
// Now, cryptographically verify the signature
if !verify(configuration, proofList, sigRequest.GetContext(), sigRequest.GetNonce(), true) {
if !verify(configuration, *irmaSignature.Signature, sigRequest.GetContext(), sigRequest.GetNonce(), true) {
return &SignatureProofResult{
ProofResult: &ProofResult{
ProofStatus: INVALID_CRYPTO,
......@@ -305,5 +300,27 @@ func VerifySig(configuration *Configuration, proofString string, sigRequest *Sig
}
// Finally, check whether attribute values in proof satisfy the original signature request
return checkProofWithRequest(configuration, proofList, sigRequest)
return checkProofWithRequest(configuration, *irmaSignature.Signature, sigRequest)
}
// Verify a signature cryptographically, but do not check/compare with a signature request
func VerifySigWithoutRequest(configuration *Configuration, irmaSignature *IrmaSignedMessage) (ProofStatus, DisclosedCredentialList) {
// First, cryptographically verify the signature
if !verify(configuration, *irmaSignature.Signature, irmaSignature.Context, irmaSignature.GetNonce(), true) {
return INVALID_CRYPTO, nil
}
// Extract attributes and return result
disclosed, err := extractDisclosedCredentials(configuration, *irmaSignature.Signature)
if err != nil {
fmt.Println(err)
return INVALID_CRYPTO, nil
}
if disclosed.IsExpired() {
return EXPIRED, disclosed
}
return VALID, disclosed
}
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