Add user-to-user encryption engine with ECDH key exchange and AES-256-GCM

Implements the complete user engine for multi-recipient envelope encryption:
- ECDH key agreement (X25519, P-256, P-384) with HKDF-derived wrapping keys
- Per-message random DEK wrapped individually for each recipient
- 9 operations: register, provision, get-public-key, list-users, encrypt,
  decrypt, re-encrypt, rotate-key, delete-user
- Auto-provisioning of sender and recipients on encrypt
- Role-based authorization (admin-only provision/delete, user-only decrypt)
- gRPC UserService with proto definitions and REST API routes
- 16 comprehensive tests covering lifecycle, crypto roundtrips, multi-recipient,
  key rotation, auth enforcement, and algorithm variants

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-16 19:44:11 -07:00
parent ac4577f778
commit be3b9d7fe0
10 changed files with 4004 additions and 1 deletions

View File

@@ -0,0 +1,143 @@
syntax = "proto3";
package metacrypt.v2;
option go_package = "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2;metacryptv2";
// UserService provides typed, authenticated access to user-to-user encryption
// engine operations. All RPCs require the service to be unsealed and
// authentication.
service UserService {
// Register self-registers the caller, creating a keypair. No-op if exists.
rpc Register(UserRegisterRequest) returns (UserRegisterResponse);
// Provision creates a keypair for a given username. Admin only.
rpc Provision(UserProvisionRequest) returns (UserProvisionResponse);
// GetPublicKey returns the public key for a given username.
rpc GetPublicKey(UserGetPublicKeyRequest) returns (UserGetPublicKeyResponse);
// ListUsers returns all registered usernames.
rpc ListUsers(UserListUsersRequest) returns (UserListUsersResponse);
// Encrypt encrypts plaintext for one or more recipients.
rpc Encrypt(UserEncryptRequest) returns (UserEncryptResponse);
// Decrypt decrypts an envelope addressed to the caller.
rpc Decrypt(UserDecryptRequest) returns (UserDecryptResponse);
// ReEncrypt decrypts and re-encrypts an envelope with current keys.
rpc ReEncrypt(UserReEncryptRequest) returns (UserReEncryptResponse);
// RotateKey generates a new keypair for the caller, replacing the old one.
rpc RotateKey(UserRotateKeyRequest) returns (UserRotateKeyResponse);
// DeleteUser removes a user's keys. Admin only.
rpc DeleteUser(UserDeleteUserRequest) returns (UserDeleteUserResponse);
}
// --- Register ---
message UserRegisterRequest {
string mount = 1;
}
message UserRegisterResponse {
string username = 1;
string public_key = 2;
string algorithm = 3;
}
// --- Provision ---
message UserProvisionRequest {
string mount = 1;
string username = 2;
}
message UserProvisionResponse {
string username = 1;
string public_key = 2;
string algorithm = 3;
}
// --- GetPublicKey ---
message UserGetPublicKeyRequest {
string mount = 1;
string username = 2;
}
message UserGetPublicKeyResponse {
string username = 1;
string public_key = 2;
string algorithm = 3;
}
// --- ListUsers ---
message UserListUsersRequest {
string mount = 1;
}
message UserListUsersResponse {
repeated string users = 1;
}
// --- Encrypt ---
message UserEncryptRequest {
string mount = 1;
string plaintext = 2;
string metadata = 3;
repeated string recipients = 4;
}
message UserEncryptResponse {
string envelope = 1;
}
// --- Decrypt ---
message UserDecryptRequest {
string mount = 1;
string envelope = 2;
}
message UserDecryptResponse {
string plaintext = 1;
string sender = 2;
string metadata = 3;
}
// --- ReEncrypt ---
message UserReEncryptRequest {
string mount = 1;
string envelope = 2;
}
message UserReEncryptResponse {
string envelope = 1;
}
// --- RotateKey ---
message UserRotateKeyRequest {
string mount = 1;
}
message UserRotateKeyResponse {
string username = 1;
string public_key = 2;
string algorithm = 3;
}
// --- DeleteUser ---
message UserDeleteUserRequest {
string mount = 1;
string username = 2;
}
message UserDeleteUserResponse {}