chiark / gitweb /
0b5e4d2c9c662cc55a01ec440a634bd97f329c97
[secnet] / dh.c
1 /*
2  * dh.c
3  */
4 /*
5  * This file is Free Software.  It was originally written for secnet.
6  *
7  * Copyright 1995-2003 Stephen Early
8  * Copyright 2002-2014 Ian Jackson
9  *
10  * You may redistribute secnet as a whole and/or modify it under the
11  * terms of the GNU General Public License as published by the Free
12  * Software Foundation; either version 3, or (at your option) any
13  * later version.
14  *
15  * You may redistribute this file and/or modify it under the terms of
16  * the GNU General Public License as published by the Free Software
17  * Foundation; either version 2, or (at your option) any later
18  * version.
19  *
20  * This software is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this software; if not, see
27  * https://www.gnu.org/licenses/gpl.html.
28  */
29
30 #include <stdio.h>
31 #include <gmp.h>
32 #include <limits.h>
33
34 #include "secnet.h"
35 #include "magic.h"
36 #include "util.h"
37
38 struct dh {
39     closure_t cl;
40     struct dh_if ops;
41     struct cloc loc;
42     MP_INT p,g; /* prime modulus and generator */
43 };
44
45 static string_t dh_makepublic(void *sst, uint8_t *secret, int32_t secretlen)
46 {
47     struct dh *st=sst;
48     string_t r;
49     MP_INT a, b; /* a is secret key, b is public key */
50
51     mpz_init(&a);
52     mpz_init(&b);
53
54     read_mpbin(&a, secret, secretlen);
55
56     mpz_powm_sec(&b, &st->g, &a, &st->p);
57
58     r=write_mpstring(&b);
59
60     mpz_clear(&a);
61     mpz_clear(&b);
62     return r;
63 }
64
65 static dh_makeshared_fn dh_makeshared;
66 static bool_t dh_makeshared(void *sst, uint8_t *secret, int32_t secretlen,
67                             cstring_t rempublic, uint8_t *sharedsecret,
68                             int32_t buflen)
69 {
70     struct dh *st=sst;
71     MP_INT a, b, c;
72
73     mpz_init(&a);
74     mpz_init(&b);
75     mpz_init(&c);
76
77     read_mpbin(&a, secret, secretlen);
78     mpz_set_str(&b, rempublic, 16);
79
80     mpz_powm_sec(&c, &b, &a, &st->p);
81
82     write_mpbin(&c,sharedsecret,buflen);
83
84     mpz_clear(&a);
85     mpz_clear(&b);
86     mpz_clear(&c);
87
88     return True;
89 }
90
91 static list_t *dh_apply(closure_t *self, struct cloc loc, dict_t *context,
92                         list_t *args)
93 {
94     struct dh *st;
95     string_t p,g;
96     dict_t *dict = 0;
97     item_t *i;
98     bool_t check = True;
99
100     NEW(st);
101     st->cl.description="dh";
102     st->cl.type=CL_DH;
103     st->cl.apply=NULL;
104     st->cl.interface=&st->ops;
105     st->ops.st=st;
106     st->ops.makepublic=dh_makepublic;
107     st->ops.makeshared=dh_makeshared;
108     st->loc=loc;
109
110     /* We either have two string arguments and maybe a boolean, or a
111      * dictionary
112      */
113     i=list_elem(args,0);
114     if (i && i->type==t_dict) {
115         dict=i->data.dict;
116         p=dict_read_string(dict,"p",True,"diffie-hellman",loc);
117         g=dict_read_string(dict,"g",True,"diffie-hellman",loc);
118         check=dict_read_bool(dict,"check",False,"diffie-hellman",loc,True);
119     } else {
120         if (!i)
121             cfgfatal(loc,"diffie-hellman","you must provide a prime modulus\n");
122         else if (i->type!=t_string)
123             cfgfatal(i->loc,"diffie-hellman",
124                      "first argument must be a string or a dictionary\n");
125         p=i->data.string;
126         i=list_elem(args,1);
127         if (!i)
128             cfgfatal(loc,"diffie-hellman","you must provide a generator\n");
129         else if (i->type!=t_string)
130             cfgfatal(i->loc,"diffie-hellman","second argument must be a "
131                      "string\n");
132         g=i->data.string;
133         i=list_elem(args,2);
134         if (i) {
135             if (i->type!=t_bool)
136                 cfgfatal(i->loc,"diffie-hellman",
137                          "third argument must be boolean or omitted\n");
138             check=i->data.bool;
139         }
140     }
141
142     if (mpz_init_set_str(&st->p,p,16)!=0)
143         cfgfatal(loc,"diffie-hellman","\"%s\" is not a hex number "
144                  "string\n",p);
145     if (mpz_init_set_str(&st->g,g,16)!=0)
146         cfgfatal(i->loc,"diffie-hellman","\"%s\" is not a hex number "
147                  "string\n",g);
148
149     if (!check) {
150         Message(M_INFO,"diffie-hellman (%s:%d): skipping modulus "
151                 "primality check\n",loc.file,loc.line);
152     } else {
153         /* Test that the modulus is really prime */
154         if (mpz_probab_prime_p(&st->p,5)==0) {
155             cfgfatal(loc,"diffie-hellman","modulus must be a prime\n");
156         }
157     }
158
159     size_t sz=mpz_sizeinbase(&st->p,2)/8;
160     if (sz>INT_MAX) {
161         cfgfatal(loc,"diffie-hellman","modulus far too large\n");
162     }
163     if (mpz_cmp(&st->g,&st->p) >= 0) {
164         cfgfatal(loc,"diffie-hellman","generator must be less than modulus\n");
165     }
166
167     st->ops.secret_len=sz;
168
169     st->ops.shared_len=(mpz_sizeinbase(&st->p,2)+7)/8;
170     /* According to the docs, mpz_sizeinbase(,256) is allowed to return
171      * an answer which is 1 too large.  But mpz_sizeinbase(,2) isn't. */
172
173     if (!dict)
174         st->ops.capab_bit = CAPAB_BIT_TRADZP;
175     else
176         st->ops.capab_bit = dict_read_number(dict, "capab-num", False,
177                                              "dh", loc, CAPAB_BIT_TRADZP);
178     if (st->ops.capab_bit > CAPAB_BIT_MAX)
179         cfgfatal(loc,"dh","capab-num out of range 0..%d\n",
180                  CAPAB_BIT_MAX);
181
182     return new_closure(&st->cl);
183 }
184
185 void dh_module(dict_t *dict)
186 {
187     add_closure(dict,"diffie-hellman",dh_apply);
188 }