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 "encoding/base64"
7 "os"
8 "strings"
9
10 family "amadan.net/rastrillo/rastrillo/crypto"
11)
12
13type RegistryInfo struct {
14 Registry string `json:"registry"`
15 Device string `json:"device"`
16 PublicKey string `json:"public_key"`
17 Sequence int64 `json:"sequence"`
18}
19
20// Initialize creates or resumes only the identity already recorded in these
21// independent directories. It never replaces a credential or existing floor.
22func Initialize(statePath, floorPath string, password []byte) (RegistryInfo, error) {
23 if statePath == floorPath || strings.HasPrefix(statePath, floorPath+"/") || strings.HasPrefix(floorPath, statePath+"/") {
24 return RegistryInfo{}, ErrRefused
25 }
26 state, e := openPrivateDirectory(statePath)
27 if e != nil {
28 return RegistryInfo{}, e
29 }
30 defer state.close()
31 floorDir, e := openPrivateDirectory(floorPath)
32 if e != nil {
33 return RegistryInfo{}, e
34 }
35 defer floorDir.close()
36 stateLock, e := state.lock()
37 if e != nil {
38 return RegistryInfo{}, e
39 }
40 defer stateLock.Close()
41 floorLock, e := floorDir.lock()
42 if e != nil {
43 return RegistryInfo{}, e
44 }
45 defer floorLock.Close()
46 names, e := directoryNames(state)
47 if e != nil {
48 return RegistryInfo{}, e
49 }
50 present := map[string]bool{}
51 for _, name := range names {
52 switch name {
53 case "lock", "credential.age", "initializing.json", "registry.db", "registry.db-wal", "registry.db-shm":
54 present[name] = true
55 default:
56 return RegistryInfo{}, ErrRefused
57 }
58 }
59 floorNames, e := directoryNames(floorDir)
60 if e != nil {
61 return RegistryInfo{}, e
62 }
63 var id floorIdentity
64 var key *family.Keypair
65 if !present["credential.age"] {
66 for name := range present {
67 if name != "lock" {
68 return RegistryInfo{}, ErrRefused
69 }
70 }
71 for _, name := range floorNames {
72 if name != "lock" {
73 return RegistryInfo{}, ErrRefused
74 }
75 }
76 key, e = family.Generate()
77 if e != nil {
78 return RegistryInfo{}, ErrUnavailable
79 }
80 registry, e := family.NewKey()
81 if e != nil {
82 return RegistryInfo{}, ErrUnavailable
83 }
84 device, e := family.NewKey()
85 if e != nil {
86 return RegistryInfo{}, ErrUnavailable
87 }
88 id = floorIdentity{base64.RawURLEncoding.EncodeToString(registry), base64.RawURLEncoding.EncodeToString(device), base64.RawURLEncoding.EncodeToString(key.SignPub())}
89 wrapped, e := wrapCredential(key, id, password)
90 if e != nil {
91 return RegistryInfo{}, e
92 }
93 if e = state.create("credential.age", wrapped); e != nil {
94 return RegistryInfo{}, e
95 }
96 } else {
97 raw, e := state.read("credential.age", 128<<10)
98 if e != nil {
99 return RegistryInfo{}, e
100 }
101 id, key, e = unwrapCredential(raw, password)
102 if e != nil {
103 return RegistryInfo{}, e
104 }
105 }
106 floor, e := newNativeFloor(floorDir, id)
107 if e != nil {
108 return RegistryInfo{}, e
109 }
110 if present["initializing.json"] {
111 raw, e := state.read("initializing.json", 8192)
112 if e != nil {
113 return RegistryInfo{}, e
114 }
115 var signed floorEnvelope
116 if closedJSON(raw, &signed) != nil || signed.Record.Version != 1 || signed.Record.Identity != id || signed.Record.Point != (floorPoint{}) || signed.Record.Previous != "" {
117 return RegistryInfo{}, ErrRefused
118 }
119 body, e := canonicalJSON(signed.Record)
120 sig, ok := tokenBytes(signed.Signature, 64)
121 if e != nil || !ok || !family.Verify(floor.public, floorContext, body, sig) {
122 return RegistryInfo{}, ErrRefused
123 }
124 } else {
125 if present["registry.db"] {
126 return RegistryInfo{}, ErrRefused
127 }
128 raw, e := floor.signed(key, floorPoint{}, "")
129 if e != nil {
130 return RegistryInfo{}, e
131 }
132 if e = state.create("initializing.json", raw); e != nil {
133 return RegistryInfo{}, e
134 }
135 }
136 hasFloor := false
137 for _, name := range floorNames {
138 if name != "lock" {
139 hasFloor = true
140 }
141 }
142 if !hasFloor {
143 if present["registry.db"] {
144 return RegistryInfo{}, ErrRefused
145 }
146 if e = floor.initialize(key); e != nil {
147 return RegistryInfo{}, e
148 }
149 } else {
150 if _, _, e = floor.current(); e != nil {
151 return RegistryInfo{}, e
152 }
153 }
154 j, e := openJournalWithLocks(state, floor, key, true, []*os.File{stateLock, floorLock}, true)
155 if e != nil {
156 return RegistryInfo{}, e
157 }
158 defer j.close()
159 point, _, e := floor.current()
160 if e != nil {
161 return RegistryInfo{}, e
162 }
163 return RegistryInfo{id.Registry, id.Device, id.Public, point.Sequence}, nil
164}
165