naeural-client 2.3.6__py3-none-any.whl → 2.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
naeural_client/_ver.py CHANGED
@@ -1,4 +1,4 @@
1
- __VER__ = "2.3.6"
1
+ __VER__ = "2.4.0"
2
2
 
3
3
  if __name__ == "__main__":
4
4
  with open("pyproject.toml", "rt") as fd:
naeural_client/bc/base.py CHANGED
@@ -344,7 +344,10 @@ class BaseBlockEngine:
344
344
  boxed=boxed,
345
345
  **kwargs
346
346
  )
347
-
347
+
348
+ @property
349
+ def name(self):
350
+ return self.__name
348
351
 
349
352
  def _init(self):
350
353
  self.P("Initializing Blockchain engine manager...", boxed=True, box_char='*', verbosity=1)
naeural_client/bc/ec.py CHANGED
@@ -3,6 +3,7 @@ import hashlib
3
3
  import os
4
4
  import binascii
5
5
  import zlib
6
+ import json
6
7
 
7
8
  from cryptography.hazmat.primitives import hashes
8
9
  from cryptography.hazmat.primitives.asymmetric import ec
@@ -273,16 +274,16 @@ class BaseBCEllipticCurveEngine(BaseBlockEngine):
273
274
  if debug:
274
275
  print('derived-shared_key: ', base64.b64encode(derived_key))
275
276
  return derived_key
276
-
277
277
 
278
- def encrypt(
278
+
279
+ def _encrypt(
279
280
  self,
280
281
  plaintext: str,
281
282
  receiver_address: str,
282
- compressed: bool = True,
283
- embed_compressed: bool = True,
283
+ compressed: bool = True, # compressed is always True
284
+ embed_compressed: bool = True, # embed_compressed is always True
284
285
  info: str = BCct.DEFAULT_INFO,
285
- debug: bool = False
286
+ debug: bool = False,
286
287
  ):
287
288
  """
288
289
  Encrypts plaintext using the sender's private key and receiver's public key,
@@ -307,6 +308,7 @@ class BaseBCEllipticCurveEngine(BaseBlockEngine):
307
308
  str
308
309
  The base64 encoded nonce and ciphertext.
309
310
  """
311
+
310
312
  if compressed:
311
313
  to_encrypt_data = zlib.compress(plaintext.encode())
312
314
  compressed_flag = (1).to_bytes(1, byteorder='big')
@@ -325,8 +327,51 @@ class BaseBCEllipticCurveEngine(BaseBlockEngine):
325
327
  encrypted_data = nonce + ciphertext # Prepend the nonce to the ciphertext
326
328
  #end if
327
329
  return base64.b64encode(encrypted_data).decode() # Encode to base64
330
+
331
+
332
+ def encrypt(
333
+ self,
334
+ plaintext: str,
335
+ receiver_address: str,
336
+ info: str = BCct.DEFAULT_INFO,
337
+ debug: bool = False,
338
+ **kwargs
339
+ ):
340
+ """
341
+ Encrypts plaintext using the sender's private key and receiver's public key,
342
+ then base64 encodes the output.
343
+
344
+ Parameters
345
+ ----------
346
+ receiver_address : str
347
+ The receiver's address
348
+
349
+ plaintext : str
350
+ The plaintext to encrypt.
351
+
352
+ Obsolete:
353
+ compressed : bool, optional
354
+ Whether to compress the plaintext before encryption. The default is True.
355
+
356
+ embed_compressed : bool, optional
357
+ Whether to embed the compressed flag in the encrypted data. The default is True.
328
358
 
329
- def decrypt(
359
+ Returns
360
+ -------
361
+ str
362
+ The base64 encoded nonce and ciphertext.
363
+ """
364
+ return self._encrypt(
365
+ plaintext=plaintext,
366
+ receiver_address=receiver_address,
367
+ compressed=True,
368
+ embed_compressed=True,
369
+ info=info,
370
+ debug=debug,
371
+ )
372
+
373
+
374
+ def _decrypt(
330
375
  self,
331
376
  encrypted_data_b64 : str,
332
377
  sender_address : str,
@@ -395,6 +440,220 @@ class BaseBCEllipticCurveEngine(BaseBlockEngine):
395
440
  result = None
396
441
  return result
397
442
 
443
+
444
+ ## Multi destination encryption
445
+ def encrypt_for_multi(
446
+ self,
447
+ plaintext: str,
448
+ receiver_addresses: list,
449
+ info: str = BCct.DEFAULT_INFO,
450
+ debug: bool = False
451
+ ):
452
+ """
453
+ Encrypts plaintext for multiple receivers.
454
+
455
+ The overall approach is to encrypt the plaintext with a symmetric key,
456
+ then encrypt the symmetric key with the public key of each receiver.
457
+
458
+ Parameters
459
+ ----------
460
+ plaintext : str
461
+ The plaintext to encrypt.
462
+
463
+ receiver_addresses : list
464
+ List of receiver addresses.
465
+
466
+ Returns
467
+ -------
468
+ str
469
+ The base64 encoded encrypted package.
470
+ """
471
+ to_encrypt_data = zlib.compress(plaintext.encode())
472
+ compressed_flag = (1).to_bytes(1, byteorder='big')
473
+
474
+ # Generate a random symmetric key
475
+ symmetric_key = os.urandom(32) # 256-bit key
476
+
477
+ # Encrypt the plaintext with the symmetric key
478
+ nonce = os.urandom(12)
479
+ aesgcm = AESGCM(symmetric_key)
480
+ ciphertext = aesgcm.encrypt(nonce, to_encrypt_data, None)
481
+
482
+ # For each receiver, encrypt the symmetric key
483
+ encrypted_keys = []
484
+ for receiver_address in receiver_addresses:
485
+ receiver_pk = self._address_to_pk(receiver_address)
486
+ shared_key = self.__derive_shared_key(receiver_pk, info=info, debug=debug)
487
+ # Use shared_key to encrypt the symmetric key
488
+ aesgcm_shared = AESGCM(shared_key)
489
+ nonce_shared = os.urandom(12)
490
+ encrypted_symmetric_key = aesgcm_shared.encrypt(nonce_shared, symmetric_key, None)
491
+ full_enc_key = nonce_shared + encrypted_symmetric_key
492
+ encrypted_keys.append({
493
+ 'a': receiver_address, # Address of the receiver
494
+ 'k': full_enc_key.hex() # Encrypted symmetric key
495
+ })
496
+
497
+ # Package the encrypted symmetric keys and the ciphertext
498
+ encrypted_package = {
499
+ 'M': True, # Multi-recipient flag
500
+ 'c': compressed_flag.hex(), # Compressed flag
501
+ 'n': nonce.hex(), # Nonce
502
+ 'd': ciphertext.hex(), # Ciphertext
503
+ 'k': encrypted_keys # Encrypted symmetric keys
504
+ }
505
+
506
+ # Convert to JSON, compress, and base64 encode
507
+ enc_data = json.dumps(encrypted_package)
508
+ enc_data_compressed = zlib.compress(enc_data.encode())
509
+ enc_data_compressed_b64 = base64.b64encode(enc_data_compressed).decode()
510
+ return enc_data_compressed_b64
511
+
512
+
513
+ def decrypt_for_multi(
514
+ self,
515
+ encrypted_data_b64: str,
516
+ sender_address: str,
517
+ info: str = BCct.DEFAULT_INFO,
518
+ debug: bool = False
519
+ ):
520
+ """
521
+ Decrypts data encrypted for multiple receivers.
522
+
523
+ Parameters
524
+ ----------
525
+ encrypted_data_b64 : str
526
+ The base64 encoded encrypted package as produced by encrypt_for_multi.
527
+
528
+ sender_address : str
529
+ The sender's address (public key address) used to derive the shared key.
530
+
531
+ info : str, optional
532
+ Additional info used in the HKDF for shared key derivation.
533
+
534
+ debug : bool, optional
535
+ If True, prints debug information.
536
+
537
+ Returns
538
+ -------
539
+ str or None
540
+ The decrypted plaintext as a string, or None if decryption fails.
541
+ """
542
+ try:
543
+ # 1. Base64 decode
544
+ enc_data_compressed = base64.b64decode(encrypted_data_b64)
545
+
546
+ # 2. Decompress the JSON structure
547
+ enc_data_json = zlib.decompress(enc_data_compressed).decode()
548
+
549
+ # 3. Parse JSON
550
+ encrypted_package = json.loads(enc_data_json)
551
+
552
+ # Expecting keys: 'M', 'c', 'n', 'd', 'k'
553
+ # 'M' = True indicates multi-recipient
554
+ if 'M' not in encrypted_package or encrypted_package['M'] != True:
555
+ if debug:
556
+ self.P("Not a multi-recipient package.", color='y')
557
+ return None
558
+
559
+ # 'c' = compressed flag (hex)
560
+ compressed_flag_hex = encrypted_package['c']
561
+ compressed_flag = int(compressed_flag_hex, 16)
562
+
563
+ # 'n' = nonce (hex)
564
+ nonce = bytes.fromhex(encrypted_package['n'])
565
+
566
+ # 'd' = ciphertext (hex)
567
+ ciphertext = bytes.fromhex(encrypted_package['d'])
568
+
569
+ # 'k' = list of encrypted symmetric keys
570
+ encrypted_keys = encrypted_package['k']
571
+
572
+ # 4. Identify this receiver's encrypted symmetric key
573
+ my_address = self.address
574
+ my_encrypted_key_hex = None
575
+ for ek in encrypted_keys:
576
+ if ek['a'] == my_address:
577
+ my_encrypted_key_hex = ek['k']
578
+ break
579
+
580
+ if my_encrypted_key_hex is None:
581
+ if debug:
582
+ self.P("No encrypted symmetric key for this receiver.", color='r')
583
+ return None
584
+
585
+ # Decode the encrypted symmetric key
586
+ my_encrypted_key = bytes.fromhex(my_encrypted_key_hex)
587
+
588
+ # The first 12 bytes are nonce_shared, rest is encrypted symmetric key
589
+ nonce_shared = my_encrypted_key[:12]
590
+ encrypted_symmetric_key = my_encrypted_key[12:]
591
+
592
+ # 5. Derive shared key using sender's public key
593
+ sender_pk = self._address_to_pk(sender_address)
594
+ shared_key = self.__derive_shared_key(sender_pk, info=info, debug=debug)
595
+
596
+ # 6. Decrypt the symmetric key
597
+ aesgcm_shared = AESGCM(shared_key)
598
+ symmetric_key = aesgcm_shared.decrypt(nonce_shared, encrypted_symmetric_key, None)
599
+
600
+ # 7. Decrypt the ciphertext using the symmetric key
601
+ aesgcm = AESGCM(symmetric_key)
602
+ plaintext = aesgcm.decrypt(nonce, ciphertext, None)
603
+
604
+ # 8. Decompress the plaintext if compressed_flag == 1
605
+ if compressed_flag == 1:
606
+ plaintext = zlib.decompress(plaintext)
607
+
608
+ return plaintext.decode()
609
+
610
+ except Exception as exc:
611
+ if debug:
612
+ self.P(f"Error decrypting multi scenario: {exc}", color='r')
613
+ return None
614
+
615
+ def decrypt(
616
+ self,
617
+ encrypted_data_b64: str,
618
+ sender_address: str,
619
+ info: str = BCct.DEFAULT_INFO,
620
+ debug: bool = False,
621
+ is_multi: bool = True
622
+ ):
623
+ """
624
+ Decrypts data encrypted for a single or multi receiver.
625
+
626
+ Parameters
627
+ ----------
628
+ encrypted_data_b64 : str
629
+ The base64 encoded encrypted data.
630
+
631
+ sender_address : str
632
+ The sender's address (public key address) used to derive the shared key.
633
+
634
+ info : str, optional
635
+ Additional info used in the HKDF for shared key derivation.
636
+
637
+ debug : bool, optional
638
+ If True, prints debug information.
639
+
640
+ is_multi : bool, optional
641
+ If True, decrypts as multi-recipient package.
642
+
643
+ Returns
644
+ -------
645
+ str or None
646
+ The decrypted plaintext as a string, or None if decryption fails.
647
+ """
648
+ result = None
649
+ if is_multi:
650
+ result = self.decrypt_for_multi(encrypted_data_b64, sender_address, info=info, debug=debug)
651
+ if result is None:
652
+ result = self._decrypt(encrypted_data_b64, sender_address, decompress=True, embed_compressed=True, info=info, debug=debug)
653
+ return result
654
+
655
+
656
+ ## end multi destination encryption
398
657
 
399
658
 
400
659
  ### ETH
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: naeural_client
3
- Version: 2.3.6
3
+ Version: 2.4.0
4
4
  Summary: `naeural_client` is the Python SDK required for client app development for the Naeural Edge Protocol Edge Protocol framework
5
5
  Project-URL: Homepage, https://github.com/NaeuralEdgeProtocol/naeural_client
6
6
  Project-URL: Bug Tracker, https://github.com/NaeuralEdgeProtocol/naeural_client/issues
@@ -1,5 +1,5 @@
1
1
  naeural_client/__init__.py,sha256=UKEDGS0wFYyxwmhEAKJGecO2vYbIfRYUP4SQgnK10IE,578
2
- naeural_client/_ver.py,sha256=4rzETsXEAg2bP8Sf1T30BpDk47bsDIkQKZQxpea5eBY,330
2
+ naeural_client/_ver.py,sha256=0vYACBG2i5GW9sUazveVQKoboeDHa3NgO4La5KF2aQg,330
3
3
  naeural_client/base_decentra_object.py,sha256=wXjl65gWxxkhV6Tq48wFfNGITvdYdkKPT-wLurGB5vc,4287
4
4
  naeural_client/plugins_manager_mixin.py,sha256=X1JdGLDz0gN1rPnTN_5mJXR8JmqoBFQISJXmPR9yvCo,11106
5
5
  naeural_client/base/__init__.py,sha256=hACh83_cIv7-PwYMM3bQm2IBmNqiHw-3PAfDfAEKz9A,259
@@ -13,9 +13,9 @@ naeural_client/base/transaction.py,sha256=bfs6td5M0fINgPQNxhrl_AUjb1YiilLDQ-Cd_o
13
13
  naeural_client/base/payload/__init__.py,sha256=y8fBI8tG2ObNfaXFWjyWZXwu878FRYj_I8GIbHT4GKE,29
14
14
  naeural_client/base/payload/payload.py,sha256=v50D7mBBD2WwWzvpbRGMSr-X6vv5ie21IY1aDxTqe1c,2275
15
15
  naeural_client/bc/__init__.py,sha256=FQj23D1PrY06NUOARiKQi4cdj0-VxnoYgYDEht8lpr8,158
16
- naeural_client/bc/base.py,sha256=ckUjK8f8pk5t70-hnDMPoqRwcQXlPFERNgCli2MKSKQ,30790
16
+ naeural_client/bc/base.py,sha256=i8xv19VBhWXcN4IRpphQS9YYmoM_6rWvPiCzBxV3KZY,30840
17
17
  naeural_client/bc/chain.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- naeural_client/bc/ec.py,sha256=9yhMq3HEICQRLD9MO3YYlWrSIA9Dled4GAh4DVNs3h0,14593
18
+ naeural_client/bc/ec.py,sha256=L0KWdlsaR7lD--LtnwyBGbpiKHperxq1JLFYcNnFB50,22456
19
19
  naeural_client/certs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  naeural_client/certs/r9092118.ala.eu-central-1.emqxsl.com.crt,sha256=y-6io0tseyx9-a4Pmde1z1gPULtJNSYUpG_YFkYaMKU,1337
21
21
  naeural_client/cli/README.md,sha256=WPdI_EjzAbUW1aPyj1sSR8rLydcJKZtoiaEtklQrjHo,74
@@ -80,8 +80,8 @@ naeural_client/utils/__init__.py,sha256=mAnke3-MeRzz3nhQvhuHqLnpaaCSmDxicd7Ck9uw
80
80
  naeural_client/utils/comm_utils.py,sha256=4cS9llRr_pK_3rNgDcRMCQwYPO0kcNU7AdWy_LtMyCY,1072
81
81
  naeural_client/utils/config.py,sha256=t_VzyBnRHJa-Kt71HUu9gXxeDOri1Aqf_-gjO04gAYs,3681
82
82
  naeural_client/utils/dotenv.py,sha256=_AgSo35n7EnQv5yDyu7C7i0kHragLJoCGydHjvOkrYY,2008
83
- naeural_client-2.3.6.dist-info/METADATA,sha256=bKTzEhDLA8ts9qA2k7fRzB3FeD_DuHP0buXM76XibTg,14449
84
- naeural_client-2.3.6.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
85
- naeural_client-2.3.6.dist-info/entry_points.txt,sha256=PNdyotDaQBAslZREx5luVyj0kqpQnwNACwkFNTPIHU4,55
86
- naeural_client-2.3.6.dist-info/licenses/LICENSE,sha256=cvOsJVslde4oIaTCadabXnPqZmzcBO2f2zwXZRmJEbE,11311
87
- naeural_client-2.3.6.dist-info/RECORD,,
83
+ naeural_client-2.4.0.dist-info/METADATA,sha256=mzcnBsvg7KtWXTjGruh8XKzuxxF7wp9UYuRPfUl-GR0,14449
84
+ naeural_client-2.4.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
85
+ naeural_client-2.4.0.dist-info/entry_points.txt,sha256=PNdyotDaQBAslZREx5luVyj0kqpQnwNACwkFNTPIHU4,55
86
+ naeural_client-2.4.0.dist-info/licenses/LICENSE,sha256=cvOsJVslde4oIaTCadabXnPqZmzcBO2f2zwXZRmJEbE,11311
87
+ naeural_client-2.4.0.dist-info/RECORD,,