Example #1
0
File: crypto.go Project: logan/heim
func EncryptMessage(msg *Message, keyID string, key *security.ManagedKey) error {
	if key == nil {
		return security.ErrInvalidKey
	}
	if key.Encrypted() {
		return security.ErrKeyMustBeDecrypted
	}

	payload := &Message{
		Sender:  msg.Sender,
		Content: msg.Content,
	}
	plaintext, err := json.Marshal(payload)
	if err != nil {
		return err
	}

	// TODO: incorporate last edit ID into nonce
	nonce := []byte(msg.ID.String())
	data := []byte(msg.Sender.ID)

	digest, ciphertext, err := security.EncryptGCM(key, nonce, plaintext, data)
	if err != nil {
		return fmt.Errorf("message encrypt: %s", err)
	}

	digestStr := base64.URLEncoding.EncodeToString(digest)
	cipherStr := base64.URLEncoding.EncodeToString(ciphertext)

	msg.Sender = SessionView{
		IdentityView: IdentityView{ID: msg.Sender.ID},
		SessionID:    msg.Sender.SessionID,
	}
	msg.Content = digestStr + "/" + cipherStr
	msg.EncryptionKeyID = "v1/" + keyID
	return nil
}
Example #2
0
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")
	})
}
Example #3
0
func (b *AccountManagerBinding) GenerateOTP(ctx scope.Context, heim *proto.Heim, kms security.KMS, account proto.Account) (*proto.OTP, error) {
	encryptedKey, err := kms.GenerateEncryptedKey(OTPKeyType, "account", account.ID().String())
	if err != nil {
		return nil, err
	}

	key := encryptedKey.Clone()
	if err := kms.DecryptKey(&key); err != nil {
		return nil, err
	}

	iv, err := kms.GenerateNonce(OTPKeyType.BlockSize())
	if err != nil {
		return nil, err
	}

	t, err := b.DbMap.Begin()
	if err != nil {
		return nil, err
	}

	rawOTP, err := b.getRawOTP(t, account.ID())
	if err != nil && err != proto.ErrOTPNotEnrolled {
		rollback(ctx, t)
		return nil, err
	}
	if err == nil {
		if rawOTP.Validated {
			rollback(ctx, t)
			return nil, proto.ErrOTPAlreadyEnrolled
		}
		row := &OTP{AccountID: account.ID().String()}
		if _, err := t.Delete(row); err != nil {
			rollback(ctx, t)
			return nil, err
		}
	}

	otp, err := heim.NewOTP(account)
	if err != nil {
		rollback(ctx, t)
		return nil, err
	}

	digest, encryptedURI, err := security.EncryptGCM(&key, iv, []byte(otp.URI), nil)
	if err != nil {
		rollback(ctx, t)
		return nil, err
	}

	row := &OTP{
		AccountID:    account.ID().String(),
		IV:           iv,
		EncryptedKey: encryptedKey.Ciphertext,
		Digest:       digest,
		EncryptedURI: encryptedURI,
	}
	if err := t.Insert(row); err != nil {
		// TODO: this could fail in the case of a race condition
		// by the time that matters we should be on postgres 9.5 and using a proper upsert
		rollback(ctx, t)
		return nil, err
	}

	if err := t.Commit(); err != nil {
		return nil, err
	}

	return otp, nil
}