| 3378 | |
| 3379 | |
| 3380 | def create_signed_value( |
| 3381 | secret: _CookieSecretTypes, |
| 3382 | name: str, |
| 3383 | value: Union[str, bytes], |
| 3384 | version: Optional[int] = None, |
| 3385 | clock: Optional[Callable[[], float]] = None, |
| 3386 | key_version: Optional[int] = None, |
| 3387 | ) -> bytes: |
| 3388 | if version is None: |
| 3389 | version = DEFAULT_SIGNED_VALUE_VERSION |
| 3390 | if clock is None: |
| 3391 | clock = time.time |
| 3392 | |
| 3393 | timestamp = utf8(str(int(clock()))) |
| 3394 | value = base64.b64encode(utf8(value)) |
| 3395 | if version == 1: |
| 3396 | assert not isinstance(secret, dict) |
| 3397 | signature = _create_signature_v1(secret, name, value, timestamp) |
| 3398 | value = b"|".join([value, timestamp, signature]) |
| 3399 | return value |
| 3400 | elif version == 2: |
| 3401 | # The v2 format consists of a version number and a series of |
| 3402 | # length-prefixed fields "%d:%s", the last of which is a |
| 3403 | # signature, all separated by pipes. All numbers are in |
| 3404 | # decimal format with no leading zeros. The signature is an |
| 3405 | # HMAC-SHA256 of the whole string up to that point, including |
| 3406 | # the final pipe. |
| 3407 | # |
| 3408 | # The fields are: |
| 3409 | # - format version (i.e. 2; no length prefix) |
| 3410 | # - key version (integer, default is 0) |
| 3411 | # - timestamp (integer seconds since epoch) |
| 3412 | # - name (not encoded; assumed to be ~alphanumeric) |
| 3413 | # - value (base64-encoded) |
| 3414 | # - signature (hex-encoded; no length prefix) |
| 3415 | def format_field(s: Union[str, bytes]) -> bytes: |
| 3416 | return utf8("%d:" % len(s)) + utf8(s) |
| 3417 | |
| 3418 | to_sign = b"|".join( |
| 3419 | [ |
| 3420 | b"2", |
| 3421 | format_field(str(key_version or 0)), |
| 3422 | format_field(timestamp), |
| 3423 | format_field(name), |
| 3424 | format_field(value), |
| 3425 | b"", |
| 3426 | ] |
| 3427 | ) |
| 3428 | |
| 3429 | if isinstance(secret, dict): |
| 3430 | assert ( |
| 3431 | key_version is not None |
| 3432 | ), "Key version must be set when sign key dict is used" |
| 3433 | assert version >= 2, "Version must be at least 2 for key version support" |
| 3434 | secret = secret[key_version] |
| 3435 | |
| 3436 | signature = _create_signature_v2(secret, to_sign) |
| 3437 | return to_sign + signature |