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

Add Paillier key (de)serializing

parent e44b1aa2
......@@ -9,7 +9,6 @@ import (
"encoding/json"
"github.com/credentials/irmago"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
......@@ -31,7 +30,7 @@ func parseMetaStore(t *testing.T) {
}
func parseStorage(t *testing.T) {
exists, err := PathExists("testdata/storage/path")
exists, err := PathExists("testdata/storage/test")
require.NoError(t, err, "pathexists() failed")
if !exists {
require.NoError(t, os.Mkdir("testdata/storage/test", 0755), "Could not create test storage")
......@@ -69,11 +68,41 @@ func verifyStoreIsUnmarshaled(t *testing.T) {
)
}
func verifyPaillierKey(t *testing.T, PrivateKey *paillierPrivateKey) {
require.NotNil(t, PrivateKey)
require.NotNil(t, PrivateKey.L)
require.NotNil(t, PrivateKey.U)
require.NotNil(t, PrivateKey.PublicKey.N)
require.Equal(t, big.NewInt(1), new(big.Int).Exp(big.NewInt(2), PrivateKey.L, PrivateKey.N))
require.Equal(t, PrivateKey.NSquared, new(big.Int).Exp(PrivateKey.N, big.NewInt(2), nil))
plaintext := "Hello Paillier!"
ciphertext, err := PrivateKey.Encrypt([]byte(plaintext))
require.NoError(t, err)
decrypted, err := PrivateKey.Decrypt(ciphertext)
require.NoError(t, err)
require.Equal(t, plaintext, string(decrypted))
}
func verifyKeyshareIsUnmarshaled(t *testing.T) {
require.NotNil(t, Manager.paillierKeyCache)
require.NotNil(t, Manager.keyshareServers)
test := NewSchemeManagerIdentifier("test")
require.Contains(t, Manager.keyshareServers, test)
kss := Manager.keyshareServers[test]
require.NotEmpty(t, kss.Nonce)
verifyPaillierKey(t, kss.PrivateKey)
verifyPaillierKey(t, Manager.paillierKeyCache)
}
func TestAndroidParse(t *testing.T) {
parseMetaStore(t)
parseStorage(t)
parseAndroidStorage(t)
verifyStoreIsUnmarshaled(t)
verifyKeyshareIsUnmarshaled(t)
teardown(t)
}
......@@ -84,9 +113,11 @@ func TestUnmarshaling(t *testing.T) {
parseAndroidStorage(t)
Manager = newCredentialManager()
Manager.Init("testdata/storage/test")
err := Manager.Init("testdata/storage/test")
require.NoError(t, err)
verifyStoreIsUnmarshaled(t)
verifyKeyshareIsUnmarshaled(t)
teardown(t)
}
......@@ -238,12 +269,12 @@ func TestCandidates(t *testing.T) {
}
func TestTimestamp(t *testing.T) {
mytime := irmago.Timestamp(time.Unix(1500000000, 0))
timestruct := struct{ Time *irmago.Timestamp }{Time: &mytime}
mytime := Timestamp(time.Unix(1500000000, 0))
timestruct := struct{ Time *Timestamp }{Time: &mytime}
bytes, err := json.Marshal(timestruct)
require.NoError(t, err)
timestruct = struct{ Time *irmago.Timestamp }{}
timestruct = struct{ Time *Timestamp }{}
require.NoError(t, json.Unmarshal(bytes, &timestruct))
require.Equal(t, time.Time(*timestruct.Time).Unix(), int64(1500000000))
}
package irmago
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"math/big"
"github.com/mcornejo/go-go-gadget-paillier"
)
type keyshareServer struct {
URL string `json:"url"`
Username string `json:"username"`
Nonce []byte `json:"nonce"`
PrivateKey *paillierPrivateKey `json:"keyPair"`
keyGenerator paillierKeygen
}
// paillierPrivateKey is an alias for paillier.PrivateKey so that we can add a custom unmarshaler to it.
type paillierPrivateKey paillier.PrivateKey
type paillierKeygen interface {
paillierKey() *paillierPrivateKey
}
func newKeyshareServer(keygen paillierKeygen) (ks *keyshareServer, err error) {
ks.Nonce = make([]byte, 0, 32)
ks.keyGenerator = keygen
_, err = rand.Read(ks.Nonce)
return
}
func (ks *keyshareServer) HashedPin(pin string) string {
hash := sha256.Sum256(append(ks.Nonce, []byte(pin)...))
return base64.RawStdEncoding.EncodeToString(hash[:])
}
func (ks *keyshareServer) GetKey() *paillierPrivateKey {
if ks.PrivateKey == nil {
ks.PrivateKey = ks.keyGenerator.paillierKey()
}
return ks.PrivateKey
}
func (psk *paillierPrivateKey) UnmarshalJSON(bytes []byte) (err error) {
// First try to unmarshal it as a keypair serialized in the old Android format
oldFormat := &struct {
PrivateKey struct {
L *big.Int `json:"lambda"`
U *big.Int `json:"preCalculatedDenominator"`
} `json:"privateKey"`
PublicKey struct {
N *big.Int `json:"n"`
G *big.Int `json:"g"`
NSquared *big.Int `json:"nSquared"`
} `json:"publicKey"`
}{}
if err = json.Unmarshal(bytes, oldFormat); err != nil {
return
}
if oldFormat.PrivateKey.L != nil {
psk.L = oldFormat.PrivateKey.L
psk.U = oldFormat.PrivateKey.U
psk.PublicKey.G = oldFormat.PublicKey.G
psk.PublicKey.N = oldFormat.PublicKey.N
psk.PublicKey.NSquared = oldFormat.PublicKey.NSquared
return nil
}
newFormat := new(paillier.PrivateKey)
if err = json.Unmarshal(bytes, newFormat); err != nil {
return
}
*psk = paillierPrivateKey(*newFormat)
return
}
func (psk *paillierPrivateKey) MarshalJSON() ([]byte, error) {
return json.Marshal(paillier.PrivateKey(*psk))
}
func (psk *paillierPrivateKey) Encrypt(bytes []byte) ([]byte, error) {
return paillier.Encrypt(&psk.PublicKey, bytes)
}
func (psk *paillierPrivateKey) Decrypt(bytes []byte) ([]byte, error) {
return paillier.Decrypt((*paillier.PrivateKey)(psk), bytes)
}
......@@ -8,6 +8,9 @@ import (
"io/ioutil"
"math/big"
"crypto/rand"
"github.com/mcornejo/go-go-gadget-paillier"
"github.com/mhe/gabi"
)
......@@ -16,15 +19,19 @@ var Manager = newCredentialManager()
// CredentialManager manages credentials.
type CredentialManager struct {
secretkey *big.Int
storagePath string
attributes map[CredentialTypeIdentifier][]*AttributeList
credentials map[CredentialTypeIdentifier]map[int]*credential
secretkey *big.Int
storagePath string
attributes map[CredentialTypeIdentifier][]*AttributeList
credentials map[CredentialTypeIdentifier]map[int]*credential
keyshareServers map[SchemeManagerIdentifier]*keyshareServer
paillierKeyCache *paillierPrivateKey
}
func newCredentialManager() *CredentialManager {
return &CredentialManager{
credentials: make(map[CredentialTypeIdentifier]map[int]*credential),
credentials: make(map[CredentialTypeIdentifier]map[int]*credential),
keyshareServers: make(map[SchemeManagerIdentifier]*keyshareServer),
}
}
......@@ -45,8 +52,15 @@ func (cm *CredentialManager) Init(path string) (err error) {
return
}
cm.attributes, err = cm.loadAttributes()
if err != nil {
return
}
cm.keyshareServers, err = cm.loadKeyshareServers()
if err != nil {
return
}
cm.paillierKeyCache, err = cm.loadPaillierKeys()
return
}
// attrs returns cm.attributes[id], initializing it to an empty slice if neccesary
......@@ -137,6 +151,7 @@ func (cm *CredentialManager) ParseAndroidStorage() (err error) {
xml.Unmarshal(bytes, &parsedxml)
parsedjson := make(map[string][]*gabi.Credential)
parsedksses := make(map[string]*keyshareServer)
for _, xmltag := range parsedxml.Strings {
if xmltag.Name == "credentials" {
jsontag := html.UnescapeString(xmltag.Content)
......@@ -144,6 +159,25 @@ func (cm *CredentialManager) ParseAndroidStorage() (err error) {
return
}
}
if xmltag.Name == "keyshare" {
jsontag := html.UnescapeString(xmltag.Content)
if err = json.Unmarshal([]byte(jsontag), &parsedksses); err != nil {
return
}
}
if xmltag.Name == "KeyshareKeypairs" {
jsontag := html.UnescapeString(xmltag.Content)
keys := make([]*paillierPrivateKey, 0, 3)
if err = json.Unmarshal([]byte(jsontag), &keys); err != nil {
return
}
cm.paillierKeyCache = keys[0]
}
}
for name, kss := range parsedksses {
kss.keyGenerator = cm
cm.keyshareServers[NewSchemeManagerIdentifier(name)] = kss
}
for _, list := range parsedjson {
......@@ -173,6 +207,21 @@ func (cm *CredentialManager) ParseAndroidStorage() (err error) {
}
}
if len(cm.keyshareServers) > 0 {
err = cm.storeKeyshareServers()
if err != nil {
return err
}
}
err = cm.storePaillierKeys()
if err != nil {
return err
}
if cm.paillierKeyCache == nil {
cm.paillierKey() // trigger calculating a new one
}
return
}
......@@ -374,3 +423,14 @@ func (cm *CredentialManager) ConstructCredentials(msg []*gabi.IssueSignatureMess
return nil
}
// PaillierKey returns a new Paillier key (and generates a new one in a goroutine).
func (cm *CredentialManager) paillierKey() *paillierPrivateKey {
retval := cm.paillierKeyCache
go func() {
newkey, _ := paillier.GenerateKey(rand.Reader, 2048)
converted := paillierPrivateKey(*newkey)
cm.paillierKeyCache = &converted
}()
return retval
}
......@@ -19,6 +19,8 @@ import (
const (
skFile = "sk"
attributesFile = "attrs"
kssFile = "kss"
paillierFile = "paillier"
signaturesDir = "sigs"
cardemuXML = "../cardemu.xml"
)
......@@ -129,6 +131,28 @@ func (cm *CredentialManager) storeAttributes() (err error) {
return
}
func (cm *CredentialManager) storeKeyshareServers() (err error) {
temp := make(map[string]*keyshareServer)
for name, kss := range cm.keyshareServers {
temp[name.String()] = kss
}
bts, err := json.Marshal(temp)
if err != nil {
return
}
err = ioutil.WriteFile(cm.path(kssFile), bts, 0600)
return
}
func (cm *CredentialManager) storePaillierKeys() (err error) {
bts, err := json.Marshal(cm.paillierKeyCache)
if err != nil {
return
}
err = ioutil.WriteFile(cm.path(paillierFile), bts, 0600)
return
}
func (cm *CredentialManager) loadSignature(id CredentialTypeIdentifier, counter int) (signature *gabi.CLSignature, err error) {
sigpath := cm.signatureFilename(id.String(), counter)
exists, err := PathExists(sigpath)
......@@ -193,3 +217,43 @@ func (cm *CredentialManager) loadAttributes() (list map[CredentialTypeIdentifier
}
return list, nil
}
func (cm *CredentialManager) loadKeyshareServers() (ksses map[SchemeManagerIdentifier]*keyshareServer, err error) {
ksses = make(map[SchemeManagerIdentifier]*keyshareServer)
temp := make(map[string]*keyshareServer)
exists, err := PathExists(cm.path(kssFile))
if err != nil || !exists {
return
}
bytes, err := ioutil.ReadFile(cm.path(kssFile))
if err != nil {
return nil, err
}
err = json.Unmarshal(bytes, &temp)
if err != nil {
return nil, err
}
for name, kss := range temp {
kss.keyGenerator = cm
ksses[NewSchemeManagerIdentifier(name)] = kss
}
return
}
func (cm *CredentialManager) loadPaillierKeys() (key *paillierPrivateKey, err error) {
exists, err := PathExists(cm.path(paillierFile))
if err != nil || !exists {
return
}
bytes, err := ioutil.ReadFile(cm.path(paillierFile))
if err != nil {
return nil, err
}
key = new(paillierPrivateKey)
err = json.Unmarshal(bytes, key)
if err != nil {
return nil, err
}
return
}
This source diff could not be displayed because it is too large. You can view the blob instead.
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