Skip to content

Commit

Permalink
Merge pull request #2485 from dedis/darc_tsm
Browse files Browse the repository at this point in the history
Add the TSM identity to Darc
  • Loading branch information
ineiti authored Feb 13, 2022
2 parents 0d1b33d + 581c742 commit 03a0f3d
Show file tree
Hide file tree
Showing 10 changed files with 1,228 additions and 233 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/full_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
- cron: "12 6 * * 1-5"

env:
GO_VERSION: 1.15
GO_VERSION: 1.17

jobs:
go:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/lint_build_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:

env:
NODE_VERSION: 10
GO_VERSION: 1.15
GO_VERSION: 1.16
JAVA_VERSION: 9

jobs:
Expand Down Expand Up @@ -39,7 +39,7 @@ jobs:

strategy:
matrix:
golang-version: [1.14, 1.15] # Cannot use env. here :(
golang-version: [1.16, 1.17] # Cannot use env. here :(

steps:
- uses: actions/checkout@main
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
- 'v*'

env:
GO_VERSION: 1.15
GO_VERSION: 1.17

jobs:
go:
Expand Down
134 changes: 120 additions & 14 deletions darc/darc.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ package darc
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/sha512"
"crypto/x509"
Expand Down Expand Up @@ -773,6 +775,10 @@ func (s Signer) Type() int {
return 3
case s.EvmContract != nil:
return 4
case s.DID != nil:
return 5
case s.tsm != nil:
return 6
default:
return -1
}
Expand All @@ -790,6 +796,8 @@ func (s Signer) Identity() Identity {
return NewIdentityProxy(s.Proxy)
case 4:
return NewIdentityEvmContract(s.EvmContract)
case 6:
return NewIdentityTSM(s.tsm.PrivateKey.PublicKey)
default:
return Identity{}
}
Expand All @@ -811,6 +819,8 @@ func (s Signer) Sign(msg []byte) ([]byte, error) {
return s.Proxy.Sign(msg)
case 4:
return s.EvmContract.Sign(msg)
case 6:
return s.tsm.Sign(msg)
default:
return nil, errors.New("unknown signer type")
}
Expand All @@ -821,7 +831,7 @@ func (s Signer) GetPrivate() (kyber.Scalar, error) {
switch s.Type() {
case 1:
return s.Ed25519.Secret, nil
case 0, 2, 3:
case 0, 2, 3, 4, 5, 6:
return nil, errors.New("signer lacks a private key")
default:
return nil, errors.New("signer is of unknown type")
Expand All @@ -845,6 +855,8 @@ func (id Identity) Equal(id2 *Identity) bool {
return id.Proxy.Equal(id2.Proxy)
case 4:
return id.EvmContract.Equal(id2.EvmContract)
case 6:
return bytes.Equal(id.TSM.PublicKey, id2.TSM.PublicKey)
}
return false
}
Expand All @@ -863,26 +875,18 @@ func (id Identity) Type() int {
return 3
case id.EvmContract != nil:
return 4
case id.DID != nil:
return 5
case id.TSM != nil:
return 6
}
return -1
}

// PrimaryIdentity returns true if the identity is a primary identity, which is
// an identity that is associated with a concrete public/private key.
func (id Identity) PrimaryIdentity() bool {
switch {
case id.Darc != nil:
return false
case id.Ed25519 != nil:
return true
case id.X509EC != nil:
return true
case id.Proxy != nil:
return true
case id.EvmContract != nil:
return true
}
return false
return id.Type() != 0
}

// TypeString returns the string of the type of the identity.
Expand All @@ -898,6 +902,10 @@ func (id Identity) TypeString() string {
return "proxy"
case 4:
return "evm_contract"
case 5:
return "did"
case 6:
return "tsm"
default:
return "No identity"
}
Expand All @@ -918,6 +926,11 @@ func (id Identity) String() string {
bevmString := hex.EncodeToString(id.EvmContract.BEvmID)
addrString := id.EvmContract.Address.Hex()
return fmt.Sprintf("%s:%s:%s", id.TypeString(), bevmString, addrString)
case 5:
return fmt.Sprintf("%s:%s", id.TypeString(), id.DID.DID)
case 6:
buf, _ := id.TSM.MarshalBinary()
return fmt.Sprintf("%s:%x", id.TypeString(), buf)
default:
return "No identity"
}
Expand All @@ -937,6 +950,8 @@ func (id Identity) Verify(msg, sig []byte) error {
return id.Proxy.Verify(msg, sig)
case 4:
return id.EvmContract.Verify(msg, sig)
case 6:
return id.TSM.Verify(msg, sig)
default:
return errors.New("unknown identity")
}
Expand Down Expand Up @@ -964,6 +979,8 @@ func (id Identity) GetPublicBytes() []byte {
return buf
case 4:
return id.EvmContract.Address[:]
case 6:
return id.TSM.PublicKey
default:
return nil
}
Expand Down Expand Up @@ -1012,6 +1029,52 @@ func NewIdentityX509EC(public []byte) Identity {
}
}

// NewIdentityTSM creates a new TSM identity struct given a public key
func NewIdentityTSM(ecKey ecdsa.PublicKey) Identity {
return Identity{
TSM: &IdentityTSM{
PublicKey: elliptic.MarshalCompressed(ecKey.Curve, ecKey.X,
ecKey.Y),
ecKey: &ecKey,
},
}
}

// GetPublic returns the public key of the Identity.
// As it is stored in binary format, it might have to be converted.
func (ide *IdentityTSM) GetPublic() ecdsa.PublicKey {
if ide.ecKey == nil {
ecKey := ecdsa.PublicKey{}
x, y := elliptic.UnmarshalCompressed(elliptic.P256(), ide.PublicKey)
ecKey.X = x
ecKey.Y = y
ecKey.Curve = elliptic.P256()
ide.ecKey = &ecKey
}
return *ide.ecKey
}

// Verify the signature of the identity.
func (ide *IdentityTSM) Verify(msg []byte, sig []byte) error {
ecKey := ide.GetPublic()
valid := ecdsa.VerifyASN1(&ecKey, msg, sig)
if !valid {
return errors.New("Signature failed to verify")
}
return nil
}

// MarshalBinary returns the compressed public key
func (ide IdentityTSM) MarshalBinary() ([]byte, error) {
return ide.PublicKey, nil
}

// UnmarshalBinary stores the compressed public key
func (ide *IdentityTSM) UnmarshalBinary(data []byte) error {
ide.PublicKey = data
return nil
}

// NewIdentityProxy creates a new OpenID Connect identity struct.
func NewIdentityProxy(s *SignerProxy) Identity {
return Identity{
Expand Down Expand Up @@ -1120,6 +1183,8 @@ func ParseIdentity(in string) (Identity, error) {
return parseIDProxy(fields[1])
case "evm_contract":
return parseIDEvmContract(fields[1])
case "tsm":
return parseIDTSM(fields[1])
default:
return Identity{}, fmt.Errorf("unknown identity type %v", fields[0])
}
Expand All @@ -1142,6 +1207,16 @@ func parseIDX509ec(in string) (Identity, error) {
return Identity{X509EC: &IdentityX509EC{Public: id}}, nil
}

func parseIDTSM(in string) (Identity, error) {
idTSM := IdentityTSM{}
_, err := hex.Decode(idTSM.PublicKey, []byte(in))
if err != nil {
return Identity{}, err
}

return Identity{TSM: &idTSM}, nil
}

func parseIDDarc(in string) (Identity, error) {
id := make([]byte, hex.DecodedLen(len(in)))
_, err := hex.Decode(id, []byte(in))
Expand Down Expand Up @@ -1447,6 +1522,37 @@ func (kcs SignerX509EC) Sign(msg []byte) ([]byte, error) {
return nil, errors.New("not yet implemented")
}

// SignerTSM holds the private key necessary to sign Darcs.
// As this is only used in testing, it is not in proto.go and thus
// not exported.
type SignerTSM struct {
PrivateKey ecdsa.PrivateKey
}

// NewSignerTSM creates a tsm signer with a SECP256K1 key.
// If a nil key is given, then a random key is generated.
// This is mostly used for testing, as the real TSM is a hardware device
// and not supported in tests.
func NewSignerTSM(private ecdsa.PrivateKey) Signer {
if private.D == nil {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic("couldn't generate key: " + err.Error())
}
private = *priv
}
return Signer{tsm: &SignerTSM{
PrivateKey: private,
}}
}

// Sign the message with the private key.
// This is not really possible with the TSM signer,
// so this is mostly useful for testing.
func (kcs SignerTSM) Sign(msg []byte) ([]byte, error) {
return ecdsa.SignASN1(rand.Reader, &kcs.PrivateKey, msg)
}

// NewSignerProxy creates a new SignerProxy. When Sign is called, the getSignature
// callback will be called, so that the caller can use the appropriate mechanism
// to retrieve and/or construct the signature.
Expand Down
39 changes: 38 additions & 1 deletion darc/darc_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package darc

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"math/big"
"net/url"
"strings"
"testing"
Expand Down Expand Up @@ -754,7 +759,6 @@ func testIdentity(t *testing.T, sig Signer) {
msg := []byte("something secret")
signed, err := sig.Sign(msg)
require.NoError(t, err)

id := sig.Identity()
require.NoError(t, id.Verify(msg, signed))
require.Error(t, id.Verify([]byte("wrong message"), signed))
Expand All @@ -763,4 +767,37 @@ func testIdentity(t *testing.T, sig Signer) {
// Test the different identities available - currently only Ed25519.
func TestIdentities(t *testing.T) {
testIdentity(t, NewSignerEd25519(nil, nil))
testIdentity(t, NewSignerTSM(ecdsa.PrivateKey{}))
}

// Test a signature from the TSM
func TestTSMSignature(t *testing.T) {
var x, _ = new(big.Int).SetString("25613385885653880697990944418179706546134037329992108968315147853972798913688", 10)
var y, _ = new(big.Int).SetString("74946767262888349555270609195205284686604880870734462312238891495596941025713", 10)
pk := ecdsa.PublicKey{
Curve: elliptic.P256(),
X: x,
Y: y,
}
id := NewIdentityTSM(pk)

msg := []byte(`Hello World`)
digest := sha256.Sum256(msg)

// Signature from code example go-tsm-sdk corresponding to tsm public
// key example
signed, _ := hex.DecodeString("304402204f0b20a44efacec7b0514683233a79552026fe80e468078f6fed6cfe3f3e8a0402201eb12db7f6fe0828cafe8b0a032a37ff377b342799cfe77cfbac40c8ec1fa9e8")

require.NoError(t, id.Verify(digest[:], signed))
require.Error(t, id.Verify([]byte("wrong message"), signed))
}

// Make sure Marshalling and Unmarshalling work
func TestTSMMarshalling(t *testing.T) {
id := NewSignerTSM(ecdsa.PrivateKey{}).Identity()
buf, err := id.TSM.MarshalBinary()
require.NoError(t, err)
var id2 IdentityTSM
require.NoError(t, id2.UnmarshalBinary(buf))
require.True(t, id.Equal(&Identity{TSM: &id2}))
}
4 changes: 2 additions & 2 deletions darc/expression/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ the syntax we use is from: https://en.wikipedia.org/wiki/Extended_Backus%E2%80%9
expr = term, [ '&', term ]*
term = factor, [ '|', factor ]*
factor = '(', expr, ')' | id | openid
identity = (darc|ed25519|x509ec):[0-9a-fA-F]+
identity = (darc|ed25519|x509ec|tsm):[0-9a-fA-F]+
proxy = proxy:[0-9a-fA-F]+:[^ \n\t]*
evm_identity = evm_contract:[0-9a-fA-F]+:0x[0-9a-fA-F]+
attr = attr:[0-9a-zA-Z\-\_]+:[^ \n\t]*
Expand Down Expand Up @@ -154,7 +154,7 @@ func (e Expr) AddAndElement(id string) Expr {
func identity() parsec.Parser {
return func(s parsec.Scanner) (parsec.ParsecNode, parsec.Scanner) {
_, s = s.SkipAny(`^[ \n\t]+`)
p := parsec.Token(`(darc|ed25519|x509ec):[0-9a-fA-F]+`, "HEX")
p := parsec.Token(`(darc|ed25519|x509ec|tsm):[0-9a-fA-F]+`, "HEX")
return p(s)
}
}
Expand Down
11 changes: 11 additions & 0 deletions darc/proto.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package darc

import (
"crypto/ecdsa"

"go.dedis.ch/cothority/v3/darc/expression"
"go.dedis.ch/kyber/v3"
"go.dedis.ch/onet/v3/network"
Expand Down Expand Up @@ -71,13 +73,21 @@ type Identity struct {
EvmContract *IdentityEvmContract
// A claim signed by one of the keys in a DID Doc
DID *IdentityDID
// Public-key identity from an ECDSA key
TSM *IdentityTSM
}

// IdentityEd25519 holds a Ed25519 public key (Point)
type IdentityEd25519 struct {
Point kyber.Point
}

// IdentityTSM holds a secp256k1 key (array of bytes)
type IdentityTSM struct {
PublicKey []byte
ecKey *ecdsa.PublicKey
}

// IdentityX509EC holds a public key from a X509EC
type IdentityX509EC struct {
Public []byte
Expand Down Expand Up @@ -159,6 +169,7 @@ type Signer struct {
Proxy *SignerProxy
EvmContract *SignerEvmContract
DID *SignerDID
tsm *SignerTSM
}

// SignerEd25519 holds a public and private keys necessary to sign Darcs
Expand Down
Loading

0 comments on commit 03a0f3d

Please sign in to comment.