diff --git a/duniterpy/key/signing_key.py b/duniterpy/key/signing_key.py index 45696723d0eb68cc88f65e7f97f737abee8e9b83..bd5651022d9ed95b2faca9cb2fb91cd304ffd7fd 100644 --- a/duniterpy/key/signing_key.py +++ b/duniterpy/key/signing_key.py @@ -22,7 +22,7 @@ from typing import Optional, Union, TypeVar, Type import libnacl.sign import pyaes from libnacl.utils import load_key -from hashlib import scrypt +from hashlib import scrypt, sha256 from .scrypt_params import ScryptParams from .base58 import Base58Encoder @@ -33,6 +33,22 @@ from ..tools import ( convert_seed_to_seedhex, ) +DEWIF_CURRENCY_CODE_NONE = 0x00000000 +DEWIF_CURRENCY_CODE_G1 = 0x00000001 +DEWIF_CURRENCY_CODE_G1_TEST = 0x10000001 + + +def chunkstring(data: bytes, length: int): + """ + Return a tuple of chunks sized at length from the data bytes + + :param data: Data to split + :param length: Size of chunks + :return: + """ + return (data[0 + i : length + i] for i in range(0, len(data), length)) + + # required to type hint cls in classmethod SigningKeyType = TypeVar("SigningKeyType", bound="SigningKey") @@ -509,3 +525,27 @@ Data: {data}""".format( seed = bytes(base64.b64decode(secret)[0:32]) return cls(seed) + + @classmethod + def from_dubp_mnemonic(cls, mnemonic: str, scrypt_params: ScryptParams = None): + """ + Generate key pair instance from a DUBP mnemonic passphrase + + :param mnemonic: Passphrase generated from a mnemonic algorithm + :param scrypt_params: ScryptParams instance (default=None) + :return: + """ + if scrypt_params is None: + scrypt_params = ScryptParams() + + _password = mnemonic.encode("utf-8") # type: bytes + _salt = sha256(b"dubp" + _password).digest() # type: bytes + _seed = scrypt( + password=_password, + salt=_salt, + n=scrypt_params.N, # 4096 + r=scrypt_params.r, # 16 + p=scrypt_params.p, # 1 + dklen=scrypt_params.seed_length, # 32 + ) # type: bytes + return cls(_seed) diff --git a/tests/key/test_signing_key.py b/tests/key/test_signing_key.py index 1bd9a4ae02935d36c14efd464a512cd49c62cee7..3ddf8b2fcd8304f3f3e9cf073c978a4bf7ed9fe2 100644 --- a/tests/key/test_signing_key.py +++ b/tests/key/test_signing_key.py @@ -14,7 +14,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. """ - +import base64 import os from duniterpy.key import VerifyingKey, SigningKey, PublicKey @@ -144,3 +144,16 @@ class TestSigningKey(unittest.TestCase): sign_key_load.vk.hex(), "d27f4cb2bfadbaf45b61714b896d4639ab90db035aee746611cdd342bdaa8996", ) + + def test_dubp_mnemonic(self): + mnemonic = ( + "tongue cute mail fossil great frozen same social weasel impact brush kind" + ) + + keypair = SigningKey.from_dubp_mnemonic(mnemonic) + + self.assertEqual( + base64.b64encode(keypair.seed).decode("utf-8"), + "qGdvpbP9lJe7ZG4ZUSyu33KFeAEs/KkshAp9gEI4ReY=", + ) + self.assertEqual(keypair.pubkey, "732SSfuwjB7jkt9th1zerGhphs6nknaCBCTozxUcPWPU")