or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

aead-ciphers.mdasymmetric-cryptography.mdhash-functions.mdindex.mdkey-derivation.mdkey-serialization.mdmessage-authentication.mdsymmetric-ciphers.mdsymmetric-encryption.mdtwo-factor-auth.mdutilities.mdx509-certificates.md

symmetric-encryption.mddocs/

0

# Symmetric Encryption (Fernet)

1

2

High-level symmetric encryption using authenticated encryption. Fernet provides secure, simple encryption/decryption with built-in authentication, timestamp verification, and key rotation support. It uses AES-128 in CBC mode with HMAC-SHA256 for authentication.

3

4

## Core Imports

5

6

```python

7

from cryptography.fernet import Fernet, MultiFernet, InvalidToken

8

```

9

10

## Capabilities

11

12

### Basic Fernet Encryption

13

14

The `Fernet` class provides symmetric authenticated encryption with a simple API that handles all cryptographic details automatically.

15

16

```python { .api }

17

class Fernet:

18

def __init__(self, key: bytes | str, backend: typing.Any = None):

19

"""

20

Initialize Fernet with a 32-byte base64url-encoded key.

21

22

Args:

23

key (bytes | str): 32-byte base64url-encoded key from generate_key()

24

backend (typing.Any, optional): Cryptographic backend (usually None)

25

"""

26

27

@classmethod

28

def generate_key(cls) -> bytes:

29

"""

30

Generate a fresh fernet key for encryption.

31

32

Returns:

33

bytes: 32-byte base64url-encoded key suitable for Fernet()

34

"""

35

36

def encrypt(self, data: bytes) -> bytes:

37

"""

38

Encrypt data with current timestamp.

39

40

Args:

41

data (bytes): Plaintext data to encrypt

42

43

Returns:

44

bytes: Encrypted token with embedded timestamp

45

"""

46

47

def encrypt_at_time(self, data: bytes, current_time: int) -> bytes:

48

"""

49

Encrypt data with a specific timestamp.

50

51

Args:

52

data (bytes): Plaintext data to encrypt

53

current_time (int): Unix timestamp to embed in token

54

55

Returns:

56

bytes: Encrypted token with specified timestamp

57

"""

58

59

def decrypt(self, token: bytes | str, ttl: int | None = None) -> bytes:

60

"""

61

Decrypt a token, optionally verifying age.

62

63

Args:

64

token (bytes): Encrypted token from encrypt()

65

ttl (int, optional): Maximum age in seconds

66

67

Returns:

68

bytes: Decrypted plaintext data

69

70

Raises:

71

InvalidToken: If token is invalid, expired, or too old

72

"""

73

74

def decrypt_at_time(self, token: bytes | str, ttl: int, current_time: int) -> bytes:

75

"""

76

Decrypt a token at a specific time with TTL verification.

77

78

Args:

79

token (bytes): Encrypted token from encrypt()

80

ttl (int): Maximum age in seconds

81

current_time (int): Unix timestamp to use as current time

82

83

Returns:

84

bytes: Decrypted plaintext data

85

86

Raises:

87

InvalidToken: If token is invalid, expired, or too old

88

"""

89

90

def extract_timestamp(self, token: bytes | str) -> int:

91

"""

92

Extract timestamp from a token without decrypting.

93

94

Args:

95

token (bytes): Encrypted token

96

97

Returns:

98

int: Unix timestamp when token was created

99

100

Raises:

101

InvalidToken: If token format is invalid

102

"""

103

```

104

105

### Key Rotation with MultiFernet

106

107

The `MultiFernet` class enables key rotation by maintaining multiple Fernet instances, encrypting with the first key and attempting decryption with all keys.

108

109

```python { .api }

110

class MultiFernet:

111

def __init__(self, fernets: List[Fernet]):

112

"""

113

Initialize with multiple Fernet instances for key rotation.

114

115

Args:

116

fernets (List[Fernet]): List of Fernet instances, first used for encryption

117

"""

118

119

def encrypt(self, msg: bytes) -> bytes:

120

"""

121

Encrypt message using the first Fernet instance.

122

123

Args:

124

msg (bytes): Plaintext data to encrypt

125

126

Returns:

127

bytes: Encrypted token

128

"""

129

130

def encrypt_at_time(self, msg: bytes, current_time: int) -> bytes:

131

"""

132

Encrypt message at specific time using first Fernet instance.

133

134

Args:

135

msg (bytes): Plaintext data to encrypt

136

current_time (int): Unix timestamp to embed

137

138

Returns:

139

bytes: Encrypted token with specified timestamp

140

"""

141

142

def decrypt(self, msg: bytes, ttl: int = None) -> bytes:

143

"""

144

Decrypt message trying each Fernet instance until successful.

145

146

Args:

147

msg (bytes): Encrypted token

148

ttl (int, optional): Maximum age in seconds

149

150

Returns:

151

bytes: Decrypted plaintext data

152

153

Raises:

154

InvalidToken: If no Fernet instance can decrypt the token

155

"""

156

157

def decrypt_at_time(self, msg: bytes, ttl: int, current_time: int) -> bytes:

158

"""

159

Decrypt at specific time trying each Fernet instance.

160

161

Args:

162

msg (bytes): Encrypted token

163

ttl (int): Maximum age in seconds

164

current_time (int): Unix timestamp as current time

165

166

Returns:

167

bytes: Decrypted plaintext data

168

169

Raises:

170

InvalidToken: If no Fernet instance can decrypt the token

171

"""

172

173

def rotate(self, msg: bytes) -> bytes:

174

"""

175

Re-encrypt message with the first (newest) key.

176

177

Args:

178

msg (bytes): Token encrypted with any of the Fernet instances

179

180

Returns:

181

bytes: Token re-encrypted with first Fernet instance

182

183

Raises:

184

InvalidToken: If message cannot be decrypted with any key

185

"""

186

187

def extract_timestamp(self, msg: bytes) -> int:

188

"""

189

Extract timestamp from token using any available key.

190

191

Args:

192

msg (bytes): Encrypted token

193

194

Returns:

195

int: Unix timestamp when token was created

196

197

Raises:

198

InvalidToken: If no key can validate the token format

199

"""

200

```

201

202

### Exception Handling

203

204

```python { .api }

205

class InvalidToken(Exception):

206

"""

207

Raised when a token is invalid, malformed, expired, or fails authentication.

208

This includes cases where:

209

- Token format is incorrect

210

- Authentication check fails

211

- Token is older than specified TTL

212

- Token timestamp is in the future (with clock skew tolerance)

213

"""

214

```

215

216

## Usage Examples

217

218

### Basic Encryption/Decryption

219

220

```python

221

from cryptography.fernet import Fernet

222

223

# Generate a key

224

key = Fernet.generate_key()

225

fernet = Fernet(key)

226

227

# Encrypt sensitive data

228

sensitive_data = b"user_id:12345,session:abc123"

229

token = fernet.encrypt(sensitive_data)

230

231

# Later, decrypt the data

232

try:

233

decrypted_data = fernet.decrypt(token)

234

print(decrypted_data) # b"user_id:12345,session:abc123"

235

except InvalidToken:

236

print("Invalid or expired token")

237

```

238

239

### Time-based Token Validation

240

241

```python

242

from cryptography.fernet import Fernet, InvalidToken

243

import time

244

245

key = Fernet.generate_key()

246

fernet = Fernet(key)

247

248

# Create a token

249

data = b"temporary data"

250

token = fernet.encrypt(data)

251

252

# Decrypt with TTL - only valid for 60 seconds

253

try:

254

decrypted = fernet.decrypt(token, ttl=60)

255

print("Token is fresh:", decrypted)

256

except InvalidToken:

257

print("Token expired or invalid")

258

259

# Check token age without decrypting

260

timestamp = fernet.extract_timestamp(token)

261

age = time.time() - timestamp

262

print(f"Token age: {age} seconds")

263

```

264

265

### Key Rotation

266

267

```python

268

from cryptography.fernet import Fernet, MultiFernet

269

270

# Current encryption key

271

new_key = Fernet.generate_key()

272

new_fernet = Fernet(new_key)

273

274

# Previous keys for decryption

275

old_key1 = Fernet.generate_key() # Previous key

276

old_key2 = Fernet.generate_key() # Even older key

277

old_fernet1 = Fernet(old_key1)

278

old_fernet2 = Fernet(old_key2)

279

280

# MultiFernet with new key first (for encryption)

281

multi_fernet = MultiFernet([

282

new_fernet, # Used for encryption

283

old_fernet1, # Can decrypt old tokens

284

old_fernet2 # Can decrypt even older tokens

285

])

286

287

# Encrypt with new key

288

token = multi_fernet.encrypt(b"data")

289

290

# Can decrypt tokens encrypted with any key

291

decrypted = multi_fernet.decrypt(token)

292

293

# Re-encrypt old token with new key

294

old_token = old_fernet1.encrypt(b"old data")

295

rotated_token = multi_fernet.rotate(old_token) # Now encrypted with new_fernet

296

```

297

298

### Secure Session Management

299

300

```python

301

from cryptography.fernet import Fernet, InvalidToken

302

import json

303

import time

304

305

class SecureSession:

306

def __init__(self, secret_key):

307

self.fernet = Fernet(secret_key)

308

309

def create_session_token(self, user_id, session_data):

310

"""Create encrypted session token"""

311

payload = {

312

'user_id': user_id,

313

'data': session_data,

314

'created': time.time()

315

}

316

json_payload = json.dumps(payload).encode()

317

return self.fernet.encrypt(json_payload)

318

319

def validate_session(self, token, max_age=3600):

320

"""Validate and extract session data"""

321

try:

322

# Decrypt with TTL check

323

decrypted = self.fernet.decrypt(token, ttl=max_age)

324

payload = json.loads(decrypted.decode())

325

return payload

326

except InvalidToken:

327

return None

328

329

# Usage

330

session_key = Fernet.generate_key()

331

session_manager = SecureSession(session_key)

332

333

# Create session

334

token = session_manager.create_session_token(

335

user_id=12345,

336

session_data={'role': 'admin', 'permissions': ['read', 'write']}

337

)

338

339

# Validate session (within 1 hour)

340

session_data = session_manager.validate_session(token, max_age=3600)

341

if session_data:

342

print(f"Valid session for user {session_data['user_id']}")

343

else:

344

print("Invalid or expired session")

345

```

346

347

## Security Considerations

348

349

- **Key Management**: Store Fernet keys securely, never hardcode them

350

- **Token Storage**: Treat encrypted tokens as sensitive data

351

- **TTL Usage**: Always use TTL for time-sensitive data

352

- **Key Rotation**: Regularly rotate keys using MultiFernet

353

- **Clock Skew**: Fernet allows 60 seconds of clock skew tolerance

354

- **Token Size**: Encrypted tokens are larger than original data (base64 overhead + metadata)