Coverage for aiocoap/util/cryptography_additions.py: 89%
27 statements
« prev ^ index » next coverage.py v7.6.8, created at 2024-11-28 12:34 +0000
« prev ^ index » next coverage.py v7.6.8, created at 2024-11-28 12:34 +0000
1# SPDX-FileCopyrightText: Christian Amsüss and the aiocoap contributors
2#
3# SPDX-License-Identifier: MIT
5"""
6Workaround for https://github.com/pyca/cryptography/issues/5557
8These functions could be methods to
9`cryptography.hazmat.primitives.asymmetric.ed25519.{Ed25519PrivateKey,
10Ed25519PublicKey}`, respectively, and are currently implemented manually or
11using ge25519.
13These conversions are not too critical in that they do not run on data an
14attacker can send arbitrarily (in the most dynamic situation, the keys are
15distributed through a KDC aka. group manager).
16"""
18from cryptography.hazmat.primitives.asymmetric import ed25519, x25519
19from cryptography.hazmat.primitives import serialization
22def sk_to_curve25519(ed: ed25519.Ed25519PrivateKey) -> x25519.X25519PrivateKey:
23 raw = ed.private_bytes(
24 encoding=serialization.Encoding.Raw,
25 format=serialization.PrivateFormat.Raw,
26 encryption_algorithm=serialization.NoEncryption(),
27 )
29 # as proposed in https://github.com/pyca/cryptography/issues/5557#issuecomment-739339132
31 from cryptography.hazmat.primitives import hashes
33 hasher = hashes.Hash(hashes.SHA512())
34 hasher.update(raw)
35 h = bytearray(hasher.finalize())
36 # curve25519 clamping
37 h[0] &= 248
38 h[31] &= 127
39 h[31] |= 64
41 return x25519.X25519PrivateKey.from_private_bytes(h[0:32])
44def pk_to_curve25519(ed: ed25519.Ed25519PublicKey) -> x25519.X25519PublicKey:
45 raw = ed.public_bytes(
46 encoding=serialization.Encoding.Raw,
47 format=serialization.PublicFormat.Raw,
48 )
50 # This is libsodium's crypto_sign_ed25519_pk_to_curve25519 translated into
51 # the Pyton module ge25519.
53 from ge25519 import ge25519, ge25519_p3
54 from fe25519 import fe25519
56 if ge25519.has_small_order(raw) != 0:
57 raise RuntimeError("Doesn' thave small order")
59 # frombytes in libsodium appears to be the same as
60 # frombytes_negate_vartime; as ge25519 only implements the from_bytes
61 # version, we have to do the root check manually.
62 A = ge25519_p3.from_bytes(raw)
63 if A.root_check:
64 raise RuntimeError("Root check failed")
66 if not A.is_on_main_subgroup():
67 raise RuntimeError("It's on the main subgroup")
69 one_minus_y = fe25519.one() - A.Y
70 x = A.Y + fe25519.one()
71 x = x * one_minus_y.invert()
73 return x25519.X25519PublicKey.from_public_bytes(bytes(x.to_bytes()))