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
786f0836
Commit
786f0836
authored
Sep 16, 2019
by
Sietse Ringers
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor: move revocation.DB and related structs from gabi to root package
parent
2919f416
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
429 additions
and
96 deletions
+429
-96
internal/servercore/api.go
internal/servercore/api.go
+4
-5
internal/servercore/handle.go
internal/servercore/handle.go
+7
-8
internal/servercore/helpers.go
internal/servercore/helpers.go
+5
-5
internal/sessiontest/requestor_test.go
internal/sessiontest/requestor_test.go
+0
-16
internal/sessiontest/server_test.go
internal/sessiontest/server_test.go
+12
-3
irmaclient/credential.go
irmaclient/credential.go
+3
-5
irmaconfig.go
irmaconfig.go
+3
-2
requests.go
requests.go
+4
-5
revocation.go
revocation.go
+385
-39
server/conf.go
server/conf.go
+4
-5
verify.go
verify.go
+2
-3
No files found.
internal/servercore/api.go
View file @
786f0836
...
...
@@ -14,7 +14,6 @@ import (
"github.com/go-errors/errors"
"github.com/jasonlvhit/gocron"
"github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/server"
"github.com/sirupsen/logrus"
...
...
@@ -54,7 +53,7 @@ func New(conf *server.Configuration) (*Server, error) {
// TODO rethink this condition
continue
}
if
err
:=
s
.
conf
.
IrmaConfiguration
.
RevocationUpdateDB
(
credid
);
err
!=
nil
{
if
err
:=
s
.
conf
.
IrmaConfiguration
.
Revocation
Storage
.
Revocation
UpdateDB
(
credid
);
err
!=
nil
{
s
.
conf
.
Logger
.
Error
(
"failed to update revocation database for %s:"
,
credid
.
String
())
_
=
server
.
LogError
(
err
)
}
...
...
@@ -67,7 +66,7 @@ func New(conf *server.Configuration) (*Server, error) {
}
func
(
s
*
Server
)
Stop
()
{
if
err
:=
s
.
conf
.
IrmaConfiguration
.
Close
();
err
!=
nil
{
if
err
:=
s
.
conf
.
IrmaConfiguration
.
RevocationStorage
.
Close
();
err
!=
nil
{
_
=
server
.
LogWarning
(
err
)
}
s
.
stopScheduler
<-
true
...
...
@@ -141,7 +140,7 @@ func (s *Server) CancelSession(token string) error {
}
func
(
s
*
Server
)
Revoke
(
credid
irma
.
CredentialTypeIdentifier
,
key
string
)
error
{
return
s
.
conf
.
IrmaConfiguration
.
Revoke
(
credid
,
key
)
return
s
.
conf
.
IrmaConfiguration
.
Revo
cationStorage
.
Revo
ke
(
credid
,
key
)
}
func
ParsePath
(
path
string
)
(
token
,
noun
string
,
arg
[]
string
,
err
error
)
{
...
...
@@ -387,7 +386,7 @@ func (s *Server) handleRevocationMessage(
return
server
.
JsonResponse
(
nil
,
server
.
RemoteError
(
server
.
ErrorInvalidRequest
,
"POST records expects 1 url arguments"
))
}
cred
:=
irma
.
NewCredentialTypeIdentifier
(
args
[
0
])
var
records
[]
*
revocation
.
Record
var
records
[]
*
irma
.
Record
if
err
:=
json
.
Unmarshal
(
message
,
&
records
);
err
!=
nil
{
return
server
.
JsonResponse
(
nil
,
server
.
RemoteError
(
server
.
ErrorMalformedInput
,
err
.
Error
()))
}
...
...
internal/servercore/handle.go
View file @
786f0836
...
...
@@ -4,7 +4,6 @@ import (
"time"
"github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/gabi/signed"
"github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/server"
...
...
@@ -37,7 +36,7 @@ func (session *session) handleGetRequest(min, max *irma.ProtocolVersion) (irma.S
// we include the latest revocation records for the client here, as opposed to when the session
// was started, so that the client always gets the very latest revocation records
var
err
error
if
err
=
session
.
conf
.
IrmaConfiguration
.
RevocationSetRecords
(
session
.
request
.
Base
());
err
!=
nil
{
if
err
=
session
.
conf
.
IrmaConfiguration
.
RevocationS
torage
.
RevocationS
etRecords
(
session
.
request
.
Base
());
err
!=
nil
{
return
nil
,
session
.
fail
(
server
.
ErrorUnknown
,
err
.
Error
())
// TODO error type
}
...
...
@@ -216,9 +215,9 @@ func (session *session) handlePostCommitments(commitments *irma.IssueCommitmentM
}
func
(
s
*
Server
)
handlePostRevocationRecords
(
cred
irma
.
CredentialTypeIdentifier
,
records
[]
*
revocation
.
Record
,
cred
irma
.
CredentialTypeIdentifier
,
records
[]
*
irma
.
Record
,
)
(
interface
{},
*
irma
.
RemoteError
)
{
db
,
err
:=
s
.
conf
.
IrmaConfiguration
.
RevocationDB
(
cred
)
db
,
err
:=
s
.
conf
.
IrmaConfiguration
.
Revocation
Storage
.
Revocation
DB
(
cred
)
if
err
!=
nil
{
return
nil
,
server
.
RemoteError
(
server
.
ErrorUnknown
,
err
.
Error
())
// TODO error type
}
...
...
@@ -230,11 +229,11 @@ func (s *Server) handlePostRevocationRecords(
func
(
s
*
Server
)
handleGetRevocationRecords
(
cred
irma
.
CredentialTypeIdentifier
,
index
int
,
)
([]
*
revocation
.
Record
,
*
irma
.
RemoteError
)
{
)
([]
*
irma
.
Record
,
*
irma
.
RemoteError
)
{
if
_
,
ok
:=
s
.
conf
.
RevocationServers
[
cred
];
!
ok
{
return
nil
,
server
.
RemoteError
(
server
.
ErrorInvalidRequest
,
"not supported by this server"
)
}
db
,
err
:=
s
.
conf
.
IrmaConfiguration
.
RevocationDB
(
cred
)
db
,
err
:=
s
.
conf
.
IrmaConfiguration
.
Revocation
Storage
.
Revocation
DB
(
cred
)
if
err
!=
nil
{
return
nil
,
server
.
RemoteError
(
server
.
ErrorUnknown
,
err
.
Error
())
// TODO error type
}
...
...
@@ -265,13 +264,13 @@ func (s *Server) handlePostIssuanceRecord(
if
err
!=
nil
{
return
""
,
server
.
RemoteError
(
server
.
ErrorUnknown
,
err
.
Error
())
}
var
rec
revocation
.
IssuanceRecord
var
rec
irma
.
IssuanceRecord
if
err
:=
signed
.
UnmarshalVerify
(
revpk
.
ECDSA
,
message
,
&
rec
);
err
!=
nil
{
return
""
,
server
.
RemoteError
(
server
.
ErrorUnauthorized
,
err
.
Error
())
}
// Insert the record into the database
db
,
err
:=
s
.
conf
.
IrmaConfiguration
.
RevocationDB
(
cred
)
db
,
err
:=
s
.
conf
.
IrmaConfiguration
.
Revocation
Storage
.
Revocation
DB
(
cred
)
if
err
!=
nil
{
return
""
,
server
.
RemoteError
(
server
.
ErrorUnknown
,
err
.
Error
())
}
...
...
internal/servercore/helpers.go
View file @
786f0836
...
...
@@ -85,12 +85,12 @@ func (session *session) issuanceHandleRevocation(
// ensure the client always gets an up to date nonrevocation witness
if
_
,
ours
:=
session
.
conf
.
RevocationServers
[
cred
.
CredentialTypeID
];
!
ours
{
if
err
=
session
.
conf
.
IrmaConfiguration
.
RevocationUpdateDB
(
cred
.
CredentialTypeID
);
err
!=
nil
{
if
err
=
session
.
conf
.
IrmaConfiguration
.
Revocation
Storage
.
Revocation
UpdateDB
(
cred
.
CredentialTypeID
);
err
!=
nil
{
return
}
}
db
,
err
:=
session
.
conf
.
IrmaConfiguration
.
RevocationDB
(
cred
.
CredentialTypeID
)
db
,
err
:=
session
.
conf
.
IrmaConfiguration
.
Revocation
Storage
.
Revocation
DB
(
cred
.
CredentialTypeID
)
if
err
!=
nil
{
return
}
...
...
@@ -102,13 +102,13 @@ func (session *session) issuanceHandleRevocation(
return
}
nonrevAttr
=
witness
.
E
issrecord
:=
&
revocation
.
IssuanceRecord
{
issrecord
:=
&
irma
.
IssuanceRecord
{
Key
:
cred
.
RevocationKey
,
Attr
:
nonrevAttr
,
Issued
:
time
.
Now
()
.
UnixNano
(),
// or (floored) cred issuance time?
ValidUntil
:
attributes
.
Expiry
()
.
UnixNano
(),
}
err
=
session
.
conf
.
IrmaConfiguration
.
SendRevocationIssuanceRecord
(
cred
.
CredentialTypeID
,
issrecord
)
err
=
session
.
conf
.
IrmaConfiguration
.
RevocationStorage
.
SendRevocationIssuanceRecord
(
cred
.
CredentialTypeID
,
issrecord
)
if
err
!=
nil
{
_
=
server
.
LogWarning
(
errors
.
WrapPrefix
(
err
,
"Failed to send issuance record to revocation server"
,
0
))
session
.
conf
.
Logger
.
Warn
(
"Storing issuance record locally"
)
...
...
@@ -144,7 +144,7 @@ func (s *Server) validateIssuanceRequest(request *irma.IssuanceRequest) error {
return
err
}
if
s
.
conf
.
IrmaConfiguration
.
CredentialTypes
[
cred
.
CredentialTypeID
]
.
SupportsRevocation
()
{
db
,
err
:=
s
.
conf
.
IrmaConfiguration
.
RevocationDB
(
cred
.
CredentialTypeID
)
db
,
err
:=
s
.
conf
.
IrmaConfiguration
.
Revocation
Storage
.
Revocation
DB
(
cred
.
CredentialTypeID
)
if
err
!=
nil
{
return
err
}
...
...
internal/sessiontest/requestor_test.go
View file @
786f0836
...
...
@@ -6,13 +6,10 @@ import (
"io/ioutil"
"net/http"
"path/filepath"
"reflect"
"testing"
"github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/internal/test"
"github.com/privacybydesign/irmago/irmaclient"
...
...
@@ -352,20 +349,7 @@ func TestRevocation(t *testing.T) {
// setup client, constants, and revocation key material
defer
test
.
ClearTestStorage
(
t
)
client
,
_
:=
parseStorage
(
t
)
iss
:=
irma
.
NewIssuerIdentifier
(
"irma-demo.MijnOverheid"
)
cred
:=
irma
.
NewCredentialTypeIdentifier
(
"irma-demo.MijnOverheid.root"
)
keystore
:=
client
.
Configuration
.
RevocationKeystore
(
iss
)
sk
,
err
:=
client
.
Configuration
.
PrivateKey
(
iss
)
require
.
NoError
(
t
,
err
)
revsk
,
err
:=
sk
.
RevocationKey
()
require
.
NoError
(
t
,
err
)
// enable revocation for our credential type by creating and saving an initial accumulator
db
,
err
:=
revocation
.
LoadDB
(
filepath
.
Join
(
testdata
,
"tmp"
,
"issuer"
,
cred
.
String
()),
keystore
)
require
.
NoError
(
t
,
err
)
require
.
NoError
(
t
,
db
.
EnableRevocation
(
revsk
))
require
.
NoError
(
t
,
db
.
Close
())
// so StartRevocationServer() can open it again
StartRevocationServer
(
t
)
// issue two MijnOverheid.root instances with revocation enabled
...
...
internal/sessiontest/server_test.go
View file @
786f0836
...
...
@@ -51,15 +51,24 @@ func StopRequestorServer() {
func
StartRevocationServer
(
t
*
testing
.
T
)
{
var
err
error
revocationServer
,
err
=
irmaserver
.
New
(
&
server
.
Configuration
{
cred
:=
irma
.
NewCredentialTypeIdentifier
(
"irma-demo.MijnOverheid.root"
)
conf
:=
&
server
.
Configuration
{
Logger
:
logger
,
DisableSchemesUpdate
:
true
,
SchemesPath
:
filepath
.
Join
(
testdata
,
"irma_configuration"
),
RevocationPath
:
filepath
.
Join
(
testdata
,
"tmp"
,
"issuer"
),
// todo rename this path to revocation?
RevocationServers
:
map
[
irma
.
CredentialTypeIdentifier
]
server
.
RevocationServer
{
irma
.
NewCredentialTypeIdentifier
(
"irma-demo.MijnOverheid.root"
)
:
{},
cred
:
{},
},
})
}
revocationServer
,
err
=
irmaserver
.
New
(
conf
)
require
.
NoError
(
t
,
err
)
sk
,
err
:=
conf
.
IrmaConfiguration
.
RevocationStorage
.
PrivateKey
(
cred
.
IssuerIdentifier
())
require
.
NoError
(
t
,
err
)
db
,
err
:=
conf
.
IrmaConfiguration
.
RevocationStorage
.
RevocationDB
(
cred
)
require
.
NoError
(
t
,
err
)
err
=
db
.
EnableRevocation
(
sk
)
require
.
NoError
(
t
,
err
)
mux
:=
http
.
NewServeMux
()
...
...
irmaclient/credential.go
View file @
786f0836
...
...
@@ -51,8 +51,7 @@ func (cred *credential) PrepareNonrevocation(conf *irma.Configuration, request i
revupdates
:=
m
[
credtype
]
nonrev
:=
len
(
revupdates
)
>
0
keystore
:=
conf
.
RevocationKeystore
(
credtype
.
IssuerIdentifier
())
if
updated
,
err
:=
cred
.
NonRevocationWitness
.
Update
(
revupdates
,
keystore
);
err
!=
nil
{
if
updated
,
err
:=
conf
.
RevocationStorage
.
UpdateWitness
(
cred
.
NonRevocationWitness
,
revupdates
,
credtype
.
IssuerIdentifier
());
err
!=
nil
{
return
false
,
err
}
else
if
updated
{
cred
.
DiscardRevocationCache
()
...
...
@@ -65,11 +64,10 @@ func (cred *credential) PrepareNonrevocation(conf *irma.Configuration, request i
// nonrevocation witness is still out of date after applying the updates from the request,
// i.e. we were too far behind. Update from revocation server.
re
cords
,
err
:=
conf
.
RevocationGetUpdates
(
credtype
,
cred
.
NonRevocationWitness
.
Index
+
1
)
re
vupdates
,
err
:=
conf
.
RevocationStorage
.
RevocationGetUpdates
(
credtype
,
cred
.
NonRevocationWitness
.
Index
+
1
)
if
err
!=
nil
{
return
nonrev
,
err
}
_
,
err
=
c
red
.
NonRevocationWitness
.
Update
(
records
,
keystore
)
_
,
err
=
c
onf
.
RevocationStorage
.
UpdateWitness
(
cred
.
NonRevocationWitness
,
revupdates
,
credtype
.
IssuerIdentifier
()
)
return
nonrev
,
err
}
irmaconfig.go
View file @
786f0836
...
...
@@ -33,7 +33,6 @@ import (
"github.com/jasonlvhit/gocron"
"github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/big"
"github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/gabi/signed"
"github.com/privacybydesign/irmago/internal/fs"
)
...
...
@@ -46,6 +45,8 @@ type Configuration struct {
CredentialTypes
map
[
CredentialTypeIdentifier
]
*
CredentialType
AttributeTypes
map
[
AttributeTypeIdentifier
]
*
AttributeType
RevocationStorage
*
RevocationStorage
// Path to the irma_configuration folder that this instance represents
Path
string
RevocationPath
string
...
...
@@ -60,7 +61,6 @@ type Configuration struct {
publicKeys
map
[
IssuerIdentifier
]
map
[
int
]
*
gabi
.
PublicKey
privateKeys
map
[
IssuerIdentifier
]
*
gabi
.
PrivateKey
reverseHashes
map
[
string
]
CredentialTypeIdentifier
revDBs
map
[
CredentialTypeIdentifier
]
*
revocation
.
DB
initialized
bool
assets
string
readOnly
bool
...
...
@@ -143,6 +143,7 @@ func newConfiguration(path string, assets string) (conf *Configuration, err erro
RevocationPath
:
filepath
.
Join
(
DefaultDataPath
(),
"revocation"
),
assets
:
assets
,
}
conf
.
RevocationStorage
=
&
RevocationStorage
{
conf
:
conf
}
if
conf
.
assets
!=
""
{
// If an assets folder is specified, then it must exist
if
err
=
fs
.
AssertPathExists
(
conf
.
assets
);
err
!=
nil
{
...
...
requests.go
View file @
786f0836
...
...
@@ -13,7 +13,6 @@ import (
"github.com/go-errors/errors"
"github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/big"
"github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/irmago/internal/fs"
)
...
...
@@ -33,10 +32,10 @@ type BaseRequest struct {
Revocation
[]
CredentialTypeIdentifier
`json:"revocation,omitempty"`
// Set by the IRMA server during the session
Context
*
big
.
Int
`json:"context,omitempty"`
Nonce
*
big
.
Int
`json:"nonce,omitempty"`
ProtocolVersion
*
ProtocolVersion
`json:"protocolVersion,omitempty"`
RevocationUpdates
map
[
CredentialTypeIdentifier
][]
*
revocation
.
Record
`json:"revocationUpdates,omitempty"`
Context
*
big
.
Int
`json:"context,omitempty"`
Nonce
*
big
.
Int
`json:"nonce,omitempty"`
ProtocolVersion
*
ProtocolVersion
`json:"protocolVersion,omitempty"`
RevocationUpdates
map
[
CredentialTypeIdentifier
][]
*
Record
`json:"revocationUpdates,omitempty"`
ids
*
IrmaIdentifierSet
// cache for Identifiers() method
...
...
revocation.go
View file @
786f0836
...
...
@@ -7,21 +7,75 @@ import (
"github.com/go-errors/errors"
"github.com/hashicorp/go-multierror"
"github.com/privacybydesign/gabi/big"
"github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/gabi/signed"
"github.com/timshannon/bolthold"
bolt
"go.etcd.io/bbolt"
)
func
(
conf
*
Configuration
)
RevocationKeystore
(
issuerid
IssuerIdentifier
)
revocation
.
Keystore
{
type
(
// Keystore provides support for revocation public key rollover.
Keystore
interface
{
// PublicKey either returns the specified, non-nil public key or an error
PublicKey
(
counter
uint
)
(
*
revocation
.
PublicKey
,
error
)
}
// DB is a bolthold database storing revocation state for a particular accumulator
// (Record instances, and IssuanceRecord instances if used by an issuer).
DB
struct
{
Current
revocation
.
Accumulator
Updated
time
.
Time
onChange
[]
func
(
*
Record
)
bolt
*
bolthold
.
Store
keystore
Keystore
}
RevocationStorage
struct
{
dbs
map
[
CredentialTypeIdentifier
]
*
DB
conf
*
Configuration
}
// Record contains a signed AccumulatorUpdate and associated information.
Record
struct
{
StartIndex
uint64
EndIndex
uint64
PublicKeyIndex
uint
Message
signed
.
Message
// signed revocation.AccumulatorUpdate
}
TimeRecord
struct
{
Index
uint64
Start
,
End
int64
}
// IssuanceRecord contains information generated during issuance, needed for later revocation.
IssuanceRecord
struct
{
Key
string
Attr
*
big
.
Int
Issued
int64
ValidUntil
int64
RevokedAt
int64
// 0 if not currently revoked
}
currentRecord
struct
{
Index
uint64
}
)
const
boltCurrentIndexKey
=
"currentIndex"
func
(
conf
*
Configuration
)
RevocationKeystore
(
issuerid
IssuerIdentifier
)
Keystore
{
return
&
issuerKeystore
{
issid
:
issuerid
,
conf
:
conf
}
}
// issuerKeystore implements
revocation.
Keystore.
// issuerKeystore implements Keystore.
type
issuerKeystore
struct
{
issid
IssuerIdentifier
conf
*
Configuration
}
var
_
revocation
.
Keystore
=
(
*
issuerKeystore
)(
nil
)
var
_
Keystore
=
(
*
issuerKeystore
)(
nil
)
func
(
ks
*
issuerKeystore
)
PublicKey
(
counter
uint
)
(
*
revocation
.
PublicKey
,
error
)
{
pk
,
err
:=
ks
.
conf
.
PublicKey
(
ks
.
issid
,
int
(
counter
))
...
...
@@ -41,9 +95,304 @@ func (ks *issuerKeystore) PublicKey(counter uint) (*revocation.PublicKey, error)
return
rpk
,
nil
}
func
(
conf
*
Configuration
)
RevocationGetUpdates
(
credid
CredentialTypeIdentifier
,
index
uint64
)
([]
*
revocation
.
Record
,
error
)
{
var
records
[]
*
revocation
.
Record
err
:=
NewHTTPTransport
(
conf
.
CredentialTypes
[
credid
]
.
RevocationServer
)
.
func
(
rdb
*
DB
)
EnableRevocation
(
sk
*
revocation
.
PrivateKey
)
error
{
msg
,
acc
,
err
:=
revocation
.
NewAccumulator
(
sk
)
if
err
!=
nil
{
return
err
}
if
err
=
rdb
.
Add
(
msg
,
sk
.
Counter
);
err
!=
nil
{
return
err
}
rdb
.
Current
=
acc
return
nil
}
// Revoke revokes the credential specified specified by key if found within the current database,
// by updating its revocation time to now, adding its revocation attribute to the current accumulator,
// and updating the revocation database on disk.
func
(
rdb
*
DB
)
Revoke
(
sk
*
revocation
.
PrivateKey
,
key
[]
byte
)
error
{
return
rdb
.
bolt
.
Bolt
()
.
Update
(
func
(
tx
*
bolt
.
Tx
)
error
{
var
err
error
cr
:=
IssuanceRecord
{}
if
err
=
rdb
.
bolt
.
TxGet
(
tx
,
key
,
&
cr
);
err
!=
nil
{
return
err
}
cr
.
RevokedAt
=
time
.
Now
()
.
UnixNano
()
if
err
=
rdb
.
bolt
.
TxUpdate
(
tx
,
key
,
&
cr
);
err
!=
nil
{
return
err
}
return
rdb
.
revokeAttr
(
sk
,
cr
.
Attr
,
tx
)
})
}
// Get 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
(
rdb
*
DB
)
RevocationRecords
(
index
int
)
([]
*
Record
,
error
)
{
var
records
[]
*
Record
if
err
:=
rdb
.
bolt
.
Find
(
&
records
,
bolthold
.
Where
(
bolthold
.
Key
)
.
Ge
(
uint64
(
index
)));
err
!=
nil
{
return
nil
,
err
}
return
records
,
nil
}
func
(
rdb
*
DB
)
LatestRecords
(
count
int
)
([]
*
Record
,
error
)
{
c
:=
int
(
rdb
.
Current
.
Index
)
-
count
+
1
if
c
<
0
{
c
=
0
}
return
rdb
.
RevocationRecords
(
c
)
}
func
(
rdb
*
DB
)
IssuanceRecordExists
(
key
[]
byte
)
(
bool
,
error
)
{
_
,
err
:=
rdb
.
IssuanceRecord
(
key
)
switch
err
{
case
nil
:
return
true
,
nil
case
bolthold
.
ErrNotFound
:
return
false
,
nil
default
:
return
false
,
err
}
}
func
(
rdb
*
DB
)
AddIssuanceRecord
(
r
*
IssuanceRecord
)
error
{
return
rdb
.
bolt
.
Insert
([]
byte
(
r
.
Key
),
r
)
}
func
(
rdb
*
DB
)
IssuanceRecord
(
key
[]
byte
)
(
*
IssuanceRecord
,
error
)
{
r
:=
&
IssuanceRecord
{}
if
err
:=
rdb
.
bolt
.
Get
(
key
,
r
);
err
!=
nil
{
return
nil
,
err
}
return
r
,
nil
}
func
(
rdb
*
DB
)
AddRecords
(
records
[]
*
Record
)
error
{
var
err
error
for
_
,
r
:=
range
records
{
if
err
=
rdb
.
Add
(
r
.
Message
,
r
.
PublicKeyIndex
);
err
!=
nil
{
return
err
}
}
rdb
.
Updated
=
time
.
Now
()
// TODO update this in add()?
return
nil
}
func
(
rdb
*
DB
)
Add
(
updateMsg
signed
.
Message
,
counter
uint
)
error
{
var
err
error
var
update
revocation
.
AccumulatorUpdate
pk
,
err
:=
rdb
.
keystore
.
PublicKey
(
counter
)
if
err
!=
nil
{
return
err
}
if
err
=
signed
.
UnmarshalVerify
(
pk
.
ECDSA
,
updateMsg
,
&
update
);
err
!=
nil
{
return
err
}
return
rdb
.
bolt
.
Bolt
()
.
Update
(
func
(
tx
*
bolt
.
Tx
)
error
{
return
rdb
.
add
(
update
,
updateMsg
,
counter
,
tx
)
})
}
func
(
rdb
*
DB
)
add
(
update
revocation
.
AccumulatorUpdate
,
updateMsg
signed
.
Message
,
pkCounter
uint
,
tx
*
bolt
.
Tx
)
error
{
var
err
error
record
:=
&
Record
{
StartIndex
:
update
.
StartIndex
,
EndIndex
:
update
.
Accumulator
.
Index
,
PublicKeyIndex
:
pkCounter
,
Message
:
updateMsg
,
}
if
err
=
rdb
.
bolt
.
TxInsert
(
tx
,
update
.
Accumulator
.
Index
,
record
);
err
!=
nil
{
return
err
}
if
update
.
Accumulator
.
Index
!=
0
{
var
tr
TimeRecord
if
err
=
rdb
.
bolt
.
TxGet
(
tx
,
update
.
Accumulator
.
Index
-
1
,
&
tr
);
err
==
nil
{
tr
.
End
=
time
.
Now
()
.
UnixNano
()
if
err
=
rdb
.
bolt
.
TxUpdate
(
tx
,
update
.
Accumulator
.
Index
-
1
,
&
tr
);
err
!=
nil
{
return
err
}
}
}
if
err
=
rdb
.
bolt
.
TxInsert
(
tx
,
update
.
Accumulator
.
Index
,
&
TimeRecord
{
Index
:
update
.
Accumulator
.
Index
,
Start
:
time
.
Now
()
.
UnixNano
(),
});
err
!=
nil
{
return
err
}
if
err
=
rdb
.
bolt
.
TxUpsert
(
tx
,
boltCurrentIndexKey
,
&
currentRecord
{
update
.
Accumulator
.
Index
});
err
!=
nil
{
return
err
}
for
_
,
f
:=
range
rdb
.
onChange
{
f
(
record
)
}
rdb
.
Current
=
update
.
Accumulator
return
nil
}
func
(
rdb
*
DB
)
Enabled
()
bool
{
var
currentIndex
currentRecord
err
:=
rdb
.
bolt
.
Get
(
boltCurrentIndexKey
,
&
currentIndex
)
return
err
==
nil
}
func
(
rdb
*
DB
)
loadCurrent
()
error
{
var
currentIndex
currentRecord
if
err
:=
rdb
.
bolt
.
Get
(
boltCurrentIndexKey
,
&
currentIndex
);
err
==
bolthold
.
ErrNotFound
{
return
errors
.
New
(
"revocation database not initialized"
)
}
else
if
err
!=
nil
{
return
err
}
var
record
Record
if
err
:=
rdb
.
bolt
.
Get
(
currentIndex
.
Index
,
&
record
);
err
!=
nil
{
return
err
}
pk
,
err
:=
rdb
.
keystore
.
PublicKey
(
record
.
PublicKeyIndex
)
if
err
!=
nil
{
return
err
}
var
u
revocation
.
AccumulatorUpdate
if
err
=
signed
.
UnmarshalVerify
(
pk
.
ECDSA
,
record
.
Message
,
&
u
);
err
!=
nil
{
return
err
}
rdb
.
Current
=
u
.
Accumulator
return
nil
}
func
(
rdb
*
DB
)
RevokeAttr
(
sk
*
revocation
.
PrivateKey
,
e
*
big
.
Int
)
error
{
return
rdb
.
bolt
.
Bolt
()
.
Update
(
func
(
tx
*
bolt
.
Tx
)
error
{
return
rdb
.
revokeAttr
(
sk
,
e
,
tx
)
})
}
func
(
rdb
*
DB
)
revokeAttr
(
sk
*
revocation
.
PrivateKey
,
e
*
big
.
Int
,
tx
*
bolt
.
Tx
)
error
{
// don't update rdb.Current until after all possible errors are handled
newAcc
,
err
:=
rdb
.
Current
.
Remove
(
sk
,
e
)
if
err
!=
nil
{
return
err
}
update
:=
revocation
.
AccumulatorUpdate
{
Accumulator
:
*
newAcc
,
StartIndex
:
newAcc
.
Index
,
Revoked
:
[]
*
big
.
Int
{
e
},
Time
:
time
.
Now
()
.
UnixNano
(),
}
updateMsg
,
err
:=
signed
.
MarshalSign
(
sk
.
ECDSA
,
update
)
if
err
!=
nil
{
return
err
}
if
err
=
rdb
.
add
(
update
,
updateMsg
,
sk
.
Counter
,
tx
);
err
!=
nil
{
return
err
}
rdb
.
Current
=
*
newAcc
return
nil
}
func
(
rdb
*
DB
)
Close
()
error
{
rdb
.
onChange
=
nil
if
rdb
.
bolt
!=
nil
{
return
rdb
.
bolt
.
Close
()
}
return
nil
}
func
(
rdb
*
DB
)
OnChange
(
handler
func
(
*
Record
))
{
rdb
.
onChange
=
append
(
rdb
.
onChange
,
handler
)
}
func
(
r
*
Record
)
UnmarshalVerify
(
keystore
Keystore
)
(
*
revocation
.
AccumulatorUpdate
,
error
)
{
pk
,
err
:=
keystore
.
PublicKey
(
r
.
PublicKeyIndex
)
if
err
!=
nil
{
return
nil
,
err
}
msg
:=
&
revocation
.
AccumulatorUpdate
{}
if
err
:=
signed
.
UnmarshalVerify
(
pk
.
ECDSA
,
r
.
Message
,
msg
);
err
!=
nil
{
return
nil
,
err
}
if
(
r
.
StartIndex
!=
msg
.
StartIndex
)
||
(
r
.
EndIndex
>
0
&&
r
.
EndIndex
!=
msg
.
StartIndex
+
uint64
(
len
(
msg
.
Revoked
))
-
1
)
{
return
nil
,
errors
.
New
(
"record has invalid start or end index"
)
}
return
msg
,
nil
}
func
(
rs
*
RevocationStorage
)
LoadDB
(
credid
CredentialTypeIdentifier
)
(
*
DB
,
error
)
{
path
:=
filepath
.
Join
(
rs
.
conf
.
RevocationPath
,
credid
.
String
())
keystore
:=
rs
.
conf
.
RevocationKeystore
(
credid
.
IssuerIdentifier
())
b
,
err
:=
bolthold
.
Open
(
path
,
0600
,
&
bolthold
.
Options
{
Options
:
&
bolt
.
Options
{
Timeout
:
1
*
time
.
Second
}})
if
err
!=
nil
{
return
nil
,
err
}
db
:=
&
DB
{
bolt
:
b
,
keystore
:
keystore
,
}
if
db
.
Enabled
()
{
if
err
=
db
.
loadCurrent
();
err
!=
nil
{
_
=
db
.
Close
()
return
nil
,
err
}
}
return
db
,
nil
}
func
(
rs
*
RevocationStorage
)
PrivateKey
(
issid
IssuerIdentifier
)
(
*
revocation
.
PrivateKey
,
error
)
{
pk
,
err
:=
rs
.
conf
.
PrivateKey
(
issid
)
if
err
!=
nil
{
return
nil
,
err
}
if
pk
==
nil
{
return
nil
,
errors
.
Errorf
(
"unknown public key: %s"
,
issid
)
}
revpk
,
err
:=
pk
.
RevocationKey
()
if
err
!=
nil
{
return
nil
,
err
}
return
revpk
,
nil
}
func
(
rs
*
RevocationStorage
)
PublicKey
(
issid
IssuerIdentifier
,
counter
uint
)
(
*
revocation
.
PublicKey
,
error
)
{
pk
,
err
:=
rs
.
conf
.
PublicKey
(
issid
,
int
(
counter
))