Check if credentials have been exposed in data breaches using k-anonymity
Project description
darkstrata-credential-check
Check if credentials have been exposed in data breaches using k-anonymity.
Features
- Privacy-first: Uses k-anonymity to check credentials without exposing them
- Type-safe: Full type hints with comprehensive dataclass definitions
- Async-first: Built on
httpxfor efficient async HTTP operations - Automatic caching: Intelligent caching aligned with server time windows
- Batch processing: Efficiently check multiple credentials with automatic prefix grouping
- Retry logic: Built-in exponential backoff for transient failures
- Comprehensive errors: Detailed error types for easy error handling
Prerequisites
- Get an API key from https://darkstrata.io
- Python 3.10 or higher
Installation
pip install darkstrata-credential-check
poetry add darkstrata-credential-check
uv add darkstrata-credential-check
Quick Start
import asyncio
from darkstrata_credential_check import DarkStrataCredentialCheck
async def main():
# Create a client
async with DarkStrataCredentialCheck(api_key='your-api-key') as client:
# Check a single credential
result = await client.check('user@example.com', 'password123')
if result.found:
print('WARNING: This credential was found in a data breach!')
else:
print('Credential not found in known breaches.')
asyncio.run(main())
How It Works
This SDK uses k-anonymity to check credentials without exposing them:
- Your credential is hashed locally:
SHA256(email:password) - Only the first 5 characters (prefix) of the hash are sent to the API
- The API returns all hashes matching that prefix (1-in-1,000,000 anonymity)
- The SDK checks if your full hash is in the returned set
Your actual credentials never leave your system.
┌─────────────────────┐ ┌─────────────────────┐
│ Your System │ │ DarkStrata API │
│ │ │ │
│ email:password │ │ │
│ ↓ │ │ │
│ SHA256 hash │ │ │
│ ↓ │ │ │
│ Extract prefix ────┼────────→│ Lookup by prefix │
│ (5 chars only) │ │ ↓ │
│ │←────────┼─ Return all matches │
│ Check membership │ │ │
│ ↓ │ │ │
│ found: true/false │ │ │
└─────────────────────┘ └─────────────────────┘
API Reference
DarkStrataCredentialCheck
The main client class.
Constructor
DarkStrataCredentialCheck(
api_key: str,
*,
base_url: str = 'https://api.darkstrata.io/v1/',
timeout: float = 30.0,
retries: int = 3,
enable_caching: bool = True,
cache_ttl: int = 3600,
)
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key |
str |
required | Your DarkStrata API key |
base_url |
str |
'https://api.darkstrata.io/v1/' |
API base URL |
timeout |
float |
30.0 |
Request timeout in seconds |
retries |
int |
3 |
Number of retry attempts |
enable_caching |
bool |
True |
Enable response caching |
cache_ttl |
int |
3600 |
Cache TTL in seconds (1 hour) |
Methods
check(email, password, options?)
Check a single credential.
result = await client.check('user@example.com', 'password123')
Returns: CheckResult
check_hash(hash, options?)
Check a pre-computed SHA-256 hash.
hash_value = '5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8...'
result = await client.check_hash(hash_value)
Returns: CheckResult
check_batch(credentials, options?)
Check multiple credentials efficiently.
from darkstrata_credential_check import Credential
results = await client.check_batch([
Credential(email='user1@example.com', password='pass1'),
Credential(email='user2@example.com', password='pass2'),
])
Returns: list[CheckResult]
clear_cache()
Clear the internal response cache.
client.clear_cache()
get_cache_size()
Get the number of cached entries.
size = client.get_cache_size()
CheckResult
The result of a credential check.
@dataclass
class CheckResult:
found: bool # true if credential was in a breach
credential: CredentialInfo # info about the credential checked
metadata: CheckMetadata # additional metadata
CheckMetadata
Metadata returned with check results.
@dataclass
class CheckMetadata:
prefix: str # The 5-char prefix used
total_results: int # Total hashes returned by API
hmac_source: Literal['server', 'client'] # Source of HMAC key
time_window: int | None # Server time window (server HMAC only)
filter_since: int | None # Epoch day filter (if since was used)
cached_result: bool # Whether result was from cache
checked_at: datetime # When the check was performed
CheckOptions
Optional parameters for check requests.
@dataclass
class CheckOptions:
client_hmac: str | None = None # Your own HMAC key (64+ hex chars)
since: int | datetime | None = None # Filter by breach date
Error Handling
The SDK provides specific error types for different failure scenarios:
from darkstrata_credential_check import (
DarkStrataCredentialCheck,
AuthenticationError,
ValidationError,
ApiError,
TimeoutError,
NetworkError,
RateLimitError,
is_darkstrata_error,
)
async with DarkStrataCredentialCheck(api_key='your-key') as client:
try:
result = await client.check('user@example.com', 'password')
except AuthenticationError:
print('Invalid API key')
except ValidationError as error:
print(f'Invalid input: {error.field}')
except RateLimitError as error:
if error.retry_after:
print(f'Rate limited. Retry after {error.retry_after} seconds')
except TimeoutError as error:
print(f'Request timed out after {error.timeout_seconds}s')
except NetworkError as error:
print(f'Network error: {error}')
except ApiError as error:
print(f'API error ({error.status_code}): {error}')
except Exception as error:
if is_darkstrata_error(error):
print(f'DarkStrata error [{error.code}]: {error}')
else:
raise
Error Types
| Error | Code | Description |
|---|---|---|
AuthenticationError |
AUTHENTICATION_ERROR |
Invalid or missing API key |
ValidationError |
VALIDATION_ERROR |
Invalid input parameters |
ApiError |
API_ERROR |
API request failed |
TimeoutError |
TIMEOUT_ERROR |
Request timed out |
NetworkError |
NETWORK_ERROR |
Network connectivity issue |
RateLimitError |
RATE_LIMIT_ERROR |
Rate limit exceeded |
Advanced Usage
Pre-computed Hashes
If you're storing hashed credentials, you can check them directly:
from darkstrata_credential_check import hash_credential, DarkStrataCredentialCheck
# Compute hash once
hash_value = hash_credential('user@example.com', 'password123')
# Store hash securely...
# Later, check the hash
async with DarkStrataCredentialCheck(api_key='your-key') as client:
result = await client.check_hash(hash_value)
Batch Processing
For checking multiple credentials, use check_batch for efficiency:
from darkstrata_credential_check import Credential
credentials = [
Credential(email='user1@example.com', password='pass1'),
Credential(email='user2@example.com', password='pass2'),
Credential(email='user3@example.com', password='pass3'),
]
results = await client.check_batch(credentials)
compromised = [r for r in results if r.found]
print(f'{len(compromised)} credentials were compromised')
Batch processing automatically groups credentials by prefix to minimise API calls.
Client-Provided HMAC Key
By default, the server generates a time-rotating HMAC key. For deterministic results across requests, provide your own key:
import secrets
from darkstrata_credential_check import CheckOptions
# Generate a secure key once and store it securely
client_hmac = secrets.token_hex(32) # 64 hex chars = 256 bits
result = await client.check('user@example.com', 'password',
CheckOptions(client_hmac=client_hmac))
# Results are now deterministic (not time-windowed)
print(result.metadata.hmac_source) # 'client'
When to use client HMAC:
- You need consistent results across multiple requests
- You're comparing results from different time periods
- You want to avoid server-side key rotation
Date Filtering
Filter results to only include breaches from a specific date onwards:
from datetime import datetime
from darkstrata_credential_check import CheckOptions
# Only check breaches from 2024 onwards
result = await client.check('user@example.com', 'password',
CheckOptions(since=datetime(2024, 1, 1)))
# Or use epoch day (days since 1 January 1970)
result = await client.check('user@example.com', 'password',
CheckOptions(since=19724)) # 2024-01-01
# Check the filter applied
print(result.metadata.filter_since) # 19724
Combined Options
You can combine multiple options:
result = await client.check('user@example.com', 'password',
CheckOptions(
client_hmac='your-256-bit-hex-key...',
since=datetime(2024, 1, 1),
))
Disabling Cache
For real-time checks where you need fresh results:
client = DarkStrataCredentialCheck(
api_key='your-key',
enable_caching=False,
)
Custom Timeout and Retries
client = DarkStrataCredentialCheck(
api_key='your-key',
timeout=60.0, # 60 seconds
retries=5, # 5 retry attempts
)
Security Considerations
What is sent to the API?
- Only the first 5 characters of the SHA-256 hash
- Your API key for authentication
What is NOT sent?
- Your email address
- Your password
- The full hash of your credentials
Best Practices
- Never log credentials - The SDK never logs credentials, and you shouldn't either
- Use HTTPS - The SDK enforces HTTPS for all API calls
- Secure your API key - Store your API key securely (environment variables, secrets manager)
- Handle errors gracefully - Don't expose internal errors to end users
Type Hints
This package includes comprehensive type hints and exports all types:
from darkstrata_credential_check import (
ClientOptions,
Credential,
CheckOptions,
CheckResult,
CheckMetadata,
CredentialInfo,
)
Context Manager
The client supports async context manager for proper resource cleanup:
async with DarkStrataCredentialCheck(api_key='your-key') as client:
result = await client.check('user@example.com', 'password')
# HTTP client is automatically closed
Alternatively, close manually:
client = DarkStrataCredentialCheck(api_key='your-key')
try:
result = await client.check('user@example.com', 'password')
finally:
await client.close()
Contributing
See the contributing guide.
Licence
Apache 2.0 (c) DarkStrata
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file darkstrata_credential_check-1.2.3.tar.gz.
File metadata
- Download URL: darkstrata_credential_check-1.2.3.tar.gz
- Upload date:
- Size: 28.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
66d590d9f08e8464a66c9312c4abb3f3db2ee34dbf1b71d088bf19b1c11bb61d
|
|
| MD5 |
84b0adcc8bc08fac0b4358b3f2410edc
|
|
| BLAKE2b-256 |
abe4dd6c855290d3ad3a445ac288bb506723458954a486cb12e1e99b303e84ba
|
Provenance
The following attestation bundles were made for darkstrata_credential_check-1.2.3.tar.gz:
Publisher:
release.yml on darkstrata/darkstrata-sdks
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
darkstrata_credential_check-1.2.3.tar.gz -
Subject digest:
66d590d9f08e8464a66c9312c4abb3f3db2ee34dbf1b71d088bf19b1c11bb61d - Sigstore transparency entry: 793831848
- Sigstore integration time:
-
Permalink:
darkstrata/darkstrata-sdks@f028255dabcec46d760930662ae1528ed7033d62 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/darkstrata
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f028255dabcec46d760930662ae1528ed7033d62 -
Trigger Event:
push
-
Statement type:
File details
Details for the file darkstrata_credential_check-1.2.3-py3-none-any.whl.
File metadata
- Download URL: darkstrata_credential_check-1.2.3-py3-none-any.whl
- Upload date:
- Size: 23.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4889b5e3644400ac3b352dfe38955cb7cbe58331de63a0dcdf9247e8528adaf
|
|
| MD5 |
f7511c0c2ac64bc0ba62cd26b000eab0
|
|
| BLAKE2b-256 |
1ae6ca925e17e9d4532a6e35cad3f0027ec8524207d9ad3de5113d524bb02b7a
|
Provenance
The following attestation bundles were made for darkstrata_credential_check-1.2.3-py3-none-any.whl:
Publisher:
release.yml on darkstrata/darkstrata-sdks
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
darkstrata_credential_check-1.2.3-py3-none-any.whl -
Subject digest:
a4889b5e3644400ac3b352dfe38955cb7cbe58331de63a0dcdf9247e8528adaf - Sigstore transparency entry: 793831906
- Sigstore integration time:
-
Permalink:
darkstrata/darkstrata-sdks@f028255dabcec46d760930662ae1528ed7033d62 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/darkstrata
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f028255dabcec46d760930662ae1528ed7033d62 -
Trigger Event:
push
-
Statement type: