Skip to content
Snippets Groups Projects
Commit e92a661d authored by Vincent Texier's avatar Vincent Texier
Browse files

[feat] #151 Add DEWIF file format support for wallets

See RFC0013 of the Duniter Project
parent e74fb6c2
No related branches found
No related tags found
No related merge requests found
Pipeline #11335 passed
...@@ -553,3 +553,75 @@ Data: {data}""".format( ...@@ -553,3 +553,75 @@ Data: {data}""".format(
dklen=scrypt_params.seed_length, # 32 dklen=scrypt_params.seed_length, # 32
) # type: bytes ) # type: bytes
return cls(_seed) return cls(_seed)
@classmethod
def from_dewif_file(
cls, path: Union[str, PathLike], password: str
) -> SigningKeyType:
"""
Load a DEWIF encrypted file using the password to decrypt
Add dewif_version and dewif_currency properties to the instance
:param path: Path of the file
:param password: Password to decrypt the file
:return:
"""
scrypt_params = ScryptParams()
aes_key = scrypt(
password=password.encode("utf-8"),
salt=sha256(f"dewif{password}".encode("utf-8")).digest(),
n=scrypt_params.N, # 4096
r=scrypt_params.r, # 16
p=scrypt_params.p, # 1
dklen=scrypt_params.seed_length, # 32
)
aes = AESModeOfOperationECB(aes_key)
with open(path, "rb") as file_handler:
file_handler.seek(8)
# header = file_handler.read(8)
# version, currency = struct.unpack("ii", header)
encrypted_data = file_handler.read()
data = b"".join(map(aes.decrypt, chunkstring(encrypted_data, 16)))
seed = data[:32]
public_key = data[32:]
signing_key = cls(seed)
assert signing_key.vk == public_key
return signing_key
def save_dewif_v1_file(
self,
path: Union[str, PathLike],
password: str,
currency: int = DEWIF_CURRENCY_CODE_G1,
) -> None:
"""
Save the instance seed in an encrypted DEWIF V1 file
Use the password to encrypt data
:param path: Path of the file to save
:param password: Password to encrypt data
:param currency: Currency code (default=tikka.domain.dewif.DEWIF_CURRENCY_CODE_G1)
:return:
"""
scrypt_params = ScryptParams()
aes_key = scrypt(
password=password.encode("utf-8"),
salt=sha256(f"dewif{password}".encode("utf-8")).digest(),
n=scrypt_params.N, # 4096
r=scrypt_params.r, # 16
p=scrypt_params.p, # 1
dklen=scrypt_params.seed_length, # 32
)
header = struct.pack(">ii", 1, currency)
data = self.seed + self.vk
aes = AESModeOfOperationECB(aes_key)
encrypted_data = b"".join(map(aes.encrypt, chunkstring(data, 16)))
with open(path, "wb") as file_handler:
file_handler.write(header + encrypted_data)
...@@ -16,11 +16,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. ...@@ -16,11 +16,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
""" """
import base64 import base64
import os import os
from hashlib import scrypt
from pathlib import Path
from duniterpy.key import VerifyingKey, SigningKey, PublicKey from duniterpy.key import VerifyingKey, SigningKey, PublicKey
from duniterpy.key.scrypt_params import ScryptParams from duniterpy.key.scrypt_params import ScryptParams
import unittest import unittest
from duniterpy.key.signing_key import DEWIF_CURRENCY_CODE_G1_TEST
TEST_FILE_PATH = "/tmp/test_file.txt" TEST_FILE_PATH = "/tmp/test_file.txt"
...@@ -157,3 +161,32 @@ class TestSigningKey(unittest.TestCase): ...@@ -157,3 +161,32 @@ class TestSigningKey(unittest.TestCase):
"qGdvpbP9lJe7ZG4ZUSyu33KFeAEs/KkshAp9gEI4ReY=", "qGdvpbP9lJe7ZG4ZUSyu33KFeAEs/KkshAp9gEI4ReY=",
) )
self.assertEqual(keypair.pubkey, "732SSfuwjB7jkt9th1zerGhphs6nknaCBCTozxUcPWPU") self.assertEqual(keypair.pubkey, "732SSfuwjB7jkt9th1zerGhphs6nknaCBCTozxUcPWPU")
def test_dewif_v1_save_and_load(self):
path = "/tmp/test.dewif"
password = "toto titi tata"
scrypt_params = ScryptParams()
seed = scrypt(
password=b"user password",
salt=b"user salt",
n=scrypt_params.N, # 4096
r=scrypt_params.r, # 16
p=scrypt_params.p, # 1
dklen=scrypt_params.seed_length,
)
signing_key = SigningKey(seed)
signing_key.save_dewif_v1_file(path, password, DEWIF_CURRENCY_CODE_G1_TEST)
with open(path, "rb") as file_handler:
b64_content = base64.b64encode(file_handler.read()).decode("utf-8")
self.assertEqual(
b64_content,
"AAAAARAAAAGfFDAs+jVZYkfhBlHZZ2fEQIvBqnG16g5+02cY18wSOjW0cUg2JV3SUTJYN2CrbQeRDwGazWnzSFBphchMmiL0",
)
signing_key_loaded = SigningKey.from_dewif_file(path, password)
self.assertEqual(signing_key_loaded.seed, signing_key.seed)
if Path(path).exists():
Path(path).unlink()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment