diff options
author | Pedram Fardzadeh <p.fardzadeh@protonmail.com> | 2023-11-05 22:40:31 +0100 |
---|---|---|
committer | Pedram Fardzadeh <p.fardzadeh@protonmail.com> | 2024-02-28 16:13:12 +0100 |
commit | 63c366f4428d2ab31d62650febd28caf774805a9 (patch) | |
tree | adff614ef0a7eeebc630c11f3a38b25f64995c42 /src | |
parent | 93b049ebd15a2658593fdf5d93672719fb51f4dd (diff) | |
download | gnunet-63c366f4428d2ab31d62650febd28caf774805a9.tar.gz gnunet-63c366f4428d2ab31d62650febd28caf774805a9.zip |
util: initial elligator implementation
Diffstat (limited to 'src')
-rw-r--r-- | src/include/gnunet_crypto_lib.h | 97 | ||||
-rw-r--r-- | src/lib/util/Makefile.am | 8 | ||||
-rw-r--r-- | src/lib/util/crypto_elligator.c | 702 | ||||
-rw-r--r-- | src/lib/util/meson.build | 11 | ||||
-rw-r--r-- | src/lib/util/test_crypto_elligator.c | 334 |
5 files changed, 1152 insertions, 0 deletions
diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h index 2c7e92fbd..5425a18dd 100644 --- a/src/include/gnunet_crypto_lib.h +++ b/src/include/gnunet_crypto_lib.h | |||
@@ -349,6 +349,18 @@ struct GNUNET_CRYPTO_Edx25519Signature | |||
349 | }; | 349 | }; |
350 | 350 | ||
351 | /** | 351 | /** |
352 | * Elligator representative (always for Curve25519) | ||
353 | */ | ||
354 | struct GNUNET_CRYPTO_ElligatorRepresentative | ||
355 | { | ||
356 | /** | ||
357 | * Represents an element of Curve25519 finite field. | ||
358 | * Always smaller than 2 ^ 254 - 10 -> Needs to be serialized into a random-looking byte stream before transmission. | ||
359 | */ | ||
360 | unsigned char r[256 / 8]; | ||
361 | }; | ||
362 | |||
363 | /** | ||
352 | * Key type for the generic public key union | 364 | * Key type for the generic public key union |
353 | */ | 365 | */ |
354 | enum GNUNET_CRYPTO_KeyType | 366 | enum GNUNET_CRYPTO_KeyType |
@@ -2652,6 +2664,91 @@ GNUNET_CRYPTO_edx25519_public_key_derive ( | |||
2652 | size_t seedsize, | 2664 | size_t seedsize, |
2653 | struct GNUNET_CRYPTO_Edx25519PublicKey *result); | 2665 | struct GNUNET_CRYPTO_Edx25519PublicKey *result); |
2654 | 2666 | ||
2667 | /** | ||
2668 | * Note: Included in header for testing purposes. GNUNET_CRYPTO_ecdhe_elligator_decoding will be the correct API for the direct map. | ||
2669 | * TODO: Make static. | ||
2670 | * @ingroup crypto | ||
2671 | * Encodes an element of the underlying finite field, so called representative, of Curve25519 to a point on the curve | ||
2672 | * This transformation is deterministic | ||
2673 | * | ||
2674 | * @param representative element of the finite field | ||
2675 | * @param point destination for the calculated point on the curve | ||
2676 | * @param high_y destination set to "True" if corresponding y-coordinate is > 2 ^ 254 - 10 | ||
2677 | */ | ||
2678 | bool | ||
2679 | GNUNET_CRYPTO_ecdhe_elligator_direct_map (uint8_t *point, bool *high_y, | ||
2680 | uint8_t *representative); | ||
2681 | |||
2682 | |||
2683 | /** | ||
2684 | * @ingroup crypto | ||
2685 | * Clears the most significant bit and second most significant bit to the serialized representaive before applying elligator direct map. | ||
2686 | * | ||
2687 | * @param serialized_representative serialized version of an element of Curves25519's finite field | ||
2688 | * @param point destination for the calculated point on the curve | ||
2689 | * @param high_y destination set to "True" if corresponding y-coordinate is > 2 ^ 254 - 10 | ||
2690 | */ | ||
2691 | bool | ||
2692 | GNUNET_CRYPTO_ecdhe_elligator_decoding (struct | ||
2693 | GNUNET_CRYPTO_EcdhePublicKey *point, | ||
2694 | bool *high_y, | ||
2695 | struct | ||
2696 | GNUNET_CRYPTO_ElligatorRepresentative * | ||
2697 | seriliazed_representative); | ||
2698 | |||
2699 | /** | ||
2700 | * @ingroup crypto | ||
2701 | * Encodes a point on Curve25519 to a an element of the underlying finite field | ||
2702 | * This transformation is deterministic | ||
2703 | * | ||
2704 | * @param point a point on the curve | ||
2705 | * @param high_y encodes if y-coordinate is > 2 ^254 - 10, which determines the representative value out of two | ||
2706 | * @param representative destination for the calculated element of the finite field | ||
2707 | */ | ||
2708 | bool | ||
2709 | GNUNET_CRYPTO_ecdhe_elligator_inverse_map (uint8_t *representative, const | ||
2710 | uint8_t *point, | ||
2711 | bool high_y); | ||
2712 | |||
2713 | |||
2714 | /** | ||
2715 | * Initializes the elligator library | ||
2716 | * THis function is thread safe | ||
2717 | */ | ||
2718 | void | ||
2719 | GNUNET_CRYPTO_ecdhe_elligator_initialize (void); | ||
2720 | |||
2721 | /** | ||
2722 | * @ingroup crypto | ||
2723 | * Generates a valid public key for elligator's inverse map by adding a lower order point to a prime order point. | ||
2724 | * | ||
2725 | * @param pub valid public key for elligator inverse map | ||
2726 | * @param pk private key for generating valid public key | ||
2727 | */ | ||
2728 | int | ||
2729 | GNUNET_CRYPTO_ecdhe_elligator_generate_public_key (unsigned char | ||
2730 | pub[ | ||
2731 | crypto_scalarmult_SCALARBYTES | ||
2732 | ], | ||
2733 | struct | ||
2734 | GNUNET_CRYPTO_EcdhePrivateKey | ||
2735 | *pk); | ||
2736 | |||
2737 | |||
2738 | /** | ||
2739 | * @ingroup crypto | ||
2740 | * Generates a private key for Curve25519 and the elligator representative of the corresponding public key | ||
2741 | * | ||
2742 | * @param repr representative of the public key | ||
2743 | * @param pk Curve25519 private key | ||
2744 | */ | ||
2745 | int | ||
2746 | GNUNET_CRYPTO_ecdhe_elligator_key_create (struct | ||
2747 | GNUNET_CRYPTO_ElligatorRepresentative | ||
2748 | *repr, | ||
2749 | struct GNUNET_CRYPTO_EcdhePrivateKey | ||
2750 | *pk); | ||
2751 | |||
2655 | 2752 | ||
2656 | /** | 2753 | /** |
2657 | * Output the given MPI value to the given buffer in network | 2754 | * Output the given MPI value to the given buffer in network |
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am index 6facc5921..f7716cd48 100644 --- a/src/lib/util/Makefile.am +++ b/src/lib/util/Makefile.am | |||
@@ -60,6 +60,7 @@ libgnunetutil_la_SOURCES = \ | |||
60 | $(DLOG) \ | 60 | $(DLOG) \ |
61 | crypto_ecc_setup.c \ | 61 | crypto_ecc_setup.c \ |
62 | crypto_edx25519.c \ | 62 | crypto_edx25519.c \ |
63 | crypto_elligator.c \ | ||
63 | crypto_hash.c \ | 64 | crypto_hash.c \ |
64 | crypto_hash_file.c \ | 65 | crypto_hash_file.c \ |
65 | crypto_hkdf.c \ | 66 | crypto_hkdf.c \ |
@@ -207,6 +208,7 @@ check_PROGRAMS = \ | |||
207 | test_crypto_ecdh_eddsa \ | 208 | test_crypto_ecdh_eddsa \ |
208 | test_crypto_ecdh_ecdsa \ | 209 | test_crypto_ecdh_ecdsa \ |
209 | test_crypto_edx25519 \ | 210 | test_crypto_edx25519 \ |
211 | test_crypto_elligator \ | ||
210 | $(DLOG_TEST) \ | 212 | $(DLOG_TEST) \ |
211 | test_crypto_hash \ | 213 | test_crypto_hash \ |
212 | test_crypto_hash_context \ | 214 | test_crypto_hash_context \ |
@@ -387,6 +389,12 @@ test_crypto_edx25519_LDADD = \ | |||
387 | libgnunetutil.la \ | 389 | libgnunetutil.la \ |
388 | $(LIBGCRYPT_LIBS) | 390 | $(LIBGCRYPT_LIBS) |
389 | 391 | ||
392 | test_crypto_elligator_SOURCES = \ | ||
393 | test_crypto_elligator.c | ||
394 | test_crypto_elligator_LDADD = \ | ||
395 | libgnunetutil.la \ | ||
396 | $(LIBGCRYPT_LIBS) | ||
397 | |||
390 | test_crypto_ecc_dlog_SOURCES = \ | 398 | test_crypto_ecc_dlog_SOURCES = \ |
391 | test_crypto_ecc_dlog.c | 399 | test_crypto_ecc_dlog.c |
392 | test_crypto_ecc_dlog_LDADD = \ | 400 | test_crypto_ecc_dlog_LDADD = \ |
diff --git a/src/lib/util/crypto_elligator.c b/src/lib/util/crypto_elligator.c new file mode 100644 index 000000000..142c0782a --- /dev/null +++ b/src/lib/util/crypto_elligator.c | |||
@@ -0,0 +1,702 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2002-2013 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | |||
20 | |||
21 | Portions of this code are derived from the Elligator-2 project, | ||
22 | which is licensed under the Creative Commons CC0 1.0 Universal Public Domain Dedication. | ||
23 | The Elligator-2 project can be found at: https://github.com/Kleshni/Elligator-2 | ||
24 | |||
25 | Note that gmp is already a dependency of GnuTLS | ||
26 | |||
27 | */ | ||
28 | |||
29 | #include "platform.h" | ||
30 | #include <gcrypt.h> | ||
31 | #include <sodium.h> | ||
32 | #include "gnunet_util_lib.h" | ||
33 | #include "benchmark.h" | ||
34 | |||
35 | #include <stdint.h> | ||
36 | #include <stdbool.h> | ||
37 | #include <string.h> | ||
38 | #include <gmp.h> | ||
39 | |||
40 | // Ed25519 subgroup of points with a low order | ||
41 | static const uint8_t lookupTable[8][crypto_scalarmult_SCALARBYTES] = { | ||
42 | { | ||
43 | 0x26, 0xE8, 0x95, 0x8F, 0xC2, 0xB2, 0x27, 0xB0, | ||
44 | 0x45, 0xC3, 0xF4, 0x89, 0xF2, 0xEF, 0x98, 0xF0, | ||
45 | 0xD5, 0xDF, 0xAC, 0x05, 0xD3, 0xC6, 0x33, 0x39, | ||
46 | 0xB1, 0x38, 0x02, 0x88, 0x6D, 0x53, 0xFC, 0x05 | ||
47 | }, | ||
48 | { | ||
49 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
50 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
51 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
52 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
53 | }, | ||
54 | { | ||
55 | 0xC7, 0x17, 0x6A, 0x70, 0x3D, 0x4D, 0xD8, 0x4F, | ||
56 | 0xBA, 0x3C, 0x0B, 0x76, 0x0D, 0x10, 0x67, 0x0F, | ||
57 | 0x2A, 0x20, 0x53, 0xFA, 0x2C, 0x39, 0xCC, 0xC6, | ||
58 | 0x4E, 0xC7, 0xFD, 0x77, 0x92, 0xAC, 0x03, 0x7A | ||
59 | }, | ||
60 | { | ||
61 | 0xEC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | ||
62 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | ||
63 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | ||
64 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F | ||
65 | }, { | ||
66 | 0xC7, 0x17, 0x6A, 0x70, 0x3D, 0x4D, 0xD8, 0x4F, | ||
67 | 0xBA, 0x3C, 0x0B, 0x76, 0x0D, 0x10, 0x67, 0x0F, | ||
68 | 0x2A, 0x20, 0x53, 0xFA, 0x2C, 0x39, 0xCC, 0xC6, | ||
69 | 0x4E, 0xC7, 0xFD, 0x77, 0x92, 0xAC, 0x03, 0xFA | ||
70 | }, { | ||
71 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
72 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
73 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
74 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 | ||
75 | }, { | ||
76 | 0x26, 0xE8, 0x95, 0x8F, 0xC2, 0xB2, 0x27, 0xB0, | ||
77 | 0x45, 0xC3, 0xF4, 0x89, 0xF2, 0xEF, 0x98, 0xF0, | ||
78 | 0xD5, 0xDF, 0xAC, 0x05, 0xD3, 0xC6, 0x33, 0x39, | ||
79 | 0xB1, 0x38, 0x02, 0x88, 0x6D, 0x53, 0xFC, 0x85 | ||
80 | },{ | ||
81 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
82 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
83 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
84 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
85 | } | ||
86 | }; | ||
87 | |||
88 | // main.h from Kleshnis's elligator implementation | ||
89 | #include <limits.h> | ||
90 | |||
91 | #define P_BITS (256) // 255 significant bits + 1 for carry | ||
92 | #define P_BYTES ((P_BITS + CHAR_BIT - 1) / CHAR_BIT) | ||
93 | #define P_LIMBS ((P_BITS + GMP_NUMB_BITS - 1) / GMP_NUMB_BITS) | ||
94 | |||
95 | |||
96 | // main.c from Kleshnis's elligator implementation | ||
97 | static const unsigned char p_bytes[P_BYTES] = { | ||
98 | 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
99 | 0xff, 0xff, 0xff, | ||
100 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
101 | 0xff, 0xff, 0x7f | ||
102 | }; | ||
103 | |||
104 | static const unsigned char negative_1_bytes[P_BYTES] = { | ||
105 | 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
106 | 0xff, 0xff, 0xff, | ||
107 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
108 | 0xff, 0xff, 0x7f | ||
109 | }; | ||
110 | |||
111 | static const unsigned char negative_2_bytes[P_BYTES] = { | ||
112 | 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
113 | 0xff, 0xff, 0xff, | ||
114 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
115 | 0xff, 0xff, 0x7f | ||
116 | }; | ||
117 | |||
118 | static const unsigned char divide_negative_1_2_bytes[P_BYTES] = { | ||
119 | 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
120 | 0xff, 0xff, 0xff, | ||
121 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
122 | 0xff, 0xff, 0x3f | ||
123 | }; | ||
124 | |||
125 | static const unsigned char divide_plus_p_3_8_bytes[P_BYTES] = { | ||
126 | 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
127 | 0xff, 0xff, 0xff, | ||
128 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
129 | 0xff, 0xff, 0x0f | ||
130 | }; | ||
131 | |||
132 | static const unsigned char divide_minus_p_1_2_bytes[P_BYTES] = { | ||
133 | 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
134 | 0xff, 0xff, 0xff, | ||
135 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
136 | 0xff, 0xff, 0x3f | ||
137 | }; | ||
138 | |||
139 | static const unsigned char square_root_negative_1_bytes[P_BYTES] = { | ||
140 | 0xb0, 0xa0, 0x0e, 0x4a, 0x27, 0x1b, 0xee, 0xc4, 0x78, 0xe4, 0x2f, 0xad, 0x06, | ||
141 | 0x18, 0x43, 0x2f, | ||
142 | 0xa7, 0xd7, 0xfb, 0x3d, 0x99, 0x00, 0x4d, 0x2b, 0x0b, 0xdf, 0xc1, 0x4f, 0x80, | ||
143 | 0x24, 0x83, 0x2b | ||
144 | }; | ||
145 | |||
146 | static const unsigned char A_bytes[P_BYTES] = { | ||
147 | 0x06, 0x6d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
148 | 0x00, 0x00, 0x00, | ||
149 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
150 | 0x00, 0x00, 0x00 | ||
151 | }; | ||
152 | |||
153 | static const unsigned char negative_A_bytes[P_BYTES] = { | ||
154 | 0xe7, 0x92, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
155 | 0xff, 0xff, 0xff, | ||
156 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
157 | 0xff, 0xff, 0x7f | ||
158 | }; | ||
159 | |||
160 | static const unsigned char u_bytes[P_BYTES] = { | ||
161 | 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
162 | 0x00, 0x00, 0x00, | ||
163 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
164 | 0x00, 0x00, 0x00 | ||
165 | }; | ||
166 | |||
167 | static const unsigned char inverted_u_bytes[P_BYTES] = { | ||
168 | 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
169 | 0xff, 0xff, 0xff, | ||
170 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
171 | 0xff, 0xff, 0x3f | ||
172 | }; | ||
173 | |||
174 | static const unsigned char d_bytes[P_BYTES] = { | ||
175 | 0xa3, 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75, 0xab, 0xd8, 0x41, 0x41, 0x4d, | ||
176 | 0x0a, 0x70, 0x00, | ||
177 | 0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c, 0x73, 0xfe, 0x6f, 0x2b, 0xee, | ||
178 | 0x6c, 0x03, 0x52 | ||
179 | }; | ||
180 | |||
181 | static mp_limb_t p[P_LIMBS]; | ||
182 | static mp_limb_t negative_1[P_LIMBS]; | ||
183 | static mp_limb_t negative_2[P_LIMBS]; | ||
184 | static mp_limb_t divide_negative_1_2[P_LIMBS]; | ||
185 | static mp_limb_t divide_plus_p_3_8[P_LIMBS]; | ||
186 | static mp_limb_t divide_minus_p_1_2[P_LIMBS]; | ||
187 | static mp_limb_t square_root_negative_1[P_LIMBS]; | ||
188 | static mp_limb_t A[P_LIMBS]; | ||
189 | static mp_limb_t negative_A[P_LIMBS]; | ||
190 | static mp_limb_t u[P_LIMBS]; | ||
191 | static mp_limb_t inverted_u[P_LIMBS]; | ||
192 | static mp_limb_t d[P_LIMBS]; | ||
193 | |||
194 | static mp_size_t scratch_space_length; | ||
195 | |||
196 | static void | ||
197 | decode_bytes (mp_limb_t *number, const uint8_t *bytes) | ||
198 | { | ||
199 | mp_limb_t scratch_space[1]; | ||
200 | |||
201 | for (size_t i = 0; i < P_BYTES; ++i) | ||
202 | { | ||
203 | mpn_lshift (number, number, P_LIMBS, 8); | ||
204 | mpn_sec_add_1 (number, number, 1, bytes[P_BYTES - i - 1], scratch_space); | ||
205 | } | ||
206 | } | ||
207 | |||
208 | |||
209 | // Erases the number | ||
210 | |||
211 | static void | ||
212 | encode_bytes (uint8_t *bytes, mp_limb_t *number) | ||
213 | { | ||
214 | for (size_t i = 0; i < P_BYTES; ++i) | ||
215 | { | ||
216 | bytes[P_BYTES - i - 1] = mpn_lshift (number, number, P_LIMBS, 8); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | |||
221 | void | ||
222 | GNUNET_CRYPTO_ecdhe_elligator_initialize (void) | ||
223 | { | ||
224 | static bool initialized = false; | ||
225 | |||
226 | if (initialized) | ||
227 | { | ||
228 | return; | ||
229 | } | ||
230 | |||
231 | decode_bytes (p, p_bytes); | ||
232 | decode_bytes (negative_1, negative_1_bytes); | ||
233 | decode_bytes (negative_2, negative_2_bytes); | ||
234 | decode_bytes (divide_negative_1_2, divide_negative_1_2_bytes); | ||
235 | decode_bytes (divide_plus_p_3_8, divide_plus_p_3_8_bytes); | ||
236 | decode_bytes (divide_minus_p_1_2, divide_minus_p_1_2_bytes); | ||
237 | decode_bytes (square_root_negative_1, square_root_negative_1_bytes); | ||
238 | decode_bytes (A, A_bytes); | ||
239 | decode_bytes (negative_A, negative_A_bytes); | ||
240 | decode_bytes (u, u_bytes); | ||
241 | decode_bytes (inverted_u, inverted_u_bytes); | ||
242 | decode_bytes (d, d_bytes); | ||
243 | |||
244 | mp_size_t scratch_space_lengths[] = { | ||
245 | // For least_square_root | ||
246 | |||
247 | mpn_sec_powm_itch (P_LIMBS, P_BITS - 1, P_LIMBS), | ||
248 | mpn_sec_sqr_itch (P_LIMBS), | ||
249 | mpn_sec_div_r_itch (P_LIMBS + P_LIMBS, P_LIMBS), | ||
250 | mpn_sec_sub_1_itch (P_LIMBS), | ||
251 | mpn_sec_mul_itch (P_LIMBS, P_LIMBS), | ||
252 | |||
253 | // For Elligator_2_Curve25519_encode | ||
254 | |||
255 | mpn_sec_powm_itch (P_LIMBS, P_BITS - 1, P_LIMBS), | ||
256 | mpn_sec_mul_itch (P_LIMBS, P_LIMBS), | ||
257 | mpn_sec_div_r_itch (P_LIMBS + P_LIMBS, P_LIMBS), | ||
258 | mpn_sec_sqr_itch (P_LIMBS), | ||
259 | mpn_sec_sub_1_itch (P_LIMBS), | ||
260 | |||
261 | // For Elligator_2_Curve25519_decode | ||
262 | |||
263 | mpn_sec_sqr_itch (P_LIMBS), | ||
264 | mpn_sec_div_r_itch (P_LIMBS + P_LIMBS, P_LIMBS), | ||
265 | mpn_sec_div_r_itch (P_LIMBS, P_LIMBS), | ||
266 | mpn_sec_mul_itch (P_LIMBS, P_LIMBS), | ||
267 | mpn_sec_add_1_itch (P_LIMBS), | ||
268 | mpn_sec_powm_itch (P_LIMBS, P_BITS - 1, P_LIMBS), | ||
269 | |||
270 | // For Elligator_2_Curve25519_convert_from_Ed25519 | ||
271 | /* | ||
272 | mpn_sec_sqr_itch (P_LIMBS), | ||
273 | mpn_sec_div_r_itch (P_LIMBS + P_LIMBS, P_LIMBS), | ||
274 | mpn_sec_mul_itch (P_LIMBS, P_LIMBS), | ||
275 | mpn_sec_add_1_itch (P_LIMBS), | ||
276 | mpn_sec_powm_itch (P_LIMBS, P_BITS - 1, P_LIMBS), | ||
277 | mpn_sec_sub_1_itch (P_LIMBS) | ||
278 | */ | ||
279 | }; | ||
280 | |||
281 | for (size_t i = 0; i < sizeof scratch_space_lengths | ||
282 | / sizeof *scratch_space_lengths; ++i) | ||
283 | { | ||
284 | if (scratch_space_lengths[i] > scratch_space_length) | ||
285 | { | ||
286 | scratch_space_length = scratch_space_lengths[i]; | ||
287 | } | ||
288 | } | ||
289 | |||
290 | initialized = true; | ||
291 | } | ||
292 | |||
293 | |||
294 | // Returns trash if the number is a quadratic non-residue | ||
295 | |||
296 | static void | ||
297 | least_square_root (mp_limb_t *root, const mp_limb_t *number, | ||
298 | mp_limb_t *scratch_space) | ||
299 | { | ||
300 | mp_limb_t a[P_LIMBS + P_LIMBS]; | ||
301 | mp_limb_t b[P_LIMBS]; | ||
302 | |||
303 | // root := number ^ ((p + 3) / 8) | ||
304 | |||
305 | mpn_add_n (b, number, p, P_LIMBS); // The next function requires a nonzero input | ||
306 | mpn_sec_powm (root, b, P_LIMBS, divide_plus_p_3_8, P_BITS - 1, p, P_LIMBS, | ||
307 | scratch_space); | ||
308 | |||
309 | // If root ^ 2 != number, root := root * square_root(-1) | ||
310 | |||
311 | mpn_sec_sqr (a, root, P_LIMBS, scratch_space); | ||
312 | mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
313 | mpn_sub_n (b, a, number, P_LIMBS); | ||
314 | |||
315 | mp_limb_t condition = mpn_sec_sub_1 (b, b, P_LIMBS, 1, scratch_space) ^ 1; | ||
316 | |||
317 | mpn_sec_mul (a, root, P_LIMBS, square_root_negative_1, P_LIMBS, | ||
318 | scratch_space); | ||
319 | mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
320 | |||
321 | mpn_cnd_swap (condition, root, a, P_LIMBS); | ||
322 | |||
323 | // If root > (p - 1) / 2, root := -root | ||
324 | |||
325 | condition = mpn_sub_n (a, divide_minus_p_1_2, root, P_LIMBS); | ||
326 | |||
327 | mpn_sub_n (a, p, root, P_LIMBS); // If root = 0, a := p | ||
328 | |||
329 | mpn_cnd_swap (condition, root, a, P_LIMBS); | ||
330 | } | ||
331 | |||
332 | |||
333 | bool | ||
334 | GNUNET_CRYPTO_ecdhe_elligator_inverse_map (uint8_t *representative, const | ||
335 | uint8_t *point, | ||
336 | bool high_y) | ||
337 | { | ||
338 | mp_limb_t scratch_space[scratch_space_length]; | ||
339 | |||
340 | mp_limb_t a[P_LIMBS + P_LIMBS]; | ||
341 | mp_limb_t b[P_LIMBS + P_LIMBS]; | ||
342 | mp_limb_t c[P_LIMBS + P_LIMBS]; | ||
343 | |||
344 | // a := point | ||
345 | |||
346 | decode_bytes (a, point); | ||
347 | |||
348 | // b := -a / (a + A), or b := p if a = 0 | ||
349 | |||
350 | mpn_add_n (b, a, A, P_LIMBS); | ||
351 | mpn_sec_powm (c, b, P_LIMBS, negative_2, P_BITS - 1, p, P_LIMBS, | ||
352 | scratch_space); | ||
353 | mpn_sec_mul (b, c, P_LIMBS, a, P_LIMBS, scratch_space); | ||
354 | mpn_sec_div_r (b, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
355 | mpn_sub_n (b, p, b, P_LIMBS); | ||
356 | |||
357 | // If high_y = true, b := 1 / b or b := 0 if it was = p | ||
358 | |||
359 | mpn_sec_powm (c, b, P_LIMBS, negative_2, P_BITS - 1, p, P_LIMBS, | ||
360 | scratch_space); | ||
361 | mpn_cnd_swap (high_y, b, c, P_LIMBS); | ||
362 | |||
363 | // c := b / u | ||
364 | |||
365 | mpn_sec_mul (c, b, P_LIMBS, inverted_u, P_LIMBS, scratch_space); | ||
366 | mpn_sec_div_r (c, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
367 | |||
368 | // If c is a square modulo p, b := least_square_root(c) | ||
369 | |||
370 | least_square_root (b, c, scratch_space); | ||
371 | |||
372 | // Determine, whether b ^ 2 = c | ||
373 | |||
374 | mpn_sec_sqr (a, b, P_LIMBS, scratch_space); | ||
375 | mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
376 | mpn_sub_n (a, a, c, P_LIMBS); | ||
377 | |||
378 | bool result = mpn_sec_sub_1 (a, a, P_LIMBS, 1, scratch_space); | ||
379 | |||
380 | encode_bytes (representative, b); | ||
381 | |||
382 | return result; | ||
383 | } | ||
384 | |||
385 | |||
386 | bool | ||
387 | GNUNET_CRYPTO_ecdhe_elligator_direct_map (uint8_t *point, bool *high_y, | ||
388 | uint8_t *representative) | ||
389 | { | ||
390 | mp_limb_t scratch_space[scratch_space_length]; | ||
391 | |||
392 | mp_limb_t a[P_LIMBS + P_LIMBS]; | ||
393 | mp_limb_t b[P_LIMBS + P_LIMBS]; | ||
394 | mp_limb_t c[P_LIMBS]; | ||
395 | mp_limb_t e[P_LIMBS + P_LIMBS]; | ||
396 | |||
397 | // a := representative | ||
398 | |||
399 | decode_bytes (a, representative); | ||
400 | |||
401 | // Determine whether a < (p - 1) / 2 | ||
402 | |||
403 | bool result = mpn_sub_n (b, divide_minus_p_1_2, a, P_LIMBS) ^ 1; | ||
404 | |||
405 | // b := -A / (1 + u * a ^ 2) | ||
406 | |||
407 | mpn_sec_sqr (b, a, P_LIMBS, scratch_space); | ||
408 | mpn_sec_div_r (b, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
409 | mpn_sec_mul (a, u, P_LIMBS, b, P_LIMBS, scratch_space); | ||
410 | mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
411 | mpn_sec_add_1 (b, a, P_LIMBS, 1, scratch_space); | ||
412 | mpn_sec_powm (a, b, P_LIMBS, negative_2, P_BITS - 1, p, P_LIMBS, | ||
413 | scratch_space); | ||
414 | mpn_sec_mul (b, a, P_LIMBS, negative_A, P_LIMBS, scratch_space); | ||
415 | mpn_sec_div_r (b, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
416 | |||
417 | // a := b ^ 3 + A * b ^ 2 + b (with 1-bit overflow) | ||
418 | |||
419 | mpn_sec_sqr (a, b, P_LIMBS, scratch_space); | ||
420 | mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
421 | mpn_add_n (c, b, A, P_LIMBS); | ||
422 | mpn_sec_mul (e, c, P_LIMBS, a, P_LIMBS, scratch_space); | ||
423 | mpn_sec_div_r (e, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
424 | mpn_add_n (a, e, b, P_LIMBS); | ||
425 | |||
426 | // If a is a quadratic residue modulo p, point := b and high_y := 1 | ||
427 | // Otherwise point := -b - A and high_y := 0 | ||
428 | |||
429 | mpn_sub_n (c, p, b, P_LIMBS); | ||
430 | mpn_add_n (c, c, negative_A, P_LIMBS); | ||
431 | mpn_sec_div_r (c, P_LIMBS, p, P_LIMBS, scratch_space); | ||
432 | |||
433 | mpn_sec_powm (e, a, P_LIMBS, divide_minus_p_1_2, P_BITS - 1, p, P_LIMBS, | ||
434 | scratch_space); | ||
435 | *high_y = mpn_sub_n (e, e, divide_minus_p_1_2, P_LIMBS); | ||
436 | |||
437 | mpn_cnd_swap (*high_y, b, c, P_LIMBS); | ||
438 | |||
439 | encode_bytes (point, c); | ||
440 | |||
441 | return result; | ||
442 | } | ||
443 | |||
444 | |||
445 | // Removes most significant bit and second most significant bit before applying elligator direct map | ||
446 | bool | ||
447 | GNUNET_CRYPTO_ecdhe_elligator_decoding (struct | ||
448 | GNUNET_CRYPTO_EcdhePublicKey *point, | ||
449 | bool *high_y, | ||
450 | struct | ||
451 | GNUNET_CRYPTO_ElligatorRepresentative * | ||
452 | representative) | ||
453 | { | ||
454 | representative->r[31] &= 63; | ||
455 | return GNUNET_CRYPTO_ecdhe_elligator_direct_map ((uint8_t *) point->q_y, | ||
456 | high_y, | ||
457 | (uint8_t *) representative->r); | ||
458 | } | ||
459 | |||
460 | |||
461 | static bool | ||
462 | Elligator_2_Curve25519_convert_from_Ed25519 (uint8_t *point, const | ||
463 | uint8_t *source) | ||
464 | { | ||
465 | mp_limb_t scratch_space[scratch_space_length]; | ||
466 | |||
467 | mp_limb_t y[P_LIMBS]; | ||
468 | mp_limb_t a[P_LIMBS + P_LIMBS]; | ||
469 | mp_limb_t b[P_LIMBS + P_LIMBS]; | ||
470 | mp_limb_t c[P_LIMBS + P_LIMBS]; | ||
471 | |||
472 | uint8_t y_bytes[P_BYTES]; | ||
473 | |||
474 | memcpy (y_bytes, source, 31); | ||
475 | |||
476 | y_bytes[31] = source[31] & 0x7f; | ||
477 | |||
478 | decode_bytes (y, y_bytes); | ||
479 | |||
480 | // Check if y < p | ||
481 | |||
482 | bool result = mpn_sub_n (a, y, p, P_LIMBS); | ||
483 | |||
484 | // a := (y ^ 2 - 1) / (1 + d * y ^ 2) | ||
485 | |||
486 | mpn_sec_sqr (a, y, P_LIMBS, scratch_space); | ||
487 | mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
488 | mpn_sec_mul (b, a, P_LIMBS, d, P_LIMBS, scratch_space); | ||
489 | mpn_sec_add_1 (b, b, P_LIMBS, 1, scratch_space); | ||
490 | mpn_sec_div_r (b, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
491 | mpn_sec_powm (c, b, P_LIMBS, negative_2, P_BITS - 1, p, P_LIMBS, | ||
492 | scratch_space); | ||
493 | mpn_add_n (b, a, negative_1, P_LIMBS); | ||
494 | mpn_sec_mul (a, b, P_LIMBS, c, P_LIMBS, scratch_space); | ||
495 | mpn_sec_div_r (a, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
496 | |||
497 | // Check, whether a is a square modulo p (including a = 0) | ||
498 | |||
499 | mpn_add_n (a, a, p, P_LIMBS); | ||
500 | mpn_sec_powm (b, a, P_LIMBS, divide_negative_1_2, P_BITS - 1, p, P_LIMBS, | ||
501 | scratch_space); | ||
502 | |||
503 | result &= mpn_sub_n (c, b, divide_minus_p_1_2, P_LIMBS); | ||
504 | |||
505 | // If a = p, the parity bit must be 0 | ||
506 | |||
507 | mpn_sub_n (a, a, p, P_LIMBS); | ||
508 | |||
509 | result ^= mpn_sec_sub_1 (a, a, P_LIMBS, 1, scratch_space) & source[31] >> 7; | ||
510 | |||
511 | // If y != 1, c := (1 + y) / (1 - y), otherwise c := 0 | ||
512 | |||
513 | mpn_sub_n (a, p, y, P_LIMBS); | ||
514 | mpn_sec_add_1 (a, a, P_LIMBS, 1, scratch_space); | ||
515 | mpn_sec_powm (b, a, P_LIMBS, negative_2, P_BITS - 1, p, P_LIMBS, | ||
516 | scratch_space); | ||
517 | mpn_sec_add_1 (a, y, P_LIMBS, 1, scratch_space); | ||
518 | mpn_sec_mul (c, a, P_LIMBS, b, P_LIMBS, scratch_space); | ||
519 | mpn_sec_div_r (c, P_LIMBS + P_LIMBS, p, P_LIMBS, scratch_space); | ||
520 | |||
521 | encode_bytes (point, c); | ||
522 | |||
523 | return result; | ||
524 | } | ||
525 | |||
526 | |||
527 | // Would call GNUNET_CRYPTO_ecdhe_key_create (struct GNUNET_CRYPTO_EcdhePrivateKey *pk) for pk which is not clamped | ||
528 | // Following Method 1 in description https://elligator.org/key-exchange section Step 2: Generate a “special” public key | ||
529 | int | ||
530 | GNUNET_CRYPTO_ecdhe_elligator_generate_public_key (unsigned char | ||
531 | pub[ | ||
532 | crypto_scalarmult_SCALARBYTES | ||
533 | ], | ||
534 | struct | ||
535 | GNUNET_CRYPTO_EcdhePrivateKey | ||
536 | *pk) | ||
537 | { | ||
538 | // eHigh | ||
539 | // Note crypto_scalarmult_ed25519_base clamps the scalar (here pk->d). TODO: test this | ||
540 | // TODO: if pk-d is zero cryto_scalarmult... return -1, otherwise 0. Problem if 0? Unlikely anyway | ||
541 | unsigned char eHigh[crypto_scalarmult_SCALARBYTES] = {0}; | ||
542 | crypto_scalarmult_ed25519_base (eHigh, pk->d); | ||
543 | |||
544 | // eLow: choose a random point of low order | ||
545 | int sLow = (pk->d)[0] % 8; | ||
546 | unsigned char eLow[crypto_scalarmult_SCALARBYTES] = {0}; | ||
547 | memcpy (eLow, lookupTable[sLow], crypto_scalarmult_SCALARBYTES); | ||
548 | |||
549 | // eHigh + eLow | ||
550 | unsigned char edPub[crypto_scalarmult_SCALARBYTES] = {0}; | ||
551 | if (crypto_core_ed25519_add (edPub, eLow, eHigh) == -1) | ||
552 | { | ||
553 | return GNUNET_SYSERR; | ||
554 | } | ||
555 | |||
556 | // Convert point in Ed25519 to Montgomery point | ||
557 | // TODO: libsodium convert function doesn't work. Figure out why. Maybe because we work on the whole curve rather than the prime subgroup. | ||
558 | /*if (crypto_sign_ed25519_pk_to_curve25519 (pub, edPub) == -1) | ||
559 | { | ||
560 | return -1; | ||
561 | }*/ | ||
562 | |||
563 | if (Elligator_2_Curve25519_convert_from_Ed25519 (pub, edPub) == false) | ||
564 | { | ||
565 | return GNUNET_SYSERR; | ||
566 | } | ||
567 | return GNUNET_OK; | ||
568 | } | ||
569 | |||
570 | |||
571 | /** | ||
572 | Doesn't work because crypto_scalarmult clamps the scalar. We don't want this. | ||
573 | Unfortunately a "noclamp" version of multiplication is only available for edwards25519 in libsodium. | ||
574 | We therefore can't implement the second (alternative) method for generate_public_key. | ||
575 | Keeping this code for discussion. Delete later. | ||
576 | |||
577 | // Curve25519 point (only x-coordinate) which is needed for the alternativ method of | ||
578 | //GNUNET_CRYPTO_ecdhe_elligator_generate_public_key_alternativ | ||
579 | static const unsigned char kPoint[] = { | ||
580 | 0xD8, 0x86, 0x1A, 0xA2, 0x78, 0x7A, 0xD9, 0x26, | ||
581 | 0x8B, 0x74, 0x74, 0xB6, 0x82, 0xE3, 0xBE, 0xC3, | ||
582 | 0xCE, 0x36, 0x9A, 0x1E, 0x5E, 0x31, 0x47, 0xA2, | ||
583 | 0x6D, 0x37, 0x7C, 0xFD, 0x20, 0xB5, 0xDF, 0x75 | ||
584 | }; | ||
585 | |||
586 | // Curve25519 order of prime order subgroup | ||
587 | static const unsigned char L[] = { | ||
588 | 0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, | ||
589 | 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14, | ||
590 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
591 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 | ||
592 | }; | ||
593 | |||
594 | static void multiplyLittleEndianArray(const unsigned char *input, int multiplier, unsigned char *output, int arraySize) { | ||
595 | int carry = 0; | ||
596 | |||
597 | for (int i = 0; i < arraySize; ++i) { | ||
598 | int result = input[i] * multiplier + carry; | ||
599 | output[i] = result & 0xFF; // Store the lower 8 bits in the output array | ||
600 | carry = result >> 8; // Carry the remaining bits to the next iteration | ||
601 | } | ||
602 | } | ||
603 | |||
604 | static void addLittleEndianArrays(const unsigned char *array1, const unsigned char *array2, unsigned char *result, int arraySize) { | ||
605 | int carry = 0; | ||
606 | |||
607 | for (int i = 0; i < arraySize; ++i) { | ||
608 | int sum = array1[i] + array2[i] + carry; | ||
609 | result[i] = sum & 0xFF; // Store the lower 8 bits in the result array | ||
610 | carry = sum >> 8; // Carry the remaining bits to the next iteration | ||
611 | } | ||
612 | } | ||
613 | |||
614 | Would call GNUNET_CRYPTO_ecdhe_key_create (struct GNUNET_CRYPTO_EcdhePrivateKey *pk) for pk which is not clamped | ||
615 | Following Method 1 in description https://elligator.org/key-exchange section Step 2: Generate a “special” public key | ||
616 | int | ||
617 | GNUNET_CRYPTO_ecdhe_elligator_generate_public_key_alternativ (unsigned char | ||
618 | pub[ | ||
619 | crypto_scalarmult_SCALARBYTES | ||
620 | ], | ||
621 | struct | ||
622 | GNUNET_CRYPTO_EcdhePrivateKey | ||
623 | *pk) | ||
624 | { | ||
625 | unsigned char sClamp[crypto_scalarmult_BYTES] = {0}; | ||
626 | memcpy(sClamp, pk->d, sizeof(sClamp)); | ||
627 | sClamp[0] &= 248; | ||
628 | sClamp[31] &= 127; | ||
629 | sClamp[31] |= 64; | ||
630 | |||
631 | unsigned char sLow[crypto_scalarmult_BYTES] = {0}; | ||
632 | int multiplier = (pk->d)[0] % 8; | ||
633 | multiplyLittleEndianArray(L, multiplier, sLow, 32); | ||
634 | unsigned char sDirty[crypto_scalarmult_BYTES] = {0}; | ||
635 | addLittleEndianArrays(sClamp, sLow, sDirty, 32); | ||
636 | |||
637 | int check = crypto_scalarmult(pub, sDirty, | ||
638 | kPoint); | ||
639 | |||
640 | if (check == -1) | ||
641 | { | ||
642 | printf("crypto_scalarmult didn't work\n"); | ||
643 | return -1; | ||
644 | } | ||
645 | |||
646 | return 0; | ||
647 | } | ||
648 | **/ | ||
649 | |||
650 | enum GNUNET_GenericReturnValue | ||
651 | GNUNET_CRYPTO_ecdhe_elligator_key_create (struct | ||
652 | GNUNET_CRYPTO_ElligatorRepresentative | ||
653 | * | ||
654 | repr, | ||
655 | struct GNUNET_CRYPTO_EcdhePrivateKey | ||
656 | *pk) | ||
657 | { | ||
658 | // inverse map can fail for some public keys generated by GNUNET_CRYPTO_ecdhe_elligator_generate_public_key | ||
659 | bool validKey = 0; | ||
660 | unsigned char pub[crypto_scalarmult_SCALARBYTES]; | ||
661 | int8_t random_tweak; | ||
662 | bool high_y; | ||
663 | bool msb_set; | ||
664 | bool smsb_set; | ||
665 | |||
666 | while (! validKey) | ||
667 | { | ||
668 | GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, | ||
669 | pk, | ||
670 | sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey)); | ||
671 | if (GNUNET_CRYPTO_ecdhe_elligator_generate_public_key (pub, pk) == | ||
672 | GNUNET_SYSERR) | ||
673 | { | ||
674 | return GNUNET_SYSERR; | ||
675 | } | ||
676 | |||
677 | GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, | ||
678 | &random_tweak, | ||
679 | sizeof(int8_t)); | ||
680 | high_y = random_tweak & 1; | ||
681 | |||
682 | validKey = GNUNET_CRYPTO_ecdhe_elligator_inverse_map ((unsigned | ||
683 | char*) &(repr->r), | ||
684 | (unsigned char*) pub, | ||
685 | high_y ? | ||
686 | GNUNET_YES : | ||
687 | GNUNET_NO); | ||
688 | } | ||
689 | |||
690 | // Setting most significant bit and second most significant bit randomly | ||
691 | msb_set = (random_tweak >> 1) & 1; | ||
692 | smsb_set = (random_tweak >> 2) & 1; | ||
693 | if (msb_set) | ||
694 | { | ||
695 | repr->r[31] |= 128; | ||
696 | } | ||
697 | if (smsb_set) | ||
698 | { | ||
699 | repr->r[31] |= 64; | ||
700 | } | ||
701 | return GNUNET_OK; | ||
702 | } \ No newline at end of file | ||
diff --git a/src/lib/util/meson.build b/src/lib/util/meson.build index c9a73bdd3..828df25fd 100644 --- a/src/lib/util/meson.build +++ b/src/lib/util/meson.build | |||
@@ -27,6 +27,7 @@ libgnunetutil_src = ['bandwidth.c', | |||
27 | 'crypto_ecc_dlog.c', | 27 | 'crypto_ecc_dlog.c', |
28 | 'crypto_ecc_setup.c', | 28 | 'crypto_ecc_setup.c', |
29 | 'crypto_edx25519.c', | 29 | 'crypto_edx25519.c', |
30 | 'crypto_elligator.c', | ||
30 | 'crypto_hash.c', | 31 | 'crypto_hash.c', |
31 | 'crypto_hash_file.c', | 32 | 'crypto_hash_file.c', |
32 | 'crypto_hkdf.c', | 33 | 'crypto_hkdf.c', |
@@ -331,6 +332,16 @@ test('test_crypto_ecc_dlog', testcrypto_ecc_dlog, | |||
331 | workdir: meson.current_build_dir(), | 332 | workdir: meson.current_build_dir(), |
332 | suite: ['util', 'util-crypto']) | 333 | suite: ['util', 'util-crypto']) |
333 | 334 | ||
335 | testcrypto_elligator = executable ('test_crypto_elligator', | ||
336 | ['test_crypto_elligator.c'], | ||
337 | dependencies: [gcrypt_dep, libgnunetutil_dep, lgmp_dep, sodium_dep], | ||
338 | include_directories: [incdir, configuration_inc], | ||
339 | build_by_default: false, | ||
340 | install: false) | ||
341 | test('test_crypto_elligator', testcrypto_elligator, | ||
342 | workdir: meson.current_build_dir(), | ||
343 | suite: ['util', 'util-crypto']) | ||
344 | |||
334 | testcrypto_hash = executable ('test_crypto_hash', | 345 | testcrypto_hash = executable ('test_crypto_hash', |
335 | ['test_crypto_hash.c'], | 346 | ['test_crypto_hash.c'], |
336 | dependencies: [gcrypt_dep, libgnunetutil_dep], | 347 | dependencies: [gcrypt_dep, libgnunetutil_dep], |
diff --git a/src/lib/util/test_crypto_elligator.c b/src/lib/util/test_crypto_elligator.c new file mode 100644 index 000000000..fc541887d --- /dev/null +++ b/src/lib/util/test_crypto_elligator.c | |||
@@ -0,0 +1,334 @@ | |||
1 | #include "platform.h" | ||
2 | #include "gnunet_util_lib.h" | ||
3 | #include "gnunet_signatures.h" | ||
4 | #include <gcrypt.h> | ||
5 | #include <stdio.h> | ||
6 | #include <sodium.h> | ||
7 | |||
8 | #define ITER 25 | ||
9 | |||
10 | // For debugging purposes | ||
11 | static void | ||
12 | printLittleEndianHex (const unsigned char *arr, size_t length) | ||
13 | { | ||
14 | for (size_t i = 0; i < length; ++i) | ||
15 | { | ||
16 | printf ("%02X", arr[i]); | ||
17 | } | ||
18 | printf ("\n"); | ||
19 | } | ||
20 | |||
21 | |||
22 | // Test vector from https://github.com/Kleshni/Elligator-2/blob/master/test-vectors.c | ||
23 | static int | ||
24 | testDirectMap (void) | ||
25 | { | ||
26 | int ok = GNUNET_OK; | ||
27 | |||
28 | uint8_t repr1[32] = { | ||
29 | 0x17, 0x9f, 0x24, 0x73, 0x0d, 0xed, 0x2c, 0xe3, 0x17, 0x39, 0x08, 0xec, | ||
30 | 0x61, 0x96, 0x46, 0x53, | ||
31 | 0xb8, 0x02, 0x7e, 0x38, 0x3f, 0x40, 0x34, 0x6c, 0x1c, 0x9b, 0x4d, 0x2b, | ||
32 | 0xdb, 0x1d, 0xb7, 0x6c | ||
33 | }; | ||
34 | |||
35 | uint8_t point1[32] = { | ||
36 | 0x10, 0x74, 0x54, 0x97, 0xd3, 0x5c, 0x6e, 0xde, 0x6e, 0xa6, 0xb3, 0x30, | ||
37 | 0x54, 0x6a, 0x6f, 0xcb, | ||
38 | 0xf1, 0x5c, 0x90, 0x3a, 0x7b, 0xe2, 0x8a, 0xe6, 0x9b, 0x1c, 0xa1, 0x4e, | ||
39 | 0x0b, 0xf0, 0x9b, 0x60 | ||
40 | }; | ||
41 | |||
42 | uint8_t pointResult[32]; | ||
43 | bool highYResult; | ||
44 | bool isLeastSqrRoot = GNUNET_CRYPTO_ecdhe_elligator_direct_map (pointResult, | ||
45 | &highYResult, | ||
46 | repr1); | ||
47 | |||
48 | if (isLeastSqrRoot == false) | ||
49 | { | ||
50 | ok = GNUNET_OK; | ||
51 | } | ||
52 | if (memcmp (point1,pointResult,sizeof(point1)) != 0) | ||
53 | { | ||
54 | ok = GNUNET_SYSERR; | ||
55 | } | ||
56 | |||
57 | return ok; | ||
58 | } | ||
59 | |||
60 | |||
61 | // Test vector from https://github.com/Kleshni/Elligator-2/blob/master/test-vectors.c | ||
62 | static int | ||
63 | testInverseMap (void) | ||
64 | { | ||
65 | int ok = GNUNET_OK; | ||
66 | uint8_t point1[32] = { | ||
67 | 0x33, 0x95, 0x19, 0x64, 0x00, 0x3c, 0x94, 0x08, 0x78, 0x06, 0x3c, 0xcf, | ||
68 | 0xd0, 0x34, 0x8a, 0xf4, | ||
69 | 0x21, 0x50, 0xca, 0x16, 0xd2, 0x64,0x6f, 0x2c, 0x58, 0x56, 0xe8, 0x33, 0x83, | ||
70 | 0x77, 0xd8, 0x00 | ||
71 | }; | ||
72 | |||
73 | uint8_t repr1[32] = { | ||
74 | 0x99, 0x9b, 0x59, 0x1b, 0x66, 0x97, 0xd0, 0x74, 0xf2, 0x66, 0x19, 0x22,0x77, | ||
75 | 0xd5, 0x54, 0xde, | ||
76 | 0xc3, 0xc2, 0x4c, 0x2e,0xf6, 0x10, 0x81, 0x01, 0xf6, 0x3d, 0x94, 0xf7, 0xff, | ||
77 | 0xf3, 0xa0, 0x13 | ||
78 | }; | ||
79 | |||
80 | uint8_t reprResult1[32]; | ||
81 | bool yHigh1 = false; | ||
82 | bool success = GNUNET_CRYPTO_ecdhe_elligator_inverse_map (reprResult1, | ||
83 | point1, | ||
84 | yHigh1); | ||
85 | if (success == false) | ||
86 | { | ||
87 | ok = GNUNET_SYSERR; | ||
88 | } | ||
89 | if (memcmp (repr1,reprResult1,sizeof(repr1)) != 0) | ||
90 | { | ||
91 | ok = GNUNET_SYSERR; | ||
92 | } | ||
93 | |||
94 | return ok; | ||
95 | } | ||
96 | |||
97 | |||
98 | /* | ||
99 | * Test description: GNUNET_CRYPTO_ecdhe_elligator_generate_public_key() projects a point from the prime subgroup to the whole curve. | ||
100 | * Both, the original point and the projectes point, should result in the same point when multiplied with a clamped scalar. | ||
101 | */ | ||
102 | static int | ||
103 | testGeneratePkScalarMult (void) | ||
104 | { | ||
105 | struct GNUNET_CRYPTO_EcdhePrivateKey pk; | ||
106 | GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, | ||
107 | &pk, | ||
108 | sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey)); | ||
109 | |||
110 | unsigned char pubWholeCurve[crypto_scalarmult_SCALARBYTES]; | ||
111 | unsigned char pubPrimeCurve[crypto_scalarmult_SCALARBYTES]; | ||
112 | |||
113 | if (GNUNET_CRYPTO_ecdhe_elligator_generate_public_key (pubWholeCurve, &pk) == | ||
114 | -1) | ||
115 | { | ||
116 | return GNUNET_SYSERR; | ||
117 | } | ||
118 | crypto_scalarmult_base (pubPrimeCurve, pk.d); | ||
119 | |||
120 | // printf ("pubWholeCurve\n"); | ||
121 | // printLittleEndianHex (pubWholeCurve,32); | ||
122 | // printf ("pubPrimeCurve\n"); | ||
123 | // printLittleEndianHex (pubPrimeCurve,32); | ||
124 | // TODO: Currently utilizing ecdsa function for ecdhe testing, due to clamping. Clean this part later. | ||
125 | struct GNUNET_CRYPTO_EcdsaPrivateKey clampedPk; | ||
126 | GNUNET_CRYPTO_ecdsa_key_create (&clampedPk); | ||
127 | crypto_scalarmult_base (pubWholeCurve, clampedPk.d); | ||
128 | crypto_scalarmult_base (pubPrimeCurve, clampedPk.d); | ||
129 | if (memcmp (pubWholeCurve, pubPrimeCurve, sizeof(pubWholeCurve)) != 0) | ||
130 | { | ||
131 | return GNUNET_SYSERR; | ||
132 | } | ||
133 | return GNUNET_OK; | ||
134 | } | ||
135 | |||
136 | |||
137 | /* | ||
138 | * Test Description: Simply testing, if function goes through. | ||
139 | */ | ||
140 | static int | ||
141 | testKeyPairEasy (void) | ||
142 | { | ||
143 | struct GNUNET_CRYPTO_ElligatorRepresentative repr; | ||
144 | struct GNUNET_CRYPTO_EcdhePrivateKey pk; | ||
145 | int i = GNUNET_CRYPTO_ecdhe_elligator_key_create (&repr, &pk); | ||
146 | if (i == GNUNET_SYSERR) | ||
147 | { | ||
148 | return GNUNET_SYSERR; | ||
149 | } | ||
150 | return GNUNET_OK; | ||
151 | } | ||
152 | |||
153 | |||
154 | /* | ||
155 | * Test Description: After generating a valid private key and the corresponding representative with | ||
156 | * GNUNET_CRYPTO_ecdhe_elligator_key_create(), check if using the direct map results in the corresponding public key. | ||
157 | */ | ||
158 | static int | ||
159 | testInverseDirect (void) | ||
160 | { | ||
161 | struct GNUNET_CRYPTO_ElligatorRepresentative repr; | ||
162 | struct GNUNET_CRYPTO_EcdhePublicKey point; | ||
163 | struct GNUNET_CRYPTO_EcdhePrivateKey pk; | ||
164 | int i = GNUNET_CRYPTO_ecdhe_elligator_key_create (&repr, &pk); | ||
165 | if (i == -1) | ||
166 | { | ||
167 | return GNUNET_SYSERR; | ||
168 | } | ||
169 | |||
170 | unsigned char pub[crypto_scalarmult_SCALARBYTES]; | ||
171 | bool highY; | ||
172 | if (GNUNET_CRYPTO_ecdhe_elligator_generate_public_key (pub, &pk) == -1) | ||
173 | { | ||
174 | return GNUNET_SYSERR; | ||
175 | } | ||
176 | |||
177 | GNUNET_CRYPTO_ecdhe_elligator_decoding (&point, &highY, &repr); | ||
178 | |||
179 | if (memcmp (pub, point.q_y, sizeof(point.q_y)) != 0) | ||
180 | { | ||
181 | return GNUNET_SYSERR; | ||
182 | } | ||
183 | |||
184 | return GNUNET_OK; | ||
185 | } | ||
186 | |||
187 | |||
188 | /* | ||
189 | * Test Description: Measuring the time it takes to generate 25 key pairs (pk, representative). | ||
190 | * Time value can vary because GNUNET_CRYPTO_ecdhe_elligator_key_create generates internally random | ||
191 | * public keys which are just valid 50% of the time for elligators inverse map. | ||
192 | * GNUNET_CRYPTO_ecdhe_elligator_key_create will therefore generate as many public keys needed | ||
193 | * till a valid public key is generated. | ||
194 | */ | ||
195 | static int | ||
196 | testTimeKeyGenerate (void) | ||
197 | { | ||
198 | struct GNUNET_CRYPTO_ElligatorRepresentative repr; | ||
199 | struct GNUNET_CRYPTO_EcdhePrivateKey pk; | ||
200 | struct GNUNET_TIME_Absolute start; | ||
201 | int ok = GNUNET_OK; | ||
202 | |||
203 | fprintf (stderr, "%s", "W"); | ||
204 | start = GNUNET_TIME_absolute_get (); | ||
205 | |||
206 | for (unsigned int i = 0; i < ITER; i++) | ||
207 | { | ||
208 | fprintf (stderr, "%s", "."); | ||
209 | fflush (stderr); | ||
210 | if (GNUNET_SYSERR == | ||
211 | GNUNET_CRYPTO_ecdhe_elligator_key_create (&repr, &pk)) | ||
212 | { | ||
213 | fprintf (stderr, | ||
214 | "GNUNET_CRYPTO_ecdhe_elligator_key_create SYSERR\n"); | ||
215 | ok = GNUNET_SYSERR; | ||
216 | } | ||
217 | // printLittleEndianHex(repr.r,32); | ||
218 | } | ||
219 | printf ("%d encoded public keys generated in %s\n", | ||
220 | ITER, | ||
221 | GNUNET_STRINGS_relative_time_to_string ( | ||
222 | GNUNET_TIME_absolute_get_duration (start), | ||
223 | GNUNET_YES)); | ||
224 | return ok; | ||
225 | } | ||
226 | |||
227 | |||
228 | static int | ||
229 | testTimeDecoding (void) | ||
230 | { | ||
231 | struct GNUNET_CRYPTO_EcdhePublicKey point; | ||
232 | struct GNUNET_CRYPTO_ElligatorRepresentative repr[ITER]; | ||
233 | struct GNUNET_CRYPTO_EcdhePrivateKey pk; | ||
234 | bool high_y; | ||
235 | struct GNUNET_TIME_Absolute start; | ||
236 | int ok = GNUNET_OK; | ||
237 | |||
238 | for (unsigned int i = 0; i < ITER; i++) | ||
239 | { | ||
240 | if (GNUNET_SYSERR == | ||
241 | GNUNET_CRYPTO_ecdhe_elligator_key_create (&repr[i], &pk)) | ||
242 | { | ||
243 | fprintf (stderr, | ||
244 | "GNUNET_CRYPTO_ecdhe_elligator_key_create SYSERR\n"); | ||
245 | ok = GNUNET_SYSERR; | ||
246 | continue; | ||
247 | } | ||
248 | } | ||
249 | |||
250 | fprintf (stderr, "%s", "W"); | ||
251 | start = GNUNET_TIME_absolute_get (); | ||
252 | |||
253 | for (unsigned int i = 0; i < ITER; i++) | ||
254 | { | ||
255 | fprintf (stderr, "%s", "."); | ||
256 | fflush (stderr); | ||
257 | if (false == | ||
258 | GNUNET_CRYPTO_ecdhe_elligator_decoding (&point, &high_y, &repr[i])) | ||
259 | { | ||
260 | fprintf (stderr, | ||
261 | "GNUNET_CRYPTO_ecdhe_elligator_decoding SYSERR\n"); | ||
262 | ok = GNUNET_SYSERR; | ||
263 | continue; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | printf ("%d decoded public keys generated in %s\n", | ||
268 | ITER, | ||
269 | GNUNET_STRINGS_relative_time_to_string ( | ||
270 | GNUNET_TIME_absolute_get_duration (start), | ||
271 | GNUNET_YES)); | ||
272 | return ok; | ||
273 | } | ||
274 | |||
275 | |||
276 | /* | ||
277 | *More tests to implement: | ||
278 | * Adding more test vectors from different sources for inverse and direct map | ||
279 | * check if inverse map rightfully fails for points which are not "encodable" | ||
280 | */ | ||
281 | |||
282 | |||
283 | int | ||
284 | main (int argc, char *argv[]) | ||
285 | { | ||
286 | GNUNET_CRYPTO_ecdhe_elligator_initialize (); | ||
287 | |||
288 | int failure_count = 0; | ||
289 | |||
290 | if (GNUNET_OK != testInverseMap ()) | ||
291 | { | ||
292 | printf ("inverse failed!"); | ||
293 | failure_count++; | ||
294 | } | ||
295 | if (GNUNET_OK != testDirectMap ()) | ||
296 | { | ||
297 | printf ("direct failed!"); | ||
298 | failure_count++; | ||
299 | } | ||
300 | if (GNUNET_OK != testGeneratePkScalarMult ()) | ||
301 | { | ||
302 | printf ("generate PK failed!"); | ||
303 | failure_count++; | ||
304 | } | ||
305 | if (GNUNET_OK != testKeyPairEasy ()) | ||
306 | { | ||
307 | printf ("key generation doesn't work!"); | ||
308 | failure_count++; | ||
309 | } | ||
310 | if (GNUNET_OK != testInverseDirect ()) | ||
311 | { | ||
312 | printf ("Inverse and direct map failed!"); | ||
313 | failure_count++; | ||
314 | } | ||
315 | if (GNUNET_OK != testTimeKeyGenerate ()) | ||
316 | { | ||
317 | printf ("Time measurement of key generation failed!"); | ||
318 | failure_count++; | ||
319 | } | ||
320 | if (GNUNET_OK != testTimeDecoding ()) | ||
321 | { | ||
322 | printf ("Time measurement of decoding failed!"); | ||
323 | failure_count++; | ||
324 | } | ||
325 | |||
326 | if (0 != failure_count) | ||
327 | { | ||
328 | fprintf (stderr, | ||
329 | "\n\n%d TESTS FAILED!\n\n", | ||
330 | failure_count); | ||
331 | return -1; | ||
332 | } | ||
333 | return 0; | ||
334 | } | ||