Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
irmago
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
IRMA
Github mirrors
irmago
Commits
8e573885
Commit
8e573885
authored
Dec 16, 2019
by
Sietse Ringers
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: support issuer key rollover by each key having its own revocation update update
parent
23ecc5de
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
350 additions
and
242 deletions
+350
-242
internal/servercore/api.go
internal/servercore/api.go
+18
-18
internal/servercore/handle.go
internal/servercore/handle.go
+5
-5
internal/servercore/helpers.go
internal/servercore/helpers.go
+7
-2
internal/sessiontest/server_test.go
internal/sessiontest/server_test.go
+4
-5
irmaclient/credential.go
irmaclient/credential.go
+14
-5
irmaconfig.go
irmaconfig.go
+28
-29
requests.go
requests.go
+2
-2
revocation.go
revocation.go
+101
-48
revocation_db.go
revocation_db.go
+120
-102
server/conf.go
server/conf.go
+40
-18
server/requestorserver/conf.go
server/requestorserver/conf.go
+2
-2
verify.go
verify.go
+9
-6
No files found.
internal/servercore/api.go
View file @
8e573885
...
...
@@ -140,18 +140,7 @@ func (s *Server) CancelSession(token string) error {
}
func
(
s
*
Server
)
Revoke
(
credid
irma
.
CredentialTypeIdentifier
,
key
string
)
error
{
sk
,
err
:=
s
.
conf
.
IrmaConfiguration
.
PrivateKey
(
credid
.
IssuerIdentifier
())
if
err
!=
nil
{
return
err
}
if
sk
==
nil
{
return
errors
.
Errorf
(
"cannot revoke: private key of %s not found"
,
credid
.
IssuerIdentifier
())
}
rsk
,
err
:=
sk
.
RevocationKey
()
if
err
!=
nil
{
return
err
}
return
s
.
conf
.
IrmaConfiguration
.
Revocation
.
Revoke
(
credid
,
key
,
rsk
)
return
s
.
conf
.
IrmaConfiguration
.
Revocation
.
Revoke
(
credid
,
key
)
}
func
ParsePath
(
path
string
)
(
token
,
noun
string
,
arg
[]
string
,
err
error
)
{
...
...
@@ -382,20 +371,31 @@ func (s *Server) handleClientMessage(
func
(
s
*
Server
)
handleRevocationMessage
(
noun
,
method
string
,
args
[]
string
,
headers
map
[
string
][]
string
,
message
[]
byte
,
)
(
int
,
[]
byte
)
{
if
(
noun
==
"updatefrom"
||
noun
==
"updatelatest"
)
&&
method
==
http
.
MethodGet
{
if
(
noun
==
"updatefrom"
)
&&
method
==
http
.
MethodGet
{
if
len
(
args
)
!=
2
{
return
server
.
BinaryResponse
(
nil
,
server
.
RemoteError
(
server
.
ErrorInvalidRequest
,
"GET
"
+
noun
+
" expects 2
url arguments"
))
return
server
.
BinaryResponse
(
nil
,
server
.
RemoteError
(
server
.
ErrorInvalidRequest
,
"GET
updatefrom expects 3
url arguments"
))
}
i
,
err
:=
strconv
.
ParseUint
(
args
[
1
],
10
,
64
)
if
err
!=
nil
{
return
server
.
BinaryResponse
(
nil
,
server
.
RemoteError
(
server
.
ErrorMalformedInput
,
err
.
Error
()))
}
pkindex
,
err
:=
strconv
.
ParseUint
(
args
[
2
],
10
,
32
)
if
err
!=
nil
{
return
server
.
BinaryResponse
(
nil
,
server
.
RemoteError
(
server
.
ErrorMalformedInput
,
err
.
Error
()))
}
cred
:=
irma
.
NewCredentialTypeIdentifier
(
args
[
0
])
if
noun
==
"updatefrom"
{
return
server
.
BinaryResponse
(
s
.
handleGetUpdateFrom
(
cred
,
i
))
}
else
{
return
server
.
BinaryResponse
(
s
.
handleGetUpdateLatest
(
cred
,
i
))
return
server
.
BinaryResponse
(
s
.
handleGetUpdateFrom
(
cred
,
uint
(
pkindex
),
i
))
}
if
noun
==
"updatelatest"
&&
method
==
http
.
MethodGet
{
if
len
(
args
)
!=
2
{
return
server
.
BinaryResponse
(
nil
,
server
.
RemoteError
(
server
.
ErrorInvalidRequest
,
"GET updatelatest expects 2 url arguments"
))
}
i
,
err
:=
strconv
.
ParseUint
(
args
[
1
],
10
,
64
)
if
err
!=
nil
{
return
server
.
BinaryResponse
(
nil
,
server
.
RemoteError
(
server
.
ErrorMalformedInput
,
err
.
Error
()))
}
cred
:=
irma
.
NewCredentialTypeIdentifier
(
args
[
0
])
return
server
.
BinaryResponse
(
s
.
handleGetUpdateLatest
(
cred
,
i
))
}
if
noun
==
"update"
&&
method
==
http
.
MethodPost
{
if
len
(
args
)
!=
1
{
...
...
internal/servercore/handle.go
View file @
8e573885
...
...
@@ -189,7 +189,7 @@ func (session *session) handlePostCommitments(commitments *irma.IssueCommitmentM
for
i
,
cred
:=
range
request
.
Credentials
{
id
:=
cred
.
CredentialTypeID
.
IssuerIdentifier
()
pk
,
_
:=
session
.
conf
.
IrmaConfiguration
.
PublicKey
(
id
,
cred
.
KeyCounter
)
sk
,
_
:=
session
.
conf
.
IrmaConfiguration
.
PrivateKey
(
id
)
sk
,
_
:=
session
.
conf
.
IrmaConfiguration
.
PrivateKey
Latest
(
id
)
issuer
:=
gabi
.
NewIssuer
(
sk
,
pk
,
one
)
proof
,
ok
:=
commitments
.
Proofs
[
i
+
discloseCount
]
.
(
*
gabi
.
ProofU
)
if
!
ok
{
...
...
@@ -223,15 +223,15 @@ func (s *Server) handlePostUpdate(typ irma.CredentialTypeIdentifier, update *rev
return
nil
,
nil
}
// GET revocation/updatefrom/{credtype}/{index}
// GET revocation/updatefrom/{credtype}/{
pkindex}/{
index}
func
(
s
*
Server
)
handleGetUpdateFrom
(
cred
irma
.
CredentialTypeIdentifier
,
index
uint64
,
cred
irma
.
CredentialTypeIdentifier
,
pkindex
uint
,
index
uint64
,
)
(
*
revocation
.
Update
,
*
irma
.
RemoteError
)
{
if
settings
:=
s
.
conf
.
RevocationSettings
[
cred
];
settings
==
nil
||
!
(
settings
.
Mode
==
irma
.
RevocationModeProxy
||
settings
.
Mode
==
irma
.
RevocationModeServer
)
{
return
nil
,
server
.
RemoteError
(
server
.
ErrorInvalidRequest
,
"not supported by this server"
)
}
update
,
err
:=
s
.
conf
.
IrmaConfiguration
.
Revocation
.
UpdateFrom
(
cred
,
index
)
update
,
err
:=
s
.
conf
.
IrmaConfiguration
.
Revocation
.
UpdateFrom
(
cred
,
pkindex
,
index
)
if
err
!=
nil
{
return
nil
,
server
.
RemoteError
(
server
.
ErrorUnknown
,
err
.
Error
())
// TODO error type
}
...
...
@@ -241,7 +241,7 @@ func (s *Server) handleGetUpdateFrom(
// GET revocation/updatelatest/{credtype}/{count}
func
(
s
*
Server
)
handleGetUpdateLatest
(
cred
irma
.
CredentialTypeIdentifier
,
count
uint64
,
)
(
*
revocation
.
Update
,
*
irma
.
RemoteError
)
{
)
(
map
[
uint
]
*
revocation
.
Update
,
*
irma
.
RemoteError
)
{
if
settings
:=
s
.
conf
.
RevocationSettings
[
cred
];
settings
==
nil
||
!
(
settings
.
Mode
==
irma
.
RevocationModeProxy
||
settings
.
Mode
==
irma
.
RevocationModeServer
)
{
return
nil
,
server
.
RemoteError
(
server
.
ErrorInvalidRequest
,
"not supported by this server"
)
...
...
internal/servercore/helpers.go
View file @
8e573885
...
...
@@ -95,10 +95,14 @@ func (session *session) issuanceHandleRevocation(
// Fetch latest revocation record, and then extract the current value of the accumulator
// from it to generate the witness from
u
,
err
:=
rs
.
UpdateLatest
(
id
,
0
)
u
pdates
,
err
:=
rs
.
UpdateLatest
(
id
,
0
)
if
err
!=
nil
{
return
}
u
:=
updates
[
uint
(
cred
.
KeyCounter
)]
if
u
==
nil
{
return
nil
,
nil
,
errors
.
Errorf
(
"no revocation updates found for key %d"
,
cred
.
KeyCounter
)
}
sig
:=
u
.
SignedAccumulator
pk
,
err
:=
rs
.
Keys
.
PublicKey
(
id
.
IssuerIdentifier
(),
sig
.
PKIndex
)
if
err
!=
nil
{
...
...
@@ -117,6 +121,7 @@ func (session *session) issuanceHandleRevocation(
nonrevAttr
=
witness
.
E
issrecord
:=
&
irma
.
IssuanceRecord
{
CredType
:
id
,
PKIndex
:
sk
.
Counter
,
Key
:
cred
.
RevocationKey
,
Attr
:
(
*
irma
.
RevocationAttribute
)(
nonrevAttr
),
Issued
:
time
.
Now
()
.
UnixNano
(),
// or (floored) cred issuance time?
...
...
@@ -130,7 +135,7 @@ func (s *Server) validateIssuanceRequest(request *irma.IssuanceRequest) error {
for
_
,
cred
:=
range
request
.
Credentials
{
// Check that we have the appropriate private key
iss
:=
cred
.
CredentialTypeID
.
IssuerIdentifier
()
privatekey
,
err
:=
s
.
conf
.
IrmaConfiguration
.
PrivateKey
(
iss
)
privatekey
,
err
:=
s
.
conf
.
IrmaConfiguration
.
PrivateKey
Latest
(
iss
)
if
err
!=
nil
{
return
err
}
...
...
internal/sessiontest/server_test.go
View file @
8e573885
...
...
@@ -59,10 +59,9 @@ func StartRevocationServer(t *testing.T) {
var
err
error
irma
.
Logger
=
logger
dbstr
:=
"host=127.0.0.1 port=5432 user=testuser dbname=test password='testpassword' sslmode=disable"
dbtype
:=
"postgres"
//dbstr := "testuser:testpassword@tcp(127.0.0.1)/test"
//dbtype := "mysql"
//dbtype, dbstr := "postgres", "host=127.0.0.1 port=5432 user=testuser dbname=test password='testpassword' sslmode=disable"
dbtype
,
dbstr
:=
"mysql"
,
"testuser:testpassword@tcp(127.0.0.1)/test"
cred
:=
irma
.
NewCredentialTypeIdentifier
(
"irma-demo.MijnOverheid.root"
)
settings
:=
map
[
irma
.
CredentialTypeIdentifier
]
*
irma
.
RevocationSetting
{
cred
:
{
Mode
:
irma
.
RevocationModeServer
},
...
...
@@ -97,7 +96,7 @@ func StartRevocationServer(t *testing.T) {
require
.
NoError
(
t
,
g
.
Close
())
// Enable revocation for our credential type
sk
,
err
:=
irmaconf
.
Revocation
.
Keys
.
PrivateKey
(
cred
.
IssuerIdentifier
())
sk
,
err
:=
irmaconf
.
Revocation
.
Keys
.
PrivateKey
Latest
(
cred
.
IssuerIdentifier
())
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
irmaconf
.
Revocation
.
EnableRevocation
(
cred
,
sk
))
...
...
irmaclient/credential.go
View file @
8e573885
package
irmaclient
import
(
"github.com/go-errors/errors"
"github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/irmago"
...
...
@@ -57,20 +58,28 @@ func (cred *credential) NonrevPrepare(conf *irma.Configuration, request irma.Ses
}
// first try to update witness by applying the revocation update messages attached to the session request
keys
:=
irma
.
RevocationKeys
{
Conf
:
conf
}
revupdates
:=
base
.
RevocationUpdates
[
credtype
]
count
:=
len
(
revupdates
.
Events
)
updated
,
err
:=
cred
.
NonrevApplyUpdates
(
revupdates
,
keys
)
var
(
keys
=
irma
.
RevocationKeys
{
Conf
:
conf
}
revupdates
=
base
.
RevocationUpdates
[
credtype
][
cred
.
Pk
.
Counter
]
updated
bool
err
error
)
if
revupdates
==
nil
{
return
false
,
errors
.
Errorf
(
"revocation updates for key %d not found in session request"
,
cred
.
Pk
.
Counter
)
}
updated
,
err
=
cred
.
NonrevApplyUpdates
(
revupdates
,
keys
)
if
err
!=
nil
{
return
updated
,
err
}
count
:=
len
(
revupdates
.
Events
)
if
cred
.
NonRevocationWitness
.
Accumulator
.
Index
>=
revupdates
.
Events
[
count
-
1
]
.
Index
{
return
updated
,
nil
}
// nonrevocation witness is still out of date after applying the updates from the request:
// we were too far behind. Update from revocation server.
revupdates
,
err
=
irma
.
RevocationClient
{
Conf
:
conf
}
.
FetchUpdateFrom
(
credtype
,
cred
.
NonRevocationWitness
.
Accumulator
.
Index
+
1
)
revupdates
,
err
=
irma
.
RevocationClient
{
Conf
:
conf
}
.
FetchUpdateFrom
(
credtype
,
cred
.
Pk
.
Counter
,
cred
.
NonRevocationWitness
.
Accumulator
.
Index
+
1
)
if
err
!=
nil
{
return
updated
,
err
}
...
...
irmaconfig.go
View file @
8e573885
...
...
@@ -48,7 +48,7 @@ type Configuration struct {
// Issuer private keys. If set (after calling ParseFolder()), will use these keys
// instead of keys in irma_configuration/$issuer/PrivateKeys.
PrivateKeys
map
[
IssuerIdentifier
]
*
gabi
.
PrivateKey
PrivateKeys
map
[
IssuerIdentifier
]
map
[
int
]
*
gabi
.
PrivateKey
Revocation
*
RevocationStorage
...
...
@@ -166,7 +166,7 @@ func (conf *Configuration) clear() {
conf
.
DisabledSchemeManagers
=
make
(
map
[
SchemeManagerIdentifier
]
*
SchemeManagerError
)
conf
.
kssPublicKeys
=
make
(
map
[
SchemeManagerIdentifier
]
map
[
int
]
*
rsa
.
PublicKey
)
conf
.
publicKeys
=
make
(
map
[
IssuerIdentifier
]
map
[
int
]
*
gabi
.
PublicKey
)
conf
.
PrivateKeys
=
make
(
map
[
IssuerIdentifier
]
*
gabi
.
PrivateKey
)
conf
.
PrivateKeys
=
make
(
map
[
IssuerIdentifier
]
map
[
int
]
*
gabi
.
PrivateKey
)
conf
.
reverseHashes
=
make
(
map
[
string
]
CredentialTypeIdentifier
)
}
...
...
@@ -320,47 +320,42 @@ func (conf *Configuration) ParseSchemeManagerFolder(dir string, manager *SchemeM
return
}
// PrivateKey returns the latest private key of the specified issuer, or nil if not present in the Configuration.
func
(
conf
*
Configuration
)
PrivateKey
(
id
IssuerIdentifier
)
(
*
gabi
.
PrivateKey
,
error
)
{
if
sk
:=
conf
.
PrivateKeys
[
id
];
sk
!=
nil
{
return
sk
,
nil
// PrivateKey returns the specified private key of the specified issuer if present; an error otherwise.
func
(
conf
*
Configuration
)
PrivateKey
(
id
IssuerIdentifier
,
counter
int
)
(
*
gabi
.
PrivateKey
,
error
)
{
if
_
,
haveIssuer
:=
conf
.
PrivateKeys
[
id
];
haveIssuer
{
if
sk
:=
conf
.
PrivateKeys
[
id
][
counter
];
sk
!=
nil
{
return
sk
,
nil
}
}
path
:=
fmt
.
Sprintf
(
privkeyPattern
,
conf
.
Path
,
id
.
SchemeManagerIdentifier
()
.
Name
(),
id
.
Name
())
files
,
err
:=
filepath
.
Glob
(
path
)
file
:=
strings
.
Replace
(
path
,
"*"
,
strconv
.
Itoa
(
counter
),
1
)
sk
,
err
:=
gabi
.
NewPrivateKeyFromFile
(
file
)
if
err
!=
nil
{
return
nil
,
err
}
if
len
(
files
)
==
0
{
return
nil
,
nil
if
int
(
sk
.
Counter
)
!=
counter
{
return
nil
,
errors
.
Errorf
(
"Private key %s of issuer %s has wrong <Counter>"
,
file
,
id
.
String
())
}
// List private keys and get highest counter
counters
:=
make
([]
int
,
0
,
len
(
files
))
for
_
,
file
:=
range
files
{
filename
:=
filepath
.
Base
(
file
)
count
:=
filename
[
:
len
(
filename
)
-
4
]
i
,
err
:=
strconv
.
Atoi
(
count
)
if
err
!=
nil
{
return
nil
,
err
}
counters
=
append
(
counters
,
i
)
if
conf
.
PrivateKeys
[
id
]
==
nil
{
conf
.
PrivateKeys
[
id
]
=
make
(
map
[
int
]
*
gabi
.
PrivateKey
)
}
sort
.
Ints
(
counters
)
counter
:=
counters
[
len
(
counters
)
-
1
]
conf
.
PrivateKeys
[
id
][
counter
]
=
sk
// Read private key
file
:=
strings
.
Replace
(
path
,
"*"
,
strconv
.
Itoa
(
counter
),
1
)
sk
,
err
:=
gabi
.
NewPrivateKeyFromFile
(
file
)
return
sk
,
nil
}
// PrivateKeyLatest returns the latest private key of the specified issuer, or nil if not present in the Configuration.
func
(
conf
*
Configuration
)
PrivateKeyLatest
(
id
IssuerIdentifier
)
(
*
gabi
.
PrivateKey
,
error
)
{
indices
,
err
:=
conf
.
PrivateKeyIndices
(
id
)
if
err
!=
nil
{
return
nil
,
err
}
if
int
(
sk
.
Counter
)
!=
counter
{
return
nil
,
errors
.
Errorf
(
"Private key %s of issuer %s has wrong <Counter>"
,
file
,
id
.
String
()
)
if
len
(
indices
)
==
0
{
return
nil
,
errors
.
New
(
"no private keys found"
)
}
conf
.
PrivateKeys
[
id
]
=
sk
return
sk
,
nil
return
conf
.
PrivateKey
(
id
,
indices
[
len
(
indices
)
-
1
])
}
// PublicKey returns the specified public key, or nil if not present in the Configuration.
...
...
@@ -535,6 +530,10 @@ func (conf *Configuration) parseKeysFolder(issuerid IssuerIdentifier) error {
return
nil
}
func
(
conf
*
Configuration
)
PrivateKeyIndices
(
issuerid
IssuerIdentifier
)
(
i
[]
int
,
err
error
)
{
return
conf
.
matchKeyPattern
(
issuerid
,
privkeyPattern
)
}
func
(
conf
*
Configuration
)
PublicKeyIndices
(
issuerid
IssuerIdentifier
)
(
i
[]
int
,
err
error
)
{
return
conf
.
matchKeyPattern
(
issuerid
,
pubkeyPattern
)
}
...
...
requests.go
View file @
8e573885
...
...
@@ -39,7 +39,7 @@ type BaseRequest struct {
Revocation
[]
CredentialTypeIdentifier
`json:"revocation,omitempty"`
// RevocationUpdates contains revocation update messages for the client to update its
// revocation state.
RevocationUpdates
map
[
CredentialTypeIdentifier
]
*
revocation
.
Update
`json:"revocationUpdates,omitempty"`
RevocationUpdates
map
[
CredentialTypeIdentifier
]
map
[
uint
]
*
revocation
.
Update
`json:"revocationUpdates,omitempty"`
ids
*
IrmaIdentifierSet
// cache for Identifiers() method
...
...
@@ -237,7 +237,7 @@ func (b *BaseRequest) GetNonce(*atum.Timestamp) *big.Int {
// RequestsRevocation indicates whether or not the requestor requires a nonrevocation proof for
// the given credential type; that is, whether or not it included revocation update messages.
func
(
b
*
BaseRequest
)
RequestsRevocation
(
id
CredentialTypeIdentifier
)
bool
{
return
len
(
b
.
RevocationUpdates
)
>
0
&&
len
(
b
.
RevocationUpdates
[
id
]
.
Events
)
>
0
return
len
(
b
.
RevocationUpdates
)
>
0
&&
len
(
b
.
RevocationUpdates
[
id
])
>
0
}
func
(
b
*
BaseRequest
)
RevocationConsistent
()
error
{
...
...
revocation.go
View file @
8e573885
...
...
@@ -74,12 +74,13 @@ type (
AccumulatorRecord
struct
{
CredType
CredentialTypeIdentifier
`gorm:"primary_key"`
Data
signedMessage
PKIndex
uint
PKIndex
uint
`gorm:"primary_key;auto_increment:false"`
}
EventRecord
struct
{
Index
uint64
`gorm:"primary_key;column:eventindex"`
CredType
CredentialTypeIdentifier
`gorm:"primary_key"`
PKIndex
uint
`gorm:"primary_key;auto_increment:false"`
E
*
RevocationAttribute
ParentHash
eventHash
}
...
...
@@ -88,6 +89,7 @@ type (
IssuanceRecord
struct
{
Key
string
`gorm:"primary_key;column:revocationkey"`
CredType
CredentialTypeIdentifier
`gorm:"primary_key"`
PKIndex
uint
Attr
*
RevocationAttribute
Issued
int64
ValidUntil
int64
...
...
@@ -136,12 +138,12 @@ const (
// only way to create such an initial accumulator and it must be called before anyone can use
// revocation for this credential type. Requires the issuer private key.
func
(
rs
*
RevocationStorage
)
EnableRevocation
(
typ
CredentialTypeIdentifier
,
sk
*
revocation
.
PrivateKey
)
error
{
hasRecords
,
err
:=
rs
.
db
.
HasRecords
(
typ
,
(
*
EventRecord
)(
nil
)
)
enabled
,
err
:=
rs
.
RevocationEnabled
(
typ
)
if
err
!=
nil
{
return
err
}
if
hasRecords
{
return
errors
.
New
(
"revocation
event record table not empty
"
)
if
enabled
{
return
errors
.
New
(
"revocation
already enabled
"
)
}
update
,
err
:=
revocation
.
NewAccumulator
(
sk
)
...
...
@@ -159,7 +161,7 @@ func (rs *RevocationStorage) EnableRevocation(typ CredentialTypeIdentifier, sk *
// by checking if any revocation record exists in the database.
func
(
rs
*
RevocationStorage
)
RevocationEnabled
(
typ
CredentialTypeIdentifier
)
(
bool
,
error
)
{
if
rs
.
sqlMode
{
return
rs
.
db
.
HasRecords
(
typ
,
(
*
EventRecord
)(
nil
)
)
return
rs
.
db
.
Exists
((
*
EventRecord
)(
nil
),
nil
)
}
else
{
return
rs
.
memdb
.
HasRecords
(
typ
),
nil
}
...
...
@@ -170,16 +172,16 @@ func (rs *RevocationStorage) RevocationEnabled(typ CredentialTypeIdentifier) (bo
// UpdateFrom returns all records that a client requires to update its revocation state if it is currently
// at the specified index, that is, all records whose end index is greater than or equal to
// the specified index.
func
(
rs
*
RevocationStorage
)
UpdateFrom
(
typ
CredentialTypeIdentifier
,
index
uint64
)
(
*
revocation
.
Update
,
error
)
{
func
(
rs
*
RevocationStorage
)
UpdateFrom
(
typ
CredentialTypeIdentifier
,
pkindex
uint
,
index
uint64
)
(
*
revocation
.
Update
,
error
)
{
// Only requires SQL implementation
var
update
*
revocation
.
Update
if
err
:=
rs
.
db
.
Transaction
(
func
(
tx
revStorage
)
error
{
acc
,
_
,
err
:=
rs
.
currentAccumulator
(
tx
,
typ
)
acc
,
_
,
err
:=
rs
.
accumulator
(
tx
,
typ
,
pkindex
)
if
err
!=
nil
{
return
err
}
var
events
[]
*
EventRecord
if
err
:=
tx
.
From
(
typ
,
"index"
,
index
,
&
events
);
err
!=
nil
{
if
err
:=
tx
.
From
(
&
events
,
"cred_type = ? and pk_index = ? and index >= ?"
,
typ
,
pkindex
,
index
);
err
!=
nil
{
return
err
}
update
=
rs
.
newUpdate
(
acc
,
events
)
...
...
@@ -190,20 +192,22 @@ func (rs *RevocationStorage) UpdateFrom(typ CredentialTypeIdentifier, index uint
return
update
,
nil
}
func
(
rs
*
RevocationStorage
)
UpdateLatest
(
typ
CredentialTypeIdentifier
,
count
uint64
)
(
*
revocation
.
Update
,
error
)
{
func
(
rs
*
RevocationStorage
)
UpdateLatest
(
typ
CredentialTypeIdentifier
,
count
uint64
)
(
map
[
uint
]
*
revocation
.
Update
,
error
)
{
// TODO what should this function and UpdateFrom return when no records are found?
if
rs
.
sqlMode
{
var
update
*
revocation
.
Update
var
update
map
[
uint
]
*
revocation
.
Update
if
err
:=
rs
.
db
.
Transaction
(
func
(
tx
revStorage
)
error
{
acc
,
_
,
err
:=
rs
.
currentAccumulator
(
tx
,
typ
)
if
err
!=
nil
{
var
(
records
[]
*
AccumulatorRecord
events
[]
*
EventRecord
)
if
err
:=
tx
.
Last
(
&
records
,
map
[
string
]
interface
{}{
"cred_type"
:
typ
});
err
!=
nil
{
return
err
}
var
events
[]
*
EventRecord
if
err
:=
tx
.
Latest
(
typ
,
"eventindex"
,
count
,
&
events
);
err
!=
nil
{
if
err
:=
tx
.
Latest
(
&
events
,
count
,
map
[
string
]
interface
{}{
"cred_type"
:
typ
});
err
!=
nil
{
return
err
}
update
=
rs
.
newUpdate
(
acc
,
events
)
update
=
rs
.
newUpdate
s
(
records
,
events
)
return
nil
});
err
!=
nil
{
return
nil
,
err
...
...
@@ -214,7 +218,28 @@ func (rs *RevocationStorage) UpdateLatest(typ CredentialTypeIdentifier, count ui
}
}
func
(
*
RevocationStorage
)
newUpdate
(
acc
*
revocation
.
SignedAccumulator
,
events
[]
*
EventRecord
)
*
revocation
.
Update
{
func
(
*
RevocationStorage
)
newUpdates
(
records
[]
*
AccumulatorRecord
,
events
[]
*
EventRecord
)
map
[
uint
]
*
revocation
.
Update
{
accs
:=
map
[
uint
]
*
revocation
.
SignedAccumulator
{}
for
_
,
r
:=
range
records
{
accs
[
r
.
PKIndex
]
=
r
.
SignedAccumulator
()
}
updates
:=
make
(
map
[
uint
]
*
revocation
.
Update
,
len
(
accs
))
for
_
,
e
:=
range
events
{
i
:=
e
.
PKIndex
if
accs
[
i
]
==
nil
{
continue
}
update
,
present
:=
updates
[
i
]
if
!
present
{
update
=
&
revocation
.
Update
{
SignedAccumulator
:
accs
[
i
]}
updates
[
i
]
=
update
}
update
.
Events
=
append
(
update
.
Events
,
e
.
Event
())
}
return
updates
}
func
(
rs
*
RevocationStorage
)
newUpdate
(
acc
*
revocation
.
SignedAccumulator
,
events
[]
*
EventRecord
)
*
revocation
.
Update
{
updates
:=
make
([]
*
revocation
.
Event
,
len
(
events
))
for
i
:=
range
events
{
updates
[
i
]
=
events
[
i
]
.
Event
()
...
...
@@ -249,7 +274,7 @@ func (rs *RevocationStorage) addUpdate(tx revStorage, typ CredentialTypeIdentifi
return
err
}
for
_
,
event
:=
range
update
.
Events
{
if
err
=
tx
.
Insert
(
new
(
EventRecord
)
.
Convert
(
typ
,
event
));
err
!=
nil
{
if
err
=
tx
.
Insert
(
new
(
EventRecord
)
.
Convert
(
typ
,
update
.
SignedAccumulator
.
PKIndex
,
event
));
err
!=
nil
{
return
err
}
}
...
...
@@ -268,7 +293,7 @@ func (rs *RevocationStorage) addUpdate(tx revStorage, typ CredentialTypeIdentifi
// Issuance records
func
(
rs
*
RevocationStorage
)
IssuanceRecordExists
(
typ
CredentialTypeIdentifier
,
key
[]
byte
)
(
bool
,
error
)
{
return
rs
.
db
.
Exists
(
typ
,
"key"
,
key
,
&
IssuanceRecord
{
})
return
rs
.
db
.
Exists
(
&
IssuanceRecord
{},
map
[
string
]
interface
{}{
"cred_type"
:
typ
,
"revocationkey"
:
key
})
}
func
(
rs
*
RevocationStorage
)
AddIssuanceRecord
(
r
*
IssuanceRecord
)
error
{
...
...
@@ -277,7 +302,7 @@ func (rs *RevocationStorage) AddIssuanceRecord(r *IssuanceRecord) error {
func
(
rs
*
RevocationStorage
)
IssuanceRecord
(
typ
CredentialTypeIdentifier
,
key
[]
byte
)
(
*
IssuanceRecord
,
error
)
{
var
r
IssuanceRecord
err
:=
rs
.
db
.
Get
(
typ
,
"key"
,
key
,
&
r
)
err
:=
rs
.
db
.
Last
(
&
r
,
map
[
string
]
interface
{}{
"cred_type"
:
typ
,
"revocationkey"
:
key
}
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -289,7 +314,7 @@ func (rs *RevocationStorage) IssuanceRecord(typ CredentialTypeIdentifier, key []
// Revoke revokes the credential specified by key if found within the current database,
// by updating its revocation time to now, removing its revocation attribute from the current accumulator,
// and updating the revocation database on disk.
func
(
rs
*
RevocationStorage
)
Revoke
(
typ
CredentialTypeIdentifier
,
key
string
,
sk
*
revocation
.
PrivateKey
)
error
{
func
(
rs
*
RevocationStorage
)
Revoke
(
typ
CredentialTypeIdentifier
,
key
string
)
error
{
if
rs
.
getSettings
(
typ
)
.
Mode
!=
RevocationModeServer
{
return
errors
.
Errorf
(
"cannot revoke %s"
,
typ
)
}
...
...
@@ -297,19 +322,23 @@ func (rs *RevocationStorage) Revoke(typ CredentialTypeIdentifier, key string, sk
return
rs
.
db
.
Transaction
(
func
(
tx
revStorage
)
error
{
var
err
error
issrecord
:=
IssuanceRecord
{}
if
err
=
tx
.
Get
(
typ
,
"revocationkey"
,
key
,
&
issrecord
);
err
!=
nil
{
if
err
=
tx
.
Last
(
&
issrecord
,
map
[
string
]
interface
{}{
"cred_type"
:
typ
,
"revocationkey"
:
key
}
);
err
!=
nil
{
return
err
}
issrecord
.
RevokedAt
=
time
.
Now
()
.
UnixNano
()
if
err
=
tx
.
Save
(
&
issrecord
);
err
!=
nil
{
return
err
}
sk
,
err
:=
rs
.
Keys
.
PrivateKey
(
typ
.
IssuerIdentifier
(),
issrecord
.
PKIndex
)
if
err
!=
nil
{
return
err
}
return
rs
.
revokeAttr
(
tx
,
typ
,
sk
,
issrecord
.
Attr
)
})
}
func
(
rs
*
RevocationStorage
)
revokeAttr
(
tx
revStorage
,
typ
CredentialTypeIdentifier
,
sk
*
revocation
.
PrivateKey
,
e
*
RevocationAttribute
)
error
{
_
,
cur
,
err
:=
rs
.
currentAccumulator
(
tx
,
typ
)
_
,
cur
,
err
:=
rs
.
accumulator
(
tx
,
typ
,
sk
.
Counter
)
if
err
!=
nil
{
return
err
}
...
...
@@ -317,7 +346,7 @@ func (rs *RevocationStorage) revokeAttr(tx revStorage, typ CredentialTypeIdentif
return
errors
.
Errorf
(
"cannot revoke for type %s, not enabled yet"
,
typ
)
}
var
parent
EventRecord
if
err
=
rs
.
db
.
Last
(
typ
,
&
parent
);
err
!=
nil
{
if
err
=
rs
.
db
.
Last
(
&
parent
,
map
[
string
]
interface
{}{
"cred_type"
:
typ
,
"pk_index"
:
sk
.
Counter
}
);
err
!=
nil
{
return
err
}
...
...
@@ -333,25 +362,25 @@ func (rs *RevocationStorage) revokeAttr(tx revStorage, typ CredentialTypeIdentif
// Accumulator methods
func
(
rs
*
RevocationStorage
)
currentAccumulator
(
tx
revStorage
,
typ
CredentialTypeIdentifier
)
(
// accumulator retrieves, verifies and deserializes the accumulator of the given type and key.
func
(
rs
*
RevocationStorage
)
accumulator
(
tx
revStorage
,
typ
CredentialTypeIdentifier
,
pkindex
uint
)
(
*
revocation
.
SignedAccumulator
,
*
revocation
.
Accumulator
,
error
,
)
{
var
err
error
var
sacc
*
revocation
.
SignedAccumulator
if
rs
.
sqlMode
{
record
:=
&
AccumulatorRecord
{}
if
err
=
tx
.
Last
(
typ
,
record
);
err
!=
nil
{
if
err
=
tx
.
Last
(
record
,
map
[
string
]
interface
{}{
"cred_type"
:
typ
,
"pk_index"
:
pkindex
}
);
err
!=
nil
{
if
gorm
.
IsRecordNotFoundError
(
err
)
{
return
nil
,
nil
,
nil
}
}
sacc
=
record
.
SignedAccumulator
()
}
else
{
u
:=
rs
.
memdb
.
Latest
(
typ
,
0
)
if
u
==
nil
{
sacc
=
rs
.
memdb
.
SignedAccumulator
(
typ
,
pkindex
)
if
sacc
==
nil
{
return
nil
,
nil
,
nil
}
sacc
=
u
.
SignedAccumulator
}
pk
,
err
:=
rs
.
Keys
.
PublicKey
(
typ
.
IssuerIdentifier
(),
sacc
.
PKIndex
)
...
...
@@ -368,15 +397,15 @@ func (rs *RevocationStorage) currentAccumulator(tx revStorage, typ CredentialTyp
// Methods to update from remote revocation server
func
(
rs
*
RevocationStorage
)
UpdateDB
(
typ
CredentialTypeIdentifier
)
error
{
update
,
err
:=
rs
.
client
.
FetchUpdateLatest
(
typ
,
revocationUpdateCount
)
update
s
,
err
:=
rs
.
client
.
FetchUpdateLatest
(
typ
,
revocationUpdateCount
)
if
err
!=
nil
{
return
err
}
if
err
=
rs
.
AddUpdate
(
typ
,
update
);
err
!=
nil
{
return
err
for
_
,
u
:=
range
updates
{
if
err
=
rs
.
AddUpdate
(
typ
,
u
);
err
!=
nil
{
return
err
}
}
// bump updated even if no new records were added
rs
.
getSettings
(
typ
)
.
updated
=
time
.
Now
()
return
nil
...
...
@@ -489,7 +518,7 @@ func (rs *RevocationStorage) SetRevocationUpdates(b *BaseRequest) error {
return
nil
}
var
err
error
b
.
RevocationUpdates
=
make
(
map
[
CredentialTypeIdentifier
]
*
revocation
.
Update
,
len
(
b
.
Revocation
))
b
.
RevocationUpdates
=
make
(
map
[
CredentialTypeIdentifier
]
map
[
uint
]
*
revocation
.
Update
,
len
(
b
.
Revocation
))
for
_
,
credid
:=
range
b
.
Revocation
{
if
!
rs
.
conf
.
CredentialTypes
[
credid
]
.
SupportsRevocation
()
{
return
errors
.
Errorf
(
"cannot request nonrevocation proof for %s: revocation not enabled in scheme"
)
...
...
@@ -549,36 +578,59 @@ func (client RevocationClient) PostIssuanceRecord(typ CredentialTypeIdentifier,
}
// FetchRevocationRecords gets revocation update messages from the revocation server, of the specified index and greater.
func
(
client
RevocationClient
)
FetchUpdateFrom
(
typ
CredentialTypeIdentifier
,
index
uint64
)
(
*
revocation
.
Update
,
error
)
{
return
client
.
fetchUpdate
(
typ
,
"updatefrom"
,
index
)
func
(
client
RevocationClient
)
FetchUpdateFrom
(
typ
CredentialTypeIdentifier
,
pkindex
uint
,
index
uint64
)
(
*
revocation
.
Update
,
error
)
{
update
:=
&
revocation
.
Update
{}
return
update
,
client
.
getMultiple
(
client
.
Conf
.
CredentialTypes
[
typ
]
.
RevocationServers
,
fmt
.
Sprintf
(
"revocation/updatefrom/%s/%d/%d"
,
typ
,
pkindex
,
index
),
&
update
,
)
}
func
(
client
RevocationClient
)
FetchUpdateLatest
(
typ
CredentialTypeIdentifier
,
count
uint64
)
(
*
revocation
.
Update
,
error
)
{
return
client
.
fetchUpdate
(
typ
,
"updatelatest"
,
count
)
func
(
client
RevocationClient
)
FetchUpdateLatest
(
typ
CredentialTypeIdentifier
,
count
uint64
)
(
map
[
uint
]
*
revocation
.
Update
,
error
)
{
update
:=
map
[
uint
]
*
revocation
.
Update
{}
return
update
,
client
.
getMultiple
(
client
.
Conf
.
CredentialTypes
[
typ
]
.
RevocationServers
,
fmt
.
Sprintf
(
"revocation/updatelatest/%s/%d"
,
typ
,
count
),
&
update
,
)
}
func
(
client
RevocationClient
)
fetchUpdate
(
typ
CredentialTypeIdentifier
,
u
string
,
i
uint64
)
(
*
revocation
.
Update
,
error
)
{
func
(
RevocationClient
)
getMultiple
(
urls
[]
string
,
path
string
,
dest
interface
{})
error
{
var
(
err
error
errs
multierror
.
Error
update
=
&
revocation
.
Update
{}
transport
=
NewHTTPTransport
(
""
)
)
transport
.
Binary
=
true
for
_
,
url
:=
range
client
.
Conf
.
CredentialTypes
[
typ
]
.
RevocationServer
s
{
for
_
,
url
:=
range
url
s
{
transport
.
Server
=
url
err
=
transport
.
Get
(
fmt
.
Sprintf
(
"revocation/%s/%s/%d"
,
u
,
typ
,
i
),
&
update
)
err
:=
transport
.
Get
(
path
,
dest
)
if
err
==
nil
{
return
update
,
nil
return
nil
}
else
{
errs
.
Errors
=
append
(
errs
.
Errors
,
err
)
}
}
return
nil
,
errors
.
WrapPrefix
(
errs
,
"failed to download revocation update"
,
0
)
return
&
errs
}
func
(
rs
RevocationKeys
)
PrivateKeyLatest
(
issid
IssuerIdentifier
)
(
*
revocation
.
PrivateKey
,
error
)
{
sk
,
err
:=
rs
.
Conf
.
PrivateKeyLatest
(
issid
)
if
err
!=
nil
{
return
nil
,
err
}