0
# Message Authentication
1
2
Message authentication codes (MAC) using HMAC-SHA512 and Poly1305 for data integrity verification and authenticity.
3
4
## Capabilities
5
6
### HMAC-SHA512 Authentication
7
8
Compute and verify HMAC-SHA512 message authentication codes.
9
10
```javascript { .api }
11
/**
12
* Compute HMAC-SHA512 authentication tag
13
* @param out - Output buffer for authentication tag (must be BYTES long)
14
* @param input - Input buffer to authenticate
15
* @param k - Key buffer (must be KEYBYTES long)
16
* @throws Error if buffer sizes are incorrect or authentication fails
17
*/
18
function crypto_auth(out: Buffer, input: Buffer, k: Buffer): void;
19
20
/**
21
* Verify HMAC-SHA512 authentication tag
22
* @param h - Authentication tag to verify (must be BYTES long)
23
* @param input - Input buffer that was authenticated
24
* @param k - Key buffer (must be KEYBYTES long)
25
* @returns true if tag is valid, false otherwise
26
*/
27
function crypto_auth_verify(h: Buffer, input: Buffer, k: Buffer): boolean;
28
```
29
30
**Usage Example:**
31
32
```javascript
33
const sodium = require('sodium-native');
34
35
// Generate authentication key
36
const key = Buffer.alloc(sodium.crypto_auth_KEYBYTES);
37
sodium.randombytes_buf(key);
38
39
// Authenticate a message
40
const message = Buffer.from('Important message');
41
const tag = Buffer.alloc(sodium.crypto_auth_BYTES);
42
sodium.crypto_auth(tag, message, key);
43
44
// Verify the authentication tag
45
if (sodium.crypto_auth_verify(tag, message, key)) {
46
console.log('Message is authentic');
47
} else {
48
console.log('Message authentication failed');
49
}
50
```
51
52
### Poly1305 One-time Authentication
53
54
Poly1305 MAC for high-performance message authentication with one-time keys.
55
56
```javascript { .api }
57
/**
58
* Compute Poly1305 authentication tag
59
* @param out - Output buffer for authentication tag (must be BYTES long)
60
* @param input - Input buffer to authenticate
61
* @param k - One-time key buffer (must be KEYBYTES long)
62
* @throws Error if buffer sizes are incorrect or authentication fails
63
*/
64
function crypto_onetimeauth(out: Buffer, input: Buffer, k: Buffer): void;
65
66
/**
67
* Verify Poly1305 authentication tag
68
* @param h - Authentication tag to verify (must be BYTES long)
69
* @param input - Input buffer that was authenticated
70
* @param k - One-time key buffer (must be KEYBYTES long)
71
* @returns true if tag is valid, false otherwise
72
*/
73
function crypto_onetimeauth_verify(h: Buffer, input: Buffer, k: Buffer): boolean;
74
```
75
76
### Poly1305 Streaming Authentication
77
78
Initialize, update, and finalize Poly1305 authentication for large data.
79
80
```javascript { .api }
81
/**
82
* Initialize Poly1305 streaming authentication
83
* @param state - State buffer (must be STATEBYTES long)
84
* @param k - One-time key buffer (must be KEYBYTES long)
85
* @throws Error if initialization fails or buffer sizes incorrect
86
*/
87
function crypto_onetimeauth_init(state: Buffer, k: Buffer): void;
88
89
/**
90
* Update Poly1305 streaming authentication with more data
91
* @param state - State buffer from init
92
* @param input - Input buffer to add to authentication
93
* @throws Error if update fails
94
*/
95
function crypto_onetimeauth_update(state: Buffer, input: Buffer): void;
96
97
/**
98
* Finalize Poly1305 streaming authentication and get tag
99
* @param state - State buffer from init/update
100
* @param out - Output buffer for authentication tag (must be BYTES long)
101
* @throws Error if finalization fails
102
*/
103
function crypto_onetimeauth_final(state: Buffer, out: Buffer): void;
104
```
105
106
**Usage Example:**
107
108
```javascript
109
const sodium = require('sodium-native');
110
111
// Generate one-time key
112
const key = Buffer.alloc(sodium.crypto_onetimeauth_KEYBYTES);
113
sodium.randombytes_buf(key);
114
115
// Streaming authentication for large data
116
const state = Buffer.alloc(sodium.crypto_onetimeauth_STATEBYTES);
117
sodium.crypto_onetimeauth_init(state, key);
118
119
// Process data in chunks
120
const chunk1 = Buffer.from('First part of ');
121
const chunk2 = Buffer.from('large message');
122
123
sodium.crypto_onetimeauth_update(state, chunk1);
124
sodium.crypto_onetimeauth_update(state, chunk2);
125
126
const tag = Buffer.alloc(sodium.crypto_onetimeauth_BYTES);
127
sodium.crypto_onetimeauth_final(state, tag);
128
129
// Verify using one-shot function
130
const fullMessage = Buffer.concat([chunk1, chunk2]);
131
if (sodium.crypto_onetimeauth_verify(tag, fullMessage, key)) {
132
console.log('Message is authentic');
133
}
134
```
135
136
## Constants
137
138
```javascript { .api }
139
// HMAC-SHA512 constants
140
const crypto_auth_BYTES: number;
141
const crypto_auth_KEYBYTES: number;
142
143
// Poly1305 constants
144
const crypto_onetimeauth_STATEBYTES: number;
145
const crypto_onetimeauth_BYTES: number;
146
const crypto_onetimeauth_KEYBYTES: number;
147
```
148
149
## Security Considerations
150
151
- **Key Reuse**: HMAC keys can be reused safely, but Poly1305 keys must be used only once.
152
- **Key Generation**: Use cryptographically secure random key generation.
153
- **Timing Attacks**: Both implementations use constant-time comparison for verification.
154
- **Key Management**: Store authentication keys securely and separately from data.
155
156
## Common Patterns
157
158
### Message Integrity Service
159
160
```javascript
161
const sodium = require('sodium-native');
162
163
class MessageIntegrity {
164
constructor() {
165
this.hmacKey = Buffer.alloc(sodium.crypto_auth_KEYBYTES);
166
sodium.randombytes_buf(this.hmacKey);
167
}
168
169
sign(message) {
170
const tag = Buffer.alloc(sodium.crypto_auth_BYTES);
171
sodium.crypto_auth(tag, message, this.hmacKey);
172
173
return {
174
message: message,
175
tag: tag
176
};
177
}
178
179
verify(signedMessage) {
180
return sodium.crypto_auth_verify(
181
signedMessage.tag,
182
signedMessage.message,
183
this.hmacKey
184
);
185
}
186
187
// For one-time use (e.g., network packets)
188
signOneTime(message, oneTimeKey) {
189
const tag = Buffer.alloc(sodium.crypto_onetimeauth_BYTES);
190
sodium.crypto_onetimeauth(tag, message, oneTimeKey);
191
return tag;
192
}
193
194
verifyOneTime(message, tag, oneTimeKey) {
195
return sodium.crypto_onetimeauth_verify(tag, message, oneTimeKey);
196
}
197
}
198
```
199
200
### File Integrity Checker
201
202
```javascript
203
const sodium = require('sodium-native');
204
const fs = require('fs');
205
206
class FileIntegrityChecker {
207
constructor(keyFile) {
208
if (fs.existsSync(keyFile)) {
209
this.key = fs.readFileSync(keyFile);
210
} else {
211
this.key = Buffer.alloc(sodium.crypto_auth_KEYBYTES);
212
sodium.randombytes_buf(this.key);
213
fs.writeFileSync(keyFile, this.key);
214
}
215
}
216
217
createChecksum(filename) {
218
const data = fs.readFileSync(filename);
219
const checksum = Buffer.alloc(sodium.crypto_auth_BYTES);
220
sodium.crypto_auth(checksum, data, this.key);
221
222
// Save checksum file
223
fs.writeFileSync(`${filename}.checksum`, checksum);
224
return checksum;
225
}
226
227
verifyChecksum(filename) {
228
const checksumFile = `${filename}.checksum`;
229
if (!fs.existsSync(checksumFile)) {
230
throw new Error('Checksum file not found');
231
}
232
233
const data = fs.readFileSync(filename);
234
const storedChecksum = fs.readFileSync(checksumFile);
235
236
return sodium.crypto_auth_verify(storedChecksum, data, this.key);
237
}
238
239
// For streaming large files
240
createStreamingChecksum(filename) {
241
const state = Buffer.alloc(sodium.crypto_auth_STATEBYTES);
242
sodium.crypto_auth_init(state, this.key);
243
244
const fileStream = fs.createReadStream(filename, { highWaterMark: 64 * 1024 });
245
246
return new Promise((resolve, reject) => {
247
fileStream.on('data', (chunk) => {
248
sodium.crypto_auth_update(state, chunk);
249
});
250
251
fileStream.on('end', () => {
252
const checksum = Buffer.alloc(sodium.crypto_auth_BYTES);
253
sodium.crypto_auth_final(state, checksum);
254
resolve(checksum);
255
});
256
257
fileStream.on('error', reject);
258
});
259
}
260
}
261
```
262
263
### API Request Authentication
264
265
```javascript
266
const sodium = require('sodium-native');
267
268
class APIAuth {
269
constructor(secretKey) {
270
this.secretKey = Buffer.from(secretKey);
271
if (this.secretKey.length !== sodium.crypto_auth_KEYBYTES) {
272
throw new Error('Invalid secret key length');
273
}
274
}
275
276
signRequest(method, url, body, timestamp) {
277
const message = Buffer.from(`${method}|${url}|${body}|${timestamp}`);
278
const signature = Buffer.alloc(sodium.crypto_auth_BYTES);
279
280
sodium.crypto_auth(signature, message, this.secretKey);
281
282
return {
283
signature: signature.toString('hex'),
284
timestamp: timestamp
285
};
286
}
287
288
verifyRequest(method, url, body, signature, timestamp) {
289
const message = Buffer.from(`${method}|${url}|${body}|${timestamp}`);
290
const signatureBuffer = Buffer.from(signature, 'hex');
291
292
if (signatureBuffer.length !== sodium.crypto_auth_BYTES) {
293
return false;
294
}
295
296
return sodium.crypto_auth_verify(signatureBuffer, message, this.secretKey);
297
}
298
}
299
300
// Usage
301
const apiAuth = new APIAuth('your-secret-key-32-bytes-long!!!!!');
302
const timestamp = Date.now();
303
const auth = apiAuth.signRequest('GET', '/api/users', '', timestamp);
304
305
console.log('Authorization:', auth.signature);
306
console.log('Timestamp:', auth.timestamp);
307
```