or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

big-number-arithmetic.mdbit-array-utilities.mdcipher-modes.mddata-encoding.mdelliptic-curve-cryptography.mdhash-functions.mdhigh-level-encryption.mdindex.mdkey-derivation.mdkey-exchange.mdmessage-authentication.mdrandom-number-generation.mdsymmetric-encryption.md

message-authentication.mddocs/

0

# Message Authentication

1

2

SJCL provides HMAC (Hash-based Message Authentication Code) for message authentication and integrity verification, ensuring that data has not been tampered with and comes from an authenticated source.

3

4

## Capabilities

5

6

### HMAC (Hash-based Message Authentication Code)

7

8

Provides message authentication using cryptographic hash functions, combining a secret key with hash algorithms for secure authentication.

9

10

```javascript { .api }

11

/**

12

* HMAC constructor

13

* @param {BitArray} key - Secret key for HMAC (any length)

14

* @param {Function} [Hash] - Hash function to use (default: SHA-256)

15

* @throws {sjcl.exception.invalid} If key or hash function is invalid

16

*/

17

new sjcl.misc.hmac(key, Hash);

18

```

19

20

**Instance Methods:**

21

22

```javascript { .api }

23

/**

24

* Compute HMAC of data (alias for mac method)

25

* @param {BitArray|string} data - Data to authenticate

26

* @returns {BitArray} HMAC authentication tag

27

* @throws {sjcl.exception.invalid} If called after update without reset

28

*/

29

sjcl.misc.hmac.prototype.encrypt(data);

30

31

/**

32

* Compute HMAC of data

33

* @param {BitArray|string} data - Data to authenticate

34

* @returns {BitArray} HMAC authentication tag

35

* @throws {sjcl.exception.invalid} If called after update without reset

36

*/

37

sjcl.misc.hmac.prototype.mac(data);

38

39

/**

40

* Reset HMAC state for reuse

41

* @returns {sjcl.misc.hmac} This HMAC instance for chaining

42

*/

43

sjcl.misc.hmac.prototype.reset();

44

45

/**

46

* Add data to HMAC computation (streaming interface)

47

* @param {BitArray|string} data - Data to add to HMAC

48

* @returns {sjcl.misc.hmac} This HMAC instance for chaining

49

*/

50

sjcl.misc.hmac.prototype.update(data);

51

52

/**

53

* Complete HMAC computation and return result

54

* @returns {BitArray} Final HMAC authentication tag

55

*/

56

sjcl.misc.hmac.prototype.digest();

57

```

58

59

**Usage Examples:**

60

61

```javascript

62

const sjcl = require('sjcl');

63

64

// Basic HMAC with SHA-256 (default)

65

const key = sjcl.random.randomWords(8); // 256-bit key

66

const hmac = new sjcl.misc.hmac(key);

67

const data = "Hello, World!";

68

const authTag = hmac.encrypt(data);

69

70

console.log("HMAC:", sjcl.codec.hex.fromBits(authTag));

71

72

// Verify HMAC

73

const hmacVerify = new sjcl.misc.hmac(key);

74

const computedTag = hmacVerify.encrypt(data);

75

const isValid = sjcl.bitArray.equal(authTag, computedTag);

76

console.log("HMAC valid:", isValid);

77

78

// Using different hash functions

79

const hmacSHA1 = new sjcl.misc.hmac(key, sjcl.hash.sha1);

80

const hmacSHA512 = new sjcl.misc.hmac(key, sjcl.hash.sha512);

81

82

const tagSHA1 = hmacSHA1.encrypt(data);

83

const tagSHA512 = hmacSHA512.encrypt(data);

84

```

85

86

### Streaming HMAC

87

88

For large data or when data arrives in chunks, use the streaming interface:

89

90

```javascript

91

const sjcl = require('sjcl');

92

93

// Streaming HMAC computation

94

const key = sjcl.random.randomWords(8);

95

const hmac = new sjcl.misc.hmac(key);

96

97

// Add data in chunks

98

hmac.update("First chunk ");

99

hmac.update("Second chunk ");

100

hmac.update("Final chunk");

101

102

// Get final result

103

const streamingTag = hmac.digest();

104

105

// Compare with one-shot computation

106

const hmacOneShot = new sjcl.misc.hmac(key);

107

const oneShotTag = hmacOneShot.encrypt("First chunk Second chunk Final chunk");

108

109

console.log("Tags equal:", sjcl.bitArray.equal(streamingTag, oneShotTag));

110

111

// Reset for reuse

112

hmac.reset();

113

const newTag = hmac.encrypt("New data");

114

```

115

116

### HMAC Key Management

117

118

Proper key management is crucial for HMAC security:

119

120

```javascript

121

const sjcl = require('sjcl');

122

123

// Generate HMAC keys

124

function generateHMACKey(keySize = 256) {

125

// Key should be at least as long as hash output

126

const minSize = 256; // SHA-256 output size

127

const actualSize = Math.max(keySize, minSize);

128

return sjcl.random.randomWords(actualSize / 32);

129

}

130

131

// Derive HMAC key from password

132

function deriveHMACKey(password, salt, iterations = 100000) {

133

return sjcl.misc.pbkdf2(password, salt, iterations, 256);

134

}

135

136

// Multiple HMAC keys from master key

137

function deriveHMACKeys(masterKey, purposes) {

138

const keys = {};

139

purposes.forEach(purpose => {

140

const info = sjcl.codec.utf8String.toBits(purpose);

141

keys[purpose] = sjcl.misc.hkdf(masterKey, 256, null, info);

142

});

143

return keys;

144

}

145

146

// Usage

147

const masterKey = sjcl.random.randomWords(8);

148

const keys = deriveHMACKeys(masterKey, ['authentication', 'integrity', 'session']);

149

150

const authHMAC = new sjcl.misc.hmac(keys.authentication);

151

const integrityHMAC = new sjcl.misc.hmac(keys.integrity);

152

```

153

154

## Authentication Patterns

155

156

### Message Authentication

157

158

Simple message authentication with HMAC:

159

160

```javascript

161

const sjcl = require('sjcl');

162

163

class MessageAuthenticator {

164

constructor(key) {

165

this.key = key;

166

}

167

168

authenticate(message) {

169

const hmac = new sjcl.misc.hmac(this.key);

170

const tag = hmac.encrypt(message);

171

172

return {

173

message: message,

174

tag: sjcl.codec.hex.fromBits(tag)

175

};

176

}

177

178

verify(message, hexTag) {

179

const hmac = new sjcl.misc.hmac(this.key);

180

const computedTag = hmac.encrypt(message);

181

const providedTag = sjcl.codec.hex.toBits(hexTag);

182

183

return sjcl.bitArray.equal(computedTag, providedTag);

184

}

185

}

186

187

// Usage

188

const authKey = sjcl.random.randomWords(8);

189

const authenticator = new MessageAuthenticator(authKey);

190

191

const authenticated = authenticator.authenticate("Important message");

192

console.log(authenticated);

193

194

const isValid = authenticator.verify("Important message", authenticated.tag);

195

console.log("Message valid:", isValid);

196

```

197

198

### Encrypt-then-MAC

199

200

Secure pattern combining encryption with authentication:

201

202

```javascript

203

const sjcl = require('sjcl');

204

205

function encryptThenMAC(plaintext, encKey, macKey) {

206

// Encrypt first

207

const iv = sjcl.random.randomWords(4);

208

const aes = new sjcl.cipher.aes(encKey);

209

const ciphertext = sjcl.mode.gcm.encrypt(aes, plaintext, iv);

210

211

// Then authenticate the ciphertext + IV

212

const hmac = new sjcl.misc.hmac(macKey);

213

const dataToAuth = sjcl.bitArray.concat(iv, ciphertext);

214

const authTag = hmac.encrypt(dataToAuth);

215

216

return {\n iv: iv,\n ciphertext: ciphertext,\n authTag: authTag\n };\n}\n\nfunction verifyThenDecrypt(encrypted, encKey, macKey) {\n // Verify authentication first\n const hmac = new sjcl.misc.hmac(macKey);\n const dataToAuth = sjcl.bitArray.concat(encrypted.iv, encrypted.ciphertext);\n const computedTag = hmac.encrypt(dataToAuth);\n \n if (!sjcl.bitArray.equal(computedTag, encrypted.authTag)) {\n throw new sjcl.exception.corrupt(\"Authentication failed\");\n }\n \n // Then decrypt\n const aes = new sjcl.cipher.aes(encKey);\n return sjcl.mode.gcm.decrypt(aes, encrypted.ciphertext, encrypted.iv);\n}\n\n// Usage\nconst encKey = sjcl.random.randomWords(8);\nconst macKey = sjcl.random.randomWords(8);\nconst plaintext = sjcl.codec.utf8String.toBits(\"Secret message\");\n\nconst encrypted = encryptThenMAC(plaintext, encKey, macKey);\nconst decrypted = verifyThenDecrypt(encrypted, encKey, macKey);\n\nconsole.log(sjcl.codec.utf8String.fromBits(decrypted));\n```\n\n### Time-safe HMAC Verification\n\nPrevent timing attacks when verifying HMAC tags:\n\n```javascript\nconst sjcl = require('sjcl');\n\nfunction timeSafeHMACVerify(key, data, providedTag) {\n const hmac = new sjcl.misc.hmac(key);\n const computedTag = hmac.encrypt(data);\n \n // Use bitArray.equal for constant-time comparison\n return sjcl.bitArray.equal(computedTag, providedTag);\n}\n\n// Usage\nconst key = sjcl.random.randomWords(8);\nconst data = \"sensitive data\";\nconst hmac = new sjcl.misc.hmac(key);\nconst tag = hmac.encrypt(data);\n\n// This is time-safe\nconst isValid1 = timeSafeHMACVerify(key, data, tag);\n\n// This is NOT time-safe (don't do this)\nconst hmac2 = new sjcl.misc.hmac(key);\nconst tag2 = hmac2.encrypt(data);\nconst hexTag1 = sjcl.codec.hex.fromBits(tag);\nconst hexTag2 = sjcl.codec.hex.fromBits(tag2);\nconst isValid2 = hexTag1 === hexTag2; // Vulnerable to timing attacks\n```\n\n## API Authentication\n\n### Request Signing\n\nSign API requests with HMAC for authentication:\n\n```javascript\nconst sjcl = require('sjcl');\n\nclass APIRequestSigner {\n constructor(apiKey, secretKey) {\n this.apiKey = apiKey;\n this.secretKey = sjcl.codec.utf8String.toBits(secretKey);\n }\n \n signRequest(method, url, body, timestamp) {\n // Create string to sign\n const stringToSign = [method, url, body || '', timestamp].join('\\n');\n \n // Generate HMAC signature\n const hmac = new sjcl.misc.hmac(this.secretKey);\n const signature = hmac.encrypt(stringToSign);\n \n return {\n apiKey: this.apiKey,\n timestamp: timestamp,\n signature: sjcl.codec.base64.fromBits(signature)\n };\n }\n \n verifyRequest(method, url, body, timestamp, signature) {\n const expected = this.signRequest(method, url, body, timestamp);\n const providedSig = sjcl.codec.base64.toBits(signature);\n const expectedSig = sjcl.codec.base64.toBits(expected.signature);\n \n return sjcl.bitArray.equal(providedSig, expectedSig);\n }\n}\n\n// Usage\nconst signer = new APIRequestSigner('api123', 'secret456');\nconst timestamp = Date.now().toString();\n\nconst signature = signer.signRequest('POST', '/api/data', '{\"test\":true}', timestamp);\nconsole.log('Authorization:', `HMAC ${signature.apiKey}:${signature.signature}`);\n\nconst isValid = signer.verifyRequest(\n 'POST', \n '/api/data', \n '{\"test\":true}', \n timestamp, \n signature.signature\n);\nconsole.log('Request valid:', isValid);\n```\n\n## Security Recommendations\n\n1. **Key Length**: Use keys at least as long as the hash output (256 bits for SHA-256)\n2. **Key Generation**: Generate keys using cryptographically secure random number generators\n3. **Key Derivation**: Use PBKDF2/scrypt/HKDF for deriving keys from passwords\n4. **Constant-time Verification**: Always use `sjcl.bitArray.equal()` for tag comparison\n5. **Hash Function**: Use SHA-256 or SHA-512, avoid SHA-1\n6. **Tag Length**: Use full-length tags, don't truncate unless absolutely necessary\n7. **Separate Keys**: Use different keys for different purposes (encryption vs authentication)\n\n## Common Pitfalls\n\n1. **Timing Attacks**: Never use string comparison for HMAC verification\n2. **Key Reuse**: Don't use encryption keys for HMAC\n3. **Weak Keys**: Don't use predictable or short keys\n4. **MAC-then-Encrypt**: Always use Encrypt-then-MAC pattern\n5. **Tag Truncation**: Avoid truncating HMAC tags without security analysis