Commit ccee1b15 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

refactor: rename keysharecore user structs and related methods

parent 5a713d10
......@@ -25,44 +25,40 @@ var (
ErrUnknownCommit = errors.New("unknown commit id")
)
// NewUser generates a new keyshare secret, secured with the given pin.
func (c *Core) NewUser(pinRaw string) (User, error) {
// NewUserSecrets generates a new keyshare secret, secured with the given pin.
func (c *Core) NewUserSecrets(pinRaw string) (UserSecrets, error) {
secret, err := gabi.NewKeyshareSecret()
if err != nil {
return User{}, err
return UserSecrets{}, err
}
return c.newUserFromSecret(pinRaw, secret)
}
func (c *Core) newUserFromSecret(pinRaw string, secret *big.Int) (User, error) {
pin, err := padPin(pinRaw)
if err != nil {
return User{}, err
return UserSecrets{}, err
}
var id [32]byte
_, err = rand.Read(id[:])
if err != nil {
return User{}, err
return UserSecrets{}, err
}
// Build unencrypted packet
var p unencryptedUser
var p unencryptedUserSecrets
p.setPin(pin)
err = p.setKeyshareSecret(secret)
if err != nil {
return User{}, err
return UserSecrets{}, err
}
p.setID(id)
// And encrypt
return c.encryptUser(p)
return c.encryptUserSecrets(p)
}
// ValidatePin checks pin for validity and generates JWT for future access.
func (c *Core) ValidatePin(ep User, pin string) (string, error) {
p, err := c.decryptUserIfPinOK(ep, pin)
func (c *Core) ValidatePin(ep UserSecrets, pin string) (string, error) {
p, err := c.decryptUserSecretsIfPinOK(ep, pin)
if err != nil {
return "", err
}
......@@ -82,38 +78,38 @@ func (c *Core) ValidatePin(ep User, pin string) (string, error) {
// ValidateJWT checks whether the given JWT is currently valid as an access token for operations
// on the provided encrypted keyshare packet.
func (c *Core) ValidateJWT(ep User, jwt string) error {
func (c *Core) ValidateJWT(ep UserSecrets, jwt string) error {
_, err := c.verifyAccess(ep, jwt)
return err
}
// ChangePin changes the pin in an encrypted keyshare packet to a new value, after validating that
// the old value is known by the caller.
func (c *Core) ChangePin(ep User, oldpinRaw, newpinRaw string) (User, error) {
p, err := c.decryptUserIfPinOK(ep, oldpinRaw)
func (c *Core) ChangePin(ep UserSecrets, oldpinRaw, newpinRaw string) (UserSecrets, error) {
p, err := c.decryptUserSecretsIfPinOK(ep, oldpinRaw)
if err != nil {
return User{}, err
return UserSecrets{}, err
}
newpin, err := padPin(newpinRaw)
if err != nil {
return User{}, err
return UserSecrets{}, err
}
// change and reencrypt
var id [32]byte
_, err = rand.Read(id[:])
if err != nil {
return User{}, err
return UserSecrets{}, err
}
p.setPin(newpin)
p.setID(id)
return c.encryptUser(p)
return c.encryptUserSecrets(p)
}
// verifyAccess checks that a given access jwt is valid, and if so, return decrypted keyshare packet.
// Note: Although this is an internal function, it is tested directly
func (c *Core) verifyAccess(ep User, jwtToken string) (unencryptedUser, error) {
func (c *Core) verifyAccess(ep UserSecrets, jwtToken string) (unencryptedUserSecrets, error) {
// Verify token validity
token, err := jwt.Parse(jwtToken, func(token *jwt.Token) (interface{}, error) {
if token.Method != jwt.SigningMethodRS256 {
......@@ -123,43 +119,43 @@ func (c *Core) verifyAccess(ep User, jwtToken string) (unencryptedUser, error) {
return &c.jwtPrivateKey.PublicKey, nil
})
if err != nil {
return unencryptedUser{}, ErrInvalidJWT
return unencryptedUserSecrets{}, ErrInvalidJWT
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok || claims.Valid() != nil {
return unencryptedUser{}, ErrInvalidJWT
return unencryptedUserSecrets{}, ErrInvalidJWT
}
if !claims.VerifyExpiresAt(time.Now().Unix(), true) {
return unencryptedUser{}, ErrInvalidJWT
return unencryptedUserSecrets{}, ErrInvalidJWT
}
if _, present := claims["token_id"]; !present {
return unencryptedUser{}, ErrInvalidJWT
return unencryptedUserSecrets{}, ErrInvalidJWT
}
tokenIDB64, ok := claims["token_id"].(string)
if !ok {
return unencryptedUser{}, ErrInvalidJWT
return unencryptedUserSecrets{}, ErrInvalidJWT
}
tokenID, err := base64.StdEncoding.DecodeString(tokenIDB64)
if err != nil {
return unencryptedUser{}, ErrInvalidJWT
return unencryptedUserSecrets{}, ErrInvalidJWT
}
p, err := c.decryptUser(ep)
p, err := c.decryptUserSecrets(ep)
if err != nil {
return unencryptedUser{}, err
return unencryptedUserSecrets{}, err
}
refId := p.id()
if subtle.ConstantTimeCompare(refId[:], tokenID) != 1 {
return unencryptedUser{}, ErrInvalidJWT
return unencryptedUserSecrets{}, ErrInvalidJWT
}
return p, nil
}
// GenerateCommitments generates keyshare commitments using the specified Idemix public key(s).
func (c *Core) GenerateCommitments(ep User, accessToken string, keyIDs []irma.PublicKeyIdentifier) ([]*gabi.ProofPCommitment, uint64, error) {
func (c *Core) GenerateCommitments(ep UserSecrets, accessToken string, keyIDs []irma.PublicKeyIdentifier) ([]*gabi.ProofPCommitment, uint64, error) {
// Validate input request and build key list
var keyList []*gabikeys.PublicKey
for _, keyID := range keyIDs {
......@@ -198,7 +194,7 @@ func (c *Core) GenerateCommitments(ep User, accessToken string, keyIDs []irma.Pu
}
// GenerateResponse generates the response of a zero-knowledge proof of the keyshare secret, for a given previous commit and challenge.
func (c *Core) GenerateResponse(ep User, accessToken string, commitID uint64, challenge *big.Int, keyID irma.PublicKeyIdentifier) (string, error) {
func (c *Core) GenerateResponse(ep UserSecrets, accessToken string, commitID uint64, challenge *big.Int, keyID irma.PublicKeyIdentifier) (string, error) {
// Validate request
if uint(challenge.BitLen()) > gabikeys.DefaultSystemParameters[1024].Lh || challenge.Cmp(big.NewInt(0)) < 0 {
return "", ErrInvalidChallenge
......
......@@ -33,7 +33,7 @@ func TestPinFunctionality(t *testing.T) {
pin := string(bpin[:])
// Generate package
ep, err := c.NewUser(pin)
ep, err := c.NewUserSecrets(pin)
require.NoError(t, err)
// Test with correct pin
......@@ -82,9 +82,9 @@ func TestVerifyAccess(t *testing.T) {
pin2 := string(bpin[:])
// and test keyshare secrets
ep1, err := c.NewUser(pin1)
ep1, err := c.NewUserSecrets(pin1)
require.NoError(t, err)
ep2, err := c.NewUser(pin2)
ep2, err := c.NewUserSecrets(pin2)
require.NoError(t, err)
// Test use jwt on wrong packet
......@@ -179,7 +179,7 @@ func TestProofFunctionality(t *testing.T) {
pin := string(bpin[:])
// generate keyshare secret
ep, err := c.NewUser(pin)
ep, err := c.NewUserSecrets(pin)
require.NoError(t, err)
// Validate pin
......@@ -229,7 +229,7 @@ func TestCorruptedPacket(t *testing.T) {
pin := string(bpin[:])
// Generate packet
ep, err := c.NewUser(pin)
ep, err := c.NewUserSecrets(pin)
require.NoError(t, err)
jwtt, err := c.ValidatePin(ep, pin)
......@@ -273,7 +273,7 @@ func TestIncorrectPin(t *testing.T) {
pin := string(bpin[:])
// Generate packet
ep, err := c.NewUser(pin)
ep, err := c.NewUserSecrets(pin)
require.NoError(t, err)
// validate pin
......@@ -310,7 +310,7 @@ func TestMissingKey(t *testing.T) {
pin := string(bpin[:])
// Generate packet
ep, err := c.NewUser(pin)
ep, err := c.NewUserSecrets(pin)
require.NoError(t, err)
// Generate jwt
......@@ -343,7 +343,7 @@ func TestInvalidChallenge(t *testing.T) {
pin := string(bpin[:])
// Generate packet
ep, err := c.NewUser(pin)
ep, err := c.NewUserSecrets(pin)
require.NoError(t, err)
// Validate pin
......@@ -384,7 +384,7 @@ func TestDoubleCommitUse(t *testing.T) {
pin := string(bpin[:])
// Generate packet
ep, err := c.NewUser(pin)
ep, err := c.NewUserSecrets(pin)
require.NoError(t, err)
// validate pin
......@@ -415,7 +415,7 @@ func TestNonExistingCommit(t *testing.T) {
pin := string(bpin[:])
// Generate packet
ep, err := c.NewUser(pin)
ep, err := c.NewUserSecrets(pin)
require.NoError(t, err)
// Generate jwt
......
......@@ -18,11 +18,11 @@ type (
// encryption layer applied before storing it. As such, we keep it here more explicit than
// is standard in go. When modifying this structure, analyse whether such changes can have a
// security impact through error side channels.
unencryptedUser [64 + 64 + 32]byte
unencryptedUserSecrets [64 + 64 + 32]byte
// User contains the encrypted data of a keyshare user.
// The size is that of unencryptedUser + 12 bytes for nonce + 16 bytes for tag + 4 bytes for key ID.
User [64 + 64 + 32 + 12 + 16 + 4]byte
// UserSecrets contains the encrypted data of a keyshare user.
// The size is that of unencryptedUserSecrets + 12 bytes for nonce + 16 bytes for tag + 4 bytes for key ID.
UserSecrets [64 + 64 + 32 + 12 + 16 + 4]byte
)
var (
......@@ -31,22 +31,22 @@ var (
ErrNoSuchKey = errors.New("Key identifier unknown")
)
func (p *unencryptedUser) pin() [64]byte {
func (p *unencryptedUserSecrets) pin() [64]byte {
var result [64]byte
copy(result[:], p[0:64])
return result
}
func (p *unencryptedUser) setPin(pw [64]byte) {
func (p *unencryptedUserSecrets) setPin(pw [64]byte) {
copy(p[0:64], pw[:])
}
func (p *unencryptedUser) keyshareSecret() *big.Int {
func (p *unencryptedUserSecrets) keyshareSecret() *big.Int {
result := new(big.Int)
return result.SetBytes(p[64:128])
}
func (p *unencryptedUser) setKeyshareSecret(val *big.Int) error {
func (p *unencryptedUserSecrets) setKeyshareSecret(val *big.Int) error {
if val.Sign() == -1 {
return ErrKeyshareSecretNegative
}
......@@ -64,18 +64,18 @@ func (p *unencryptedUser) setKeyshareSecret(val *big.Int) error {
return nil
}
func (p *unencryptedUser) id() [32]byte {
func (p *unencryptedUserSecrets) id() [32]byte {
var result [32]byte
copy(result[:], p[128:160])
return result
}
func (p *unencryptedUser) setID(id [32]byte) {
func (p *unencryptedUserSecrets) setID(id [32]byte) {
copy(p[128:160], id[:])
}
func (c *Core) encryptUser(p unencryptedUser) (User, error) {
var result User
func (c *Core) encryptUserSecrets(p unencryptedUserSecrets) (UserSecrets, error) {
var result UserSecrets
// Store key id
binary.LittleEndian.PutUint32(result[0:], c.decryptionKeyID)
......@@ -83,56 +83,56 @@ func (c *Core) encryptUser(p unencryptedUser) (User, error) {
// Generate and store nonce
_, err := rand.Read(result[4:16])
if err != nil {
return User{}, err
return UserSecrets{}, err
}
// Encrypt packet
gcm, err := newGCM(c.decryptionKey)
if err != nil {
return User{}, err
return UserSecrets{}, err
}
gcm.Seal(result[:16], result[4:16], p[:], nil)
return result, nil
}
func (c *Core) decryptUser(p User) (unencryptedUser, error) {
func (c *Core) decryptUserSecrets(p UserSecrets) (unencryptedUserSecrets, error) {
// determine key id
id := binary.LittleEndian.Uint32(p[0:])
// Fetch key
key, ok := c.decryptionKeys[id]
if !ok {
return unencryptedUser{}, ErrNoSuchKey
return unencryptedUserSecrets{}, ErrNoSuchKey
}
// try and decrypt packet
gcm, err := newGCM(key)
if err != nil {
return unencryptedUser{}, err
return unencryptedUserSecrets{}, err
}
var result unencryptedUser
var result unencryptedUserSecrets
_, err = gcm.Open(result[:0], p[4:16], p[16:], nil)
if err != nil {
return unencryptedUser{}, err
return unencryptedUserSecrets{}, err
}
return result, nil
}
func (c *Core) decryptUserIfPinOK(ep User, pin string) (unencryptedUser, error) {
func (c *Core) decryptUserSecretsIfPinOK(ep UserSecrets, pin string) (unencryptedUserSecrets, error) {
paddedPin, err := padPin(pin)
if err != nil {
return unencryptedUser{}, err
return unencryptedUserSecrets{}, err
}
p, err := c.decryptUser(ep)
p, err := c.decryptUserSecrets(ep)
if err != nil {
return unencryptedUser{}, err
return unencryptedUserSecrets{}, err
}
refPin := p.pin()
if subtle.ConstantTimeCompare(refPin[:], paddedPin[:]) != 1 {
return unencryptedUser{}, ErrInvalidPin
return unencryptedUserSecrets{}, ErrInvalidPin
}
return p, nil
}
......
......@@ -15,7 +15,7 @@ func TestPacketAccess(t *testing.T) {
_, err := rand.Read(testPassword[:])
require.NoError(t, err)
var p unencryptedUser
var p unencryptedUserSecrets
p.setPin(testPassword)
err = p.setKeyshareSecret(testSecret)
require.NoError(t, err)
......@@ -37,15 +37,15 @@ func TestPacketEncryptDecrypt(t *testing.T) {
require.NoError(t, err)
// Create and encrypt packet
var p_before unencryptedUser
var p_before unencryptedUserSecrets
p_before.setPin(testPassword)
err = p_before.setKeyshareSecret(testSecret)
require.NoError(t, err)
p_encypted, err := c.encryptUser(p_before)
p_encypted, err := c.encryptUserSecrets(p_before)
require.NoError(t, err)
// Decrypt and test values
p_after, err := c.decryptUser(p_encypted)
p_after, err := c.decryptUserSecrets(p_encypted)
require.NoError(t, err)
assert.Equal(t, testPassword, p_after.pin(), "passwords don't match")
assert.Equal(t, 0, p_after.keyshareSecret().Cmp(testSecret), "keyshare secrets don't match")
......@@ -65,17 +65,17 @@ func TestPacketAuthentication(t *testing.T) {
require.NoError(t, err)
// Create and encrypt packet
var p_before unencryptedUser
var p_before unencryptedUserSecrets
p_before.setPin(testPassword)
err = p_before.setKeyshareSecret(testSecret)
require.NoError(t, err)
p_encrypted, err := c.encryptUser(p_before)
p_encrypted, err := c.encryptUserSecrets(p_before)
require.NoError(t, err)
// Modify encrypted packet and check that it no longer decrypts
p_encrypted[33] = 0
p_encrypted[34] = 15
_, err = c.decryptUser(p_encrypted)
_, err = c.decryptUserSecrets(p_encrypted)
assert.Error(t, err, "Tampering not detected")
}
......@@ -96,7 +96,7 @@ func TestMultiKey(t *testing.T) {
require.NoError(t, err)
// Create packet
var p_before unencryptedUser
var p_before unencryptedUserSecrets
p_before.setPin(testPassword)
err = p_before.setKeyshareSecret(testSecret)
require.NoError(t, err)
......@@ -104,27 +104,27 @@ func TestMultiKey(t *testing.T) {
// encrypt with key 1
c.decryptionKeyID = 1
c.decryptionKey = c.decryptionKeys[c.decryptionKeyID]
e1, err := c.encryptUser(p_before)
e1, err := c.encryptUserSecrets(p_before)
require.NoError(t, err)
// encrypt with key 2
c.decryptionKeyID = 2
c.decryptionKey = c.decryptionKeys[c.decryptionKeyID]
e2, err := c.encryptUser(p_before)
e2, err := c.encryptUserSecrets(p_before)
require.NoError(t, err)
// Check e1
p_after, err := c.decryptUser(e1)
p_after, err := c.decryptUserSecrets(e1)
assert.NoError(t, err)
assert.Equal(t, p_before, p_after, "packet mismatch on key 1")
// Check e2
p_after, err = c.decryptUser(e2)
p_after, err = c.decryptUserSecrets(e2)
assert.NoError(t, err)
assert.Equal(t, p_before, p_after, "packet mismatch on key 2")
// check that unknown key is detected correctly
delete(c.decryptionKeys, 1)
_, err = c.decryptUser(e1)
_, err = c.decryptUserSecrets(e1)
assert.Error(t, err, "Missing decryption key not detected.")
}
......@@ -24,16 +24,16 @@ func StartKeyshareServer(t *testing.T, l *logrus.Logger) {
db := keyshareserver.NewMemoryDB()
err := db.AddUser(&keyshareserver.User{
Username: "",
UserData: keysharecore.User{},
Secrets: keysharecore.UserSecrets{},
})
require.NoError(t, err)
var ep keysharecore.User
var ep keysharecore.UserSecrets
p, err := base64.StdEncoding.DecodeString("YWJjZK4w5SC+7D4lDrhiJGvB1iwxSeF90dGGPoGqqG7g3ivbfHibOdkKoOTZPbFlttBzn2EJgaEsL24Re8OWWWw5pd31/GCd14RXcb9Wy2oWhbr0pvJDLpIxXZt/qiQC0nJiIAYWLGZOdj5o0irDfqP1CSfw3IoKkVEl4lHRj0LCeINJIOpEfGlFtl4DHlWu8SMQFV1AIm3Gv64XzGncdkclVd41ti7cicBrcK8N2u9WvY/jCS4/Lxa2syp/O4IY")
require.NoError(t, err)
copy(ep[:], p)
err = db.AddUser(&keyshareserver.User{
Username: "testusername",
UserData: ep,
Secrets: ep,
})
require.NoError(t, err)
......
......@@ -56,6 +56,6 @@ type DB interface {
type User struct {
Username string
Language string
UserData keysharecore.User
Secrets keysharecore.UserSecrets
id int64
}
......@@ -13,11 +13,11 @@ import (
type memoryDB struct {
sync.Mutex
users map[string]keysharecore.User
users map[string]keysharecore.UserSecrets
}
func NewMemoryDB() DB {
return &memoryDB{users: map[string]keysharecore.User{}}
return &memoryDB{users: map[string]keysharecore.UserSecrets{}}
}
func (db *memoryDB) user(username string) (*User, error) {
......@@ -26,11 +26,11 @@ func (db *memoryDB) user(username string) (*User, error) {
defer db.Unlock()
// Check and fetch user data
data, ok := db.users[username]
secrets, ok := db.users[username]
if !ok {
return nil, keyshare.ErrUserNotFound
}
return &User{Username: username, UserData: data}, nil
return &User{Username: username, Secrets: secrets}, nil
}
func (db *memoryDB) AddUser(user *User) error {
......@@ -43,7 +43,7 @@ func (db *memoryDB) AddUser(user *User) error {
if exists {
return errUserAlreadyExists
}
db.users[user.Username] = user.UserData
db.users[user.Username] = user.Secrets
return nil
}
......@@ -57,7 +57,7 @@ func (db *memoryDB) updateUser(user *User) error {
if !exists {
return keyshare.ErrUserNotFound
}
db.users[user.Username] = user.UserData
db.users[user.Username] = user.Secrets
return nil
}
......
......@@ -43,7 +43,7 @@ func (db *postgresDB) AddUser(user *User) error {
res, err := db.db.Query("INSERT INTO irma.users (username, language, coredata, last_seen, pin_counter, pin_block_date) VALUES ($1, $2, $3, $4, 0, 0) RETURNING id",
user.Username,
user.Language,
user.UserData[:],
user.Secrets[:],
time.Now().Unix())
if err != nil {
return err
......@@ -75,10 +75,10 @@ func (db *postgresDB) user(username string) (*User, error) {
if err != nil {
return nil, err
}
if len(ep) != len(result.UserData[:]) {
if len(ep) != len(result.Secrets[:]) {
return nil, errInvalidRecord
}
copy(result.UserData[:], ep)
copy(result.Secrets[:], ep)
return &result, nil
}
......@@ -87,7 +87,7 @@ func (db *postgresDB) updateUser(user *User) error {
"UPDATE irma.users SET username = $1, language = $2, coredata = $3 WHERE id=$4",
user.Username,
user.Language,
user.UserData[:],
user.Secrets[:],
user.id,
)
}
......
......@@ -177,7 +177,7 @@ func (s *Server) handleCommitments(w http.ResponseWriter, r *http.Request) {
func (s *Server) generateCommitments(user *User, authorization string, keys []irma.PublicKeyIdentifier) (*irma.ProofPCommitmentMap, error) {
// Generate commitments
commitments, commitID, err := s.core.GenerateCommitments(user.UserData, authorization, keys)
commitments, commitID, err := s.core.GenerateCommitments(user.Secrets, authorization, keys)
if err != nil {
s.conf.Logger.WithField("error", err).Warn("Could not generate commitments for request")
return nil, err
......@@ -261,7 +261,7 @@ func (s *Server) generateResponses(user *User, authorization string, challenge *
return "", err
}
proofResponse, err := s.core.GenerateResponse(user.UserData, authorization, commitID, challenge, keyID)
proofResponse, err := s.core.GenerateResponse(user.Secrets, authorization, commitID, challenge, keyID)
if err != nil {
s.conf.Logger.WithField("error", err).Error("Could not generate response for request")
return "", err
......@@ -318,7 +318,7 @@ func (s *Server) verifyPin(user *User, pin string) (irma.KeysharePinStatus, erro
}
// At this point, we are allowed to do an actual check (we have successfully reserved a spot for it), so do it.
jwtt, err := s.core.ValidatePin(user.UserData, pin)
jwtt, err := s.core.ValidatePin(user.Secrets, pin)
if err != nil && err != keysharecore.ErrInvalidPin {
// Errors other than invalid pin are real errors
s.conf.Logger.WithField("error", err).Error("Could not validate pin")
......@@ -401,7 +401,7 @@ func (s *Server) updatePin(user *User, oldPin, newPin string) (irma.KeysharePinS
}
// Try to do the update
user.UserData, err = s.core.ChangePin(user.UserData, oldPin, newPin)
user.Secrets, err = s.core.ChangePin(user.Secrets, oldPin, newPin)