diff options
Diffstat (limited to 'src/json/json_wire.c')
-rw-r--r-- | src/json/json_wire.c | 426 |
1 files changed, 39 insertions, 387 deletions
diff --git a/src/json/json_wire.c b/src/json/json_wire.c index a49e7a54a..3d7e8a81b 100644 --- a/src/json/json_wire.c +++ b/src/json/json_wire.c | |||
@@ -24,378 +24,6 @@ | |||
24 | #include "taler_json_lib.h" | 24 | #include "taler_json_lib.h" |
25 | 25 | ||
26 | 26 | ||
27 | /* Taken from GNU gettext */ | ||
28 | |||
29 | /** | ||
30 | * Entry in the country table. | ||
31 | */ | ||
32 | struct CountryTableEntry | ||
33 | { | ||
34 | /** | ||
35 | * 2-Character international country code. | ||
36 | */ | ||
37 | const char *code; | ||
38 | |||
39 | /** | ||
40 | * Long English name of the country. | ||
41 | */ | ||
42 | const char *english; | ||
43 | }; | ||
44 | |||
45 | |||
46 | /* Keep the following table in sync with gettext. | ||
47 | WARNING: the entries should stay sorted according to the code */ | ||
48 | /** | ||
49 | * List of country codes. | ||
50 | */ | ||
51 | static const struct CountryTableEntry country_table[] = { | ||
52 | { "AE", "U.A.E." }, | ||
53 | { "AF", "Afghanistan" }, | ||
54 | { "AL", "Albania" }, | ||
55 | { "AM", "Armenia" }, | ||
56 | { "AN", "Netherlands Antilles" }, | ||
57 | { "AR", "Argentina" }, | ||
58 | { "AT", "Austria" }, | ||
59 | { "AU", "Australia" }, | ||
60 | { "AZ", "Azerbaijan" }, | ||
61 | { "BA", "Bosnia and Herzegovina" }, | ||
62 | { "BD", "Bangladesh" }, | ||
63 | { "BE", "Belgium" }, | ||
64 | { "BG", "Bulgaria" }, | ||
65 | { "BH", "Bahrain" }, | ||
66 | { "BN", "Brunei Darussalam" }, | ||
67 | { "BO", "Bolivia" }, | ||
68 | { "BR", "Brazil" }, | ||
69 | { "BT", "Bhutan" }, | ||
70 | { "BY", "Belarus" }, | ||
71 | { "BZ", "Belize" }, | ||
72 | { "CA", "Canada" }, | ||
73 | { "CG", "Congo" }, | ||
74 | { "CH", "Switzerland" }, | ||
75 | { "CI", "Cote d'Ivoire" }, | ||
76 | { "CL", "Chile" }, | ||
77 | { "CM", "Cameroon" }, | ||
78 | { "CN", "People's Republic of China" }, | ||
79 | { "CO", "Colombia" }, | ||
80 | { "CR", "Costa Rica" }, | ||
81 | { "CS", "Serbia and Montenegro" }, | ||
82 | { "CZ", "Czech Republic" }, | ||
83 | { "DE", "Germany" }, | ||
84 | { "DK", "Denmark" }, | ||
85 | { "DO", "Dominican Republic" }, | ||
86 | { "DZ", "Algeria" }, | ||
87 | { "EC", "Ecuador" }, | ||
88 | { "EE", "Estonia" }, | ||
89 | { "EG", "Egypt" }, | ||
90 | { "ER", "Eritrea" }, | ||
91 | { "ES", "Spain" }, | ||
92 | { "ET", "Ethiopia" }, | ||
93 | { "FI", "Finland" }, | ||
94 | { "FO", "Faroe Islands" }, | ||
95 | { "FR", "France" }, | ||
96 | { "GB", "United Kingdom" }, | ||
97 | { "GD", "Caribbean" }, | ||
98 | { "GE", "Georgia" }, | ||
99 | { "GL", "Greenland" }, | ||
100 | { "GR", "Greece" }, | ||
101 | { "GT", "Guatemala" }, | ||
102 | { "HK", "Hong Kong" }, | ||
103 | { "HK", "Hong Kong S.A.R." }, | ||
104 | { "HN", "Honduras" }, | ||
105 | { "HR", "Croatia" }, | ||
106 | { "HT", "Haiti" }, | ||
107 | { "HU", "Hungary" }, | ||
108 | { "ID", "Indonesia" }, | ||
109 | { "IE", "Ireland" }, | ||
110 | { "IL", "Israel" }, | ||
111 | { "IN", "India" }, | ||
112 | { "IQ", "Iraq" }, | ||
113 | { "IR", "Iran" }, | ||
114 | { "IS", "Iceland" }, | ||
115 | { "IT", "Italy" }, | ||
116 | { "JM", "Jamaica" }, | ||
117 | { "JO", "Jordan" }, | ||
118 | { "JP", "Japan" }, | ||
119 | { "KE", "Kenya" }, | ||
120 | { "KG", "Kyrgyzstan" }, | ||
121 | { "KH", "Cambodia" }, | ||
122 | { "KR", "South Korea" }, | ||
123 | { "KW", "Kuwait" }, | ||
124 | { "KZ", "Kazakhstan" }, | ||
125 | { "LA", "Laos" }, | ||
126 | { "LB", "Lebanon" }, | ||
127 | { "LI", "Liechtenstein" }, | ||
128 | { "LK", "Sri Lanka" }, | ||
129 | { "LT", "Lithuania" }, | ||
130 | { "LU", "Luxembourg" }, | ||
131 | { "LV", "Latvia" }, | ||
132 | { "LY", "Libya" }, | ||
133 | { "MA", "Morocco" }, | ||
134 | { "MC", "Principality of Monaco" }, | ||
135 | { "MD", "Moldava" }, | ||
136 | { "MD", "Moldova" }, | ||
137 | { "ME", "Montenegro" }, | ||
138 | { "MK", "Former Yugoslav Republic of Macedonia" }, | ||
139 | { "ML", "Mali" }, | ||
140 | { "MM", "Myanmar" }, | ||
141 | { "MN", "Mongolia" }, | ||
142 | { "MO", "Macau S.A.R." }, | ||
143 | { "MT", "Malta" }, | ||
144 | { "MV", "Maldives" }, | ||
145 | { "MX", "Mexico" }, | ||
146 | { "MY", "Malaysia" }, | ||
147 | { "NG", "Nigeria" }, | ||
148 | { "NI", "Nicaragua" }, | ||
149 | { "NL", "Netherlands" }, | ||
150 | { "NO", "Norway" }, | ||
151 | { "NP", "Nepal" }, | ||
152 | { "NZ", "New Zealand" }, | ||
153 | { "OM", "Oman" }, | ||
154 | { "PA", "Panama" }, | ||
155 | { "PE", "Peru" }, | ||
156 | { "PH", "Philippines" }, | ||
157 | { "PK", "Islamic Republic of Pakistan" }, | ||
158 | { "PL", "Poland" }, | ||
159 | { "PR", "Puerto Rico" }, | ||
160 | { "PT", "Portugal" }, | ||
161 | { "PY", "Paraguay" }, | ||
162 | { "QA", "Qatar" }, | ||
163 | { "RE", "Reunion" }, | ||
164 | { "RO", "Romania" }, | ||
165 | { "RS", "Serbia" }, | ||
166 | { "RU", "Russia" }, | ||
167 | { "RW", "Rwanda" }, | ||
168 | { "SA", "Saudi Arabia" }, | ||
169 | { "SE", "Sweden" }, | ||
170 | { "SG", "Singapore" }, | ||
171 | { "SI", "Slovenia" }, | ||
172 | { "SK", "Slovak" }, | ||
173 | { "SN", "Senegal" }, | ||
174 | { "SO", "Somalia" }, | ||
175 | { "SR", "Suriname" }, | ||
176 | { "SV", "El Salvador" }, | ||
177 | { "SY", "Syria" }, | ||
178 | { "TH", "Thailand" }, | ||
179 | { "TJ", "Tajikistan" }, | ||
180 | { "TM", "Turkmenistan" }, | ||
181 | { "TN", "Tunisia" }, | ||
182 | { "TR", "Turkey" }, | ||
183 | { "TT", "Trinidad and Tobago" }, | ||
184 | { "TW", "Taiwan" }, | ||
185 | { "TZ", "Tanzania" }, | ||
186 | { "UA", "Ukraine" }, | ||
187 | { "US", "United States" }, | ||
188 | { "UY", "Uruguay" }, | ||
189 | { "VA", "Vatican" }, | ||
190 | { "VE", "Venezuela" }, | ||
191 | { "VN", "Viet Nam" }, | ||
192 | { "YE", "Yemen" }, | ||
193 | { "ZA", "South Africa" }, | ||
194 | { "ZW", "Zimbabwe" } | ||
195 | }; | ||
196 | |||
197 | |||
198 | /** | ||
199 | * Country code comparator function, for binary search with bsearch(). | ||
200 | * | ||
201 | * @param ptr1 pointer to a `struct table_entry` | ||
202 | * @param ptr2 pointer to a `struct table_entry` | ||
203 | * @return result of memcmp()'ing the 2-digit country codes of the entries | ||
204 | */ | ||
205 | static int | ||
206 | cmp_country_code (const void *ptr1, | ||
207 | const void *ptr2) | ||
208 | { | ||
209 | const struct CountryTableEntry *cc1 = ptr1; | ||
210 | const struct CountryTableEntry *cc2 = ptr2; | ||
211 | |||
212 | return memcmp (cc1->code, | ||
213 | cc2->code, | ||
214 | 2); | ||
215 | } | ||
216 | |||
217 | |||
218 | /** | ||
219 | * Validates given IBAN according to the European Banking Standards. See: | ||
220 | * http://www.europeanpaymentscouncil.eu/documents/ECBS%20IBAN%20standard%20EBS204_V3.2.pdf | ||
221 | * | ||
222 | * @param iban the IBAN number to validate | ||
223 | * @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not | ||
224 | */ | ||
225 | static enum GNUNET_GenericReturnValue | ||
226 | validate_iban (const char *iban) | ||
227 | { | ||
228 | char cc[2]; | ||
229 | char ibancpy[35]; | ||
230 | struct CountryTableEntry cc_entry; | ||
231 | unsigned int len; | ||
232 | char *nbuf; | ||
233 | unsigned long long dividend; | ||
234 | unsigned long long remainder; | ||
235 | unsigned int i; | ||
236 | unsigned int j; | ||
237 | |||
238 | len = strlen (iban); | ||
239 | if (len > 34) | ||
240 | { | ||
241 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
242 | "IBAN number too long to be valid\n"); | ||
243 | return GNUNET_NO; | ||
244 | } | ||
245 | memcpy (cc, iban, 2); | ||
246 | memcpy (ibancpy, iban + 4, len - 4); | ||
247 | memcpy (ibancpy + len - 4, iban, 4); | ||
248 | ibancpy[len] = '\0'; | ||
249 | cc_entry.code = cc; | ||
250 | cc_entry.english = NULL; | ||
251 | if (NULL == | ||
252 | bsearch (&cc_entry, | ||
253 | country_table, | ||
254 | sizeof (country_table) / sizeof (struct CountryTableEntry), | ||
255 | sizeof (struct CountryTableEntry), | ||
256 | &cmp_country_code)) | ||
257 | { | ||
258 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
259 | "Country code `%c%c' not supported\n", | ||
260 | cc[0], | ||
261 | cc[1]); | ||
262 | return GNUNET_NO; | ||
263 | } | ||
264 | nbuf = GNUNET_malloc ((len * 2) + 1); | ||
265 | for (i = 0, j = 0; i < len; i++) | ||
266 | { | ||
267 | if (isalpha ((unsigned char) ibancpy[i])) | ||
268 | { | ||
269 | if (2 != snprintf (&nbuf[j], | ||
270 | 3, | ||
271 | "%2u", | ||
272 | (ibancpy[i] - 'A' + 10))) | ||
273 | { | ||
274 | GNUNET_free (nbuf); | ||
275 | return GNUNET_NO; | ||
276 | } | ||
277 | j += 2; | ||
278 | continue; | ||
279 | } | ||
280 | nbuf[j] = ibancpy[i]; | ||
281 | j++; | ||
282 | } | ||
283 | for (j = 0; '\0' != nbuf[j]; j++) | ||
284 | { | ||
285 | if (! isdigit ( (unsigned char) nbuf[j])) | ||
286 | { | ||
287 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
288 | "IBAN `%s' didn't convert to numeric format\n", | ||
289 | iban); | ||
290 | return GNUNET_NO; | ||
291 | } | ||
292 | } | ||
293 | GNUNET_assert (sizeof(dividend) >= 8); | ||
294 | remainder = 0; | ||
295 | for (unsigned int i = 0; i<j; i += 16) | ||
296 | { | ||
297 | int nread; | ||
298 | |||
299 | if (1 != | ||
300 | sscanf (&nbuf[i], | ||
301 | "%16llu %n", | ||
302 | ÷nd, | ||
303 | &nread)) | ||
304 | { | ||
305 | GNUNET_free (nbuf); | ||
306 | GNUNET_break_op (0); | ||
307 | return GNUNET_NO; | ||
308 | } | ||
309 | if (0 != remainder) | ||
310 | dividend += remainder * (pow (10, nread)); | ||
311 | remainder = dividend % 97; | ||
312 | } | ||
313 | GNUNET_free (nbuf); | ||
314 | if (1 != remainder) | ||
315 | { | ||
316 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
317 | "IBAN `%s' has the wrong checksum\n", | ||
318 | iban); | ||
319 | return GNUNET_NO; | ||
320 | } | ||
321 | return GNUNET_YES; | ||
322 | } | ||
323 | |||
324 | |||
325 | /** | ||
326 | * Validate payto://iban/ account URL (only account information, | ||
327 | * wire subject and amount are ignored). | ||
328 | * | ||
329 | * @param account_url URL to parse | ||
330 | * @return #GNUNET_YES if @a account_url is a valid payto://iban URI, | ||
331 | * #GNUNET_NO if @a account_url is a payto URI of a different type, | ||
332 | * #GNUNET_SYSERR if the IBAN (checksum) is incorrect or this is not a payto://-URI | ||
333 | */ | ||
334 | static enum GNUNET_GenericReturnValue | ||
335 | validate_payto_iban (const char *account_url) | ||
336 | { | ||
337 | const char *iban; | ||
338 | const char *q; | ||
339 | char *result; | ||
340 | |||
341 | #define IBAN_PREFIX "payto://iban/" | ||
342 | if (0 != strncasecmp (account_url, | ||
343 | IBAN_PREFIX, | ||
344 | strlen (IBAN_PREFIX))) | ||
345 | return GNUNET_NO; | ||
346 | |||
347 | iban = strrchr (account_url, '/') + 1; | ||
348 | #undef IBAN_PREFIX | ||
349 | q = strchr (iban, | ||
350 | '?'); | ||
351 | if (NULL != q) | ||
352 | { | ||
353 | result = GNUNET_strndup (iban, | ||
354 | q - iban); | ||
355 | } | ||
356 | else | ||
357 | { | ||
358 | result = GNUNET_strdup (iban); | ||
359 | } | ||
360 | if (GNUNET_OK != | ||
361 | validate_iban (result)) | ||
362 | { | ||
363 | GNUNET_free (result); | ||
364 | return GNUNET_SYSERR; | ||
365 | } | ||
366 | GNUNET_free (result); | ||
367 | return GNUNET_YES; | ||
368 | } | ||
369 | |||
370 | |||
371 | enum GNUNET_GenericReturnValue | ||
372 | TALER_JSON_validate_payto (const char *payto_uri) | ||
373 | { | ||
374 | enum GNUNET_GenericReturnValue ret; | ||
375 | const char *start; | ||
376 | const char *end; | ||
377 | |||
378 | #define PAYTO_PREFIX "payto://" | ||
379 | if (0 != strncasecmp (payto_uri, | ||
380 | PAYTO_PREFIX, | ||
381 | strlen (PAYTO_PREFIX))) | ||
382 | return GNUNET_SYSERR; /* not payto */ | ||
383 | start = &payto_uri[strlen (PAYTO_PREFIX)]; | ||
384 | #undef PAYTO_PREFIX | ||
385 | end = strchr (start, | ||
386 | (unsigned char) '/'); | ||
387 | if (NULL == end) | ||
388 | return GNUNET_SYSERR; | ||
389 | if (GNUNET_NO != (ret = validate_payto_iban (payto_uri))) | ||
390 | { | ||
391 | GNUNET_break_op (GNUNET_SYSERR != ret); | ||
392 | return ret; /* got a definitive answer */ | ||
393 | } | ||
394 | /* Insert other bank account validation methods here later! */ | ||
395 | return GNUNET_NO; | ||
396 | } | ||
397 | |||
398 | |||
399 | /** | 27 | /** |
400 | * Compute the hash of the given wire details. The resulting | 28 | * Compute the hash of the given wire details. The resulting |
401 | * hash is what is put into the contract. | 29 | * hash is what is put into the contract. |
@@ -429,11 +57,19 @@ TALER_JSON_merchant_wire_signature_hash (const json_t *wire_s, | |||
429 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 57 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
430 | "Validating `%s'\n", | 58 | "Validating `%s'\n", |
431 | payto_uri); | 59 | payto_uri); |
432 | if (GNUNET_SYSERR == | ||
433 | TALER_JSON_validate_payto (payto_uri)) | ||
434 | { | 60 | { |
435 | GNUNET_break_op (0); | 61 | char *err; |
436 | return GNUNET_SYSERR; | 62 | |
63 | err = TALER_payto_validate (payto_uri); | ||
64 | if (NULL != err) | ||
65 | { | ||
66 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
67 | "URI `%s' ill-formed: %s\n", | ||
68 | payto_uri, | ||
69 | err); | ||
70 | GNUNET_free (err); | ||
71 | return GNUNET_SYSERR; | ||
72 | } | ||
437 | } | 73 | } |
438 | TALER_merchant_wire_signature_hash (payto_uri, | 74 | TALER_merchant_wire_signature_hash (payto_uri, |
439 | salt, | 75 | salt, |
@@ -472,11 +108,19 @@ TALER_JSON_exchange_wire_signature_check ( | |||
472 | return GNUNET_SYSERR; | 108 | return GNUNET_SYSERR; |
473 | } | 109 | } |
474 | 110 | ||
475 | if (GNUNET_SYSERR == | ||
476 | TALER_JSON_validate_payto (payto_uri)) | ||
477 | { | 111 | { |
478 | GNUNET_break_op (0); | 112 | char *err; |
479 | return GNUNET_SYSERR; | 113 | |
114 | err = TALER_payto_validate (payto_uri); | ||
115 | if (NULL != err) | ||
116 | { | ||
117 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
118 | "URI `%s' ill-formed: %s\n", | ||
119 | payto_uri, | ||
120 | err); | ||
121 | GNUNET_free (err); | ||
122 | return GNUNET_SYSERR; | ||
123 | } | ||
480 | } | 124 | } |
481 | 125 | ||
482 | return TALER_exchange_wire_signature_check (payto_uri, | 126 | return TALER_exchange_wire_signature_check (payto_uri, |
@@ -498,11 +142,16 @@ TALER_JSON_exchange_wire_signature_make ( | |||
498 | const struct TALER_MasterPrivateKeyP *master_priv) | 142 | const struct TALER_MasterPrivateKeyP *master_priv) |
499 | { | 143 | { |
500 | struct TALER_MasterSignatureP master_sig; | 144 | struct TALER_MasterSignatureP master_sig; |
145 | char *err; | ||
501 | 146 | ||
502 | if (GNUNET_SYSERR == | 147 | if (NULL != |
503 | TALER_JSON_validate_payto (payto_uri)) | 148 | (err = TALER_payto_validate (payto_uri))) |
504 | { | 149 | { |
505 | GNUNET_break_op (0); | 150 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |
151 | "Invalid payto URI `%s': %s\n", | ||
152 | payto_uri, | ||
153 | err); | ||
154 | GNUNET_free (err); | ||
506 | return NULL; | 155 | return NULL; |
507 | } | 156 | } |
508 | TALER_exchange_wire_signature_make (payto_uri, | 157 | TALER_exchange_wire_signature_make (payto_uri, |
@@ -528,6 +177,7 @@ TALER_JSON_wire_to_payto (const json_t *wire_s) | |||
528 | { | 177 | { |
529 | json_t *payto_o; | 178 | json_t *payto_o; |
530 | const char *payto_str; | 179 | const char *payto_str; |
180 | char *err; | ||
531 | 181 | ||
532 | payto_o = json_object_get (wire_s, | 182 | payto_o = json_object_get (wire_s, |
533 | "payto_uri"); | 183 | "payto_uri"); |
@@ -538,12 +188,14 @@ TALER_JSON_wire_to_payto (const json_t *wire_s) | |||
538 | "Malformed wire record encountered: lacks payto://-url\n"); | 188 | "Malformed wire record encountered: lacks payto://-url\n"); |
539 | return NULL; | 189 | return NULL; |
540 | } | 190 | } |
541 | if (GNUNET_SYSERR == | 191 | if (NULL != |
542 | TALER_JSON_validate_payto (payto_str)) | 192 | (err = TALER_payto_validate (payto_str))) |
543 | { | 193 | { |
544 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 194 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |
545 | "Malformed wire record encountered: payto URI `%s' invalid\n", | 195 | "Malformed wire record encountered: payto URI `%s' invalid: %s\n", |
546 | payto_str); | 196 | payto_str, |
197 | err); | ||
198 | GNUNET_free (err); | ||
547 | return NULL; | 199 | return NULL; |
548 | } | 200 | } |
549 | return GNUNET_strdup (payto_str); | 201 | return GNUNET_strdup (payto_str); |