-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: gyuguen <gyuguen.jang@medibloc.org>
- Loading branch information
Showing
4 changed files
with
226 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package cmd | ||
|
||
import ( | ||
"encoding/base64" | ||
"encoding/hex" | ||
"os" | ||
|
||
"github.com/btcsuite/btcd/btcec" | ||
"github.com/cosmos/cosmos-sdk/client" | ||
"github.com/cosmos/cosmos-sdk/client/flags" | ||
"github.com/cosmos/cosmos-sdk/crypto/keyring" | ||
"github.com/medibloc/panacea-core/v2/crypto" | ||
oracletypes "github.com/medibloc/panacea-core/v2/x/oracle/types" | ||
"github.com/spf13/cobra" | ||
"github.com/tendermint/tendermint/libs/cli" | ||
) | ||
|
||
func EncryptDataCmd(defaultNodeHome string) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "encrypt-data [input-file-path] [output-file-path] [key-name]", | ||
Short: "Encrypt data with shared key which consists of oracle public key and provider's private key", | ||
Long: ` | ||
This command can encrypt data with shared key which consists of oracle public key and provider's private key. | ||
The key to be used for encryption should be stored in the localStore. | ||
If not stored, please add the key first via the following command. | ||
panacead keys add ... | ||
`, | ||
Args: cobra.ExactArgs(3), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
clientCtx, err := client.GetClientQueryContext(cmd) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
queryClient := oracletypes.NewQueryClient(clientCtx) | ||
|
||
origData, err := os.ReadFile(args[0]) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
params, err := queryClient.Params(cmd.Context(), &oracletypes.QueryOracleParamsRequest{}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
oraclePubKey := params.GetParams().GetOraclePublicKey() | ||
|
||
encryptedData, err := encrypt(clientCtx, args[2], origData, oraclePubKey) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err := os.WriteFile(args[1], encryptedData, 0644); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
|
||
cmd.PersistentFlags().String(flags.FlagHome, defaultNodeHome, "The application home directory") | ||
cmd.PersistentFlags().String(flags.FlagKeyringDir, "", "The client Keyring directory; if omitted, the default 'home' directory will be used") | ||
cmd.PersistentFlags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|test)") | ||
cmd.PersistentFlags().String(cli.OutputFlag, "text", "Output format (text|json)") | ||
cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID") | ||
|
||
flags.AddQueryFlagsToCmd(cmd) | ||
|
||
return cmd | ||
} | ||
|
||
func encrypt(clientCtx client.Context, keyName string, origData []byte, oraclePubKeyStr string) ([]byte, error) { | ||
// get unsafe export private key from keystore | ||
privKeyHex, err := keyring.NewUnsafe(clientCtx.Keyring).UnsafeExportPrivKeyHex(keyName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
privKeyBz, err := hex.DecodeString(privKeyHex) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBz) | ||
|
||
// oracle public key | ||
oraclePubKeyBz, err := base64.StdEncoding.DecodeString(oraclePubKeyStr) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
oraclePubKey, err := btcec.ParsePubKey(oraclePubKeyBz, btcec.S256()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// shared key | ||
sharedKey := crypto.DeriveSharedKey(privKey, oraclePubKey, crypto.KDFSHA256) | ||
|
||
// encrypt data | ||
encryptedData, err := crypto.Encrypt(sharedKey, nil, origData) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return encryptedData, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package crypto | ||
|
||
import ( | ||
"crypto/aes" | ||
"crypto/cipher" | ||
"crypto/rand" | ||
"crypto/sha256" | ||
"fmt" | ||
"io" | ||
|
||
"github.com/btcsuite/btcd/btcec" | ||
) | ||
|
||
// DeriveSharedKey derives a shared key (which can be used for asymmetric encryption) | ||
// using a specified KDF (Key Derivation Function) | ||
// from a shared secret generated by Diffie-Hellman key exchange (ECDH). | ||
func DeriveSharedKey(priv *btcec.PrivateKey, pub *btcec.PublicKey, kdf func([]byte) []byte) []byte { | ||
sharedSecret := btcec.GenerateSharedSecret(priv, pub) | ||
return kdf(sharedSecret) | ||
} | ||
|
||
// KDFSHA256 is a key derivation function which uses SHA256. | ||
func KDFSHA256(in []byte) []byte { | ||
out := sha256.Sum256(in) | ||
return out[:] | ||
} | ||
|
||
// Encrypt combines secretKey and secondKey to encrypt with AES256-GCM method. | ||
func Encrypt(secretKey, additional, data []byte) ([]byte, error) { | ||
if len(secretKey) != 32 { | ||
return nil, fmt.Errorf("secret key is not for AES-256: total %d bits", 8*len(secretKey)) | ||
} | ||
|
||
// prepare AES-256-GSM cipher | ||
block, err := aes.NewCipher(secretKey) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
aesGCM, err := cipher.NewGCM(block) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// make random nonce | ||
nonce := make([]byte, aesGCM.NonceSize()) | ||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil { | ||
return nil, err | ||
} | ||
|
||
// encrypt data with second key | ||
ciphertext := aesGCM.Seal(nonce, nonce, data, additional) | ||
return ciphertext, nil | ||
} | ||
|
||
// Decrypt combines secretKey and secondKey to decrypt AES256-GCM. | ||
func Decrypt(secretKey []byte, additional []byte, ciphertext []byte) ([]byte, error) { | ||
if len(secretKey) != 32 { | ||
return nil, fmt.Errorf("secret key is not for AES-256: total %d bits", 8*len(secretKey)) | ||
} | ||
|
||
// prepare AES-256-GCM cipher | ||
block, err := aes.NewCipher(secretKey) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
aesgcm, err := cipher.NewGCM(block) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
nonceSize := aesgcm.NonceSize() | ||
nonce, pureCiphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] | ||
|
||
// decrypt ciphertext with second key | ||
plaintext, err := aesgcm.Open(nil, nonce, pureCiphertext, additional) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return plaintext, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package crypto | ||
|
||
import ( | ||
"crypto/rand" | ||
"io" | ||
"testing" | ||
|
||
"github.com/btcsuite/btcd/btcec" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestDecryptWithAES256(t *testing.T) { | ||
privKey1, err := btcec.NewPrivateKey(btcec.S256()) | ||
require.NoError(t, err) | ||
privKey2, err := btcec.NewPrivateKey(btcec.S256()) | ||
require.NoError(t, err) | ||
|
||
data := []byte("hello, Panacea") | ||
|
||
shareKey1 := DeriveSharedKey(privKey1, privKey2.PubKey(), KDFSHA256) | ||
shareKey2 := DeriveSharedKey(privKey2, privKey1.PubKey(), KDFSHA256) | ||
|
||
nonce := make([]byte, 12) | ||
_, err = io.ReadFull(rand.Reader, nonce) | ||
require.NoError(t, err) | ||
|
||
encryptedData, err := Encrypt(shareKey1, nonce, data) | ||
require.NoError(t, err) | ||
|
||
decryptedData, err := Decrypt(shareKey2, nonce, encryptedData) | ||
require.NoError(t, err) | ||
|
||
require.Equal(t, decryptedData, data) | ||
} |