mirror of
https://github.com/FiloSottile/age.git
synced 2025-12-23 05:25:14 +00:00
age: replace ParseX25519Identities with ParseIdentities
The latter returns a []Identity that can be used with Decrypt directly.
This commit is contained in:
16
age.go
16
age.go
@@ -28,7 +28,7 @@
|
||||
// There is no default path for age keys. Instead, they should be stored at
|
||||
// application-specific paths. The CLI supports files where private keys are
|
||||
// listed one per line, ignoring empty lines and lines starting with "#". These
|
||||
// files can be parsed with ParseX25519Identities.
|
||||
// files can be parsed with ParseIdentities.
|
||||
//
|
||||
// When integrating age into a new system, it's recommended that you only
|
||||
// support X25519 keys, and not SSH keys. The latter are supported for manual
|
||||
@@ -90,11 +90,13 @@ type Stanza struct {
|
||||
const fileKeySize = 16
|
||||
const streamNonceSize = 16
|
||||
|
||||
// Encrypt returns a WriteCloser. Writes to the returned value are encrypted and
|
||||
// written to dst as an age file. Every recipient will be able to decrypt the file.
|
||||
// Encrypt encrypts a file to one or more recipients.
|
||||
//
|
||||
// The caller must call Close on the returned value when done for the last chunk
|
||||
// to be encrypted and flushed to dst.
|
||||
// Writes to the returned WriteCloser are encrypted and written to dst as an age
|
||||
// file. Every recipient will be able to decrypt the file.
|
||||
//
|
||||
// The caller must call Close on the WriteCloser when done for the last chunk to
|
||||
// be encrypted and flushed to dst.
|
||||
func Encrypt(dst io.Writer, recipients ...Recipient) (io.WriteCloser, error) {
|
||||
if len(recipients) == 0 {
|
||||
return nil, errors.New("no recipients specified")
|
||||
@@ -137,7 +139,9 @@ func Encrypt(dst io.Writer, recipients ...Recipient) (io.WriteCloser, error) {
|
||||
return stream.NewWriter(streamKey(fileKey, nonce), dst)
|
||||
}
|
||||
|
||||
// Decrypt returns a Reader reading the decrypted plaintext of the age file read
|
||||
// Decrypt decrypts a file encrypted to one or more identities.
|
||||
//
|
||||
// It returns a Reader reading the decrypted plaintext of the age file read
|
||||
// from src. All identities will be tried until one successfully decrypts the file.
|
||||
func Decrypt(src io.Reader, identities ...Identity) (io.Reader, error) {
|
||||
if len(identities) == 0 {
|
||||
|
||||
35
age_test.go
35
age_test.go
@@ -13,6 +13,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -78,6 +79,32 @@ func ExampleDecrypt() {
|
||||
// File contents: "Black lives matter."
|
||||
}
|
||||
|
||||
func ExampleParseIdentities() {
|
||||
keyFile, err := os.Open("testdata/keys.txt")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open private keys file: %v", err)
|
||||
}
|
||||
identities, err := age.ParseIdentities(keyFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse private key %q: %v", privateKey, err)
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
f := bytes.NewReader(fileContents)
|
||||
|
||||
r, err := age.Decrypt(f, identities...)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open encrypted file: %v", err)
|
||||
}
|
||||
if _, err := io.Copy(out, r); err != nil {
|
||||
log.Fatalf("Failed to read encrypted file: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("File contents: %q\n", out.Bytes())
|
||||
// Output:
|
||||
// File contents: "Black lives matter."
|
||||
}
|
||||
|
||||
func ExampleGenerateX25519Identity() {
|
||||
identity, err := age.GenerateX25519Identity()
|
||||
if err != nil {
|
||||
@@ -164,7 +191,7 @@ func TestEncryptDecryptScrypt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseX25519Identities(t *testing.T) {
|
||||
func TestParseIdentities(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
wantCount int
|
||||
@@ -184,13 +211,13 @@ AGE-SECRET-KEY--1D6K0SGAX3NU66R4GYFZY0UQWCLM3UUSF3CXLW4KXZM342WQSJ82QKU59Q`},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := age.ParseX25519Identities(strings.NewReader(tt.file))
|
||||
got, err := age.ParseIdentities(strings.NewReader(tt.file))
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ParseX25519Identities() error = %v, wantErr %v", err, tt.wantErr)
|
||||
t.Errorf("ParseIdentities() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if len(got) != tt.wantCount {
|
||||
t.Errorf("ParseX25519Identities() returned %d identities, want %d", len(got), tt.wantCount)
|
||||
t.Errorf("ParseIdentities() returned %d identities, want %d", len(got), tt.wantCount)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -51,15 +51,11 @@ func parseIdentitiesFile(name string) ([]age.Identity, error) {
|
||||
return parseSSHIdentity(name, contents)
|
||||
}
|
||||
|
||||
ids, err := age.ParseX25519Identities(b)
|
||||
ids, err := age.ParseIdentities(b)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read %q: %v", name, err)
|
||||
}
|
||||
res := make([]age.Identity, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
res = append(res, id)
|
||||
}
|
||||
return res, nil
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func parseSSHIdentity(name string, pemBytes []byte) ([]age.Identity, error) {
|
||||
|
||||
2
testdata/keys.txt
vendored
Normal file
2
testdata/keys.txt
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Test key for ExampleParseX25519Identities.
|
||||
AGE-SECRET-KEY-184JMZMVQH3E6U0PSL869004Y3U2NYV7R30EU99CSEDNPH02YUVFSZW44VU
|
||||
13
x25519.go
13
x25519.go
@@ -150,7 +150,7 @@ func ParseX25519Identity(s string) (*X25519Identity, error) {
|
||||
return nil, fmt.Errorf("malformed secret key: %v", err)
|
||||
}
|
||||
if t != "AGE-SECRET-KEY-" {
|
||||
return nil, fmt.Errorf("malformed secret key: invalid type %q", t)
|
||||
return nil, fmt.Errorf("malformed secret key: unknown type %q", t)
|
||||
}
|
||||
r, err := newX25519IdentityFromScalar(k)
|
||||
if err != nil {
|
||||
@@ -159,15 +159,18 @@ func ParseX25519Identity(s string) (*X25519Identity, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// ParseX25519Identities parses a file with one or more Bech32 private key
|
||||
// encodings, one per line. Empty lines and lines starting with "#" are ignored.
|
||||
// ParseIdentities parses a file with one or more private key encodings, one per
|
||||
// line. Empty lines and lines starting with "#" are ignored.
|
||||
//
|
||||
// This is the same syntax as the private key files accepted by the CLI, except
|
||||
// the CLI also accepts SSH private keys, which are not recommended for the
|
||||
// average application.
|
||||
func ParseX25519Identities(f io.Reader) ([]*X25519Identity, error) {
|
||||
//
|
||||
// Currently, all returned values are of type X25519Identity, but different
|
||||
// types might be returned in the future.
|
||||
func ParseIdentities(f io.Reader) ([]Identity, error) {
|
||||
const privateKeySizeLimit = 1 << 24 // 16 MiB
|
||||
var ids []*X25519Identity
|
||||
var ids []Identity
|
||||
scanner := bufio.NewScanner(io.LimitReader(f, privateKeySizeLimit))
|
||||
var n int
|
||||
for scanner.Scan() {
|
||||
|
||||
Reference in New Issue
Block a user