mirror of
https://github.com/status-im/keycard-go.git
synced 2025-01-21 09:18:48 +00:00
add card secrets initialization
This commit is contained in:
parent
de86c19fcc
commit
61bc1a2764
@ -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
20
lightwallet/commands.go
Normal 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,
|
||||
)
|
||||
}
|
49
lightwallet/crypto/crypto.go
Normal file
49
lightwallet/crypto/crypto.go
Normal 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...)
|
||||
}
|
20
lightwallet/crypto/crypto_test.go
Normal file
20
lightwallet/crypto/crypto_test.go
Normal 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)
|
||||
}
|
@ -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.
|
||||
|
@ -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
|
||||
|
48
lightwallet/secure_channel.go
Normal file
48
lightwallet/secure_channel.go
Normal 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)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user