mirror of
https://github.com/alexgo-io/gaze-indexer.git
synced 2026-04-29 20:25:24 +08:00
feat: generate key command and crypto pkg
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,6 +3,8 @@
|
||||
# Eg. ignore.foo_test.go, ignore.credentials.json, ignore.config.yml
|
||||
ignore.*
|
||||
|
||||
/key
|
||||
|
||||
**/cmd.local/**
|
||||
**/cmd.local.**/**
|
||||
|
||||
|
||||
@@ -14,13 +14,14 @@ var (
|
||||
// root command
|
||||
cmd = &cobra.Command{
|
||||
Use: "gaze",
|
||||
Long: `Description of gaze indexer`,
|
||||
Long: `Gaze in a Bitcoin meta-protocol indexer`,
|
||||
}
|
||||
|
||||
// sub-commands
|
||||
// sub-commandsf
|
||||
cmds = []*cobra.Command{
|
||||
NewVersionCommand(),
|
||||
NewRunCommand(),
|
||||
NewGenerateKeypairCommand(),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
82
cmd/cmd_generate_keypair.go
Normal file
82
cmd/cmd_generate_keypair.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/gaze-network/indexer-network/common/errs"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type generateKeypairCmdOptions struct {
|
||||
Path string
|
||||
}
|
||||
|
||||
func NewGenerateKeypairCommand() *cobra.Command {
|
||||
opts := &generateKeypairCmdOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "generate-keypair",
|
||||
Short: "Generate new public/private keypair for encryption and signature generation",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return generateKeypairHandler(opts, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&opts.Path, "path", "/data/keys", `Path to save to key pair file`)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func generateKeypairHandler(opts *generateKeypairCmdOptions, _ *cobra.Command, _ []string) error {
|
||||
fmt.Printf("Generating key pair\n")
|
||||
privKeyBytes := make([]byte, 32)
|
||||
|
||||
_, err := rand.Read(privKeyBytes)
|
||||
if err != nil {
|
||||
return errors.Wrap(errs.SomethingWentWrong, "random bytes")
|
||||
}
|
||||
_, pubKey := btcec.PrivKeyFromBytes(privKeyBytes)
|
||||
serializedPubKey := pubKey.SerializeCompressed()
|
||||
|
||||
// fmt.Println(hex.EncodeToString(privKeyBytes))
|
||||
|
||||
fmt.Printf("Public key: %s\n", hex.EncodeToString(serializedPubKey))
|
||||
err = os.MkdirAll(opts.Path, 0o755)
|
||||
if err != nil {
|
||||
return errors.Wrap(errs.SomethingWentWrong, "create directory")
|
||||
}
|
||||
|
||||
privateKeyPath := path.Join(opts.Path, "priv.key")
|
||||
|
||||
_, err = os.Stat(privateKeyPath)
|
||||
if err == nil {
|
||||
fmt.Printf("Existing private key found at %s\n[WARNING] THE EXISTING PRIVATE KEY WILL BE LOST\nType [replace] to replace existing private key: ", privateKeyPath)
|
||||
var ans string
|
||||
fmt.Scanln(&ans)
|
||||
if ans != "replace" {
|
||||
fmt.Printf("Keypair generation aborted\n")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
err = os.WriteFile(privateKeyPath, []byte(hex.EncodeToString(privKeyBytes)), 0o644)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "write private key file")
|
||||
}
|
||||
fmt.Printf("Private key saved at %s\n", privateKeyPath)
|
||||
|
||||
publicKeyPath := path.Join(opts.Path, "pub.key")
|
||||
err = os.WriteFile(publicKeyPath, []byte(hex.EncodeToString(serializedPubKey)), 0o644)
|
||||
if err != nil {
|
||||
return errors.Wrap(errs.SomethingWentWrong, "write public key file")
|
||||
}
|
||||
fmt.Printf("Public key saved at %s\n", publicKeyPath)
|
||||
return nil
|
||||
}
|
||||
8
go.mod
8
go.mod
@@ -26,15 +26,17 @@ require (
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.3 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||
github.com/ecies/go/v2 v2.0.9 // indirect
|
||||
github.com/ethereum/go-ethereum v1.13.5 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.18.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
|
||||
92
pkg/crypto/crypto.go
Normal file
92
pkg/crypto/crypto.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec/v2"
|
||||
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/cockroachdb/errors"
|
||||
ecies "github.com/ecies/go/v2"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
privateKey *btcec.PrivateKey
|
||||
eciesPrivateKey *ecies.PrivateKey
|
||||
}
|
||||
|
||||
func New(privateKeyStr string) (*Client, error) {
|
||||
if privateKeyStr != "" {
|
||||
privateKeyBytes, err := hex.DecodeString(privateKeyStr)
|
||||
println(len(privateKeyBytes))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode private key")
|
||||
}
|
||||
privateKey, _ := btcec.PrivKeyFromBytes(privateKeyBytes)
|
||||
|
||||
eciesPrivateKey := ecies.NewPrivateKeyFromBytes(privateKeyBytes)
|
||||
return &Client{
|
||||
privateKey: privateKey,
|
||||
eciesPrivateKey: eciesPrivateKey,
|
||||
}, nil
|
||||
}
|
||||
return &Client{}, nil
|
||||
}
|
||||
|
||||
func (c *Client) Sign(message string) string {
|
||||
messageHash := chainhash.DoubleHashB([]byte(message))
|
||||
signature := ecdsa.Sign(c.privateKey, messageHash)
|
||||
return hex.EncodeToString(signature.Serialize())
|
||||
}
|
||||
|
||||
func (c *Client) Verify(message, sigStr, pubKeyStr string) (bool, error) {
|
||||
sigBytes, err := hex.DecodeString(sigStr)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "signature decode")
|
||||
}
|
||||
|
||||
pubBytes, err := hex.DecodeString(pubKeyStr)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "pubkey decode")
|
||||
}
|
||||
pubKey, err := btcec.ParsePubKey(pubBytes)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "pubkey parse")
|
||||
}
|
||||
|
||||
messageHash := chainhash.DoubleHashB([]byte(message))
|
||||
|
||||
signature, err := ecdsa.ParseSignature(sigBytes)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "signature parse")
|
||||
}
|
||||
return signature.Verify(messageHash, pubKey), nil
|
||||
}
|
||||
|
||||
func (c *Client) Encrypt(message, pubKeyStr string) (string, error) {
|
||||
pubKey, err := ecies.NewPublicKeyFromHex(pubKeyStr)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "parse pubkey")
|
||||
}
|
||||
|
||||
ciphertext, err := ecies.Encrypt(pubKey, []byte(message))
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "encrypt message")
|
||||
}
|
||||
|
||||
ciphertextStr := base64.StdEncoding.EncodeToString(ciphertext)
|
||||
return ciphertextStr, nil
|
||||
}
|
||||
|
||||
func (c *Client) Decrypt(ciphertextStr string) (string, error) {
|
||||
ciphertext, err := base64.StdEncoding.DecodeString(ciphertextStr)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "decode ciphertext")
|
||||
}
|
||||
plaintext, err := ecies.Decrypt(c.eciesPrivateKey, ciphertext)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "decrypt")
|
||||
}
|
||||
return string(plaintext), nil
|
||||
}
|
||||
53
pkg/crypto/crypto_test.go
Normal file
53
pkg/crypto/crypto_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
privateKeyStr = "ce9c2fd75623e82a83ed743518ec7749f6f355f7301dd432400b087717fed2f2"
|
||||
pubKeyStr = "0251e2dfcdeea17cc9726e4be0855cd0bae19e64f3e247b10760cd76851e7df47e"
|
||||
)
|
||||
|
||||
func TestEncryptDecrypt(t *testing.T) {
|
||||
plaintext := "hello world"
|
||||
|
||||
privClient, err := New(privateKeyStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
pubClient, err := New("")
|
||||
assert.NoError(t, err)
|
||||
|
||||
ciphertext, err := pubClient.Encrypt(plaintext, pubKeyStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
decrypted, err := privClient.Decrypt(ciphertext)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, plaintext, decrypted)
|
||||
}
|
||||
|
||||
func TestSignVerify(t *testing.T) {
|
||||
plaintext := "hello world"
|
||||
invalidSignature := "3044022066504a82e2bc23167214e05497a1ca957add9cacc078aa69f5417079a4d56f0002206b215920b046c779d4a58d4029c26dbadcaf6d3c884b3463f44e70ef9146c1cd"
|
||||
|
||||
privClient, err := New(privateKeyStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
pubClient, err := New("")
|
||||
assert.NoError(t, err)
|
||||
|
||||
signature := privClient.Sign(plaintext)
|
||||
println(signature)
|
||||
verified, err := pubClient.Verify(plaintext, signature, pubKeyStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.True(t, verified)
|
||||
|
||||
verified, err = pubClient.Verify(plaintext, invalidSignature, pubKeyStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.False(t, verified)
|
||||
}
|
||||
Reference in New Issue
Block a user