Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jose/jwe.py: 29%

207 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:16 +0000

1import binascii 

2import json 

3import zlib 

4from collections.abc import Mapping 

5from struct import pack 

6 

7from . import jwk 

8from .backends import get_random_bytes 

9from .constants import ALGORITHMS, ZIPS 

10from .exceptions import JWEError, JWEParseError 

11from .utils import base64url_decode, base64url_encode, ensure_binary 

12 

13 

14def encrypt(plaintext, key, encryption=ALGORITHMS.A256GCM, algorithm=ALGORITHMS.DIR, zip=None, cty=None, kid=None): 

15 """Encrypts plaintext and returns a JWE cmpact serialization string. 

16 

17 Args: 

18 plaintext (bytes): A bytes object to encrypt 

19 key (str or dict): The key(s) to use for encrypting the content. Can be 

20 individual JWK or JWK set. 

21 encryption (str, optional): The content encryption algorithm used to 

22 perform authenticated encryption on the plaintext to produce the 

23 ciphertext and the Authentication Tag. Defaults to A256GCM. 

24 algorithm (str, optional): The cryptographic algorithm used 

25 to encrypt or determine the value of the CEK. Defaults to dir. 

26 zip (str, optional): The compression algorithm) applied to the 

27 plaintext before encryption. Defaults to None. 

28 cty (str, optional): The media type for the secured content. 

29 See http://www.iana.org/assignments/media-types/media-types.xhtml 

30 kid (str, optional): Key ID for the provided key 

31 

32 Returns: 

33 bytes: The string representation of the header, encrypted key, 

34 initialization vector, ciphertext, and authentication tag. 

35 

36 Raises: 

37 JWEError: If there is an error signing the token. 

38 

39 Examples: 

40 >>> from jose import jwe 

41 >>> jwe.encrypt('Hello, World!', 'asecret128bitkey', algorithm='dir', encryption='A128GCM') 

42 'eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..McILMB3dYsNJSuhcDzQshA.OfX9H_mcUpHDeRM4IA.CcnTWqaqxNsjT4eCaUABSg' 

43 

44 """ 

45 plaintext = ensure_binary(plaintext) # Make sure it's bytes 

46 if algorithm not in ALGORITHMS.SUPPORTED: 

47 raise JWEError("Algorithm %s not supported." % algorithm) 

48 if encryption not in ALGORITHMS.SUPPORTED: 

49 raise JWEError("Algorithm %s not supported." % encryption) 

50 key = jwk.construct(key, algorithm) 

51 encoded_header = _encoded_header(algorithm, encryption, zip, cty, kid) 

52 

53 plaintext = _compress(zip, plaintext) 

54 enc_cek, iv, cipher_text, auth_tag = _encrypt_and_auth(key, algorithm, encryption, zip, plaintext, encoded_header) 

55 

56 jwe_string = _jwe_compact_serialize(encoded_header, enc_cek, iv, cipher_text, auth_tag) 

57 return jwe_string 

58 

59 

60def decrypt(jwe_str, key): 

61 """Decrypts a JWE compact serialized string and returns the plaintext. 

62 

63 Args: 

64 jwe_str (str): A JWE to be decrypt. 

65 key (str or dict): A key to attempt to decrypt the payload with. Can be 

66 individual JWK or JWK set. 

67 

68 Returns: 

69 bytes: The plaintext bytes, assuming the authentication tag is valid. 

70 

71 Raises: 

72 JWEError: If there is an exception verifying the token. 

73 

74 Examples: 

75 >>> from jose import jwe 

76 >>> jwe.decrypt(jwe_string, 'asecret128bitkey') 

77 'Hello, World!' 

78 """ 

79 header, encoded_header, encrypted_key, iv, cipher_text, auth_tag = _jwe_compact_deserialize(jwe_str) 

80 

81 # Verify that the implementation understands and can process all 

82 # fields that it is required to support, whether required by this 

83 # specification, by the algorithms being used, or by the "crit" 

84 # Header Parameter value, and that the values of those parameters 

85 # are also understood and supported. 

86 

87 try: 

88 # Determine the Key Management Mode employed by the algorithm 

89 # specified by the "alg" (algorithm) Header Parameter. 

90 alg = header["alg"] 

91 enc = header["enc"] 

92 if alg not in ALGORITHMS.SUPPORTED: 

93 raise JWEError("Algorithm %s not supported." % alg) 

94 if enc not in ALGORITHMS.SUPPORTED: 

95 raise JWEError("Algorithm %s not supported." % enc) 

96 

97 except KeyError: 

98 raise JWEParseError("alg and enc headers are required!") 

99 

100 # Verify that the JWE uses a key known to the recipient. 

101 key = jwk.construct(key, alg) 

102 

103 # When Direct Key Agreement or Key Agreement with Key Wrapping are 

104 # employed, use the key agreement algorithm to compute the value 

105 # of the agreed upon key. When Direct Key Agreement is employed, 

106 # let the CEK be the agreed upon key. When Key Agreement with Key 

107 # Wrapping is employed, the agreed upon key will be used to 

108 # decrypt the JWE Encrypted Key. 

109 # 

110 # When Key Wrapping, Key Encryption, or Key Agreement with Key 

111 # Wrapping are employed, decrypt the JWE Encrypted Key to produce 

112 # the CEK. The CEK MUST have a length equal to that required for 

113 # the content encryption algorithm. Note that when there are 

114 # multiple recipients, each recipient will only be able to decrypt 

115 # JWE Encrypted Key values that were encrypted to a key in that 

116 # recipient's possession. It is therefore normal to only be able 

117 # to decrypt one of the per-recipient JWE Encrypted Key values to 

118 # obtain the CEK value. Also, see Section 11.5 for security 

119 # considerations on mitigating timing attacks. 

120 if alg == ALGORITHMS.DIR: 

121 # When Direct Key Agreement or Direct Encryption are employed, 

122 # verify that the JWE Encrypted Key value is an empty octet 

123 # sequence. 

124 

125 # Record whether the CEK could be successfully determined for this 

126 # recipient or not. 

127 cek_valid = encrypted_key == b"" 

128 

129 # When Direct Encryption is employed, let the CEK be the shared 

130 # symmetric key. 

131 cek_bytes = _get_key_bytes_from_key(key) 

132 else: 

133 try: 

134 cek_bytes = key.unwrap_key(encrypted_key) 

135 

136 # Record whether the CEK could be successfully determined for this 

137 # recipient or not. 

138 cek_valid = True 

139 except NotImplementedError: 

140 raise JWEError(f"alg {alg} is not implemented") 

141 except Exception: 

142 # Record whether the CEK could be successfully determined for this 

143 # recipient or not. 

144 cek_valid = False 

145 

146 # To mitigate the attacks described in RFC 3218 [RFC3218], the 

147 # recipient MUST NOT distinguish between format, padding, and length 

148 # errors of encrypted keys. It is strongly recommended, in the event 

149 # of receiving an improperly formatted key, that the recipient 

150 # substitute a randomly generated CEK and proceed to the next step, to 

151 # mitigate timing attacks. 

152 cek_bytes = _get_random_cek_bytes_for_enc(enc) 

153 

154 # Compute the Encoded Protected Header value BASE64URL(UTF8(JWE 

155 # Protected Header)). If the JWE Protected Header is not present 

156 # (which can only happen when using the JWE JSON Serialization and 

157 # no "protected" member is present), let this value be the empty 

158 # string. 

159 protected_header = encoded_header 

160 

161 # Let the Additional Authenticated Data encryption parameter be 

162 # ASCII(Encoded Protected Header). However, if a JWE AAD value is 

163 # present (which can only be the case when using the JWE JSON 

164 # Serialization), instead let the Additional Authenticated Data 

165 # encryption parameter be ASCII(Encoded Protected Header || '.' || 

166 # BASE64URL(JWE AAD)). 

167 aad = protected_header 

168 

169 # Decrypt the JWE Ciphertext using the CEK, the JWE Initialization 

170 # Vector, the Additional Authenticated Data value, and the JWE 

171 # Authentication Tag (which is the Authentication Tag input to the 

172 # calculation) using the specified content encryption algorithm, 

173 # returning the decrypted plaintext and validating the JWE 

174 # Authentication Tag in the manner specified for the algorithm, 

175 # rejecting the input without emitting any decrypted output if the 

176 # JWE Authentication Tag is incorrect. 

177 try: 

178 plain_text = _decrypt_and_auth(cek_bytes, enc, cipher_text, iv, aad, auth_tag) 

179 except NotImplementedError: 

180 raise JWEError(f"enc {enc} is not implemented") 

181 except Exception as e: 

182 raise JWEError(e) 

183 

184 # If a "zip" parameter was included, uncompress the decrypted 

185 # plaintext using the specified compression algorithm. 

186 if plain_text is not None: 

187 plain_text = _decompress(header.get("zip"), plain_text) 

188 

189 return plain_text if cek_valid else None 

190 

191 

192def get_unverified_header(jwe_str): 

193 """Returns the decoded headers without verification of any kind. 

194 

195 Args: 

196 jwe_str (str): A compact serialized JWE to decode the headers from. 

197 

198 Returns: 

199 dict: The dict representation of the JWE headers. 

200 

201 Raises: 

202 JWEError: If there is an exception decoding the JWE. 

203 """ 

204 header = _jwe_compact_deserialize(jwe_str)[0] 

205 return header 

206 

207 

208def _decrypt_and_auth(cek_bytes, enc, cipher_text, iv, aad, auth_tag): 

209 """ 

210 Decrypt and verify the data 

211 

212 Args: 

213 cek_bytes (bytes): cek to derive encryption and possible auth key to 

214 verify the auth tag 

215 cipher_text (bytes): Encrypted data 

216 iv (bytes): Initialization vector (iv) used to encrypt data 

217 aad (bytes): Additional Authenticated Data used to verify the data 

218 auth_tag (bytes): Authentication ntag to verify the data 

219 

220 Returns: 

221 (bytes): Decrypted data 

222 """ 

223 # Decrypt the JWE Ciphertext using the CEK, the JWE Initialization 

224 # Vector, the Additional Authenticated Data value, and the JWE 

225 # Authentication Tag (which is the Authentication Tag input to the 

226 # calculation) using the specified content encryption algorithm, 

227 # returning the decrypted plaintext 

228 # and validating the JWE 

229 # Authentication Tag in the manner specified for the algorithm, 

230 if enc in ALGORITHMS.HMAC_AUTH_TAG: 

231 encryption_key, mac_key, key_len = _get_encryption_key_mac_key_and_key_length_from_cek(cek_bytes, enc) 

232 auth_tag_check = _auth_tag(cipher_text, iv, aad, mac_key, key_len) 

233 elif enc in ALGORITHMS.GCM: 

234 encryption_key = jwk.construct(cek_bytes, enc) 

235 auth_tag_check = auth_tag # GCM check auth on decrypt 

236 else: 

237 raise NotImplementedError(f"enc {enc} is not implemented!") 

238 

239 plaintext = encryption_key.decrypt(cipher_text, iv, aad, auth_tag) 

240 if auth_tag != auth_tag_check: 

241 raise JWEError("Invalid JWE Auth Tag") 

242 

243 return plaintext 

244 

245 

246def _get_encryption_key_mac_key_and_key_length_from_cek(cek_bytes, enc): 

247 derived_key_len = len(cek_bytes) // 2 

248 mac_key_bytes = cek_bytes[0:derived_key_len] 

249 mac_key = _get_hmac_key(enc, mac_key_bytes) 

250 encryption_key_bytes = cek_bytes[-derived_key_len:] 

251 encryption_alg, _ = enc.split("-") 

252 encryption_key = jwk.construct(encryption_key_bytes, encryption_alg) 

253 return encryption_key, mac_key, derived_key_len 

254 

255 

256def _jwe_compact_deserialize(jwe_bytes): 

257 """ 

258 Deserialize and verify the header and segments are appropriate. 

259 

260 Args: 

261 jwe_bytes (bytes): The compact serialized JWE 

262 Returns: 

263 (dict, bytes, bytes, bytes, bytes, bytes) 

264 """ 

265 

266 # Base64url decode the encoded representations of the JWE 

267 # Protected Header, the JWE Encrypted Key, the JWE Initialization 

268 # Vector, the JWE Ciphertext, the JWE Authentication Tag, and the 

269 # JWE AAD, following the restriction that no line breaks, 

270 # whitespace, or other additional characters have been used. 

271 jwe_bytes = ensure_binary(jwe_bytes) 

272 try: 

273 header_segment, encrypted_key_segment, iv_segment, cipher_text_segment, auth_tag_segment = jwe_bytes.split( 

274 b".", 4 

275 ) 

276 header_data = base64url_decode(header_segment) 

277 except ValueError: 

278 raise JWEParseError("Not enough segments") 

279 except (TypeError, binascii.Error): 

280 raise JWEParseError("Invalid header") 

281 

282 # Verify that the octet sequence resulting from decoding the 

283 # encoded JWE Protected Header is a UTF-8-encoded representation 

284 # of a completely valid JSON object conforming to RFC 7159 

285 # [RFC7159]; let the JWE Protected Header be this JSON object. 

286 # 

287 # If using the JWE Compact Serialization, let the JOSE Header be 

288 # the JWE Protected Header. Otherwise, when using the JWE JSON 

289 # Serialization, let the JOSE Header be the union of the members 

290 # of the JWE Protected Header, the JWE Shared Unprotected Header 

291 # and the corresponding JWE Per-Recipient Unprotected Header, all 

292 # of which must be completely valid JSON objects. During this 

293 # step, verify that the resulting JOSE Header does not contain 

294 # duplicate Header Parameter names. When using the JWE JSON 

295 # Serialization, this restriction includes that the same Header 

296 # Parameter name also MUST NOT occur in distinct JSON object 

297 # values that together comprise the JOSE Header. 

298 

299 try: 

300 header = json.loads(header_data) 

301 except ValueError as e: 

302 raise JWEParseError(f"Invalid header string: {e}") 

303 

304 if not isinstance(header, Mapping): 

305 raise JWEParseError("Invalid header string: must be a json object") 

306 

307 try: 

308 encrypted_key = base64url_decode(encrypted_key_segment) 

309 except (TypeError, binascii.Error): 

310 raise JWEParseError("Invalid encrypted key") 

311 

312 try: 

313 iv = base64url_decode(iv_segment) 

314 except (TypeError, binascii.Error): 

315 raise JWEParseError("Invalid IV") 

316 

317 try: 

318 ciphertext = base64url_decode(cipher_text_segment) 

319 except (TypeError, binascii.Error): 

320 raise JWEParseError("Invalid cyphertext") 

321 

322 try: 

323 auth_tag = base64url_decode(auth_tag_segment) 

324 except (TypeError, binascii.Error): 

325 raise JWEParseError("Invalid auth tag") 

326 

327 return header, header_segment, encrypted_key, iv, ciphertext, auth_tag 

328 

329 

330def _encoded_header(alg, enc, zip, cty, kid): 

331 """ 

332 Generate an appropriate JOSE header based on the values provided 

333 Args: 

334 alg (str): Key wrap/negotiation algorithm 

335 enc (str): Encryption algorithm 

336 zip (str): Compression method 

337 cty (str): Content type of the encrypted data 

338 kid (str): ID for the key used for the operation 

339 

340 Returns: 

341 bytes: JSON object of header based on input 

342 """ 

343 header = {"alg": alg, "enc": enc} 

344 if zip: 

345 header["zip"] = zip 

346 if cty: 

347 header["cty"] = cty 

348 if kid: 

349 header["kid"] = kid 

350 json_header = json.dumps( 

351 header, 

352 separators=(",", ":"), 

353 sort_keys=True, 

354 ).encode("utf-8") 

355 return base64url_encode(json_header) 

356 

357 

358def _big_endian(int_val): 

359 return pack("!Q", int_val) 

360 

361 

362def _encrypt_and_auth(key, alg, enc, zip, plaintext, aad): 

363 """ 

364 Generate a content encryption key (cek) and initialization 

365 vector (iv) based on enc and alg, compress the plaintext based on zip, 

366 encrypt the compressed plaintext using the cek and iv based on enc 

367 

368 Args: 

369 key (Key): The key provided for encryption 

370 alg (str): The algorithm use for key wrap/negotiation 

371 enc (str): The encryption algorithm with which to encrypt the plaintext 

372 zip (str): The compression algorithm with which to compress the plaintext 

373 plaintext (bytes): The data to encrypt 

374 aad (str): Additional authentication data utilized for generating an 

375 auth tag 

376 

377 Returns: 

378 (bytes, bytes, bytes, bytes): A tuple of the following data 

379 (key wrapped cek, iv, cipher text, auth tag) 

380 """ 

381 try: 

382 cek_bytes, kw_cek = _get_cek(enc, alg, key) 

383 except NotImplementedError: 

384 raise JWEError(f"alg {alg} is not implemented") 

385 

386 if enc in ALGORITHMS.HMAC_AUTH_TAG: 

387 encryption_key, mac_key, key_len = _get_encryption_key_mac_key_and_key_length_from_cek(cek_bytes, enc) 

388 iv, ciphertext, tag = encryption_key.encrypt(plaintext, aad) 

389 auth_tag = _auth_tag(ciphertext, iv, aad, mac_key, key_len) 

390 elif enc in ALGORITHMS.GCM: 

391 encryption_key = jwk.construct(cek_bytes, enc) 

392 iv, ciphertext, auth_tag = encryption_key.encrypt(plaintext, aad) 

393 else: 

394 raise NotImplementedError(f"enc {enc} is not implemented!") 

395 

396 return kw_cek, iv, ciphertext, auth_tag 

397 

398 

399def _get_hmac_key(enc, mac_key_bytes): 

400 """ 

401 Get an HMACKey for the provided encryption algorithm and key bytes 

402 

403 Args: 

404 enc (str): Encryption algorithm 

405 mac_key_bytes (bytes): vytes for the HMAC key 

406 

407 Returns: 

408 (HMACKey): The key to perform HMAC actions 

409 """ 

410 _, hash_alg = enc.split("-") 

411 mac_key = jwk.construct(mac_key_bytes, hash_alg) 

412 return mac_key 

413 

414 

415def _compress(zip, plaintext): 

416 """ 

417 Compress the plaintext based on the algorithm supplied 

418 

419 Args: 

420 zip (str): Compression Algorithm 

421 plaintext (bytes): plaintext to compress 

422 

423 Returns: 

424 (bytes): Compressed plaintext 

425 """ 

426 if zip not in ZIPS.SUPPORTED: 

427 raise NotImplementedError("ZIP {} is not supported!") 

428 if zip is None: 

429 compressed = plaintext 

430 elif zip == ZIPS.DEF: 

431 compressed = zlib.compress(plaintext) 

432 else: 

433 raise NotImplementedError("ZIP {} is not implemented!") 

434 return compressed 

435 

436 

437def _decompress(zip, compressed): 

438 """ 

439 Decompress the plaintext based on the algorithm supplied 

440 

441 Args: 

442 zip (str): Compression Algorithm 

443 plaintext (bytes): plaintext to decompress 

444 

445 Returns: 

446 (bytes): Compressed plaintext 

447 """ 

448 if zip not in ZIPS.SUPPORTED: 

449 raise NotImplementedError("ZIP {} is not supported!") 

450 if zip is None: 

451 decompressed = compressed 

452 elif zip == ZIPS.DEF: 

453 decompressed = zlib.decompress(compressed) 

454 else: 

455 raise NotImplementedError("ZIP {} is not implemented!") 

456 return decompressed 

457 

458 

459def _get_cek(enc, alg, key): 

460 """ 

461 Get the content encryption key 

462 

463 Args: 

464 enc (str): Encryption algorithm 

465 alg (str): kwy wrap/negotiation algorithm 

466 key (Key): Key provided to encryption method 

467 

468 Return: 

469 (bytes, bytes): Tuple of (cek bytes and wrapped cek) 

470 """ 

471 if alg == ALGORITHMS.DIR: 

472 cek, wrapped_cek = _get_direct_key_wrap_cek(key) 

473 else: 

474 cek, wrapped_cek = _get_key_wrap_cek(enc, key) 

475 

476 return cek, wrapped_cek 

477 

478 

479def _get_direct_key_wrap_cek(key): 

480 """ 

481 Get the cek and wrapped cek from the encryption key direct 

482 

483 Args: 

484 key (Key): Key provided to encryption method 

485 

486 Return: 

487 (Key, bytes): Tuple of (cek Key object and wrapped cek) 

488 """ 

489 # Get the JWK data to determine how to derive the cek 

490 jwk_data = key.to_dict() 

491 if jwk_data["kty"] == "oct": 

492 # Get the last half of an octal key as the cek 

493 cek_bytes = _get_key_bytes_from_key(key) 

494 wrapped_cek = b"" 

495 else: 

496 raise NotImplementedError("JWK type {} not supported!".format(jwk_data["kty"])) 

497 return cek_bytes, wrapped_cek 

498 

499 

500def _get_key_bytes_from_key(key): 

501 """ 

502 Get the raw key bytes from a Key object 

503 

504 Args: 

505 key (Key): Key from which to extract the raw key bytes 

506 Returns: 

507 (bytes) key data 

508 """ 

509 jwk_data = key.to_dict() 

510 encoded_key = jwk_data["k"] 

511 cek_bytes = base64url_decode(encoded_key) 

512 return cek_bytes 

513 

514 

515def _get_key_wrap_cek(enc, key): 

516 """_get_rsa_key_wrap_cek 

517 Get the content encryption key for RSA key wrap 

518 

519 Args: 

520 enc (str): Encryption algorithm 

521 key (Key): Key provided to encryption method 

522 

523 Returns: 

524 (Key, bytes): Tuple of (cek Key object and wrapped cek) 

525 """ 

526 cek_bytes = _get_random_cek_bytes_for_enc(enc) 

527 wrapped_cek = key.wrap_key(cek_bytes) 

528 return cek_bytes, wrapped_cek 

529 

530 

531def _get_random_cek_bytes_for_enc(enc): 

532 """ 

533 Get the random cek bytes based on the encryptionn algorithm 

534 

535 Args: 

536 enc (str): Encryption algorithm 

537 

538 Returns: 

539 (bytes) random bytes for cek key 

540 """ 

541 if enc == ALGORITHMS.A128GCM: 

542 num_bits = 128 

543 elif enc == ALGORITHMS.A192GCM: 

544 num_bits = 192 

545 elif enc in (ALGORITHMS.A128CBC_HS256, ALGORITHMS.A256GCM): 

546 num_bits = 256 

547 elif enc == ALGORITHMS.A192CBC_HS384: 

548 num_bits = 384 

549 elif enc == ALGORITHMS.A256CBC_HS512: 

550 num_bits = 512 

551 else: 

552 raise NotImplementedError(f"{enc} not supported") 

553 cek_bytes = get_random_bytes(num_bits // 8) 

554 return cek_bytes 

555 

556 

557def _auth_tag(ciphertext, iv, aad, mac_key, tag_length): 

558 """ 

559 Get ann auth tag from the provided data 

560 

561 Args: 

562 ciphertext (bytes): Encrypted value 

563 iv (bytes): Initialization vector 

564 aad (bytes): Additional Authenticated Data 

565 mac_key (bytes): Key to use in generating the MAC 

566 tag_length (int): How log the tag should be 

567 

568 Returns: 

569 (bytes) Auth tag 

570 """ 

571 al = _big_endian(len(aad) * 8) 

572 auth_tag_input = aad + iv + ciphertext + al 

573 signature = mac_key.sign(auth_tag_input) 

574 auth_tag = signature[0:tag_length] 

575 return auth_tag 

576 

577 

578def _jwe_compact_serialize(encoded_header, encrypted_cek, iv, cipher_text, auth_tag): 

579 """ 

580 Generate a compact serialized JWE 

581 

582 Args: 

583 encoded_header (bytes): Base64 URL Encoded JWE header JSON 

584 encrypted_cek (bytes): Encrypted content encryption key (cek) 

585 iv (bytes): Initialization vector (IV) 

586 cipher_text (bytes): Cipher text 

587 auth_tag (bytes): JWE Auth Tag 

588 

589 Returns: 

590 (str): JWE compact serialized string 

591 """ 

592 cipher_text = ensure_binary(cipher_text) 

593 encoded_encrypted_cek = base64url_encode(encrypted_cek) 

594 encoded_iv = base64url_encode(iv) 

595 encoded_cipher_text = base64url_encode(cipher_text) 

596 encoded_auth_tag = base64url_encode(auth_tag) 

597 return ( 

598 encoded_header 

599 + b"." 

600 + encoded_encrypted_cek 

601 + b"." 

602 + encoded_iv 

603 + b"." 

604 + encoded_cipher_text 

605 + b"." 

606 + encoded_auth_tag 

607 )