github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/auth/credential_authority_test.go (about) 1 package auth 2 3 import ( 4 "crypto/rand" 5 "encoding/binary" 6 "fmt" 7 libkb "github.com/keybase/client/go/libkb" 8 "github.com/keybase/client/go/logger" 9 keybase1 "github.com/keybase/client/go/protocol/keybase1" 10 context "golang.org/x/net/context" 11 "sync" 12 "testing" 13 "time" 14 ) 15 16 type testUser struct { 17 uid keybase1.UID 18 username libkb.NormalizedUsername 19 sibkeys []keybase1.KID 20 subkeys []keybase1.KID 21 } 22 23 type testState struct { 24 sync.Mutex 25 26 users map[keybase1.UID](*testUser) 27 changes []keybase1.UID 28 now time.Time 29 evictCh chan keybase1.UID 30 pokeCh chan struct{} 31 numGets int 32 } 33 34 var seq uint32 35 36 func genKID() keybase1.KID { 37 var kid [35]byte 38 kid[0] = 0x1 39 kid[1] = 0x20 40 binary.BigEndian.PutUint32(kid[30:34], seq) 41 seq++ 42 kid[34] = 0xa0 43 return keybase1.KIDFromSlice(kid[:]) 44 } 45 46 func genUsername() string { 47 w, _ := libkb.SecWordList(1) 48 var buf [4]byte 49 _, _ = rand.Read(buf[:]) 50 return fmt.Sprintf("%s%x", w[0], buf) 51 } 52 53 func newTestUser(nKeys int) *testUser { 54 un := genUsername() 55 ret := testUser{ 56 username: libkb.NewNormalizedUsername(un), 57 uid: libkb.UsernameToUID(un), 58 sibkeys: make([]keybase1.KID, nKeys), 59 subkeys: make([]keybase1.KID, nKeys), 60 } 61 for i := 0; i < nKeys; i++ { 62 ret.sibkeys[i] = genKID() 63 ret.subkeys[i] = genKID() 64 } 65 return &ret 66 } 67 68 func (ts *testState) newTestUser(nKeys int) *testUser { 69 ts.Lock() 70 defer ts.Unlock() 71 ret := newTestUser(nKeys) 72 ts.users[ret.uid] = ret 73 return ret 74 } 75 76 func (ts *testState) mutateUser(uid keybase1.UID, mutator func(u *testUser)) bool { 77 ts.Lock() 78 defer ts.Unlock() 79 u := ts.users[uid] 80 if u == nil { 81 return false 82 } 83 mutator(u) 84 ts.changes = append(ts.changes, uid) 85 return true 86 } 87 88 func newTestState() *testState { 89 return &testState{ 90 users: make(map[keybase1.UID](*testUser)), 91 now: time.Unix(100, 0), 92 evictCh: make(chan keybase1.UID, 1), 93 pokeCh: make(chan struct{}), 94 } 95 } 96 97 type userNotFoundError struct { 98 } 99 100 func (e userNotFoundError) Error() string { 101 return "user not found" 102 } 103 104 func (ts *testState) GetUser(_ context.Context, uid keybase1.UID) ( 105 un libkb.NormalizedUsername, sibkeys, subkeys []keybase1.KID, isDeleted bool, err error) { 106 ts.Lock() 107 defer ts.Unlock() 108 u := ts.users[uid] 109 if u == nil { 110 return libkb.NormalizedUsername(""), nil, nil, false, userNotFoundError{} 111 } 112 ts.numGets++ 113 return u.username, u.sibkeys, u.subkeys, false, nil 114 } 115 116 func (ts *testState) PollForChanges(_ context.Context) ([]keybase1.UID, error) { 117 ts.Lock() 118 defer ts.Unlock() 119 ret := ts.changes 120 ts.changes = nil 121 return ret, nil 122 } 123 124 var _ UserKeyAPIer = (*testState)(nil) 125 var _ engine = (*testState)(nil) 126 127 func (ts *testState) tick(d time.Duration) { 128 ts.pokeCh <- struct{}{} 129 ts.Lock() 130 ts.now = ts.now.Add(d) 131 ts.Unlock() 132 ts.pokeCh <- struct{}{} 133 } 134 135 func (ts *testState) Now() time.Time { 136 ts.Lock() 137 ret := ts.now 138 ts.Unlock() 139 return ret 140 } 141 142 func (ts *testState) GetPokeCh() <-chan struct{} { return ts.pokeCh } 143 144 func (ts *testState) Evicted(uid keybase1.UID) { 145 ts.evictCh <- uid 146 } 147 148 func newTestSetup() (*testState, *CredentialAuthority) { 149 s := newTestState() 150 c := newCredentialAuthorityWithEngine(logger.New("test"), s, s) 151 return s, c 152 } 153 154 func TestSimple(t *testing.T) { 155 state, credentialAuthority := newTestSetup() 156 u0 := state.newTestUser(4) 157 158 key0 := u0.sibkeys[0] 159 key1 := u0.sibkeys[1] 160 161 if state.numGets != 0 { 162 t.Fatal("expected 0 gets") 163 } 164 165 err := credentialAuthority.CheckUserKey(context.TODO(), u0.uid, &u0.username, &key0, false) 166 if err != nil { 167 t.Fatal(err) 168 } 169 if state.numGets != 1 { 170 t.Fatal("expected 1 get") 171 } 172 err = credentialAuthority.CheckUserKey(context.TODO(), u0.uid, &u0.username, &key0, false) 173 if err != nil { 174 t.Fatal(err) 175 } 176 if state.numGets != 1 { 177 t.Fatal("expected 1 get") 178 } 179 180 state.mutateUser(u0.uid, func(u *testUser) { 181 u.sibkeys = u.sibkeys[1:] 182 }) 183 184 // Advance just an iota, so that our polling of the server 185 // has a chance to complete. 186 state.tick(pollWait) 187 188 // wait for the first eviction 189 uid := <-state.evictCh 190 if uid != u0.uid { 191 t.Fatalf("Wrong UID on eviction: %s != %s\n", uid, u0.uid) 192 } 193 194 err = credentialAuthority.CheckUserKey(context.TODO(), u0.uid, &u0.username, &key0, false) 195 if err == nil { 196 t.Fatal("Expected an error") 197 } 198 bke, ok := err.(BadKeyError) 199 switch { 200 case !ok: 201 t.Fatal("Expected a bad key error") 202 case bke.uid != u0.uid: 203 t.Fatalf("Expected a bad key error on %s (not %s)", u0.uid, bke.uid) 204 case bke.kid != key0: 205 t.Fatalf("Expected a bad key error on key %s (not %s)", key0, bke.kid) 206 } 207 208 err = credentialAuthority.CheckUserKey(context.TODO(), u0.uid, &u0.username, &key1, false) 209 if err != nil { 210 t.Fatal(err) 211 } 212 if state.numGets != 2 { 213 t.Fatal("expected 2 gets") 214 } 215 state.tick(userTimeout + time.Millisecond) 216 err = credentialAuthority.CheckUserKey(context.TODO(), u0.uid, &u0.username, &key1, false) 217 if err != nil { 218 t.Fatal(err) 219 } 220 if state.numGets != 3 { 221 t.Fatal("expected 3 gets") 222 } 223 state.tick(cacheTimeout + time.Millisecond) 224 225 // u0 should now be gone since we haven't touched him in over cacheTimeout 226 // duration. 227 uid = <-state.evictCh 228 if uid != u0.uid { 229 t.Fatalf("Wrong UID on eviction: %s != %s\n", uid, u0.uid) 230 } 231 232 // Make a new user -- u1! 233 u1 := state.newTestUser(4) 234 235 ng := 3 236 for i := 0; i < 10; i++ { 237 err = credentialAuthority.CheckUserKey(context.TODO(), u1.uid, &u1.username, &u1.sibkeys[0], false) 238 if err != nil { 239 t.Fatal(err) 240 } 241 ng++ 242 if state.numGets != ng { 243 t.Fatalf("expected %d gets, got %d", ng, state.numGets) 244 } 245 state.tick(userTimeout + time.Millisecond) 246 247 select { 248 case uid = <-state.evictCh: 249 t.Fatalf("Got unwanted eviction for %s", uid) 250 default: 251 } 252 } 253 254 state.tick(cacheTimeout - userTimeout + 3*time.Millisecond) 255 uid = <-state.evictCh 256 if uid != u1.uid { 257 t.Fatalf("Got wrong eviction: wanted %s but got %s\n", u1.uid, uid) 258 } 259 260 // Make a new user -- u2! 261 u2 := state.newTestUser(4) 262 err = credentialAuthority.CheckUserKey(context.TODO(), u2.uid, &u2.username, &u2.sibkeys[0], false) 263 if err != nil { 264 t.Fatal(err) 265 } 266 ng++ 267 if state.numGets != ng { 268 t.Fatalf("expected %d gets, got %d", ng, state.numGets) 269 } 270 271 // Check that u2 is evicted properly after we shutdown the CA. 272 credentialAuthority.Shutdown() 273 uid = <-state.evictCh 274 if uid != u2.uid { 275 t.Fatalf("Got wrong eviction: wanted %s but got %s\n", u2.uid, uid) 276 } 277 278 } 279 280 func TestCheckUsers(t *testing.T) { 281 state, credentialAuthority := newTestSetup() 282 283 var users, usersWithDud []keybase1.UID 284 for i := 0; i < 10; i++ { 285 u := state.newTestUser(2) 286 users = append(users, u.uid) 287 usersWithDud = append(usersWithDud, u.uid) 288 } 289 usersWithDud = append(usersWithDud, libkb.UsernameToUID(genUsername())) 290 291 if state.numGets != 0 { 292 t.Fatal("expected 0 gets") 293 } 294 295 err := credentialAuthority.CheckUsers(context.TODO(), users) 296 if err != nil { 297 t.Fatal(err) 298 } 299 if state.numGets != 10 { 300 t.Fatal("expected 10 gets") 301 } 302 err = credentialAuthority.CheckUsers(context.TODO(), users) 303 if err != nil { 304 t.Fatal(err) 305 } 306 if state.numGets != 10 { 307 t.Fatal("expected 10 gets") 308 } 309 310 err = credentialAuthority.CheckUsers(context.TODO(), usersWithDud) 311 if err == nil { 312 t.Fatal("Expected an error") 313 } else if _, ok := err.(userNotFoundError); !ok { 314 t.Fatal("Expected a user not found error") 315 } 316 credentialAuthority.Shutdown() 317 } 318 319 func TestCompareKeys(t *testing.T) { 320 state, credentialAuthority := newTestSetup() 321 u := state.newTestUser(10) 322 323 err := credentialAuthority.CompareUserKeys(context.TODO(), u.uid, u.sibkeys, u.subkeys) 324 if err != nil { 325 t.Fatal(err) 326 } 327 328 err = credentialAuthority.CompareUserKeys(context.TODO(), u.uid, nil, u.subkeys) 329 if err != nil { 330 t.Fatal(err) 331 } 332 333 err = credentialAuthority.CompareUserKeys(context.TODO(), u.uid, u.sibkeys, nil) 334 if err != nil { 335 t.Fatal(err) 336 } 337 338 missingSibkey := u.sibkeys[1:] 339 err = credentialAuthority.CompareUserKeys(context.TODO(), u.uid, missingSibkey, u.subkeys) 340 if err != ErrKeysNotEqual { 341 t.Fatal("Expected an ErrKeysNotEqual") 342 } 343 344 missingSubkey := u.subkeys[1:] 345 err = credentialAuthority.CompareUserKeys(context.TODO(), u.uid, u.sibkeys, missingSubkey) 346 if err != ErrKeysNotEqual { 347 t.Fatal("Expected an ErrKeysNotEqual") 348 } 349 credentialAuthority.Shutdown() 350 }