add card secrets initialization

This commit is contained in:
Andrea Franz 2018-10-22 19:33:53 +02:00
parent de86c19fcc
commit 61bc1a2764
No known key found for this signature in database
GPG Key ID: 4F0D2F2D9DE7F29D
7 changed files with 209 additions and 11 deletions

View File

@ -26,6 +26,10 @@ var (
)
func initLogger() {
if *flagLogLevel == "" {
*flagLogLevel = "info"
}
level, err := log.LvlFromString(strings.ToLower(*flagLogLevel))
if err != nil {
stdlog.Fatal(err)
@ -44,6 +48,7 @@ func init() {
"install": commandInstall,
"status": commandStatus,
"delete": commandDelete,
"init": commandInit,
}
}
@ -144,14 +149,12 @@ func commandInstall(i *lightwallet.Installer) error {
}
defer f.Close()
secrets, err := i.Install(f, *flagOverwrite)
err = i.Install(f, *flagOverwrite)
if err != nil {
fail("installation error", "error", err)
}
fmt.Printf("\n\nPUK %s\n", secrets.Puk())
fmt.Printf("Pairing password: %s\n", secrets.PairingPass())
fmt.Printf("applet installed successfully.\n")
return nil
}
@ -180,3 +183,16 @@ func commandDelete(i *lightwallet.Installer) error {
return nil
}
func commandInit(i *lightwallet.Installer) error {
secrets, err := i.Init()
if err != nil {
return err
}
fmt.Printf("PIN %s\n", secrets.Pin())
fmt.Printf("PUK %s\n", secrets.Puk())
fmt.Printf("Pairing password: %s\n", secrets.PairingPass())
return nil
}

20
lightwallet/commands.go Normal file
View File

@ -0,0 +1,20 @@
package lightwallet
import (
"github.com/status-im/smartcard-go/apdu"
"github.com/status-im/smartcard-go/globalplatform"
)
const (
InsInit = uint8(0xFE)
)
func NewCommandInit(data []byte) *apdu.Command {
return apdu.NewCommand(
globalplatform.ClaGp,
InsInit,
uint8(0x00),
uint8(0x00),
data,
)
}

View File

@ -0,0 +1,49 @@
package crypto
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/ecdsa"
"crypto/rand"
"github.com/ethereum/go-ethereum/crypto"
)
func GenerateECDHSharedSecret(priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey) []byte {
x, _ := crypto.S256().ScalarMult(pub.X, pub.Y, priv.D.Bytes())
return x.Bytes()
}
func OneShotEncrypt(pubKeyData, secret, data []byte) ([]byte, error) {
data = appendPadding(16, data)
iv := make([]byte, 16)
_, err := rand.Read(iv)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(secret)
if err != nil {
return nil, err
}
ciphertext := make([]byte, len(data))
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext, data)
encrypted := append([]byte{byte(len(pubKeyData))}, pubKeyData...)
encrypted = append(encrypted, iv...)
encrypted = append(encrypted, ciphertext...)
return encrypted, nil
}
func appendPadding(blockSize int, data []byte) []byte {
paddingSize := blockSize - (len(data)+1)%blockSize
zeroes := bytes.Repeat([]byte{0x00}, paddingSize)
padding := append([]byte{0x80}, zeroes...)
return append(data, padding...)
}

View File

@ -0,0 +1,20 @@
package crypto
import (
"testing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/assert"
)
func TestECDH(t *testing.T) {
pk1, err := crypto.GenerateKey()
assert.NoError(t, err)
pk2, err := crypto.GenerateKey()
assert.NoError(t, err)
sharedSecret1 := GenerateECDHSharedSecret(pk1, &pk2.PublicKey)
sharedSecret2 := GenerateECDHSharedSecret(pk2, &pk1.PublicKey)
assert.Equal(t, sharedSecret1, sharedSecret2)
}

View File

@ -34,34 +34,66 @@ func NewInstaller(t globalplatform.Transmitter) *Installer {
}
// Install installs the applet from the specified capFile.
func (i *Installer) Install(capFile *os.File, overwriteApplet bool) (*Secrets, error) {
func (i *Installer) Install(capFile *os.File, overwriteApplet bool) error {
err := i.initSecureChannel(cardManagerAID)
if err != nil {
return nil, err
return err
}
installed, err := i.isAppletInstalled()
if err != nil {
return nil, err
return err
}
if installed && !overwriteApplet {
return nil, errors.New("applet already installed")
return errors.New("applet already installed")
}
err = i.deleteAID(ndefInstanceAID, walletAID, pkgAID)
if err != nil {
return nil, err
return err
}
err = i.installApplets(capFile)
if err != nil {
return err
}
return err
}
func (i *Installer) Init() (*Secrets, error) {
secrets, err := NewSecrets()
if err != nil {
return nil, err
}
secrets, err := NewSecrets()
sel := globalplatform.NewCommandSelect(walletAID)
resp, err := i.send("select applet", sel)
if err != nil {
return nil, err
}
return secrets, err
cardKeyData := resp.Data[2:]
secureChannel, err := NewSecureChannel(i.c, cardKeyData)
if err != nil {
return nil, err
}
data, err := secureChannel.OneShotEncrypt(secrets)
if err != nil {
return nil, err
}
cmd := NewCommandInit(data)
resp, err = i.send("init card", cmd)
if err != nil {
return nil, err
}
fmt.Printf("RESP: %+v\n", resp)
return secrets, nil
}
// Info returns if the applet is already installed in the card.

View File

@ -14,10 +14,12 @@ import (
const (
pairingTokenSalt = "Status Hardware Wallet Lite"
maxPukNumber = int64(999999999999)
maxPinNumber = int64(999999)
)
// Secrets contains the secret data needed to pair a client with a card.
type Secrets struct {
pin string
puk string
pairingPass string
pairingToken []byte
@ -35,13 +37,24 @@ func NewSecrets() (*Secrets, error) {
return nil, err
}
pin, err := rand.Int(rand.Reader, big.NewInt(maxPinNumber))
if err != nil {
return nil, err
}
return &Secrets{
pin: fmt.Sprintf("%06d", pin.Int64()),
puk: fmt.Sprintf("%012d", puk.Int64()),
pairingPass: pairingPass,
pairingToken: generatePairingToken(pairingPass),
}, nil
}
// Pin returns the pin string.
func (s *Secrets) Pin() string {
return s.pin
}
// Puk returns the puk string.
func (s *Secrets) Puk() string {
return s.puk

View File

@ -0,0 +1,48 @@
package lightwallet
import (
"crypto/ecdsa"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/status-im/smartcard-go/apdu"
"github.com/status-im/smartcard-go/globalplatform"
"github.com/status-im/smartcard-go/lightwallet/crypto"
)
type SecureChannel struct {
c globalplatform.Channel
secret []byte
pubKey *ecdsa.PublicKey
}
func NewSecureChannel(c globalplatform.Channel, cardKeyData []byte) (*SecureChannel, error) {
key, err := ethcrypto.GenerateKey()
if err != nil {
return nil, err
}
cardPubKey, err := ethcrypto.UnmarshalPubkey(cardKeyData)
if err != nil {
return nil, err
}
secret := crypto.GenerateECDHSharedSecret(key, cardPubKey)
return &SecureChannel{
c: c,
secret: secret,
pubKey: &key.PublicKey,
}, nil
}
func (sc *SecureChannel) Send(cmd *apdu.Command) (*apdu.Response, error) {
return sc.c.Send(cmd)
}
func (sc *SecureChannel) OneShotEncrypt(secrets *Secrets) ([]byte, error) {
pubKeyData := ethcrypto.FromECDSAPub(sc.pubKey)
data := append([]byte(secrets.Pin()), []byte(secrets.Puk())...)
data = append(data, secrets.PairingToken()...)
return crypto.OneShotEncrypt(pubKeyData, sc.secret, data)
}