Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Initial db table and api for identity keys #2771

Merged
merged 19 commits into from
Sep 25, 2024
Merged

refactor: Initial db table and api for identity keys #2771

merged 19 commits into from
Sep 25, 2024

Conversation

gak
Copy link
Contributor

@gak gak commented Sep 23, 2024

A WIP for #2595

@gak gak requested a review from alecthomas as a code owner September 23, 2024 00:24
@gak gak requested review from a team and deniseli and removed request for a team September 23, 2024 00:24
@ftl-robot ftl-robot mentioned this pull request Sep 23, 2024
@gak gak changed the title wip: initial db table and api for identity keys refactor: Initial db table and api for identity keys Sep 24, 2024
Copy link
Collaborator

@alecthomas alecthomas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

backend/controller/identity/dal/dal.go Show resolved Hide resolved

type EncryptedIdentity = sql.GetOnlyIdentityKeyRow

func (d *DAL) GetOnlyIdentityKey(ctx context.Context) (*EncryptedIdentity, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return a value here rather than a pointer so you're using the same kind consistently (eg. a value is used below).

backend/controller/identity/dal/internal/sql/queries.sql Outdated Show resolved Hide resolved
backend/controller/identity/identity.go Show resolved Hide resolved
backend/controller/identity/identity.go Show resolved Hide resolved
return keyPair, nil
}

const verificationText = "My voice is my passport, verify me."
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😂

return fmt.Errorf("failed to write keyset: %w", err)
}
encryptedIdentityColumn := api.EncryptedIdentityColumn{}
encryptedIdentityColumn.Set(buf.Bytes())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this encrypted or plain text? If it's plain text it shouldn't be in an EncryptedIdentityColumn?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buf.Bytes() is actually encrypted already!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tink strongly prefers (and thus makes it the default) to export / write keysets in encrypted form only. The Write operation on line 162 takes the AEAD primitive and uses it to encrypt the keyset. This encrypted keyset is FLE encrypted again via our EncryptedColumen abtraction

sqlc.yaml Show resolved Hide resolved
@gak gak merged commit 9a4944d into main Sep 25, 2024
91 checks passed
@gak gak deleted the gak/identity branch September 25, 2024 01:32
}

func (d *DAL) CreateOnlyIdentityKey(ctx context.Context, e EncryptedIdentity) error {
if err := d.db.CreateOnlyIdentityKey(ctx, e.Private, e.Public, e.VerifySignature); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be protected from inserting a second identity key?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should already be protected. It's inside a transaction and other instances will fail if there are two rows anyway.


CREATE TABLE identity_keys (
id BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
private encrypted_identity NOT NULL,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is KMS encrypted right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is encrypted the same way as timeline/async. The root key is encrypted by KMS and left unencrypted in memory. The id key is encrypted using a subkey.

private encrypted_identity NOT NULL,
public BYTEA NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT (NOW() AT TIME ZONE 'utc'),
verify_signature BYTEA NOT NULL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this signature?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a sanity check signature done on the first generation of the key. It is checked on startup to make sure the key wasn't changed. Mostly to stop bugs and users doing silly things.


_, err = s.dal.GetOnlyIdentityKey(ctx)
if err != nil {
if !errors.Is(err, libdal.ErrNotFound) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ensureIdentity generates a new identity if one isn't found in the database. This is keyed off the error returned from GetONlyIdentityKey.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it should only trigger when there are no rows from inside GetOnlyIdentityKey.

}

// For total sanity, verify immediately
if err = verifier.Verify(*signed); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ye with little faith ;)

return fmt.Errorf("failed to write keyset: %w", err)
}
encryptedIdentityColumn := api.EncryptedIdentityColumn{}
encryptedIdentityColumn.Set(buf.Bytes())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tink strongly prefers (and thus makes it the default) to export / write keysets in encrypted form only. The Write operation on line 162 takes the AEAD primitive and uses it to encrypt the keyset. This encrypted keyset is FLE encrypted again via our EncryptedColumen abtraction

backend/controller/identity/identity.go Show resolved Hide resolved

var _ Signer = &TinkSigner{}

type TinkSigner struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why wrap the tink abstractions again?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally it was because of having a NoOpEncryptor equivalent, but now probably will be dropping NoOp as well as this abstraction.

@gak gak mentioned this pull request Sep 30, 2024
gak added a commit that referenced this pull request Oct 1, 2024
continuation from #2771 for #2595

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Alec Thomas <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants