add certificate validation
This commit is contained in:
parent
104f9ded0a
commit
a839ed4597
|
@ -154,6 +154,22 @@ func (cs *CommandSet) Unpair(index uint8) error {
|
|||
return cs.checkOK(resp, err)
|
||||
}
|
||||
|
||||
func (cs *CommandSet) Identify() ([]byte, error) {
|
||||
challenge := make([]byte, 32)
|
||||
if _, err := rand.Read(challenge); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := NewCommandIdentify(challenge)
|
||||
resp, err := cs.sc.Send(cmd)
|
||||
|
||||
if err = cs.checkOK(resp, err); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return types.VerifyIdentity(challenge, resp.Data)
|
||||
}
|
||||
|
||||
func (cs *CommandSet) OpenSecureChannel() error {
|
||||
if cs.ApplicationInfo == nil {
|
||||
return errors.New("cannot open secure channel without setting PairingInfo")
|
||||
|
|
17
commands.go
17
commands.go
|
@ -16,6 +16,7 @@ const (
|
|||
InsMutuallyAuthenticate = 0x11
|
||||
InsPair = 0x12
|
||||
InsUnpair = 0x13
|
||||
InsIdentify = 0x14
|
||||
InsGetStatus = 0xF2
|
||||
InsGenerateKey = 0xD4
|
||||
InsRemoveKey = 0xD3
|
||||
|
@ -53,6 +54,7 @@ const (
|
|||
P1ExportKeyDeriveAndMakeCurrent = 0x02
|
||||
P2ExportKeyPrivateAndPublic = 0x00
|
||||
P2ExportKeyPublicOnly = 0x01
|
||||
P2ExportKeyExtendedPublic = 0x02
|
||||
P1LoadKeySeed = 0x03
|
||||
|
||||
SwNoAvailablePairingSlots = 0x6A84
|
||||
|
@ -98,6 +100,16 @@ func NewCommandUnpair(index uint8) *apdu.Command {
|
|||
)
|
||||
}
|
||||
|
||||
func NewCommandIdentify(challenge []byte) *apdu.Command {
|
||||
return apdu.NewCommand(
|
||||
globalplatform.ClaGp,
|
||||
InsIdentify,
|
||||
0,
|
||||
0,
|
||||
challenge,
|
||||
)
|
||||
}
|
||||
|
||||
func NewCommandOpenSecureChannel(pairingIndex uint8, pubKey []byte) *apdu.Command {
|
||||
return apdu.NewCommand(
|
||||
globalplatform.ClaGp,
|
||||
|
@ -247,13 +259,14 @@ func NewCommandDeriveKey(pathStr string) (*apdu.Command, error) {
|
|||
|
||||
// Export a key
|
||||
//
|
||||
// @param {p1}
|
||||
// @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
|
||||
// 0x02: return extended 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) {
|
||||
|
@ -334,7 +347,7 @@ func NewCommandSign(data []byte, p1 uint8, pathStr string) (*apdu.Command, error
|
|||
globalplatform.ClaGp,
|
||||
InsSign,
|
||||
p1,
|
||||
0,
|
||||
1,
|
||||
data,
|
||||
), nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/status-im/keycard-go/apdu"
|
||||
)
|
||||
|
||||
type Certificate struct {
|
||||
identPub []byte
|
||||
signature *Signature
|
||||
}
|
||||
|
||||
var (
|
||||
TagCertificate = uint8(0x8A)
|
||||
)
|
||||
|
||||
func ParseCertificate(data []byte) (*Certificate, error) {
|
||||
if len(data) != 98 {
|
||||
return nil, errors.New("certificate must be 98 byte long")
|
||||
}
|
||||
|
||||
identPub := data[0:33]
|
||||
sigData := data[33:97]
|
||||
msg := sha256.Sum256(identPub)
|
||||
|
||||
sig, err := ParseRecoverableSignature(msg[:], sigData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Certificate{
|
||||
identPub: identPub,
|
||||
signature: sig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func VerifyIdentity(challenge []byte, tlvData []byte) ([]byte, error) {
|
||||
template, err := apdu.FindTag(tlvData, apdu.Tag{TagSignatureTemplate})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certData, err := apdu.FindTag(template, apdu.Tag{TagCertificate})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert, err := ParseCertificate(certData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, s, err := DERSignatureToRS(template)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig := append(r, s...)
|
||||
|
||||
if !crypto.VerifySignature(cert.identPub, challenge, sig) {
|
||||
return nil, errors.New("invalid signature")
|
||||
}
|
||||
|
||||
return compressPublicKey(cert.signature.pubKey), nil
|
||||
}
|
||||
|
||||
func compressPublicKey(pubKey []byte) []byte {
|
||||
if len(pubKey) == 33 {
|
||||
return pubKey
|
||||
}
|
||||
|
||||
if (pubKey[63] & 1) == 1 {
|
||||
pubKey[0] = 3
|
||||
} else {
|
||||
pubKey[0] = 2
|
||||
}
|
||||
|
||||
return pubKey[0:33]
|
||||
}
|
|
@ -2,6 +2,7 @@ package types
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/status-im/keycard-go/apdu"
|
||||
|
@ -9,6 +10,7 @@ import (
|
|||
|
||||
var (
|
||||
TagSignatureTemplate = uint8(0xA0)
|
||||
TagRawSignature = uint8(0x80)
|
||||
)
|
||||
|
||||
type Signature struct {
|
||||
|
@ -19,42 +21,61 @@ type Signature struct {
|
|||
}
|
||||
|
||||
func ParseSignature(message, resp []byte) (*Signature, error) {
|
||||
pubKey, err := apdu.FindTag(resp, apdu.Tag{TagSignatureTemplate}, apdu.Tag{0x80})
|
||||
// check for old template first because TagRawSignature matches the pubkey tag
|
||||
template, err := apdu.FindTag(resp, apdu.Tag{TagSignatureTemplate})
|
||||
if err == nil {
|
||||
return parseLegacySignature(message, template)
|
||||
}
|
||||
|
||||
sig, err := apdu.FindTag(resp, apdu.Tag{TagRawSignature})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, err := apdu.FindTagN(resp, 0, apdu.Tag{TagSignatureTemplate}, apdu.Tag{0x30}, apdu.Tag{0x02})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return ParseRecoverableSignature(message, sig)
|
||||
}
|
||||
|
||||
func ParseRecoverableSignature(message, sig []byte) (*Signature, error) {
|
||||
if len(sig) != 65 {
|
||||
return nil, errors.New("invalid signature")
|
||||
}
|
||||
|
||||
if len(r) > 32 {
|
||||
r = r[len(r)-32:]
|
||||
}
|
||||
|
||||
s, err := apdu.FindTagN(resp, 1, apdu.Tag{TagSignatureTemplate}, apdu.Tag{0x30}, apdu.Tag{0x02})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(s) > 32 {
|
||||
s = s[len(s)-32:]
|
||||
}
|
||||
|
||||
v, err := calculateV(message, pubKey, r, s)
|
||||
pubKey, err := crypto.Ecrecover(message, sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Signature{
|
||||
pubKey: pubKey,
|
||||
r: r,
|
||||
s: s,
|
||||
v: v,
|
||||
r: sig[0:32],
|
||||
s: sig[32:64],
|
||||
v: sig[64],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DERSignatureToRS(tlv []byte) ([]byte, []byte, error) {
|
||||
r, err := apdu.FindTagN(tlv, 0, apdu.Tag{0x30}, apdu.Tag{0x02})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if len(r) > 32 {
|
||||
r = r[len(r)-32:]
|
||||
}
|
||||
|
||||
s, err := apdu.FindTagN(tlv, 1, apdu.Tag{0x30}, apdu.Tag{0x02})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if len(s) > 32 {
|
||||
s = s[len(s)-32:]
|
||||
}
|
||||
|
||||
return r, s, nil
|
||||
}
|
||||
|
||||
func (s *Signature) PubKey() []byte {
|
||||
return s.pubKey
|
||||
}
|
||||
|
@ -71,9 +92,33 @@ func (s *Signature) V() byte {
|
|||
return s.v
|
||||
}
|
||||
|
||||
func parseLegacySignature(message, template []byte) (*Signature, error) {
|
||||
pubKey, err := apdu.FindTag(template, apdu.Tag{0x80})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r, s, err := DERSignatureToRS(template)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v, err := calculateV(message, pubKey, r, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Signature{
|
||||
pubKey: pubKey,
|
||||
r: r,
|
||||
s: s,
|
||||
v: v,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func calculateV(message, pubKey, r, s []byte) (v byte, err error) {
|
||||
rs := append(r, s...)
|
||||
for i := 0; i < 2; i++ {
|
||||
for i := 0; i < 4; i++ {
|
||||
v = byte(i)
|
||||
sig := append(rs, v)
|
||||
rec, err := crypto.Ecrecover(message, sig)
|
||||
|
|
Loading…
Reference in New Issue