Beispiel #1
0
func (m *accountManager) GrantStaff(
	ctx scope.Context, accountID snowflake.Snowflake, kmsCred security.KMSCredential) error {

	m.b.Lock()
	defer m.b.Unlock()

	account, ok := m.b.accounts[accountID]
	if !ok {
		return proto.ErrAccountNotFound
	}
	memAcc := account.(*memAccount)

	kms := kmsCred.KMS()
	key := memAcc.sec.SystemKey.Clone()
	if err := kms.DecryptKey(&key); err != nil {
		return err
	}

	nonce, err := kms.GenerateNonce(key.KeyType.BlockSize())
	if err != nil {
		return err
	}

	capability, err := security.GrantSharedSecretCapability(&key, nonce, kmsCred.KMSType(), kmsCred)
	if err != nil {
		return err
	}

	memAcc.staffCapability = capability
	return nil
}
Beispiel #2
0
func (gs *GrantManager) GrantToPasscode(
	ctx scope.Context, manager Account, managerKey *security.ManagedKey, passcode string) error {

	_, public, private, err := gs.Authority(ctx, manager, managerKey)
	if err != nil {
		return err
	}

	c, err := security.GrantSharedSecretCapability(
		security.KeyFromPasscode([]byte(passcode), gs.SubjectNonce, security.AES128),
		gs.SubjectNonce, public, private)
	if err != nil {
		return err
	}

	return gs.Capabilities.Save(ctx, nil, c)
}
Beispiel #3
0
func (b *AccountManagerBinding) GrantStaff(
	ctx scope.Context, accountID snowflake.Snowflake, kmsCred security.KMSCredential) error {

	// Look up the target account's (system) encrypted client key. This is
	// not part of the transaction, because we want to interact with KMS
	// before we proceed. That should be fine, since this is an infrequently
	// used action.
	var row struct {
		EncryptedClientKey []byte `db:"encrypted_system_key"`
		Nonce              []byte `db:"nonce"`
	}
	err := b.DbMap.SelectOne(
		&row, "SELECT encrypted_system_key, nonce FROM account WHERE id = $1", accountID.String())
	if err != nil {
		if err == sql.ErrNoRows {
			return proto.ErrAccountNotFound
		}
		return err
	}

	// Use kmsCred to obtain kms and decrypt the client's key.
	kms := kmsCred.KMS()
	clientKey := &security.ManagedKey{
		KeyType:      proto.ClientKeyType,
		Ciphertext:   row.EncryptedClientKey,
		ContextKey:   "nonce",
		ContextValue: base64.URLEncoding.EncodeToString(row.Nonce),
	}
	if err := kms.DecryptKey(clientKey); err != nil {
		return err
	}

	// Grant staff capability. This involves marshalling kmsCred to JSON and
	// encrypting it with the client key.
	nonce, err := kms.GenerateNonce(clientKey.KeyType.BlockSize())
	if err != nil {
		return err
	}

	capability, err := security.GrantSharedSecretCapability(clientKey, nonce, kmsCred.KMSType(), kmsCred)
	if err != nil {
		return err
	}

	// Store capability and update account table.
	t, err := b.DbMap.Begin()
	if err != nil {
		return err
	}

	rollback := func() {
		if err := t.Rollback(); err != nil {
			backend.Logger(ctx).Printf("rollback error: %s", err)
		}
	}

	dbCap := &Capability{
		ID:                   capability.CapabilityID(),
		NonceBytes:           capability.Nonce(),
		EncryptedPrivateData: capability.EncryptedPayload(),
		PublicData:           capability.PublicPayload(),
	}
	if err := t.Insert(dbCap); err != nil {
		rollback()
		return err
	}

	result, err := t.Exec(
		"UPDATE account SET staff_capability_id = $2 WHERE id = $1",
		accountID.String(), capability.CapabilityID())
	if err != nil {
		rollback()
		return err
	}
	n, err := result.RowsAffected()
	if err != nil {
		rollback()
		return err
	}
	if n != 1 {
		rollback()
		return proto.ErrAccountNotFound
	}

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

	return nil
}
Beispiel #4
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")
	})
}