-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add encryption for sr25519 and change all crypto types to sr25519
- Loading branch information
Showing
13 changed files
with
253 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
custom_components/robonomics/encryption_utils/sr25519/sr25519_const.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Consts for encryption | ||
ENCRYPTION_KEY_SIZE = 32 | ||
PUBLIC_KEY_SIZE = 32 | ||
MAC_KEY_SIZE = 32 | ||
MAC_VALUE_SIZE = 32 | ||
DERIVATION_KEY_SALT_SIZE = 32 | ||
DERIVATION_KEY_ROUNDS = 2048 # Number of iterations | ||
DERIVATION_KEY_SIZE = 64 # Desired length of the derived key | ||
PBKDF2_HASH_ALGORITHM = 'sha512' # Hashing algorithm | ||
NONCE_SIZE = 24 |
59 changes: 59 additions & 0 deletions
59
custom_components/robonomics/encryption_utils/sr25519/sr25519_decrypt.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
from .sr25519_const import NONCE_SIZE, DERIVATION_KEY_SALT_SIZE, PUBLIC_KEY_SIZE, MAC_VALUE_SIZE | ||
from .sr25519_utils import get_sr25519_agreement, bytes_concat, derive_key, generate_mac_data | ||
from nacl.secret import SecretBox | ||
import typing as tp | ||
|
||
|
||
def sr25519_decrypt(encrypted_message: bytes, receiver_private_key: bytes) -> tp.Tuple: | ||
# Split encrypted_message to parts | ||
message_public_key, salt, mac_value, nonce, sealed_message = _decapsulate_encrypted_message(encrypted_message) | ||
|
||
# Repeat encryption_key and mac_key generation based on encrypted_message | ||
agreement_key = get_sr25519_agreement(secret_key=receiver_private_key, | ||
public_key=message_public_key) | ||
master_secret = bytes_concat(message_public_key, agreement_key) | ||
encryption_key, mac_key = derive_key(master_secret, salt) | ||
|
||
# Get decrypted MAC value | ||
decrypted_mac_value = generate_mac_data( | ||
nonce=nonce, | ||
encrypted_message=sealed_message, | ||
message_public_key=message_public_key, | ||
mac_key=mac_key | ||
) | ||
|
||
# Check if MAC values the same | ||
assert mac_value == decrypted_mac_value, "MAC values do not match" | ||
|
||
# Decrypt the message | ||
decrypted_message = _nacl_decrypt(sealed_message, nonce, encryption_key) | ||
return decrypted_message, message_public_key | ||
|
||
|
||
def _decapsulate_encrypted_message(encrypted_message: bytes): | ||
assert len(encrypted_message) > NONCE_SIZE + DERIVATION_KEY_SALT_SIZE + PUBLIC_KEY_SIZE + MAC_VALUE_SIZE, \ | ||
"Wrong encrypted message length" | ||
|
||
message_public_key = encrypted_message[ | ||
NONCE_SIZE + DERIVATION_KEY_SALT_SIZE: NONCE_SIZE + DERIVATION_KEY_SALT_SIZE + PUBLIC_KEY_SIZE] | ||
|
||
salt = encrypted_message[NONCE_SIZE: NONCE_SIZE + DERIVATION_KEY_SALT_SIZE] | ||
mac_value = encrypted_message[ | ||
NONCE_SIZE + DERIVATION_KEY_SALT_SIZE + PUBLIC_KEY_SIZE: NONCE_SIZE + DERIVATION_KEY_SALT_SIZE | ||
+ PUBLIC_KEY_SIZE + MAC_VALUE_SIZE] | ||
|
||
nonce = encrypted_message[:NONCE_SIZE] | ||
sealed_message = encrypted_message[NONCE_SIZE + DERIVATION_KEY_SALT_SIZE + PUBLIC_KEY_SIZE + MAC_VALUE_SIZE:] | ||
|
||
return message_public_key, salt, mac_value, nonce, sealed_message | ||
|
||
|
||
def _nacl_decrypt(sealed_message: bytes, nonce: bytes, encryption_key: bytes): | ||
# Create a nacl SecretBox using the encryption key | ||
box = SecretBox(encryption_key) | ||
try: | ||
# Decrypt the message | ||
decrypted_message = box.decrypt(sealed_message, nonce) | ||
return decrypted_message | ||
except Exception as e: | ||
raise ValueError("Invalid secret or pubkey provided") from e |
70 changes: 70 additions & 0 deletions
70
custom_components/robonomics/encryption_utils/sr25519/sr25519_encrypt.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import os | ||
import typing as tp | ||
from substrateinterface import KeypairType, Keypair | ||
from nacl.secret import SecretBox | ||
|
||
from .sr25519_const import NONCE_SIZE, DERIVATION_KEY_SALT_SIZE | ||
from .sr25519_utils import get_sr25519_agreement, derive_key, bytes_concat, generate_mac_data | ||
|
||
|
||
def sr25519_encrypt(message: str | bytes, | ||
receiver_public_key: bytes, | ||
sender_keypair: tp.Optional[Keypair] = None | ||
) -> bytes: | ||
|
||
# 1. Ephemeral key generation if no sender keypair is provided | ||
if sender_keypair is not None: | ||
message_keypair = sender_keypair | ||
else: | ||
message_keypair = Keypair.create_from_mnemonic( | ||
mnemonic=Keypair.generate_mnemonic(), | ||
crypto_type=KeypairType.SR25519 | ||
) | ||
|
||
# 2. Key agreement | ||
agreement_key = get_sr25519_agreement(secret_key=message_keypair.private_key, | ||
public_key=receiver_public_key) | ||
|
||
# 2.5 Master secret and cryptographic random salt with KEY_DERIVATION_SALT_SIZE bytes | ||
master_secret = bytes_concat(message_keypair.public_key, agreement_key) | ||
salt = os.urandom(DERIVATION_KEY_SALT_SIZE) | ||
|
||
# 3. Key derivation | ||
encryption_key, mac_key = derive_key(master_secret, salt) | ||
|
||
# 4 Encryption | ||
nonce = os.urandom(NONCE_SIZE) | ||
encrypted_message = _nacl_encrypt(message, encryption_key, nonce) | ||
|
||
# 5 MAC Generation | ||
mac_value = generate_mac_data( | ||
nonce=nonce, | ||
encrypted_message=encrypted_message, | ||
message_public_key=message_keypair.public_key, | ||
mac_key=mac_key | ||
) | ||
|
||
return bytes_concat(nonce, salt, message_keypair.public_key, mac_value, encrypted_message) | ||
|
||
def _nacl_encrypt(message: str | bytes, encryption_key: bytes, nonce: bytes) -> bytes: | ||
# Ensure the encryption key is 32 bytes | ||
if len(encryption_key) != 32: | ||
raise ValueError("Encryption key must be 32 bytes long.") | ||
|
||
# Create a nacl SecretBox using the encryption key | ||
box = SecretBox(encryption_key) | ||
|
||
try: | ||
# Encrypt the message | ||
encrypted_message = box.encrypt(_message_to_bytes(message), nonce) | ||
return encrypted_message.ciphertext | ||
except Exception as e: | ||
raise ValueError("Invalid secret or pubkey provided") from e | ||
|
||
def _message_to_bytes(value): | ||
if isinstance(value, (bytes, bytearray)): | ||
return value | ||
elif isinstance(value, str): | ||
return value.encode('utf-8') | ||
else: | ||
raise TypeError("Unsupported message type for encryption") |
66 changes: 66 additions & 0 deletions
66
custom_components/robonomics/encryption_utils/sr25519/sr25519_utils.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import hmac | ||
import hashlib | ||
import rbcl | ||
|
||
from .sr25519_const import PBKDF2_HASH_ALGORITHM, DERIVATION_KEY_ROUNDS, DERIVATION_KEY_SIZE, MAC_KEY_SIZE, ENCRYPTION_KEY_SIZE | ||
|
||
def get_sr25519_agreement(secret_key: bytes, public_key: bytes) -> bytes: | ||
try: | ||
# Get canonical part of secret key | ||
canonical_secret_key = secret_key[:32] | ||
|
||
# Perform elliptic curve point multiplication | ||
# Since secret and public key are already in sr25519, that can be used as scalar and Ristretto point | ||
shared_secret = rbcl.crypto_scalarmult_ristretto255(s=canonical_secret_key, p=public_key) | ||
|
||
return shared_secret | ||
except Exception as e: | ||
raise ValueError("Invalid secret or pubkey provided") from e | ||
|
||
|
||
def derive_key(master_secret: bytes, salt: bytes) -> tuple: | ||
# Derive a 64-byte key using PBKDF2 | ||
password = hashlib.pbkdf2_hmac( | ||
PBKDF2_HASH_ALGORITHM, | ||
master_secret, | ||
salt, | ||
DERIVATION_KEY_ROUNDS, # Number of iterations | ||
dklen=DERIVATION_KEY_SIZE # Desired length of the derived key | ||
) | ||
|
||
assert len(password) >= MAC_KEY_SIZE + ENCRYPTION_KEY_SIZE, "Wrong derived key length" | ||
|
||
# Split the derived password into encryption key and MAC key | ||
mac_key = password[:MAC_KEY_SIZE] | ||
encryption_key = password[MAC_KEY_SIZE: MAC_KEY_SIZE + ENCRYPTION_KEY_SIZE] | ||
|
||
return encryption_key, mac_key | ||
|
||
|
||
|
||
def generate_mac_data(nonce: bytes, encrypted_message: bytes, message_public_key: bytes, mac_key: bytes) -> bytes: | ||
if len(mac_key) != 32: | ||
raise ValueError("MAC key must be 32 bytes long.") | ||
|
||
# Concatenate nonce, message public key, and encrypted message | ||
data_to_mac = bytes_concat(nonce, message_public_key, encrypted_message) | ||
|
||
# Generate HMAC-SHA256 | ||
mac_data = hmac.new( | ||
key=mac_key, | ||
msg=data_to_mac, | ||
digestmod=hashlib.sha256).digest() | ||
return mac_data | ||
|
||
|
||
def bytes_concat(*arrays) -> bytes: | ||
""" | ||
Concatenate multiple byte arrays into a single byte array. | ||
Args: | ||
*arrays: Variable length argument list of byte arrays to concatenate. | ||
Returns: | ||
bytes: A single concatenated byte array. | ||
""" | ||
return b''.join(arrays) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.