Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
IRMA
Github mirrors
irmago
Commits
183c7256
Commit
183c7256
authored
May 06, 2018
by
Sietse Ringers
Browse files
Add initial support for obtaining and verifying timestamps in ABS sessions
parent
f884e022
Changes
6
Hide whitespace changes
Inline
Side-by-side
irma_signature.go
View file @
183c7256
...
...
@@ -3,22 +3,26 @@ package irma
import
(
"crypto/sha256"
"encoding/asn1"
"github.com/mhe/gabi"
"log"
"math/big"
"github.com/bwesterb/go-atum"
"github.com/mhe/gabi"
)
// IrmaSignedMessage is a message signed with an attribute-based signature
// The 'realnonce' will be calculated as: SigRequest.GetNonce() = ASN1(sha256(message), sha256(nonce))
// // TODO: remove pointer from Signature
type
IrmaSignedMessage
struct
{
Signature
*
gabi
.
ProofList
`json:"signature"`
Nonce
*
big
.
Int
`json:"nonce"`
Context
*
big
.
Int
`json:"context"`
Message
string
`json:"message"`
Timestamp
*
atum
.
Timestamp
`json:"timestamp"`
}
func
(
im
*
IrmaSignedMessage
)
GetNonce
()
*
big
.
Int
{
return
ASN1ConvertSignatureNonce
(
im
.
Message
,
im
.
Nonce
)
return
ASN1ConvertSignatureNonce
(
im
.
Message
,
im
.
Nonce
,
im
.
Timestamp
)
}
func
(
im
*
IrmaSignedMessage
)
MatchesNonceAndContext
(
request
*
SignatureRequest
)
bool
{
...
...
@@ -29,13 +33,17 @@ func (im *IrmaSignedMessage) MatchesNonceAndContext(request *SignatureRequest) b
// 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
})
func
ASN1ConvertSignatureNonce
(
message
string
,
nonce
*
big
.
Int
,
timestamp
*
atum
.
Timestamp
)
*
big
.
Int
{
msgHash
:=
sha256
.
Sum256
([]
byte
(
message
))
tohash
:=
[]
interface
{}{
nonce
,
new
(
big
.
Int
)
.
SetBytes
(
msgHash
[
:
])}
if
timestamp
!=
nil
{
tohash
=
append
(
tohash
,
timestamp
.
Sig
.
Data
)
}
// TODO remove the 2, or keep backwards compatible?
tohash
=
append
([]
interface
{}{
big
.
NewInt
(
int64
(
len
(
tohash
)))},
tohash
...
)
asn1bytes
,
err
:=
asn1
.
Marshal
(
tohash
)
if
err
!=
nil
{
log
.
Print
(
err
)
// TODO
? does this happen?
log
.
Print
(
err
)
// TODO
}
asn1hash
:=
sha256
.
Sum256
(
asn1bytes
)
return
new
(
big
.
Int
)
.
SetBytes
(
asn1hash
[
:
])
...
...
irmaclient/client.go
View file @
183c7256
...
...
@@ -547,6 +547,24 @@ func (client *Client) Proofs(choice *irma.DisclosureChoice, request irma.IrmaSes
if
err
!=
nil
{
return
nil
,
err
}
if
issig
{
var
sigs
[]
*
big
.
Int
var
disclosed
[][]
*
big
.
Int
var
s
*
big
.
Int
var
d
[]
*
big
.
Int
for
_
,
builder
:=
range
builders
{
s
,
d
=
builder
.
(
*
gabi
.
DisclosureProofBuilder
)
.
TimestampRequestContributions
()
sigs
=
append
(
sigs
,
s
)
disclosed
=
append
(
disclosed
,
d
)
}
r
:=
request
.
(
*
irma
.
SignatureRequest
)
r
.
Timestamp
,
err
=
irma
.
GetTimestamp
(
r
.
Message
,
sigs
,
disclosed
)
if
err
!=
nil
{
return
nil
,
err
}
}
return
builders
.
BuildProofList
(
request
.
GetContext
(),
request
.
GetNonce
(),
issig
),
nil
}
...
...
irmaclient/manual_session_test.go
View file @
183c7256
...
...
@@ -82,7 +82,7 @@ func TestManualSession(t *testing.T) {
result
:=
<-
ms
.
resultChannel
if
ps
:=
result
.
ProofStatus
;
ps
!=
irma
.
VALID
{
t
.
Logf
(
"Invalid proof result: %v Expected: %v"
,
ps
,
irma
.
VALID
)
t
.
Fa
i
l
()
t
.
Fa
ta
l
()
}
if
attrStatus
:=
result
.
ToAttributeResultList
()[
0
]
.
AttributeProofStatus
;
attrStatus
!=
irma
.
PRESENT
{
t
.
Logf
(
"Invalid attribute result value: %v Expected: %v"
,
attrStatus
,
irma
.
PRESENT
)
...
...
@@ -293,7 +293,6 @@ func (sh *ManualSessionHandler) Success(irmaAction irma.Action, result string) {
// Make proof corrupt if we want to test invalid proofs
resultBytes
:=
corruptAndConvertProofString
(
result
)
irmaSignedMessage
:=
&
irma
.
IrmaSignedMessage
{}
json
.
Unmarshal
(
resultBytes
,
irmaSignedMessage
)
if
err
:=
json
.
Unmarshal
(
resultBytes
,
irmaSignedMessage
);
err
!=
nil
{
sh
.
errorChannel
<-
&
irma
.
SessionError
{
...
...
requests.go
View file @
183c7256
...
...
@@ -7,6 +7,8 @@ import (
"time"
"encoding/json"
"github.com/bwesterb/go-atum"
"github.com/go-errors/errors"
"github.com/mhe/gabi"
)
...
...
@@ -56,7 +58,8 @@ type DisclosureRequest struct {
// A SignatureRequest is a a request to sign a message with certain attributes.
type
SignatureRequest
struct
{
DisclosureRequest
Message
string
`json:"message"`
Message
string
`json:"message"`
Timestamp
*
atum
.
Timestamp
`json:"-"`
}
// An IssuanceRequest is a request to issue certain credentials,
...
...
@@ -289,7 +292,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
{
return
ASN1ConvertSignatureNonce
(
sr
.
Message
,
sr
.
Nonce
)
return
ASN1ConvertSignatureNonce
(
sr
.
Message
,
sr
.
Nonce
,
sr
.
Timestamp
)
}
// Convert fields in JSON string to BigInterger if they are string
...
...
@@ -346,6 +349,7 @@ func (sr *SignatureRequest) SignatureFromMessage(message interface{}) (*IrmaSign
Nonce
:
sr
.
Nonce
,
Context
:
sr
.
Context
,
Message
:
sr
.
Message
,
Timestamp
:
sr
.
Timestamp
,
},
nil
}
...
...
timestamp.go
0 → 100644
View file @
183c7256
package
irma
import
(
"crypto/sha256"
"encoding/asn1"
"errors"
"math/big"
"github.com/bwesterb/go-atum"
"github.com/mhe/gabi"
)
// GetTimestamp GETs a signed timestamp (a signature over the current time and the parameters)
// over the message to be signed, the randomized signatures over the attributes, and the disclosed
// attributes, for in attribute-based signature sessions.
func
GetTimestamp
(
message
string
,
sigs
[]
*
big
.
Int
,
disclosed
[][]
*
big
.
Int
)
(
*
atum
.
Timestamp
,
error
)
{
nonce
,
err
:=
TimestampRequest
(
message
,
sigs
,
disclosed
)
if
err
!=
nil
{
return
nil
,
err
}
alg
:=
atum
.
Ed25519
return
atum
.
SendRequest
(
TimestampServerURL
,
atum
.
Request
{
Nonce
:
nonce
,
PreferredSigAlg
:
&
alg
,
})
}
// TimestampRequest computes the nonce to be signed by a timestamp server, given a message to be signed
// in an attribute-based signature session along with the randomized signatures over the attributes
// and the disclosed attributes.
func
TimestampRequest
(
message
string
,
sigs
[]
*
big
.
Int
,
disclosed
[][]
*
big
.
Int
)
([]
byte
,
error
)
{
msgHash
:=
sha256
.
Sum256
([]
byte
(
message
))
bts
,
err
:=
asn1
.
Marshal
(
struct
{
Sigs
[]
*
big
.
Int
MsgHash
[]
byte
Disclosed
[][]
*
big
.
Int
}{
sigs
,
msgHash
[
:
],
disclosed
,
})
if
err
!=
nil
{
return
nil
,
err
}
hashed
:=
sha256
.
Sum256
(
bts
)
return
hashed
[
:
],
nil
}
const
TimestampServerURL
=
"https://metrics.privacybydesign.foundation/atum"
// Given an IrmaSignedMessage, verify the timestamp over the signed message, disclosed attributes,
// and rerandomized CL-signatures.
func
VerifyTimestamp
(
irmaSignature
*
IrmaSignedMessage
,
message
string
,
conf
*
Configuration
)
error
{
if
irmaSignature
.
Timestamp
.
ServerUrl
!=
TimestampServerURL
{
return
errors
.
New
(
"Untrusted timestamp server"
)
}
// Extract the disclosed attributes and randomized CL-signatures from the proofs in order to
// construct the nonce that should be signed by the timestamp server.
zero
:=
big
.
NewInt
(
0
)
size
:=
len
(
*
irmaSignature
.
Signature
)
sigs
:=
make
([]
*
big
.
Int
,
size
)
disclosed
:=
make
([][]
*
big
.
Int
,
size
)
for
i
,
proof
:=
range
*
irmaSignature
.
Signature
{
proofd
:=
proof
.
(
*
gabi
.
ProofD
)
meta
:=
MetadataFromInt
(
proofd
.
ADisclosed
[
1
],
conf
)
sigs
[
i
]
=
proofd
.
A
// TODO check for nil
attrcount
:=
len
(
meta
.
CredentialType
()
.
Attributes
)
+
2
// plus secret key and metadata
disclosed
[
i
]
=
make
([]
*
big
.
Int
,
attrcount
)
for
j
:=
0
;
j
<
attrcount
;
j
++
{
val
,
ok
:=
proofd
.
ADisclosed
[
j
]
if
!
ok
{
disclosed
[
i
][
j
]
=
zero
}
else
{
disclosed
[
i
][
j
]
=
val
}
}
}
bts
,
err
:=
TimestampRequest
(
message
,
sigs
,
disclosed
)
if
err
!=
nil
{
return
err
}
valid
,
err
:=
irmaSignature
.
Timestamp
.
Verify
(
bts
)
if
err
!=
nil
{
return
err
}
if
!
valid
{
return
errors
.
New
(
"Timestamp signature invalid"
)
}
return
nil
}
verify.go
View file @
183c7256
...
...
@@ -14,10 +14,14 @@ type ProofStatus string
const
(
VALID
=
ProofStatus
(
"VALID"
)
EXPIRED
=
ProofStatus
(
"EXPIRED"
)
INVALID_CRYPTO
=
ProofStatus
(
"INVALID_CRYPTO"
)
INVALID_TIMESTAMP
=
ProofStatus
(
"INVALID_TIMESTAMP"
)
UNMATCHED_REQUEST
=
ProofStatus
(
"UNMATCHED_REQUEST"
)
MISSING_ATTRIBUTES
=
ProofStatus
(
"MISSING_ATTRIBUTES"
)
// The contained attributes are currently expired, but it is not certain if they already were expired
// during creation of the ABS.
EXPIRED
=
ProofStatus
(
"EXPIRED"
)
)
// ProofResult is a result of a complete proof, containing all the disclosed attributes and corresponding request
...
...
@@ -103,10 +107,10 @@ func (disclosed DisclosedCredentialList) createAndCheckSignatureProofResult(conf
return
&
signatureProofResult
}
// Returns true if one of the disclosed credentials is expired
func
(
disclosed
DisclosedCredentialList
)
IsExpired
()
bool
{
// Returns true if one of the disclosed credentials is expired
at the specified time
func
(
disclosed
DisclosedCredentialList
)
IsExpired
(
t
time
.
Time
)
bool
{
for
_
,
cred
:=
range
disclosed
{
if
cred
.
IsExpired
()
{
if
cred
.
IsExpired
(
t
)
{
return
true
}
}
...
...
@@ -141,8 +145,8 @@ func (proofResult *ProofResult) ContainsAttribute(attrId AttributeTypeIdentifier
return
false
}
func
(
cred
*
DisclosedCredential
)
IsExpired
()
bool
{
return
cred
.
metadataAttribute
.
Expiry
()
.
Before
(
t
ime
.
Now
()
)
func
(
cred
*
DisclosedCredential
)
IsExpired
(
t
time
.
Time
)
bool
{
return
cred
.
metadataAttribute
.
Expiry
()
.
Before
(
t
)
}
func
NewDisclosedCredentialFromADisclosed
(
aDisclosed
map
[
int
]
*
big
.
Int
,
configuration
*
Configuration
)
*
DisclosedCredential
{
...
...
@@ -228,8 +232,8 @@ func addExtraAttributes(disclosed DisclosedCredentialList, proofResult *ProofRes
}
// Check an gabi prooflist against a signature proofrequest
func
checkProofWithRequest
(
configuration
*
Configuration
,
proofList
gabi
.
ProofList
,
sigRequest
*
SignatureRequest
)
*
SignatureProofResult
{
disclosed
,
err
:=
extractDisclosedCredentials
(
configuration
,
proofList
)
func
checkProofWithRequest
(
configuration
*
Configuration
,
irmaSignature
*
IrmaSignedMessage
,
sigRequest
*
SignatureRequest
)
*
SignatureProofResult
{
disclosed
,
err
:=
extractDisclosedCredentials
(
configuration
,
*
irmaSignature
.
Signature
)
if
err
!=
nil
{
fmt
.
Println
(
err
)
...
...
@@ -249,9 +253,21 @@ func checkProofWithRequest(configuration *Configuration, proofList gabi.ProofLis
}
// If all disjunctions are satisfied, check if a credential is expired
if
disclosed
.
IsExpired
()
{
signatureProofResult
.
ProofStatus
=
EXPIRED
return
signatureProofResult
if
irmaSignature
.
Timestamp
==
nil
{
if
disclosed
.
IsExpired
(
time
.
Now
())
{
// At least one of the contained attributes has currently expired. We don't know the
// creation time of the ABS so we can't ascertain that the attributes were still valid then.
// Otherwise the signature is valid.
signatureProofResult
.
ProofStatus
=
EXPIRED
return
signatureProofResult
}
}
else
{
if
disclosed
.
IsExpired
(
time
.
Unix
(
irmaSignature
.
Timestamp
.
Time
,
0
))
{
// The ABS contains attributes that were expired at the time of creation of the ABS.
// This must not happen and in this case the signature is invalid
signatureProofResult
.
ProofStatus
=
INVALID_CRYPTO
return
signatureProofResult
}
}
// All disjunctions satisfied and nothing expired, proof is valid!
...
...
@@ -272,7 +288,19 @@ 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
,
irmaSignature
*
IrmaSignedMessage
,
sigRequest
*
SignatureRequest
)
*
SignatureProofResult
{
// First, check if nonce and context of the signature match those of the signature request
// First, verify the timestamp, if any
if
irmaSignature
.
Timestamp
!=
nil
{
if
err
:=
VerifyTimestamp
(
irmaSignature
,
sigRequest
.
Message
,
configuration
);
err
!=
nil
{
return
&
SignatureProofResult
{
ProofResult
:
&
ProofResult
{
ProofStatus
:
INVALID_TIMESTAMP
,
},
}
}
sigRequest
.
Timestamp
=
irmaSignature
.
Timestamp
}
// Then, check if nonce and context of the signature match those of the signature request
if
!
irmaSignature
.
MatchesNonceAndContext
(
sigRequest
)
{
return
&
SignatureProofResult
{
ProofResult
:
&
ProofResult
{
...
...
@@ -291,12 +319,19 @@ func VerifySig(configuration *Configuration, irmaSignature *IrmaSignedMessage, s
}
// Finally, check whether attribute values in proof satisfy the original signature request
return
checkProofWithRequest
(
configuration
,
*
irmaSignature
.
Signature
,
sigRequest
)
return
checkProofWithRequest
(
configuration
,
irmaSignature
,
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
// First, verify the timestamp, if any
if
irmaSignature
.
Timestamp
!=
nil
{
if
err
:=
VerifyTimestamp
(
irmaSignature
,
irmaSignature
.
Message
,
configuration
);
err
!=
nil
{
return
INVALID_TIMESTAMP
,
nil
}
}
// Cryptographically verify the signature
if
!
verify
(
configuration
,
*
irmaSignature
.
Signature
,
irmaSignature
.
Context
,
irmaSignature
.
GetNonce
(),
true
)
{
return
INVALID_CRYPTO
,
nil
}
...
...
@@ -309,8 +344,14 @@ func VerifySigWithoutRequest(configuration *Configuration, irmaSignature *IrmaSi
return
INVALID_CRYPTO
,
nil
}
if
disclosed
.
IsExpired
()
{
return
EXPIRED
,
disclosed
if
irmaSignature
.
Timestamp
==
nil
{
if
disclosed
.
IsExpired
(
time
.
Now
())
{
return
EXPIRED
,
disclosed
}
}
else
{
if
disclosed
.
IsExpired
(
time
.
Unix
(
irmaSignature
.
Timestamp
.
Time
,
0
))
{
return
INVALID_CRYPTO
,
nil
}
}
return
VALID
,
disclosed
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment