Compare commits

...

7 Commits

Author SHA1 Message Date
akulij
14439a6479 use flags --output and --raw-output
Now logic for output looks like:
password from stdin (stays as was)
keys: private to stdout OR private to file (via -o flag) and public to stdout
stderr for errors and password prompt
2025-02-26 23:22:47 +00:00
akulij
3b1079985a add usage flags 2025-02-26 23:07:36 +00:00
akulij
2a22ce4cc5 delete password hash wich however stayed till this commit 2025-02-26 21:44:50 +00:00
akulij
7527faa139 go fmt 2025-02-26 21:44:34 +00:00
akulij
c935f4ba81 make key output behaviour closer to the one in age-keygen 2025-02-26 21:44:01 +00:00
akulij
be0aeac776 README: delete task to rint recipients file from identity
age-keygen already can do it
2025-02-26 21:21:24 +00:00
akulij
7ef14e3cd1 README: add todos 2025-02-26 21:07:46 +00:00
2 changed files with 73 additions and 6 deletions

View File

@ -7,3 +7,8 @@ Strong password highly recomended
## Password selection ## Password selection
To keep entropy at at the same level of $\geq 2^{256}$ bits, as in private key of curve25519 (which is used in age encryption), you should use long password. To keep entropy at at the same level of $\geq 2^{256}$ bits, as in private key of curve25519 (which is used in age encryption), you should use long password.
Exact amount of required characters can be calculated by formula: $\lceil 256 / log_2(Nchars) \rceil$ Exact amount of required characters can be calculated by formula: $\lceil 256 / log_2(Nchars) \rceil$
## TODO
[ ] piped/terminal output as raw/verbose
[ ] handle broken pipe signal since program will so much depend on pipes

View File

@ -2,6 +2,7 @@ package main
import ( import (
"errors" "errors"
"flag"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -21,30 +22,69 @@ type X25519Identity struct {
secretKey, ourPublicKey []byte secretKey, ourPublicKey []byte
} }
const usage = `Usage:
age-gen-passphrase [-o OUTPUT] [--raw-input]
Options:
-o, --output OUTPUT Write the result to the file at path OUTPUT.
--raw-output Print stripped keys (without additional text or comments)
Everything is similar to age-keygen`
func main() { func main() {
log.SetFlags(0)
flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n", usage) }
var (
rawOutput bool
outputFile string
)
flag.BoolVar(&rawOutput, "raw-output", false, "Print stripped keys (without additional text or comments)")
flag.StringVar(&outputFile, "o", "", "Write the result to the file at path OUTPUT")
flag.StringVar(&outputFile, "output", "", "Write the result to the file at path OUTPUT")
flag.Parse()
passbytes, err := getPasswordBytes() passbytes, err := getPasswordBytes()
if err != nil { if err != nil {
errorf("Failed to get password, error: %s\n", err) errorf("Failed to get password, error: %s\n", err)
} }
sum := sha256.Sum256(passbytes) sum := sha256.Sum256(passbytes)
fmt.Printf("Password hash: %x\n", sum)
k, err := newX25519IdentityFromScalar(sum[:]) k, err := newX25519IdentityFromScalar(sum[:])
if err != nil { if err != nil {
errorf("internal error: %v", err) errorf("internal error: %v", err)
} }
fmt.Printf("Public key: %s\n", k.Recipient()) // if user is not seeing private keyfile, which also contains public key,
// also duplicate public key it to stderr,
// but if user sees public key via stdout, no need for duplication
if outputFile != "" {
if !rawOutput {
fmt.Printf("Public key: %s\n", k.Recipient())
} else {
fmt.Printf("%s", k.Recipient())
}
}
fmt.Printf("# created: %s\n", time.Now().Format(time.RFC3339)) output := os.Stdout
fmt.Printf("# public key: %s\n", k.Recipient()) if outputFile != "" {
fmt.Printf("%s\n", k) output, err = os.Create(outputFile)
if err != nil {
errorf("failed to create output file, error: %s", err)
}
}
err = writeSecretKey(output, k, !rawOutput)
if err != nil {
fmt.Printf("Failed to write secret key to file, error: %s\n", err)
}
} }
func getPasswordBytes() ([]byte, error) { func getPasswordBytes() ([]byte, error) {
if term.IsTerminal(int(os.Stdin.Fd())) { if term.IsTerminal(int(os.Stdin.Fd())) {
fmt.Print("Enter password: ") fmt.Fprintf(os.Stderr, "Enter password: ")
passbytes, err := term.ReadPassword(int(os.Stdin.Fd())) passbytes, err := term.ReadPassword(int(os.Stdin.Fd()))
fmt.Println() fmt.Println()
return passbytes, err return passbytes, err
@ -53,6 +93,28 @@ func getPasswordBytes() ([]byte, error) {
} }
} }
func writeSecretKey(f *os.File, key *age.X25519Identity, verbose bool) error {
var err error
if verbose {
_, err = fmt.Fprintf(f, "# created: %s\n", time.Now().Format(time.RFC3339))
if err != nil {
return err
}
_, err = fmt.Fprintf(f, "# public key: %s\n", key.Recipient())
if err != nil {
return err
}
_, err = fmt.Fprintf(f, "%s\n", key)
} else {
_, err = fmt.Fprintf(f, "%s", key)
}
return err
}
// almost a copy of private function in age/x25519.go // almost a copy of private function in age/x25519.go
func newX25519IdentityFromScalar(secretKey []byte) (*age.X25519Identity, error) { func newX25519IdentityFromScalar(secretKey []byte) (*age.X25519Identity, error) {
if len(secretKey) != curve25519.ScalarSize { if len(secretKey) != curve25519.ScalarSize {