Skip to content

Commit 597c31e

Browse files
cursoragentlovasoa
andcommitted
feat: Add sqlpage.hmac function for cryptographic signing
Co-authored-by: contact <[email protected]>
1 parent ebdac5b commit 597c31e

File tree

9 files changed

+496
-0
lines changed

9 files changed

+496
-0
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ actix-web-httpauth = "0.8.0"
6161
rand = "0.9.0"
6262
actix-multipart = "0.7.2"
6363
base64 = "0.22"
64+
hmac = "0.12"
65+
sha2 = "0.10"
6466
rustls-acme = "0.14"
6567
dotenvy = "0.15.7"
6668
csv-async = { version = "1.2.6", features = ["tokio"] }

HMAC_FEATURE_SUMMARY.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# SQLPage HMAC Function - Feature Summary
2+
3+
## Overview
4+
This document summarizes the addition of the `sqlpage.hmac()` function to SQLPage, which provides cryptographic HMAC (Hash-based Message Authentication Code) capabilities.
5+
6+
## Changes Made
7+
8+
### 1. Dependencies Added (Cargo.toml)
9+
- `hmac = "0.12"` - HMAC implementation from RustCrypto
10+
- `sha2 = "0.10"` - SHA-2 family of hash functions
11+
12+
### 2. Function Implementation (src/webserver/database/sqlpage_functions/functions.rs)
13+
14+
#### Function Declaration
15+
```rust
16+
hmac(data: Option<Cow<str>>, key: Option<Cow<str>>, algorithm: Option<Cow<str>>);
17+
```
18+
19+
#### Implementation Features
20+
- **Parameters:**
21+
- `data`: The input data to compute HMAC for
22+
- `key`: The secret key used for HMAC
23+
- `algorithm`: Optional hash algorithm (defaults to "sha256")
24+
25+
- **Supported Algorithms:**
26+
- `sha256` (default)
27+
- `sha512`
28+
29+
- **Return Value:**
30+
- Hexadecimal string representation of the HMAC
31+
- Returns NULL if either data or key is NULL
32+
33+
- **Security:**
34+
- Uses industry-standard RustCrypto libraries
35+
- Constant-time comparison available via HMAC library
36+
- Proper error handling for invalid inputs
37+
38+
### 3. Documentation (examples/official-site/sqlpage/migrations/08_functions.sql)
39+
40+
Added comprehensive documentation including:
41+
- Function description with Wikipedia link
42+
- Common use cases (API authentication, webhook verification, token generation)
43+
- Three detailed code examples:
44+
1. API request signing
45+
2. Webhook signature verification
46+
3. Secure download tokens
47+
- Security notes and best practices
48+
- Parameter descriptions
49+
50+
### 4. Interactive Example (examples/official-site/examples/hmac.sql)
51+
52+
Created a full interactive example page that allows users to:
53+
- Try the HMAC function with custom data and keys
54+
- Select different hash algorithms
55+
- See the resulting signature
56+
- Learn about verification techniques
57+
- Understand real-world use cases
58+
59+
### 5. Test Suite (tests/sql_test_files/)
60+
61+
Added four test files:
62+
- `it_works_hmac_sha256.sql` - Test SHA-256 algorithm
63+
- `it_works_hmac_sha512.sql` - Test SHA-512 algorithm
64+
- `it_works_hmac_default.sql` - Test default algorithm behavior
65+
- `it_works_hmac_null.sql` - Test NULL handling
66+
67+
## Usage Examples
68+
69+
### Basic Usage
70+
```sql
71+
SELECT sqlpage.hmac('Hello, World!', 'secret-key', 'sha256') as signature;
72+
```
73+
74+
### API Request Signing
75+
```sql
76+
SELECT sqlpage.hmac(
77+
'user_id=123&action=update',
78+
'my-secret-api-key',
79+
'sha256'
80+
) as request_signature;
81+
```
82+
83+
### Webhook Verification
84+
```sql
85+
SELECT
86+
CASE
87+
WHEN sqlpage.hmac(sqlpage.request_body(), 'webhook-secret', 'sha256') = :signature
88+
THEN 'Valid webhook'
89+
ELSE 'Invalid signature'
90+
END as status;
91+
```
92+
93+
### Secure Token Generation
94+
```sql
95+
INSERT INTO download_tokens (file_id, token, expires_at)
96+
VALUES (
97+
:file_id,
98+
sqlpage.hmac(
99+
:file_id || '|' || datetime('now', '+1 hour'),
100+
sqlpage.environment_variable('SECRET_KEY'),
101+
'sha256'
102+
),
103+
datetime('now', '+1 hour')
104+
);
105+
```
106+
107+
## Security Considerations
108+
109+
1. **Key Management:**
110+
- Keys should be stored securely using environment variables
111+
- Never hardcode keys in SQL files or expose them client-side
112+
- Use `sqlpage.environment_variable()` to access keys
113+
114+
2. **Algorithm Selection:**
115+
- SHA-256 is the default and suitable for most use cases
116+
- SHA-512 provides additional security for highly sensitive applications
117+
- Both algorithms are cryptographically secure
118+
119+
3. **Key Length:**
120+
- Keys can be of any length
121+
- For maximum security, use keys at least as long as the hash output:
122+
- SHA-256: 32 bytes (64 hex characters)
123+
- SHA-512: 64 bytes (128 hex characters)
124+
125+
## Testing
126+
127+
To run the tests:
128+
```bash
129+
cargo test
130+
```
131+
132+
The test suite includes:
133+
- Algorithm verification (SHA-256, SHA-512)
134+
- Default parameter handling
135+
- NULL value handling
136+
- Integration with SQLPage's query execution
137+
138+
## Documentation Links
139+
140+
Once deployed, the function will be documented at:
141+
- Function reference: `/functions.sql?function=hmac`
142+
- Interactive example: `/examples/hmac.sql`
143+
144+
## Version
145+
146+
Introduced in SQLPage version **0.38.0**
147+
148+
## Files Modified
149+
150+
1. `/workspace/Cargo.toml` - Added dependencies
151+
2. `/workspace/src/webserver/database/sqlpage_functions/functions.rs` - Implementation
152+
3. `/workspace/examples/official-site/sqlpage/migrations/08_functions.sql` - Documentation
153+
4. `/workspace/examples/official-site/examples/hmac.sql` - Interactive example
154+
5. `/workspace/tests/sql_test_files/it_works_hmac_*.sql` - Test suite
155+
156+
## Related Functions
157+
158+
- `sqlpage.hash_password()` - Password hashing using Argon2
159+
- `sqlpage.random_string()` - Generate cryptographically secure random strings
160+
- `sqlpage.environment_variable()` - Access environment variables for secrets
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
select 'dynamic' as component, properties FROM example WHERE component = 'shell' LIMIT 1;
2+
3+
select 'text' as component, '
4+
5+
# HMAC (Hash-based Message Authentication Code)
6+
7+
In SQLPage, you can use the [`sqlpage.hmac`](/functions.sql?function=hmac) function to
8+
compute a cryptographic signature for your data. HMAC is a type of message authentication code
9+
that uses a cryptographic hash function and a secret key to verify both the data integrity
10+
and authenticity of a message.
11+
12+
HMAC is commonly used for:
13+
- **API authentication**: Generate secure signatures for API requests
14+
- **Webhook verification**: Verify that webhook requests are authentic and haven''t been tampered with
15+
- **Token generation**: Create secure tokens for downloads, password resets, or temporary access
16+
- **Data integrity**: Ensure data hasn''t been modified during transmission or storage
17+
18+
The `sqlpage.hmac` function takes three parameters:
19+
1. **data**: The message or data to be signed
20+
2. **key**: A secret key that should be kept confidential
21+
3. **algorithm**: The hash algorithm to use (optional, defaults to `sha256`)
22+
23+
The function returns a hexadecimal string that represents the HMAC signature.
24+
25+
## Security Notes
26+
27+
- Keep your secret keys secure and never expose them in client-side code or version control
28+
- Use environment variables to store secret keys: `sqlpage.environment_variable(''SECRET_KEY'')`
29+
- For maximum security, use a key that is at least as long as the hash output
30+
- HMAC is deterministic: the same data and key will always produce the same signature
31+
32+
## Example Use Cases
33+
34+
### 1. API Request Signing
35+
36+
Generate a signature for API requests to verify they haven''t been tampered with:
37+
38+
```sql
39+
SELECT sqlpage.hmac(
40+
''user_id=123&action=update&timestamp='' || strftime(''%s'', ''now''),
41+
sqlpage.environment_variable(''API_SECRET''),
42+
''sha256''
43+
) as request_signature;
44+
```
45+
46+
### 2. Webhook Signature Verification
47+
48+
Verify that a webhook request is authentic by comparing signatures:
49+
50+
```sql
51+
SELECT
52+
CASE
53+
WHEN sqlpage.hmac(sqlpage.request_body(), ''webhook-secret'', ''sha256'') = :signature
54+
THEN ''Valid webhook''
55+
ELSE ''Invalid signature - request rejected''
56+
END as status;
57+
```
58+
59+
### 3. Secure Download Tokens
60+
61+
Create time-limited download tokens that can be verified without storing them:
62+
63+
```sql
64+
INSERT INTO download_tokens (file_id, token, expires_at)
65+
VALUES (
66+
:file_id,
67+
sqlpage.hmac(
68+
:file_id || ''|'' || datetime(''now'', ''+1 hour''),
69+
sqlpage.environment_variable(''DOWNLOAD_SECRET''),
70+
''sha256''
71+
),
72+
datetime(''now'', ''+1 hour'')
73+
);
74+
```
75+
76+
# Try it out
77+
78+
You can try the HMAC function out by entering data, a secret key, and selecting an algorithm below.
79+
' as contents_md;
80+
81+
select 'form' as component, 'Generate HMAC' as validate;
82+
select 'data' as name, 'text' as type, 'Data to Sign' as label,
83+
'Enter the data you want to sign' as placeholder,
84+
coalesce(:data, 'Hello, World!') as value;
85+
select 'key' as name, 'text' as type, 'Secret Key' as label,
86+
'Enter your secret key' as placeholder,
87+
coalesce(:key, 'my-secret-key') as value;
88+
select 'algorithm' as name, 'select' as type, 'Hash Algorithm' as label;
89+
select 'sha256' as value, :algorithm = 'sha256' or :algorithm is null as selected;
90+
select 'sha512' as value, :algorithm = 'sha512' as selected;
91+
92+
select 'text' as component, '
93+
94+
### HMAC Signature
95+
96+
The HMAC signature for your data is:
97+
98+
```
99+
' || sqlpage.hmac(:data, :key, :algorithm) || '
100+
```
101+
102+
### Verification
103+
104+
To verify data later, simply recompute the HMAC with the same key and algorithm,
105+
then compare the result with the original signature. If they match, the data is authentic and unmodified.
106+
107+
### Example Verification Code
108+
109+
```sql
110+
SELECT
111+
CASE
112+
WHEN sqlpage.hmac(:received_data, :secret_key, :algorithm) = :stored_signature
113+
THEN ''Data is authentic''
114+
ELSE ''Data has been tampered with''
115+
END as verification_result;
116+
```
117+
' as contents_md
118+
where :data is not null and :key is not null;

examples/official-site/sqlpage/migrations/08_functions.sql

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,4 +401,101 @@ VALUES (
401401
'string',
402402
'The string to encode.',
403403
'TEXT'
404+
);
405+
INSERT INTO sqlpage_functions (
406+
"name",
407+
"introduced_in_version",
408+
"icon",
409+
"description_md"
410+
)
411+
VALUES (
412+
'hmac',
413+
'0.38.0',
414+
'shield-lock',
415+
'Computes the [HMAC](https://en.wikipedia.org/wiki/HMAC) (Hash-based Message Authentication Code) of the input data using a secret key and a cryptographic hash function.
416+
417+
HMAC is used to verify both the data integrity and authenticity of a message. It is commonly used for:
418+
- Generating secure tokens and signatures
419+
- API request authentication
420+
- Webhook signature verification
421+
- Data integrity validation
422+
423+
### Example
424+
425+
#### Generate an HMAC for API authentication
426+
427+
```sql
428+
-- Generate a secure signature for an API request
429+
SELECT sqlpage.hmac(
430+
''user_id=123&action=update'',
431+
''my-secret-api-key'',
432+
''sha256''
433+
) as request_signature;
434+
```
435+
436+
#### Verify a webhook signature
437+
438+
```sql
439+
-- Verify that a webhook request is authentic
440+
SELECT
441+
CASE
442+
WHEN sqlpage.hmac(sqlpage.request_body(), ''webhook-secret'', ''sha256'') = :signature
443+
THEN ''Valid webhook''
444+
ELSE ''Invalid signature''
445+
END as status;
446+
```
447+
448+
#### Create a secure download token
449+
450+
```sql
451+
-- Generate a time-limited download token
452+
INSERT INTO download_tokens (file_id, token, expires_at)
453+
VALUES (
454+
:file_id,
455+
sqlpage.hmac(
456+
:file_id || ''|'' || datetime(''now'', ''+1 hour''),
457+
sqlpage.environment_variable(''SECRET_KEY''),
458+
''sha256''
459+
),
460+
datetime(''now'', ''+1 hour'')
461+
);
462+
```
463+
464+
### Notes
465+
466+
- The function returns a hexadecimal string representation of the HMAC.
467+
- If either `data` or `key` is NULL, the function returns NULL.
468+
- The `algorithm` parameter is optional and defaults to `sha256` if not specified.
469+
- Supported algorithms: `sha256`, `sha512`.
470+
- The key can be of any length. For maximum security, use a key that is at least as long as the hash output (32 bytes for SHA-256, 64 bytes for SHA-512).
471+
- Keep your secret keys secure and never expose them in client-side code or version control.
472+
'
473+
);
474+
INSERT INTO sqlpage_function_parameters (
475+
"function",
476+
"index",
477+
"name",
478+
"description_md",
479+
"type"
480+
)
481+
VALUES (
482+
'hmac',
483+
1,
484+
'data',
485+
'The input data to compute the HMAC for. Can be any text string.',
486+
'TEXT'
487+
),
488+
(
489+
'hmac',
490+
2,
491+
'key',
492+
'The secret key used to compute the HMAC. Should be kept confidential.',
493+
'TEXT'
494+
),
495+
(
496+
'hmac',
497+
3,
498+
'algorithm',
499+
'The hash algorithm to use. Optional, defaults to `sha256`. Supported values: `sha256`, `sha512`.',
500+
'TEXT'
404501
);

0 commit comments

Comments
 (0)