Ejemplo n.º 1
0
def gen_mlsag(pk, xx, index):
    """
    Multilayered Spontaneous Anonymous Group Signatures (MLSAG signatures)

    These are aka MG signatutes in earlier drafts of the ring ct paper
    c.f. http://eprint.iacr.org/2015/1098 section 2.
    keyImageV just does I[i] = xx[i] * Hash(xx[i] * G) for each i

    Gen creates a signature which proves that for some column in the keymatrix "pk"
       the signer knows a secret key for each row in that column
    Ver verifies that the MG sig was created correctly

    :param pk:
    :param xx:
    :param index:
    :return:
    """
    rows = len(xx)
    cols = len(pk)
    logger.debug("Generating MG sig of size %s x %s" % (rows, cols))
    logger.debug("index is: %s, %s" % (index, pk[index]))
    c = [None] * cols
    alpha = scalar_gen_vector(rows)
    I = key_image_vector(xx)
    L = key_matrix(rows, cols)
    R = key_matrix(rows, cols)
    s = key_matrix(rows, cols)

    m = "".join(pk[0])
    for i in range(1, cols):
        m = m + "".join(pk[i])

    L[index] = [crypto.scalarmult_base(aa) for aa in alpha]  # L = aG
    Hi = hash_key_vector(pk[index])
    R[index] = [crypto.scalarmult(Hi[ii], alpha[ii])
                for ii in range(0, rows)]  # R = aI

    oldi = index
    i = (index + 1) % cols
    c[i] = crypto.cn_fast_hash(m + "".join(L[oldi]) + "".join(R[oldi]))

    while i != index:
        s[i] = scalar_gen_vector(rows)
        L[i] = [
            crypto.add_keys2(s[i][j], c[i], pk[i][j]) for j in range(0, rows)
        ]

        Hi = hash_key_vector(pk[i])
        R[i] = [
            crypto.add_keys3(s[i][j], Hi[j], c[i], I[j])
            for j in range(0, rows)
        ]
        oldi = i
        i = (i + 1) % cols
        c[i] = crypto.cn_fast_hash(m + "".join(L[oldi]) + "".join(R[oldi]))

    s[index] = [
        crypto.sc_mulsub(c[index], xx[j], alpha[j]) for j in range(0, rows)
    ]  # alpha - c * x
    return I, c[0], s
Ejemplo n.º 2
0
    def test_encrypt_base(self):
        for i in range(10):
            key = crypto.cn_fast_hash(crypto.encodeint(crypto.random_scalar()))
            data = crypto.cn_fast_hash(crypto.encodeint(
                crypto.random_scalar())) * (i + 1)

            ciphertext = chacha.encrypt(key, data)
            plaintext = chacha.decrypt(key, ciphertext)
            self.assertEqual(plaintext, data)

            plaintext2 = chacha.decrypt(
                key,
                bytes(int(ciphertext[0]) ^ 0xff) + ciphertext[1:])
            self.assertNotEqual(plaintext2, data)
Ejemplo n.º 3
0
    def back_encrypt(self, authenticated=True):
        for i in range(5):
            priv_key = crypto.random_scalar()
            data = crypto.cn_fast_hash(crypto.encodeint(
                crypto.random_scalar())) * (i + 1)

            blob = chacha.encrypt_xmr(priv_key,
                                      data,
                                      authenticated=authenticated)
            plaintext = chacha.decrypt_xmr(priv_key,
                                           blob,
                                           authenticated=authenticated)
            self.assertEqual(data, plaintext)

            try:
                plaintext2 = chacha.decrypt_xmr(
                    crypto.sc_add(priv_key, crypto.sc_init(1)),
                    blob,
                    authenticated=authenticated,
                )
                if authenticated:
                    self.fail("Signature error expected")
                else:
                    self.assertNotEqual(data, plaintext2)

            except:
                if not authenticated:
                    raise
Ejemplo n.º 4
0
def ver_mlsag(pk, I, c0, s):
    """
    Verify MLSAG
    :param pk:
    :param I:
    :param c0:
    :param s:
    :return:
    """
    rows = len(pk[0])
    cols = len(pk)
    logger.debug("verifying MG sig of dimensions %s x %s" % (rows, cols))
    c = [None] * (cols + 1)
    c[0] = c0
    L = key_matrix(rows, cols)
    R = key_matrix(rows, cols)

    m = "".join(pk[0])
    for i in range(1, cols):
        m = m + "".join(pk[i])

    i = 0
    while i < cols:
        L[i] = [crypto.add_keys2(s[i][j], c[i], pk[i][j]) for j in range(0, rows)]

        Hi = hash_key_vector(pk[i])
        R[i] = [crypto.add_keys3(s[i][j], Hi[j], c[i], I[j]) for j in range(0, rows)]

        oldi = i
        i = i + 1
        c[i] = crypto.cn_fast_hash(m + "".join(L[oldi]) + "".join(R[oldi]))

    return c0 == c[cols]
Ejemplo n.º 5
0
    def set_seed(self, seed, path=None, slip0010=False):
        """
        Sets master secret for BIP44 derivation
        :param seed:
        :param path:
        :param slip0010:
        :return:
        """
        self.master_seed = seed
        self.is_slip0010 = slip0010
        if path is None:
            self.path = DEFAULT_BIP44_PATH if not slip0010 else DEFAULT_SLIP0010_PATH
        else:
            self.path = path
        wl = bip32.Wallet.from_master_secret(seed, use_ed25519=slip0010)

        # Generate private keys based on the gen mechanism. Bip44 path + Monero backward compatible
        data = wl.get_child_for_path(self.path)
        self.pre_hash = binascii.unhexlify(data.private_key.get_key())

        if slip0010:
            self.monero_master = crypto.encodeint(
                crypto.decodeint(self.pre_hash))

        else:
            # Ledger way = words -> bip39 pbkdf -> master seed -> bip32 normal with
            #  "Bitcoin seed" seed, get private key node -> cn_fast_hash -> monero master secret
            self.monero_master = crypto.cn_fast_hash(self.pre_hash)

        self.set_monero_seed(self.monero_master)
Ejemplo n.º 6
0
 def test_cn_fast_hash(self):
     inp = unhexlify(
         b"259ef2aba8feb473cf39058a0fe30b9ff6d245b42b6826687ebd6b63128aff6405"
     )
     res = crypto.cn_fast_hash(inp)
     self.assertEqual(
         res,
         unhexlify(
             b"86db87b83fb1246efca5f3b0db09ce3fa4d605b0d10e6507cac253dd31a3ec16"
         ),
     )
Ejemplo n.º 7
0
def gen_schnorr_non_linkable(x, P1, P2, index):
    """
    Generates simple Schnorr
    :param x:
    :param P1:
    :param P2:
    :param index:
    :return:
    """
    a = crypto.random_scalar()
    L1 = crypto.scalarmult_base(a)
    s2 = crypto.random_scalar()
    c2 = crypto.cn_fast_hash(crypto.encodepoint(L1))
    L2 = crypto.point_add(
        crypto.scalarmult_base(s2),
        crypto.scalarmult(P2 if index == 0 else P1, crypto.decodeint(c2)),
    )
    c1 = crypto.cn_fast_hash(crypto.encodepoint(L2))
    s1 = crypto.sc_mulsub(x, crypto.decodeint(c1), a)

    return (L1, s1, s2) if index == 0 else (L2, s2, s1)
Ejemplo n.º 8
0
def generate_monero_keys(seed):
    """
    Generates spend key / view key from the seed in the same manner as Monero code does.

    account.cpp:
    crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random).
    :param seed:
    :return:
    """
    spend_sec, spend_pub = generate_keys(crypto.decodeint(seed))
    hash = crypto.cn_fast_hash(crypto.encodeint(spend_sec))
    view_sec, view_pub = generate_keys(crypto.decodeint(hash))
    return spend_sec, spend_pub, view_sec, view_pub
Ejemplo n.º 9
0
    def test_signature(self):
        for i in range(10):
            priv = crypto.random_scalar()
            data = crypto.cn_fast_hash(bytes(bytearray([i])))

            c, r, pub = crypto.generate_signature(data, priv)
            res = crypto.check_signature(data, c, r, pub)
            self.assertEqual(res, 1)

            res2 = crypto.check_signature(data,
                                          crypto.sc_add(c, crypto.sc_init(1)),
                                          r, pub)
            self.assertEqual(res2, 0)
Ejemplo n.º 10
0
def ver_schnorr_non_linkable(P1, P2, L1, s1, s2):
    """
    Verifies Schnorr signature generated by gen_schnorr_non_linkable
    :param P1:
    :param P2:
    :param L1:
    :param s1:
    :param s2:
    :return:
    """
    c2 = crypto.cn_fast_hash(crypto.encodepoint(L1))
    L2 = crypto.point_add(crypto.scalarmult_base(s2),
                          crypto.scalarmult(P2, crypto.decodeint(c2)))
    c1 = crypto.cn_fast_hash(L2)
    L1p = crypto.point_add(crypto.scalarmult_base(s1),
                           crypto.scalarmult(P1, crypto.decodeint(c1)))

    if L1 == L1p:
        return 0

    else:
        logger.warning("Didn't verify L1: %s, L1p: %s" % (L1, L1p))
        return -1
Ejemplo n.º 11
0
    def set_seed(self, seed, path="m/44'/128'/0'/0/0"):
        """
        Sets master secret for BIP44 derivation
        :param seed:
        :param path:
        :return:
        """
        self.master_seed = seed
        wl = bip32.Wallet.from_master_secret(seed)

        # Generate private keys based on the gen mechanism. Bip44 path + Monero backward compatible
        data = wl.get_child_for_path(path)
        self.pre_hash = binascii.unhexlify(data.private_key.get_key())
        self.hashed = crypto.cn_fast_hash(self.pre_hash)
        self.set_monero_seed(self.hashed)
Ejemplo n.º 12
0
def encrypt_payment_id(payment_id, public_key, secret_key):
    """
    Encrypts payment_id hex.
    Used in the transaction extra. Only recipient is able to decrypt.
    :param payment_id:
    :param public_key:
    :param secret_key:
    :return:
    """
    derivation_p = crypto.generate_key_derivation(public_key, secret_key)
    derivation = bytearray(33)
    derivation = crypto.encodepoint_into(derivation_p, derivation)
    derivation[32] = 0x8b
    hash = crypto.cn_fast_hash(derivation)
    pm_copy = bytearray(payment_id)
    for i in range(8):
        pm_copy[i] ^= hash[i]
    return pm_copy
Ejemplo n.º 13
0
def encrypt_xmr(priv_key, plaintext, authenticated=True):
    """
    Monero-like authenticated encryption with Chacha20 and EC signature

    :param priv_key:
    :param plaintext:
    :param authenticated:
    :return:
    """
    key = generate_key(crypto.encodeint(priv_key))
    ciphertext = encrypt(key, plaintext)
    if not authenticated:
        return ciphertext

    hash = crypto.cn_fast_hash(ciphertext)
    c, r, pub = crypto.generate_signature(hash, priv_key)
    signature = crypto.encodeint(c) + crypto.encodeint(r)
    return ciphertext + signature
Ejemplo n.º 14
0
def decrypt_xmr(priv_key, ciphertext, authenticated=True):
    """
    Monero-like authenticated decryption with Chacha20 and EC signature

    :param priv_key:
    :param ciphertext:
    :param authenticated:
    :return:
    """
    if authenticated:
        ciphertext, signature = ciphertext[:-64], ciphertext[-64:]
        hash = crypto.cn_fast_hash(ciphertext)
        pub = crypto.scalarmult_base(priv_key)
        c = crypto.decodeint(signature[:32])
        r = crypto.decodeint(signature[32:])
        res = crypto.check_signature(hash, c, r, pub)
        if not res:
            raise ValueError("Signature invalid")

    key = generate_key(crypto.encodeint(priv_key))
    return decrypt(key, ciphertext)
Ejemplo n.º 15
0
    def create_wallet(self, line):
        """
        Creates a new account
        :return:
        """
        if self.args.account_file:
            if os.path.exists(self.args.account_file):
                logger.error("Wallet file exists, could not overwrite")
                return

        print("Generating new wallet...")
        seed = crypto.random_bytes(32)

        wl = bip32.Wallet.from_master_secret(seed)
        seed_bip32_words, seed_bip32_words_indices = wl.to_seed_words()
        seed_bip32_b58 = wl.serialize_b58()

        # Generate private keys based on the gen mechanism. Bip44 path + Monero backward compatible
        data = wl.get_child_for_path("m/44'/128'/0'/0/0")
        to_hash = data.chain_code + binascii.unhexlify(
            data.private_key.get_key())

        # to_hash is initial seed in the Monero sense, recoverable from this seed
        hashed = crypto.cn_fast_hash(to_hash)
        electrum_words = " ".join(mnemonic.mn_encode(hashed))

        keys = monero.generate_monero_keys(hashed)
        spend_sec, spend_pub, view_sec, view_pub = keys

        print("Seed:             0x%s" %
              binascii.hexlify(seed).decode("ascii"))
        print("Seed bip39 words: %s" % " ".join(seed_bip32_words))
        print("Seed bip32 b58:   %s" % seed_bip32_b58)

        print("Seed Monero:      0x%s" %
              binascii.hexlify(hashed).decode("ascii"))
        print("Seed Monero wrds: %s" % electrum_words)

        print("")
        print("Spend key priv:   0x%s" %
              binascii.hexlify(crypto.encodeint(spend_sec)).decode("ascii"))
        print("View key priv:    0x%s" %
              binascii.hexlify(crypto.encodeint(view_sec)).decode("ascii"))
        print("")
        print("Spend key pub:    0x%s" %
              binascii.hexlify(crypto.encodepoint(spend_pub)).decode("ascii"))
        print("View key pub:     0x%s" %
              binascii.hexlify(crypto.encodepoint(view_pub)).decode("ascii"))

        self.init_with_keys(spend_sec, view_sec)
        print("")
        print("Address:          %s" % self.creds.address.decode("ascii"))

        self.account_data = collections.OrderedDict()
        self.account_data["seed"] = binascii.hexlify(seed).decode("ascii")
        self.account_data["spend_key"] = binascii.hexlify(
            crypto.encodeint(spend_sec)).decode("ascii")
        self.account_data["view_key"] = binascii.hexlify(
            crypto.encodeint(view_sec)).decode("ascii")
        self.account_data["meta"] = collections.OrderedDict([
            ("addr", self.creds.address.decode("ascii")),
            ("bip44_seed", binascii.hexlify(seed).decode("ascii")),
            ("bip32_39_words", " ".join(seed_bip32_words)),
            ("bip32_b58", seed_bip32_b58),
            ("monero_seed", binascii.hexlify(hashed).decode("ascii")),
            ("monero_words", electrum_words),
        ])

        if self.args.account_file:
            with open(self.args.account_file, "w+") as fh:
                json.dump(self.account_data, fh, indent=2)
        print("Wallet generated")
        self.update_prompt()
Ejemplo n.º 16
0
 def test_cn_fast_hash(self):
     inp = bytes(
         [
             0x25,
             0x9e,
             0xf2,
             0xab,
             0xa8,
             0xfe,
             0xb4,
             0x73,
             0xcf,
             0x39,
             0x05,
             0x8a,
             0x0f,
             0xe3,
             0x0b,
             0x9f,
             0xf6,
             0xd2,
             0x45,
             0xb4,
             0x2b,
             0x68,
             0x26,
             0x68,
             0x7e,
             0xbd,
             0x6b,
             0x63,
             0x12,
             0x8a,
             0xff,
             0x64,
             0x05,
         ]
     )
     res = crypto.cn_fast_hash(inp)
     self.assertEqual(
         res,
         bytes(
             [
                 0x86,
                 0xdb,
                 0x87,
                 0xb8,
                 0x3f,
                 0xb1,
                 0x24,
                 0x6e,
                 0xfc,
                 0xa5,
                 0xf3,
                 0xb0,
                 0xdb,
                 0x09,
                 0xce,
                 0x3f,
                 0xa4,
                 0xd6,
                 0x05,
                 0xb0,
                 0xd1,
                 0x0e,
                 0x65,
                 0x07,
                 0xca,
                 0xc2,
                 0x53,
                 0xdd,
                 0x31,
                 0xa3,
                 0xec,
                 0x16,
             ]
         ),
     )