Skip to content
This repository has been archived by the owner on Nov 27, 2023. It is now read-only.

Commit

Permalink
Merge pull request #11 from Zondax/dev
Browse files Browse the repository at this point in the history
Add validator app support
  • Loading branch information
jleni authored Jul 2, 2020
2 parents ed83356 + 8f3d8d4 commit f52dccf
Show file tree
Hide file tree
Showing 7 changed files with 1,255 additions and 78 deletions.
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
test_mock:
go test -tags ledger_mock

test_zemu:
go test -tags ledger_zemu

test_hid:
go test
153 changes: 119 additions & 34 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@
package ledger_oasis_go

import (
"encoding/hex"
"fmt"
"github.com/oasisprotocol/oasis-core/go/common/crypto/signature"
"github.com/oasisprotocol/oasis-core/go/common/logging"
"github.com/zondax/ledger-go"
)

const (
CLA = 0x05
CLAConsumer = 0x05
CLAValidator = 0xF5

INSGetVersion = 0
INSGetAddrEd25519 = 1
Expand All @@ -35,16 +39,49 @@ const (
PayloadChunkLast = 2
)

type LedgerAppMode int

const (
ValidatorMode LedgerAppMode = 1 + iota
ConsumerMode
UnknownMode
)

const (
// PathPurposeConsensus is set to 43, matching ledger's Validator app
PathPurposeConsensus uint32 = 43
)


// LedgerOasis represents a connection to the Ledger app
type LedgerOasis struct {
api *ledger_go.Ledger
device ledger_go.LedgerDevice
version VersionInfo
}

var logger = logging.GetLogger(LogModuleName)

//SetLoggerModule sets logging module
func (ledger *LedgerOasis)SetLoggerModule(module string) {
logger = logging.GetLogger(module)
}

//GetModeForRole returns the app mode compatible with role
func GetModeForRole(role signature.SignerRole) LedgerAppMode {
switch role {
case signature.SignerConsensus:
return ValidatorMode
default:
return ConsumerMode
}
}

// Displays existing Ledger Oasis apps by address
func ListOasisDevices(path []uint32) {
for i := uint(0); i < ledger_go.CountLedgerDevices(); i += 1 {
ledgerDevice, err := ledger_go.GetLedger(i)
ledgerAdmin := ledger_go.NewLedgerAdmin()

for i := 0; i < ledgerAdmin.CountDevices(); i += 1 {
ledgerDevice, err := ledgerAdmin.Connect(i)
if err != nil {
continue
}
Expand All @@ -69,15 +106,33 @@ func ListOasisDevices(path []uint32) {
}
}

func GetModeForPath(path []uint32) LedgerAppMode {

mode := UnknownMode

if path[0] == PathPurposeConsensus {
mode = ValidatorMode
} else {
mode = ConsumerMode
}

return mode
}

// ConnectLedgerOasisApp connects to Oasis app based on address
func ConnectLedgerOasisApp(seekingAddress string, path []uint32) (*LedgerOasis, error) {
for i := uint(0); i < ledger_go.CountLedgerDevices(); i += 1 {
ledgerDevice, err := ledger_go.GetLedger(i)
ledgerAdmin := ledger_go.NewLedgerAdmin()

mode := GetModeForPath(path)

for i := 0; i < ledgerAdmin.CountDevices(); i += 1 {
ledgerDevice, err := ledgerAdmin.Connect(i)
if err != nil {
continue
}

app := LedgerOasis{ledgerDevice, VersionInfo{}}
app := LedgerOasis{ledgerDevice, VersionInfo{uint8(mode), 0, 0, 0}}

_, address, err := app.GetAddressPubKeyEd25519(path)
if err != nil {
defer app.Close()
Expand All @@ -92,48 +147,65 @@ func ConnectLedgerOasisApp(seekingAddress string, path []uint32) (*LedgerOasis,

// FindLedgerOasisApp finds the Oasis app running in a Ledger device
func FindLedgerOasisApp() (*LedgerOasis, error) {
ledgerAPI, err := ledger_go.FindLedger()
ledgerAdmin := ledger_go.NewLedgerAdmin()

if err != nil {
return nil, err
}
for i := 0; i < ledgerAdmin.CountDevices(); i += 1 {
ledgerDevice, err := ledgerAdmin.Connect(i)
if err != nil {
continue
}

app := LedgerOasis{ledgerAPI, VersionInfo{}}
appVersion, err := app.GetVersion()
app := LedgerOasis{ledgerDevice, VersionInfo{}}

if err != nil {
defer ledgerAPI.Close()
if err.Error() == "[APDU_CODE_CLA_NOT_SUPPORTED] Class not supported" {
return nil, fmt.Errorf("are you sure the Oasis app is open?")
appVersion, err := app.GetVersion()
if err != nil {
app.Close()
continue
}
return nil, err
}

err = app.CheckVersion(*appVersion)
if err != nil {
defer ledgerAPI.Close()
return nil, err
err = app.CheckVersion(*appVersion)
if err != nil {
app.Close()
continue
}

return &app, err
}

return &app, err
return nil, fmt.Errorf("no Oasis app found")
}

// Close closes a connection with the Oasis user app
func (ledger *LedgerOasis) Close() error {
return ledger.api.Close()
return ledger.device.Close()
}

// VersionIsSupported returns true if the App version is supported by this library
func (ledger *LedgerOasis) CheckVersion(ver VersionInfo) error {
return CheckVersion(ver, VersionInfo{0, 0, 3, 0})
}

//getCLA returns the CLA value for the current app mode
func (ledger *LedgerOasis) getCLA() byte {
switch LedgerAppMode(ledger.version.AppMode) {
case ValidatorMode:
return CLAValidator
default:
return CLAConsumer
}
}

// GetVersion returns the current version of the Oasis user app
func (ledger *LedgerOasis) GetVersion() (*VersionInfo, error) {
message := []byte{CLA, INSGetVersion, 0, 0, 0}
response, err := ledger.api.Exchange(message)
message := []byte{ledger.getCLA(), INSGetVersion, 0, 0, 0}
response, err := ledger.device.Exchange(message)

logger.Debug("GetVersion requested:")
logger.Debug("message: " + hex.EncodeToString(message))
logger.Debug("response: " + hex.EncodeToString(response))

if err != nil {
logger.Error("error while getting version: %q", err)
return nil, err
}

Expand Down Expand Up @@ -192,7 +264,7 @@ func (ledger *LedgerOasis) sign(bip44Path []uint32, context []byte, transaction
return nil, err
}

chunks, err := prepareChunks(pathBytes, context, transaction)
chunks, err := prepareChunks(pathBytes, context, transaction, userMessageChunkSize)
if err != nil {
return nil, err
}
Expand All @@ -207,7 +279,7 @@ func (ledger *LedgerOasis) sign(bip44Path []uint32, context []byte, transaction
payloadLen := byte(len(chunks[chunkIndex]))

if chunkIndex == 0 {
header := []byte{CLA, INSSignEd25519, PayloadChunkInit, 0, payloadLen}
header := []byte{ledger.getCLA(), INSSignEd25519, PayloadChunkInit, 0, payloadLen}
message = append(header, chunks[chunkIndex]...)
} else {

Expand All @@ -216,11 +288,16 @@ func (ledger *LedgerOasis) sign(bip44Path []uint32, context []byte, transaction
payloadDesc = byte(PayloadChunkLast)
}

header := []byte{CLA, INSSignEd25519, payloadDesc, 0, payloadLen}
header := []byte{ledger.getCLA(), INSSignEd25519, payloadDesc, 0, payloadLen}
message = append(header, chunks[chunkIndex]...)
}

response, err := ledger.api.Exchange(message)
response, err := ledger.device.Exchange(message)

logger.Debug("Sign requested:")
logger.Debug("message: " + hex.EncodeToString(message))
logger.Debug("response: " + hex.EncodeToString(response))

if err != nil {
// FIXME: CBOR will be used instead
if err.Error() == "[APDU_CODE_BAD_KEY_HANDLE] The parameters in the data field are incorrect" {
Expand All @@ -232,6 +309,10 @@ func (ledger *LedgerOasis) sign(bip44Path []uint32, context []byte, transaction
errorMsg := string(response)
return nil, fmt.Errorf(errorMsg)
}
if err.Error() == "[APDU_CODE_COMMAND_NOT_ALLOWED] Sign request rejected" {
errorMsg := string(response)
return nil, fmt.Errorf(errorMsg)
}
return nil, err
}

Expand All @@ -255,17 +336,21 @@ func (ledger *LedgerOasis) retrieveAddressPubKeyEd25519(bip44Path []uint32, requ
}

// Prepare message
header := []byte{CLA, INSGetAddrEd25519, p1, 0, 0}
header := []byte{ledger.getCLA(), INSGetAddrEd25519, p1, 0, 0}
message := append(header, pathBytes...)
message[4] = byte(len(message) - len(header)) // update length

response, err := ledger.api.Exchange(message)
response, err := ledger.device.Exchange(message)

logger.Debug("PubKey requested:")
logger.Debug("message: " + hex.EncodeToString(message))
logger.Debug("response: " + hex.EncodeToString(response))

if err != nil {
return nil, "", err
}
if len(response) < 39 {
return nil, "", fmt.Errorf("Invalid response")
return nil, "", fmt.Errorf("invalid response")
}

pubkey = response[0:32]
Expand Down
26 changes: 7 additions & 19 deletions app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
"testing"
)

var coinContext = "oasis-core/consensus: tx for chain test-chain-id"
var coinContext = "oasis-core/consensus: tx for chain 7b02d647e8997bacebce96723f6904029ec78b67c261c4bdddb5e47de1ab31fa"

// Ledger Test Mnemonic: equip will roof matter pink blind book anxiety banner elbow sun young

Expand All @@ -48,8 +48,6 @@ func Test_UserGetVersion(t *testing.T) {
}
defer app.Close()

app.api.Logging = true

version, err := app.GetVersion()
require.Nil(t, err, "Detected error")
fmt.Println(version)
Expand All @@ -66,8 +64,6 @@ func Test_UserGetPublicKey(t *testing.T) {
}
defer app.Close()

app.api.Logging = true

path := []uint32{44, 474, 5, 0, 21}

pubKey, err := app.GetPublicKeyEd25519(path)
Expand All @@ -92,8 +88,6 @@ func Test_GetAddressPubKeyEd25519_Zero(t *testing.T) {
}
defer app.Close()

app.api.Logging = true

path := []uint32{44, 474, 0, 0, 0}

pubKey, addr, err := app.GetAddressPubKeyEd25519(path)
Expand All @@ -117,8 +111,6 @@ func Test_GetAddressPubKeyEd25519(t *testing.T) {
}
defer app.Close()

app.api.Logging = true

path := []uint32{44, 474, 5, 0, 21}

pubKey, addr, err := app.GetAddressPubKeyEd25519(path)
Expand All @@ -142,8 +134,6 @@ func Test_ShowAddressPubKeyEd25519(t *testing.T) {
}
defer app.Close()

app.api.Logging = true

path := []uint32{44, 474, 5, 0, 21}

pubKey, addr, err := app.ShowAddressPubKeyEd25519(path)
Expand All @@ -167,8 +157,6 @@ func Test_UserPK_HDPaths(t *testing.T) {
}
defer app.Close()

app.api.Logging = true

path := []uint32{44, 474, 0, 0, 0}

expected := []string{
Expand Down Expand Up @@ -207,9 +195,9 @@ func Test_UserPK_HDPaths(t *testing.T) {
}

func getDummyTx() []byte {
base64tx := "pGNmZWWiY2dhcwBmYW1vdW50QGRib2R5omd4ZmVyX3RvWCBkNhaFWEyIEubmS3EVtRLTanD3U+vDV5fke4Obyq" +
"83CWt4ZmVyX3Rva2Vuc0Blbm9uY2UAZm1ldGhvZHBzdGFraW5nLlRyYW5zZmVy"
base64tx := "pGNmZWWiY2dhcxkD6GZhbW91bnRCB9BkYm9keaJneGZlcl90b1UA4ywoibwEEhHt7fqvlNL9hmmLsH9reGZlcl90b2tlbnNFJ5TKJABlbm9uY2UHZm1ldGhvZHBzdGFraW5nLlRyYW5zZmVy"
tx, _ := base64.StdEncoding.DecodeString(base64tx)
println(hex.EncodeToString(tx))
return tx
}

Expand All @@ -220,11 +208,13 @@ func Test_Sign(t *testing.T) {
}
defer app.Close()

app.api.Logging = true

path := []uint32{44, 474, 0, 0, 5}

message := getDummyTx()

println(coinContext)
println(hex.EncodeToString(message))

signature, err := app.SignEd25519(path, []byte(coinContext), message)
if err != nil {
t.Fatalf("[Sign] Error: %s\n", err.Error())
Expand Down Expand Up @@ -258,8 +248,6 @@ func Test_Sign_Fails(t *testing.T) {
}
defer app.Close()

app.api.Logging = true

path := []uint32{44, 474, 0, 0, 5}

message := getDummyTx()
Expand Down
Loading

0 comments on commit f52dccf

Please sign in to comment.