github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/chat/boxer_test.go (about) 1 package chat 2 3 // Copyright 2016 Keybase, Inc. All rights reserved. Use of 4 // this source code is governed by the included BSD license. 5 6 import ( 7 "crypto/sha256" 8 "encoding/hex" 9 "testing" 10 "time" 11 12 "github.com/stretchr/testify/require" 13 "golang.org/x/net/context" 14 15 "github.com/keybase/client/go/chat/globals" 16 "github.com/keybase/client/go/chat/signencrypt" 17 "github.com/keybase/client/go/chat/types" 18 "github.com/keybase/client/go/engine" 19 "github.com/keybase/client/go/ephemeral" 20 "github.com/keybase/client/go/externalstest" 21 "github.com/keybase/client/go/kbtest" 22 "github.com/keybase/client/go/libkb" 23 "github.com/keybase/client/go/protocol/chat1" 24 "github.com/keybase/client/go/protocol/gregor1" 25 "github.com/keybase/client/go/protocol/keybase1" 26 "github.com/keybase/go-codec/codec" 27 insecureTriplesec "github.com/keybase/go-triplesec-insecure" 28 ) 29 30 var mockTLFID = chat1.TLFID([]byte{0, 1}) 31 32 func cryptKey(t *testing.T) *keybase1.CryptKey { 33 kp, err := libkb.GenerateNaclDHKeyPair() 34 require.NoError(t, err) 35 36 return &keybase1.CryptKey{ 37 KeyGeneration: 1, 38 Key: keybase1.Bytes32(*kp.Private), 39 } 40 } 41 42 func textMsg(t *testing.T, text string, mbVersion chat1.MessageBoxedVersion) chat1.MessagePlaintext { 43 uid, err := libkb.RandBytes(16) 44 require.NoError(t, err) 45 uid[15] = keybase1.UID_SUFFIX_2 46 return textMsgWithSender(t, text, gregor1.UID(uid), mbVersion) 47 } 48 49 func textMsgWithSender(t *testing.T, text string, uid gregor1.UID, mbVersion chat1.MessageBoxedVersion) chat1.MessagePlaintext { 50 header := chat1.MessageClientHeader{ 51 Conv: chat1.ConversationIDTriple{ 52 Tlfid: mockTLFID, 53 }, 54 Sender: uid, 55 MessageType: chat1.MessageType_TEXT, 56 } 57 switch mbVersion { 58 case chat1.MessageBoxedVersion_V1: 59 // no-op 60 case chat1.MessageBoxedVersion_V2, chat1.MessageBoxedVersion_V3, chat1.MessageBoxedVersion_V4: 61 header.MerkleRoot = &chat1.MerkleRoot{ 62 Seqno: 12, 63 Hash: []byte{123, 117, 0, 99, 99, 79, 223, 37, 180, 168, 111, 107, 210, 227, 128, 35, 47, 158, 221, 210, 151, 242, 182, 199, 50, 29, 236, 93, 106, 149, 133, 221, 156, 216, 167, 79, 91, 28, 9, 196, 107, 173, 61, 248, 123, 97, 101, 34, 7, 15, 30, 80, 246, 162, 198, 12, 20, 19, 130, 151, 45, 2, 130, 170}, 64 } 65 default: 66 panic("unrecognized version: " + mbVersion.String()) 67 } 68 return textMsgWithHeader(t, text, header) 69 } 70 71 func textMsgWithHeader(t *testing.T, text string, header chat1.MessageClientHeader) chat1.MessagePlaintext { 72 return chat1.MessagePlaintext{ 73 ClientHeader: header, 74 MessageBody: chat1.NewMessageBodyWithText(chat1.MessageText{Body: text}), 75 } 76 } 77 78 func setupChatTest(t *testing.T, name string) (*kbtest.ChatTestContext, *Boxer) { 79 tc := externalstest.SetupTest(t, name, 2) 80 81 // use an insecure triplesec in tests 82 tc.G.NewTriplesec = func(passphrase []byte, salt []byte) (libkb.Triplesec, error) { 83 warner := func() { tc.G.Log.Warning("Installing insecure Triplesec with weak stretch parameters") } 84 isProduction := func() bool { 85 return tc.G.Env.GetRunMode() == libkb.ProductionRunMode 86 } 87 return insecureTriplesec.NewCipher(passphrase, salt, libkb.ClientTriplesecVersion, warner, isProduction) 88 } 89 90 ctc := kbtest.ChatTestContext{ 91 TestContext: tc, 92 ChatG: &globals.ChatContext{ 93 EmojiSource: types.DummyEmojiSource{}, 94 }, 95 } 96 g := globals.NewContext(ctc.G, ctc.ChatG) 97 g.CtxFactory = NewCtxFactory(g) 98 99 return &ctc, NewBoxer(ctc.Context()) 100 } 101 102 func getSigningKeyPairForTest(t *testing.T, tc *kbtest.ChatTestContext, u *kbtest.FakeUser) libkb.NaclSigningKeyPair { 103 var err error 104 if u == nil { 105 u, err = kbtest.CreateAndSignupFakeUser("unbox", tc.G) 106 require.NoError(t, err) 107 } 108 kp, err := tc.G.Keyrings.GetSecretKeyWithPassphrase(kbtest.NewMetaContextForTest(*tc), u.User, u.Passphrase, nil) 109 require.NoError(t, err) 110 signKP, ok := kp.(libkb.NaclSigningKeyPair) 111 require.True(t, ok) 112 return signKP 113 } 114 115 func getActiveDevicesAndKeys(tc *kbtest.ChatTestContext, u *kbtest.FakeUser) ([]*libkb.Device, []libkb.GenericKey) { 116 arg := libkb.NewLoadUserByNameArg(tc.G, u.Username).WithPublicKeyOptional() 117 user, err := libkb.LoadUser(arg) 118 if err != nil { 119 tc.T.Fatal(err) 120 } 121 sibkeys := user.GetComputedKeyFamily().GetAllActiveSibkeys() 122 subkeys := user.GetComputedKeyFamily().GetAllActiveSubkeys() 123 124 activeDevices := []*libkb.Device{} 125 for _, device := range user.GetComputedKeyFamily().GetAllDevices() { 126 if device.Status != nil && *device.Status == libkb.DeviceStatusActive { 127 activeDevices = append(activeDevices, device.Device) 128 } 129 } 130 return activeDevices, append(sibkeys, subkeys...) 131 } 132 133 func doRevokeDevice(tc *kbtest.ChatTestContext, u *kbtest.FakeUser, id keybase1.DeviceID, force bool) error { 134 revokeEngine := engine.NewRevokeDeviceEngine(tc.G, engine.RevokeDeviceEngineArgs{ID: id, ForceSelf: force}) 135 uis := libkb.UIs{ 136 LogUI: tc.G.UI.GetLogUI(), 137 SecretUI: u.NewSecretUI(), 138 } 139 m := libkb.NewMetaContextTODO(tc.G).WithUIs(uis) 140 err := engine.RunEngine2(m, revokeEngine) 141 return err 142 } 143 144 func doWithMBVersions(f func(chat1.MessageBoxedVersion)) { 145 f(chat1.MessageBoxedVersion_V1) 146 f(chat1.MessageBoxedVersion_V2) 147 } 148 149 func TestChatMessageBox(t *testing.T) { 150 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 151 key := cryptKey(t) 152 msg := textMsg(t, "hello", mbVersion) 153 tc, boxer := setupChatTest(t, "box") 154 defer tc.Cleanup() 155 boxed, err := boxer.box(context.TODO(), msg, key, nil, getSigningKeyPairForTest(t, tc, nil), mbVersion, nil) 156 require.NoError(t, err) 157 158 require.Equal(t, mbVersion, boxed.Version) 159 if len(boxed.BodyCiphertext.E) == 0 { 160 t.Error("after boxMessage, BodyCipherText.E is empty") 161 } 162 }) 163 } 164 165 var convInfo = newBasicUnboxConversationInfo([]byte{0, 0, 1}, chat1.ConversationMembersType_KBFS, nil, 166 keybase1.TLFVisibility_PRIVATE) 167 168 func TestChatMessageUnbox(t *testing.T) { 169 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 170 key := cryptKey(t) 171 text := "hi" 172 tc, boxer := setupChatTest(t, "unbox") 173 defer tc.Cleanup() 174 175 // need a real user 176 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 177 require.NoError(t, err) 178 179 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 180 outboxID := chat1.OutboxID{0xdc, 0x74, 0x6, 0x5d, 0xf9, 0x5f, 0x1c, 0x48} 181 msg.ClientHeader.OutboxID = &outboxID 182 183 signKP := getSigningKeyPairForTest(t, tc, u) 184 185 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 186 require.NoError(t, err) 187 boxed = remarshalBoxed(t, boxed) 188 189 if boxed.ClientHeader.OutboxID == msg.ClientHeader.OutboxID { 190 t.Fatalf("defective test: %+v == %+v", boxed.ClientHeader.OutboxID, msg.ClientHeader.OutboxID) 191 } 192 193 // need to give it a server header... 194 boxed.ServerHeader = &chat1.MessageServerHeader{ 195 Ctime: gregor1.ToTime(time.Now()), 196 } 197 198 unboxed, err := boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 199 require.NoError(t, err) 200 body := unboxed.MessageBody 201 typ, err := body.MessageType() 202 require.NoError(t, err) 203 require.Equal(t, typ, chat1.MessageType_TEXT) 204 require.Equal(t, body.Text().Body, text) 205 require.Nil(t, unboxed.SenderDeviceRevokedAt, "message should not be from revoked device") 206 require.NotNil(t, unboxed.BodyHash) 207 }) 208 } 209 210 func TestChatMessageUnboxWithPairwiseMacs(t *testing.T) { 211 tc, boxer := setupChatTest(t, "unbox") 212 defer tc.Cleanup() 213 214 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 215 require.NoError(t, err) 216 217 text := "hi" 218 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), chat1.MessageBoxedVersion_V3) 219 outboxID := chat1.OutboxID{0xdc, 0x74, 0x6, 0x5d, 0xf9, 0x5f, 0x1c, 0x48} 220 msg.ClientHeader.OutboxID = &outboxID 221 // Pairwise MACs rely on the sender's DeviceID in the header. 222 deviceID := make([]byte, libkb.DeviceIDLen) 223 err = tc.G.ActiveDevice.DeviceID().ToBytes(deviceID) 224 require.NoError(t, err) 225 msg.ClientHeader.SenderDevice = gregor1.DeviceID(deviceID) 226 227 signKP := getSigningKeyPairForTest(t, tc, u) 228 229 encryptionKeypair, err := tc.G.ActiveDevice.NaclEncryptionKey() 230 require.NoError(t, err) 231 pairwiseMACRecipients := []keybase1.KID{encryptionKeypair.GetKID()} 232 233 key := cryptKey(t) 234 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, chat1.MessageBoxedVersion_V3, pairwiseMACRecipients) 235 require.NoError(t, err) 236 boxed = remarshalBoxed(t, boxed) 237 require.NotEqual(t, outboxID, boxed.ClientHeader.OutboxID) 238 239 // need to give it a server header... 240 boxed.ServerHeader = &chat1.MessageServerHeader{ 241 Ctime: gregor1.ToTime(time.Now()), 242 } 243 244 unboxed, err := boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 245 require.NoError(t, err) 246 247 require.True(t, unboxed.ClientHeader.HasPairwiseMacs) 248 body := unboxed.MessageBody 249 typ, err := body.MessageType() 250 require.NoError(t, err) 251 252 require.Equal(t, typ, chat1.MessageType_TEXT) 253 require.Equal(t, body.Text().Body, text) 254 255 require.Nil(t, unboxed.SenderDeviceRevokedAt, "message should not be from revoked device") 256 require.NotNil(t, unboxed.BodyHash) 257 } 258 259 func TestChatMessageUnboxWithPairwiseMacsCorrupted(t *testing.T) { 260 tc, boxer := setupChatTest(t, "unbox") 261 defer tc.Cleanup() 262 263 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 264 require.NoError(t, err) 265 266 text := "hi" 267 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), chat1.MessageBoxedVersion_V3) 268 outboxID := chat1.OutboxID{0xdc, 0x74, 0x6, 0x5d, 0xf9, 0x5f, 0x1c, 0x48} 269 msg.ClientHeader.OutboxID = &outboxID 270 // Pairwise MACs rely on the sender's DeviceID in the header. 271 deviceID := make([]byte, libkb.DeviceIDLen) 272 err = tc.G.ActiveDevice.DeviceID().ToBytes(deviceID) 273 require.NoError(t, err) 274 275 msg.ClientHeader.SenderDevice = gregor1.DeviceID(deviceID) 276 signKP := getSigningKeyPairForTest(t, tc, u) 277 278 encryptionKeypair, err := tc.G.ActiveDevice.NaclEncryptionKey() 279 require.NoError(t, err) 280 pairwiseMACRecipients := []keybase1.KID{encryptionKeypair.GetKID()} 281 282 key := cryptKey(t) 283 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, chat1.MessageBoxedVersion_V3, pairwiseMACRecipients) 284 require.NoError(t, err) 285 286 boxed = remarshalBoxed(t, boxed) 287 require.NotEqual(t, outboxID, boxed.ClientHeader.OutboxID) 288 289 // need to give it a server header... 290 boxed.ServerHeader = &chat1.MessageServerHeader{ 291 Ctime: gregor1.ToTime(time.Now()), 292 } 293 294 // CORRUPT THE AUTHENTICATOR!!! 295 for _, mac := range boxed.ClientHeader.PairwiseMacs { 296 mac[0] ^= 1 297 } 298 299 _, err = boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 300 require.Error(t, err) 301 302 // Check that we get the right failure type. 303 permErr, ok := err.(PermanentUnboxingError) 304 require.True(t, ok) 305 _, ok = permErr.Inner().(InvalidMACError) 306 require.True(t, ok) 307 } 308 309 func TestChatMessageUnboxWithPairwiseMacsMissing(t *testing.T) { 310 tc, boxer := setupChatTest(t, "unbox") 311 defer tc.Cleanup() 312 313 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 314 require.NoError(t, err) 315 316 text := "hi" 317 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), chat1.MessageBoxedVersion_V3) 318 outboxID := chat1.OutboxID{0xdc, 0x74, 0x6, 0x5d, 0xf9, 0x5f, 0x1c, 0x48} 319 msg.ClientHeader.OutboxID = &outboxID 320 321 // Pairwise MACs rely on the sender's DeviceID in the header. 322 deviceID := make([]byte, libkb.DeviceIDLen) 323 err = tc.G.ActiveDevice.DeviceID().ToBytes(deviceID) 324 require.NoError(t, err) 325 msg.ClientHeader.SenderDevice = gregor1.DeviceID(deviceID) 326 327 // Put a bogus key in the recipients list, instead of our own. 328 bogusKey, err := libkb.GenerateNaclDHKeyPair() 329 require.NoError(t, err) 330 pairwiseMACRecipients := []keybase1.KID{bogusKey.GetKID()} 331 332 key := cryptKey(t) 333 signKP := getSigningKeyPairForTest(t, tc, u) 334 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, chat1.MessageBoxedVersion_V3, pairwiseMACRecipients) 335 require.NoError(t, err) 336 boxed = remarshalBoxed(t, boxed) 337 require.NotEqual(t, outboxID, boxed.ClientHeader.OutboxID) 338 339 // need to give it a server header... 340 boxed.ServerHeader = &chat1.MessageServerHeader{ 341 Ctime: gregor1.ToTime(time.Now()), 342 } 343 344 _, err = boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 345 require.Error(t, err) 346 347 // Check that we get the right failure type. 348 permErr, ok := err.(PermanentUnboxingError) 349 require.True(t, ok) 350 _, ok = permErr.Inner().(NotAuthenticatedForThisDeviceError) 351 require.True(t, ok) 352 } 353 354 func TestChatMessageUnboxWithPairwiseMacsV4DummySigner(t *testing.T) { 355 tc, boxer := setupChatTest(t, "unbox") 356 defer tc.Cleanup() 357 358 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 359 require.NoError(t, err) 360 361 text := "hi" 362 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), chat1.MessageBoxedVersion_V4) 363 outboxID := chat1.OutboxID{0xdc, 0x74, 0x6, 0x5d, 0xf9, 0x5f, 0x1c, 0x48} 364 msg.ClientHeader.OutboxID = &outboxID 365 366 // Pairwise MACs rely on the sender's DeviceID in the header. 367 deviceID := make([]byte, libkb.DeviceIDLen) 368 err = tc.G.ActiveDevice.DeviceID().ToBytes(deviceID) 369 require.NoError(t, err) 370 msg.ClientHeader.SenderDevice = gregor1.DeviceID(deviceID) 371 372 // V4 messages require this keypair. 373 encryptionKeypair, err := tc.G.ActiveDevice.NaclEncryptionKey() 374 require.NoError(t, err) 375 pairwiseMACRecipients := []keybase1.KID{encryptionKeypair.GetKID()} 376 377 key := cryptKey(t) 378 signKP := dummySigningKey() 379 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, chat1.MessageBoxedVersion_V4, pairwiseMACRecipients) 380 require.NoError(t, err) 381 boxed = remarshalBoxed(t, boxed) 382 require.NotEqual(t, outboxID, boxed.ClientHeader.OutboxID) 383 384 // need to give it a server header... 385 boxed.ServerHeader = &chat1.MessageServerHeader{ 386 Ctime: gregor1.ToTime(time.Now()), 387 } 388 389 unboxed, err := boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 390 require.NoError(t, err) 391 392 require.True(t, unboxed.ClientHeader.HasPairwiseMacs) 393 body := unboxed.MessageBody 394 typ, err := body.MessageType() 395 require.NoError(t, err) 396 require.Equal(t, typ, chat1.MessageType_TEXT) 397 require.Equal(t, body.Text().Body, text) 398 require.Nil(t, unboxed.SenderDeviceRevokedAt, "message should not be from revoked device") 399 require.NotNil(t, unboxed.BodyHash) 400 } 401 402 func TestChatMessageMissingOutboxID(t *testing.T) { 403 // Test with an outbox ID missing. 404 mbVersion := chat1.MessageBoxedVersion_V2 405 key := cryptKey(t) 406 text := "hi" 407 tc, boxer := setupChatTest(t, "unbox") 408 defer tc.Cleanup() 409 410 // need a real user 411 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 412 require.NoError(t, err) 413 414 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 415 outboxID := chat1.OutboxID{0xdc, 0x74, 0x6, 0x5d, 0xf9, 0x5f, 0x1c, 0x48} 416 msg.ClientHeader.OutboxID = &outboxID 417 418 signKP := getSigningKeyPairForTest(t, tc, u) 419 420 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 421 require.NoError(t, err) 422 boxed = remarshalBoxed(t, boxed) 423 424 if boxed.ClientHeader.OutboxID == msg.ClientHeader.OutboxID { 425 t.Fatalf("defective test: %+v == %+v", boxed.ClientHeader.OutboxID, msg.ClientHeader.OutboxID) 426 } 427 // omit outbox id 428 boxed.ClientHeader.OutboxID = nil 429 430 // need to give it a server header... 431 boxed.ServerHeader = &chat1.MessageServerHeader{ 432 Ctime: gregor1.ToTime(time.Now()), 433 } 434 435 _, uberr := boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 436 require.Error(t, uberr) 437 ierr, ok := uberr.Inner().(HeaderMismatchError) 438 require.True(t, ok, "unexpected error: %T -> %T -> %v", uberr, uberr.Inner(), uberr) 439 require.Equal(t, "OutboxID", ierr.Field) 440 } 441 442 func TestChatMessageInvalidBodyHash(t *testing.T) { 443 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 444 key := cryptKey(t) 445 text := "hi" 446 tc, boxer := setupChatTest(t, "unbox") 447 defer tc.Cleanup() 448 449 // need a real user 450 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 451 require.NoError(t, err) 452 453 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 454 455 signKP := getSigningKeyPairForTest(t, tc, u) 456 457 origHashFn := boxer.hashV1 458 boxer.hashV1 = func(data []byte) chat1.Hash { 459 data = append(data, []byte{1, 2, 3}...) 460 sum := sha256.Sum256(data) 461 return sum[:] 462 } 463 464 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 465 require.NoError(t, err) 466 467 // need to give it a server header... 468 boxed.ServerHeader = &chat1.MessageServerHeader{ 469 Ctime: gregor1.ToTime(time.Now()), 470 } 471 472 // put original hash fn back 473 boxer.hashV1 = origHashFn 474 475 _, ierr := boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 476 _, ok := ierr.Inner().(BodyHashInvalid) 477 require.True(t, ok) 478 }) 479 } 480 481 func TestChatMessageMismatchMessageType(t *testing.T) { 482 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 483 tc, boxer := setupChatTest(t, "unbox") 484 defer tc.Cleanup() 485 486 world := NewChatMockWorld(t, "unbox", 4) 487 defer world.Cleanup() 488 u := world.GetUsers()[0] 489 tc = world.Tcs[u.Username] 490 uid := u.User.GetUID().ToBytes() 491 tlf := kbtest.NewTlfMock(world) 492 ctx := newTestContextWithTlfMock(tc, tlf) 493 494 ni, err := tlf.LookupID(ctx, u.Username, false) 495 require.NoError(t, err) 496 header := chat1.MessageClientHeader{ 497 Conv: chat1.ConversationIDTriple{ 498 Tlfid: ni.ID, 499 }, 500 Sender: uid, 501 TlfPublic: false, 502 TlfName: u.Username, 503 MessageType: chat1.MessageType_TEXT, 504 } 505 msg := textMsgWithHeader(t, "MIKE", header) 506 msg.MessageBody = chat1.NewMessageBodyWithEdit(chat1.MessageEdit{}) 507 508 signKP := getSigningKeyPairForTest(t, tc, u) 509 boxer.boxVersionForTesting = &mbVersion 510 boxed, err := boxer.BoxMessage(ctx, msg, chat1.ConversationMembersType_KBFS, signKP, nil) 511 require.NoError(t, err) 512 boxed.ServerHeader = &chat1.MessageServerHeader{ 513 Ctime: gregor1.ToTime(time.Now()), 514 } 515 516 convID := header.Conv.ToConversationID([2]byte{0, 0}) 517 conv := chat1.Conversation{ 518 Metadata: chat1.ConversationMetadata{ 519 ConversationID: convID, 520 }, 521 } 522 decmsg, err := boxer.UnboxMessage(ctx, boxed, conv, nil) 523 require.NoError(t, err) 524 require.False(t, decmsg.IsValid()) 525 }) 526 } 527 528 func TestChatMessageUnboxInvalidBodyHash(t *testing.T) { 529 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 530 tc, boxer := setupChatTest(t, "unbox") 531 defer tc.Cleanup() 532 533 world := NewChatMockWorld(t, "unbox", 4) 534 defer world.Cleanup() 535 u := world.GetUsers()[0] 536 tc = world.Tcs[u.Username] 537 uid := u.User.GetUID().ToBytes() 538 tlf := kbtest.NewTlfMock(world) 539 ctx := newTestContextWithTlfMock(tc, tlf) 540 541 ni, err := tlf.LookupID(ctx, u.Username, false) 542 require.NoError(t, err) 543 header := chat1.MessageClientHeader{ 544 Conv: chat1.ConversationIDTriple{ 545 Tlfid: ni.ID, 546 }, 547 Sender: uid, 548 TlfPublic: false, 549 TlfName: u.Username, 550 } 551 text := "hi" 552 msg := textMsgWithHeader(t, text, header) 553 554 signKP := getSigningKeyPairForTest(t, tc, u) 555 556 origHashFn := boxer.hashV1 557 boxer.hashV1 = func(data []byte) chat1.Hash { 558 data = append(data, []byte{1, 2, 3}...) 559 sum := sha256.Sum256(data) 560 return sum[:] 561 } 562 563 boxer.boxVersionForTesting = &mbVersion 564 boxed, err := boxer.BoxMessage(ctx, msg, chat1.ConversationMembersType_KBFS, signKP, nil) 565 require.NoError(t, err) 566 567 // need to give it a server header... 568 boxed.ServerHeader = &chat1.MessageServerHeader{ 569 Ctime: gregor1.ToTime(time.Now()), 570 } 571 572 // put original hash fn back 573 boxer.hashV1 = origHashFn 574 575 // NOTE: this is hashing a lot of zero values, and it might break when we add more checks 576 convID := header.Conv.ToConversationID([2]byte{0, 0}) 577 conv := chat1.Conversation{ 578 Metadata: chat1.ConversationMetadata{ 579 ConversationID: convID, 580 }, 581 } 582 583 // This should produce a permanent error. So err will be nil, but the decmsg will be state=error. 584 decmsg, err := boxer.UnboxMessage(ctx, boxed, conv, nil) 585 require.NoError(t, err) 586 require.False(t, decmsg.IsValid()) 587 }) 588 } 589 590 func TestChatMessageUnboxNoCryptKey(t *testing.T) { 591 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 592 tc, boxer := setupChatTest(t, "unbox") 593 defer tc.Cleanup() 594 595 world := NewChatMockWorld(t, "unbox", 4) 596 defer world.Cleanup() 597 u := world.GetUsers()[0] 598 uid := u.User.GetUID().ToBytes() 599 tc = world.Tcs[u.Username] 600 tlf := kbtest.NewTlfMock(world) 601 ctx := newTestContextWithTlfMock(tc, tlf) 602 603 header := chat1.MessageClientHeader{ 604 Sender: uid, 605 TlfPublic: false, 606 TlfName: u.Username + "M", 607 } 608 text := "hi" 609 msg := textMsgWithHeader(t, text, header) 610 611 signKP := getSigningKeyPairForTest(t, tc, u) 612 613 boxer.boxVersionForTesting = &mbVersion 614 _, err := boxer.BoxMessage(ctx, msg, chat1.ConversationMembersType_KBFS, signKP, nil) 615 require.Error(t, err) 616 }) 617 } 618 619 func TestChatMessageInvalidHeaderSig(t *testing.T) { 620 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 621 key := cryptKey(t) 622 text := "hi" 623 tc, boxer := setupChatTest(t, "unbox") 624 defer tc.Cleanup() 625 626 // need a real user 627 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 628 require.NoError(t, err) 629 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 630 631 signKP := getSigningKeyPairForTest(t, tc, u) 632 633 // flip a bit in the sig 634 called := false 635 boxer.testingSignatureMangle = func(sig []byte) []byte { 636 called = true 637 sig[4] ^= 0x10 638 return sig 639 } 640 641 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 642 require.NoError(t, err) 643 644 require.True(t, called, "mangle must be called") 645 646 // need to give it a server header... 647 boxed.ServerHeader = &chat1.MessageServerHeader{ 648 Ctime: gregor1.ToTime(time.Now()), 649 } 650 651 _, ierr := boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 652 require.NotNil(t, ierr, "must have unbox error") 653 switch mbVersion { 654 case chat1.MessageBoxedVersion_V1: 655 _, ok := ierr.Inner().(libkb.BadSigError) 656 require.True(t, ok) 657 case chat1.MessageBoxedVersion_V2: 658 _, ok := ierr.Inner().(signencrypt.Error) 659 require.True(t, ok) 660 default: 661 require.Fail(t, "unexpected version: %v", mbVersion) 662 } 663 }) 664 } 665 666 func TestChatMessageInvalidSenderKey(t *testing.T) { 667 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 668 key := cryptKey(t) 669 text := "hi" 670 tc, boxer := setupChatTest(t, "unbox") 671 defer tc.Cleanup() 672 673 // need a real user 674 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 675 require.NoError(t, err) 676 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 677 678 // use a random signing key, not one of u's keys 679 signKP, err := libkb.GenerateNaclSigningKeyPair() 680 require.NoError(t, err) 681 682 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 683 require.NoError(t, err) 684 685 boxed.ServerHeader = &chat1.MessageServerHeader{ 686 Ctime: gregor1.ToTime(time.Now()), 687 } 688 689 _, ierr := boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 690 if ierr != nil { 691 _, ok := ierr.Inner().(libkb.NoKeyError) 692 require.True(t, ok) 693 } 694 }) 695 } 696 697 // Sent with a revoked sender key after revocation 698 func TestChatMessageRevokedKeyThenSent(t *testing.T) { 699 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 700 key := cryptKey(t) 701 text := "hi" 702 tc, boxer := setupChatTest(t, "unbox") 703 defer tc.Cleanup() 704 705 // need a real user 706 u, err := kbtest.CreateAndSignupFakeUserPaper("unbox", tc.G) 707 require.NoError(t, err) 708 t.Logf("using username:%+v uid: %+v", u.Username, u.User.GetUID()) 709 710 // pick a device 711 devices, _ := getActiveDevicesAndKeys(tc, u) 712 var thisDevice *libkb.Device 713 for _, device := range devices { 714 if device.Type != keybase1.DeviceTypeV2_PAPER { 715 thisDevice = device 716 } 717 } 718 require.NotNil(t, thisDevice, "thisDevice should be non-nil") 719 720 // Find the key 721 signingKey, err := engine.GetMySecretKey(context.TODO(), tc.G, libkb.DeviceSigningKeyType, "some chat or something test") 722 require.NoError(t, err, "get device signing key") 723 signKP, ok := signingKey.(libkb.NaclSigningKeyPair) 724 require.Equal(t, true, ok, "signing key must be nacl") 725 t.Logf("found signing kp: %+v", signKP.GetKID()) 726 727 // Revoke the key 728 t.Logf("revoking device id:%+v", thisDevice.ID) 729 err = doRevokeDevice(tc, u, thisDevice.ID, true) 730 require.NoError(t, err, "revoke device") 731 732 // Sleep for a second because revocation timestamps are only second-resolution. 733 time.Sleep(1 * time.Second) 734 735 // Reset the cache 736 // tc.G.CachedUserLoader = libkb.NewCachedUserLoader(tc.G, libkb.CachedUserTimeout) 737 738 // Sign a message using a key of u's that has been revoked 739 t.Logf("signing message") 740 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 741 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 742 require.NoError(t, err) 743 744 boxed.ServerHeader = &chat1.MessageServerHeader{ 745 Ctime: gregor1.ToTime(time.Now()), 746 } 747 748 // The message should not unbox 749 _, ierr := boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 750 require.NotNil(t, ierr, "unboxing must err because key was revoked before send") 751 require.IsType(t, libkb.NoKeyError{}, ierr.Inner(), "unexpected error for revoked sender key: %v", ierr) 752 753 // Test key validity 754 revoked, err := boxer.ValidSenderKey(context.TODO(), gregor1.UID(u.User.GetUID().ToBytes()), signKP.GetBinaryKID(), boxed.ServerHeader.Ctime) 755 require.Error(t, err, "ValidSenderKey") 756 require.IsType(t, PermanentUnboxingError{}, err) 757 require.Equal(t, "key invalid for sender at message ctime", 758 err.(PermanentUnboxingError).Inner().(libkb.NoKeyError).Msg) 759 require.NotNil(t, revoked) 760 761 // test out quick mode and resolve 762 ctx := globals.CtxModifyUnboxMode(context.TODO(), types.UnboxModeQuick) 763 unboxed, ierr := boxer.unbox(ctx, boxed, convInfo, key, nil) 764 require.NoError(t, ierr) 765 require.NotNil(t, unboxed) 766 resolved, modified, err := boxer.ResolveSkippedUnboxed(ctx, 767 chat1.NewMessageUnboxedWithValid(*unboxed)) 768 require.NoError(t, err) 769 require.True(t, resolved.IsError()) 770 require.True(t, modified) 771 }) 772 } 773 774 // Sent with a revoked sender key before revocation 775 func TestChatMessageSentThenRevokedSenderKey(t *testing.T) { 776 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 777 key := cryptKey(t) 778 text := "hi" 779 tc, boxer := setupChatTest(t, "unbox") 780 defer tc.Cleanup() 781 782 // need a real user 783 u, err := kbtest.CreateAndSignupFakeUserPaper("unbox", tc.G) 784 require.NoError(t, err) 785 t.Logf("using username:%+v uid: %+v", u.Username, u.User.GetUID()) 786 787 // pick a device 788 devices, _ := getActiveDevicesAndKeys(tc, u) 789 var thisDevice *libkb.Device 790 for _, device := range devices { 791 if device.Type != keybase1.DeviceTypeV2_PAPER { 792 thisDevice = device 793 } 794 } 795 require.NotNil(t, thisDevice, "thisDevice should be non-nil") 796 797 // Find the key 798 signingKey, err := engine.GetMySecretKey(context.TODO(), tc.G, libkb.DeviceSigningKeyType, "some chat or something test") 799 require.NoError(t, err, "get device signing key") 800 signKP, ok := signingKey.(libkb.NaclSigningKeyPair) 801 require.Equal(t, true, ok, "signing key must be nacl") 802 t.Logf("found signing kp: %+v", signKP.GetKID()) 803 804 // Sign a message using a key of u's that has not yet been revoked 805 t.Logf("signing message") 806 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 807 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 808 require.NoError(t, err) 809 810 boxed.ServerHeader = &chat1.MessageServerHeader{ 811 Ctime: gregor1.ToTime(time.Now()), 812 } 813 814 // Sleep for a second because revocation timestamps are only second-resolution. 815 time.Sleep(1 * time.Second) 816 817 // Revoke the key 818 t.Logf("revoking device id:%+v", thisDevice.ID) 819 err = doRevokeDevice(tc, u, thisDevice.ID, true) 820 require.NoError(t, err, "revoke device") 821 822 // The message should unbox but with senderDeviceRevokedAt set 823 unboxed, ierr := boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 824 require.Nil(t, ierr, "unboxing err") 825 require.NotNil(t, unboxed.SenderDeviceRevokedAt, "message should be noticed as signed by revoked key") 826 827 // Test key validity 828 revoked, err := boxer.ValidSenderKey(context.TODO(), gregor1.UID(u.User.GetUID().ToBytes()), signKP.GetBinaryKID(), boxed.ServerHeader.Ctime) 829 require.NoError(t, err, "ValidSenderKey") 830 require.NotNil(t, revoked) 831 832 // test quick mode and resolver 833 ctx := globals.CtxModifyUnboxMode(context.TODO(), types.UnboxModeQuick) 834 unboxed, ierr = boxer.unbox(ctx, boxed, convInfo, key, nil) 835 require.NoError(t, ierr) 836 require.Nil(t, unboxed.SenderDeviceRevokedAt) 837 resolved, modified, err := boxer.ResolveSkippedUnboxed(ctx, 838 chat1.NewMessageUnboxedWithValid(*unboxed)) 839 require.NoError(t, err) 840 require.True(t, resolved.IsValid()) 841 require.NotNil(t, resolved.Valid().SenderDeviceRevokedAt) 842 require.True(t, modified) 843 }) 844 } 845 846 func TestChatMessagePublic(t *testing.T) { 847 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 848 text := "hi" 849 tc, boxer := setupChatTest(t, "unbox") 850 defer tc.Cleanup() 851 852 world := NewChatMockWorld(t, "unbox", 4) 853 defer world.Cleanup() 854 u := world.GetUsers()[0] 855 uid := u.User.GetUID().ToBytes() 856 tc = world.Tcs[u.Username] 857 tlf := kbtest.NewTlfMock(world) 858 ctx := newTestContextWithTlfMock(tc, tlf) 859 860 ni, err := tlf.LookupID(ctx, u.Username, true) 861 require.NoError(t, err) 862 header := chat1.MessageClientHeader{ 863 Conv: chat1.ConversationIDTriple{ 864 Tlfid: ni.ID, 865 }, 866 Sender: uid, 867 TlfPublic: true, 868 TlfName: u.Username, 869 MessageType: chat1.MessageType_TEXT, 870 } 871 msg := textMsgWithHeader(t, text, header) 872 873 signKP := getSigningKeyPairForTest(t, tc, u) 874 875 boxer.boxVersionForTesting = &mbVersion 876 boxed, err := boxer.BoxMessage(ctx, msg, chat1.ConversationMembersType_KBFS, signKP, nil) 877 require.NoError(t, err) 878 879 _ = boxed 880 881 // need to give it a server header... 882 boxed.ServerHeader = &chat1.MessageServerHeader{ 883 Ctime: gregor1.ToTime(time.Now()), 884 } 885 886 // NOTE: this is hashing a lot of zero values, and it might break when we add more checks 887 convID := header.Conv.ToConversationID([2]byte{0, 0}) 888 conv := chat1.Conversation{ 889 Metadata: chat1.ConversationMetadata{ 890 ConversationID: convID, 891 Visibility: keybase1.TLFVisibility_PUBLIC, 892 }, 893 } 894 895 decmsg, err := boxer.UnboxMessage(ctx, boxed, conv, nil) 896 require.NoError(t, err) 897 require.True(t, decmsg.IsValid()) 898 899 body := decmsg.Valid().MessageBody 900 typ, err := body.MessageType() 901 require.NoError(t, err) 902 require.Equal(t, typ, chat1.MessageType_TEXT) 903 require.Equal(t, body.Text().Body, text) 904 }) 905 } 906 907 // Test that you cannot unbox a message whose client header sender does not match. 908 // This prevents one kind of misattribution within a tlf. 909 // Device mismatches are probably tolerated. 910 func TestChatMessageSenderMismatch(t *testing.T) { 911 912 var allUIDs []keybase1.UID 913 914 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 915 key := cryptKey(t) 916 text := "hi" 917 tc, boxer := setupChatTest(t, "unbox") 918 defer tc.Cleanup() 919 920 u2, err := kbtest.CreateAndSignupFakeUser("chat", tc.G) 921 require.NoError(t, err) 922 allUIDs = append(allUIDs, u2.GetUID()) 923 924 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 925 require.NoError(t, err) 926 allUIDs = append(allUIDs, u.GetUID()) 927 928 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 929 930 signKP := getSigningKeyPairForTest(t, tc, u) 931 932 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 933 require.NoError(t, err) 934 935 // need to give it a server header... 936 boxed.ServerHeader = &chat1.MessageServerHeader{ 937 Ctime: gregor1.ToTime(time.Now()), 938 } 939 940 // Set the outer sender to something else 941 boxed.ClientHeader.Sender = gregor1.UID(u2.User.GetUID().ToBytes()) 942 943 _, err = boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 944 require.Error(t, err, "should not unbox with sender mismatch") 945 }) 946 947 tc := libkb.SetupTest(t, "batch", 0) 948 defer tc.Cleanup() 949 950 // Just check that batch fetching of device encryption keys work. 951 // Use the 4 users we've collected in this test. 952 var uvs []keybase1.UserVersion 953 for _, uid := range allUIDs { 954 uvs = append(uvs, keybase1.UserVersion{Uid: uid}) 955 } 956 957 keys, err := batchLoadEncryptionKIDs(context.TODO(), tc.G, uvs) 958 require.NoError(t, err, "no error") 959 require.Equal(t, len(keys), len(uvs), "one key per user") 960 } 961 962 // Test a message that deletes 963 func TestChatMessageDeletes(t *testing.T) { 964 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 965 key := cryptKey(t) 966 text := "hi" 967 tc, boxer := setupChatTest(t, "unbox") 968 defer tc.Cleanup() 969 970 // need a real user 971 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 972 require.NoError(t, err) 973 974 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 975 deleteIDs := []chat1.MessageID{5, 6, 7} 976 msg.MessageBody = chat1.NewMessageBodyWithDelete(chat1.MessageDelete{MessageIDs: deleteIDs}) 977 msg.ClientHeader.Supersedes = deleteIDs[0] 978 msg.ClientHeader.Deletes = deleteIDs 979 980 signKP := getSigningKeyPairForTest(t, tc, u) 981 982 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 983 require.NoError(t, err) 984 985 // need to give it a server header... 986 boxed.ServerHeader = &chat1.MessageServerHeader{ 987 Ctime: gregor1.ToTime(time.Now()), 988 SupersededBy: 4, 989 } 990 991 unboxed, err := boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 992 require.NoError(t, err) 993 body := unboxed.MessageBody 994 typ, err := body.MessageType() 995 require.NoError(t, err) 996 require.Equal(t, typ, chat1.MessageType_DELETE) 997 require.Nil(t, unboxed.SenderDeviceRevokedAt, "message should not be from revoked device") 998 }) 999 } 1000 1001 // Test a message with a deleted body 1002 func TestChatMessageDeleted(t *testing.T) { 1003 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 1004 for _, supersededBy := range []chat1.MessageID{0, 4} { 1005 key := cryptKey(t) 1006 text := "hi" 1007 tc, boxer := setupChatTest(t, "unbox") 1008 defer tc.Cleanup() 1009 1010 // need a real user 1011 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 1012 require.NoError(t, err) 1013 1014 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 1015 1016 signKP := getSigningKeyPairForTest(t, tc, u) 1017 1018 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 1019 require.NoError(t, err) 1020 1021 // need to give it a server header... 1022 boxed.ServerHeader = &chat1.MessageServerHeader{ 1023 Ctime: gregor1.ToTime(time.Now()), 1024 SupersededBy: supersededBy, 1025 } 1026 1027 // Delete the body 1028 boxed.BodyCiphertext = chat1.EncryptedData{} 1029 1030 unboxed, err := boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 1031 require.NoError(t, err, "deleted message should still unbox") 1032 body := unboxed.MessageBody 1033 typ, err := body.MessageType() 1034 require.NoError(t, err) 1035 require.Equal(t, typ, chat1.MessageType_NONE) 1036 require.Nil(t, unboxed.SenderDeviceRevokedAt, "message should not be from revoked device") 1037 } 1038 }) 1039 } 1040 1041 // Test a message with a deleted body but missing a supersededby header 1042 func TestChatMessageDeletedNotSuperseded(t *testing.T) { 1043 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 1044 key := cryptKey(t) 1045 text := "hi" 1046 tc, boxer := setupChatTest(t, "unbox") 1047 defer tc.Cleanup() 1048 1049 // need a real user 1050 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 1051 require.NoError(t, err) 1052 1053 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 1054 1055 signKP := getSigningKeyPairForTest(t, tc, u) 1056 1057 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 1058 require.NoError(t, err) 1059 1060 // need to give it a server header... 1061 boxed.ServerHeader = &chat1.MessageServerHeader{ 1062 Ctime: gregor1.ToTime(time.Now()), 1063 } 1064 1065 // Delete the body 1066 boxed.BodyCiphertext.E = []byte{} 1067 1068 unboxed, err := boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 1069 // The server was not setting supersededBy on EDITs when their TEXT got deleted. 1070 // So there are existing messages which have no supersededBy but are legitimately deleted. 1071 // Tracked in CORE-4662 1072 require.NoError(t, err, "suprisingly, should be able to unbox with deleted but no supersededby") 1073 require.Equal(t, chat1.MessageBody{}, unboxed.MessageBody) 1074 }) 1075 } 1076 1077 // Test a DeleteHistory message. 1078 func TestChatMessageDeleteHistory(t *testing.T) { 1079 mbVersion := chat1.MessageBoxedVersion_V2 1080 key := cryptKey(t) 1081 text := "hi" 1082 tc, boxer := setupChatTest(t, "unbox") 1083 defer tc.Cleanup() 1084 1085 // need a real user 1086 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 1087 require.NoError(t, err) 1088 1089 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 1090 mdh := chat1.MessageDeleteHistory{Upto: chat1.MessageID(4)} 1091 msg.MessageBody = chat1.NewMessageBodyWithDeletehistory(mdh) 1092 msg.ClientHeader.DeleteHistory = &mdh 1093 1094 signKP := getSigningKeyPairForTest(t, tc, u) 1095 1096 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 1097 require.NoError(t, err) 1098 1099 // need to give it a server header... 1100 boxed.ServerHeader = &chat1.MessageServerHeader{ 1101 Ctime: gregor1.ToTime(time.Now()), 1102 SupersededBy: 4, 1103 } 1104 1105 unboxed, err := boxer.unbox(context.TODO(), boxed, convInfo, key, nil) 1106 require.NoError(t, err) 1107 body := unboxed.MessageBody 1108 typ, err := body.MessageType() 1109 require.NoError(t, err) 1110 require.Equal(t, typ, chat1.MessageType_DELETEHISTORY) 1111 require.Nil(t, unboxed.SenderDeviceRevokedAt, "message should not be from revoked device") 1112 require.Equal(t, mdh, unboxed.MessageBody.Deletehistory()) 1113 } 1114 1115 func TestV1Message1(t *testing.T) { 1116 // Unbox a canned V1 message from before V2 was thought up. 1117 1118 tc, boxer := setupChatTest(t, "unbox") 1119 defer tc.Cleanup() 1120 1121 canned := getCannedMessage(t, "alice25-bob25-1") 1122 boxed := canned.AsBoxed(t) 1123 modifyBoxerForTesting(t, boxer, &canned) 1124 1125 // Check some features before unboxing 1126 require.Equal(t, chat1.MessageBoxedVersion_VNONE, boxed.Version) 1127 require.Equal(t, "1fb5a5e7585a43aba1a59520939e2420", boxed.ClientHeader.Conv.TopicID.String()) 1128 require.Equal(t, canned.encryptionKeyGeneration, boxed.KeyGeneration) 1129 1130 // Unbox 1131 unboxed, err := boxer.unbox(context.TODO(), canned.AsBoxed(t), convInfo, 1132 canned.EncryptionKey(t), nil) 1133 require.NoError(t, err) 1134 1135 // Check some features of the unboxed 1136 // ClientHeader 1137 require.Equal(t, "d1fec1a2287b473206e282f4d4f30116", unboxed.ClientHeader.Conv.Tlfid.String()) 1138 require.Equal(t, "1fb5a5e7585a43aba1a59520939e2420", unboxed.ClientHeader.Conv.TopicID.String()) 1139 require.Equal(t, chat1.TopicType_CHAT, unboxed.ClientHeader.Conv.TopicType) 1140 require.Equal(t, "alice25,bob25", unboxed.ClientHeader.TlfName) 1141 require.Equal(t, false, unboxed.ClientHeader.TlfPublic) 1142 require.Equal(t, chat1.MessageType_TLFNAME, unboxed.ClientHeader.MessageType) 1143 require.Nil(t, unboxed.ClientHeader.Prev) 1144 require.Equal(t, canned.SenderUID(t), unboxed.ClientHeader.Sender) 1145 require.Equal(t, canned.SenderDeviceID(t), unboxed.ClientHeader.SenderDevice) 1146 // CORE-4540: Uncomment this assertion when MerkleRoot is added to MessageClientHeaderVerified 1147 require.Nil(t, unboxed.ClientHeader.MerkleRoot) 1148 require.Nil(t, unboxed.ClientHeader.OutboxID) 1149 require.Nil(t, unboxed.ClientHeader.OutboxInfo) 1150 1151 // ServerHeader 1152 require.Equal(t, chat1.MessageID(1), unboxed.ServerHeader.MessageID) 1153 require.Equal(t, chat1.MessageID(0), unboxed.ServerHeader.SupersededBy) 1154 1155 // MessageBody 1156 mTyp, err2 := unboxed.MessageBody.MessageType() 1157 require.NoError(t, err2) 1158 // MessageBody has no TLFNAME variant, so the type comes out as 0. 1159 require.Equal(t, chat1.MessageType(0), mTyp) 1160 1161 // Other attributes of unboxed 1162 require.Equal(t, canned.senderUsername, unboxed.SenderUsername) 1163 require.Equal(t, canned.senderDeviceName, unboxed.SenderDeviceName) 1164 require.Equal(t, canned.senderDeviceType, unboxed.SenderDeviceType) 1165 require.Equal(t, canned.headerHash, unboxed.HeaderHash.String()) 1166 require.NotNil(t, unboxed.HeaderSignature) 1167 require.Equal(t, canned.VerifyKey(t), unboxed.HeaderSignature.K) 1168 require.NotNil(t, unboxed.VerificationKey) 1169 require.Nil(t, unboxed.SenderDeviceRevokedAt) 1170 } 1171 1172 func TestV1Message2(t *testing.T) { 1173 // Unbox a canned V1 message from before V2 was thought up. 1174 1175 tc, boxer := setupChatTest(t, "unbox") 1176 defer tc.Cleanup() 1177 1178 canned := getCannedMessage(t, "alice25-bob25-2") 1179 boxed := canned.AsBoxed(t) 1180 modifyBoxerForTesting(t, boxer, &canned) 1181 1182 // Check some features before unboxing 1183 require.Equal(t, chat1.MessageBoxedVersion_VNONE, boxed.Version) 1184 require.Equal(t, "1fb5a5e7585a43aba1a59520939e2420", boxed.ClientHeader.Conv.TopicID.String()) 1185 require.Equal(t, canned.encryptionKeyGeneration, boxed.KeyGeneration) 1186 1187 // Unbox 1188 unboxed, err := boxer.unbox(context.TODO(), canned.AsBoxed(t), convInfo, 1189 canned.EncryptionKey(t), nil) 1190 require.NoError(t, err) 1191 1192 // Check some features of the unboxed 1193 // ClientHeader 1194 require.Equal(t, "d1fec1a2287b473206e282f4d4f30116", unboxed.ClientHeader.Conv.Tlfid.String()) 1195 require.Equal(t, "1fb5a5e7585a43aba1a59520939e2420", unboxed.ClientHeader.Conv.TopicID.String()) 1196 require.Equal(t, chat1.TopicType_CHAT, unboxed.ClientHeader.Conv.TopicType) 1197 require.Equal(t, "alice25,bob25", unboxed.ClientHeader.TlfName) 1198 require.Equal(t, false, unboxed.ClientHeader.TlfPublic) 1199 require.Equal(t, chat1.MessageType_TEXT, unboxed.ClientHeader.MessageType) 1200 expectedPrevs := []chat1.MessagePreviousPointer{{Id: 0x1, Hash: chat1.Hash{0xc9, 0x6e, 0x28, 0x6d, 0x88, 0x2e, 0xfc, 0x44, 0xdb, 0x80, 0xe5, 0x1d, 0x8e, 0x8, 0xf1, 0xde, 0x28, 0xb4, 0x93, 0x4c, 0xc8, 0x49, 0x1f, 0xbe, 0x88, 0x42, 0xf, 0x31, 0x10, 0x65, 0x14, 0xbe}}} 1201 require.Equal(t, expectedPrevs, unboxed.ClientHeader.Prev) 1202 require.Equal(t, canned.SenderUID(t), unboxed.ClientHeader.Sender) 1203 require.Equal(t, canned.SenderDeviceID(t), unboxed.ClientHeader.SenderDevice) 1204 // CORE-4540: Uncomment this assertion when MerkleRoot is added to MessageClientHeaderVerified 1205 require.Nil(t, unboxed.ClientHeader.MerkleRoot) 1206 require.Nil(t, unboxed.ClientHeader.OutboxID) 1207 require.Nil(t, unboxed.ClientHeader.OutboxInfo) 1208 1209 // ServerHeader 1210 require.Equal(t, chat1.MessageID(2), unboxed.ServerHeader.MessageID) 1211 require.Equal(t, chat1.MessageID(0), unboxed.ServerHeader.SupersededBy) 1212 1213 // MessageBody 1214 require.Equal(t, "test1", unboxed.MessageBody.Text().Body) 1215 1216 // Other attributes of unboxed 1217 require.Equal(t, canned.senderUsername, unboxed.SenderUsername) 1218 require.Equal(t, canned.senderDeviceName, unboxed.SenderDeviceName) 1219 require.Equal(t, canned.senderDeviceType, unboxed.SenderDeviceType) 1220 require.Equal(t, canned.headerHash, unboxed.HeaderHash.String()) 1221 require.NotNil(t, unboxed.HeaderSignature) 1222 require.Equal(t, canned.VerifyKey(t), unboxed.HeaderSignature.K) 1223 require.NotNil(t, unboxed.VerificationKey) 1224 require.Nil(t, unboxed.SenderDeviceRevokedAt) 1225 } 1226 1227 func TestV1Message3(t *testing.T) { 1228 // Unbox a canned V1 message from before V2 was thought up. 1229 1230 tc, boxer := setupChatTest(t, "unbox") 1231 defer tc.Cleanup() 1232 1233 canned := getCannedMessage(t, "bob25-alice25-3-deleted") 1234 boxed := canned.AsBoxed(t) 1235 modifyBoxerForTesting(t, boxer, &canned) 1236 1237 // Check some features before unboxing 1238 require.Equal(t, chat1.MessageBoxedVersion_VNONE, boxed.Version) 1239 require.Equal(t, "1fb5a5e7585a43aba1a59520939e2420", boxed.ClientHeader.Conv.TopicID.String()) 1240 require.Equal(t, canned.encryptionKeyGeneration, boxed.KeyGeneration) 1241 require.Equal(t, chat1.MessageID(3), boxed.ServerHeader.MessageID) 1242 require.Equal(t, chat1.MessageID(0), boxed.ClientHeader.Supersedes) 1243 require.Nil(t, boxed.ClientHeader.Deletes) 1244 1245 // Unbox 1246 unboxed, err := boxer.unbox(context.TODO(), canned.AsBoxed(t), convInfo, 1247 canned.EncryptionKey(t), nil) 1248 require.NoError(t, err) 1249 1250 // Check some features of the unboxed 1251 // ClientHeader 1252 require.Equal(t, "d1fec1a2287b473206e282f4d4f30116", unboxed.ClientHeader.Conv.Tlfid.String()) 1253 require.Equal(t, "1fb5a5e7585a43aba1a59520939e2420", unboxed.ClientHeader.Conv.TopicID.String()) 1254 require.Equal(t, chat1.TopicType_CHAT, unboxed.ClientHeader.Conv.TopicType) 1255 require.Equal(t, "alice25,bob25", unboxed.ClientHeader.TlfName) 1256 require.Equal(t, false, unboxed.ClientHeader.TlfPublic) 1257 require.Equal(t, chat1.MessageType_TEXT, unboxed.ClientHeader.MessageType) 1258 expectedPrevs := []chat1.MessagePreviousPointer{{Id: 2, Hash: chat1.Hash{0xbe, 0xb2, 0x7c, 0x41, 0xdb, 0xeb, 0x2e, 0x90, 0x04, 0xf2, 0x48, 0xf2, 0x78, 0x24, 0x3a, 0xde, 0x5e, 0x12, 0x0c, 0xb7, 0xc4, 0x1f, 0x40, 0xe8, 0x47, 0xa2, 0xe2, 0x2f, 0xe8, 0x2c, 0xd3, 0xb4}}} 1259 require.Equal(t, expectedPrevs, unboxed.ClientHeader.Prev) 1260 require.Equal(t, canned.SenderUID(t), unboxed.ClientHeader.Sender) 1261 require.Equal(t, canned.SenderDeviceID(t), unboxed.ClientHeader.SenderDevice) 1262 // CORE-4540: Uncomment this assertion when MerkleRoot is added to MessageClientHeaderVerified 1263 require.Nil(t, unboxed.ClientHeader.MerkleRoot) 1264 require.Nil(t, unboxed.ClientHeader.OutboxID) 1265 require.Nil(t, unboxed.ClientHeader.OutboxInfo) 1266 1267 // ServerHeader 1268 require.Equal(t, chat1.MessageID(3), unboxed.ServerHeader.MessageID) 1269 require.Equal(t, chat1.MessageID(5), unboxed.ServerHeader.SupersededBy) 1270 1271 // MessageBody 1272 require.Equal(t, chat1.MessageBody{}, unboxed.MessageBody) 1273 1274 // Other attributes of unboxed 1275 require.Equal(t, canned.senderUsername, unboxed.SenderUsername) 1276 require.Equal(t, canned.senderDeviceName, unboxed.SenderDeviceName) 1277 require.Equal(t, canned.senderDeviceType, unboxed.SenderDeviceType) 1278 require.Equal(t, canned.headerHash, unboxed.HeaderHash.String()) 1279 require.NotNil(t, unboxed.HeaderSignature) 1280 require.Equal(t, canned.VerifyKey(t), unboxed.HeaderSignature.K) 1281 require.NotNil(t, unboxed.VerificationKey) 1282 require.Nil(t, unboxed.SenderDeviceRevokedAt) 1283 } 1284 1285 func TestV1Message4(t *testing.T) { 1286 // Unbox a canned V1 message from before V2 was thought up. 1287 1288 tc, boxer := setupChatTest(t, "unbox") 1289 defer tc.Cleanup() 1290 1291 canned := getCannedMessage(t, "bob25-alice25-4-deleted-edit") 1292 boxed := canned.AsBoxed(t) 1293 modifyBoxerForTesting(t, boxer, &canned) 1294 1295 // Check some features before unboxing 1296 require.Equal(t, chat1.MessageBoxedVersion_VNONE, boxed.Version) 1297 require.Equal(t, "1fb5a5e7585a43aba1a59520939e2420", boxed.ClientHeader.Conv.TopicID.String()) 1298 require.Equal(t, canned.encryptionKeyGeneration, boxed.KeyGeneration) 1299 require.Equal(t, chat1.MessageID(4), boxed.ServerHeader.MessageID) 1300 require.Equal(t, chat1.MessageID(3), boxed.ClientHeader.Supersedes) 1301 require.Nil(t, boxed.ClientHeader.Deletes) 1302 1303 // Unbox 1304 unboxed, err := boxer.unbox(context.TODO(), canned.AsBoxed(t), convInfo, 1305 canned.EncryptionKey(t), nil) 1306 require.NoError(t, err) 1307 1308 // Check some features of the unboxed 1309 // ClientHeader 1310 require.Equal(t, "d1fec1a2287b473206e282f4d4f30116", unboxed.ClientHeader.Conv.Tlfid.String()) 1311 require.Equal(t, "1fb5a5e7585a43aba1a59520939e2420", unboxed.ClientHeader.Conv.TopicID.String()) 1312 require.Equal(t, chat1.TopicType_CHAT, unboxed.ClientHeader.Conv.TopicType) 1313 require.Equal(t, "alice25,bob25", unboxed.ClientHeader.TlfName) 1314 require.Equal(t, false, unboxed.ClientHeader.TlfPublic) 1315 require.Equal(t, chat1.MessageType_EDIT, unboxed.ClientHeader.MessageType) 1316 1317 expectedPrevs := []chat1.MessagePreviousPointer{{Id: 3, Hash: chat1.Hash{0x3b, 0x54, 0x7a, 0x7a, 0xdd, 0x32, 0x5c, 0xcc, 0x9f, 0x4d, 0x30, 0x12, 0xc5, 0x6e, 0xb1, 0xab, 0xa0, 0x1c, 0xf7, 0x68, 0x7e, 0x26, 0x13, 0x49, 0x3f, 0xf5, 0xc9, 0xb7, 0x16, 0xaf, 0xd5, 0x07}}} 1318 require.Equal(t, expectedPrevs, unboxed.ClientHeader.Prev) 1319 require.Equal(t, canned.SenderUID(t), unboxed.ClientHeader.Sender) 1320 require.Equal(t, canned.SenderDeviceID(t), unboxed.ClientHeader.SenderDevice) 1321 // CORE-4540: Uncomment this assertion when MerkleRoot is added to MessageClientHeaderVerified 1322 require.Nil(t, unboxed.ClientHeader.MerkleRoot) 1323 expectedOutboxID := chat1.OutboxID{0x8e, 0xcc, 0x94, 0xb7, 0xff, 0x50, 0x5c, 0x4} 1324 require.Equal(t, &expectedOutboxID, unboxed.ClientHeader.OutboxID) 1325 expectedOutboxInfo := &chat1.OutboxInfo{Prev: 0x3, ComposeTime: 1487708373568} 1326 require.Equal(t, expectedOutboxInfo, unboxed.ClientHeader.OutboxInfo) 1327 1328 // ServerHeader 1329 require.Equal(t, chat1.MessageID(4), unboxed.ServerHeader.MessageID) 1330 // At the time this message was canned, supersededBy was not set deleted edits. 1331 require.Equal(t, chat1.MessageID(0), unboxed.ServerHeader.SupersededBy) 1332 1333 // MessageBody 1334 require.Equal(t, chat1.MessageBody{}, unboxed.MessageBody) 1335 1336 // Other attributes of unboxed 1337 require.Equal(t, canned.senderUsername, unboxed.SenderUsername) 1338 require.Equal(t, canned.senderDeviceName, unboxed.SenderDeviceName) 1339 require.Equal(t, canned.senderDeviceType, unboxed.SenderDeviceType) 1340 require.Equal(t, canned.headerHash, unboxed.HeaderHash.String()) 1341 require.NotNil(t, unboxed.HeaderSignature) 1342 require.Equal(t, canned.VerifyKey(t), unboxed.HeaderSignature.K) 1343 require.NotNil(t, unboxed.VerificationKey) 1344 require.Nil(t, unboxed.SenderDeviceRevokedAt) 1345 } 1346 1347 func TestV1Message5(t *testing.T) { 1348 // Unbox a canned V1 message from before V2 was thought up. 1349 1350 tc, boxer := setupChatTest(t, "unbox") 1351 defer tc.Cleanup() 1352 1353 canned := getCannedMessage(t, "bob25-alice25-5-delete") 1354 boxed := canned.AsBoxed(t) 1355 modifyBoxerForTesting(t, boxer, &canned) 1356 1357 // Check some features before unboxing 1358 require.Equal(t, chat1.MessageBoxedVersion_VNONE, boxed.Version) 1359 require.Equal(t, "1fb5a5e7585a43aba1a59520939e2420", boxed.ClientHeader.Conv.TopicID.String()) 1360 require.Equal(t, canned.encryptionKeyGeneration, boxed.KeyGeneration) 1361 require.Equal(t, chat1.MessageID(5), boxed.ServerHeader.MessageID) 1362 require.Equal(t, chat1.MessageID(3), boxed.ClientHeader.Supersedes) 1363 expectedDeletesIDs := []chat1.MessageID{3, 4} 1364 require.Equal(t, expectedDeletesIDs, boxed.ClientHeader.Deletes) 1365 1366 // Unbox 1367 unboxed, err := boxer.unbox(context.TODO(), canned.AsBoxed(t), convInfo, 1368 canned.EncryptionKey(t), nil) 1369 require.NoError(t, err) 1370 1371 // Check some features of the unboxed 1372 // ClientHeader 1373 require.Equal(t, "d1fec1a2287b473206e282f4d4f30116", unboxed.ClientHeader.Conv.Tlfid.String()) 1374 require.Equal(t, "1fb5a5e7585a43aba1a59520939e2420", unboxed.ClientHeader.Conv.TopicID.String()) 1375 require.Equal(t, chat1.TopicType_CHAT, unboxed.ClientHeader.Conv.TopicType) 1376 require.Equal(t, "alice25,bob25", unboxed.ClientHeader.TlfName) 1377 require.Equal(t, false, unboxed.ClientHeader.TlfPublic) 1378 require.Equal(t, chat1.MessageType_DELETE, unboxed.ClientHeader.MessageType) 1379 1380 expectedPrevs := []chat1.MessagePreviousPointer{{Id: 4, Hash: chat1.Hash{0xea, 0x68, 0x5e, 0x0f, 0x26, 0xb5, 0xb4, 0xfc, 0x1d, 0xe4, 0x15, 0x11, 0x34, 0x40, 0xcc, 0x3d, 0x54, 0x65, 0xa1, 0x52, 0x42, 0xd6, 0x83, 0xa7, 0xf4, 0x88, 0x96, 0xec, 0xd2, 0xc6, 0xd6, 0x26}}} 1381 require.Equal(t, expectedPrevs, unboxed.ClientHeader.Prev) 1382 require.Equal(t, canned.SenderUID(t), unboxed.ClientHeader.Sender) 1383 require.Equal(t, canned.SenderDeviceID(t), unboxed.ClientHeader.SenderDevice) 1384 // CORE-4540: Uncomment this assertion when MerkleRoot is added to MessageClientHeaderVerified 1385 require.Nil(t, unboxed.ClientHeader.MerkleRoot) 1386 expectedOutboxID := chat1.OutboxID{0xdc, 0x74, 0x6, 0x5d, 0xf9, 0x5f, 0x1c, 0x48} 1387 require.Equal(t, &expectedOutboxID, unboxed.ClientHeader.OutboxID) 1388 expectedOutboxInfo := &chat1.OutboxInfo{Prev: 0x3, ComposeTime: 1487708384552} 1389 require.Equal(t, expectedOutboxInfo, unboxed.ClientHeader.OutboxInfo) 1390 1391 // ServerHeader 1392 require.Equal(t, chat1.MessageID(5), unboxed.ServerHeader.MessageID) 1393 require.Equal(t, chat1.MessageID(0), unboxed.ServerHeader.SupersededBy) 1394 1395 // MessageBody 1396 require.Equal(t, chat1.MessageDelete{MessageIDs: expectedDeletesIDs}, unboxed.MessageBody.Delete()) 1397 1398 // Other attributes of unboxed 1399 require.Equal(t, canned.senderUsername, unboxed.SenderUsername) 1400 require.Equal(t, canned.senderDeviceName, unboxed.SenderDeviceName) 1401 require.Equal(t, canned.senderDeviceType, unboxed.SenderDeviceType) 1402 require.Equal(t, canned.headerHash, unboxed.HeaderHash.String()) 1403 require.NotNil(t, unboxed.HeaderSignature) 1404 require.Equal(t, canned.VerifyKey(t), unboxed.HeaderSignature.K) 1405 require.NotNil(t, unboxed.VerificationKey) 1406 require.Nil(t, unboxed.SenderDeviceRevokedAt) 1407 } 1408 1409 func modifyBoxerForTesting(t *testing.T, boxer *Boxer, canned *cannedMessage) { 1410 boxer.testingGetSenderInfoLocal = func(ctx context.Context, uid gregor1.UID, did gregor1.DeviceID) (senderUsername string, senderDeviceName string, senderDeviceType keybase1.DeviceTypeV2) { 1411 require.Equal(t, canned.SenderUID(t), uid) 1412 require.Equal(t, canned.SenderDeviceID(t), did) 1413 return canned.senderUsername, canned.senderDeviceName, canned.senderDeviceType 1414 } 1415 boxer.testingValidSenderKey = func(ctx context.Context, uid gregor1.UID, verifyKey []byte, ctime gregor1.Time) (revoked *gregor1.Time, unboxingErr types.UnboxingError) { 1416 require.Equal(t, canned.SenderUID(t), uid) 1417 require.Equal(t, canned.VerifyKey(t), verifyKey) 1418 // ignore ctime, always report the key as still valid 1419 return nil, nil 1420 } 1421 } 1422 1423 func requireValidMessage(t *testing.T, unboxed chat1.MessageUnboxed, description string) { 1424 state, err := unboxed.State() 1425 require.NoError(t, err, "failed to get the unboxed message state") 1426 require.Equal(t, chat1.MessageUnboxedState_VALID, state, description) 1427 } 1428 1429 func requireErrorMessage(t *testing.T, unboxed chat1.MessageUnboxed, description string) { 1430 state, err := unboxed.State() 1431 require.NoError(t, err, "failed to get the unboxed message state") 1432 require.Equal(t, chat1.MessageUnboxedState_ERROR, state, description) 1433 } 1434 1435 func TestChatMessageBodyHashReplay(t *testing.T) { 1436 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 1437 text := "hi" 1438 tc, boxer := setupChatTest(t, "unbox") 1439 defer tc.Cleanup() 1440 1441 // need a real user 1442 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 1443 require.NoError(t, err) 1444 1445 signKP := getSigningKeyPairForTest(t, tc, u) 1446 1447 // Generate an encryption key and create a fake finder to fetch it. 1448 key := cryptKey(t) 1449 g := globals.NewContext(tc.G, tc.ChatG) 1450 g.CtxFactory = newMockCtxFactory(g, []keybase1.CryptKey{*key}) 1451 boxerContext := globals.BackgroundChatCtx(context.TODO(), g) 1452 1453 // This message has an all zeros ConversationIDTriple, but that's fine. We 1454 // can still extract the ConvID from it. 1455 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 1456 convID := msg.ClientHeader.Conv.ToConversationID([2]byte{0, 0}) 1457 conv := chat1.Conversation{ 1458 Metadata: chat1.ConversationMetadata{ 1459 ConversationID: convID, 1460 }, 1461 } 1462 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 1463 require.NoError(t, err) 1464 1465 // Need to give it a server header... 1466 boxed.ServerHeader = &chat1.MessageServerHeader{ 1467 Ctime: gregor1.ToTime(time.Now()), 1468 MessageID: 1, 1469 } 1470 1471 // Unbox the message once. 1472 unboxed, err := boxer.UnboxMessage(boxerContext, boxed, conv, nil) 1473 require.NoError(t, err) 1474 requireValidMessage(t, unboxed, "we expected msg4 to succeed") 1475 1476 // Unbox it again. This should be fine. 1477 unboxed, err = boxer.UnboxMessage(boxerContext, boxed, conv, nil) 1478 require.NoError(t, err) 1479 requireValidMessage(t, unboxed, "we expected msg4 to succeed the second time too") 1480 1481 // Now try to unbox it again with a different MessageID. This must fail. 1482 boxed.ServerHeader.MessageID = 2 1483 unboxed, err = boxer.UnboxMessage(boxerContext, boxed, conv, nil) 1484 require.NoError(t, err) 1485 requireErrorMessage(t, unboxed, "replay must be detected") 1486 }) 1487 } 1488 1489 func TestChatMessagePrevPointerInconsistency(t *testing.T) { 1490 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 1491 tc, boxer := setupChatTest(t, "unbox") 1492 defer tc.Cleanup() 1493 1494 // need a real user 1495 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 1496 require.NoError(t, err) 1497 1498 signKP := getSigningKeyPairForTest(t, tc, u) 1499 1500 // Generate an encryption key and create a fake finder to fetch it. 1501 key := cryptKey(t) 1502 g := globals.NewContext(tc.G, tc.ChatG) 1503 g.CtxFactory = newMockCtxFactory(g, []keybase1.CryptKey{*key}) 1504 boxerContext := globals.BackgroundChatCtx(context.TODO(), g) 1505 1506 // Everything below will use the zero convID. 1507 convID := chat1.ConversationIDTriple{Tlfid: mockTLFID}.ToConversationID([2]byte{0, 0}) 1508 conv := chat1.Conversation{ 1509 Metadata: chat1.ConversationMetadata{ 1510 ConversationID: convID, 1511 }, 1512 } 1513 1514 makeMsg := func(id chat1.MessageID, prevs []chat1.MessagePreviousPointer) chat1.MessageBoxed { 1515 msg := textMsgWithSender(t, "foo text", gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 1516 msg.ClientHeader.Prev = prevs 1517 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 1518 require.NoError(t, err) 1519 boxed.ServerHeader = &chat1.MessageServerHeader{ 1520 Ctime: gregor1.ToTime(time.Now()), 1521 MessageID: id, 1522 } 1523 return boxed 1524 } 1525 1526 // Generate a couple of initial messages, with no prev pointers of their own. 1527 boxed1 := makeMsg(1, nil) 1528 boxed2 := makeMsg(2, nil) 1529 1530 // Now unbox the first message. That caches its header hash. Leave the 1531 // second one out of the cache for now though. (We'll use it to cause an 1532 // error later.) 1533 unboxed1, err := boxer.UnboxMessage(boxerContext, boxed1, conv, nil) 1534 require.NoError(t, err) 1535 1536 // Create two more messages, which both have bad prev pointers. Msg3 has a 1537 // bad prev for msg1, and must fail to unbox, because we've already unboxed 1538 // msg1. Msg4 has a bad prev for msg2 (which we haven't unboxed yet) 1539 // and a good prev for msg1. Msg4 will therefore succeed at unboxing now, 1540 // and we will check that msg2 fails later. 1541 boxed3 := makeMsg(3, []chat1.MessagePreviousPointer{ 1542 { 1543 Id: 1, 1544 // Bad pointer for msg1. This will cause an immediate unboxing failure. 1545 Hash: []byte("BAD PREV POINTER HERE"), 1546 }, 1547 }) 1548 boxed4 := makeMsg(4, []chat1.MessagePreviousPointer{ 1549 { 1550 Id: 1, 1551 // Good pointer for msg1. 1552 Hash: unboxed1.Valid().HeaderHash, 1553 }, 1554 { 1555 Id: 2, 1556 // Bad pointer for msg2. Because we've never unboxed message 2 1557 // before, though, we'll cache this bad prev pointer, and it's msg2 1558 // that will fail to unbox. 1559 Hash: []byte("ANOTHER BAD PREV POINTER OMG"), 1560 }, 1561 }) 1562 1563 unboxed, err := boxer.UnboxMessage(boxerContext, boxed3, conv, nil) 1564 require.NoError(t, err) 1565 requireErrorMessage(t, unboxed, "msg3 has a known bad prev pointer and must fail to unbox") 1566 1567 unboxed, err = boxer.UnboxMessage(boxerContext, boxed4, conv, nil) 1568 require.NoError(t, err) 1569 requireValidMessage(t, unboxed, "we expected msg4 to succeed") 1570 1571 // Now try to unbox msg2. Because of msg4's bad pointer, this should fail. 1572 unboxed, err = boxer.UnboxMessage(boxerContext, boxed2, conv, nil) 1573 require.NoError(t, err) 1574 requireErrorMessage(t, unboxed, "msg2 should fail to unbox, because of msg4's bad pointer") 1575 }) 1576 } 1577 1578 func TestChatMessageBadConvID(t *testing.T) { 1579 doWithMBVersions(func(mbVersion chat1.MessageBoxedVersion) { 1580 text := "hi" 1581 tc, boxer := setupChatTest(t, "unbox") 1582 defer tc.Cleanup() 1583 1584 // need a real user 1585 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 1586 require.NoError(t, err) 1587 1588 signKP := getSigningKeyPairForTest(t, tc, u) 1589 1590 // Generate an encryption key and create a fake finder to fetch it. 1591 key := cryptKey(t) 1592 g := globals.NewContext(tc.G, tc.ChatG) 1593 g.CtxFactory = newMockCtxFactory(g, []keybase1.CryptKey{*key}) 1594 boxerContext := globals.BackgroundChatCtx(context.TODO(), g) 1595 1596 // This message has an all zeros ConversationIDTriple, but that's fine. We 1597 // can still extract the ConvID from it. 1598 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), mbVersion) 1599 boxed, err := boxer.box(context.TODO(), msg, key, nil, signKP, mbVersion, nil) 1600 require.NoError(t, err) 1601 boxed.ServerHeader = &chat1.MessageServerHeader{ 1602 Ctime: gregor1.ToTime(time.Now()), 1603 MessageID: 1, 1604 } 1605 1606 // Confirm that this message fails to unbox if we use a convID that doesn't 1607 // derive from the same triple. 1608 badTriple := chat1.ConversationIDTriple{ 1609 Tlfid: []byte("random non-matching TLF ID"), 1610 } 1611 badConvID := badTriple.ToConversationID([2]byte{0, 0}) 1612 badConv := chat1.Conversation{ 1613 Metadata: chat1.ConversationMetadata{ 1614 ConversationID: badConvID, 1615 }, 1616 } 1617 1618 unboxed, err := boxer.UnboxMessage(boxerContext, boxed, badConv, nil) 1619 require.NoError(t, err) 1620 requireErrorMessage(t, unboxed, "expected a bad convID to fail the unboxing") 1621 }) 1622 } 1623 1624 func remarshalBoxed(t *testing.T, v chat1.MessageBoxed) chat1.MessageBoxed { 1625 // encode 1626 mh := codec.MsgpackHandle{WriteExt: true} 1627 var data []byte 1628 enc := codec.NewEncoderBytes(&data, &mh) 1629 err := enc.Encode(v) 1630 require.NoError(t, err) 1631 1632 // decode 1633 var v2 chat1.MessageBoxed 1634 mh = codec.MsgpackHandle{WriteExt: true} 1635 dec := codec.NewDecoderBytes(data, &mh) 1636 err = dec.Decode(&v2) 1637 require.NoError(t, err) 1638 return v2 1639 } 1640 1641 func TestRemarshalBoxed(t *testing.T) { 1642 outboxID1 := chat1.OutboxID{0xdc, 0x74, 0x6, 0x5d, 0xf9, 0x5f, 0x1c, 0x48} 1643 boxed1 := chat1.MessageBoxed{ 1644 ClientHeader: chat1.MessageClientHeader{ 1645 OutboxID: &outboxID1, 1646 }, 1647 } 1648 1649 boxed2 := remarshalBoxed(t, boxed1) 1650 1651 require.NotEqual(t, chat1.MessageBoxed{}, boxed2, "second shouldn't be zeroed") 1652 require.Equal(t, boxed1.ClientHeader.OutboxID == nil, boxed2.ClientHeader.OutboxID == nil, "obids should have same nility") 1653 1654 if boxed1.ClientHeader.OutboxID == boxed2.ClientHeader.OutboxID { 1655 t.Fatalf("obids should not have same address") 1656 } 1657 1658 require.NotNil(t, boxed1.ClientHeader.OutboxID, "obid1 should not be nil") 1659 require.NotNil(t, boxed2.ClientHeader.OutboxID, "obid2 should not be nil") 1660 1661 require.Equal(t, boxed1.ClientHeader.OutboxID, boxed2.ClientHeader.OutboxID, "obids should have same value") 1662 } 1663 1664 func randomTeamEK() types.EphemeralCryptKey { 1665 randBytes, err := libkb.RandBytes(32) 1666 if err != nil { 1667 panic(err) 1668 } 1669 seed := libkb.MakeByte32(randBytes) 1670 dhKey := (*ephemeral.TeamEKSeed)(&seed).DeriveDHKey() 1671 return keybase1.NewTeamEphemeralKeyWithTeam(keybase1.TeamEk{ 1672 Seed: seed, 1673 Metadata: keybase1.TeamEkMetadata{ 1674 Kid: dhKey.GetKID(), 1675 }, 1676 }) 1677 } 1678 1679 func TestExplodingMessageUnbox(t *testing.T) { 1680 tc, boxer := setupChatTest(t, "exploding") 1681 defer tc.Cleanup() 1682 1683 key := cryptKey(t) 1684 ephemeralKey := randomTeamEK() 1685 text := "hello exploding" 1686 // We need a user for unboxing to work. 1687 u, err := kbtest.CreateAndSignupFakeUser("unbox", tc.G) 1688 require.NoError(t, err) 1689 msg := textMsgWithSender(t, text, gregor1.UID(u.User.GetUID().ToBytes()), chat1.MessageBoxedVersion_V3) 1690 1691 // Set the ephemeral metadata, to indicate that the messages is exploding. 1692 msg.ClientHeader.EphemeralMetadata = &chat1.MsgEphemeralMetadata{ 1693 Lifetime: 99999, 1694 } 1695 1696 // Box it! Note that we pass in the ephemeral/exploding key, and also set 1697 // V3 explicitly. 1698 boxed, err := boxer.box(context.TODO(), msg, key, ephemeralKey, 1699 getSigningKeyPairForTest(t, tc, u), chat1.MessageBoxedVersion_V3, nil) 1700 require.NoError(t, err) 1701 require.Equal(t, chat1.MessageBoxedVersion_V3, boxed.Version) 1702 require.True(t, len(boxed.BodyCiphertext.E) > 0) 1703 1704 // We need to give it a server header for unboxing... 1705 boxed.ServerHeader = &chat1.MessageServerHeader{ 1706 Ctime: gregor1.ToTime(time.Now()), 1707 } 1708 1709 // Unbox it!!! 1710 unboxed, err := boxer.unbox(context.TODO(), boxed, convInfo, 1711 key, ephemeralKey) 1712 require.NoError(t, err) 1713 body := unboxed.MessageBody 1714 typ, err := body.MessageType() 1715 require.NoError(t, err) 1716 require.Equal(t, typ, chat1.MessageType_TEXT) 1717 require.Equal(t, body.Text().Body, text) 1718 require.Nil(t, unboxed.SenderDeviceRevokedAt, "message should not be from revoked device") 1719 require.NotNil(t, unboxed.BodyHash) 1720 require.True(t, unboxed.IsEphemeral()) 1721 require.NotNil(t, unboxed.EphemeralMetadata()) 1722 require.Equal(t, msg.EphemeralMetadata().Lifetime, unboxed.EphemeralMetadata().Lifetime) 1723 } 1724 1725 func TestVersionErrorBasic(t *testing.T) { 1726 // Test basic functionality for parsing the error message 1727 // TODO remove this when we drop parsing 1728 maxMessageBoxedVersion := chat1.MaxMessageBoxedVersion 1729 err := NewMessageBoxedVersionError(maxMessageBoxedVersion) 1730 e := chat1.MessageUnboxedError{ 1731 ErrType: err.ExportType(), 1732 ErrMsg: err.Error(), 1733 } 1734 require.True(t, e.ParseableVersion()) 1735 1736 err2 := NewMessageBoxedVersionError(maxMessageBoxedVersion) 1737 e2 := chat1.MessageUnboxedError{ 1738 ErrType: err2.ExportType(), 1739 ErrMsg: err2.Error(), 1740 VersionKind: err2.VersionKind(), 1741 VersionNumber: err2.VersionNumber(), 1742 IsCritical: err2.IsCritical(), 1743 } 1744 require.True(t, e2.ParseableVersion()) 1745 1746 // not recoverable until we bump the max 1747 err = NewMessageBoxedVersionError(maxMessageBoxedVersion + 1) 1748 e = chat1.MessageUnboxedError{ 1749 ErrType: err.ExportType(), 1750 ErrMsg: err.Error(), 1751 } 1752 require.False(t, e.ParseableVersion()) 1753 1754 chat1.MaxMessageBoxedVersion = maxMessageBoxedVersion + 1 1755 defer func() { chat1.MaxMessageBoxedVersion = maxMessageBoxedVersion }() 1756 require.True(t, e.ParseableVersion()) 1757 } 1758 1759 func TestVersionError(t *testing.T) { 1760 tc, boxer := setupChatTest(t, "box") 1761 defer tc.Cleanup() 1762 1763 // These tests will fail when we update the boxer code to accept new 1764 // maximums. This forces 1765 // chat1/extras.go#MessageUnboxedError.ParseableVersion to stay up to date 1766 // for it's max accepted versions. Vx__ will also have to be updated in the 1767 // checks below 1768 assertErr := func(err types.UnboxingError) { 1769 require.Error(t, err) 1770 typ := err.ExportType() 1771 switch typ { 1772 case chat1.MessageUnboxedErrorType_BADVERSION, chat1.MessageUnboxedErrorType_BADVERSION_CRITICAL: 1773 // pass 1774 default: 1775 require.Fail(t, "invalid error type (%s) for error: %s", typ, err.Error()) 1776 } 1777 1778 e := chat1.MessageUnboxedError{ 1779 ErrType: err.ExportType(), 1780 ErrMsg: err.Error(), 1781 } 1782 require.False(t, e.ParseableVersion()) 1783 } 1784 maxMessageBoxedVersion := chat1.MaxMessageBoxedVersion + 1 1785 maxHeaderVersion := chat1.MaxHeaderVersion + 1 1786 maxBodyVersion := chat1.MaxBodyVersion + 1 1787 1788 key := cryptKey(t) 1789 t.Logf("unbox") 1790 _, err := boxer.unbox(context.Background(), chat1.MessageBoxed{Version: maxMessageBoxedVersion}, 1791 convInfo, key, nil) 1792 assertErr(err) 1793 1794 t.Logf("unversionHeaderMBV2") 1795 hv2 := chat1.HeaderPlaintextUnsupported{} 1796 hp := chat1.MessageServerHeader{} 1797 _, _, err = boxer.unversionHeaderMBV2(context.Background(), &hp, chat1.HeaderPlaintext{Version__: maxHeaderVersion, V2__: &hv2}) 1798 assertErr(err) 1799 1800 t.Logf("unversionBody") 1801 bv3 := chat1.BodyPlaintextUnsupported{} 1802 _, err = boxer.unversionBody(context.Background(), chat1.BodyPlaintext{Version__: maxBodyVersion, V3__: &bv3}) 1803 assertErr(err) 1804 } 1805 1806 func TestMakeOnePairwiseMAC(t *testing.T) { 1807 var mySecret [32]byte 1808 copy(mySecret[:], "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") 1809 var theirSecret [32]byte 1810 copy(theirSecret[:], "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") 1811 1812 myKey, err := libkb.MakeNaclDHKeyPairFromSecret(mySecret) 1813 require.NoError(t, err) 1814 theirKey, err := libkb.MakeNaclDHKeyPairFromSecret(theirSecret) 1815 require.NoError(t, err) 1816 1817 mac := makeOnePairwiseMAC(*myKey.Private, theirKey.Public, []byte("hello world")) 1818 1819 // We used the following Python program to get the expected output of this 1820 // test case. If we tweak something about the MAC, please tweak this Python 1821 // example to match, rather than just pasting in whatever the new answer 1822 // happens to be. This is important for making sure our MAC contains 1823 // exactly the inputs its supposed to. 1824 // 1825 // #! /usr/bin/python3 1826 // from hashlib import sha256 1827 // import hmac 1828 // from nacl.public import PrivateKey, Box # https://github.com/pyca/pynacl 1829 // my_key = PrivateKey(b"a" * 32) 1830 // their_key = PrivateKey(b"b" * 32) 1831 // raw_shared = Box(my_key, their_key.public_key).shared_key() 1832 // context = b"Derived-Chat-Pairwise-HMAC-SHA256-1" 1833 // derived_shared = sha256(context + b"\0" + raw_shared).digest() 1834 // derived_shared = hmac.new(key=raw_shared, msg=context, digestmod=sha256).digest() 1835 // mac = hmac.new(key=derived_shared, msg=b"hello world", digestmod=sha256).digest() 1836 // print(mac.hex()) 1837 expected := "ec6d999902fa02a1e96b0a2d97526999db49843f3e2d961e7a398d53f9a910e0" 1838 1839 require.Equal(t, expected, hex.EncodeToString(mac)) 1840 }