Raw
 1package userbase
 2
 3import (
 4	"bytes"
 5	"errors"
 6	"fmt"
 7	"io"
 8
 9	"filippo.io/age"
10)
11
12var (
13	ErrUndecryptable = errors.New("userbase: the export cannot be decrypted with this identity")
14	ErrNoRecipients  = errors.New("userbase: an export needs at least one recipient")
15)
16
17func Seal(plaintext []byte, recipients ...*age.X25519Recipient) ([]byte, error) {
18	if len(recipients) == 0 {
19		return nil, ErrNoRecipients
20	}
21	rs := make([]age.Recipient, 0, len(recipients))
22	for _, r := range recipients {
23		if r == nil {
24			return nil, ErrNoRecipients
25		}
26		rs = append(rs, r)
27	}
28	var out bytes.Buffer
29	w, err := age.Encrypt(&out, rs...)
30	if err != nil {
31		return nil, fmt.Errorf("userbase: seal: %w", err)
32	}
33	if _, err := w.Write(plaintext); err != nil {
34		return nil, fmt.Errorf("userbase: seal: %w", err)
35	}
36	if err := w.Close(); err != nil {
37		return nil, fmt.Errorf("userbase: seal: %w", err)
38	}
39	return out.Bytes(), nil
40}
41
42func Open(ciphertext []byte, identity *age.X25519Identity) ([]byte, error) {
43	if identity == nil {
44		return nil, ErrUndecryptable
45	}
46	r, err := age.Decrypt(bytes.NewReader(ciphertext), identity)
47	var noMatch *age.NoIdentityMatchError
48	if errors.As(err, &noMatch) {
49		return nil, ErrUndecryptable
50	}
51	if err != nil {
52		return nil, fmt.Errorf("%w: %w", ErrUndecryptable, err)
53	}
54	plain, err := io.ReadAll(r)
55	if err != nil {
56		return nil, fmt.Errorf("%w: %w", ErrUndecryptable, err)
57	}
58	return plain, nil
59}