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

29 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-16 16:09 +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 

21def sk_to_curve25519(ed: ed25519.Ed25519PrivateKey) -> x25519.X25519PrivateKey: 

22 raw = ed.private_bytes( 

23 encoding=serialization.Encoding.Raw, 

24 format=serialization.PrivateFormat.Raw, 

25 encryption_algorithm=serialization.NoEncryption(), 

26 ) 

27 

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

29 

30 from cryptography.hazmat.primitives import hashes 

31 from cryptography.hazmat.backends.openssl.backend import backend 

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 backend.x25519_load_private_bytes(h[0:32]) 

42 

43def pk_to_curve25519(ed: ed25519.Ed25519PublicKey) -> x25519.X25519PublicKey: 

44 raw = ed.public_bytes( 

45 encoding=serialization.Encoding.Raw, 

46 format=serialization.PublicFormat.Raw, 

47 ) 

48 

49 # This is libsodium's crypto_sign_ed25519_pk_to_curve25519 translated into 

50 # the Pyton module ge25519. 

51 

52 from ge25519 import ge25519, ge25519_p3 

53 from fe25519 import fe25519 

54 

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

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

57 

58 # frombytes in libsodium appears to be the same as 

59 # frombytes_negate_vartime; as ge25519 only implements the from_bytes 

60 # version, we have to do the root check manually. 

61 A = ge25519_p3.from_bytes(raw) 

62 if A.root_check: 

63 raise RuntimeError("Root check failed") 

64 

65 if not A.is_on_main_subgroup(): 

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

67 

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

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

70 x = x * one_minus_y.invert() 

71 

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