| 3579 | |
| 3580 | |
| 3581 | def create_signed_value( |
| 3582 | secret: _CookieSecretTypes, |
| 3583 | name: str, |
| 3584 | value: Union[str, bytes], |
| 3585 | version: Optional[int] = None, |
| 3586 | clock: Optional[Callable[[], float]] = None, |
| 3587 | key_version: Optional[int] = None, |
| 3588 | ) -> bytes: |
| 3589 | if version is None: |
| 3590 | version = DEFAULT_SIGNED_VALUE_VERSION |
| 3591 | if clock is None: |
| 3592 | clock = time.time |
| 3593 | |
| 3594 | timestamp = utf8(str(int(clock()))) |
| 3595 | value = base64.b64encode(utf8(value)) |
| 3596 | if version == 1: |
| 3597 | assert not isinstance(secret, dict) |
| 3598 | signature = _create_signature_v1(secret, name, value, timestamp) |
| 3599 | value = b"|".join([value, timestamp, signature]) |
| 3600 | return value |
| 3601 | elif version == 2: |
| 3602 | # The v2 format consists of a version number and a series of |
| 3603 | # length-prefixed fields "%d:%s", the last of which is a |
| 3604 | # signature, all separated by pipes. All numbers are in |
| 3605 | # decimal format with no leading zeros. The signature is an |
| 3606 | # HMAC-SHA256 of the whole string up to that point, including |
| 3607 | # the final pipe. |
| 3608 | # |
| 3609 | # The fields are: |
| 3610 | # - format version (i.e. 2; no length prefix) |
| 3611 | # - key version (integer, default is 0) |
| 3612 | # - timestamp (integer seconds since epoch) |
| 3613 | # - name (not encoded; assumed to be ~alphanumeric) |
| 3614 | # - value (base64-encoded) |
| 3615 | # - signature (hex-encoded; no length prefix) |
| 3616 | def format_field(s: Union[str, bytes]) -> bytes: |
| 3617 | return utf8("%d:" % len(s)) + utf8(s) |
| 3618 | |
| 3619 | to_sign = b"|".join( |
| 3620 | [ |
| 3621 | b"2", |
| 3622 | format_field(str(key_version or 0)), |
| 3623 | format_field(timestamp), |
| 3624 | format_field(name), |
| 3625 | format_field(value), |
| 3626 | b"", |
| 3627 | ] |
| 3628 | ) |
| 3629 | |
| 3630 | if isinstance(secret, dict): |
| 3631 | assert ( |
| 3632 | key_version is not None |
| 3633 | ), "Key version must be set when sign key dict is used" |
| 3634 | assert version >= 2, "Version must be at least 2 for key version support" |
| 3635 | secret = secret[key_version] |
| 3636 | |
| 3637 | signature = _create_signature_v2(secret, to_sign) |
| 3638 | return to_sign + signature |