예제 #1
0
func (kc *KMSConfig) local() (security.KMS, error) {
	f, err := os.Open(kc.AES256.KeyFile)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	keySize := security.AES256.KeySize()
	fi, err := f.Stat()
	if err != nil {
		return nil, err
	}
	if fi.Size() != int64(keySize) {
		return nil, fmt.Errorf("key must be exactly %d bytes in size", keySize)
	}

	masterKey, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}

	kms := security.LocalKMS()
	kms.SetMasterKey(masterKey)
	return kms, nil
}
예제 #2
0
파일: room_test.go 프로젝트: logan/heim
func TestRoomPresence(t *testing.T) {
	userA := newSession("A", "A1", "ip1")
	userA2 := newSession("A", "A2", "ip2")
	userB := newSession("B", "B1", "ip3")

	ctx := scope.New()
	kms := security.LocalKMS()
	kms.SetMasterKey(make([]byte, security.AES256.KeySize()))

	roomp, err := NewRoom(ctx, kms, false, "test", "testver")
	if err != nil {
		t.Fatal(err)
	}

	room := roomp.(*memRoom)

	client := &proto.Client{Agent: &proto.Agent{}}
	client.FromRequest(ctx, &http.Request{})

	Convey("First join", t, func() {
		_, err := room.Join(ctx, userA)
		So(err, ShouldBeNil)
		So(room.identities, ShouldResemble,
			map[proto.UserID]proto.Identity{"A": userA.Identity()})
		So(room.live, ShouldResemble,
			map[proto.UserID][]proto.Session{"A": []proto.Session{userA}})
	})

	Convey("Second join", t, func() {
		_, err := room.Join(ctx, userB)
		So(err, ShouldBeNil)
		So(room.identities["B"], ShouldResemble, userB.Identity())
		So(room.live["B"], ShouldResemble, []proto.Session{userB})
	})

	Convey("Duplicate join", t, func() {
		_, err := room.Join(ctx, userA2)
		So(err, ShouldBeNil)
		So(room.live["A"], ShouldResemble, []proto.Session{userA, userA2})
	})

	Convey("Deduplicate part", t, func() {
		So(room.Part(ctx, userA), ShouldBeNil)
		So(room.identities["A"], ShouldResemble, userA.Identity())
		So(room.live["A"], ShouldResemble, []proto.Session{userA2})
	})

	Convey("More parts", t, func() {
		So(room.Part(ctx, userA2), ShouldBeNil)
		So(room.identities["A"], ShouldBeNil)
		So(room.live["A"], ShouldBeNil)
		So(room.Part(ctx, userB), ShouldBeNil)
		So(room.identities["B"], ShouldBeNil)
		So(room.live["B"], ShouldBeNil)
	})
}
예제 #3
0
파일: account_test.go 프로젝트: logan/heim
func TestNewAccountSecurity(t *testing.T) {
	kms := security.LocalKMS()
	kms.SetMasterKey(make([]byte, security.AES256.KeySize()))

	unlock := func(sec *AccountSecurity, password string) (*security.ManagedKeyPair, error) {
		return sec.Unlock(security.KeyFromPasscode([]byte(password), sec.Nonce, sec.UserKey.KeyType))
	}

	Convey("Encryption and decryption of generated keys", t, func() {
		sec, clientKey, err := NewAccountSecurity(kms, "hunter2")
		So(err, ShouldBeNil)
		So(sec.SystemKey.Encrypted(), ShouldBeTrue)
		So(sec.UserKey.Encrypted(), ShouldBeTrue)
		So(sec.KeyPair.Encrypted(), ShouldBeTrue)
		So(len(sec.Nonce), ShouldEqual, sec.KeyPair.NonceSize())
		So(clientKey.Encrypted(), ShouldBeFalse)

		kek := sec.SystemKey.Clone()
		So(kms.DecryptKey(&kek), ShouldBeNil)

		skp := sec.KeyPair.Clone()
		So(skp.Decrypt(&kek), ShouldBeNil)

		kp, err := unlock(sec, "")
		So(err, ShouldEqual, ErrAccessDenied)
		So(kp, ShouldBeNil)

		kp, err = unlock(sec, "hunter2")
		So(err, ShouldBeNil)
		So(kp.PrivateKey, ShouldResemble, skp.PrivateKey)
	})

	Convey("Password resets", t, func() {
		sec, _, err := NewAccountSecurity(kms, "hunter2")
		So(err, ShouldBeNil)

		nsec, err := sec.ResetPassword(kms, "hunter3")
		So(err, ShouldBeNil)

		skp, err := unlock(sec, "hunter2")
		So(err, ShouldBeNil)

		_, err = unlock(nsec, "hunter2")
		So(err, ShouldEqual, ErrAccessDenied)

		kp, err := unlock(nsec, "hunter3")
		So(err, ShouldBeNil)
		So(kp.PrivateKey, ShouldResemble, skp.PrivateKey)
	})
}
예제 #4
0
파일: room_test.go 프로젝트: logan/heim
func TestRoomBroadcast(t *testing.T) {
	userA := newSession("A", "A1", "ip1")
	userB := newSession("B", "B1", "ip2")
	userC := newSession("C", "C1", "ip3")

	ctx := scope.New()
	kms := security.LocalKMS()
	kms.SetMasterKey(make([]byte, security.AES256.KeySize()))

	roomp, err := NewRoom(ctx, kms, false, "test", "testver")
	if err != nil {
		t.Fatal(err)
	}
	room := roomp.(*memRoom)

	client := &proto.Client{Agent: &proto.Agent{}}
	client.FromRequest(ctx, &http.Request{})

	Convey("Setup", t, func() {
		_, err := room.Join(ctx, userA)
		So(err, ShouldBeNil)
		_, err = room.Join(ctx, userB)
		So(err, ShouldBeNil)
		_, err = room.Join(ctx, userC)
		So(err, ShouldBeNil)
	})

	Convey("Multiple exclude", t, func() {
		So(room.broadcast(ctx, proto.SendType, proto.Message{Content: "1"}, userA, userB),
			ShouldBeNil)
		So(userA.history, ShouldResemble,
			[]message{
				{
					cmdType: proto.JoinEventType,
					payload: &proto.PresenceEvent{
						SessionID:    "B",
						IdentityView: proto.IdentityView{ID: "B"},
					},
				},
				{
					cmdType: proto.JoinEventType,
					payload: &proto.PresenceEvent{
						SessionID:    "C",
						IdentityView: proto.IdentityView{ID: "C"},
					},
				},
			})
		So(userB.history, ShouldResemble,
			[]message{
				{
					cmdType: proto.JoinEventType,
					payload: &proto.PresenceEvent{
						SessionID:    "C",
						IdentityView: proto.IdentityView{ID: "C"},
					},
				},
			})
		So(userC.history, ShouldResemble,
			[]message{{cmdType: proto.SendEventType, payload: proto.Message{Content: "1"}}})
	})

	Convey("No exclude", t, func() {
		So(room.broadcast(ctx, proto.SendType, proto.Message{Content: "2"}), ShouldBeNil)
		So(userA.history, ShouldResemble,
			[]message{
				{
					cmdType: proto.JoinEventType,
					payload: &proto.PresenceEvent{
						SessionID:    "B",
						IdentityView: proto.IdentityView{ID: "B"},
					},
				},
				{
					cmdType: proto.JoinEventType,
					payload: &proto.PresenceEvent{
						SessionID:    "C",
						IdentityView: proto.IdentityView{ID: "C"},
					},
				},
				{
					cmdType: proto.SendEventType,
					payload: proto.Message{Content: "2"},
				},
			})
		So(userB.history, ShouldResemble,
			[]message{
				{
					cmdType: proto.JoinEventType,
					payload: &proto.PresenceEvent{
						SessionID:    "C",
						IdentityView: proto.IdentityView{ID: "C"},
					},
				},
				{cmdType: proto.SendEventType, payload: proto.Message{Content: "2"}},
			})
		So(userC.history, ShouldResemble,
			[]message{
				{cmdType: proto.SendEventType, payload: proto.Message{Content: "1"}},
				{cmdType: proto.SendEventType, payload: proto.Message{Content: "2"}},
			})
	})
}
예제 #5
0
파일: message_test.go 프로젝트: logan/heim
func TestDeleteMessage(t *testing.T) {
	ctx := scope.New()
	kms := security.LocalKMS()
	kms.SetMasterKey(make([]byte, security.AES256.KeySize()))
	session := mock.TestSession("test", "T1", "ip1")

	sendMessage := func(room proto.Room) (proto.Message, error) {
		msg := proto.Message{
			Sender: proto.SessionView{
				SessionID:    "test",
				IdentityView: proto.IdentityView{ID: "test"},
			},
			Content: "test",
		}

		if managedRoom, ok := room.(proto.ManagedRoom); ok {
			key, err := managedRoom.MessageKey(ctx)
			if err != nil {
				return proto.Message{}, err
			}

			if key != nil {
				mkey := key.ManagedKey()
				if err := kms.DecryptKey(&mkey); err != nil {
					return proto.Message{}, err
				}
				if err := proto.EncryptMessage(&msg, key.KeyID(), &mkey); err != nil {
					return proto.Message{}, err
				}
			}
		}

		return room.Send(ctx, session, msg)
	}

	Convey("Delete message in public room", t, func() {
		ctrl := &Controller{
			backend: &mock.TestBackend{},
			kms:     kms,
		}
		term := &testTerm{}

		public, err := ctrl.backend.CreateRoom(ctx, kms, false, "public")
		So(err, ShouldBeNil)
		sent, err := sendMessage(public)
		So(err, ShouldBeNil)

		runCommand(ctx, ctrl, "delete-message", term, []string{"public:" + sent.ID.String()})

		deleted, err := public.GetMessage(ctx, sent.ID)
		So(err, ShouldBeNil)
		So(time.Time(deleted.Deleted).IsZero(), ShouldBeFalse)
	})

	Convey("Delete message in private room", t, func() {
		ctrl := &Controller{
			backend: &mock.TestBackend{},
			kms:     kms,
		}
		term := &testTerm{}

		private, err := ctrl.backend.CreateRoom(ctx, kms, true, "private")
		So(err, ShouldBeNil)
		runCommand(ctx, ctrl, "lock-room", term, []string{"private"})

		sent, err := sendMessage(private)
		So(err, ShouldBeNil)

		runCommand(ctx, ctrl, "delete-message", term, []string{"private:" + sent.ID.String()})

		deleted, err := private.GetMessage(ctx, sent.ID)
		So(err, ShouldBeNil)
		So(time.Time(deleted.Deleted).IsZero(), ShouldBeFalse)
	})
}
예제 #6
0
파일: etcd_test.go 프로젝트: logan/heim
func TestEtcdCluster(t *testing.T) {
	s, err := clustertest.StartEtcd()
	if err != nil {
		t.Fatal(err)
	}
	if s == nil {
		t.Skipf("etcd not in PATH, skipping tests")
	}
	defer s.Shutdown()

	Convey("Observe peer departure", t, func() {
		a := s.Join("/departure", "a", "0")
		// no defer a.Part() because we'll do that explicitly
		b := s.Join("/departure", "b", "0")
		defer b.Part()

		So(<-a.Watch(), ShouldResemble, &cluster.PeerJoinedEvent{cluster.PeerDesc{ID: "b", Era: "0"}})
		a.Part()
		So(<-b.Watch(), ShouldResemble, &cluster.PeerLostEvent{cluster.PeerDesc{ID: "a"}})
	})

	Convey("Observe initial peers upon joining", t, func() {
		a := s.Join("/initial", "a", "0")
		defer a.Part()
		So(a.Peers(), ShouldResemble,
			[]cluster.PeerDesc{
				{ID: "a", Era: "0"},
			})

		b := s.Join("/initial", "b", "0")
		defer b.Part()
		So(b.Peers(), ShouldResemble,
			[]cluster.PeerDesc{
				{ID: "a", Era: "0"},
				{ID: "b", Era: "0"},
			})
	})

	Convey("Updates are seen", t, func() {
		a := s.Join("/updates", "a", "0")
		defer a.Part()
		b := s.Join("/updates", "b", "0")
		defer b.Part()

		b.Update(&cluster.PeerDesc{ID: "b", Era: "1"})
		b.Update(&cluster.PeerDesc{ID: "b", Era: "2"})
		So(<-a.Watch(), ShouldResemble, &cluster.PeerJoinedEvent{cluster.PeerDesc{ID: "b", Era: "0"}})
		So(<-a.Watch(), ShouldResemble, &cluster.PeerAliveEvent{cluster.PeerDesc{ID: "b", Era: "1"}})
		So(<-a.Watch(), ShouldResemble, &cluster.PeerAliveEvent{cluster.PeerDesc{ID: "b", Era: "2"}})
	})

	Convey("Secrets are created if necessary", t, func() {
		kms := security.LocalKMS()
		a := s.Join("/secrets1", "a", "0")
		defer a.Part()

		secret, err := a.GetSecret(kms, "test1", 16)
		So(err, ShouldBeNil)
		So(len(secret), ShouldEqual, 16)

		secretCopy, err := a.GetSecret(kms, "test1", 16)
		So(err, ShouldBeNil)
		So(string(secretCopy), ShouldEqual, string(secret))
	})

	Convey("Race to create secret is conceded gracefully", t, func() {
		kms := &syncKMS{
			KMS: security.LocalKMS(),
			c:   make(chan struct{}),
		}

		a := s.Join("/secrets1", "a", "0")
		defer a.Part()

		sc := make(chan []byte)
		errc := make(chan error)
		go func() {
			s, err := a.GetSecret(kms, "test2", 16)
			errc <- err
			sc <- s
		}()

		// Synchronize with secret generation.
		<-kms.c

		// Set the secret before releasing the goroutine.
		secret, err := a.GetSecret(kms.KMS, "test2", 16)
		So(err, ShouldBeNil)

		// Release the goroutine and verify it gets the secret that was set.
		kms.c <- struct{}{}
		So(<-errc, ShouldBeNil)
		So(string(<-sc), ShouldEqual, string(secret))
	})
}
예제 #7
0
파일: grants_test.go 프로젝트: logan/heim
func TestGrants(t *testing.T) {
	Convey("Grant a capability on a room", t, func() {
		kms := security.LocalKMS()
		kms.SetMasterKey(make([]byte, security.AES256.KeySize()))
		ctx := scope.New()
		client := &proto.Client{Agent: &proto.Agent{}}
		client.FromRequest(ctx, &http.Request{})
		backend := &mock.TestBackend{}
		room, err := backend.CreateRoom(ctx, kms, true, "test")
		So(err, ShouldBeNil)

		rkey, err := room.MessageKey(ctx)
		So(err, ShouldBeNil)
		mkey := rkey.ManagedKey()
		So(kms.DecryptKey(&mkey), ShouldBeNil)

		// Sign in as alice and send an encrypted message with aliceSendTime
		// as the nonce.
		aliceSendTime := time.Now()
		msgNonce := []byte(snowflake.NewFromTime(aliceSendTime).String())

		aliceKey := &security.ManagedKey{
			KeyType:   security.AES256,
			Plaintext: make([]byte, security.AES256.KeySize()),
		}

		grant, err := security.GrantSharedSecretCapability(aliceKey, rkey.Nonce(), nil, mkey.Plaintext)
		So(err, ShouldBeNil)

		alice := mock.TestSession("Alice", "A1", "ip1")
		_, err = room.Join(ctx, alice)
		So(err, ShouldBeNil)

		msg := proto.Message{
			ID:       snowflake.NewFromTime(aliceSendTime),
			UnixTime: proto.Time(aliceSendTime),
			Content:  "hello",
		}

		iv, err := base64.URLEncoding.DecodeString(grant.CapabilityID())
		So(err, ShouldBeNil)
		payload := grant.EncryptedPayload()
		So(aliceKey.BlockCrypt(iv, aliceKey.Plaintext, payload, false), ShouldBeNil)
		key := &security.ManagedKey{
			KeyType: security.AES128,
		}
		So(json.Unmarshal(aliceKey.Unpad(payload), &key.Plaintext), ShouldBeNil)

		digest, ciphertext, err := security.EncryptGCM(
			key, msgNonce, []byte(msg.Content), []byte("Alice"))
		So(err, ShouldBeNil)

		digestStr := base64.URLEncoding.EncodeToString(digest)
		cipherStr := base64.URLEncoding.EncodeToString(ciphertext)
		msg.Content = digestStr + "/" + cipherStr
		_, err = room.Send(ctx, alice, msg)
		So(err, ShouldBeNil)

		// Now sign in as bob and decrypt the message.
		bobKey := &security.ManagedKey{
			KeyType:   security.AES256,
			Plaintext: make([]byte, security.AES256.KeySize()),
		}
		//bobKey.Plaintext[0] = 1
		grant, err = security.GrantSharedSecretCapability(bobKey, rkey.Nonce(), nil, mkey.Plaintext)
		So(err, ShouldBeNil)

		iv, err = base64.URLEncoding.DecodeString(grant.CapabilityID())
		So(err, ShouldBeNil)
		payload = grant.EncryptedPayload()
		So(bobKey.BlockCrypt(iv, bobKey.Plaintext, payload, false), ShouldBeNil)
		key = &security.ManagedKey{
			KeyType: security.AES128,
		}
		So(json.Unmarshal(bobKey.Unpad(payload), &key.Plaintext), ShouldBeNil)

		bob := mock.TestSession("Bob", "B1", "ip2")
		_, err = room.Join(ctx, bob)
		So(err, ShouldBeNil)
		log, err := room.Latest(ctx, 1, 0)
		So(err, ShouldBeNil)
		So(len(log), ShouldEqual, 1)
		msg = log[0]

		parts := strings.Split(msg.Content, "/")
		So(len(parts), ShouldEqual, 2)
		digest, err = base64.URLEncoding.DecodeString(parts[0])
		So(err, ShouldBeNil)
		ciphertext, err = base64.URLEncoding.DecodeString(parts[1])
		So(err, ShouldBeNil)

		plaintext, err := security.DecryptGCM(key, msgNonce, digest, ciphertext, []byte("Alice"))
		So(err, ShouldBeNil)
		So(string(plaintext), ShouldEqual, "hello")
	})
}