aboutsummaryrefslogtreecommitdiff
path: root/src/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'src/crypto')
-rw-r--r--src/crypto/crypto.cpp125
-rw-r--r--src/crypto/crypto.h27
2 files changed, 111 insertions, 41 deletions
diff --git a/src/crypto/crypto.cpp b/src/crypto/crypto.cpp
index 5fb670f87..95ba34828 100644
--- a/src/crypto/crypto.cpp
+++ b/src/crypto/crypto.cpp
@@ -87,7 +87,7 @@ namespace crypto {
random_scalar_not_thread_safe(res);
}
- static inline void hash_to_scalar(const void *data, size_t length, ec_scalar &res) {
+ void hash_to_scalar(const void *data, size_t length, ec_scalar &res) {
cn_fast_hash(data, length, reinterpret_cast<hash &>(res));
sc_reduce32(&res);
}
@@ -189,6 +189,25 @@ namespace crypto {
sc_add(&derived_key, &base, &scalar);
}
+ bool crypto_ops::derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &derived_key) {
+ ec_scalar scalar;
+ ge_p3 point1;
+ ge_p3 point2;
+ ge_cached point3;
+ ge_p1p1 point4;
+ ge_p2 point5;
+ if (ge_frombytes_vartime(&point1, &out_key) != 0) {
+ return false;
+ }
+ derivation_to_scalar(derivation, output_index, scalar);
+ ge_scalarmult_base(&point2, &scalar);
+ ge_p3_to_cached(&point3, &point2);
+ ge_sub(&point4, &point1, &point3);
+ ge_p1p1_to_p2(&point5, &point4);
+ ge_tobytes(&derived_key, &point5);
+ return true;
+ }
+
struct s_comm {
hash h;
ec_point key;
@@ -246,22 +265,33 @@ namespace crypto {
return sc_isnonzero(&c) == 0;
}
- void crypto_ops::generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const secret_key &r, signature &sig) {
+ void crypto_ops::generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) {
// sanity check
ge_p3 R_p3;
ge_p3 A_p3;
+ ge_p3 B_p3;
ge_p3 D_p3;
if (ge_frombytes_vartime(&R_p3, &R) != 0) throw std::runtime_error("tx pubkey is invalid");
if (ge_frombytes_vartime(&A_p3, &A) != 0) throw std::runtime_error("recipient view pubkey is invalid");
+ if (B && ge_frombytes_vartime(&B_p3, &*B) != 0) throw std::runtime_error("recipient spend pubkey is invalid");
if (ge_frombytes_vartime(&D_p3, &D) != 0) throw std::runtime_error("key derivation is invalid");
#if !defined(NDEBUG)
{
assert(sc_check(&r) == 0);
- // check R == r*G
- ge_p3 dbg_R_p3;
- ge_scalarmult_base(&dbg_R_p3, &r);
+ // check R == r*G or R == r*B
public_key dbg_R;
- ge_p3_tobytes(&dbg_R, &dbg_R_p3);
+ if (B)
+ {
+ ge_p2 dbg_R_p2;
+ ge_scalarmult(&dbg_R_p2, &r, &B_p3);
+ ge_tobytes(&dbg_R, &dbg_R_p2);
+ }
+ else
+ {
+ ge_p3 dbg_R_p3;
+ ge_scalarmult_base(&dbg_R_p3, &r);
+ ge_p3_tobytes(&dbg_R, &dbg_R_p3);
+ }
assert(R == dbg_R);
// check D == r*A
ge_p2 dbg_D_p2;
@@ -276,43 +306,84 @@ namespace crypto {
ec_scalar k;
random_scalar(k);
- // compute X = k*G
- ge_p3 X_p3;
- ge_scalarmult_base(&X_p3, &k);
+ s_comm_2 buf;
+ buf.msg = prefix_hash;
+ buf.D = D;
+
+ if (B)
+ {
+ // compute X = k*B
+ ge_p2 X_p2;
+ ge_scalarmult(&X_p2, &k, &B_p3);
+ ge_tobytes(&buf.X, &X_p2);
+ }
+ else
+ {
+ // compute X = k*G
+ ge_p3 X_p3;
+ ge_scalarmult_base(&X_p3, &k);
+ ge_p3_tobytes(&buf.X, &X_p3);
+ }
// compute Y = k*A
ge_p2 Y_p2;
ge_scalarmult(&Y_p2, &k, &A_p3);
+ ge_tobytes(&buf.Y, &Y_p2);
// sig.c = Hs(Msg || D || X || Y)
- s_comm_2 buf;
- buf.msg = prefix_hash;
- buf.D = D;
- ge_p3_tobytes(&buf.X, &X_p3);
- ge_tobytes(&buf.Y, &Y_p2);
- hash_to_scalar(&buf, sizeof(s_comm_2), sig.c);
+ hash_to_scalar(&buf, sizeof(buf), sig.c);
// sig.r = k - sig.c*r
sc_mulsub(&sig.r, &sig.c, &r, &k);
}
- bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const signature &sig) {
+ bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
// sanity check
ge_p3 R_p3;
ge_p3 A_p3;
+ ge_p3 B_p3;
ge_p3 D_p3;
if (ge_frombytes_vartime(&R_p3, &R) != 0) return false;
if (ge_frombytes_vartime(&A_p3, &A) != 0) return false;
+ if (B && ge_frombytes_vartime(&B_p3, &*B) != 0) return false;
if (ge_frombytes_vartime(&D_p3, &D) != 0) return false;
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0) return false;
// compute sig.c*R
- ge_p2 cR_p2;
- ge_scalarmult(&cR_p2, &sig.c, &R_p3);
+ ge_p3 cR_p3;
+ {
+ ge_p2 cR_p2;
+ ge_scalarmult(&cR_p2, &sig.c, &R_p3);
+ public_key cR;
+ ge_tobytes(&cR, &cR_p2);
+ if (ge_frombytes_vartime(&cR_p3, &cR) != 0) return false;
+ }
- // compute sig.r*G
- ge_p3 rG_p3;
- ge_scalarmult_base(&rG_p3, &sig.r);
+ ge_p1p1 X_p1p1;
+ if (B)
+ {
+ // compute X = sig.c*R + sig.r*B
+ ge_p2 rB_p2;
+ ge_scalarmult(&rB_p2, &sig.r, &B_p3);
+ public_key rB;
+ ge_tobytes(&rB, &rB_p2);
+ ge_p3 rB_p3;
+ if (ge_frombytes_vartime(&rB_p3, &rB) != 0) return false;
+ ge_cached rB_cached;
+ ge_p3_to_cached(&rB_cached, &rB_p3);
+ ge_add(&X_p1p1, &cR_p3, &rB_cached);
+ }
+ else
+ {
+ // compute X = sig.c*R + sig.r*G
+ ge_p3 rG_p3;
+ ge_scalarmult_base(&rG_p3, &sig.r);
+ ge_cached rG_cached;
+ ge_p3_to_cached(&rG_cached, &rG_p3);
+ ge_add(&X_p1p1, &cR_p3, &rG_cached);
+ }
+ ge_p2 X_p2;
+ ge_p1p1_to_p2(&X_p2, &X_p1p1);
// compute sig.c*D
ge_p2 cD_p2;
@@ -322,18 +393,6 @@ namespace crypto {
ge_p2 rA_p2;
ge_scalarmult(&rA_p2, &sig.r, &A_p3);
- // compute X = sig.c*R + sig.r*G
- public_key cR;
- ge_tobytes(&cR, &cR_p2);
- ge_p3 cR_p3;
- if (ge_frombytes_vartime(&cR_p3, &cR) != 0) return false;
- ge_cached rG_cached;
- ge_p3_to_cached(&rG_cached, &rG_p3);
- ge_p1p1 X_p1p1;
- ge_add(&X_p1p1, &cR_p3, &rG_cached);
- ge_p2 X_p2;
- ge_p1p1_to_p2(&X_p2, &X_p1p1);
-
// compute Y = sig.c*D + sig.r*A
public_key cD;
public_key rA;
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 94f924296..abdea0165 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -35,6 +35,7 @@
#include <boost/thread/mutex.hpp>
#include <boost/thread/lock_guard.hpp>
#include <boost/utility/value_init.hpp>
+#include <boost/optional.hpp>
#include <vector>
#include "common/pod-class.h"
@@ -98,6 +99,8 @@ namespace crypto {
};
#pragma pack(pop)
+ void hash_to_scalar(const void *data, size_t length, ec_scalar &res);
+
static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 &&
sizeof(public_key) == 32 && sizeof(secret_key) == 32 &&
sizeof(key_derivation) == 32 && sizeof(key_image) == 32 &&
@@ -123,14 +126,16 @@ namespace crypto {
friend bool derive_public_key(const key_derivation &, std::size_t, const public_key &, public_key &);
static void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &);
friend void derive_secret_key(const key_derivation &, std::size_t, const secret_key &, secret_key &);
+ static bool derive_subaddress_public_key(const public_key &, const key_derivation &, std::size_t, public_key &);
+ friend bool derive_subaddress_public_key(const public_key &, const key_derivation &, std::size_t, public_key &);
static void generate_signature(const hash &, const public_key &, const secret_key &, signature &);
friend void generate_signature(const hash &, const public_key &, const secret_key &, signature &);
static bool check_signature(const hash &, const public_key &, const signature &);
friend bool check_signature(const hash &, const public_key &, const signature &);
- static void generate_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const secret_key &, signature &);
- friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const secret_key &, signature &);
- static bool check_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const signature &);
- friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const public_key &, const signature &);
+ static void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
+ friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
+ static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
+ friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
static void generate_key_image(const public_key &, const secret_key &, key_image &);
friend void generate_key_image(const public_key &, const secret_key &, key_image &);
static void generate_ring_signature(const hash &, const key_image &,
@@ -198,6 +203,9 @@ namespace crypto {
const secret_key &base, secret_key &derived_key) {
crypto_ops::derive_secret_key(derivation, output_index, base, derived_key);
}
+ inline bool derive_subaddress_public_key(const public_key &out_key, const key_derivation &derivation, std::size_t output_index, public_key &result) {
+ return crypto_ops::derive_subaddress_public_key(out_key, derivation, output_index, result);
+ }
/* Generation and checking of a standard signature.
*/
@@ -210,12 +218,13 @@ namespace crypto {
/* Generation and checking of a tx proof; given a tx pubkey R, the recipient's view pubkey A, and the key
* derivation D, the signature proves the knowledge of the tx secret key r such that R=r*G and D=r*A
+ * When the recipient's address is a subaddress, the tx pubkey R is defined as R=r*B where B is the recipient's spend pubkey
*/
- inline void generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const secret_key &r, signature &sig) {
- crypto_ops::generate_tx_proof(prefix_hash, R, A, D, r, sig);
+ inline void generate_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) {
+ crypto_ops::generate_tx_proof(prefix_hash, R, A, B, D, r, sig);
}
- inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const public_key &D, const signature &sig) {
- return crypto_ops::check_tx_proof(prefix_hash, R, A, D, sig);
+ inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
+ return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig);
}
/* To send money to a key:
@@ -270,8 +279,10 @@ namespace crypto {
}
const static crypto::public_key null_pkey = boost::value_initialized<crypto::public_key>();
+ const static crypto::secret_key null_skey = boost::value_initialized<crypto::secret_key>();
}
CRYPTO_MAKE_HASHABLE(public_key)
+CRYPTO_MAKE_HASHABLE(secret_key)
CRYPTO_MAKE_HASHABLE(key_image)
CRYPTO_MAKE_COMPARABLE(signature)