Exemplo n.º 1
0
    def verify(self, commitment, index_range):
        """For an object created without a private key,
        check that the opened commitment verifies for at least
        one NUMS point as defined by the range in index_range.
        """
        if not all([self.P, self.P2, self.s, self.e]):
            raise PoDLEError("Verify called without sufficient data")
        if not self.get_commitment() == commitment:
            return False

        for J in [getNUMS(i) for i in index_range]:
            sig_priv = podle_PrivateKey(self.s)
            sG = sig_priv.pub
            sJ = multiply(self.s, J)
            e_int = int.from_bytes(self.e, byteorder="big")
            minus_e = (-e_int % N).to_bytes(32, byteorder="big")
            minus_e_P = multiply(minus_e, self.P)
            minus_e_P2 = multiply(minus_e, self.P2)
            KGser = add_pubkeys([sG, minus_e_P])
            KJser = add_pubkeys([sJ, minus_e_P2])
            #check 2: e =?= H(K_G || K_J || P || P2)
            e_check = hashlib.sha256(KGser + KJser + self.P + self.P2).digest()
            if e_check == self.e:
                return True
        #commitment fails for any NUMS in the provided range
        return False
Exemplo n.º 2
0
 def verify(self, commitment, index_range):
     """For an object created without a private key,
     check that the opened commitment verifies for at least
     one NUMS point as defined by the range in index_range
     """
     if not all([self.P, self.P2, self.s, self.e]):
         raise PoDLEError("Verify called without sufficient data")
     if not self.get_commitment() == commitment:
         return False
     for J in [getNUMS(i) for i in index_range]:
         sig_priv = podle_PrivateKey(self.s)
         sG = sig_priv.public_key
         sJ = multiply(self.s, J.format(), False)
         e_int = decode(self.e, 256)
         minus_e = encode(-e_int % N, 256, minlen=32)
         minus_e_P = multiply(minus_e, self.P.format(), False)
         minus_e_P2 = multiply(minus_e, self.P2.format(), False)
         KGser = add_pubkeys([sG.format(), minus_e_P], False)
         KJser = add_pubkeys([sJ, minus_e_P2], False)
         #check 2: e =?= H(K_G || K_J || P || P2)
         e_check = hashlib.sha256(KGser + KJser + self.P.format() +
                                  self.P2.format()).digest()
         if e_check == self.e:
             return True
     #commitment fails for any NUMS in the provided range
     return False
Exemplo n.º 3
0
def ecmult(scalar, point, usehex, rawpub=True, return_serialized=True):
    if isinstance(scalar, (int, long)):
        scalar = encode(scalar, 256, minlen=32)
    if decode(scalar, 256) == 0:
        return None
    return multiply(scalar, point, usehex, rawpub=rawpub,
                    return_serialized=return_serialized)
def ecies_encrypt(message, pubkey):
    """ Take a privkey in raw byte serialization,
    and a pubkey serialized in compressed, binary format (33 bytes),
    and output the shared secret as a 32 byte hash digest output.
    The exact calculation is:
    shared_secret = SHA256(privkey * pubkey)
    .. where * is elliptic curve scalar multiplication.
    See https://github.com/bitcoin/bitcoin/blob/master/src/secp256k1/src/modules/ecdh/main_impl.h
    for implementation details.
    """
    # create an ephemeral pubkey for this encryption:
    while True:
        r = os.urandom(32)
        # use compressed serialization of the pubkey R:
        try:
            R = btc.privkey_to_pubkey(r + b"\x01")
            break
        except:
            # accounts for improbable overflow:
            continue
    # note that this is *not* ECDH as in the secp256k1_ecdh module,
    # since it uses sha512:
    ecdh_key = btc.multiply(r, pubkey)
    key = hashlib.sha512(ecdh_key).digest()
    iv, key_e, key_m = key[0:16], key[16:32], key[32:]
    ciphertext = aes_encrypt(key_e, message, iv=iv)
    encrypted = ECIES_MAGIC_BYTES + R + ciphertext
    mac = hmac.new(key_m, encrypted, hashlib.sha256).digest()
    return base64.b64encode(encrypted + mac)
Exemplo n.º 5
0
def getP2(priv, nums_pt):
    """Given a secp256k1.PrivateKey priv and a
    secp256k1.PublicKey nums_pt, an alternate
    generator point (note: it's in no sense a
    pubkey, its privkey is unknowable - that's
    just the most easy way to manipulate it in the
    library), calculate priv*nums_pt
    """
    priv_raw = priv.secret_bytes
    return multiply(priv_raw, nums_pt, return_serialized=False)
Exemplo n.º 6
0
def donation_address(reusable_donation_pubkey=None): #pragma: no cover
    #Donation code currently disabled, so not tested.
    if not reusable_donation_pubkey:
        reusable_donation_pubkey = ('02be838257fbfddabaea03afbb9f16e852'
                                    '9dfe2de921260a5c46036d97b5eacf2a')
    sign_k = binascii.hexlify(os.urandom(32)).decode('ascii')
    c = btc.sha256(btc.multiply(sign_k, reusable_donation_pubkey, True))
    sender_pubkey = btc.add_pubkeys(
        [reusable_donation_pubkey, btc.privtopub(c + '01', True)], True)
    sender_address = btc.pubtoaddr(sender_pubkey, get_p2pk_vbyte())
    log.debug('sending coins to ' + sender_address)
    return sender_address, sign_k
def test_ecdh():
    """Using private key test vectors from Bitcoin Core.
    1. Import a set of private keys from the json file.
    2. Calculate the corresponding public keys.
    3. Do ECDH on the cartesian product (x, Y), with x private
    and Y public keys, for all combinations.
    4. Compare the result from CoinCurve with the manual
    multiplication xY following by hash (sha256). Note that
    sha256(xY) is the default hashing function used for ECDH
    in libsecp256k1.

    Since there are about 20 private keys in the json file, this
    creates around 400 test cases (note xX is still valid).
    """
    with open(os.path.join(testdir, "base58_keys_valid.json"), "r") as f:
        json_data = f.read()
        valid_keys_list = json.loads(json_data)
        extracted_privkeys = []
        for a in valid_keys_list:
            key, hex_key, prop_dict = a
            if prop_dict["isPrivkey"]:
                c, k = btc.read_privkey(hextobin(hex_key))
                extracted_privkeys.append(k)
    extracted_pubkeys = [btc.privkey_to_pubkey(x) for x in extracted_privkeys]
    for p in extracted_privkeys:
        for P in extracted_pubkeys:
            c, k = btc.read_privkey(p)
            shared_secret = btc.ecdh(k, P)
            assert len(shared_secret) == 32
            # try recreating the shared secret manually:
            pre_secret = btc.multiply(p, P)
            derived_secret = hashlib.sha256(pre_secret).digest()
            assert derived_secret == shared_secret

    # test some important failure cases; null key, overflow case
    privkeys_invalid = [
        b'\x00' * 32,
        hextobin(
            'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141')
    ]
    for p in privkeys_invalid:
        with pytest.raises(Exception) as e_info:
            shared_secret = btc.ecdh(p, extracted_pubkeys[0])
    pubkeys_invalid = [b'0xff' + extracted_pubkeys[0][1:], b'0x00' * 12]
    for p in extracted_privkeys:
        with pytest.raises(Exception) as e_info:
            shared_secret = btc.ecdh(p, pubkeys_invalid[0])
        with pytest.raises(Exception) as e_info:
            shared_secret = btc.ecdh(p, pubkeys_invalid[1])
Exemplo n.º 8
0
def encrypt_message(message, pubkey_hex):
    alice_r = binascii.hexlify(os.urandom(32))
    alice_R = btc.privkey_to_pubkey(alice_r +
                                    "01")  #use compression flag for pubkey
    ecdh_key = btc.multiply(alice_r,
                            pubkey_hex,
                            True,
                            rawpub=True,
                            return_serialized=True)
    key = hashlib.sha512(binascii.unhexlify(ecdh_key)).digest()
    iv, key_e, key_m = key[0:16], key[16:32], key[32:]
    ciphertext = encryptData(key_e, message, iv=iv)
    encrypted = b'BIE1' + binascii.unhexlify(alice_R) + ciphertext
    mac = hmac.new(key_m, encrypted, hashlib.sha256).digest()

    return base64.b64encode(encrypted + mac)
Exemplo n.º 9
0
    def get_proof_recursive(self, a, b, P, g, h, n):
        """The prover starts with the full a*, b*, then recursively
        constructs the case n=1 where the proof is output in the form a', b',
        these are scalars, and c' = a' * b'. This will be checked by the verifier
        against the modified P', which the verifier can calculate independently,
        and it should satisfy P' = a'*G_1 + b'*H_1 + c'*U.
        So the prover must provide (L[], R[], a', b') as output to the verifier.
        The verifier checks against the pre-known P and c.
        """
        if n == 1:
            #return the tuple: a', b', L[], R[]
            #note total size is 2 * scalar_size + log(n) * 2 * point_size
            return (a[0], b[0], self.L, self.R)
        #Split the existing vectors into halves
        aL, aR = halves(a)
        bL, bR = halves(b)
        gL, gR = halves(g)
        hL, hR = halves(h)
        self.L.append(IPC(aL, bR, g=gR, h=hL, u=self.U).get_commitment())
        self.R.append(IPC(aR, bL, g=gL, h=hR, u=self.U).get_commitment())
        x, xb, x_sq, x_sqb, xinv, xinvb, x_sq_inv, x_sq_invb = self.fiat_shamir(
            self.L[-1], self.R[-1], P)
        #Construct change of coordinates for base points, and for vector terms
        gprime = []
        hprime = []
        aprime = []
        bprime = []
        for i in range(n / 2):
            gprime.append(
                add_pubkeys([
                    multiply(xinvb, g[i], False),
                    multiply(xb, g[i + n / 2], False)
                ], False))
            hprime.append(
                add_pubkeys([
                    multiply(xb, h[i], False),
                    multiply(xinvb, h[i + n / 2], False)
                ], False))
            aprime.append(
                encode(
                    (x * decode(a[i], 256) + xinv * decode(a[i + n / 2], 256))
                    % N, 256, 32))
            bprime.append(
                encode(
                    (xinv * decode(b[i], 256) + x * decode(b[i + n / 2], 256))
                    % N, 256, 32))

        Pprime = add_pubkeys([
            P,
            multiply(x_sqb, self.L[-1], False),
            multiply(x_sq_invb, self.R[-1], False)
        ], False)
        return self.get_proof_recursive(aprime, bprime, Pprime, gprime, hprime,
                                        n / 2)
Exemplo n.º 10
0
    def generate_podle(self, index=0, k=None):
        """Given a raw private key, in hex format,
        construct a commitment sha256(P2), which is
        the hash of the value x*J, where x is the private
        key as a raw scalar, and J is a NUMS alternative
        basepoint on the Elliptic Curve; we use J(i) where i
        is an index, so as to be able to create multiple
        commitments against the same privkey. The procedure
        for generating the J(i) value is shown in getNUMS().
        Also construct a signature (s,e) of Schnorr type,
        which will serve as a zero knowledge proof that the
        private key of P2 is the same as the private key of P (=x*G).
        Signature is constructed as:
        s = k + x*e
        where k is a standard 32 byte nonce and:
        e = sha256(k*G || k*J || P || P2)

        Possibly Joinmarket specific comment:
        Users *should* generate with lower indices first,
        since verifiers will give preference to lower indices
        (each verifier may have their own policy about how high
        an index to allow, which really means how many reuses of utxos
        to allow in Joinmarket).

        Returns a commitment of form H(P2) which, note, will depend
        on the index choice. Repeated calls will reset the commitment
        and the associated signature data that can be used to open
        the commitment.
        """
        #TODO nonce could be rfc6979?
        if not k:
            k = os.urandom(32)
        J = getNUMS(index)
        KG = podle_PrivateKey(k).public_key
        KJ = multiply(k, J.format(), False, return_serialized=False)
        self.P2 = getP2(self.priv, J)
        self.get_commitment()
        self.e = hashlib.sha256(b''.join(
            [x.format() for x in [KG, KJ, self.P, self.P2]])).digest()
        k_int = decode(k, 256)
        priv_int = decode(self.priv.secret, 256)
        e_int = decode(self.e, 256)
        sig_int = (k_int + priv_int * e_int) % N
        self.s = encode(sig_int, 256, minlen=32)
        return self.reveal()
def ecies_decrypt(privkey, encrypted):
    if len(privkey) == 33 and privkey[-1] == 1:
        privkey = privkey[:32]
    encrypted = base64.b64decode(encrypted)
    if len(encrypted) < 85:
        raise Exception('invalid ciphertext: length')
    magic = encrypted[:4]
    if magic != ECIES_MAGIC_BYTES:
        raise ECIESDecryptionError()
    ephemeral_pubkey = encrypted[4:37]
    testR = CPubKey(ephemeral_pubkey)
    if not testR.is_fullyvalid():
        raise ECIESDecryptionError()
    ciphertext = encrypted[37:-32]
    mac = encrypted[-32:]
    ecdh_key = btc.multiply(privkey, ephemeral_pubkey)
    key = hashlib.sha512(ecdh_key).digest()
    iv, key_e, key_m = key[0:16], key[16:32], key[32:]
    if mac != hmac.new(key_m, encrypted[:-32], hashlib.sha256).digest():
        raise ECIESDecryptionError()
    return aes_decrypt(key_e, ciphertext, iv=iv)
Exemplo n.º 12
0
    def verify_proof_recursive(self, P, L, R, a, b, g, h, n):
        """The verifier starts with the lists of L and R values, then recursively
        constructs the case n=1 where the the verifier calculates the modified P',
        and checks it satisfies P' = a*G_1 + b*H_1 + c*U, where c = a*b
        So the prover must provide (L[], R[], a, b) as output to the verifier.
        Note here 'a' and 'b' are scalars, the final step of recursive reduction
        from the prover's original a* and b* vectors; they are passed through the
        recursion but here, unlike for the proof function, only used in the final
        step.
        """
        if n == 1:
            Pprime = IPC([a], [b], g=g, h=h, u=self.U).get_commitment()
            #print("Finished recursive verify; now comparing original P: ",
            #      binascii.hexlify(P))
            #print("..with calculated P': ", binascii.hexlify(Pprime))
            return P == Pprime
        x, xb, x_sq, x_sqb, xinv, xinvb, x_sq_inv, x_sq_invb = self.fiat_shamir(
            L[self.verif_iter], R[self.verif_iter], P)
        #Construct change of coordinates for base points, and for vector terms
        gprime = []
        hprime = []
        for i in range(n / 2):
            gprime.append(
                add_pubkeys([
                    multiply(xinvb, g[i], False),
                    multiply(xb, g[i + n / 2], False)
                ], False))
            hprime.append(
                add_pubkeys([
                    multiply(xb, h[i], False),
                    multiply(xinvb, h[i + n / 2], False)
                ], False))

        Pprime = add_pubkeys([
            P,
            multiply(x_sqb, L[self.verif_iter], False),
            multiply(x_sq_invb, R[self.verif_iter], False)
        ], False)
        self.verif_iter += 1
        return self.verify_proof_recursive(Pprime, L, R, a, b, gprime, hprime,
                                           n / 2)