diff --git a/cmd/readme.md b/cmd/readme.md index b16b0c100..28adcc4e4 100644 --- a/cmd/readme.md +++ b/cmd/readme.md @@ -143,3 +143,13 @@ go run main.go generate poow --node-seed "concur vocalist rotten busload gap quo ```bash go run main.go rollback blockchain --to-height 10 --db-path "../resource" --db-name "zoobc.db" ``` + +### Signature Signing data using Ed25519 +```bash +go run main.go signature sign ed25519 --data-bytes='1, 222, 54, 12, 32' --use-slip10=true +``` + +### Signature Verifying data +```bash +go run main.go signature verify --data-bytes='1, 222, 54, 12, 32' --signature-hex=0000000063851d61318eaf923ff72457fd9b5716db9904aacbe53eb1bc25cd8a7bf2816c61402b0c52d4324e1336bae4ea28194d6f5c531292fd266e63a293519f20c20b --account-address=WI-u0jyKMGsPHk6K7eT1Utnxc6WiKehkIEs87Zf3fIsH +``` \ No newline at end of file diff --git a/cmd/signature/cmd.go b/cmd/signature/cmd.go index 8e2dff020..357ad5c44 100644 --- a/cmd/signature/cmd.go +++ b/cmd/signature/cmd.go @@ -12,58 +12,185 @@ import ( "golang.org/x/crypto/sha3" ) +type ( + // GeneratorCommands represent struct of signature generator commands + GeneratorCommands struct { + Signature crypto.SignatureInterface + } +) + var ( + signatureCmdInstance *GeneratorCommands /* Signer command line tools */ + signatureCmd = &cobra.Command{ + Use: "signature", + Short: "signature command is a parent command for signature stuffs", + } signerCmd = &cobra.Command{ Use: "sign", Short: "sign provided data", Long: "sign any provided data by using the --seed parameter", } + + ed25519SignerCmd = &cobra.Command{ + Use: "ed25519", + Short: "sign using ed25519 algoritmn", + Long: "sign any provided data by using the --seed parameter", + } + + verifyCmd = &cobra.Command{ + Use: "verify", + Short: "verify provided signature ", + Long: "verify provided signature against provided data using public key", + } ) func init() { - signerCmd.Flags().StringVar(&dataHex, "data-hex", "", "hex string of the data to sign") - signerCmd.Flags().StringVar(&dataBytes, "data-bytes", "", "data bytes separated by `, `. eg:"+ + signatureCmd.PersistentFlags().StringVar(&dataHex, "data-hex", "", "hex string of the data to sign") + signatureCmd.PersistentFlags().StringVar(&dataBytes, "data-bytes", "", "data bytes separated by `, `. eg:"+ "--data-bytes='1, 222, 54, 12, 32'") - signerCmd.Flags().StringVar(&seed, "seed", "", "your secret phrase") - signerCmd.Flags().BoolVar(&hash, "hash", false, "turn this flag on to hash the data before signing") + + signerCmd.PersistentFlags().StringVar(&seed, "seed", "", "your secret phrase") + signerCmd.PersistentFlags().BoolVar(&hash, "hash", false, "turn this flag on to hash the data before signing") + ed25519SignerCmd.Flags().BoolVar(&ed25519UseSlip10, "use-slip10", false, "use slip10 to generate ed25519 private key for signing") + + verifyCmd.Flags().StringVar(&signatureHex, "signature-hex", "", "hex string of the signature") + verifyCmd.Flags().StringVar(&signatureBytes, "signature-bytes", "", "signature bytes stseparated by `, `. eg:"+ + "--signature-bytes='1, 222, 54, 12, 32'") + verifyCmd.Flags().StringVar(&accountAddress, "account-address", "", "the address who sign the data") + } +// Commands return main command of signature func Commands() *cobra.Command { - signerCmd.Run = SignData - return signerCmd + if signatureCmdInstance == nil { + signatureCmdInstance = &GeneratorCommands{ + Signature: &crypto.Signature{}, + } + } + ed25519SignerCmd.Run = signatureCmdInstance.SignEd25519 + signerCmd.AddCommand(ed25519SignerCmd) + signerCmd.Run = signatureCmdInstance.SignEd25519 + signatureCmd.AddCommand(signerCmd) + + verifyCmd.Run = signatureCmdInstance.VerySignature + signatureCmd.AddCommand(verifyCmd) + return signatureCmd } -func SignData(*cobra.Command, []string) { +// SignEd25519 is sign command handler using Ed25519 algorithm +func (gc *GeneratorCommands) SignEd25519(*cobra.Command, []string) { var ( unsignedBytes []byte hashedUnsignedBytes [32]byte + accountAddress string signature []byte + err error ) + if dataHex != "" { - unsignedBytes, _ = hex.DecodeString(dataHex) + unsignedBytes, err = hex.DecodeString(dataHex) + if err != nil { + panic("failed to decode data hex") + } } else { - txByteCharSlice := strings.Split(dataBytes, ", ") - for _, v := range txByteCharSlice { - byteValue, err := strconv.Atoi(v) - if err != nil { - panic("failed to parse transaction bytes") - } - unsignedBytes = append(unsignedBytes, byte(byteValue)) + unsignedBytes, err = parseBytesArgs(dataBytes, ", ") + if err != nil { + panic("failed to parse data bytes") } } + _, _, _, accountAddress, err = gc.Signature.GenerateAccountFromSeed( + model.SignatureType_DefaultSignature, + seed, + ed25519UseSlip10, + ) + if err != nil { + panic(err.Error()) + } if hash { hashedUnsignedBytes = sha3.Sum256(unsignedBytes) - signature, _ = (&crypto.Signature{}).Sign(hashedUnsignedBytes[:], model.SignatureType_DefaultSignature, seed) - } else { - signature, _ = (&crypto.Signature{}).Sign(unsignedBytes, model.SignatureType_DefaultSignature, seed) + unsignedBytes = hashedUnsignedBytes[:] + } + signature, err = gc.Signature.Sign( + unsignedBytes, + model.SignatureType_DefaultSignature, + seed, + ed25519UseSlip10, + ) + if err != nil { + panic(err.Error()) } - edUtil := crypto.NewEd25519Signature() - fmt.Printf("account-address:\t%v\n", edUtil.GetAddressFromSeed(seed)) + + fmt.Printf("account-address:\t%v\n", accountAddress) fmt.Printf("transaction-bytes:\t%v\n", unsignedBytes) fmt.Printf("transaction-hash:\t%v\n", hex.EncodeToString(hashedUnsignedBytes[:])) fmt.Printf("signature-bytes:\t%v\n", signature) fmt.Printf("signature-hex:\t%v\n", hex.EncodeToString(signature)) } + +// VerySignature is verify signature command hendler +func (gc *GeneratorCommands) VerySignature(*cobra.Command, []string) { + var ( + unsignedBytes []byte + signature []byte + failedVerifyCause = "none" + isVerified = true + err error + ) + if dataHex != "" { + unsignedBytes, err = hex.DecodeString(dataHex) + if err != nil { + panic("failed to decode data hex") + } + } else { + unsignedBytes, err = parseBytesArgs(dataBytes, ", ") + if err != nil { + panic("failed to parse data bytes") + } + } + + if signatureHex != "" { + signature, err = hex.DecodeString(signatureHex) + if err != nil { + panic("failed to decode signature hex") + } + } else { + signature, err = parseBytesArgs(signatureBytes, ", ") + if err != nil { + panic("failed to parse data bytes") + } + } + + err = gc.Signature.VerifySignature(unsignedBytes, signature, accountAddress) + if err != nil { + failedVerifyCause = err.Error() + isVerified = false + } + + fmt.Printf("verify-status:\t%v\n", isVerified) + fmt.Printf("failed-causes:\t%v\n", failedVerifyCause) + fmt.Printf("address:\t%v\n", accountAddress) + fmt.Printf("payload-bytes:\t%v\n", unsignedBytes) + fmt.Printf("payload-hex:\t%v\n", hex.EncodeToString(unsignedBytes)) + + fmt.Printf("signature-hex:\t%v\n", hex.EncodeToString(signature)) + fmt.Printf("signature-bytes:%v\n", signature) + +} + +func parseBytesArgs(argsBytesString, separated string) ([]byte, error) { + var ( + parsedByte []byte + byteCharSlice = strings.Split(argsBytesString, separated) + ) + for _, v := range byteCharSlice { + byteValue, err := strconv.Atoi(v) + if err != nil { + return nil, err + } + parsedByte = append(parsedByte, byte(byteValue)) + } + return parsedByte, nil +} diff --git a/cmd/signature/const.go b/cmd/signature/const.go index a92680bab..956b0fef5 100644 --- a/cmd/signature/const.go +++ b/cmd/signature/const.go @@ -1,8 +1,15 @@ package signature var ( - seed string - dataHex string - dataBytes string - hash bool + // sign + seed string + dataHex string + dataBytes string + hash bool + ed25519UseSlip10 bool + + // verify + signatureBytes string + signatureHex string + accountAddress string ) diff --git a/cmd/transaction/cmd.go b/cmd/transaction/cmd.go index 75c974f4c..470c198e8 100644 --- a/cmd/transaction/cmd.go +++ b/cmd/transaction/cmd.go @@ -201,6 +201,7 @@ func Commands() *cobra.Command { func (*TXGeneratorCommands) SendMoneyProcess() RunCommand { return func(ccmd *cobra.Command, args []string) { tx := GenerateBasicTransaction( + senderAddress, senderSeed, senderSignatureType, version, @@ -221,6 +222,7 @@ func (*TXGeneratorCommands) RegisterNodeProcess() RunCommand { return func(ccmd *cobra.Command, args []string) { var ( tx = GenerateBasicTransaction( + senderAddress, senderSeed, senderSignatureType, version, @@ -256,6 +258,7 @@ func (*TXGeneratorCommands) UpdateNodeProcess() RunCommand { return func(ccmd *cobra.Command, args []string) { var ( tx = GenerateBasicTransaction( + senderAddress, senderSeed, senderSignatureType, version, @@ -291,6 +294,7 @@ func (*TXGeneratorCommands) UpdateNodeProcess() RunCommand { func (*TXGeneratorCommands) RemoveNodeProcess() RunCommand { return func(ccmd *cobra.Command, args []string) { tx := GenerateBasicTransaction( + senderAddress, senderSeed, senderSignatureType, version, @@ -312,6 +316,7 @@ func (*TXGeneratorCommands) ClaimNodeProcess() RunCommand { return func(ccmd *cobra.Command, args []string) { var ( tx = GenerateBasicTransaction( + senderAddress, senderSeed, senderSignatureType, version, @@ -345,6 +350,7 @@ func (*TXGeneratorCommands) SetupAccountDatasetProcess() RunCommand { return func(ccmd *cobra.Command, args []string) { senderAccountAddress := crypto.NewEd25519Signature().GetAddressFromSeed(senderSeed) tx := GenerateBasicTransaction( + senderAddress, senderSeed, senderSignatureType, version, @@ -372,6 +378,7 @@ func (*TXGeneratorCommands) RemoveAccountDatasetProcess() RunCommand { return func(ccmd *cobra.Command, args []string) { senderAccountAddress := crypto.NewEd25519Signature().GetAddressFromSeed(senderSeed) tx := GenerateBasicTransaction( + senderAddress, senderSeed, senderSignatureType, version, @@ -391,6 +398,7 @@ func (*TXGeneratorCommands) RemoveAccountDatasetProcess() RunCommand { func (*TXGeneratorCommands) EscrowApprovalProcess() RunCommand { return func(ccmd *cobra.Command, args []string) { tx := GenerateBasicTransaction( + senderAddress, senderSeed, senderSignatureType, version, @@ -407,6 +415,7 @@ func (*TXGeneratorCommands) EscrowApprovalProcess() RunCommand { func (*TXGeneratorCommands) MultiSignatureProcess() RunCommand { return func(ccmd *cobra.Command, args []string) { tx := GenerateBasicTransaction( + senderAddress, senderSeed, senderSignatureType, version, diff --git a/cmd/transaction/generator.go b/cmd/transaction/generator.go index ea249b6f6..a4ee6b98c 100644 --- a/cmd/transaction/generator.go +++ b/cmd/transaction/generator.go @@ -223,7 +223,7 @@ func GenerateTxRemoveAccountDataset( // GenerateBasicTransaction return basic transaction based on common transaction field func GenerateBasicTransaction( - senderSeed string, + senderAddress, senderSeed string, senderSignatureType int32, version uint32, timestamp, fee int64, @@ -318,7 +318,12 @@ func PrintTx(signedTxBytes []byte, outputType string) { } // GenerateSignedTxBytes retrun signed transaction bytes -func GenerateSignedTxBytes(tx *model.Transaction, senderSeed string, signatureType int32) []byte { +func GenerateSignedTxBytes( + tx *model.Transaction, + senderSeed string, + signatureType int32, + optionalSignParams ...interface{}, +) []byte { var ( transactionUtil = &transaction.Util{} txType transaction.TypeAction @@ -335,6 +340,7 @@ func GenerateSignedTxBytes(tx *model.Transaction, senderSeed string, signatureTy unsignedTxBytes, model.SignatureType(signatureType), senderSeed, + optionalSignParams..., ) signedTxBytes, _ := transactionUtil.GetTransactionBytes(tx, true) return signedTxBytes diff --git a/common/crypto/signature.go b/common/crypto/signature.go index baf64c429..4d4e2b687 100644 --- a/common/crypto/signature.go +++ b/common/crypto/signature.go @@ -11,7 +11,7 @@ import ( type ( // SignatureInterface represent interface of signature SignatureInterface interface { - Sign(payload []byte, signatureType model.SignatureType, seed string) ([]byte, error) + Sign(payload []byte, signatureType model.SignatureType, seed string, optionalParams ...interface{}) ([]byte, error) SignByNode(payload []byte, nodeSeed string) []byte VerifySignature(payload, signature []byte, accountAddress string) error VerifyNodeSignature(payload, signature []byte, nodePublicKey []byte) bool @@ -34,16 +34,44 @@ func NewSignature() *Signature { // Sign accept account ID and payload to be signed then return the signature byte based on the // signature method associated with account.Type -func (*Signature) Sign(payload []byte, signatureType model.SignatureType, seed string) ([]byte, error) { +func (*Signature) Sign( + payload []byte, + signatureType model.SignatureType, + seed string, + optionalParams ...interface{}, +) ([]byte, error) { buffer := bytes.NewBuffer([]byte{}) buffer.Write(util.ConvertUint32ToBytes(uint32(signatureType))) switch signatureType { case model.SignatureType_DefaultSignature: var ( ed25519Signature = NewEd25519Signature() - accountPrivateKey = ed25519Signature.GetPrivateKeyFromSeed(seed) - signature = ed25519Signature.Sign(accountPrivateKey, payload) + accountPrivateKey []byte + useSlip10, ok bool + err error ) + // optionalParams index 0 used for flag boolean slip10 + if len(optionalParams) != 0 { + useSlip10, ok = optionalParams[0].(bool) + if !ok { + return nil, blocker.NewBlocker(blocker.AppErr, "failedAssertType") + } + } + if useSlip10 { + accountPrivateKey, err = ed25519Signature.GetPrivateKeyFromSeedUseSlip10(seed) + if err != nil { + return nil, blocker.NewBlocker(blocker.AppErr, err.Error()) + } + publicKey, err := ed25519Signature.GetPublicKeyFromPrivateKeyUseSlip10(accountPrivateKey) + if err != nil { + return nil, blocker.NewBlocker(blocker.AppErr, err.Error()) + } + accountPrivateKey = append(accountPrivateKey, publicKey...) + } else { + accountPrivateKey = ed25519Signature.GetPrivateKeyFromSeed(seed) + } + + signature := ed25519Signature.Sign(accountPrivateKey, payload) buffer.Write(signature) return buffer.Bytes(), nil case model.SignatureType_BitcoinSignature: