diff --git a/README.md b/README.md index 05aa05b..a147286 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,12 @@ - Signed envelopes detect tampering and sender spoofing. - Plaintext output to stdout is blocked by default unless `--out -` is explicitly passed. +## Terminology +- `peer`: the person or device you are sending a secret to +- `share code`: the `ENDE-PUB-1:...` string used for peer onboarding +- `send` / `receive`: task-oriented names for `encrypt` / `decrypt` +- `recipient` / `sender`: lower-level trust model terms still used in advanced commands + ## Install/build ```bash go build ./cmd/ende @@ -110,18 +116,18 @@ The tutorial guides you through: ./ende key keygen --name bob --export-public --export-dir . ``` -2. Alice shares `share:` token from keygen output to Bob. +2. Alice shares the `share:` code from keygen output to Bob. -You can re-print a share token later: +You can re-print a share code later: ```bash ./ende key share --name alice ``` -3. Bob registers interactively in one command (recipient + sender): +3. Bob adds Alice as a peer in one command: ```bash -./ende register -# share token (ENDE-PUB-1:...): ENDE-PUB-1:... -# alias override (optional, Enter to use token id): +./ende add-peer +# share code (ENDE-PUB-1:...): ENDE-PUB-1:... +# peer name override (optional, Enter to use the shared name): ``` 4. Run a local safety check before first real use: @@ -132,77 +138,77 @@ You can re-print a share token later: - keyring file presence and permissions - default signer configuration - private key file paths and `0600` permissions -- recipient/trusted-sender registration consistency +- peer / trusted-signing-key registration consistency To remove a registered alias later: ```bash ./ende unregister alice ``` -5. Encrypt + sign (default: text to stdout): +5. Send a secret securely (default: text to stdout): ```bash -echo 'TOKEN=abc123' | ./ende encrypt -t bob +echo 'TOKEN=abc123' | ./ende send -t bob ``` 5-0. Encrypt from file input: ```bash -./ende encrypt -t bob -f secrets.env -o secret.txt +./ende send -t bob -f secrets.env -o secret.txt ``` 5-1. Save text output to file (optional): ```bash -echo 'TOKEN=abc123' | ./ende encrypt -t bob --text -o secret.txt +echo 'TOKEN=abc123' | ./ende send -t bob --text -o secret.txt ``` 5-2. Raw binary output (optional): ```bash -echo 'TOKEN=abc123' | ./ende encrypt -t bob --binary -o secret.ende +echo 'TOKEN=abc123' | ./ende send -t bob --binary -o secret.ende ``` 5-3. Prompt for a secret interactively without echoing it to the terminal: ```bash -./ende encrypt -t bob --prompt -o secret.txt +./ende send -t bob --prompt -o secret.txt ``` Interactive prompt notes: - TTY input is masked so the secret is not echoed while typing. - Empty prompt input is rejected. - Non-interactive stdin/file workflows continue to work as before. -5-4. Review recipients and output details before encrypting: +5-4. Review peer and output details before sending: ```bash -echo 'TOKEN=abc123' | ./ende encrypt -t bob --confirm -o secret.txt +echo 'TOKEN=abc123' | ./ende send -t bob --confirm -o secret.txt ``` `--confirm` shows: -- recipient alias and short fingerprint +- peer alias and short fingerprint - signer key id - output target - output format For automation, you can keep the summary behavior in scripts and skip the prompt explicitly: ```bash -echo 'TOKEN=abc123' | ./ende encrypt -t bob --confirm --yes -o secret.txt +echo 'TOKEN=abc123' | ./ende send -t bob --confirm --yes -o secret.txt ``` -6. Verify and decrypt: +6. Receive and decrypt: ```bash ./ende verify -i secret.ende -./ende decrypt -i secret.ende -o decrypted.txt +./ende receive -i secret.ende -o decrypted.txt ``` Text envelope input is also supported: ```bash ./ende verify -i secret.txt -./ende decrypt -i secret.txt -o decrypted.txt -./ende decrypt -i secret.txt --text-out +./ende receive -i secret.txt -o decrypted.txt +./ende receive -i secret.txt --text-out ``` Safer plaintext output options: ```bash # Refuse to overwrite an existing plaintext file -./ende decrypt -i secret.ende -o decrypted.txt --no-clobber +./ende receive -i secret.ende -o decrypted.txt --no-clobber # Write plaintext to a temporary 0600 file and print the path -./ende decrypt -i secret.ende --out-temp +./ende receive -i secret.ende --out-temp ``` `--out-temp` is useful when you want Ende to choose a short-lived secure file path for you. diff --git a/cmd/ende/crypto_cmd.go b/cmd/ende/crypto_cmd.go index 6a410ae..98a3461 100644 --- a/cmd/ende/crypto_cmd.go +++ b/cmd/ende/crypto_cmd.go @@ -47,7 +47,7 @@ func newEncryptCommand() *cobra.Command { var yes bool cmd := &cobra.Command{ Use: "encrypt", - Short: "Encrypt and sign secret payload", + Short: "Send a secret securely", Aliases: []string{ "enc", }, @@ -151,7 +151,7 @@ func newEncryptCommand() *cobra.Command { return nil }, } - cmd.Flags().StringSliceVarP(&tos, "to", "t", nil, "recipient alias, github:user, or age1... public key") + cmd.Flags().StringSliceVarP(&tos, "to", "t", nil, "peer alias, github:user, or age1... public key") cmd.Flags().StringVarP(&signAs, "sign-as", "s", "", "local signing key id (optional if default signer is set)") cmd.Flags().StringVarP(&in, "in", "i", "-", "input path or -") cmd.Flags().StringVarP(&fileInput, "file", "f", "", "input file path (alias of --in)") @@ -159,7 +159,7 @@ func newEncryptCommand() *cobra.Command { cmd.Flags().BoolVar(&textOut, "text", true, "output ASCII-armored envelope for copy/paste transport (default true)") cmd.Flags().BoolVar(&binaryOut, "binary", false, "output raw binary envelope") cmd.Flags().BoolVar(&prompt, "prompt", false, "prompt for secret value interactively") - cmd.Flags().BoolVar(&confirm, "confirm", false, "show recipient summary and ask for confirmation before encrypting") + cmd.Flags().BoolVar(&confirm, "confirm", false, "show peer summary and ask for confirmation before encrypting") cmd.Flags().BoolVar(&yes, "yes", false, "skip confirmation prompt (useful with --confirm in automation)") return cmd } @@ -172,7 +172,7 @@ func newDecryptCommand() *cobra.Command { var outTemp bool cmd := &cobra.Command{ Use: "decrypt", - Short: "Verify and decrypt envelope", + Short: "Receive and decrypt a secret", Aliases: []string{ "dec", }, @@ -258,7 +258,7 @@ func newVerifyCommand() *cobra.Command { var in string cmd := &cobra.Command{ Use: "verify", - Short: "Verify signature without decrypting", + Short: "Verify who sent a secret without decrypting", Aliases: []string{ "v", }, @@ -315,7 +315,7 @@ func resolveRecipient(store *keyring.Store, target string) (age.Recipient, strin } r, ok := store.Recipient(target) if !ok { - return nil, "", encryptRecipientSummary{}, fmt.Errorf("recipient alias not found: %s", target) + return nil, "", encryptRecipientSummary{}, fmt.Errorf("peer alias not found: %s", target) } rec, err := age.ParseX25519Recipient(r.AgePublic) if err != nil { @@ -344,9 +344,9 @@ func openConfirmationReader(in io.Reader) (io.Reader, io.Closer, error) { } func confirmEncrypt(in io.Reader, errw io.Writer, summary encryptSummary) error { - fmt.Fprintln(errw, "Encrypt summary:") + fmt.Fprintln(errw, "Send summary:") for _, recipient := range summary.Recipients { - fmt.Fprintf(errw, "- recipient: %s (fp=%s source=%s)\n", recipient.Label, recipient.Fingerprint, recipient.Source) + fmt.Fprintf(errw, "- peer: %s (fp=%s source=%s)\n", recipient.Label, recipient.Fingerprint, recipient.Source) } fmt.Fprintf(errw, "- signer: %s\n", summary.SignerID) fmt.Fprintf(errw, "- output: %s\n", summary.OutputPath) diff --git a/cmd/ende/crypto_cmd_test.go b/cmd/ende/crypto_cmd_test.go index bf6c572..33a5be2 100644 --- a/cmd/ende/crypto_cmd_test.go +++ b/cmd/ende/crypto_cmd_test.go @@ -129,8 +129,8 @@ func TestConfirmEncryptAcceptsYes(t *testing.T) { t.Fatalf("confirmEncrypt: %v", err) } got := errBuf.String() - if !strings.Contains(got, "recipient: bob") { - t.Fatalf("expected recipient summary, got:\n%s", got) + if !strings.Contains(got, "peer: bob") { + t.Fatalf("expected peer summary, got:\n%s", got) } if !strings.Contains(got, "Continue? [y/N]: ") { t.Fatalf("expected confirmation prompt, got:\n%s", got) diff --git a/cmd/ende/key_cmd.go b/cmd/ende/key_cmd.go index f96b8c9..474dc90 100644 --- a/cmd/ende/key_cmd.go +++ b/cmd/ende/key_cmd.go @@ -274,7 +274,7 @@ func newKeyShareCommand() *cobra.Command { var name string cmd := &cobra.Command{ Use: "share", - Short: "Print share token for an existing local key", + Short: "Print a share code for an existing local key", RunE: func(cmd *cobra.Command, args []string) error { if name == "" && len(args) == 1 { name = args[0] diff --git a/cmd/ende/parties_cmd.go b/cmd/ende/parties_cmd.go index b621254..eff7140 100644 --- a/cmd/ende/parties_cmd.go +++ b/cmd/ende/parties_cmd.go @@ -12,7 +12,7 @@ import ( ) func newSenderCommand() *cobra.Command { - cmd := &cobra.Command{Use: "sender", Short: "Manage trusted sender signing keys", Aliases: []string{"snd"}} + cmd := &cobra.Command{Use: "sender", Short: "Manage trusted peer signing keys", Aliases: []string{"snd"}} cmd.AddCommand(newSenderAddCommand(), newSenderShowCommand(), newSenderRotateCommand(), newSenderListCommand()) return cmd } @@ -22,7 +22,7 @@ func newSenderAddCommand() *cobra.Command { var force bool cmd := &cobra.Command{ Use: "add", - Short: "Add trusted sender signing public key", + Short: "Add a trusted peer signing public key", RunE: func(cmd *cobra.Command, args []string) error { if id == "" || signingPublic == "" { return fmt.Errorf("--id and --signing-public are required") @@ -56,7 +56,7 @@ func newSenderAddCommand() *cobra.Command { } func newRecipientCommand() *cobra.Command { - cmd := &cobra.Command{Use: "recipient", Short: "Manage recipient aliases", Aliases: []string{"rcpt"}} + cmd := &cobra.Command{Use: "recipient", Short: "Manage peer aliases", Aliases: []string{"rcpt"}} cmd.AddCommand(newRecipientAddCommand(), newRecipientShowCommand(), newRecipientRotateCommand()) return cmd } @@ -66,7 +66,7 @@ func newRegisterCommand() *cobra.Command { var force bool cmd := &cobra.Command{ Use: "register", - Short: "Register recipient and trusted sender in one step", + Short: "Add a peer from a share code or public keys", Aliases: []string{"reg"}, RunE: func(cmd *cobra.Command, args []string) error { store, err := keyring.Load() @@ -151,7 +151,7 @@ func newUnregisterCommand() *cobra.Command { var force bool cmd := &cobra.Command{ Use: "unregister ", - Short: "Remove recipient and trusted sender registration for an alias", + Short: "Remove a peer alias and its trusted signing key", Aliases: []string{"unreg"}, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -185,7 +185,7 @@ func newRecipientAddCommand() *cobra.Command { var force bool cmd := &cobra.Command{ Use: "add", - Short: "Add recipient by alias or GitHub username", + Short: "Add a peer by alias, share code, or GitHub username", RunE: func(cmd *cobra.Command, args []string) error { store, err := keyring.Load() if err != nil { @@ -288,9 +288,9 @@ func newRecipientAddCommand() *cobra.Command { return nil }, } - cmd.Flags().StringVar(&alias, "alias", "", "recipient alias") + cmd.Flags().StringVar(&alias, "alias", "", "peer alias") cmd.Flags().StringVar(&key, "key", "", "age recipient public key") - cmd.Flags().StringVar(&share, "share", "", "share token from keygen output") + cmd.Flags().StringVar(&share, "share", "", "share code from keygen output") cmd.Flags().StringVar(&githubUser, "github", "", "github username (optional resolver)") cmd.Flags().IntVar(&keyIndex, "key-index", 0, "github ssh key index for pinning") cmd.Flags().BoolVar(&force, "force", false, "overwrite existing recipient alias") @@ -300,7 +300,7 @@ func newRecipientAddCommand() *cobra.Command { func newSenderListCommand() *cobra.Command { cmd := &cobra.Command{ Use: "list", - Short: "List trusted senders", + Short: "List trusted peer signing keys", Aliases: []string{ "ls", }, @@ -322,7 +322,7 @@ func newSenderListCommand() *cobra.Command { func newSenderShowCommand() *cobra.Command { cmd := &cobra.Command{ Use: "show ", - Short: "Show trusted sender details", + Short: "Show trusted peer signing key details", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { store, err := keyring.Load() @@ -344,7 +344,7 @@ func newSenderRotateCommand() *cobra.Command { var signingPublic string cmd := &cobra.Command{ Use: "rotate ", - Short: "Rotate trusted sender signing public key", + Short: "Rotate a trusted peer signing public key", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if signingPublic == "" { @@ -378,7 +378,7 @@ func newSenderRotateCommand() *cobra.Command { func newRecipientShowCommand() *cobra.Command { cmd := &cobra.Command{ Use: "show ", - Short: "Show recipient details", + Short: "Show peer details", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { store, err := keyring.Load() @@ -400,7 +400,7 @@ func newRecipientRotateCommand() *cobra.Command { var key string cmd := &cobra.Command{ Use: "rotate ", - Short: "Rotate recipient public key", + Short: "Rotate a peer public key", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if key == "" { diff --git a/cmd/ende/prompt.go b/cmd/ende/prompt.go index 9898437..6fc869b 100644 --- a/cmd/ende/prompt.go +++ b/cmd/ende/prompt.go @@ -19,12 +19,12 @@ type fdReader interface { func promptRecipientInput(in io.Reader, errw io.Writer) (alias string, keyOrShare string, err error) { r := bufio.NewReader(in) - fmt.Fprint(errw, "alias: ") + fmt.Fprint(errw, "peer alias: ") alias, err = r.ReadString('\n') if err != nil && err != io.EOF { return "", "", fmt.Errorf("read alias: %w", err) } - fmt.Fprint(errw, "key/share: ") + fmt.Fprint(errw, "peer public key or share code: ") keyOrShare, err = r.ReadString('\n') if err != nil && err != io.EOF { return "", "", fmt.Errorf("read key/share: %w", err) @@ -67,7 +67,7 @@ func promptRegisterInput(in io.Reader, errw io.Writer) (alias string, recipientO if err != nil && err != io.EOF { return "", "", "", fmt.Errorf("read alias: %w", err) } - fmt.Fprint(errw, "recipient key or share token: ") + fmt.Fprint(errw, "peer public key or share code: ") recipientOrShare, err = r.ReadString('\n') if err != nil && err != io.EOF { return "", "", "", fmt.Errorf("read recipient/share: %w", err) @@ -108,16 +108,16 @@ func readEnvelopeInteractive(in io.Reader, errw io.Writer) ([]byte, error) { func promptShareRegisterInput(in io.Reader, errw io.Writer) (share string, aliasOverride string, err error) { r := bufio.NewReader(in) - fmt.Fprint(errw, "share token (ENDE-PUB-1:...): ") + fmt.Fprint(errw, "share code (ENDE-PUB-1:...): ") share, err = r.ReadString('\n') if err != nil && err != io.EOF { - return "", "", fmt.Errorf("read share token: %w", err) + return "", "", fmt.Errorf("read share code: %w", err) } share = strings.TrimSpace(share) if share == "" { - return "", "", fmt.Errorf("share token is required") + return "", "", fmt.Errorf("share code is required") } - fmt.Fprint(errw, "alias override (optional, Enter to use token id): ") + fmt.Fprint(errw, "peer name override (optional, Enter to use the shared name): ") aliasOverride, err = r.ReadString('\n') if err != nil && err != io.EOF { return "", "", fmt.Errorf("read alias override: %w", err) diff --git a/cmd/ende/root.go b/cmd/ende/root.go index 490ce45..f2a64b7 100644 --- a/cmd/ende/root.go +++ b/cmd/ende/root.go @@ -18,7 +18,7 @@ func main() { var debug bool root := &cobra.Command{ Use: "ende", - Short: "Ende securely encrypts secrets between developers", + Short: "Send secrets securely between developers and peers", Version: version, PersistentPreRun: func(cmd *cobra.Command, args []string) { if debug { diff --git a/cmd/ende/tutorial_cmd.go b/cmd/ende/tutorial_cmd.go index 933243a..087ccdf 100644 --- a/cmd/ende/tutorial_cmd.go +++ b/cmd/ende/tutorial_cmd.go @@ -28,11 +28,11 @@ const ( colorWhite = "\033[97m" ) -func bold(s string) string { return colorBold + s + colorReset } -func cyan(s string) string { return colorCyan + s + colorReset } -func green(s string) string { return colorGreen + s + colorReset } -func yellow(s string) string { return colorYellow + s + colorReset } -func gray(s string) string { return colorGray + s + colorReset } +func bold(s string) string { return colorBold + s + colorReset } +func cyan(s string) string { return colorCyan + s + colorReset } +func green(s string) string { return colorGreen + s + colorReset } +func yellow(s string) string { return colorYellow + s + colorReset } +func gray(s string) string { return colorGray + s + colorReset } func highlight(s string) string { return colorBold + colorBgBlack + colorWhite + " " + s + " " + colorReset } @@ -102,23 +102,23 @@ var langs = map[string]tutorialLang{ keygenOpt2: " [2] Use an existing key", keygenNameHint: " --name is the identity used between sender and recipient (e.g. -t, --sign-as).", keygenCmd: " ende key keygen --name --export-public", - keygenDone: "Key generated. Share the token below with your peer:", - keygenExistDone: "Key found. Share the token below with your peer:", + keygenDone: "Key generated. Share the code below with your peer:", + keygenExistDone: "Key found. Share the code below with your peer:", keygenNotFound: "Key not found. Please check the name with `ende key list`.", keygenShareLabel: " share: ", register: "Register a peer (recipient)", registerOption: "How do you want to register the peer?", - registerOpt1: " [1] Paste peer's share token (peer already has a key)", + registerOpt1: " [1] Paste peer's share code (peer already has a key)", registerOpt2: " [2] Generate a key for the peer now (local test)", - registerDesc: "Paste the peer's share token to register them as a recipient and trusted sender.", - registerShareHint: " The share token is the 'share:' value printed during the peer's keygen.", + registerDesc: "Paste the peer's share code to add them as a peer and trusted signing key.", + registerShareHint: " The share code is the 'share:' value printed during the peer's keygen.", registerExists: "Already registered? Remove first:", registerUnreg: " ende unregister ", registerRetry: "Then run register again.", registerOk: "Registered peer: ", registerSkipped: "No token entered, skipping registration.", registerPeerName: "Enter peer key name: ", - registerPeerShare: "Peer's share token: ", + registerPeerShare: "Peer's share code: ", encryptTitle: "Encrypt a secret", encryptDesc: "Encrypt plaintext and sign it with your key.", encryptToHint: " -t : the alias registered in Step 2 (the recipient)", @@ -143,7 +143,7 @@ var langs = map[string]tutorialLang{ done: "Tutorial complete. Happy encrypting!", pressEnter: " [Press Enter to continue]", inputName: "Enter your key name: ", - inputShare: "Enter peer's share token (ENDE-PUB-1:...) or press Enter to skip: ", + inputShare: "Enter peer's share code (ENDE-PUB-1:...) or press Enter to skip: ", }, "kr": { selectLang: "Select language / 언어 선택 [en/kr]: ", @@ -155,23 +155,23 @@ var langs = map[string]tutorialLang{ keygenOpt2: " [2] 기존 키 사용", keygenNameHint: " --name은 송신자와 수신자 간 식별에 사용되는 이름입니다 (예: -t, --sign-as).", keygenCmd: " ende key keygen --name <이름> --export-public", - keygenDone: "키가 생성됐습니다. 아래 토큰을 상대방에게 전달하세요:", - keygenExistDone: "키를 찾았습니다. 아래 토큰을 상대방에게 전달하세요:", + keygenDone: "키가 생성됐습니다. 아래 공유 코드를 상대방에게 전달하세요:", + keygenExistDone: "키를 찾았습니다. 아래 공유 코드를 상대방에게 전달하세요:", keygenNotFound: "키를 찾을 수 없습니다. `ende key list`로 이름을 확인하세요.", keygenShareLabel: " share: ", register: "상대방 등록 (수신자)", registerOption: "상대방 등록 방법을 선택하세요:", - registerOpt1: " [1] 상대방 share 토큰 입력 (상대방이 이미 키를 가진 경우)", + registerOpt1: " [1] 상대방 share 코드 입력 (상대방이 이미 키를 가진 경우)", registerOpt2: " [2] 상대방 키를 직접 생성해서 등록 (로컬 테스트용)", - registerDesc: "상대방의 share 토큰을 붙여넣으면 수신자 및 신뢰 발신자로 자동 등록됩니다.", - registerShareHint: " share 토큰은 상대방이 keygen 시 출력된 'share:' 값입니다.", + registerDesc: "상대방의 share 코드를 붙여넣으면 peer와 신뢰 발신자로 자동 등록됩니다.", + registerShareHint: " share 코드는 상대방이 keygen 시 출력된 'share:' 값입니다.", registerExists: "이미 등록된 경우 먼저 제거하세요:", registerUnreg: " ende unregister ", registerRetry: "그 다음 다시 register를 실행하세요.", registerOk: "등록 완료: ", registerSkipped: "토큰 미입력, 등록을 건너뜁니다.", registerPeerName: "상대방 키 이름을 입력하세요: ", - registerPeerShare: "상대방 share 토큰: ", + registerPeerShare: "상대방 share 코드: ", encryptTitle: "비밀 암호화", encryptDesc: "평문을 암호화하고 내 키로 서명합니다.", encryptToHint: " -t : 단계 2에서 등록한 상대방 alias (수신자)", @@ -196,7 +196,7 @@ var langs = map[string]tutorialLang{ done: "튜토리얼 완료. 안전하게 사용하세요!", pressEnter: " [Enter 키를 눌러 계속]", inputName: "키 이름을 입력하세요: ", - inputShare: "상대방 share 토큰을 입력하세요 (ENDE-PUB-1:...) 또는 Enter로 건너뛰기: ", + inputShare: "상대방 share 코드를 입력하세요 (ENDE-PUB-1:...) 또는 Enter로 건너뛰기: ", }, } @@ -475,8 +475,8 @@ func tutorialKeygen(name string) (string, error) { store.AddKey(keyring.KeyEntry{ ID: name, AgeIdentity: agePath, - SignPrivate: signPath, - SignPublic: signPub, + SignPrivate: signPath, + SignPublic: signPub, }) if err := store.AddSender(name, signPub, "local-key", "", true); err != nil { return "", err