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
0bdde251
Commit
0bdde251
authored
Sep 21, 2017
by
Sietse Ringers
Browse files
Adding keyshare protocol
parent
73acf247
Changes
10
Hide whitespace changes
Inline
Side-by-side
descriptions.go
View file @
0bdde251
...
...
@@ -127,6 +127,10 @@ func (sm *SchemeManager) Identifier() SchemeManagerIdentifier {
return
NewSchemeManagerIdentifier
(
sm
.
ID
)
}
func
(
sm
*
SchemeManager
)
Distributed
()
bool
{
return
len
(
sm
.
KeyshareServer
)
>
0
}
// CurrentPublicKey returns the latest known public key of the issuer identified by this instance.
func
(
id
*
Issuer
)
CurrentPublicKey
()
*
gabi
.
PublicKey
{
keys
:=
MetaStore
.
PublicKeys
[
id
.
Identifier
()]
...
...
keyshare.go
View file @
0bdde251
...
...
@@ -4,13 +4,36 @@ import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"strconv"
"math/big"
"github.com/mhe/gabi"
)
type
KeyshareSessionHandler
interface
{
AskPin
(
remainingAttempts
int
,
callback
func
(
pin
string
))
KeyshareDone
(
message
interface
{})
KeyshareBlocked
(
duration
int
)
KeyshareError
(
err
error
)
}
type
keyshareSession
struct
{
session
Session
builders
[]
gabi
.
ProofBuilder
transports
map
[
SchemeManagerIdentifier
]
*
HTTPTransport
sessionHandler
KeyshareSessionHandler
keyshareServer
*
keyshareServer
}
type
keyshareServer
struct
{
URL
string
`json:"url"`
Username
string
`json:"username"`
Nonce
[]
byte
`json:"nonce"`
PrivateKey
*
paillierPrivateKey
`json:"keyPair"`
token
string
}
type
keyshareRegistration
struct
{
...
...
@@ -19,10 +42,56 @@ type keyshareRegistration struct {
PublicKey
*
paillierPublicKey
`json:"publicKey"`
}
type
keyshareAuthorization
struct
{
Status
string
`json:"status"`
Candidates
[]
string
`json:"candidates"`
}
type
keysharePinMessage
struct
{
Username
string
`json:"id"`
Pin
string
`json:"pin"`
}
type
keysharePinStatus
struct
{
Status
string
`json:"status"`
Message
string
`json:"message"`
}
type
publicKeyIdentifier
struct
{
Issuer
string
`json:"issuer"`
Counter
uint
`json:"counter"`
}
// TODO update protocol so this can go away
func
(
pki
*
publicKeyIdentifier
)
MarshalJSON
()
([]
byte
,
error
)
{
temp
:=
struct
{
Issuer
map
[
string
]
string
`json:"issuer"`
Counter
uint
`json:"counter"`
}{
Issuer
:
map
[
string
]
string
{
"identifier"
:
pki
.
Issuer
},
Counter
:
pki
.
Counter
,
}
return
json
.
Marshal
(
temp
)
}
type
proofPCommitmentMap
struct
{
Commitments
map
[
publicKeyIdentifier
]
*
gabi
.
ProofPCommitment
`json:"c"`
}
type
KeyshareHandler
interface
{
StartKeyshareRegistration
(
manager
*
SchemeManager
,
registrationCallback
func
(
email
,
pin
string
))
}
const
(
kssUsernameHeader
=
"IRMA_Username"
kssAuthHeader
=
"IRMA_Authorization"
kssAuthorized
=
"authorized"
kssTokenExpired
=
"expired"
kssPinSuccess
=
"success"
kssPinFailure
=
"failure"
kssPinError
=
"error"
)
func
newKeyshareServer
(
privatekey
*
paillierPrivateKey
,
url
,
email
string
)
(
ks
*
keyshareServer
,
err
error
)
{
ks
=
&
keyshareServer
{
Nonce
:
make
([]
byte
,
32
),
...
...
@@ -38,3 +107,318 @@ func (ks *keyshareServer) HashedPin(pin string) string {
hash
:=
sha256
.
Sum256
(
append
(
ks
.
Nonce
,
[]
byte
(
pin
)
...
))
return
base64
.
RawStdEncoding
.
EncodeToString
(
hash
[
:
])
}
func
StartKeyshareSession
(
session
Session
,
builders
[]
gabi
.
ProofBuilder
,
sessionHandler
KeyshareSessionHandler
,
)
{
ksscount
:=
0
for
_
,
managerId
:=
range
session
.
SchemeManagers
()
{
if
MetaStore
.
SchemeManagers
[
managerId
]
.
Distributed
()
{
ksscount
++
if
_
,
registered
:=
Manager
.
keyshareServers
[
managerId
];
!
registered
{
err
:=
errors
.
New
(
"Not registered to keyshare server of scheme manager "
+
managerId
.
String
())
sessionHandler
.
KeyshareError
(
err
)
return
}
}
}
if
_
,
issuing
:=
session
.
(
*
IssuanceRequest
);
issuing
&&
ksscount
>
1
{
err
:=
errors
.
New
(
"Issuance session involving more than one keyshare servers are not supported"
)
sessionHandler
.
KeyshareError
(
err
)
return
}
ks
:=
&
keyshareSession
{
session
:
session
,
builders
:
builders
,
sessionHandler
:
sessionHandler
,
transports
:
map
[
SchemeManagerIdentifier
]
*
HTTPTransport
{},
}
askPin
:=
false
for
_
,
managerId
:=
range
session
.
SchemeManagers
()
{
if
!
MetaStore
.
SchemeManagers
[
managerId
]
.
Distributed
()
{
continue
}
ks
.
keyshareServer
=
Manager
.
keyshareServers
[
managerId
]
transport
:=
NewHTTPTransport
(
ks
.
keyshareServer
.
URL
)
transport
.
SetHeader
(
kssUsernameHeader
,
ks
.
keyshareServer
.
Username
)
transport
.
SetHeader
(
kssAuthHeader
,
ks
.
keyshareServer
.
token
)
ks
.
transports
[
managerId
]
=
transport
authstatus
:=
&
keyshareAuthorization
{}
err
:=
transport
.
Post
(
"users/isAuthorized"
,
authstatus
,
""
)
if
err
!=
nil
{
ks
.
sessionHandler
.
KeyshareError
(
err
)
return
}
switch
authstatus
.
Status
{
case
kssAuthorized
:
// nop
case
kssTokenExpired
:
askPin
=
true
default
:
ks
.
sessionHandler
.
KeyshareError
(
errors
.
New
(
"Keyshare server returned unrecognized authirization status"
))
return
}
}
if
askPin
{
ks
.
VerifyPin
(
-
1
)
}
}
// Ask for a pin, repeatedly if necessary, and either continue the keyshare protocol
// with authorization, or stop the keyshare protocol and inform of failure.
func
(
ks
*
keyshareSession
)
VerifyPin
(
attempts
int
)
{
ks
.
sessionHandler
.
AskPin
(
attempts
,
func
(
pin
string
)
{
success
,
attemptsRemaining
,
blocked
,
err
:=
ks
.
verifyPinAttempt
(
pin
)
if
err
!=
nil
{
ks
.
sessionHandler
.
KeyshareError
(
err
)
return
}
if
blocked
!=
0
{
ks
.
sessionHandler
.
KeyshareBlocked
(
blocked
)
return
}
if
success
{
ks
.
GetCommitments
()
return
}
// Not successful but no error and not yet blocked: try again
ks
.
VerifyPin
(
attemptsRemaining
)
})
}
// Verify the specified pin at each of the keyshare servers involved in the specified session.
//
// - If the pin did not verify at one of the keyshare servers but there are attempts remaining,
// the amount of remaining attempts is returned as the second return value.
//
// - If the pin did not verify at one of the keyshare servers and there are no attempts remaining,
// the amount of time for which we are blocked at the keyshare server is returned as the third
// parameter.
//
// - If this or anything else (specified in err) goes wrong, success will be false.
// If all is ok, success will be true.
func
(
ks
*
keyshareSession
)
verifyPinAttempt
(
pin
string
)
(
success
bool
,
tries
int
,
blocked
int
,
err
error
)
{
for
_
,
managerId
:=
range
ks
.
session
.
SchemeManagers
()
{
if
!
MetaStore
.
SchemeManagers
[
managerId
]
.
Distributed
()
{
continue
}
kss
:=
Manager
.
keyshareServers
[
managerId
]
transport
:=
ks
.
transports
[
managerId
]
pinmsg
:=
keysharePinMessage
{
Username
:
kss
.
Username
,
Pin
:
kss
.
HashedPin
(
pin
)}
pinresult
:=
&
keysharePinStatus
{}
err
=
transport
.
Post
(
"users/verify/pin"
,
pinresult
,
pinmsg
)
if
err
!=
nil
{
return
}
switch
pinresult
.
Status
{
case
kssPinSuccess
:
kss
.
token
=
pinresult
.
Message
transport
.
SetHeader
(
kssAuthHeader
,
kss
.
token
)
case
kssPinFailure
:
tries
,
err
=
strconv
.
Atoi
(
pinresult
.
Message
)
if
err
!=
nil
{
return
}
return
case
kssPinError
:
blocked
,
err
=
strconv
.
Atoi
(
pinresult
.
Message
)
if
err
!=
nil
{
return
}
return
default
:
err
=
errors
.
New
(
"Keyshare server returned unrecognized PIN status"
)
return
}
}
success
=
true
return
}
// GetCommitments gets the commitments (first message in Schnorr zero-knowledge protocol)
// of all keyshare servers of their part of the private key, and merges these commitments
// in our own proof builders.
func
(
ks
*
keyshareSession
)
GetCommitments
()
{
pkids
:=
map
[
SchemeManagerIdentifier
][]
*
publicKeyIdentifier
{}
commitments
:=
map
[
publicKeyIdentifier
]
*
gabi
.
ProofPCommitment
{}
// For each scheme manager, build a list of public keys under this manager
// that we will use in the keyshare protocol with the keyshare server of this manager
for
_
,
builder
:=
range
ks
.
builders
{
pk
:=
builder
.
PublicKey
()
managerId
:=
NewIssuerIdentifier
(
pk
.
Issuer
)
.
SchemeManagerIdentifier
()
if
!
MetaStore
.
SchemeManagers
[
managerId
]
.
Distributed
()
{
continue
}
if
_
,
contains
:=
pkids
[
managerId
];
!
contains
{
pkids
[
managerId
]
=
[]
*
publicKeyIdentifier
{}
}
pkids
[
managerId
]
=
append
(
pkids
[
managerId
],
&
publicKeyIdentifier
{
Issuer
:
pk
.
Issuer
,
Counter
:
pk
.
Counter
})
}
// Now inform each keyshare server of with respect to which public keys
// we want them to send us commitments
for
_
,
managerId
:=
range
ks
.
session
.
SchemeManagers
()
{
if
!
MetaStore
.
SchemeManagers
[
managerId
]
.
Distributed
()
{
continue
}
transport
:=
ks
.
transports
[
managerId
]
comms
:=
&
proofPCommitmentMap
{}
err
:=
transport
.
Post
(
"prove/getCommitments"
,
comms
,
pkids
[
managerId
])
if
err
!=
nil
{
ks
.
sessionHandler
.
KeyshareError
(
err
)
return
}
for
pki
,
c
:=
range
comms
.
Commitments
{
commitments
[
pki
]
=
c
}
}
// Merge in the commitments
for
_
,
builder
:=
range
ks
.
builders
{
pk
:=
builder
.
PublicKey
()
pki
:=
publicKeyIdentifier
{
Issuer
:
pk
.
Issuer
,
Counter
:
pk
.
Counter
}
comm
,
distributed
:=
commitments
[
pki
]
if
!
distributed
{
continue
}
builder
.
MergeProofPCommitment
(
comm
)
}
ks
.
GetProofPs
()
}
// GetProofPs uses the combined commitments of all keyshare servers and ourself
// to calculate the challenge, which is sent to the keyshare servers in order to
// receive their responses (2nd and 3rd message in Schnorr zero-knowledge protocol).
func
(
ks
*
keyshareSession
)
GetProofPs
()
{
_
,
issig
:=
ks
.
session
.
(
*
SignatureRequest
)
_
,
issuing
:=
ks
.
session
.
(
*
IssuanceRequest
)
challenge
:=
gabi
.
DistributedChallenge
(
ks
.
session
.
GetContext
(),
ks
.
session
.
GetNonce
(),
ks
.
builders
,
issig
)
kssChallenge
:=
challenge
// In disclosure or signature sessions the challenge is Paillier encrypted.
if
!
issuing
{
bytes
,
err
:=
ks
.
keyshareServer
.
PrivateKey
.
Encrypt
(
challenge
.
Bytes
())
if
err
!=
nil
{
ks
.
sessionHandler
.
KeyshareError
(
err
)
}
kssChallenge
=
new
(
big
.
Int
)
.
SetBytes
(
bytes
)
}
// Post the challenge, obtaining JWT's containing the ProofP's
responses
:=
map
[
SchemeManagerIdentifier
]
string
{}
for
_
,
managerId
:=
range
ks
.
session
.
SchemeManagers
()
{
transport
,
distributed
:=
ks
.
transports
[
managerId
]
if
!
distributed
{
continue
}
var
jwt
string
err
:=
transport
.
Post
(
"prove/getResponse"
,
&
jwt
,
kssChallenge
)
if
err
!=
nil
{
ks
.
sessionHandler
.
KeyshareError
(
err
)
return
}
responses
[
managerId
]
=
jwt
}
ks
.
Finish
(
challenge
,
responses
)
}
// Finish the keyshare protocol: in case of issuance, put the keyshare jwt in the
// IssueCommitmentMessage; in case of disclosure and signing, parse each keyshare jwt,
// merge in the received ProofP's, and finish.
func
(
ks
*
keyshareSession
)
Finish
(
challenge
*
big
.
Int
,
responses
map
[
SchemeManagerIdentifier
]
string
)
{
switch
ks
.
session
.
(
type
)
{
case
*
DisclosureRequest
:
case
*
SignatureRequest
:
proofPs
:=
make
([]
*
gabi
.
ProofP
,
len
(
ks
.
builders
))
for
i
,
builder
:=
range
ks
.
builders
{
// Parse each received JWT
managerId
:=
NewIssuerIdentifier
(
builder
.
PublicKey
()
.
Issuer
)
.
SchemeManagerIdentifier
()
if
!
MetaStore
.
SchemeManagers
[
managerId
]
.
Distributed
()
{
continue
}
msg
:=
struct
{
ProofP
*
gabi
.
ProofP
}{}
_
,
err
:=
JwtDecode
(
responses
[
managerId
],
msg
)
if
err
!=
nil
{
ks
.
sessionHandler
.
KeyshareError
(
err
)
return
}
// Decrypt the responses and populate a slice of ProofP's
proofPs
[
i
]
=
msg
.
ProofP
bytes
,
err
:=
ks
.
keyshareServer
.
PrivateKey
.
Decrypt
(
proofPs
[
i
]
.
C
.
Bytes
())
if
err
!=
nil
{
ks
.
sessionHandler
.
KeyshareError
(
err
)
return
}
proofPs
[
i
]
.
C
=
new
(
big
.
Int
)
.
SetBytes
(
bytes
)
}
// Create merged proofs and finish protocol
list
,
err
:=
gabi
.
BuildDistributedProofList
(
challenge
,
ks
.
builders
,
proofPs
)
if
err
!=
nil
{
ks
.
sessionHandler
.
KeyshareError
(
err
)
return
}
ks
.
sessionHandler
.
KeyshareDone
(
list
)
case
*
IssuanceRequest
:
// Calculate IssueCommitmentMessage, without merging in any of the received ProofP's:
// instead, include the keyshare server's JWT in the IssueCommitmentMessage for the
// issuance server to verify
message
,
err
:=
Manager
.
IssueCommitments
(
ks
.
session
.
(
*
IssuanceRequest
))
if
err
!=
nil
{
ks
.
sessionHandler
.
KeyshareError
(
err
)
return
}
for
_
,
response
:=
range
responses
{
message
.
ProofPjwt
=
response
break
}
ks
.
sessionHandler
.
KeyshareDone
(
message
)
}
}
// TODO this message is ugly, should update protocol
func
(
comms
*
proofPCommitmentMap
)
UnmarshalJSON
(
bytes
[]
byte
)
error
{
comms
.
Commitments
=
map
[
publicKeyIdentifier
]
*
gabi
.
ProofPCommitment
{}
temp
:=
struct
{
C
[][]
*
json
.
RawMessage
`json:"c"`
}{}
if
err
:=
json
.
Unmarshal
(
bytes
,
&
temp
);
err
!=
nil
{
return
err
}
for
_
,
raw
:=
range
temp
.
C
{
tempPkId
:=
struct
{
Issuer
struct
{
Identifier
string
`json:"identifier"`
}
`json:"issuer"`
Counter
uint
`json:"counter"`
}{}
comm
:=
gabi
.
ProofPCommitment
{}
if
err
:=
json
.
Unmarshal
([]
byte
(
*
raw
[
0
]),
&
tempPkId
);
err
!=
nil
{
return
err
}
if
err
:=
json
.
Unmarshal
([]
byte
(
*
raw
[
1
]),
&
comm
);
err
!=
nil
{
return
err
}
pkid
:=
publicKeyIdentifier
{
Issuer
:
tempPkId
.
Issuer
.
Identifier
,
Counter
:
tempPkId
.
Counter
}
comms
.
Commitments
[
pkid
]
=
&
comm
}
return
nil
}
manager.go
View file @
0bdde251
...
...
@@ -231,9 +231,13 @@ type Session interface {
GetContext
()
*
big
.
Int
SetContext
(
*
big
.
Int
)
DisjunctionList
()
AttributeDisjunctionList
DisclosureChoice
()
*
DisclosureChoice
SetDisclosureChoice
(
choice
*
DisclosureChoice
)
Distributed
()
bool
SchemeManagers
()
[]
SchemeManagerIdentifier
}
func
(
cm
*
CredentialManager
)
p
roof
s
Builders
(
choice
*
DisclosureChoice
)
([]
gabi
.
ProofBuilder
,
error
)
{
func
(
cm
*
CredentialManager
)
P
roofBuilders
(
choice
*
DisclosureChoice
)
([]
gabi
.
ProofBuilder
,
error
)
{
todisclose
,
err
:=
cm
.
groupCredentials
(
choice
)
if
err
!=
nil
{
return
nil
,
err
...
...
@@ -252,16 +256,14 @@ func (cm *CredentialManager) proofsBuilders(choice *DisclosureChoice) ([]gabi.Pr
// Proofs computes disclosure proofs containing the attributes specified by choice.
func
(
cm
*
CredentialManager
)
Proofs
(
choice
*
DisclosureChoice
,
request
Session
,
issig
bool
)
(
gabi
.
ProofList
,
error
)
{
builders
,
err
:=
cm
.
p
roof
s
Builders
(
choice
)
builders
,
err
:=
cm
.
P
roofBuilders
(
choice
)
if
err
!=
nil
{
return
nil
,
err
}
return
gabi
.
BuildProofList
(
request
.
GetContext
(),
request
.
GetNonce
(),
builders
,
issig
),
nil
}
// IssueCommitments computes issuance commitments, along with disclosure proofs
// specified by choice.
func
(
cm
*
CredentialManager
)
IssueCommitments
(
choice
*
DisclosureChoice
,
request
*
IssuanceRequest
)
(
*
gabi
.
IssueCommitmentMessage
,
error
)
{
func
(
cm
*
CredentialManager
)
IssuanceProofBuilders
(
request
*
IssuanceRequest
)
([]
gabi
.
ProofBuilder
,
error
)
{
state
,
err
:=
newIssuanceState
()
if
err
!=
nil
{
return
nil
,
err
...
...
@@ -276,14 +278,23 @@ func (cm *CredentialManager) IssueCommitments(choice *DisclosureChoice, request
proofBuilders
=
append
(
proofBuilders
,
credBuilder
)
}
disclosures
,
err
:=
cm
.
p
roof
s
Builders
(
choice
)
disclosures
,
err
:=
cm
.
P
roofBuilders
(
request
.
choice
)
if
err
!=
nil
{
return
nil
,
err
}
proofBuilders
=
append
(
disclosures
,
proofBuilders
...
)
return
proofBuilders
,
nil
}
// IssueCommitments computes issuance commitments, along with disclosure proofs
// specified by choice.
func
(
cm
*
CredentialManager
)
IssueCommitments
(
request
*
IssuanceRequest
)
(
*
gabi
.
IssueCommitmentMessage
,
error
)
{
proofBuilders
,
err
:=
cm
.
IssuanceProofBuilders
(
request
)
if
err
!=
nil
{
return
nil
,
err
}
list
:=
gabi
.
BuildProofList
(
request
.
GetContext
(),
request
.
GetNonce
(),
proofBuilders
,
false
)
return
&
gabi
.
IssueCommitmentMessage
{
Proofs
:
list
,
Nonce2
:
state
.
nonce2
},
nil
return
&
gabi
.
IssueCommitmentMessage
{
Proofs
:
list
,
Nonce2
:
request
.
state
.
nonce2
},
nil
}
// ConstructCredentials constructs and saves new credentials
...
...
@@ -316,13 +327,21 @@ func (cm *CredentialManager) ConstructCredentials(msg []*gabi.IssueSignatureMess
}
// PaillierKey returns a new Paillier key (and generates a new one in a goroutine).
func
(
cm
*
CredentialManager
)
paillierKey
()
*
paillierPrivateKey
{
func
(
cm
*
CredentialManager
)
paillierKey
(
wait
bool
)
*
paillierPrivateKey
{
retval
:=
cm
.
paillierKeyCache
ch
:=
make
(
chan
bool
)
go
func
()
{
newkey
,
_
:=
paillier
.
GenerateKey
(
rand
.
Reader
,
2048
)
converted
:=
paillierPrivateKey
(
*
newkey
)
cm
.
paillierKeyCache
=
&
converted
if
wait
&&
retval
==
nil
{
ch
<-
true
}
}()
if
wait
&&
retval
==
nil
{
<-
ch
return
cm
.
paillierKeyCache
}
return
retval
}
...
...
@@ -349,7 +368,7 @@ func (cm *CredentialManager) KeyshareEnroll(managerId SchemeManagerIdentifier, e
}
transport
:=
NewHTTPTransport
(
manager
.
KeyshareServer
)
kss
,
err
:=
newKeyshareServer
(
Manager
.
paillierKey
(),
manager
.
URL
,
email
)
kss
,
err
:=
newKeyshareServer
(
Manager
.
paillierKey
(
true
),
manager
.
KeyshareServer
,
email
)
if
err
!=
nil
{
return
err
}
...
...
messages.go
0 → 100644
View file @
0bdde251
package
irmago
import
(
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"strings"
)
// Action encodes the session type of an IRMA session (e.g., disclosing).
type
Action
string
// ErrorCode are session errors.
type
ErrorCode
string
// Error is a protocol error.
type
Error
struct
{
Err
error
ErrorCode
*
ApiError
Info
string
Status
int
}
// ApiError is an error message returned by the API server on errors.
type
ApiError
struct
{
Status
int
`json:"status"`
ErrorName
string
`json:"error"`
Description
string
`json:"description"`
Message
string
`json:"message"`
Stacktrace
string
`json:"stacktrace"`
}
// Actions
const
(
ActionDisclosing
=
Action
(
"disclosing"
)
ActionSigning
=
Action
(
"signing"
)
ActionIssuing
=
Action
(
"issuing"
)
ActionUnknown
=
Action
(
"unknown"
)
)
// Protocol errors
const
(
// Protocol version not supported
ErrorProtocolVersionNotSupported
=
ErrorCode
(
"versionNotSupported"
)
// Error in HTTP communication
ErrorTransport
=
ErrorCode
(
"httpError"
)
// Invalid client JWT in first IRMA message
ErrorInvalidJWT
=
ErrorCode
(
"invalidJwt"
)
// Unkown session type (not disclosing, signing, or issuing)
ErrorUnknownAction
=
ErrorCode
(
"unknownAction"
)
// Crypto error during calculation of our response (second IRMA message)
ErrorCrypto
=
ErrorCode
(
"cryptoResponseError"
)
// Server rejected our response (second IRMA message)
ErrorRejected
=
ErrorCode
(
"rejectedByServer"
)
// (De)serializing of a message failed
ErrorSerialization
=
ErrorCode
(
"serializationError"
)
ErrorKeyshare
=
ErrorCode
(
"keyshare"
)
ErrorKeyshareBlocked
=
ErrorCode
(
"keyshareBlocked"
)
)
func
(
e
*
Error
)
Error
()
string
{
if
e
.
Err
!=
nil
{
return
fmt
.
Sprintf
(
"%s: %s"
,
string
(
e
.
ErrorCode
),
e
.
Err
.
Error
())
}
return
string
(
e
.
ErrorCode
)
}
func
JwtDecode
(
jwt
string
,
body
interface
{})
(
string
,
error
)
{
jwtparts
:=
strings
.
Split
(
jwt
,
"."
)
if
jwtparts
==
nil
||
len
(
jwtparts
)
<
2
{
return
""
,
errors
.
New
(
"Not a JWT"
)
}
headerbytes
,
err
:=
base64
.
RawStdEncoding
.
DecodeString
(
jwtparts
[
0
])
if
err
!=
nil
{
return
""
,
err
}
var
header
struct
{
Issuer
string
`json:"iss"`
}
err
=
json
.
Unmarshal
([]
byte
(
headerbytes
),
&
header
)
if
err
!=
nil
{
return
""
,
err
}
bodybytes
,
err
:=
base64
.
RawStdEncoding
.
DecodeString
(
jwtparts
[
1
])
if
err
!=
nil
{
return
""
,
err
}
return
header
.
Issuer
,
json
.
Unmarshal
(
bodybytes
,
body
)
}
protocol/session.go
View file @
0bdde251
...
...
@@ -6,9 +6,6 @@ import (
"strconv"
"strings"
"encoding/base64"
"encoding/json"
"github.com/credentials/irmago"
"github.com/mhe/gabi"
)
...
...
@@ -28,6 +25,8 @@ type Handler interface {
AskIssuancePermission
(
request
irmago
.
IssuanceRequest
,
ServerName
string
,
callback
PermissionHandler
)
AskVerificationPermission
(
request
irmago
.
DisclosureRequest
,
ServerName
string
,
callback
PermissionHandler
)
AskSignaturePermission
(
request
irmago
.
SignatureRequest
,
ServerName
string
,
callback
PermissionHandler
)
AskPin
(
remainingAttempts
int
,
callback
func
(
pin
string
))
}
// A session is an IRMA session.
...
...
@@ -40,6 +39,7 @@ type session struct {
jwt
RequestorJwt
irmaSession
irmago
.
Session