Skip to content
GitLab
Menu
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
e516be97
Commit
e516be97
authored
Jan 22, 2020
by
David Venhoek
Committed by
Sietse Ringers
Dec 11, 2020
Browse files
Initial version of postgres database connection for keyshare server.
parent
35e1aa5a
Changes
4
Hide whitespace changes
Inline
Side-by-side
keyshareServerCore/conf.go
View file @
e516be97
...
...
@@ -16,6 +16,15 @@ import (
"github.com/sirupsen/logrus"
)
type
DatabaseType
string
var
ErrUnknownDatabaseType
=
errors
.
New
(
"Unknown database type"
)
const
(
DatabaseTypeMemory
=
"memory"
DatabaseTypePostgres
=
"postgres"
)
// Configuration contains configuration for the irmaserver library and irmad.
type
Configuration
struct
{
// Irma server configuration. If not given, this will be populated using information here
...
...
@@ -40,6 +49,12 @@ type Configuration struct {
// ensure (using eg a reverse proxy with TLS enabled) that the attributes are protected in transit.
DisableTLS
bool
`json:"no_tls" mapstructure:"no_tls"`
// Database configuration (ignored when database is provided)
DbType
DatabaseType
`json:"db_type" mapstructure:"db_type"`
DbConnstring
string
`json:"db_connstring" mapstructure:"db_connstring"`
// Provide a prepared database (useful for testing)
DB
KeyshareDB
`json:"-"`
// Configuration of secure Core
// Private key used to sign JWTs with
JwtKeyId
int
`json:"jwt_key_id" mapstructure:"jwt_key_id"`
...
...
@@ -140,6 +155,22 @@ func processConfiguration(conf *Configuration) (*keyshareCore.KeyshareCore, erro
}
}
// Setup database
if
conf
.
DB
==
nil
{
switch
conf
.
DbType
{
case
DatabaseTypeMemory
:
conf
.
DB
=
NewMemoryDatabase
()
case
DatabaseTypePostgres
:
var
err
error
conf
.
DB
,
err
=
NewPostgresDatabase
(
conf
.
DbConnstring
)
if
err
!=
nil
{
return
nil
,
server
.
LogError
(
err
)
}
default
:
return
nil
,
server
.
LogError
(
ErrUnknownDatabaseType
)
}
}
// Setup server urls
if
!
strings
.
HasSuffix
(
conf
.
URL
,
"/"
)
{
conf
.
URL
=
conf
.
URL
+
"/"
...
...
keyshareServerCore/db.go
View file @
e516be97
package
keyshareServerCore
import
(
"database/sql"
"encoding/base64"
"errors"
"sync"
"time"
"github.com/privacybydesign/irmago/keyshareCore"
_
"github.com/jackc/pgx/stdlib"
)
var
(
ErrUserAlreadyExists
=
errors
.
New
(
"Cannot create user, username already taken"
)
ErrUserNotFound
=
errors
.
New
(
"Could not find specified user"
)
ErrInvalidData
=
errors
.
New
(
"Invalid user datastructure passed"
)
)
type
KeyshareDB
interface
{
NewUser
(
user
*
KeyshareUser
)
error
User
(
username
string
)
(
*
KeyshareUser
,
error
)
UpdateUser
(
user
*
KeyshareUser
)
error
NewUser
(
user
KeyshareUser
Data
)
error
User
(
username
string
)
(
KeyshareUser
,
error
)
UpdateUser
(
user
KeyshareUser
)
error
// Reserve returns (allow, tries, wait, error)
ReservePincheck
(
user
*
KeyshareUser
)
(
bool
,
int
,
int
,
error
)
ClearPincheck
(
user
*
KeyshareUser
)
error
ReservePincheck
(
user
KeyshareUser
)
(
bool
,
int
,
int64
,
error
)
ClearPincheck
(
user
KeyshareUser
)
error
}
type
KeyshareUser
interface
{
Data
()
*
KeyshareUserData
}
type
KeyshareUser
struct
{
type
KeyshareUser
Data
struct
{
Username
string
Coredata
keyshareCore
.
EncryptedKeysharePacket
}
...
...
@@ -32,11 +42,19 @@ type keyshareMemoryDB struct {
users
map
[
string
]
keyshareCore
.
EncryptedKeysharePacket
}
type
keyshareMemoryUser
struct
{
KeyshareUserData
}
func
(
m
*
keyshareMemoryUser
)
Data
()
*
KeyshareUserData
{
return
&
m
.
KeyshareUserData
}
func
NewMemoryDatabase
()
KeyshareDB
{
return
&
keyshareMemoryDB
{
users
:
map
[
string
]
keyshareCore
.
EncryptedKeysharePacket
{}}
}
func
(
db
*
keyshareMemoryDB
)
User
(
username
string
)
(
*
KeyshareUser
,
error
)
{
func
(
db
*
keyshareMemoryDB
)
User
(
username
string
)
(
KeyshareUser
,
error
)
{
// Ensure access to database is single-threaded
db
.
lock
.
Lock
()
defer
db
.
lock
.
Unlock
()
...
...
@@ -46,10 +64,10 @@ func (db *keyshareMemoryDB) User(username string) (*KeyshareUser, error) {
if
!
ok
{
return
nil
,
ErrUserNotFound
}
return
&
KeyshareUser
{
Username
:
username
,
Coredata
:
data
},
nil
return
&
keyshareMemoryUser
{
KeyshareUser
Data
{
Username
:
username
,
Coredata
:
data
}
}
,
nil
}
func
(
db
*
keyshareMemoryDB
)
NewUser
(
user
*
KeyshareUser
)
error
{
func
(
db
*
keyshareMemoryDB
)
NewUser
(
user
KeyshareUser
Data
)
error
{
// Ensure access to database is single-threaded
db
.
lock
.
Lock
()
defer
db
.
lock
.
Unlock
()
...
...
@@ -63,26 +81,198 @@ func (db *keyshareMemoryDB) NewUser(user *KeyshareUser) error {
return
nil
}
func
(
db
*
keyshareMemoryDB
)
UpdateUser
(
user
*
KeyshareUser
)
error
{
func
(
db
*
keyshareMemoryDB
)
UpdateUser
(
user
KeyshareUser
)
error
{
userdata
,
ok
:=
user
.
(
*
keyshareMemoryUser
)
if
!
ok
{
return
ErrInvalidData
}
// Ensure access to database is single-threaded
db
.
lock
.
Lock
()
defer
db
.
lock
.
Unlock
()
// Check and update user.
_
,
exists
:=
db
.
users
[
user
.
Username
]
_
,
exists
:=
db
.
users
[
user
data
.
Username
]
if
!
exists
{
return
ErrUserNotFound
}
db
.
users
[
user
.
Username
]
=
user
.
Coredata
db
.
users
[
user
data
.
Username
]
=
user
data
.
Coredata
return
nil
}
func
(
db
*
keyshareMemoryDB
)
ReservePincheck
(
user
*
KeyshareUser
)
(
bool
,
int
,
int
,
error
)
{
func
(
db
*
keyshareMemoryDB
)
ReservePincheck
(
user
KeyshareUser
)
(
bool
,
int
,
int
64
,
error
)
{
// Since this is a testing DB, implementing anything more than always allow creates hastle
return
false
,
1
,
0
,
nil
}
func
(
db
*
keyshareMemoryDB
)
ClearPincheck
(
user
*
KeyshareUser
)
error
{
func
(
db
*
keyshareMemoryDB
)
ClearPincheck
(
user
KeyshareUser
)
error
{
// Since this is a testing DB, implementing anything more than always allow creates hastle
return
nil
}
type
keysharePostgresDatabase
struct
{
db
*
sql
.
DB
}
type
keysharePostgresUser
struct
{
KeyshareUserData
id
int
}
func
(
m
*
keysharePostgresUser
)
Data
()
*
KeyshareUserData
{
return
&
m
.
KeyshareUserData
}
const
MAX_PIN_TRIES
=
3
const
BACKOFF_START
=
30
func
NewPostgresDatabase
(
connstring
string
)
(
KeyshareDB
,
error
)
{
db
,
err
:=
sql
.
Open
(
"pgx"
,
connstring
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
keysharePostgresDatabase
{
db
:
db
,
},
nil
}
func
(
db
*
keysharePostgresDatabase
)
NewUser
(
user
KeyshareUserData
)
error
{
ep
:=
base64
.
StdEncoding
.
EncodeToString
(
user
.
Coredata
[
:
])
res
,
err
:=
db
.
db
.
Exec
(
"INSERT INTO irma.users (username, coredata) VALUES (?, ?)"
,
user
.
Username
,
ep
)
if
err
!=
nil
{
return
err
}
c
,
err
:=
res
.
RowsAffected
()
if
err
!=
nil
{
return
err
}
if
c
==
0
{
return
ErrUserAlreadyExists
}
return
nil
}
func
(
db
*
keysharePostgresDatabase
)
User
(
username
string
)
(
KeyshareUser
,
error
)
{
rows
,
err
:=
db
.
db
.
Query
(
"SELECT id, username, coredata FROM irma.users WHERE username = ?"
,
username
)
if
err
!=
nil
{
return
nil
,
err
}
defer
rows
.
Close
()
if
!
rows
.
Next
()
{
return
nil
,
ErrUserNotFound
}
var
result
keysharePostgresUser
var
epEnc
string
err
=
rows
.
Scan
(
&
result
.
id
,
&
result
.
Username
,
epEnc
)
if
err
!=
nil
{
return
nil
,
err
}
ep
,
err
:=
base64
.
StdEncoding
.
DecodeString
(
epEnc
)
if
err
!=
nil
{
return
nil
,
err
}
if
len
(
ep
)
!=
len
(
result
.
Coredata
[
:
])
{
return
nil
,
ErrInvalidData
}
copy
(
result
.
Coredata
[
:
],
ep
)
return
&
result
,
nil
}
func
(
db
*
keysharePostgresDatabase
)
UpdateUser
(
user
KeyshareUser
)
error
{
userdata
,
ok
:=
user
.
(
*
keysharePostgresUser
)
if
!
ok
{
return
ErrInvalidData
}
ep
:=
base64
.
StdEncoding
.
EncodeToString
(
userdata
.
Coredata
[
:
])
res
,
err
:=
db
.
db
.
Exec
(
"UPDATE irma.users SET username=?, coredata=? WHERE id=?"
,
userdata
.
Username
,
ep
,
userdata
.
id
)
if
err
!=
nil
{
return
err
}
c
,
err
:=
res
.
RowsAffected
()
if
err
!=
nil
{
return
err
}
if
c
==
0
{
return
ErrUserNotFound
}
return
nil
}
func
(
db
*
keysharePostgresDatabase
)
ReservePincheck
(
user
KeyshareUser
)
(
bool
,
int
,
int64
,
error
)
{
// Extract data
userdata
,
ok
:=
user
.
(
*
keysharePostgresUser
)
if
!
ok
{
return
false
,
0
,
0
,
ErrInvalidData
}
// Check that account is not blocked already, and if not,
// update pinCounter and pinBlockDate
uprows
,
err
:=
db
.
db
.
Query
(
`
UPDATE irma.users
SET pinCounter = pinCounter+1,
pinBlockDate = ?+?*2^MIN(0, pinCounter-?)
WHERE id=?, pinBlockDate<=?
RETURNING pinCounter, pinBlockDate`
,
time
.
Now
()
.
Unix
()
/
1000000000
-
1
-
BACKOFF_START
,
// Grace time of 2 seconds on pinBlockDate set
BACKOFF_START
,
MAX_PIN_TRIES
-
2
,
userdata
.
id
,
time
.
Now
()
.
Unix
()
/
1000000000
)
if
err
!=
nil
{
return
false
,
0
,
0
,
err
}
defer
uprows
.
Close
()
// Check whether we have results
if
!
uprows
.
Next
()
{
// if no, then account either does not exist (which would be weird here) or is blocked
// so request wait timeout
pinrows
,
err
:=
db
.
db
.
Query
(
"SELECT pinBlockDate FROM irma.users WHERE id=?"
,
userdata
.
id
)
if
err
!=
nil
{
return
false
,
0
,
0
,
err
}
defer
pinrows
.
Close
()
if
!
pinrows
.
Next
()
{
return
false
,
0
,
0
,
ErrUserNotFound
}
var
wait
int64
err
=
pinrows
.
Scan
(
&
wait
)
if
err
!=
nil
{
return
false
,
0
,
0
,
err
}
return
false
,
0
,
wait
-
time
.
Now
()
.
Unix
()
/
1000000000
,
nil
}
// Pin check is allowed (implied since there is a result, so pinBlockDate <= now)
// calculate tries remaining and wait time
var
tries
int
var
wait
int64
err
=
uprows
.
Scan
(
&
tries
,
&
wait
)
if
err
!=
nil
{
return
false
,
0
,
0
,
err
}
tries
=
MAX_PIN_TRIES
-
tries
if
tries
<
0
{
tries
=
0
}
return
true
,
tries
,
wait
-
time
.
Now
()
.
Unix
()
/
1000000000
,
nil
}
func
(
db
*
keysharePostgresDatabase
)
ClearPincheck
(
user
KeyshareUser
)
error
{
userdata
,
ok
:=
user
.
(
*
keysharePostgresUser
)
if
!
ok
{
return
ErrInvalidData
}
res
,
err
:=
db
.
db
.
Exec
(
"UPDATE irma.users SET pinCounter=0, pinBlockDate=0 WHERE id=?"
,
userdata
.
id
)
if
err
!=
nil
{
return
err
}
c
,
err
:=
res
.
RowsAffected
()
if
err
!=
nil
{
return
err
}
if
c
==
0
{
return
ErrUserNotFound
}
return
nil
}
keyshareServerCore/server.go
View file @
e516be97
...
...
@@ -65,8 +65,8 @@ func New(conf *Configuration) (*Server, error) {
return
nil
,
err
}
// Setup DB
(TODO: make configurable)
s
.
db
=
NewMemoryDatabase
()
// Setup DB
s
.
db
=
conf
.
DB
return
s
,
nil
}
...
...
@@ -138,7 +138,7 @@ func (s *Server) handleCommitments(w http.ResponseWriter, r *http.Request) {
}
// Generate commitments
commitments
,
commitId
,
err
:=
s
.
core
.
GenerateCommitments
(
user
.
Coredata
,
authorization
,
keys
)
commitments
,
commitId
,
err
:=
s
.
core
.
GenerateCommitments
(
user
.
Data
()
.
Coredata
,
authorization
,
keys
)
if
err
!=
nil
{
s
.
conf
.
Logger
.
WithField
(
"error"
,
err
)
.
Warn
(
"Could not generate commitments for request"
)
server
.
WriteError
(
w
,
server
.
ErrorInvalidRequest
,
err
.
Error
())
...
...
@@ -199,7 +199,7 @@ func (s *Server) handleResponse(w http.ResponseWriter, r *http.Request) {
}
// verify access (avoids leaking information to unauthorized callers)
err
=
s
.
core
.
ValidateJWT
(
user
.
Coredata
,
authorization
)
err
=
s
.
core
.
ValidateJWT
(
user
.
Data
()
.
Coredata
,
authorization
)
if
err
!=
nil
{
s
.
conf
.
Logger
.
WithField
(
"error"
,
err
)
.
Warn
(
"Could not generate keyshare response"
)
server
.
WriteError
(
w
,
server
.
ErrorInvalidRequest
,
err
.
Error
())
...
...
@@ -216,7 +216,7 @@ func (s *Server) handleResponse(w http.ResponseWriter, r *http.Request) {
return
}
proofResponse
,
err
:=
s
.
core
.
GenerateResponse
(
user
.
Coredata
,
authorization
,
sessionData
.
LastCommitID
,
challenge
,
sessionData
.
LastKeyid
)
proofResponse
,
err
:=
s
.
core
.
GenerateResponse
(
user
.
Data
()
.
Coredata
,
authorization
,
sessionData
.
LastCommitID
,
challenge
,
sessionData
.
LastKeyid
)
if
err
!=
nil
{
s
.
conf
.
Logger
.
WithField
(
"error"
,
err
)
.
Error
(
"Could not generate response for request"
)
server
.
WriteError
(
w
,
server
.
ErrorInvalidRequest
,
err
.
Error
())
...
...
@@ -240,7 +240,7 @@ func (s *Server) handleValidate(w http.ResponseWriter, r *http.Request) {
}
// Validate jwt
err
=
s
.
core
.
ValidateJWT
(
user
.
Coredata
,
authorization
)
err
=
s
.
core
.
ValidateJWT
(
user
.
Data
()
.
Coredata
,
authorization
)
if
err
!=
nil
{
server
.
WriteJson
(
w
,
&
keyshareAuthorization
{
Status
:
"expired"
,
Candidates
:
[]
string
{
"pin"
}})
}
else
{
...
...
@@ -283,7 +283,7 @@ func (s *Server) handleVerifyPin(w http.ResponseWriter, r *http.Request) {
server
.
WriteJson
(
w
,
keysharePinStatus
{
Status
:
"error"
,
Message
:
fmt
.
Sprintf
(
"%v"
,
wait
)})
return
}
jwtt
,
err
:=
s
.
core
.
ValidatePin
(
user
.
Coredata
,
msg
.
Pin
,
msg
.
Username
)
jwtt
,
err
:=
s
.
core
.
ValidatePin
(
user
.
Data
()
.
Coredata
,
msg
.
Pin
,
msg
.
Username
)
if
err
==
keyshareCore
.
ErrInvalidPin
{
if
tries
==
0
{
server
.
WriteJson
(
w
,
keysharePinStatus
{
Status
:
"error"
,
Message
:
fmt
.
Sprintf
(
"%v"
,
wait
)})
...
...
@@ -338,7 +338,7 @@ func (s *Server) handleChangePin(w http.ResponseWriter, r *http.Request) {
server
.
WriteJson
(
w
,
keysharePinStatus
{
Status
:
"error"
,
Message
:
fmt
.
Sprintf
(
"%v"
,
wait
)})
return
}
user
.
Coredata
,
err
=
s
.
core
.
ChangePin
(
user
.
Coredata
,
msg
.
OldPin
,
msg
.
NewPin
)
user
.
Data
()
.
Coredata
,
err
=
s
.
core
.
ChangePin
(
user
.
Data
()
.
Coredata
,
msg
.
OldPin
,
msg
.
NewPin
)
if
err
==
keyshareCore
.
ErrInvalidPin
{
if
tries
==
0
{
server
.
WriteJson
(
w
,
keysharePinStatus
{
Status
:
"error"
,
Message
:
fmt
.
Sprintf
(
"%v"
,
wait
)})
...
...
@@ -393,7 +393,7 @@ func (s *Server) handleRegister(w http.ResponseWriter, r *http.Request) {
server
.
WriteError
(
w
,
server
.
ErrorInvalidRequest
,
err
.
Error
())
return
}
err
=
s
.
db
.
NewUser
(
&
KeyshareUser
{
Username
:
username
,
Coredata
:
coredata
})
err
=
s
.
db
.
NewUser
(
KeyshareUser
Data
{
Username
:
username
,
Coredata
:
coredata
})
if
err
!=
nil
{
s
.
conf
.
Logger
.
WithField
(
"error"
,
err
)
.
Error
(
"Could not store new user in database"
)
server
.
WriteError
(
w
,
server
.
ErrorInternal
,
err
.
Error
())
...
...
keyshareServerCore/tmpmain/main.go
View file @
e516be97
...
...
@@ -10,6 +10,8 @@ func main() {
s
,
err
:=
keyshareServerCore
.
New
(
&
keyshareServerCore
.
Configuration
{
SchemesPath
:
"schemes/"
,
URL
:
"http://10.0.2.2:8080/"
,
DbType
:
keyshareServerCore
.
DatabaseTypePostgres
,
DbConnstring
:
"postgresql://localhost:5432/test"
,
JwtKeyId
:
0
,
JwtPrivateKeyFile
:
"schemes/test/kss-0.private.pem"
,
StoragePrimaryKeyFile
:
"storagekey"
,
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment