package ageio import ( "bytes" "fmt" "io" "os" "filippo.io/age" ) // Encryptor handles age encryption operations type Encryptor struct { recipients []age.Recipient } // Decryptor handles age decryption operations type Decryptor struct { identities []age.Identity } // NewEncryptor creates an encryptor from a recipients file func NewEncryptor(recipientsPath string) (*Encryptor, error) { if recipientsPath == "" { return nil, fmt.Errorf("recipients path is empty") } data, err := os.ReadFile(recipientsPath) if err != nil { return nil, fmt.Errorf("read recipients file: %w", err) } recipients, err := age.ParseRecipients(bytes.NewReader(data)) if err != nil { return nil, fmt.Errorf("parse recipients: %w", err) } if len(recipients) == 0 { return nil, fmt.Errorf("no recipients found in file") } return &Encryptor{recipients: recipients}, nil } // NewDecryptor creates a decryptor from an identity file func NewDecryptor(identityPath string) (*Decryptor, error) { if identityPath == "" { return nil, fmt.Errorf("identity path is empty") } data, err := os.ReadFile(identityPath) if err != nil { return nil, fmt.Errorf("read identity file: %w", err) } identities, err := age.ParseIdentities(bytes.NewReader(data)) if err != nil { return nil, fmt.Errorf("parse identities: %w", err) } if len(identities) == 0 { return nil, fmt.Errorf("no identities found in file") } return &Decryptor{identities: identities}, nil } // Encrypt encrypts plaintext data with age func (e *Encryptor) Encrypt(plaintext []byte) ([]byte, error) { if len(plaintext) == 0 { return nil, fmt.Errorf("plaintext is empty") } var buf bytes.Buffer w, err := age.Encrypt(&buf, e.recipients...) if err != nil { return nil, fmt.Errorf("create encryptor: %w", err) } if _, err := w.Write(plaintext); err != nil { return nil, fmt.Errorf("write plaintext: %w", err) } if err := w.Close(); err != nil { return nil, fmt.Errorf("close encryptor: %w", err) } return buf.Bytes(), nil } // Decrypt decrypts age-encrypted data func (d *Decryptor) Decrypt(ciphertext []byte) ([]byte, error) { if len(ciphertext) == 0 { return nil, fmt.Errorf("ciphertext is empty") } r, err := age.Decrypt(bytes.NewReader(ciphertext), d.identities...) if err != nil { return nil, fmt.Errorf("create decryptor: %w", err) } plaintext, err := io.ReadAll(r) if err != nil { return nil, fmt.Errorf("read plaintext: %w", err) } return plaintext, nil } // EncryptStream creates an encrypted writer for streaming func (e *Encryptor) EncryptStream(w io.Writer) (io.WriteCloser, error) { ew, err := age.Encrypt(w, e.recipients...) if err != nil { return nil, fmt.Errorf("create stream encryptor: %w", err) } return ew, nil } // DecryptStream creates a decrypted reader for streaming func (d *Decryptor) DecryptStream(r io.Reader) (io.Reader, error) { dr, err := age.Decrypt(r, d.identities...) if err != nil { return nil, fmt.Errorf("create stream decryptor: %w", err) } return dr, nil }