Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,9 @@ type Command struct {
defaultSetup bool
apiKey string
nftValue float64
accountType uint
changeAddr uint
addrIndex uint
}

func showVersion() {
Expand Down Expand Up @@ -552,6 +555,9 @@ func Run(args []string) {
flag.BoolVar(&cmd.defaultSetup, "defaultSetup", false, "Add Faucet Quorums")
flag.StringVar(&cmd.apiKey, "apikey", "", "Give the API Key corresponding to the DID")
flag.Float64Var(&cmd.nftValue, "nftValue", 0.0, "Value of the NFT")
flag.UintVar(&cmd.accountType, "accountType", 0, "to organise addreses of different wallets")
flag.UintVar(&cmd.changeAddr, "changeAddr", 0, "to create new address(did) for each transaction to maintain privacy")
flag.UintVar(&cmd.addrIndex, "addrIndex", 0, "to create a new child address(did) by changing the index")

if len(os.Args) < 2 {
fmt.Println("Invalid Command")
Expand Down
4 changes: 4 additions & 0 deletions command/did.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ func (cmd *Command) CreateDID() {
MnemonicFile: cmd.mnemonicFile,
ChildPath: cmd.ChildPath,
}
cfg.HDPath[2] = uint32(cmd.accountType)
cfg.HDPath[3] = uint32(cmd.changeAddr)
cfg.HDPath[4] = uint32(cmd.addrIndex)

msg, status := cmd.c.CreateDID(&cfg)
if !status {
cmd.log.Error("Failed to create DID", "message", msg)
Expand Down
23 changes: 23 additions & 0 deletions core/did.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ import (
"github.com/rubixchain/rubixgoplatform/did"
"github.com/rubixchain/rubixgoplatform/setup"
"github.com/rubixchain/rubixgoplatform/util"
"github.com/tyler-smith/go-bip32"
)

const (
HDPurpose uint32 = bip32.FirstHardenedChild + 44 // 44 for BIP-44
RBTcoinType uint32 = bip32.FirstHardenedChild + 1001 // for RBT tokens in Rubix mainnet
TestRbtcoinType uint32 = bip32.FirstHardenedChild + 1002 // for test RBTs in Rubix testnet
)

const (
NonWalletAccount uint32 = iota
XellAcount
SafePassAccount
)

// Struct to match the API response
Expand Down Expand Up @@ -218,6 +231,15 @@ func (c *Core) CreateDID(didCreate *did.DIDCreate) (string, error) {
c.log.Error("root did is already exist")
return "", fmt.Errorf("root did is already exist")
}
// HDPath defines the required path to create a new child address/did
didCreate.HDPath[0] = HDPurpose
if c.testNet {
didCreate.HDPath[1] = TestRbtcoinType
} else {
didCreate.HDPath[1] = RBTcoinType
}
didCreate.HDPath[2] = bip32.FirstHardenedChild + didCreate.HDPath[2]

did, err := c.d.CreateDID(didCreate)
if err != nil {
return "", err
Expand All @@ -234,6 +256,7 @@ func (c *Core) CreateDID(didCreate *did.DIDCreate) (string, error) {
if didCreate.RootDID {
dt.RootDID = 1
}

err = c.w.CreateDID(&dt)
if err != nil {
c.log.Error("Failed to create did in the wallet", "err", err)
Expand Down
8 changes: 4 additions & 4 deletions crypto/bip39_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import (
secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4"
)

func BIPtest(t *testing.T, mnemonic string, pwd string) {
func BIPtest(t *testing.T, mnemonic string, pwd string, childPath int) {

masterKey, err := BIPGenerateMasterKeyFromMnemonic(mnemonic)
if err != nil {
t.Fatal("failed to generate key pair", "err", err)
}

priv, pub, err := BIPGenerateChild(string(masterKey), 0, pwd)
priv, pub, err := BIPGenerateChild(string(masterKey), childPath, pwd)
if err != nil {
t.Fatal("failed to generate child", "err", err)
}
Expand Down Expand Up @@ -44,6 +44,6 @@ func BIPtest(t *testing.T, mnemonic string, pwd string) {
}
}
func TestBIPKeyGeneration(t *testing.T) {
BIPtest(t, "cup symbol flee find decline market tube border artist clever make plastic unfold chaos float artwork sustain suspect risk process fox decrease west seven", "test1")
BIPtest(t, "cub symbol flee find decline market tube border artist clever make plastic unfold chaos float artwork sustain suspect risk process fox decrease west seven", "test2")
BIPtest(t, "cup symbol flee find decline market tube border artist clever make plastic unfold chaos float artwork sustain suspect risk process fox decrease west seven", "test1", 0)
BIPtest(t, "cub symbol flee find decline market tube border artist clever make plastic unfold chaos float artwork sustain suspect risk process fox decrease west seven", "test2", 1)
}
136 changes: 136 additions & 0 deletions crypto/bip44.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package crypto

import (
"encoding/pem"
"fmt"

// "github.com/ethereum/go-ethereum/common"
// "github.com/ethereum/go-ethereum/core/types"
// "github.com/ethereum/go-ethereum/crypto"
secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/tyler-smith/go-bip32"
"github.com/tyler-smith/go-bip39"
)

// HDWallet manages a hierarchical deterministic wallet
type HDWallet struct {
Mnemonic string
Seed []byte
MasterKey *bip32.Key
Accounts map[uint32][]string // account index -> list of addresses
}

const (
Purpose uint32 = 44 // 44 for BIP-44
RBTcoinType uint32 = 1001 // for RBT tokens in Rubix mainnet
TestRbtcoinType uint32 = 1002 // for test RBTs in Rubix testnet
)

const (
NonWalletAccount uint32 = iota
XellAcount
SafePassAccount
)

// NewHDWallet creates a new HD wallet from a mnemonic or generates a new one
func NewHDWallet(mnemonic string) (*HDWallet, error) {
if mnemonic == "" {
// Generate a new 24-word mnemonic (256-bit entropy)
mnemonic = BIPGenerateMnemonic()
}

// Validate mnemonic
if !bip39.IsMnemonicValid(mnemonic) {
return nil, fmt.Errorf("invalid mnemonic")
}

// Derive seed
seed := bip39.NewSeed(mnemonic, "") // Empty passphrase

// Derive master key
masterKey, err := bip32.NewMasterKey(seed)
if err != nil {
return nil, fmt.Errorf("failed to create master key: %v", err)
}

return &HDWallet{
Mnemonic: mnemonic,
Seed: seed,
MasterKey: masterKey,
Accounts: make(map[uint32][]string),
}, nil
}

// DeriveKey derives a key for a given BIP-44 path (m/purpose'/coin_type'/account'/change/index)
func (w *HDWallet) DerivePrivateKey(path []uint32) (*bip32.Key, error) {
// path := []uint32{
// bip32.FirstHardenedChild + Purpose, // e.g., 44'
// bip32.FirstHardenedChild + coinType, // e.g., 1001'
// bip32.FirstHardenedChild + accountType, // e.g., 0'
// change, // 0 (external) or 1 (change)
// index, // 0, 1, 2, ...
// }
if len(path) != 5 {
return nil, fmt.Errorf("failed to derive key, invalid HD path length: %v, should be precisely 5", len(path))
}

currentKey := w.MasterKey
for _, i := range path {
var err error
currentKey, err = currentKey.NewChildKey(i)
if err != nil {
return nil, fmt.Errorf("failed to derive key at index %d: %v", i, err)
}
}
return currentKey, nil
}

func DeriveKeyPair(privateKey *bip32.Key, pwd string) ([]byte, []byte, error) {
privKey := secp256k1.PrivKeyFromBytes(privateKey.Key)
privkeybyte := privKey.Serialize()

pubkeybyte := privKey.PubKey().SerializeUncompressed()
var pemEncPriv []byte
if pwd != "" {
encBlock, err := Seal(pwd, privkeybyte)
if err != nil {
return nil, nil, err
}
_, err = UnSeal(pwd, encBlock)
if err != nil {
return nil, nil, err
}
pemEncPriv = pem.EncodeToMemory(&pem.Block{Type: "ENCRYPTED PRIVATE KEY", Bytes: encBlock})
} else {
pemEncPriv = pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privkeybyte})
}
pemEncPub := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubkeybyte})

return pemEncPriv, pemEncPub, nil
}

// func GenerateHDChildKeys(coinType, accountType, change, index uint32, mnemonic, pwd string) ([]byte, []byte, error) {

// hdWallet, err := NewHDWallet(mnemonic)
// if err != nil {
// return nil, nil, err
// }

// path := []uint32{
// bip32.FirstHardenedChild + Purpose, // e.g., 44'
// bip32.FirstHardenedChild + coinType, // e.g., 1001
// bip32.FirstHardenedChild + accountType, // e.g., 0'
// change, // 0 (external) or 1 (change)
// index, // 0, 1, 2, ...
// }
// privateKey, err := hdWallet.DerivePrivateKey(path)
// if err != nil {
// return nil, nil, err
// }

// privKey, pubKey, err := DeriveKeyPair(privateKey, pwd)
// if err != nil {
// return nil, nil, err
// }
// return privKey, pubKey, nil
// }
65 changes: 65 additions & 0 deletions crypto/bip44_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package crypto

import (
"crypto/rand"
"testing"

secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/tyler-smith/go-bip32"
)

func TestHDKeyGeneration(t *testing.T) {
GenerateHDKeysTest(t, "cup symbol flee find decline market tube border artist clever make plastic unfold chaos float artwork sustain suspect risk process fox decrease west seven", "test1", TestRbtcoinType, NonWalletAccount, 0, 0)
GenerateHDKeysTest(t, "cup symbol flee find decline market tube border artist clever make plastic unfold chaos float artwork sustain suspect risk process fox decrease west seven", "test1", TestRbtcoinType, NonWalletAccount, 0, 1)
}

func GenerateHDKeysTest(t *testing.T, mnemonic string, pwd string, coinType, accountType, change, index uint32) {
hdWallet, err := NewHDWallet(mnemonic)
if err != nil {
t.Fatal("failed to generate HD key pair", "err", err)
}

path := []uint32{
bip32.FirstHardenedChild + Purpose, // e.g., 44'
bip32.FirstHardenedChild + coinType, // e.g., 1001
bip32.FirstHardenedChild + accountType, // e.g., 0'
change, // 0 (external) or 1 (change)
index, // 0, 1, 2, ...
}
privateKey, err := hdWallet.DerivePrivateKey(path)
if err != nil {
t.Fatal("failed to derive HD priv key", "err", err)
}

privKey, pubKey, err := DeriveKeyPair(privateKey, pwd)
if err != nil {
t.Fatal("failed to derive HD key pair", "err", err)
}

privkey, pubkey, err := DecodeBIPKeyPair(pwd, privKey, pubKey)
if err != nil {
t.Fatal("failed to decode key pair", "err", err)
}

data, err := GetRandBytes(rand.Reader, 20)
if err != nil {
t.Fatal("failed to generate random number", "err", err)
}

privkeyback := secp256k1.PrivKeyFromBytes(privkey)
privKeySer := privkeyback.ToECDSA()
pubkeyback, err := secp256k1.ParsePubKey(pubkey)
if err != nil {
t.Fatal("failed to parse pub key", "err", err)
}
pubKeySer := pubkeyback.ToECDSA()

sig, err := BIPSign(privKeySer, data)
if err != nil {
t.Fatal("failed to do signature", "err", err)
}

if !BIPVerify(pubKeySer, data, sig) {
t.Fatal("failed to do verify signature", "err", err)
}
}
17 changes: 12 additions & 5 deletions did/did.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,22 @@ func (d *DID) CreateDID(didCreate *DIDCreate) (string, error) {
mnemonic = string(_mnemonic)
}

masterKey, err := crypto.BIPGenerateMasterKeyFromMnemonic(mnemonic)
//integrating bip-44 to support HD wallets
hdWallet, err := crypto.NewHDWallet(mnemonic)
if err != nil {
d.log.Error("failed to create keypair", "err", err)
d.log.Error("failed to create keypair from mnemonic", "err", err)
return "", err
}
privateKey, err := hdWallet.DerivePrivateKey(didCreate.HDPath[:])
if err != nil {
d.log.Error("failed to create hd keypair", "err", err)
return "", err
}

//generating private and public key pair
pvtKey, pubKey, err := crypto.BIPGenerateChild(string(masterKey), didCreate.ChildPath, didCreate.PrivPWD)
pvtKey, pubKey, err := crypto.DeriveKeyPair(privateKey, didCreate.PrivPWD)
if err != nil {
d.log.Error("failed to create child", "err", err)
d.log.Error("failed to derive keypair", "err", err)
return "", err
}

err = util.FileWrite(dirName+"/private/"+MnemonicFileName, []byte(mnemonic))
Expand Down
37 changes: 19 additions & 18 deletions did/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,25 @@ const (
)

type DIDCreate struct {
Type int `json:"type"`
Dir string `json:"dir"`
Config string `json:"config"`
RootDID bool `json:"root_did"`
MasterDID string `json:"master_did"`
Secret string `json:"secret"`
PrivPWD string `json:"priv_pwd"`
QuorumPWD string `json:"quorum_pwd"`
ImgFile string `json:"img_file"`
DIDImgFileName string `json:"did_img_file"`
PubImgFile string `json:"pub_img_file"`
PrivImgFile string `json:"priv_img_file"`
PubKeyFile string `json:"pub_key_file"`
PrivKeyFile string `json:"priv_key_file"`
QuorumPubKeyFile string `json:"quorum_pub_key_file"`
QuorumPrivKeyFile string `json:"quorum_priv_key_file"`
MnemonicFile string `json:"mnemonic_file"`
ChildPath int `json:"childPath"`
Type int `json:"type"`
Dir string `json:"dir"`
Config string `json:"config"`
RootDID bool `json:"root_did"`
MasterDID string `json:"master_did"`
Secret string `json:"secret"`
PrivPWD string `json:"priv_pwd"`
QuorumPWD string `json:"quorum_pwd"`
ImgFile string `json:"img_file"`
DIDImgFileName string `json:"did_img_file"`
PubImgFile string `json:"pub_img_file"`
PrivImgFile string `json:"priv_img_file"`
PubKeyFile string `json:"pub_key_file"`
PrivKeyFile string `json:"priv_key_file"`
QuorumPubKeyFile string `json:"quorum_pub_key_file"`
QuorumPrivKeyFile string `json:"quorum_priv_key_file"`
MnemonicFile string `json:"mnemonic_file"`
ChildPath int `json:"childPath"`
HDPath [5]uint32 `json:"hd_path"`
}

type DIDSignature struct {
Expand Down
Loading