Author

Topic: Lattice Attack Script Modified (Read 330 times)

member
Activity: 127
Merit: 14
Life aint interesting without any cuts and bruises
May 06, 2024, 08:16:48 PM
#7
Not sure where in the code should it go the definitions you mentioned.

I get another error, but I think I am closer now. I think I simply need to change of private_value to integer, But I am not sure how.

There is a comment in the lattice_attack.py that says how to convert between integer and hex, but it doesnt say where those given equations should go:

# To convert to integer :
# if got bytes use : int.from_bytes(bytesvar, bytesorder="big")
# if got hex use : int(hexintvar, 16)
#

Has anyone got this to actually work? just curious...


theres no point running this. as the rsz created will not point to the correct private key or public key. ran it a long time ago.
member
Activity: 873
Merit: 22
$$P2P BTC BRUTE.JOIN NOW ! https://uclck.me/SQPJk
May 06, 2024, 07:22:23 PM
#6
Not sure where in the code should it go the definitions you mentioned.

I get another error, but I think I am closer now. I think I simply need to change of private_value to integer, But I am not sure how.

There is a comment in the lattice_attack.py that says how to convert between integer and hex, but it doesnt say where those given equations should go:

# To convert to integer :
# if got bytes use : int.from_bytes(bytesvar, bytesorder="big")
# if got hex use : int(hexintvar, 16)
#

Has anyone got this to actually work? just curious...


krossfire know kode, he can do it
jr. member
Activity: 34
Merit: 2
May 06, 2024, 05:18:35 PM
#5
Not sure where in the code should it go the definitions you mentioned.

I get another error, but I think I am closer now. I think I simply need to change of private_value to integer, But I am not sure how.

There is a comment in the lattice_attack.py that says how to convert between integer and hex, but it doesnt say where those given equations should go:

# To convert to integer :
# if got bytes use : int.from_bytes(bytesvar, bytesorder="big")
# if got hex use : int(hexintvar, 16)
#

Has anyone got this to actually work? just curious...
jr. member
Activity: 34
Merit: 2
May 06, 2024, 05:11:18 PM
#4
KRASH

Can you add calculation of privkey to scrypt?

pvk = (inv(r) *  ( (k*s)-z )    ) % N

Quote
k   = (inv(s) * ( z + r * pvk ) ) % N
s   = (inv(k) * ( z + r * pvk)  ) % N
pvk = (inv(r) *  ( (k*s)-z )    ) % N
r * pvk = (  (k*s)-z )  % N

r , s , z, private Key , k  
All may be odd or even numbers

I have added those definitions but I get an error. Is that the right part of the code on the lattice_attack.py for them to go in? THANKS.

mahuro@machine1:~/lattice-attack$ python3 lattice_attack.py -f data7.json
  File "/home/mahuro/lattice-attack/lattice_attack.py", line 41
    r * pvk = (  (k*s)-z )  % N
    ^^^^^^^
SyntaxError: cannot assign to expression here. Maybe you meant '==' instead of '='?
member
Activity: 873
Merit: 22
$$P2P BTC BRUTE.JOIN NOW ! https://uclck.me/SQPJk
May 06, 2024, 03:15:47 PM
#3
KRASH

Can you add calculation of privkey to scrypt?

pvk = (inv(r) *  ( (k*s)-z )    ) % N

Quote
k   = (inv(s) * ( z + r * pvk ) ) % N
s   = (inv(k) * ( z + r * pvk)  ) % N
pvk = (inv(r) *  ( (k*s)-z )    ) % N
r * pvk = (  (k*s)-z )  % N

r , s , z, private Key , k  
All may be odd or even numbers
jr. member
Activity: 34
Merit: 2
May 06, 2024, 01:38:07 PM
#2
Hi there,
 This is interesting, but is not quite working for me. After I generate the json file, once I run the lattice.py I get this error:

Loading data from file data7.json
Public key data invalid, not on the given SECP256K1 curve.


I think it has to do with the format that the xY coordinates I am putting in is not correct. My question it, where and how you get this XY ( or RS) coordinates from, so that they are in the right format to go in gendata.py?

Thanks for your input

member
Activity: 127
Merit: 14
Life aint interesting without any cuts and bruises
January 05, 2023, 02:03:38 PM
#1
Modified the gen_data.py script in https://github.com/bitlogik/lattice-attack.

 Now every set of R,S,Z(HASH) Signatures shows the K_Nonce value used. Does not show up in the json file like the other datas though. It's directly on Terminal. Modified full code below.

In gen_data.py, don't forget to input your own public keys XY coordinates.
Code:

#!/usr/bin/env python3

import argparse
import random
import json

import ecdsa_lib


def generates_signatures(number_sigs, message, kbits, data_type, curve):
    print("Preparing Data")
    d_key = random.randrange(ecdsa_lib.curve_n(curve))
    print("Private key to be found (as demo) :")
    print(hex(d_key))
    sigs = []
    sz_curve = ecdsa_lib.curve_size(curve)
    kbi = int(2 ** kbits)
    print(f"Generating {number_sigs} signatures with curve {curve.upper()}")
    print(f" leaking {kbits} bits for k ({data_type})  ...")
    if message is not None:
        msg = message.encode("utf8")
        # Always hash message provided with SHA2-256, whatever
        hash_int = ecdsa_lib.sha2_int(msg)
    for _ in range(number_sigs):
        if message is None:
            # Use a random different message for each signature
            # Note : there is no associated message from the hash
            #  Do not ever that in practice, this is insecure, only here for demo
            hash_int = random.randrange(ecdsa_lib.curve_n(curve))
        # Compute signatures with k (nonce), r, s, hash
        sig_info = ecdsa_lib.ecdsa_sign_kout(hash_int, d_key, curve)
        # pack and save data as : r, s, hash, k%(2^bits) (partial k : "kp")
        
        sigs.append(
            {
                "r": sig_info[0],
                "s": sig_info[1],
                "kp": sig_info[2] % kbi
                if data_type == "MSB"
                else sig_info[2] >> (sz_curve - kbits),
            }
            
        )
        print(f"k_nonce:{sig_info[2]}")

        if message is None:
            sigs[-1]["hash"] = hash_int
    ret = {
        "curve": curve.upper(),
        "public_key": (31504125288796341338541169388783846543997786027594142627385926708036691251730,
        29015715595623874326232564738946807912877814040423899127791236573353650594580),
        "known_type": data_type,
        "known_bits": kbits,
        "signatures": sigs,
    }
    if message is not None:
        ret["message"] = list(msg)
    return ret


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Generate data for ECDSA attack."
    )
    parser.add_argument(
        "-f",
        default="data1.json",
        help="File name output",
        metavar="fileout",
    )
    parser.add_argument(
        "-m",
        help="Message string",
        metavar="msg",
    )
    parser.add_argument(
        "-c", default="secp256k1", help="Elliptic curve name", metavar="curve"
    )
    parser.add_argument(
        "-b",
        default=8,
        type=int,
        help="Number of known bits (at least 4)",
        metavar="nbits",
    )
    parser.add_argument(
        "-t", default="MSB", help="bits type : MSB or LSB", metavar="type"
    )
    parser.add_argument(
        "-n",
        default=100,
        type=int,
        help="Number of signatures to generate",
        metavar="num",
    )
    arg = parser.parse_args()
    sigs_data = generates_signatures(arg.n, arg.m, arg.b, arg.t, arg.c)
    with open(arg.f, "w") as fout:
        json.dump(sigs_data, fout)
    print(f"File {arg.f} written with all data.")


Updated the ecdsa_lib.py to include print function for K_Nonce as well.

Code:

#!/usr/bin/env python3

# Lattice ECDSA Attack : ECDSA and cryptographic library

# Install cryptography
#  pip3 install cryptography
#   or
#  apt install python3-cryptography


import hashlib
import secrets

from cryptography.hazmat import backends
from cryptography.hazmat.primitives.asymmetric import ec


CURVES_ORDER = {
    "SECP224R1": int(
        "2695994666715063979466701508701962594045780771442439172168272236" "8061"
    ),
    "SECP256K1": int(
        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16
    ),
    "SECP256R1": int(
        "11579208921035624876269744694940757352999695522413576034242225906"
        "1068512044369"
    ),
    "SECP384R1": int(
        "39402006196394479212279040100143613805079739270465446667946905279"
        "627659399113263569398956308152294913554433653942643"
    ),
    "SECP521R1": int(
        "68647976601306097149819007990813932172694353001433054093944634591"
        "85543183397655394245057746333217197532963996371363321113864768612"
        "440380340372808892707005449"
    ),
}


def inverse_mod(a_num, m_mod):
    # a_num^-1 mod m_mod, m_mod must be prime
    # If not used on a prime modulo,
    #  can throw ZeroDivisionError.
    if a_num < 0 or m_mod <= a_num:
        a_num = a_num % m_mod
    i, j = a_num, m_mod
    x_a, x_b = 1, 0
    while i != 1:
        quot, rem = divmod(j, i)
        x_rem = x_b - quot * x_a
        j, i, x_b, x_a = i, rem, x_a, x_rem
    return x_a % m_mod


def sha2(raw_message):
    # SHA-2 256
    return hashlib.sha256(raw_message).digest()


def bytes_to_int(bytes_data):
    return int.from_bytes(bytes_data, "big")


def sha2_int(data):
    return bytes_to_int(sha2(data))


def curve_size(curve_name):
    # return the curve size (log2 N) from its name string
    try:
        curve_obj = getattr(ec, curve_name.upper())()
    except Exception as exc:
        raise Exception(
            f"Unknown curves. Curves names available : {list(CURVES_ORDER.keys())}"
        ) from exc
    return curve_obj.key_size


def curve_n(curve_name):
    # return the curve order "N" from its name string
    order = CURVES_ORDER.get(curve_name.upper())
    if not order:
        raise Exception(
            f"Unknown curves. Curves names available : {list(CURVES_ORDER.keys())}"
        )
    return order


def check_publickey(pubkey, curve_str):
    # Check pubkey (x,y) belongs on the curve
    try:
        curve_obj = getattr(ec, curve_str.upper())()
    except Exception as exc:
        raise Exception(
            f"Unknown curves. Curves names available : {list(CURVES_ORDER.keys())}"
        ) from exc
    if len(pubkey) != 2:
        raise Exception(
            'Public key data shall be provided as :\n "public_key" : [ x, y ]'
        )
    publickey_obj = ec.EllipticCurvePublicNumbers(pubkey[0], pubkey[1], curve_obj)
    ret = False
    try:
        publickey_obj.public_key(backends.default_backend())
        ret = True
    except ValueError:
        pass
    return ret


def privkey_to_pubkey(pv_key_int, curve_name):
    # Return public point coordinates (Scalar multiplication of pvkey with base point G)
    ec_backend = getattr(ec, curve_name.upper())()
    pubkey = (
        ec.derive_private_key(pv_key_int, ec_backend, backends.default_backend())
        .public_key()
        .public_numbers()
    )
    return [pubkey.x, pubkey.y]


def ecdsa_sign_kout(z_hash, pvkey, curve_name):
    # Perform ECDSA, but insecurely return the private k nonce
    n_mod = curve_n(curve_name)
    k_nonce = secrets.randbelow(n_mod)
    r_sig = scalar_mult_x(k_nonce, curve_name)
    s_sig = inverse_mod(k_nonce, n_mod) * (z_hash + r_sig * pvkey) % n_mod
    print(f"k_nonce: {k_nonce}")
    return r_sig, s_sig, k_nonce
    


def scalar_mult_x(d_scalar, curve):
    # Scalar multiplication of d with base point G
    # and return x, like ECDH with G.
    return privkey_to_pubkey(d_scalar, curve)[0]


With R,S,Z(HASH) & now K nonce revealed, You can now proceed here and input your datas. https://rawcdn.githack.com/nlitsme/bitcoinexplainer/aa50e86e8c72c04a7986f5f7c43bc2f98df94107/ecdsacrack.html to get to the Private key.

Wth this, its almost a done deal to get to the private key. Good luck.
Jump to: