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) } }