Go library for server-side verification of auroprint device fingerprints.
go get github.com/arrorLabArts/auroprint-gopackage main
import (
"context"
"log"
"time"
"github.com/arrorLabArts/auroprint-go"
)
func main() {
ctx := context.Background()
// Request from Flutter client
req := auroprint.Request{
Payload: `{"did":"device123","ts":1700000000,"nonce":"abc123"}`,
Signature: "base64signature...",
PublicKey: "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----",
AttestationChain: []string{"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"},
IntegrityToken: "optional-play-integrity-token",
}
// Verify the request
result := auroprint.Verify(ctx, req, &auroprint.VerifyOptions{
PlayIntegrityPackageName: "com.yourapp.package",
})
if !result.Valid {
log.Fatal("Verification failed:", result.Error)
}
// Use the verified payload
deviceID := result.Payload.DeviceID
nonce := result.Payload.Nonce
timestamp := result.Payload.Timestamp
// Application-specific anti-replay checks
if time.Now().Unix()-timestamp > 30 {
log.Fatal("Request expired")
}
// Check nonce against your store (Redis, database, etc.)
// ...
log.Printf("Verified device: %s, nonce: %s", deviceID, nonce)
}- Signature - Proves the payload was signed by the private key holder
- Attestation Chain (Android) - Proves the key is hardware-backed (TEE) and signed by Google
- Play Integrity (optional) - Proves real device, unmodified app from Play Store
- Device integrity (not rooted/emulator)
- App integrity (Play Store recognized)
- Optional: Nonce validation (prevents replay attacks)
- Optional: Timestamp validation (ensures token freshness)
DeviceID- Unique device identifierNonce- Random value for anti-replay protectionTimestamp- When the fingerprint was generated
You must implement these checks on the auroprint payload:
- Check the timestamp is within acceptable window (e.g., 30 seconds)
- Store and check nonces to prevent replay attacks
- Implement rate limiting per device ID
The library now supports optional Play Integrity anti-replay protection:
- Nonce validation: Set
PlayIntegrityExpectedNonceto verify the token was generated for your specific request - Timestamp validation: Set
PlayIntegrityMaxAgeSecondsto ensure token freshness (recommended: 60 seconds) - Duplicate prevention: Optionally track used tokens in your application (Redis, database, etc.)
opts := &auroprint.VerifyOptions{
// Skip attestation chain verification
SkipAttestationVerification: false,
// Required if using Play Integrity
PlayIntegrityPackageName: "com.yourapp.package",
// Service account credentials for Play Integrity API
// If empty, uses default credentials
GoogleCredentialsJSON: []byte(`{...}`),
// Optional: Expected nonce for Play Integrity token
// If set, validates token nonce matches (prevents replay attacks)
PlayIntegrityExpectedNonce: "server-generated-nonce-123",
// Optional: Maximum age in seconds for Play Integrity token
// If > 0, validates token timestamp is within this window
// Recommended: 60 seconds
PlayIntegrityMaxAgeSeconds: 60,
}For maximum security with Play Integrity, use nonce and timestamp validation:
import (
"context"
"crypto/rand"
"encoding/base64"
)
// 1. Generate a unique nonce for this request
func generateNonce() string {
b := make([]byte, 32)
rand.Read(b)
return base64.URLEncoding.EncodeToString(b)
}
func handleRequest(ctx context.Context, req auroprint.Request) {
// 2. Generate nonce (you should have sent this to client earlier)
expectedNonce := generateNonce()
// In practice, send this to client first, client includes it in Play Integrity request
// 3. Verify with anti-replay protection
result := auroprint.Verify(ctx, req, &auroprint.VerifyOptions{
PlayIntegrityPackageName: "com.yourapp.package",
PlayIntegrityExpectedNonce: expectedNonce,
PlayIntegrityMaxAgeSeconds: 60, // Token must be < 60 seconds old
})
if !result.Valid {
log.Printf("Verification failed: %v", result.Error)
return
}
// 4. Optional: Track used tokens to prevent duplicate use
// trackUsedToken(req.IntegrityToken)
log.Printf("Verified device: %s", result.Payload.DeviceID)
}MIT