func (b *AccountManagerBinding) getOTP(db gorp.SqlExecutor, kms security.KMS, accountID snowflake.Snowflake) (*proto.OTP, error) { encryptedOTP, err := b.getRawOTP(db, accountID) if err != nil { return nil, err } key := security.ManagedKey{ KeyType: OTPKeyType, IV: encryptedOTP.IV, Ciphertext: encryptedOTP.EncryptedKey, ContextKey: "account", ContextValue: accountID.String(), } if err := kms.DecryptKey(&key); err != nil { return nil, err } uriBytes, err := security.DecryptGCM(&key, encryptedOTP.IV, encryptedOTP.Digest, encryptedOTP.EncryptedURI, nil) if err != nil { return nil, err } otp := &proto.OTP{ URI: string(uriBytes), Validated: encryptedOTP.Validated, } return otp, nil }
func DecryptMessage(msg Message, auths map[string]*security.ManagedKey) (Message, error) { if msg.EncryptionKeyID == "" { return msg, nil } keyVersion := 0 keyID := msg.EncryptionKeyID if strings.HasPrefix(keyID, "v1") { keyVersion = 1 keyID = keyID[3:] } auth, ok := auths[keyID] if !ok { return msg, nil } if auth.Encrypted() { return msg, security.ErrKeyMustBeDecrypted } parts := strings.Split(msg.Content, "/") if len(parts) != 2 { fmt.Printf("bad content: %s\n", msg.Content) return msg, fmt.Errorf("message corrupted") } digest, err := base64.URLEncoding.DecodeString(parts[0]) if err != nil { return msg, err } ciphertext, err := base64.URLEncoding.DecodeString(parts[1]) if err != nil { return msg, err } plaintext, err := security.DecryptGCM( auth, []byte(msg.ID.String()), digest, ciphertext, []byte(msg.Sender.ID)) if err != nil { return msg, fmt.Errorf("message decrypt: %s", err) } switch keyVersion { case 0: // unversioned keys briefly existed when private rooms first rolled out msg.Content = string(plaintext) case 1: // v1 keys embed an encrypted partial message, hiding the sender and content var payload Message if err := json.Unmarshal(plaintext, &payload); err != nil { return msg, fmt.Errorf("message corrupted: %s", err) } msg.Sender = payload.Sender msg.Content = payload.Content } return msg, 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") }) }