1 /* curve25519.c
2  * NaCl/Sodium-compatible API for Curve25519 cryptography.
3  *
4  * Copyright (c) 2018, Peter Wu <peter@lekensteyn.nl>
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12 
13 #include "curve25519.h"
14 #include "ws_attributes.h"
15 
16 #if GCRYPT_VERSION_NUMBER >= 0x010700 /* 1.7.0 */
17 #define HAVE_X25519
18 #endif
19 
20 #ifdef HAVE_X25519
21 static inline void
copy_and_reverse(unsigned char * dest,const unsigned char * src,size_t n)22 copy_and_reverse(unsigned char *dest, const unsigned char *src, size_t n)
23 {
24     for (size_t i = 0; i < n; i++) {
25         dest[n - 1 - i] = src[i];
26     }
27 }
28 
29 static int
x25519_mpi(unsigned char * q,const unsigned char * n,gcry_mpi_t mpi_p)30 x25519_mpi(unsigned char *q, const unsigned char *n, gcry_mpi_t mpi_p)
31 {
32     unsigned char priv_be[32];
33     unsigned char result_be[32];
34     size_t result_len = 0;
35     gcry_mpi_t mpi = NULL;
36     gcry_ctx_t ctx = NULL;
37     gcry_mpi_point_t P = NULL;
38     gcry_mpi_point_t Q = NULL;
39     int r = -1;
40 
41     /* Default to infinity (all zeroes). */
42     memset(q, 0, 32);
43 
44     /* Keys are in little-endian, but gcry_mpi_scan expects big endian. Convert
45      * keys and ensure that the result is a valid Curve25519 secret scalar. */
46     copy_and_reverse(priv_be, n, 32);
47     priv_be[0] &= 127;
48     priv_be[0] |= 64;
49     priv_be[31] &= 248;
50     gcry_mpi_scan(&mpi, GCRYMPI_FMT_USG, priv_be, 32, NULL);
51 
52     if (gcry_mpi_ec_new(&ctx, NULL, "Curve25519")) {
53         /* Should not happen, possibly out-of-memory. */
54         goto leave;
55     }
56 
57     /* Compute Q = nP */
58     Q = gcry_mpi_point_new(0);
59     P = gcry_mpi_point_set(NULL, mpi_p, NULL, GCRYMPI_CONST_ONE);
60     gcry_mpi_ec_mul(Q, mpi, P, ctx);
61 
62     /* Note: mpi is reused to store the result. */
63     if (gcry_mpi_ec_get_affine(mpi, NULL, Q, ctx)) {
64         /* Infinity. */
65         goto leave;
66     }
67 
68     if (gcry_mpi_print(GCRYMPI_FMT_USG, result_be, 32, &result_len, mpi)) {
69         /* Should not happen, possibly out-of-memory. */
70         goto leave;
71     }
72     copy_and_reverse(q, result_be, result_len);
73     r = 0;
74 
75 leave:
76     gcry_mpi_point_release(P);
77     gcry_mpi_point_release(Q);
78     gcry_ctx_release(ctx);
79     gcry_mpi_release(mpi);
80     /* XXX erase priv_be and result_be */
81     return r;
82 }
83 
84 int
crypto_scalarmult_curve25519(unsigned char * q,const unsigned char * n,const unsigned char * p)85 crypto_scalarmult_curve25519(unsigned char *q, const unsigned char *n,
86                              const unsigned char *p)
87 {
88     unsigned char p_be[32];
89     gcry_mpi_t mpi_p = NULL;
90 
91     copy_and_reverse(p_be, p, 32);
92     /* Clear unused bit. */
93     p_be[0] &= 0x7f;
94     gcry_mpi_scan(&mpi_p, GCRYMPI_FMT_USG, p_be, 32, NULL);
95     int r = x25519_mpi(q, n, mpi_p);
96     gcry_mpi_release(mpi_p);
97     return r;
98 }
99 
100 int
crypto_scalarmult_curve25519_base(unsigned char * q,const unsigned char * n)101 crypto_scalarmult_curve25519_base(unsigned char *q, const unsigned char *n)
102 {
103     gcry_mpi_t mpi_basepoint_x = gcry_mpi_set_ui(NULL, 9);
104     int r = x25519_mpi(q, n, mpi_basepoint_x);
105     gcry_mpi_release(mpi_basepoint_x);
106     return r;
107 }
108 #else
109 int
crypto_scalarmult_curve25519(unsigned char * q _U_,const unsigned char * n _U_,const unsigned char * p _U_)110 crypto_scalarmult_curve25519(unsigned char *q _U_, const unsigned char *n _U_,
111                              const unsigned char *p _U_)
112 {
113     return -1;
114 }
115 
116 int
crypto_scalarmult_curve25519_base(unsigned char * q _U_,const unsigned char * n _U_)117 crypto_scalarmult_curve25519_base(unsigned char *q _U_, const unsigned char *n _U_)
118 {
119     return -1;
120 }
121 #endif /* HAVE_X25519 */
122