github.com/code-to-go/safepool.lib@v0.0.0-20221205180519-ee25e63c226e/pool/access.go (about) 1 package pool 2 3 import ( 4 "bytes" 5 "crypto/aes" 6 "hash" 7 "path" 8 "time" 9 10 "github.com/code-to-go/safepool.lib/core" 11 "github.com/code-to-go/safepool.lib/security" 12 "github.com/code-to-go/safepool.lib/transport" 13 14 "github.com/godruoyi/go-snowflake" 15 ) 16 17 type Grant struct { 18 Identity security.Identity 19 Since uint64 20 KeystoreKey []byte 21 } 22 23 type AccessFile struct { 24 Version float32 25 Grants []Grant 26 Nonce []byte 27 MasterKeyId uint64 28 Keystore []byte 29 } 30 31 func (p *Pool) ImportAccess(e transport.Exchanger) (hash.Hash, error) { 32 l, err := p.lockAccessFile(e) 33 if core.IsErr(err, "cannot lock access on %s: %v", p.e) { 34 return nil, err 35 } 36 defer p.unlockAccessFile(e, l) 37 38 a, h, err := p.readAccessFile(e) 39 if core.IsErr(err, "cannot read access file:%v") { 40 return nil, err 41 } 42 if bytes.Equal(h.Sum(nil), p.accessHash) { 43 return h, nil 44 } 45 46 if p.masterKeyId != a.MasterKeyId { 47 err = p.importMasterKey(a) 48 if core.IsErr(err, "cannot import master key: %v") { 49 return nil, err 50 } 51 } 52 53 ks, err := p.importKeystore(a) 54 if core.IsErr(err, "cannot sync grants: %v") { 55 return nil, err 56 } 57 58 err = p.importGrants(a, ks) 59 if core.IsErr(err, "cannot sync grants: %v") { 60 return nil, err 61 } 62 63 p.accessHash = h.Sum(nil) 64 return h, nil 65 } 66 67 func (p *Pool) ExportAccessFile(e transport.Exchanger) error { 68 identities, err := p.sqlGetIdentities(false) 69 if core.IsErr(err, "cannot get identities for pool '%s':%v", p.Name) { 70 return err 71 } 72 73 var grants []Grant 74 for _, i := range identities { 75 keystoreKey, err := security.EcEncrypt(i.Identity, p.masterKey) 76 if core.IsErr(err, "cannot encrypt master key for identity %s: %v", i.Nick) { 77 return err 78 } 79 grants = append(grants, Grant{ 80 Identity: i.Public(), 81 Since: p.masterKeyId, 82 KeystoreKey: keystoreKey, 83 }) 84 } 85 86 ks, err := p.sqlGetKeystore() 87 if core.IsErr(err, "cannot read keystore from db for pool '%s': %v", p.Name) { 88 return err 89 } 90 91 noonce := security.GenerateBytesKey(aes.BlockSize) 92 cipherks, err := p.marshalKeystore(p.masterKey, noonce, ks) 93 if core.IsErr(err, "cannot marshal keystore for pool '%s': %v", p.Name) { 94 return err 95 } 96 97 a := AccessFile{ 98 Version: 0.0, 99 Nonce: noonce, 100 MasterKeyId: p.masterKeyId, 101 Grants: grants, 102 Keystore: cipherks, 103 } 104 105 l, err := p.lockAccessFile(p.e) 106 if core.IsErr(err, "cannot lock access on %s: %v", p.e) { 107 return err 108 } 109 defer p.unlockAccessFile(e, l) 110 _, err = p.writeAccessFile(e, a) 111 return err 112 } 113 114 func (p *Pool) importMasterKey(a AccessFile) error { 115 for _, g := range a.Grants { 116 if security.SameIdentity(p.Self, g.Identity) { 117 masterKey, err := security.EcDecrypt(p.Self, g.KeystoreKey) 118 if !core.IsErr(err, "corrupted master key in access grant: %v", err) { 119 err = p.sqlSetKey(a.MasterKeyId, masterKey) 120 if core.IsErr(err, "cannot write master key to db: %v", err) { 121 return err 122 } 123 } 124 } 125 } 126 p.masterKeyId = a.MasterKeyId 127 return nil 128 } 129 130 func (p *Pool) importGrants(a AccessFile, ks Keystore) error { 131 identities, err := p.sqlGetIdentities(false) 132 if core.IsErr(err, "cannot read identities during grant import: %v", err) { 133 return err 134 } 135 is := map[string]Identity{} 136 for _, i := range identities { 137 k := string(append(i.SignatureKey.Public, i.EncryptionKey.Public...)) 138 is[k] = i 139 } 140 141 for _, g := range a.Grants { 142 i := g.Identity 143 k := string(append(i.SignatureKey.Public, i.EncryptionKey.Public...)) 144 if _, found := is[k]; !found { 145 err := security.SetIdentity(i) 146 if !core.IsErr(err, "cannot add identity %s: %v", g.Identity.Nick) { 147 p.sqlSetIdentity(i, g.Since) 148 } 149 delete(is, k) 150 } 151 } 152 153 needNewMasterKey := false 154 for _, i := range is { 155 if _, ok := ks[i.Since]; ok { 156 p.sqlDeleteIdentity(i) 157 needNewMasterKey = true 158 } 159 } 160 if needNewMasterKey { 161 p.masterKeyId = snowflake.ID() 162 p.masterKey = security.GenerateBytesKey(32) 163 err = p.sqlSetKey(p.masterKeyId, p.masterKey) 164 if core.IsErr(err, "çannot store master encryption key to db: %v") { 165 return err 166 } 167 } 168 169 return nil 170 } 171 172 func (p *Pool) lockAccessFile(e transport.Exchanger) (uint64, error) { 173 lockFile := path.Join(p.Name, ".access.lock") 174 lockId, err := transport.LockFile(e, lockFile, time.Minute) 175 core.IsErr(err, "cannot lock access on %s: %v", p.Name, err) 176 return lockId, err 177 } 178 179 func (p *Pool) unlockAccessFile(e transport.Exchanger, lockId uint64) { 180 lockFile := path.Join(p.Name, ".access.lock") 181 transport.UnlockFile(e, lockFile, lockId) 182 } 183 184 func (p *Pool) readAccessFile(e transport.Exchanger) (AccessFile, hash.Hash, error) { 185 var a AccessFile 186 var sh security.SignedHash 187 signatureFile := path.Join(p.Name, ".access.sign") 188 accessFile := path.Join(p.Name, ".access") 189 190 err := transport.ReadJSON(e, signatureFile, &sh, nil) 191 if core.IsErr(err, "cannot read signature file '%s': %v", signatureFile, err) { 192 return AccessFile{}, nil, err 193 } 194 195 h := security.NewHash() 196 err = transport.ReadJSON(e, accessFile, &a, h) 197 if core.IsErr(err, "cannot read access file: %s", err) { 198 return AccessFile{}, nil, err 199 } 200 201 if security.VerifySignedHash(sh, []security.Identity{p.Self}, h.Sum(nil)) { 202 return a, h, nil 203 } 204 205 trusted, err := p.sqlGetIdentities(true) 206 if core.IsErr(err, "cannot get trusted identities: %v") { 207 return AccessFile{}, nil, nil 208 } 209 210 var is []security.Identity 211 for _, t := range trusted { 212 is = append(is, t.Identity) 213 } 214 215 if !security.VerifySignedHash(sh, is, h.Sum(nil)) { 216 return AccessFile{}, nil, ErrNotTrusted 217 } 218 219 _ = security.AppendToSignedHash(sh, p.Self) 220 if !core.IsErr(err, "cannot lock access on %s: %v", p.Name, err) { 221 if security.AppendToSignedHash(sh, p.Self) == nil { 222 err = transport.WriteJSON(e, signatureFile, sh, nil) 223 core.IsErr(err, "cannot write signature file on %s: %v", p.Name, err) 224 } 225 } 226 227 return a, h, nil 228 } 229 230 func (p *Pool) writeAccessFile(e transport.Exchanger, a AccessFile) (hash.Hash, error) { 231 lockFile := path.Join(p.Name, ".access.lock") 232 signatureFile := path.Join(p.Name, ".access.sign") 233 accessFile := path.Join(p.Name, ".access") 234 235 lockId, err := transport.LockFile(e, lockFile, time.Minute) 236 if core.IsErr(err, "cannot lock access on %s: %v", p.Name, err) { 237 return nil, err 238 } 239 defer transport.UnlockFile(e, lockFile, lockId) 240 241 h := security.NewHash() 242 err = transport.WriteJSON(e, accessFile, a, h) 243 if core.IsErr(err, "cannot write access file on %s: %v", p.Name, err) { 244 return nil, err 245 } 246 247 sh, err := security.NewSignedHash(h.Sum(nil), p.Self) 248 if core.IsErr(err, "cannot generate signature hash on %s: %v", p.Name, err) { 249 return nil, err 250 } 251 err = transport.WriteJSON(e, signatureFile, sh, nil) 252 if core.IsErr(err, "cannot write signature file on %s: %v", p.Name, err) { 253 return nil, err 254 } 255 256 return h, nil 257 }