Commit 98271b20 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

refactor: move SQL database conversion methods from gabi to dedicated revocation types

parent 93fdbec3
...@@ -118,7 +118,7 @@ func (session *session) issuanceHandleRevocation( ...@@ -118,7 +118,7 @@ func (session *session) issuanceHandleRevocation(
issrecord := &irma.IssuanceRecord{ issrecord := &irma.IssuanceRecord{
CredType: id, CredType: id,
Key: cred.RevocationKey, Key: cred.RevocationKey,
Attr: nonrevAttr, Attr: (*irma.RevocationAttribute)(nonrevAttr),
Issued: time.Now().UnixNano(), // or (floored) cred issuance time? Issued: time.Now().UnixNano(), // or (floored) cred issuance time?
ValidUntil: attributes.Expiry().UnixNano(), ValidUntil: attributes.Expiry().UnixNano(),
} }
......
...@@ -405,7 +405,7 @@ func TestRevocation(t *testing.T) { ...@@ -405,7 +405,7 @@ func TestRevocation(t *testing.T) {
// client notices that his credential is revoked and aborts // client notices that his credential is revoked and aborts
logger.Info("step 5") logger.Info("step 5")
result = revocationSession(t, client, sessionOptionIgnoreClientError) result = revocationSession(t, client, sessionOptionIgnoreClientError)
require.Equal(t, result.Status, server.StatusCancelled) require.Equal(t, server.StatusCancelled, result.Status)
// client revocation callback was called // client revocation callback was called
require.NotNil(t, handler.(*TestClientHandler).revoked) require.NotNil(t, handler.(*TestClientHandler).revoked)
require.Equal(t, cred, handler.(*TestClientHandler).revoked.Type) require.Equal(t, cred, handler.(*TestClientHandler).revoked.Type)
......
package irma package irma
import ( import (
"database/sql/driver"
"fmt" "fmt"
"time" "time"
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres"
"github.com/privacybydesign/gabi" "github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/big" "github.com/privacybydesign/gabi/big"
"github.com/privacybydesign/gabi/revocation" "github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/gabi/signed" "github.com/privacybydesign/gabi/signed"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres"
) )
type ( type (
...@@ -59,23 +61,33 @@ type ( ...@@ -59,23 +61,33 @@ type (
RevocationMode string RevocationMode string
) )
// Structs corresponding to SQL table rows. All of them end in Record. // Structs corresponding to SQL table rows, ending in Record
type ( type (
// signedMessage is a signed.Message with DB (un)marshaling methods.
signedMessage signed.Message
// RevocationAttribute is a big.Int with DB (un)marshaling methods.
RevocationAttribute big.Int
// eventHash is a revocation.Hash with DB (un)marshaling methods.
eventHash revocation.Hash
AccumulatorRecord struct { AccumulatorRecord struct {
*revocation.SignedAccumulator `gorm:"embedded"` CredType CredentialTypeIdentifier `gorm:"primary_key"`
CredType CredentialTypeIdentifier `gorm:"primary_key"` Data signedMessage
PKIndex uint
} }
EventRecord struct { EventRecord struct {
*revocation.Event `gorm:"embedded"` Index uint64 `gorm:"primary_key;column:eventindex"`
CredType CredentialTypeIdentifier `gorm:"primary_key"` CredType CredentialTypeIdentifier `gorm:"primary_key"`
E *RevocationAttribute
ParentHash eventHash
} }
// IssuanceRecord contains information generated during issuance, needed for later revocation. // IssuanceRecord contains information generated during issuance, needed for later revocation.
IssuanceRecord struct { IssuanceRecord struct {
CredType CredentialTypeIdentifier `gorm:"primary_key"`
Key string `gorm:"primary_key;column:revocationkey"` Key string `gorm:"primary_key;column:revocationkey"`
Attr *big.Int CredType CredentialTypeIdentifier `gorm:"primary_key"`
Attr *RevocationAttribute
Issued int64 Issued int64
ValidUntil int64 ValidUntil int64
RevokedAt int64 // 0 if not currently revoked RevokedAt int64 // 0 if not currently revoked
...@@ -204,7 +216,7 @@ func (rs *RevocationStorage) UpdateLatest(typ CredentialTypeIdentifier, count ui ...@@ -204,7 +216,7 @@ func (rs *RevocationStorage) UpdateLatest(typ CredentialTypeIdentifier, count ui
func (*RevocationStorage) newUpdate(acc *revocation.SignedAccumulator, events []*EventRecord) *revocation.Update { func (*RevocationStorage) newUpdate(acc *revocation.SignedAccumulator, events []*EventRecord) *revocation.Update {
updates := make([]*revocation.Event, len(events)) updates := make([]*revocation.Event, len(events))
for i := range events { for i := range events {
updates[i] = events[i].Event updates[i] = events[i].Event()
} }
return &revocation.Update{ return &revocation.Update{
SignedAccumulator: acc, SignedAccumulator: acc,
...@@ -232,11 +244,11 @@ func (rs *RevocationStorage) addUpdate(tx revStorage, typ CredentialTypeIdentifi ...@@ -232,11 +244,11 @@ func (rs *RevocationStorage) addUpdate(tx revStorage, typ CredentialTypeIdentifi
if create { if create {
save = tx.Insert save = tx.Insert
} }
if err = save(&AccumulatorRecord{SignedAccumulator: update.SignedAccumulator, CredType: typ}); err != nil { if err = save(new(AccumulatorRecord).Convert(typ, update.SignedAccumulator)); err != nil {
return err return err
} }
for _, event := range update.Events { for _, event := range update.Events {
if err = tx.Insert(&EventRecord{Event: event, CredType: typ}); err != nil { if err = tx.Insert(new(EventRecord).Convert(typ, event)); err != nil {
return err return err
} }
} }
...@@ -295,7 +307,7 @@ func (rs *RevocationStorage) Revoke(typ CredentialTypeIdentifier, key string, sk ...@@ -295,7 +307,7 @@ func (rs *RevocationStorage) Revoke(typ CredentialTypeIdentifier, key string, sk
}) })
} }
func (rs *RevocationStorage) revokeAttr(tx revStorage, typ CredentialTypeIdentifier, sk *revocation.PrivateKey, e *big.Int) error { func (rs *RevocationStorage) revokeAttr(tx revStorage, typ CredentialTypeIdentifier, sk *revocation.PrivateKey, e *RevocationAttribute) error {
_, cur, err := rs.currentAccumulator(tx, typ) _, cur, err := rs.currentAccumulator(tx, typ)
if err != nil { if err != nil {
return err return err
...@@ -308,7 +320,7 @@ func (rs *RevocationStorage) revokeAttr(tx revStorage, typ CredentialTypeIdentif ...@@ -308,7 +320,7 @@ func (rs *RevocationStorage) revokeAttr(tx revStorage, typ CredentialTypeIdentif
return err return err
} }
update, err := cur.Remove(sk, e, parent.Event) update, err := cur.Remove(sk, (*big.Int)(e), parent.Event())
if err != nil { if err != nil {
return err return err
} }
...@@ -323,31 +335,33 @@ func (rs *RevocationStorage) revokeAttr(tx revStorage, typ CredentialTypeIdentif ...@@ -323,31 +335,33 @@ func (rs *RevocationStorage) revokeAttr(tx revStorage, typ CredentialTypeIdentif
func (rs *RevocationStorage) currentAccumulator(tx revStorage, typ CredentialTypeIdentifier) ( func (rs *RevocationStorage) currentAccumulator(tx revStorage, typ CredentialTypeIdentifier) (
*revocation.SignedAccumulator, *revocation.Accumulator, error, *revocation.SignedAccumulator, *revocation.Accumulator, error,
) { ) {
record := &AccumulatorRecord{}
var err error var err error
var sacc *revocation.SignedAccumulator
if rs.sqlMode { if rs.sqlMode {
record := &AccumulatorRecord{}
if err = tx.Last(typ, record); err != nil { if err = tx.Last(typ, record); err != nil {
if gorm.IsRecordNotFoundError(err) { if gorm.IsRecordNotFoundError(err) {
return nil, nil, nil return nil, nil, nil
} }
} }
sacc = record.SignedAccumulator()
} else { } else {
u := rs.memdb.Latest(typ, 0) u := rs.memdb.Latest(typ, 0)
if u == nil { if u == nil {
return nil, nil, nil return nil, nil, nil
} }
record.SignedAccumulator = u.SignedAccumulator sacc = u.SignedAccumulator
} }
pk, err := rs.Keys.PublicKey(typ.IssuerIdentifier(), record.PKIndex) pk, err := rs.Keys.PublicKey(typ.IssuerIdentifier(), sacc.PKIndex)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
acc, err := record.UnmarshalVerify(pk) acc, err := sacc.UnmarshalVerify(pk)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
return record.SignedAccumulator, acc, nil return sacc, acc, nil
} }
// Methods to update from remote revocation server // Methods to update from remote revocation server
...@@ -591,3 +605,108 @@ func (rs RevocationKeys) PublicKey(issid IssuerIdentifier, counter uint) (*revoc ...@@ -591,3 +605,108 @@ func (rs RevocationKeys) PublicKey(issid IssuerIdentifier, counter uint) (*revoc
} }
return revpk, nil return revpk, nil
} }
// Conversion methods to/from database structs, SQL table rows, gob
func (e *EventRecord) Event() *revocation.Event {
return &revocation.Event{
Index: e.Index,
E: (*big.Int)(e.E),
ParentHash: revocation.Hash(e.ParentHash),
}
}
func (e *EventRecord) Convert(typ CredentialTypeIdentifier, event *revocation.Event) *EventRecord {
*e = EventRecord{
Index: event.Index,
E: (*RevocationAttribute)(event.E),
ParentHash: eventHash(event.ParentHash),
CredType: typ,
}
return e
}
func (a *AccumulatorRecord) SignedAccumulator() *revocation.SignedAccumulator {
return &revocation.SignedAccumulator{
PKIndex: a.PKIndex,
Data: signed.Message(a.Data),
}
}
func (a *AccumulatorRecord) Convert(typ CredentialTypeIdentifier, sacc *revocation.SignedAccumulator) *AccumulatorRecord {
*a = AccumulatorRecord{
Data: signedMessage(sacc.Data),
PKIndex: sacc.PKIndex,
CredType: typ,
}
return a
}
func (signedMessage) GormDataType(dialect gorm.Dialect) string {
switch dialect.GetName() {
case "postgres":
return "bytea"
case "mysql":
return "blob"
default:
return ""
}
}
// Value implements driver.Valuer, for SQL marshaling (to []byte).
func (i *RevocationAttribute) Value() (driver.Value, error) {
return (*big.Int)(i).Bytes(), nil
}
// Scan implements sql.Scanner, for SQL unmarshaling (from a []byte).
func (i *RevocationAttribute) Scan(src interface{}) error {
b, ok := src.([]byte)
if !ok {
return errors.New("cannot convert source: not a byte slice")
}
(*big.Int)(i).SetBytes(b)
return nil
}
func (RevocationAttribute) GormDataType(dialect gorm.Dialect) string {
switch dialect.GetName() {
case "postgres":
return "bytea"
case "mysql":
return "blob"
default:
return ""
}
}
func (i *RevocationAttribute) GobEncode() ([]byte, error) {
return MarshalBinary((*big.Int)(i))
}
func (i *RevocationAttribute) GobDecode(data []byte) error {
return UnmarshalBinary(data, (*big.Int)(i))
}
func (hash eventHash) Value() (driver.Value, error) {
return hash[:], nil
}
func (hash *eventHash) Scan(src interface{}) error {
s, ok := src.([]byte)
if !ok {
return errors.New("cannot convert source: not a []byte")
}
copy((*hash)[:], s)
return nil
}
func (eventHash) GormDataType(dialect gorm.Dialect) string {
switch dialect.GetName() {
case "postgres":
return "bytea"
case "mysql":
return "blob"
default:
return ""
}
}
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