PIN Generation¶
The PIN generation package provides cryptographically secure PIN generation and comparison utilities. It supports numeric and alphanumeric PINs with automatic dash formatting for readability.
Features¶
- Cryptographically secure - Uses
crypto/randfor secure random number generation - Numeric PINs - Generate digit-only PINs (0-9)
- Alphanumeric PINs - Generate uppercase letter and digit PINs (A-Z, 0-9)
- Auto-formatting - Dashes inserted every 3 characters for readability
- Constant-time comparison - Timing attack protection using
crypto/subtle - Case-insensitive matching - Alphanumeric comparison ignores case
Complete API Reference¶
Generation Functions¶
GenerateNumeric¶
Generates a cryptographically secure numeric PIN of the specified length.
Parameters:
- length: The number of digits in the PIN (before formatting)
Returns:
- string: The generated PIN with dashes every 3 characters
- error: ErrInvalidLength if length is 0 or negative
Example output: "123-456-789" for length 9
GenerateAlphanumeric¶
Generates a cryptographically secure alphanumeric PIN of the specified length. The generated PIN contains uppercase letters (A-Z) and digits (0-9).
Parameters:
- length: The number of characters in the PIN (before formatting)
Returns:
- string: The generated PIN with dashes every 3 characters
- error: ErrInvalidLength if length is 0 or negative
Example output: "AB3-XY7-Z9K" for length 9
Comparison Functions¶
CompareNumeric¶
Performs a constant-time comparison of two numeric PINs.
Dashes are stripped before comparison, so "123-456" matches "123456".
Parameters:
- pin1: First PIN to compare
- pin2: Second PIN to compare
Returns:
- bool: true if the PINs match, false otherwise
CompareAlphanumeric¶
Performs a constant-time, case-insensitive comparison of two alphanumeric PINs.
Dashes are stripped before comparison, so "ABC-123" matches "abc123".
Parameters:
- pin1: First PIN to compare
- pin2: Second PIN to compare
Returns:
- bool: true if the PINs match (case-insensitive), false otherwise
Error Constants¶
Usage Examples¶
Basic PIN Generation¶
import (
"github.com/oddbit-project/blueprint/crypt/pin"
"log"
)
func basicExample() {
// Generate a 6-digit numeric PIN
numericPIN, err := pin.GenerateNumeric(6)
if err != nil {
log.Fatal(err)
}
log.Printf("Numeric PIN: %s", numericPIN)
// Output: Numeric PIN: 123-456
// Generate an 8-character alphanumeric PIN
alphaPIN, err := pin.GenerateAlphanumeric(8)
if err != nil {
log.Fatal(err)
}
log.Printf("Alphanumeric PIN: %s", alphaPIN)
// Output: Alphanumeric PIN: AB3-XY7-Z9
}
PIN Verification¶
func verificationExample() {
// Generate a PIN
storedPIN, _ := pin.GenerateNumeric(6)
// User enters PIN (possibly without dashes)
userInput := "123456"
// Compare - dashes are automatically stripped
if pin.CompareNumeric(storedPIN, userInput) {
log.Println("PIN verified successfully")
} else {
log.Println("Invalid PIN")
}
}
Case-Insensitive Alphanumeric Comparison¶
func caseInsensitiveExample() {
// Generate alphanumeric PIN
storedPIN, _ := pin.GenerateAlphanumeric(6)
// storedPIN might be: "ABC-123"
// User enters lowercase
userInput := "abc123"
// Comparison is case-insensitive
if pin.CompareAlphanumeric(storedPIN, userInput) {
log.Println("PIN verified successfully")
}
}
One-Time PIN (OTP) Use Case¶
func otpExample() {
// Generate a 6-digit OTP for email verification
otp, err := pin.GenerateNumeric(6)
if err != nil {
log.Fatal(err)
}
// Send to user (formatted for readability)
sendEmail(user.Email, fmt.Sprintf("Your verification code is: %s", otp))
// Store for later verification
storeOTP(user.ID, otp, time.Now().Add(15*time.Minute))
}
func verifyOTP(userID string, enteredOTP string) bool {
storedOTP, expiry := getStoredOTP(userID)
if time.Now().After(expiry) {
return false // OTP expired
}
// Constant-time comparison prevents timing attacks
return pin.CompareNumeric(storedOTP, enteredOTP)
}
Account Recovery Code Generation¶
func recoveryCodeExample() {
// Generate 8 recovery codes for account recovery
codes := make([]string, 8)
for i := range codes {
code, err := pin.GenerateAlphanumeric(9)
if err != nil {
log.Fatal(err)
}
codes[i] = code
}
// Display to user: ABC-DEF-GH1, XYZ-123-AB4, etc.
for i, code := range codes {
log.Printf("Recovery code %d: %s", i+1, code)
}
// Store hashed versions for security
storeRecoveryCodes(user.ID, codes)
}
Two-Factor Authentication Setup¶
func twoFactorSetupExample() {
// Generate backup codes for 2FA
backupCodes := make([]string, 10)
for i := range backupCodes {
code, _ := pin.GenerateAlphanumeric(8)
backupCodes[i] = code
}
// Present to user once
log.Println("Save these backup codes in a safe place:")
for _, code := range backupCodes {
log.Printf(" %s", code)
}
}
Security Considerations¶
Cryptographic Security¶
The package uses crypto/rand for random number generation, which provides cryptographically secure randomness suitable for security-sensitive applications.
Timing Attack Protection¶
Both comparison functions use crypto/subtle.ConstantTimeCompare to prevent timing attacks. This ensures that comparing PINs takes the same amount of time regardless of how many characters match.
// Safe - uses constant-time comparison
if pin.CompareNumeric(storedPIN, userInput) {
// Valid
}
// UNSAFE - do not use direct string comparison
if storedPIN == userInput {
// Vulnerable to timing attacks
}
PIN Length Recommendations¶
| Use Case | Recommended Length | Type |
|---|---|---|
| SMS verification | 6 digits | Numeric |
| Email verification | 6-8 digits | Numeric |
| Recovery codes | 8-12 characters | Alphanumeric |
| Temporary passwords | 12+ characters | Alphanumeric |
Entropy Calculation¶
| Length | Numeric Entropy | Alphanumeric Entropy |
|---|---|---|
| 4 | ~13 bits | ~21 bits |
| 6 | ~20 bits | ~31 bits |
| 8 | ~27 bits | ~41 bits |
| 10 | ~33 bits | ~52 bits |
| 12 | ~40 bits | ~62 bits |