feat: establish v0.70 platform milestone
ci / verify (push) Canceled after 0s

This commit is contained in:
Codex Local
2026-08-29 21:56:51 +08:00
parent d97bbf7cf5
commit e652516147
133 changed files with 12907 additions and 801 deletions
+178
View File
@@ -0,0 +1,178 @@
package deviceauth
import (
"context"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
)
const databaseCredentialKeyBytes = 32
var ErrCredentialAlreadyExists = errors.New("active device credential already exists")
type DatabaseSecretResolver struct {
pool *pgxpool.Pool
aead cipher.AEAD
}
func NewDatabaseSecretResolver(pool *pgxpool.Pool, masterKeyHex string) (*DatabaseSecretResolver, error) {
if pool == nil {
return nil, errors.New("device credential database is required")
}
key, err := hex.DecodeString(strings.TrimSpace(masterKeyHex))
if err != nil || len(key) != databaseCredentialKeyBytes {
return nil, errors.New("device credential master key must be 32 bytes encoded as 64 hexadecimal characters")
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("create device credential cipher: %w", err)
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("create device credential AEAD: %w", err)
}
return &DatabaseSecretResolver{pool: pool, aead: aead}, nil
}
func (r *DatabaseSecretResolver) Resolve(ctx context.Context, reference string) ([]byte, error) {
var encrypted, nonce []byte
var version int64
err := r.pool.QueryRow(ctx, `
SELECT encrypted_secret,nonce,key_version
FROM device_credential
WHERE credential_ref=$1 AND status='active'`, strings.TrimSpace(reference),
).Scan(&encrypted, &nonce, &version)
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrCredentialInvalid
}
if err != nil {
return nil, fmt.Errorf("read device credential: %w", err)
}
secret, err := r.aead.Open(nil, nonce, encrypted, credentialAAD(reference, version))
if err != nil || len(secret) < 32 {
return nil, ErrCredentialInvalid
}
return secret, nil
}
// ImportMissingLegacyCredentials moves existing deployment credentials into
// the database without replacing an already imported or rotated credential.
func (r *DatabaseSecretResolver) ImportMissingLegacyCredentials(ctx context.Context, credentials map[string][]byte) error {
for reference, secret := range credentials {
if len(secret) < 32 {
return ErrCredentialInvalid
}
var deviceID, tenantID string
err := r.pool.QueryRow(ctx, `SELECT id::text,tenant_id::text FROM device WHERE credential_ref=$1`, reference).Scan(&deviceID, &tenantID)
if errors.Is(err, pgx.ErrNoRows) {
continue
}
if err != nil {
return fmt.Errorf("resolve legacy device credential %s: %w", reference, err)
}
nonce := make([]byte, r.aead.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return fmt.Errorf("generate device credential nonce: %w", err)
}
const version int64 = 1
encrypted := r.aead.Seal(nil, nonce, secret, credentialAAD(reference, version))
if _, err := r.pool.Exec(ctx, `
INSERT INTO device_credential(device_id,tenant_id,credential_ref,key_version,encrypted_secret,nonce)
VALUES($1::uuid,$2::uuid,$3,$4,$5,$6)
ON CONFLICT (device_id) DO NOTHING`, deviceID, tenantID, reference, version, encrypted, nonce); err != nil {
return fmt.Errorf("import legacy device credential %s: %w", reference, err)
}
}
return nil
}
func (r *DatabaseSecretResolver) Provision(ctx context.Context, devicePublicID string, secret []byte, rotate bool) (int64, error) {
if len(secret) < 32 {
return 0, ErrCredentialInvalid
}
tx, err := r.pool.Begin(ctx)
if err != nil {
return 0, fmt.Errorf("begin device credential provisioning: %w", err)
}
defer tx.Rollback(ctx)
version, err := r.ProvisionTx(ctx, tx, devicePublicID, secret, rotate)
if err != nil {
return 0, err
}
if err := tx.Commit(ctx); err != nil {
return 0, fmt.Errorf("commit device credential provisioning: %w", err)
}
return version, nil
}
// ProvisionTx stores a credential inside a caller-owned transaction so device
// registration and first credential issuance can commit atomically.
func (r *DatabaseSecretResolver) ProvisionTx(ctx context.Context, tx pgx.Tx, devicePublicID string, secret []byte, rotate bool) (int64, error) {
if len(secret) < 32 {
return 0, ErrCredentialInvalid
}
var deviceID, tenantID, reference string
if err := tx.QueryRow(ctx, `
SELECT id::text,tenant_id::text,credential_ref FROM device
WHERE public_id=$1::uuid AND status <> 'revoked' FOR UPDATE`, strings.TrimSpace(devicePublicID),
).Scan(&deviceID, &tenantID, &reference); errors.Is(err, pgx.ErrNoRows) {
return 0, ErrDeviceNotFound
} else if err != nil {
return 0, fmt.Errorf("resolve device for credential provisioning: %w", err)
}
var currentVersion int64
err := tx.QueryRow(ctx, `SELECT key_version FROM device_credential WHERE device_id=$1::uuid FOR UPDATE`, deviceID).Scan(&currentVersion)
if err != nil && !errors.Is(err, pgx.ErrNoRows) {
return 0, fmt.Errorf("read current device credential: %w", err)
}
if err == nil && !rotate {
return 0, ErrCredentialAlreadyExists
}
version := currentVersion + 1
nonce := make([]byte, r.aead.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return 0, fmt.Errorf("generate device credential nonce: %w", err)
}
encrypted := r.aead.Seal(nil, nonce, secret, credentialAAD(reference, version))
if currentVersion == 0 {
_, err = tx.Exec(ctx, `
INSERT INTO device_credential(device_id,tenant_id,credential_ref,key_version,encrypted_secret,nonce)
VALUES($1::uuid,$2::uuid,$3,$4,$5,$6)`, deviceID, tenantID, reference, version, encrypted, nonce)
} else {
_, err = tx.Exec(ctx, `
UPDATE device_credential SET key_version=$2,encrypted_secret=$3,nonce=$4,status='active',
rotated_at=now(),revoked_at=NULL
WHERE device_id=$1::uuid`, deviceID, version, encrypted, nonce)
}
if err != nil {
return 0, fmt.Errorf("store device credential: %w", err)
}
return version, nil
}
func (r *DatabaseSecretResolver) Revoke(ctx context.Context, devicePublicID string) error {
result, err := r.pool.Exec(ctx, `
UPDATE device_credential dc SET status='revoked',revoked_at=now()
FROM device d WHERE d.id=dc.device_id AND d.public_id=$1::uuid AND dc.status='active'`, strings.TrimSpace(devicePublicID))
if err != nil {
return fmt.Errorf("revoke device credential: %w", err)
}
if result.RowsAffected() != 1 {
return ErrDeviceNotFound
}
return nil
}
func credentialAAD(reference string, version int64) []byte {
return []byte(fmt.Sprintf("device-credential-v1\n%s\n%d", strings.TrimSpace(reference), version))
}
@@ -0,0 +1,89 @@
package deviceauth
import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"os"
"testing"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgxpool"
)
func TestDatabaseSecretResolverImportsEncryptedCredential(t *testing.T) {
databaseURL := os.Getenv("TEST_DATABASE_URL")
if databaseURL == "" {
t.Skip("TEST_DATABASE_URL is not set")
}
ctx := context.Background()
pool, err := pgxpool.New(ctx, databaseURL)
if err != nil {
t.Fatal(err)
}
defer pool.Close()
deviceID := uuid.NewString()
publicID := uuid.NewString()
reference := "database-test://" + publicID
serial := sha256.Sum256([]byte(publicID))
_, err = pool.Exec(ctx, `
INSERT INTO device(id,public_id,tenant_id,serial_digest,model,status,credential_ref)
VALUES($1::uuid,$2::uuid,'10000000-0000-0000-0000-000000000001'::uuid,$3,'database-credential-test','registered',$4)`,
deviceID, publicID, hex.EncodeToString(serial[:]), reference)
if err != nil {
t.Fatal(err)
}
defer func() {
_, _ = pool.Exec(ctx, `DELETE FROM device_credential WHERE device_id=$1::uuid`, deviceID)
_, _ = pool.Exec(ctx, `DELETE FROM device WHERE id=$1::uuid`, deviceID)
}()
const masterKey = "101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f"
secret := []byte("database-device-secret-at-least-32-bytes")
resolver, err := NewDatabaseSecretResolver(pool, masterKey)
if err != nil {
t.Fatal(err)
}
if err := resolver.ImportMissingLegacyCredentials(ctx, map[string][]byte{reference: secret}); err != nil {
t.Fatal(err)
}
resolved, err := resolver.Resolve(ctx, reference)
if err != nil || !bytes.Equal(resolved, secret) {
t.Fatalf("resolved credential does not match: len=%d err=%v", len(resolved), err)
}
var encrypted []byte
if err := pool.QueryRow(ctx, `SELECT encrypted_secret FROM device_credential WHERE device_id=$1::uuid`, deviceID).Scan(&encrypted); err != nil {
t.Fatal(err)
}
if bytes.Contains(encrypted, secret) || bytes.Equal(encrypted, secret) {
t.Fatal("database stored plaintext device credential")
}
wrongResolver, err := NewDatabaseSecretResolver(pool, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
if err != nil {
t.Fatal(err)
}
if _, err := wrongResolver.Resolve(ctx, reference); !errors.Is(err, ErrCredentialInvalid) {
t.Fatalf("wrong master key error=%v", err)
}
if _, err := resolver.Provision(ctx, publicID, []byte("replacement-device-secret-at-least-32-bytes"), false); !errors.Is(err, ErrCredentialAlreadyExists) {
t.Fatalf("duplicate provision error=%v", err)
}
replacement := []byte("replacement-device-secret-at-least-32-bytes")
version, err := resolver.Provision(ctx, publicID, replacement, true)
if err != nil || version != 2 {
t.Fatalf("rotate version=%d err=%v", version, err)
}
resolved, err = resolver.Resolve(ctx, reference)
if err != nil || !bytes.Equal(resolved, replacement) {
t.Fatalf("rotated credential does not match: len=%d err=%v", len(resolved), err)
}
if err := resolver.Revoke(ctx, publicID); err != nil {
t.Fatal(err)
}
if _, err := resolver.Resolve(ctx, reference); !errors.Is(err, ErrCredentialInvalid) {
t.Fatalf("revoked credential error=%v", err)
}
}