oficina / home Public

Clone
git clone https://amadan.net/oficina/home

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux
2
3package provisioner
4
5import (
6 "bytes"
7 "encoding/base64"
8 "io"
9
10 family "amadan.net/rastrillo/rastrillo/crypto"
11 "filippo.io/age"
12)
13
14type credential struct {
15 Version int `json:"version"`
16 Identity floorIdentity `json:"identity"`
17 Keypair string `json:"keypair"`
18}
19
20func wrapCredential(k *family.Keypair, id floorIdentity, password []byte) ([]byte, error) {
21 if len(password) == 0 || len(password) > 1024 || k == nil || k.SignPriv == nil || k.BoxPriv == nil || base64.RawURLEncoding.EncodeToString(k.SignPub()) != id.Public {
22 return nil, ErrRefused
23 }
24 raw, e := family.MarshalKeypair(k)
25 if e != nil {
26 return nil, ErrRefused
27 }
28 body, e := canonicalJSON(credential{1, id, base64.RawURLEncoding.EncodeToString(raw)})
29 if e != nil {
30 return nil, e
31 }
32 return encryptPrivate(body, password)
33}
34func unwrapCredential(ciphertext, password []byte) (floorIdentity, *family.Keypair, error) {
35 body, e := decryptPrivate(ciphertext, password, 16384)
36 if e != nil {
37 return floorIdentity{}, nil, e
38 }
39 var c credential
40 if closedJSON(body, &c) != nil || c.Version != 1 {
41 return floorIdentity{}, nil, ErrRefused
42 }
43 raw, e := base64.RawURLEncoding.DecodeString(c.Keypair)
44 if e != nil || base64.RawURLEncoding.EncodeToString(raw) != c.Keypair {
45 return floorIdentity{}, nil, ErrRefused
46 }
47 k, e := family.UnmarshalKeypair(raw)
48 if e != nil || base64.RawURLEncoding.EncodeToString(k.SignPub()) != c.Identity.Public {
49 return floorIdentity{}, nil, ErrRefused
50 }
51 if _, ok := tokenBytes(c.Identity.Registry, 32); !ok {
52 return floorIdentity{}, nil, ErrRefused
53 }
54 if _, ok := tokenBytes(c.Identity.Device, 32); !ok {
55 return floorIdentity{}, nil, ErrRefused
56 }
57 return c.Identity, k, nil
58}
59func encryptPrivate(body, password []byte) ([]byte, error) {
60 if len(body) > 64<<10 || len(password) == 0 || len(password) > 1024 {
61 return nil, ErrRefused
62 }
63 recipient, e := age.NewScryptRecipient(string(password))
64 if e != nil {
65 return nil, ErrRefused
66 }
67 var out bytes.Buffer
68 w, e := age.Encrypt(&out, recipient)
69 if e != nil {
70 return nil, ErrUnavailable
71 }
72 if _, e = w.Write(body); e != nil {
73 return nil, ErrUnavailable
74 }
75 if e = w.Close(); e != nil {
76 return nil, ErrUnavailable
77 }
78 return out.Bytes(), nil
79}
80func decryptPrivate(ciphertext, password []byte, limit int) ([]byte, error) {
81 if len(ciphertext) == 0 || len(ciphertext) > 128<<10 || len(password) == 0 || len(password) > 1024 || limit <= 0 || limit > 64<<10 {
82 return nil, ErrRefused
83 }
84 identity, e := age.NewScryptIdentity(string(password))
85 if e != nil {
86 return nil, ErrRefused
87 }
88 identity.SetMaxWorkFactor(18)
89 r, e := age.Decrypt(bytes.NewReader(ciphertext), identity)
90 if e != nil {
91 return nil, ErrRefused
92 }
93 body, e := io.ReadAll(io.LimitReader(r, int64(limit)+1))
94 if e != nil || len(body) > limit {
95 return nil, ErrRefused
96 }
97 return body, nil
98}
99