package userbase import ( "bytes" "errors" "fmt" "io" "filippo.io/age" ) var ( ErrUndecryptable = errors.New("userbase: the export cannot be decrypted with this identity") ErrNoRecipients = errors.New("userbase: an export needs at least one recipient") ) func Seal(plaintext []byte, recipients ...*age.X25519Recipient) ([]byte, error) { if len(recipients) == 0 { return nil, ErrNoRecipients } rs := make([]age.Recipient, 0, len(recipients)) for _, r := range recipients { if r == nil { return nil, ErrNoRecipients } rs = append(rs, r) } var out bytes.Buffer w, err := age.Encrypt(&out, rs...) if err != nil { return nil, fmt.Errorf("userbase: seal: %w", err) } if _, err := w.Write(plaintext); err != nil { return nil, fmt.Errorf("userbase: seal: %w", err) } if err := w.Close(); err != nil { return nil, fmt.Errorf("userbase: seal: %w", err) } return out.Bytes(), nil } func Open(ciphertext []byte, identity *age.X25519Identity) ([]byte, error) { if identity == nil { return nil, ErrUndecryptable } r, err := age.Decrypt(bytes.NewReader(ciphertext), identity) var noMatch *age.NoIdentityMatchError if errors.As(err, &noMatch) { return nil, ErrUndecryptable } if err != nil { return nil, fmt.Errorf("%w: %w", ErrUndecryptable, err) } plain, err := io.ReadAll(r) if err != nil { return nil, fmt.Errorf("%w: %w", ErrUndecryptable, err) } return plain, nil }