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.
trezor-firmware/core/src/apps/monero/xmr/crypto/__init__.py

324 lines
7.9 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 monero as tcry, random
from trezor.crypto.hashlib import sha3_256
if False:
from apps.monero.xmr.types import Sc25519, Ge25519
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):
digestmod = keccak_factory
inner = digestmod()
block_size = inner.block_size
if len(key) > block_size:
key = digestmod(key).digest()
key_block = bytearray(block_size)
for i in range(block_size):
key_block[i] = 0x36
for i in range(len(key)):
key_block[i] ^= key[i]
inner.update(key_block)
inner.update(msg)
outer = digestmod()
for i in range(block_size):
key_block[i] = 0x5C
for i in range(len(key)):
key_block[i] ^= key[i]
outer.update(key_block)
outer.update(inner.digest())
return outer.digest()
#
# EC
#
new_point = tcry.ge25519_set_neutral
def new_scalar() -> Sc25519:
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() -> Sc25519:
return INV_EIGHT_SC
#
# Zmod(order), scalar values field
#
def sc_0() -> Sc25519:
return tcry.init256_modm(0)
def sc_0_into(r: Sc25519) -> Sc25519:
return tcry.init256_modm(r, 0)
def sc_init(x: int) -> Sc25519:
if x >= (1 << 64):
raise ValueError("Initialization works up to 64-bit only")
return tcry.init256_modm(x)
def sc_init_into(r: Sc25519, x: int) -> Sc25519:
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: Sc25519) -> bool:
"""
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) -> Sc25519:
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) -> Ge25519:
"""
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) -> Ge25519 | bytes:
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: bytes, length: int | None = None):
"""
H_s(P)
"""
dt = data[:length] if length else data
return tcry.xmr_hash_to_scalar(dt)
def hash_to_scalar_into(r: Sc25519, data: bytes, length: int | None = 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) -> Ge25519:
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: Ge25519, sec: Sc25519) -> Ge25519:
"""
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: Ge25519, output_index: int) -> Sc25519:
"""
H_s(derivation || varint(output_index))
"""
check_ed25519point(derivation)
return tcry.xmr_derivation_to_scalar(derivation, output_index)
def derive_public_key(derivation: Ge25519, output_index: int, B: Ge25519) -> Ge25519:
"""
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: Ge25519, output_index: int, base: Sc25519) -> Sc25519:
"""
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: Sc25519, major: int = 0, minor: int = 0
) -> Sc25519:
"""
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)
def generate_signature(data: bytes, priv: Sc25519) -> tuple[Sc25519, Sc25519, Ge25519]:
"""
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: bytes, c: Sc25519, r: Sc25519, pub: Ge25519) -> bool:
"""
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: bytes, key: bytes) -> bytes:
for i in range(8):
buff[i] ^= key[i]
return buff