Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem: no encrypt and decrypt cmds for message #1411

Merged
merged 7 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@

### Features

* [#1406](https://github.com/crypto-org-chain/cronos/pull/1406) Add set-encryption-key for encryption module.
* [#1406](https://github.com/crypto-org-chain/cronos/pull/1406) Add set-encryption-key for encryption module.
* [#](https://github.com/crypto-org-chain/cronos/pull/) Add encrypt and decrypt cmds for message.

*April 8, 2024*

Expand Down
7 changes: 1 addition & 6 deletions client/docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@
}
},
{
"url": "./tmp-swagger-gen/e2ee/query.swagger.json",
"operationIds": {
"rename": {
"Params": "Keys"
}
}
"url": "./tmp-swagger-gen/e2ee/query.swagger.json"
},
{
"url": "./tmp-swagger-gen/ethermint/evm/v1/query.swagger.json",
Expand Down
100 changes: 100 additions & 0 deletions cmd/cronosd/cmd/decrypt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package cmd

import (
"bufio"
"fmt"
"io"
"os"
"strings"

"filippo.io/age"
"github.com/spf13/cobra"
)

const flagI = "i"

func DecryptMsgCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "decrypt [input]",
Short: "Decrypt input messages to user",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
inputs, err := cmd.Flags().GetStringArray(flagI)
if err != nil {
return err
}
var in io.Reader = os.Stdin
var out io.Writer = os.Stdout
if name := args[0]; name != "" && name != "-" {
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close()
in = f
}
return decrypt(inputs, in, out)
},
}
cmd.Flags().StringArray(flagI, []string{}, "identity (can be repeated)")
return cmd
}

func decrypt(inputs []string, in io.Reader, out io.Writer) error {
identities := []age.Identity{}
for _, input := range inputs {
ids, err := parseIdentitiesFile(input)
if err != nil {
return err
}
identities = append(identities, ids...)
}
r, err := age.Decrypt(in, identities...)
if err != nil {
return err
}
if _, err := io.Copy(out, r); err != nil {
return err
}
return nil
}

func parseIdentitiesFile(name string) ([]age.Identity, error) {
f, err := os.Open(name)
if err != nil {
return nil, fmt.Errorf("failed to open file: %v", err)
}
defer f.Close()
b := bufio.NewReader(f)
ids, err := parseIdentities(b)
if err != nil {
return nil, fmt.Errorf("failed to read %q: %v", name, err)
}
return ids, nil
}

func parseIdentities(f io.Reader) ([]age.Identity, error) {
const privateKeySizeLimit = 1 << 24 // 16 MiB
var ids []age.Identity
scanner := bufio.NewScanner(io.LimitReader(f, privateKeySizeLimit))
var n int
for scanner.Scan() {
n++
line := scanner.Text()
if strings.HasPrefix(line, "#") || line == "" {
continue
}
i, err := age.ParseX25519Identity(line)
if err != nil {
return nil, fmt.Errorf("error at line %d: %v", n, err)
}
ids = append(ids, i)
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("failed to read secret keys file: %v", err)
}
if len(ids) == 0 {
return nil, fmt.Errorf("no secret keys found")
}
return ids, nil
}
103 changes: 103 additions & 0 deletions cmd/cronosd/cmd/encrypt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package cmd

import (
"io"
"os"

"filippo.io/age"
"github.com/spf13/cobra"
)

const (
flagR = "r"
flagO = "o"
)

func EncryptMsgCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "encrypt [input]",
Short: "Encrypt input messages to multiple users",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
f := cmd.Flags()
output, err := f.GetString(flagO)
if err != nil {
return err
}
recs, err := f.GetStringArray(flagR)
if err != nil {
return err
}
var in io.Reader = os.Stdin
var out io.Writer = os.Stdout
if name := args[0]; name != "" && name != "-" {
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close()
in = f
}
if name := output; name != "" && name != "-" {
f := newLazyOpener(name)
defer f.Close()
out = f
}
return encrypt(recs, in, out)
},
}
f := cmd.Flags()
f.StringArray(flagR, []string{}, "recipient (can be repeated)")
f.StringArray(flagI, []string{}, "identity (can be repeated)")
f.String(flagO, "", "output to `FILE`")
return cmd
}

func encrypt(recs []string, in io.Reader, out io.Writer) error {
var recipients []age.Recipient
for _, arg := range recs {
r, err := age.ParseX25519Recipient(arg)
if err != nil {
return err
}
recipients = append(recipients, r)
}
w, err := age.Encrypt(out, recipients...)
if err != nil {
return err
}
if _, err := io.Copy(w, in); err != nil {
return err
}
if err := w.Close(); err != nil {
return err
}
return nil
}

type lazyOpener struct {
name string
f *os.File
err error
}

func newLazyOpener(name string) io.WriteCloser {
return &lazyOpener{name: name}
}

func (l *lazyOpener) Write(p []byte) (n int, err error) {
if l.f == nil && l.err == nil {
l.f, l.err = os.Create(l.name)
}
if l.err != nil {
return 0, l.err
}
return l.f.Write(p)
}

func (l *lazyOpener) Close() error {
if l.f != nil {
return l.f.Close()
}
return nil
}
50 changes: 50 additions & 0 deletions cmd/cronosd/cmd/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package cmd

import (
"fmt"
"os"
"time"

"filippo.io/age"
"github.com/spf13/cobra"
)

func KeygenCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "keygen",
Short: "Generates a new native X25519 key pair",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
f := cmd.Flags()
output, err := f.GetString(flagO)
if err != nil {
return err
}
out := os.Stdout
if output != "" {
f, err := os.OpenFile(output, os.O_WRONLY|os.O_CREATE|os.O_EXCL, os.ModePerm)
if err != nil {
return err
}
defer f.Close()
Fixed Show fixed Hide fixed
out = f
}
return generate(out)
},
}
cmd.Flags().String(flagO, "", "output to `FILE`")
return cmd
}

func generate(out *os.File) error {
k, err := age.GenerateX25519Identity()
if err != nil {
return err
}
pubkey := k.Recipient()
fmt.Fprintf(out, "# created: %s\n", time.Now().Format(time.RFC3339))
Fixed Show fixed Hide fixed
fmt.Fprintf(out, "# public key: %s\n", pubkey)
fmt.Fprintf(out, "%s\n", k)
fmt.Println(pubkey)
return nil
}
3 changes: 3 additions & 0 deletions cmd/cronosd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ func initRootCmd(
queryCommand(),
txCommand(),
ethermintclient.KeyCommands(app.DefaultNodeHome),
KeygenCommand(),
EncryptMsgCommand(),
DecryptMsgCommand(),
)

// add rosetta
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
cosmossdk.io/x/evidence v0.1.0
cosmossdk.io/x/feegrant v0.1.0
cosmossdk.io/x/upgrade v0.1.1
filippo.io/age v1.1.2-0.20240110114017-29b68c20fc24
github.com/cometbft/cometbft v0.38.7-0.20240412124004-1f67e396cf45
github.com/cosmos/cosmos-db v1.0.3-0.20240408151834-e75f6e4b28d8
github.com/cosmos/cosmos-proto v1.0.0-beta.4
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
c2sp.org/CCTV/age v0.0.0-20221230231406-5ea85644bd03 h1:0e2QjhWG02SgzlUOvNYaFraf04OBsUPOLxf+K+Ae/yM=
c2sp.org/CCTV/age v0.0.0-20221230231406-5ea85644bd03/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
Expand Down Expand Up @@ -211,6 +213,8 @@ cosmossdk.io/x/feegrant v0.1.0/go.mod h1:4r+FsViJRpcZif/yhTn+E0E6OFfg4n0Lx+6cCtn
cosmossdk.io/x/upgrade v0.1.1 h1:aoPe2gNvH+Gwt/Pgq3dOxxQVU3j5P6Xf+DaUJTDZATc=
cosmossdk.io/x/upgrade v0.1.1/go.mod h1:MNLptLPcIFK9CWt7Ra//8WUZAxweyRDNcbs5nkOcQy0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/age v1.1.2-0.20240110114017-29b68c20fc24 h1:vQIe2pCVvdZjX8OtZjbJ33nBKPjTnmy0zbdJxRjhH3w=
filippo.io/age v1.1.2-0.20240110114017-29b68c20fc24/go.mod h1:y3Zb/i2jHg/kL8xc3ocrI0Wd0Vm+VWV6DKfsKzSGUmU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
Expand Down
3 changes: 3 additions & 0 deletions gomod2nix.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ schema = 3
[mod."cosmossdk.io/x/upgrade"]
version = "v0.1.1"
hash = "sha256-bM9ybpaibMH7k4M6/QAXCZ3fJcADfJHxvMgp4AVUihs="
[mod."filippo.io/age"]
version = "v1.1.2-0.20240110114017-29b68c20fc24"
hash = "sha256-LZ6W2ZNQyA1s6SAY5rXOhztgF0pmCKQR7B+73V1tB4Q="
[mod."filippo.io/edwards25519"]
version = "v1.1.0"
hash = "sha256-9ACANrgWZSd5HYPfDZHY8DVbPSC9LOMgy8deq3rDOoc="
Expand Down
14 changes: 14 additions & 0 deletions integration_tests/cosmoscli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1870,3 +1870,17 @@ def set_e2ee_key(self, key, **kwargs):
if rsp["code"] == 0:
rsp = self.event_query_tx_for(rsp["txhash"])
return rsp

def keygen(self, **kwargs):
return self.raw("keygen", home=self.data_dir, **kwargs).strip().decode()

def encrypt(self, input, receipts, **kwargs):
return self.raw(
"encrypt",
input,
*[val for a in [["--r", val] for val in receipts] for val in a],
**kwargs,
)

def decrypt(self, input, **kwargs):
return self.raw("decrypt", input, home=self.data_dir, **kwargs).strip().decode()
17 changes: 17 additions & 0 deletions integration_tests/test_e2ee.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import base64
import tempfile


def test_set_key(cronos):
Expand All @@ -8,3 +9,19 @@ def test_set_key(cronos):
adr = cli.address("community")
p = cli.query_e2ee_key(adr)
assert p["key"] == key


def test_encrypt_decrypt(cronos):
cli = cronos.cosmos_cli()
key_dir = tempfile.mkdtemp(dir=cronos.base_dir)
key0 = f"{key_dir}/key0"
key1 = f"{key_dir}/key1"
pubkey0 = cli.keygen(o=key0)
pubkey1 = cli.keygen(o=key1)
input = f"{key_dir}/input"
decrypted = f"{key_dir}/input.age"
data = "Hello, World!"
with open(input, "w") as file:
file.write(data)
cli.encrypt(input, [pubkey0, pubkey1], o=decrypted)
assert cli.decrypt(decrypted, i=key0) == cli.decrypt(decrypted, i=key1) == data
Loading