sidan-gin 0.1.0__py3-none-any.whl → 0.1.2__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.
sidan_gin/__init__.py CHANGED
@@ -1,3 +1,5 @@
1
1
  # flake8: noqa
2
2
 
3
+ from .encryption import *
3
4
  from .types import *
5
+ from .wallet import *
@@ -0,0 +1,3 @@
1
+ # flake8: noqa
2
+
3
+ from .cipher import *
@@ -0,0 +1,127 @@
1
+ import base64
2
+ import json
3
+ import os
4
+
5
+ from cryptography.hazmat.backends import default_backend
6
+ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
7
+ from cryptography.hazmat.primitives.hashes import SHA256
8
+ from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
9
+
10
+
11
+ def encrypt_with_cipher(
12
+ data: str, key: str, initialization_vector_size: int = 12
13
+ ) -> str:
14
+ """
15
+ Encrypt data using AES-GCM with a derived key from PBKDF2 and SHA-256.
16
+
17
+ :param data: The plaintext data to encrypt.
18
+ :param key: The input key used for encryption.
19
+ :param initialization_vector_size: The size of the IV (default is 12 bytes for AES-GCM).
20
+ :return: A JSON string containing the IV and ciphertext (both base64-encoded).
21
+ :raises ValueError: If encryption fails or input data is invalid.
22
+ """
23
+ # Derive a cryptographic key from the input key using PBKDF2 and SHA-256
24
+ salt = bytes(
25
+ initialization_vector_size
26
+ ) # Using a fixed salt (empty for simplicity)
27
+ kdf = PBKDF2HMAC(
28
+ algorithm=SHA256(),
29
+ length=32, # AES-256 requires a 256-bit key (32 bytes)
30
+ salt=salt,
31
+ iterations=100_000,
32
+ backend=default_backend(),
33
+ )
34
+ derived_key = kdf.derive(key.encode())
35
+
36
+ # Generate a random IV
37
+ iv = os.urandom(initialization_vector_size) # Generate a random IV
38
+ try:
39
+ # Initialize AES-GCM cipher
40
+ cipher = Cipher(
41
+ algorithms.AES(derived_key),
42
+ modes.GCM(iv),
43
+ backend=default_backend(),
44
+ )
45
+ encryptor = cipher.encryptor()
46
+
47
+ # Encrypt the data
48
+ ciphertext = encryptor.update(data.encode()) + encryptor.finalize()
49
+
50
+ # Get the authentication tag
51
+ tag = encryptor.tag
52
+
53
+ # Append the tag to the ciphertext to match Web Crypto API behavior
54
+ ciphertext_with_tag = ciphertext + tag
55
+ except Exception as e:
56
+ raise ValueError("Encryption failed") from e
57
+
58
+ # Encode the IV and ciphertext (with tag) in base64
59
+ iv_base64 = base64.b64encode(iv).decode("utf-8")
60
+ ciphertext_base64 = base64.b64encode(ciphertext_with_tag).decode("utf-8")
61
+
62
+ # Create a JSON-like string containing the IV and ciphertext
63
+ result = {
64
+ "iv": iv_base64,
65
+ "ciphertext": ciphertext_base64,
66
+ }
67
+
68
+ return json.dumps(result)
69
+
70
+
71
+ def decrypt_with_cipher(encrypted_data_json: str, key: str) -> str:
72
+ """
73
+ Decrypt data encrypted with AES-GCM using a derived key from PBKDF2 and SHA-256.
74
+ """
75
+ # Parse the encrypted data from JSON
76
+ try:
77
+ encrypted_data = json.loads(encrypted_data_json)
78
+ iv_base64 = encrypted_data["iv"]
79
+ ciphertext_base64 = encrypted_data["ciphertext"]
80
+ except (KeyError, json.JSONDecodeError) as e:
81
+ raise ValueError("Invalid encrypted data JSON") from e
82
+
83
+ # Decode the IV and ciphertext from base64
84
+ try:
85
+ iv = base64.b64decode(iv_base64)
86
+ ciphertext_with_tag = base64.b64decode(ciphertext_base64)
87
+ except base64.binascii.Error as e:
88
+ raise ValueError("Base64 decoding failed") from e
89
+
90
+ # In Web Crypto API, the tag is appended to the ciphertext
91
+ # Standard GCM tag length is 16 bytes (128 bits)
92
+ tag_length = 16
93
+ ciphertext = ciphertext_with_tag[:-tag_length]
94
+ tag = ciphertext_with_tag[-tag_length:]
95
+
96
+ # Derive a cryptographic key from the input key using PBKDF2 and SHA-256
97
+ salt = bytes(len(iv)) # Use the same salt size as the IV
98
+ kdf = PBKDF2HMAC(
99
+ algorithm=SHA256(),
100
+ length=32, # AES-256 requires a 256-bit key (32 bytes)
101
+ salt=salt,
102
+ iterations=100_000,
103
+ backend=default_backend(),
104
+ )
105
+ derived_key = kdf.derive(key.encode())
106
+
107
+ # Initialize AES-GCM cipher for decryption
108
+ try:
109
+ cipher = Cipher(
110
+ algorithms.AES(derived_key),
111
+ modes.GCM(iv, tag), # Pass the extracted tag to GCM mode
112
+ backend=default_backend(),
113
+ )
114
+ decryptor = cipher.decryptor()
115
+
116
+ # Decrypt the data
117
+ decrypted_data = decryptor.update(ciphertext) + decryptor.finalize()
118
+ except Exception as e:
119
+ raise ValueError("Decryption failed") from e
120
+
121
+ # Convert the decrypted data back to a string
122
+ try:
123
+ decrypted_str = decrypted_data.decode("utf-8")
124
+ except UnicodeDecodeError as e:
125
+ raise ValueError("Failed to convert decrypted data to UTF-8") from e
126
+
127
+ return decrypted_str
@@ -0,0 +1,3 @@
1
+ # flake8: noqa
2
+
3
+ from .wallet import *
@@ -0,0 +1,40 @@
1
+ # flake8: noqa: E501
2
+
3
+ from cbor2 import dumps, loads
4
+ from nacl.encoding import RawEncoder
5
+ from nacl.hash import blake2b
6
+ from pycardano import crypto, key
7
+
8
+
9
+ class HDWallet:
10
+ def __init__(self, mnemonic):
11
+ self.mnemonic = mnemonic
12
+ self.hd_wallet = crypto.HDWallet.from_mnemonic(self.mnemonic).derive_from_path(
13
+ "m/1852'/1815'/0'/0/0"
14
+ )
15
+ self.signing_key = key.ExtendedSigningKey.from_hdwallet(self.hd_wallet)
16
+ self.verification_key = self.signing_key.to_verification_key()
17
+
18
+ def sign_tx(self, tx_hex):
19
+ raw_decoded_cbor = loads(bytes.fromhex(tx_hex))
20
+ raw_tx_body = raw_decoded_cbor[0]
21
+ signature = self.sign(blake2b(dumps(raw_tx_body), 32, encoder=RawEncoder))
22
+ raw_witness_set = raw_decoded_cbor[1]
23
+ if 0 in raw_witness_set:
24
+ raw_vkeys = raw_witness_set[0]
25
+ raw_vkeys.append(
26
+ [self.verification_key.to_non_extended().to_cbor()[2::], signature]
27
+ )
28
+ raw_witness_set[0] = raw_vkeys
29
+ else:
30
+ raw_witness_set[0] = [
31
+ [self.verification_key.to_non_extended().to_cbor()[2::], signature]
32
+ ]
33
+ raw_decoded_cbor[1] = raw_witness_set
34
+ return dumps(raw_decoded_cbor).hex()
35
+
36
+ def sign_message_hex(self, message_hex):
37
+ return self.sign(bytes.fromhex(message_hex))
38
+
39
+ def sign(self, message):
40
+ return self.signing_key.sign(message)
@@ -1,13 +1,12 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: sidan-gin
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: A python library for Cardano development, compatible with Mesh and Whisky types.
5
- Home-page: https://github.com/sidan-lab/gin
6
5
  License: Apache-2.0
7
6
  Keywords: cardano
8
7
  Author: HinsonSIDAN
9
8
  Author-email: wongkahinhinson@gmail.com
10
- Requires-Python: >3.11
9
+ Requires-Python: >3.11,<4.0.0
11
10
  Classifier: Intended Audience :: Developers
12
11
  Classifier: License :: OSI Approved :: Apache Software License
13
12
  Classifier: Natural Language :: English
@@ -15,8 +14,12 @@ Classifier: Programming Language :: Python :: 3
15
14
  Classifier: Programming Language :: Python :: 3.12
16
15
  Classifier: Programming Language :: Python :: 3.13
17
16
  Classifier: Programming Language :: Python :: 3.11
17
+ Requires-Dist: cbor2 (>=5.6.5,<6.0.0)
18
+ Requires-Dist: cryptography (>=44.0.2,<45.0.0)
19
+ Requires-Dist: pycardano (>=0.12.3,<0.13.0)
18
20
  Requires-Dist: requests (>=2.25,<3.0)
19
21
  Project-URL: Documentation, https://github.com/sidan-lab/gin
22
+ Project-URL: Homepage, https://github.com/sidan-lab/gin
20
23
  Description-Content-Type: text/markdown
21
24
 
22
25
  # gin
@@ -1,4 +1,6 @@
1
- sidan_gin/__init__.py,sha256=w38yqTv24z2PXNpyyMy-fC5isk_bkxG7YC6KHTpjjc8,37
1
+ sidan_gin/__init__.py,sha256=vu8JEo4ADPBvK7l6_QhnhlKEnSYEmphSc7NE3jZG1dY,85
2
+ sidan_gin/encryption/__init__.py,sha256=4eCzQX7vgrrnnQFWVt86Kc_bBq9mzCdAuJbnNNaA62Y,38
3
+ sidan_gin/encryption/cipher.py,sha256=UuuwQ-1A9XcCZ716ng8hcUvwVcZPAaBVUKlYbn3_LRo,4414
2
4
  sidan_gin/types/__init__.py,sha256=r5Z6Bto1lE22yhNO9UThri6xoSUnoKk1Gb9A8aHMI24,241
3
5
  sidan_gin/types/account_info.py,sha256=Q5TIXE6Ofj3DlSlMdeL2jhrSQYaJHIqhEzW_oMe_5OU,193
4
6
  sidan_gin/types/asset.py,sha256=9LXNaCIJ0lyA0sGJOnusb_hDRf_ODZK4b3onUZmMPfI,2088
@@ -9,7 +11,9 @@ sidan_gin/types/protocol.py,sha256=MxC2TLtQVrQao6csvPx459JwIpM98XNwBYK05d-JRA8,5
9
11
  sidan_gin/types/transaction_info.py,sha256=c7rh7I38uTksnyDgAZKPdLlw0zVeQHfIVWrcWw-IwrA,220
10
12
  sidan_gin/types/utxo.py,sha256=HvnAJXxyLm0KUBnrqprMm8Pc8H2ebxGW339pFLKo7K4,452
11
13
  sidan_gin/types/value.py,sha256=cGFqxk15FrG2zy6PB5zMRq1vOBYfBQbMWbuqiCTEgRg,3345
12
- sidan_gin-0.1.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
13
- sidan_gin-0.1.0.dist-info/METADATA,sha256=nS976pOzqOo4mAxYzreb4HZZTr3UhmZPfTR5mXvMHAo,935
14
- sidan_gin-0.1.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
15
- sidan_gin-0.1.0.dist-info/RECORD,,
14
+ sidan_gin/wallet/__init__.py,sha256=_ylJyQIxP_-knJYHUxgDVYWSWG3MMhd_xiI9CH0sFmA,38
15
+ sidan_gin/wallet/wallet.py,sha256=ijZ2ICI91E8MPc73ueytqV5s-6bbj3O3hJs8RBnuljw,1442
16
+ sidan_gin-0.1.2.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
17
+ sidan_gin-0.1.2.dist-info/METADATA,sha256=xAecfFzR9ZMNb4lIiN4CMD3ROej70nxuQ9Nlvf5HNQ4,1083
18
+ sidan_gin-0.1.2.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
19
+ sidan_gin-0.1.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.1
2
+ Generator: poetry-core 2.1.2
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any