add keycard-sign-with-path command

This commit is contained in:
Andrea Franz 2019-11-14 12:52:23 +01:00
parent 9ab74c9fb1
commit 5d79f7868f
No known key found for this signature in database
GPG Key ID: 4F0D2F2D9DE7F29D
5 changed files with 165 additions and 30 deletions

4
Gopkg.lock generated
View File

@ -42,7 +42,7 @@
[[projects]]
branch = "develop"
digest = "1:641843b5cf82353b41cdd2e93f7d9009ea761434b2a085279455036c4d10c95e"
digest = "1:428d2c2f86c07c78f39d379b809c317b3792d0248e76d60bf78dcf101596c770"
name = "github.com/status-im/keycard-go"
packages = [
".",
@ -57,7 +57,7 @@
"types",
]
pruneopts = "NUT"
revision = "f383a0d5830c5eee07e25cdd2d38ed76a19ec7f1"
revision = "9d48af884d5b92339229bf57df18d0f882ba70be"
[[projects]]
branch = "master"

View File

@ -138,6 +138,7 @@ func NewShell(t keycardio.Transmitter) *Shell {
"keycard-remove-key": s.commandKeycardRemoveKey,
"keycard-derive-key": s.commandKeycardDeriveKey,
"keycard-sign": s.commandKeycardSign,
"keycard-sign-with-path": s.commandKeycardSignWithPath,
"keycard-sign-message": s.commandKeycardSignMessage,
"keycard-sign-pinless": s.commandKeycardSignPinless,
"keycard-sign-message-pinless": s.commandKeycardSignMessagePinless,
@ -680,6 +681,29 @@ func (s *Shell) commandKeycardSign(args ...string) error {
return nil
}
func (s *Shell) commandKeycardSignWithPath(args ...string) error {
if err := s.requireArgs(args, 2); err != nil {
return err
}
data, err := s.parseHex(args[0])
if err != nil {
logger.Error("failed parsing hex data", "error", err)
return err
}
logger.Info("sign with path")
sig, err := s.kCmdSet.SignWithPath(data, args[1])
if err != nil {
logger.Error("sign with path failed", "error", err)
return err
}
s.writeSignatureInfo(sig)
return nil
}
func (s *Shell) commandKeycardSignMessage(args ...string) error {
if len(args) < 1 {
return errors.New("keycard-sign-message require at least 1 parameter")

View File

@ -229,6 +229,35 @@ func (cs *CommandSet) DeriveKey(path string) error {
return cs.checkOK(resp, err)
}
func (cs *CommandSet) ExportKey(derive bool, makeCurrent bool, onlyPublic bool, path string) ([]byte, error) {
var p1 uint8
if derive == false {
p1 = P1ExportKeyCurrent
} else if makeCurrent == false {
p1 = P1ExportKeyDerive
} else {
p1 = P1ExportKeyDeriveAndMakeCurrent
}
var p2 uint8
if onlyPublic == true {
p2 = P2ExportKeyPublicOnly
} else {
p2 = P2ExportKeyPrivateAndPublic
}
cmd, err := NewCommandExportKey(p1, p2, path)
if err != nil {
return nil, err
}
resp, err := cs.sc.Send(cmd)
err = cs.checkOK(resp, err)
if err != nil {
return nil, err
}
return resp.Data, nil
}
func (cs *CommandSet) SetPinlessPath(path string) error {
cmd, err := NewCommandSetPinlessPath(path)
if err != nil {
@ -240,7 +269,21 @@ func (cs *CommandSet) SetPinlessPath(path string) error {
}
func (cs *CommandSet) Sign(data []byte) (*types.Signature, error) {
cmd, err := NewCommandSign(data, P1SignCurrentKey)
cmd, err := NewCommandSign(data, P1SignCurrentKey, "")
if err != nil {
return nil, err
}
resp, err := cs.sc.Send(cmd)
if err = cs.checkOK(resp, err); err != nil {
return nil, err
}
return types.ParseSignature(data, resp.Data)
}
func (cs *CommandSet) SignWithPath(data []byte, path string) (*types.Signature, error) {
cmd, err := NewCommandSign(data, P1SignDerive, path)
if err != nil {
return nil, err
}
@ -254,7 +297,7 @@ func (cs *CommandSet) Sign(data []byte) (*types.Signature, error) {
}
func (cs *CommandSet) SignPinless(data []byte) (*types.Signature, error) {
cmd, err := NewCommandSign(data, P1SignPinless)
cmd, err := NewCommandSign(data, P1SignPinless, "")
if err != nil {
return nil, err
}

View File

@ -22,23 +22,30 @@ const (
InsVerifyPIN = 0x20
InsChangePIN = 0x21
InsDeriveKey = 0xD1
InsExportKey = 0xC2
InsSign = 0xC0
InsSetPinlessPath = 0xC1
P1PairingFirstStep = 0x00
P1PairingFinalStep = 0x01
P1GetStatusApplication = 0x00
P1GetStatusKeyPath = 0x01
P1DeriveKeyFromMaster = 0x00
P1DeriveKeyFromParent = 0x40
P1DeriveKeyFromCurrent = 0x80
P1ChangePinPIN = 0x00
P1ChangePinPUK = 0x01
P1ChangePinPairingSecret = 0x02
P1SignCurrentKey = 0x00
P1SignDerive = 0x01
P1SignDeriveAndMakeCurrent = 0x02
P1SignPinless = 0x03
P1PairingFirstStep = 0x00
P1PairingFinalStep = 0x01
P1GetStatusApplication = 0x00
P1GetStatusKeyPath = 0x01
P1DeriveKeyFromMaster = 0x00
P1DeriveKeyFromParent = 0x40
P1DeriveKeyFromCurrent = 0x80
P1ChangePinPIN = 0x00
P1ChangePinPUK = 0x01
P1ChangePinPairingSecret = 0x02
P1SignCurrentKey = 0x00
P1SignDerive = 0x01
P1SignDeriveAndMakeCurrent = 0x02
P1SignPinless = 0x03
P1ExportKeyCurrent = uint8(0x00)
P1ExportKeyDerive = uint8(0x01)
P1ExportKeyDeriveAndMakeCurrent = uint8(0x02)
P2ExportKeyPrivateAndPublic = uint8(0x00)
P2ExportKeyPublicOnly = uint8(0x01)
SwNoAvailablePairingSlots = 0x6A84
)
@ -179,16 +186,9 @@ func NewCommandDeriveKey(pathStr string) (*apdu.Command, error) {
return nil, err
}
var p1 uint8
switch startingPoint {
case derivationpath.StartingPointMaster:
p1 = P1DeriveKeyFromMaster
case derivationpath.StartingPointParent:
p1 = P1DeriveKeyFromParent
case derivationpath.StartingPointCurrent:
p1 = P1DeriveKeyFromCurrent
default:
return nil, fmt.Errorf("invalid startingPoint %d", startingPoint)
p1, err := derivationP1FromStartingPoint(startingPoint)
if err != nil {
return nil, err
}
data := new(bytes.Buffer)
@ -207,6 +207,43 @@ func NewCommandDeriveKey(pathStr string) (*apdu.Command, error) {
), nil
}
// Export a key
// @param {p1}
// 0x00: current key - returns the key that is currently loaded and ready for signing. Does not use derivation path
// 0x01: derive - returns derived key
// 0x02: derive and make current - returns derived key and also sets it to the current key
// @param {p2}
// 0x00: return public and private key pair
// 0x01: return only the public key
// @param {pathStr}
// Derivation path of format "m/x/x/x/x/x", e.g. "m/44'/0'/0'/0/0"
func NewCommandExportKey(p1 uint8, p2 uint8, pathStr string) (*apdu.Command, error) {
startingPoint, path, err := derivationpath.Decode(pathStr)
if err != nil {
return nil, err
}
deriveP1, err := derivationP1FromStartingPoint(startingPoint)
if err != nil {
return nil, err
}
data := new(bytes.Buffer)
for _, segment := range path {
if err := binary.Write(data, binary.BigEndian, segment); err != nil {
return nil, err
}
}
return apdu.NewCommand(
globalplatform.ClaGp,
InsExportKey,
p1|deriveP1,
p2,
data.Bytes(),
), nil
}
func NewCommandSetPinlessPath(pathStr string) (*apdu.Command, error) {
startingPoint, path, err := derivationpath.Decode(pathStr)
if err != nil {
@ -233,11 +270,27 @@ func NewCommandSetPinlessPath(pathStr string) (*apdu.Command, error) {
), nil
}
func NewCommandSign(data []byte, p1 uint8) (*apdu.Command, error) {
func NewCommandSign(data []byte, p1 uint8, pathStr string) (*apdu.Command, error) {
if len(data) != 32 {
return nil, fmt.Errorf("data length must be 32, got %d", len(data))
}
if p1 == P1SignDerive || p1 == P1SignDeriveAndMakeCurrent {
_, path, err := derivationpath.Decode(pathStr)
if err != nil {
return nil, err
}
pathData := new(bytes.Buffer)
for _, segment := range path {
if err := binary.Write(pathData, binary.BigEndian, segment); err != nil {
return nil, err
}
}
data = append(data, pathData.Bytes()...)
}
return apdu.NewCommand(
globalplatform.ClaGp,
InsSign,
@ -246,3 +299,18 @@ func NewCommandSign(data []byte, p1 uint8) (*apdu.Command, error) {
data,
), nil
}
// Internal function. Get the type of starting point for the derivation path.
// Used for both DeriveKey and ExportKey
func derivationP1FromStartingPoint(s derivationpath.StartingPoint) (uint8, error) {
switch s {
case derivationpath.StartingPointMaster:
return P1DeriveKeyFromMaster, nil
case derivationpath.StartingPointParent:
return P1DeriveKeyFromParent, nil
case derivationpath.StartingPointCurrent:
return P1DeriveKeyFromCurrent, nil
default:
return uint8(0), fmt.Errorf("invalid startingPoint %d", s)
}
}

View File

@ -10,4 +10,4 @@ type Channel interface {
type PairingInfo struct {
Key []byte
Index int
}
}