Coverage for aiocoap/util/cryptography_additions.py: 89%

27 statements  

« prev     ^ index     » next       coverage.py v7.5.4, created at 2024-07-10 11:47 +0000

1# SPDX-FileCopyrightText: Christian Amsüss and the aiocoap contributors 

2# 

3# SPDX-License-Identifier: MIT 

4 

5""" 

6Workaround for https://github.com/pyca/cryptography/issues/5557 

7 

8These functions could be methods to 

9`cryptography.hazmat.primitives.asymmetric.ed25519.{Ed25519PrivateKey, 

10Ed25519PublicKey}`, respectively, and are currently implemented manually or 

11using ge25519. 

12 

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""" 

17 

18from cryptography.hazmat.primitives.asymmetric import ed25519, x25519 

19from cryptography.hazmat.primitives import serialization 

20 

21 

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 ) 

28 

29 # as proposed in https://github.com/pyca/cryptography/issues/5557#issuecomment-739339132 

30 

31 from cryptography.hazmat.primitives import hashes 

32 

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 

40 

41 return x25519.X25519PrivateKey.from_private_bytes(h[0:32]) 

42 

43 

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 ) 

49 

50 # This is libsodium's crypto_sign_ed25519_pk_to_curve25519 translated into 

51 # the Pyton module ge25519. 

52 

53 from ge25519 import ge25519, ge25519_p3 

54 from fe25519 import fe25519 

55 

56 if ge25519.has_small_order(raw) != 0: 

57 raise RuntimeError("Doesn' thave small order") 

58 

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") 

65 

66 if not A.is_on_main_subgroup(): 

67 raise RuntimeError("It's on the main subgroup") 

68 

69 one_minus_y = fe25519.one() - A.Y 

70 x = A.Y + fe25519.one() 

71 x = x * one_minus_y.invert() 

72 

73 return x25519.X25519PublicKey.from_public_bytes(bytes(x.to_bytes()))