Add some basic RC serialization tests
This commit is contained in:
parent
4432c1a2ea
commit
33e16df586
@ -42,6 +42,7 @@ var WebsiteBaseURL string = "{kc.website_base_url}"
|
|||||||
var Version VersionType = VersionType{{Major: {kc.version.major}, Minor: {kc.version.minor}, Patch: {kc.version.patch},}}
|
var Version VersionType = VersionType{{Major: {kc.version.major}, Minor: {kc.version.minor}, Patch: {kc.version.patch},}}
|
||||||
var DefaultPager []string = []string{{ {dp} }}
|
var DefaultPager []string = []string{{ {dp} }}
|
||||||
var VCSRevision string = ""
|
var VCSRevision string = ""
|
||||||
|
var RC_ENCRYPTION_PROTOCOL_VERSION string = "{kc.RC_ENCRYPTION_PROTOCOL_VERSION}"
|
||||||
''')
|
''')
|
||||||
with open('tools/cmd/at/template.go') as f:
|
with open('tools/cmd/at/template.go') as f:
|
||||||
template = f.read()
|
template = f.read()
|
||||||
|
|||||||
@ -13,6 +13,6 @@ func TestFormatLineWithIndent(t *testing.T) {
|
|||||||
format_line_with_indent(&output, "testing \x1b[31mstyled\x1b[m", indent, 11)
|
format_line_with_indent(&output, "testing \x1b[31mstyled\x1b[m", indent, 11)
|
||||||
expected := indent + "testing \n" + indent + "\x1b[31mstyled\x1b[m\n"
|
expected := indent + "testing \n" + indent + "\x1b[31mstyled\x1b[m\n"
|
||||||
if output.String() != expected {
|
if output.String() != expected {
|
||||||
t.Errorf("%#v != %#v", expected, output.String())
|
t.Fatalf("%#v != %#v", expected, output.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package at
|
package at
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
@ -11,24 +12,86 @@ import (
|
|||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
|
|
||||||
|
"kitty"
|
||||||
|
"kitty/tools/base85"
|
||||||
"kitty/tools/cli"
|
"kitty/tools/cli"
|
||||||
"kitty/tools/crypto"
|
"kitty/tools/crypto"
|
||||||
|
"kitty/tools/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var encrypt_cmd = crypto.Encrypt_cmd
|
|
||||||
|
|
||||||
type GlobalOptions struct {
|
type GlobalOptions struct {
|
||||||
to, password, use_password string
|
to_address, password string
|
||||||
to_from_env bool
|
to_address_is_from_env_var bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var global_options GlobalOptions
|
var global_options GlobalOptions
|
||||||
|
|
||||||
func get_password(password string, password_file string, password_env string, use_password string) (string, error) {
|
func get_pubkey(encoded_key string) (encryption_version string, pubkey []byte, err error) {
|
||||||
if use_password == "never" {
|
if encoded_key == "" {
|
||||||
return "", nil
|
encoded_key = os.Getenv("KITTY_PUBLIC_KEY")
|
||||||
|
if encoded_key == "" {
|
||||||
|
err = fmt.Errorf("Password usage requested but KITTY_PUBLIC_KEY environment variable is not available")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
encryption_version, encoded_key, found := strings.Cut(encoded_key, ":")
|
||||||
|
if !found {
|
||||||
|
err = fmt.Errorf("KITTY_PUBLIC_KEY environment variable does not have a : in it")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if encryption_version != kitty.RC_ENCRYPTION_PROTOCOL_VERSION {
|
||||||
|
err = fmt.Errorf("KITTY_PUBLIC_KEY has unknown version, if you are running on a remote system, update kitty on this system")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pubkey = make([]byte, base85.DecodedLen(len(encoded_key)))
|
||||||
|
n, err := base85.Decode(pubkey, []byte(encoded_key))
|
||||||
|
if err == nil {
|
||||||
|
pubkey = pubkey[:n]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func simple_serializer(rc *utils.RemoteControlCmd) (ans []byte, err error) {
|
||||||
|
ans, err = json.Marshal(rc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type serializer_func func(rc *utils.RemoteControlCmd) ([]byte, error)
|
||||||
|
|
||||||
|
var serializer serializer_func = simple_serializer
|
||||||
|
|
||||||
|
func create_serializer(password string, encoded_pubkey string) (ans serializer_func, err error) {
|
||||||
|
if password != "" {
|
||||||
|
encryption_version, pubkey, err := get_pubkey(encoded_pubkey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ans = func(rc *utils.RemoteControlCmd) (ans []byte, err error) {
|
||||||
|
ec, err := crypto.Encrypt_cmd(rc, global_options.password, pubkey, encryption_version)
|
||||||
|
ans, err = json.Marshal(ec)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return simple_serializer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func send_rc_command(rc *utils.RemoteControlCmd) (err error) {
|
||||||
|
serializer, err = create_serializer(global_options.password, "")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d, err := serializer(rc)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println(string(d))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func get_password(password string, password_file string, password_env string, use_password string) (ans string, err error) {
|
||||||
|
if use_password == "never" {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
ans := ""
|
|
||||||
if password != "" {
|
if password != "" {
|
||||||
ans = password
|
ans = password
|
||||||
}
|
}
|
||||||
@ -82,10 +145,9 @@ func EntryPoint(tool_root *cobra.Command) *cobra.Command {
|
|||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if *to == "" {
|
if *to == "" {
|
||||||
*to = os.Getenv("KITTY_LISTEN_ON")
|
*to = os.Getenv("KITTY_LISTEN_ON")
|
||||||
global_options.to_from_env = true
|
global_options.to_address_is_from_env_var = true
|
||||||
}
|
}
|
||||||
global_options.to = *to
|
global_options.to_address = *to
|
||||||
global_options.use_password = *use_password
|
|
||||||
q, err := get_password(*password, *password_file, *password_env, *use_password)
|
q, err := get_password(*password, *password_file, *password_env, *use_password)
|
||||||
global_options.password = q
|
global_options.password = q
|
||||||
return err
|
return err
|
||||||
|
|||||||
51
tools/cmd/at/main_test.go
Normal file
51
tools/cmd/at/main_test.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package at
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"kitty/tools/crypto"
|
||||||
|
"kitty/tools/utils"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRCSerialization(t *testing.T) {
|
||||||
|
serializer, err := create_serializer("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var ver = [3]int{1, 2, 3}
|
||||||
|
rc := utils.RemoteControlCmd{
|
||||||
|
Cmd: "test", Version: ver,
|
||||||
|
}
|
||||||
|
simple := func(expected string) {
|
||||||
|
actual, err := serializer(&rc)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
as := string(actual)
|
||||||
|
if as != expected {
|
||||||
|
t.Fatalf("Incorrect serialization: %s != %s", expected, as)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
simple(`{"cmd":"test","version":[1,2,3]}`)
|
||||||
|
pubkey_b, _, err := crypto.KeyPair("1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pubkey, err := crypto.EncodePublicKey(pubkey_b, "1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
serializer, err = create_serializer("tpw", pubkey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
raw, err := serializer(&rc)
|
||||||
|
var ec utils.EncryptedRemoteControlCmd
|
||||||
|
err = json.Unmarshal([]byte(raw), &ec)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if ec.Version != ver {
|
||||||
|
t.Fatal("Incorrect version in encrypted command: ", ec.Version)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,13 +8,24 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"kitty/tools/cli"
|
"kitty/tools/cli"
|
||||||
|
"kitty/tools/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func run_CMD_NAME(cmd *cobra.Command, args []string) (err error) {
|
||||||
|
rc := utils.RemoteControlCmd{
|
||||||
|
Cmd: "CLI_NAME",
|
||||||
|
Version: [3]int{0, 20, 0},
|
||||||
|
}
|
||||||
|
err = send_rc_command(&rc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func setup_CMD_NAME(root *cobra.Command) *cobra.Command {
|
func setup_CMD_NAME(root *cobra.Command) *cobra.Command {
|
||||||
ans := cli.CreateCommand(&cobra.Command{
|
ans := cli.CreateCommand(&cobra.Command{
|
||||||
Use: "CLI_NAME [options]",
|
Use: "CLI_NAME [options]",
|
||||||
Short: "SHORT_DESC",
|
Short: "SHORT_DESC",
|
||||||
Long: "LONG_DESC",
|
Long: "LONG_DESC",
|
||||||
|
RunE: run_CMD_NAME,
|
||||||
})
|
})
|
||||||
|
|
||||||
return ans
|
return ans
|
||||||
|
|||||||
@ -6,13 +6,10 @@ import (
|
|||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/crypto/curve25519"
|
"golang.org/x/crypto/curve25519"
|
||||||
"kitty/tools/base85"
|
"kitty/tools/base85"
|
||||||
"kitty/tools/utils"
|
"kitty/tools/utils"
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -40,8 +37,8 @@ func b85_decode(data string) (decoded []byte, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func encrypt(plaintext []byte, alice_public_key []byte) (iv []byte, tag []byte, ciphertext []byte, bob_public_key []byte, err error) {
|
func encrypt(plaintext []byte, alice_public_key []byte, encryption_protocol string) (iv []byte, tag []byte, ciphertext []byte, bob_public_key []byte, err error) {
|
||||||
bob_private_key, bob_public_key, err := curve25519_key_pair()
|
bob_private_key, bob_public_key, err := KeyPair(encryption_protocol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -70,31 +67,40 @@ func encrypt(plaintext []byte, alice_public_key []byte) (iv []byte, tag []byte,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func Encrypt_cmd(cmd *utils.RemoteControlCmd, password string, other_pubkey []byte) (encrypted_cmd utils.EncryptedRemoteControlCmd, err error) {
|
func KeyPair(encryption_protocol string) (private_key []byte, public_key []byte, err error) {
|
||||||
if len(other_pubkey) == 0 {
|
switch encryption_protocol {
|
||||||
raw := os.Getenv("KITTY_PUBLIC_KEY")
|
case "1":
|
||||||
if len(raw) == 0 {
|
return curve25519_key_pair()
|
||||||
err = errors.New("No KITTY_PUBLIC_KEY environment variable set cannot use passwords")
|
default:
|
||||||
return
|
err = fmt.Errorf("Unknown encryption protocol: %s", encryption_protocol)
|
||||||
}
|
return
|
||||||
if !strings.HasPrefix(raw, "1:") {
|
|
||||||
err = fmt.Errorf("KITTY_PUBLIC_KEY has unknown protocol: %s", raw[:2])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
other_pubkey, err = b85_decode(raw[2:])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodePublicKey(pubkey []byte, encryption_protocol string) (ans string, err error) {
|
||||||
|
switch encryption_protocol {
|
||||||
|
case "1":
|
||||||
|
ans = fmt.Sprintf("1:%s", b85_encode(pubkey))
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("Unknown encryption protocol: %s", encryption_protocol)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Encrypt_cmd(cmd *utils.RemoteControlCmd, password string, other_pubkey []byte, encryption_protocol string) (encrypted_cmd utils.EncryptedRemoteControlCmd, err error) {
|
||||||
cmd.Password = password
|
cmd.Password = password
|
||||||
cmd.Timestamp = time.Now().UnixNano()
|
cmd.Timestamp = time.Now().UnixNano()
|
||||||
plaintext, err := json.Marshal(cmd)
|
plaintext, err := json.Marshal(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
iv, tag, ciphertext, pubkey, err := encrypt(plaintext, other_pubkey)
|
iv, tag, ciphertext, pubkey, err := encrypt(plaintext, other_pubkey, encryption_protocol)
|
||||||
encrypted_cmd = utils.EncryptedRemoteControlCmd{
|
encrypted_cmd = utils.EncryptedRemoteControlCmd{
|
||||||
Version: cmd.Version, IV: b85_encode(iv), Tag: b85_encode(tag), Pubkey: b85_encode(pubkey), Encrypted: b85_encode(ciphertext)}
|
Version: cmd.Version, IV: b85_encode(iv), Tag: b85_encode(tag), Pubkey: b85_encode(pubkey), Encrypted: b85_encode(ciphertext)}
|
||||||
|
if encryption_protocol != "1" {
|
||||||
|
encrypted_cmd.EncProto = encryption_protocol
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,10 +3,10 @@ package utils
|
|||||||
type RemoteControlCmd struct {
|
type RemoteControlCmd struct {
|
||||||
Cmd string `json:"cmd"`
|
Cmd string `json:"cmd"`
|
||||||
Version [3]int `json:"version"`
|
Version [3]int `json:"version"`
|
||||||
NoResponse bool `json:"no_response,omitifempty"`
|
NoResponse bool `json:"no_response,omitempty"`
|
||||||
Payload map[string]interface{} `json:"payload,omitifempty"`
|
Payload map[string]interface{} `json:"payload,omitempty"`
|
||||||
Timestamp int64 `json:"timestamp,omitifempty"`
|
Timestamp int64 `json:"timestamp,omitempty"`
|
||||||
Password string `json:"password,omitifempty"`
|
Password string `json:"password,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type EncryptedRemoteControlCmd struct {
|
type EncryptedRemoteControlCmd struct {
|
||||||
@ -15,4 +15,5 @@ type EncryptedRemoteControlCmd struct {
|
|||||||
Tag string `json:"tag"`
|
Tag string `json:"tag"`
|
||||||
Pubkey string `json:"pubkey"`
|
Pubkey string `json:"pubkey"`
|
||||||
Encrypted string `json:"encrypted"`
|
Encrypted string `json:"encrypted"`
|
||||||
|
EncProto string `json:"enc_proto,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user