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 }
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") }) }
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 }