You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
6.8 KiB
307 lines
6.8 KiB
# Author: Dusan Klinec, ph4r05, 2018
|
|
#
|
|
# Resources:
|
|
# https://cr.yp.to
|
|
# https://github.com/monero-project/mininero
|
|
# https://godoc.org/github.com/agl/ed25519/edwards25519
|
|
# https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-00#section-4
|
|
# https://github.com/monero-project/research-lab
|
|
|
|
from trezor.crypto import hmac, monero as tcry, random
|
|
from trezor.crypto.hashlib import sha3_256
|
|
|
|
NULL_KEY_ENC = b"\x00" * 32
|
|
|
|
random_bytes = random.bytes
|
|
ct_equals = tcry.ct_equals
|
|
|
|
|
|
def keccak_factory(data=None):
|
|
return sha3_256(data=data, keccak=True)
|
|
|
|
|
|
get_keccak = keccak_factory
|
|
keccak_hash = tcry.xmr_fast_hash
|
|
keccak_hash_into = tcry.xmr_fast_hash
|
|
|
|
|
|
def keccak_2hash(inp, buff=None):
|
|
buff = buff if buff else bytearray(32)
|
|
keccak_hash_into(buff, inp)
|
|
keccak_hash_into(buff, buff)
|
|
return buff
|
|
|
|
|
|
def compute_hmac(key, msg=None):
|
|
h = hmac.new(key, msg=msg, digestmod=keccak_factory)
|
|
return h.digest()
|
|
|
|
|
|
#
|
|
# EC
|
|
#
|
|
|
|
|
|
new_point = tcry.ge25519_set_neutral
|
|
|
|
|
|
def new_scalar():
|
|
return tcry.init256_modm(0)
|
|
|
|
|
|
decodepoint = tcry.ge25519_unpack_vartime
|
|
decodepoint_into = tcry.ge25519_unpack_vartime
|
|
encodepoint = tcry.ge25519_pack
|
|
encodepoint_into = tcry.ge25519_pack
|
|
|
|
decodeint = tcry.unpack256_modm
|
|
decodeint_into_noreduce = tcry.unpack256_modm_noreduce
|
|
decodeint_into = tcry.unpack256_modm
|
|
encodeint = tcry.pack256_modm
|
|
encodeint_into = tcry.pack256_modm
|
|
|
|
check_ed25519point = tcry.ge25519_check
|
|
|
|
scalarmult_base = tcry.ge25519_scalarmult_base
|
|
scalarmult_base_into = tcry.ge25519_scalarmult_base
|
|
scalarmult = tcry.ge25519_scalarmult
|
|
scalarmult_into = tcry.ge25519_scalarmult
|
|
|
|
point_add = tcry.ge25519_add
|
|
point_add_into = tcry.ge25519_add
|
|
point_sub = tcry.ge25519_sub
|
|
point_sub_into = tcry.ge25519_sub
|
|
point_eq = tcry.ge25519_eq
|
|
point_double = tcry.ge25519_double
|
|
point_double_into = tcry.ge25519_double
|
|
point_mul8 = tcry.ge25519_mul8
|
|
point_mul8_into = tcry.ge25519_mul8
|
|
|
|
INV_EIGHT = b"\x79\x2f\xdc\xe2\x29\xe5\x06\x61\xd0\xda\x1c\x7d\xb3\x9d\xd3\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06"
|
|
INV_EIGHT_SC = decodeint(INV_EIGHT)
|
|
|
|
|
|
def sc_inv_eight():
|
|
return INV_EIGHT_SC
|
|
|
|
|
|
#
|
|
# Zmod(order), scalar values field
|
|
#
|
|
|
|
|
|
def sc_0():
|
|
return tcry.init256_modm(0)
|
|
|
|
|
|
def sc_0_into(r):
|
|
return tcry.init256_modm(r, 0)
|
|
|
|
|
|
def sc_init(x):
|
|
if x >= (1 << 64):
|
|
raise ValueError("Initialization works up to 64-bit only")
|
|
return tcry.init256_modm(x)
|
|
|
|
|
|
def sc_init_into(r, x):
|
|
if x >= (1 << 64):
|
|
raise ValueError("Initialization works up to 64-bit only")
|
|
return tcry.init256_modm(r, x)
|
|
|
|
|
|
sc_copy = tcry.init256_modm
|
|
sc_get64 = tcry.get256_modm
|
|
sc_check = tcry.check256_modm
|
|
check_sc = tcry.check256_modm
|
|
|
|
sc_add = tcry.add256_modm
|
|
sc_add_into = tcry.add256_modm
|
|
sc_sub = tcry.sub256_modm
|
|
sc_sub_into = tcry.sub256_modm
|
|
sc_mul = tcry.mul256_modm
|
|
sc_mul_into = tcry.mul256_modm
|
|
|
|
|
|
def sc_isnonzero(c):
|
|
"""
|
|
Returns true if scalar is non-zero
|
|
"""
|
|
return not tcry.iszero256_modm(c)
|
|
|
|
|
|
sc_eq = tcry.eq256_modm
|
|
sc_mulsub = tcry.mulsub256_modm
|
|
sc_mulsub_into = tcry.mulsub256_modm
|
|
sc_muladd = tcry.muladd256_modm
|
|
sc_muladd_into = tcry.muladd256_modm
|
|
sc_inv_into = tcry.inv256_modm
|
|
|
|
|
|
def random_scalar(r=None):
|
|
return tcry.xmr_random_scalar(r if r is not None else new_scalar())
|
|
|
|
|
|
#
|
|
# GE - ed25519 group
|
|
#
|
|
|
|
|
|
def ge25519_double_scalarmult_base_vartime(a, A, b):
|
|
"""
|
|
void ge25519_double_scalarmult_vartime(ge25519 *r, const ge25519 *p1, const bignum256modm s1, const bignum256modm s2);
|
|
r = a * A + b * B
|
|
"""
|
|
R = tcry.ge25519_double_scalarmult_vartime(A, a, b)
|
|
return R
|
|
|
|
|
|
ge25519_double_scalarmult_vartime2 = tcry.xmr_add_keys3
|
|
|
|
|
|
def identity(byte_enc=False):
|
|
idd = tcry.ge25519_set_neutral()
|
|
return idd if not byte_enc else encodepoint(idd)
|
|
|
|
|
|
identity_into = tcry.ge25519_set_neutral
|
|
|
|
"""
|
|
https://www.imperialviolet.org/2013/12/25/elligator.html
|
|
http://elligator.cr.yp.to/
|
|
http://elligator.cr.yp.to/elligator-20130828.pdf
|
|
"""
|
|
|
|
#
|
|
# Monero specific
|
|
#
|
|
|
|
|
|
cn_fast_hash = keccak_hash
|
|
|
|
|
|
def hash_to_scalar(data, length=None):
|
|
"""
|
|
H_s(P)
|
|
"""
|
|
dt = data[:length] if length else data
|
|
return tcry.xmr_hash_to_scalar(dt)
|
|
|
|
|
|
def hash_to_scalar_into(r, data, length=None):
|
|
dt = data[:length] if length else data
|
|
return tcry.xmr_hash_to_scalar(r, dt)
|
|
|
|
|
|
"""
|
|
H_p(buf)
|
|
|
|
Code adapted from MiniNero: https://github.com/monero-project/mininero
|
|
https://github.com/monero-project/research-lab/blob/master/whitepaper/ge_fromfe_writeup/ge_fromfe.pdf
|
|
http://archive.is/yfINb
|
|
"""
|
|
hash_to_point = tcry.xmr_hash_to_ec
|
|
hash_to_point_into = tcry.xmr_hash_to_ec
|
|
|
|
|
|
#
|
|
# XMR
|
|
#
|
|
|
|
|
|
xmr_H = tcry.ge25519_set_h
|
|
|
|
|
|
def scalarmult_h(i):
|
|
return scalarmult(xmr_H(), sc_init(i) if isinstance(i, int) else i)
|
|
|
|
|
|
add_keys2 = tcry.xmr_add_keys2_vartime
|
|
add_keys2_into = tcry.xmr_add_keys2_vartime
|
|
add_keys3 = tcry.xmr_add_keys3_vartime
|
|
add_keys3_into = tcry.xmr_add_keys3_vartime
|
|
gen_commitment = tcry.xmr_gen_c
|
|
|
|
|
|
def generate_key_derivation(pub, sec):
|
|
"""
|
|
Key derivation: 8*(key2*key1)
|
|
"""
|
|
sc_check(sec) # checks that the secret key is uniform enough...
|
|
check_ed25519point(pub)
|
|
return tcry.xmr_generate_key_derivation(pub, sec)
|
|
|
|
|
|
def derivation_to_scalar(derivation, output_index):
|
|
"""
|
|
H_s(derivation || varint(output_index))
|
|
"""
|
|
check_ed25519point(derivation)
|
|
return tcry.xmr_derivation_to_scalar(derivation, output_index)
|
|
|
|
|
|
def derive_public_key(derivation, output_index, B):
|
|
"""
|
|
H_s(derivation || varint(output_index))G + B
|
|
"""
|
|
check_ed25519point(B)
|
|
return tcry.xmr_derive_public_key(derivation, output_index, B)
|
|
|
|
|
|
def derive_secret_key(derivation, output_index, base):
|
|
"""
|
|
base + H_s(derivation || varint(output_index))
|
|
"""
|
|
sc_check(base)
|
|
return tcry.xmr_derive_private_key(derivation, output_index, base)
|
|
|
|
|
|
def get_subaddress_secret_key(secret_key, major=0, minor=0):
|
|
"""
|
|
Builds subaddress secret key from the subaddress index
|
|
Hs(SubAddr || a || index_major || index_minor)
|
|
"""
|
|
return tcry.xmr_get_subaddress_secret_key(major, minor, secret_key)
|
|
|
|
|
|
#
|
|
# Repr invariant
|
|
#
|
|
|
|
|
|
def generate_signature(data, priv):
|
|
"""
|
|
Generate EC signature
|
|
crypto_ops::generate_signature(const hash &prefix_hash, const public_key &pub, const secret_key &sec, signature &sig)
|
|
"""
|
|
pub = scalarmult_base(priv)
|
|
|
|
k = random_scalar()
|
|
comm = scalarmult_base(k)
|
|
|
|
buff = data + encodepoint(pub) + encodepoint(comm)
|
|
c = hash_to_scalar(buff)
|
|
r = sc_mulsub(priv, c, k)
|
|
return c, r, pub
|
|
|
|
|
|
def check_signature(data, c, r, pub):
|
|
"""
|
|
EC signature verification
|
|
"""
|
|
check_ed25519point(pub)
|
|
if sc_check(c) != 0 or sc_check(r) != 0:
|
|
raise ValueError("Signature error")
|
|
|
|
tmp2 = point_add(scalarmult(pub, c), scalarmult_base(r))
|
|
buff = data + encodepoint(pub) + encodepoint(tmp2)
|
|
tmp_c = hash_to_scalar(buff)
|
|
res = sc_sub(tmp_c, c)
|
|
return not sc_isnonzero(res)
|
|
|
|
|
|
def xor8(buff, key):
|
|
for i in range(8):
|
|
buff[i] ^= key[i]
|
|
return buff
|