~ubuntu-branches/ubuntu/edgy/lasso/edgy

« back to all changes in this revision

Viewing changes to lasso/xml/tools.c

  • Committer: Bazaar Package Importer
  • Author(s): Frederic Peters
  • Date: 2004-09-13 09:26:34 UTC
  • Revision ID: james.westby@ubuntu.com-20040913092634-01vdfl8j9cp94exa
Tags: upstream-0.4.1
ImportĀ upstreamĀ versionĀ 0.4.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: tools.c,v 1.34 2004/09/01 09:59:53 fpeters Exp $ 
 
2
 *
 
3
 * Lasso - A free implementation of the Liberty Alliance specifications.
 
4
 *
 
5
 * Copyright (C) 2004 Entr'ouvert
 
6
 * http://lasso.entrouvert.org
 
7
 * 
 
8
 * Authors: Nicolas Clapies <nclapies@entrouvert.com>
 
9
 *          Valery Febvre <vfebvre@easter-eggs.com>
 
10
 *
 
11
 * This program is free software; you can redistribute it and/or modify
 
12
 * it under the terms of the GNU General Public License as published by
 
13
 * the Free Software Foundation; either version 2 of the License, or
 
14
 * (at your option) any later version.
 
15
 * 
 
16
 * This program is distributed in the hope that it will be useful,
 
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
19
 * GNU General Public License for more details.
 
20
 * 
 
21
 * You should have received a copy of the GNU General Public License
 
22
 * along with this program; if not, write to the Free Software
 
23
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
24
 */
 
25
 
 
26
#include <string.h>
 
27
 
 
28
#include <libxml/uri.h>
 
29
 
 
30
#include <openssl/sha.h>
 
31
 
 
32
#include <xmlsec/xmltree.h>
 
33
#include <xmlsec/base64.h>
 
34
#include <xmlsec/xmldsig.h>
 
35
#include <xmlsec/templates.h>
 
36
#include <xmlsec/crypto.h>
 
37
 
 
38
#include <lasso/xml/tools.h>
 
39
 
 
40
xmlChar *
 
41
lasso_build_random_sequence(guint8 size)
 
42
{
 
43
  int i, val;
 
44
  xmlChar *seq;
 
45
 
 
46
  g_return_val_if_fail(size > 0, NULL);
 
47
 
 
48
  seq = xmlMalloc(size+1);
 
49
 
 
50
  for (i=0; i<size; i++) {
 
51
    val = g_random_int_range(0, 16);
 
52
    if (val < 10)
 
53
      seq[i] = 48 + val;
 
54
    else
 
55
      seq[i] = 65 + val-10;
 
56
  }
 
57
  seq[size] = '\0';
 
58
 
 
59
  return seq;
 
60
}
 
61
 
 
62
/**
 
63
 * lasso_build_unique_id:
 
64
 * @size: the ID's length (between 32 and 40)
 
65
 * 
 
66
 * Builds an ID which has an unicity probability of 2^(-size*4).
 
67
 * 
 
68
 * Return value: a "unique" ID (begin always with _ character)
 
69
 **/
 
70
xmlChar *
 
71
lasso_build_unique_id(guint8 size)
 
72
{
 
73
  /*
 
74
    The probability of 2 randomly chosen identifiers being identical MUST be
 
75
    less than 2^-128 and SHOULD be less than 2^-160.
 
76
    so we must have 128 <= exp <= 160
 
77
    we could build a 128-bit binary number but hexa system is shorter
 
78
    32 <= hexa number size <= 40
 
79
  */
 
80
  int i, val;
 
81
  xmlChar *id; /* , *enc_id; */
 
82
 
 
83
  g_return_val_if_fail((size >= 32 && size <= 40) || size == 0, NULL);
 
84
 
 
85
  if (size == 0) size = 32;
 
86
  id = g_malloc(size+1+1); /* one for _ and one for \0 */
 
87
 
 
88
  /* build hex number (<= 2^exp-1) */
 
89
  id[0] = '_';
 
90
  for (i=1; i<size+1; i++) {
 
91
    val = g_random_int_range(0, 16);
 
92
    if (val < 10)
 
93
      id[i] = 48 + val;
 
94
    else
 
95
      id[i] = 65 + val-10;
 
96
  }
 
97
  id[size+1] = '\0';
 
98
 
 
99
  /* base64 encoding of build string */
 
100
  /* enc_id = xmlSecBase64Encode((const xmlChar *)id, size+1, 0); */
 
101
 
 
102
  /* g_free(id); */
 
103
  /* return (enc_id); */
 
104
  return id;
 
105
}
 
106
 
 
107
/**
 
108
 * lasso_doc_get_node_content:
 
109
 * @doc: a doc
 
110
 * @name: the name
 
111
 * 
 
112
 * Gets the value of the first node having given @name.
 
113
 * 
 
114
 * Return value: a node value or NULL if no node found or if no content is
 
115
 * available
 
116
 **/
 
117
xmlChar *
 
118
lasso_doc_get_node_content(xmlDocPtr doc, const xmlChar *name)
 
119
{
 
120
  xmlNodePtr node;
 
121
 
 
122
  /* FIXME: bad namespace used */
 
123
  node = xmlSecFindNode(xmlDocGetRootElement(doc), name, xmlSecDSigNs);
 
124
  if (node != NULL)
 
125
    /* val returned must be xmlFree() */
 
126
    return xmlNodeGetContent(node);
 
127
  else
 
128
    return NULL;
 
129
}
 
130
 
 
131
/**
 
132
 * lasso_g_ptr_array_index:
 
133
 * @a: a GPtrArray
 
134
 * @i: the index
 
135
 * 
 
136
 * Gets the pointer at the given index @i of the pointer array.
 
137
 * 
 
138
 * Return value: the pointer at the given index.
 
139
 **/
 
140
xmlChar*
 
141
lasso_g_ptr_array_index(GPtrArray *a, guint i)
 
142
{
 
143
  if (a != NULL) {
 
144
    return g_ptr_array_index(a, i);
 
145
  }
 
146
  else {
 
147
    return NULL;
 
148
  }
 
149
}
 
150
 
 
151
/**
 
152
 * lasso_get_current_time:
 
153
 * 
 
154
 * Returns the current time, format is "yyyy-mm-ddThh:mm:ssZ".
 
155
 * 
 
156
 * Return value: a string
 
157
 **/
 
158
gchar *
 
159
lasso_get_current_time()
 
160
{
 
161
  struct tm *tm;
 
162
  GTimeVal time_val;
 
163
  gchar *ret = g_new0(gchar, 21);
 
164
 
 
165
  g_get_current_time(&time_val);
 
166
  tm = localtime(&(time_val.tv_sec));
 
167
  strftime((char *)ret, 21, "%Y-%m-%dT%H:%M:%SZ", tm);
 
168
 
 
169
  return ret;
 
170
}
 
171
 
 
172
/**
 
173
 * lasso_query_get_value:
 
174
 * @query: a query (an url-encoded node)
 
175
 * @param: the parameter
 
176
 * 
 
177
 * Returns the value of the given @param
 
178
 * 
 
179
 * Return value: a string or NULL if no parameter found
 
180
 **/
 
181
GPtrArray *
 
182
lasso_query_get_value(const gchar   *query,
 
183
                      const xmlChar *param)
 
184
{
 
185
  guint i;
 
186
  GData *gd;
 
187
  GPtrArray *tmp_array, *array = NULL;
 
188
 
 
189
  gd = lasso_query_to_dict(query);
 
190
  tmp_array = (GPtrArray *)g_datalist_get_data(&gd, (gchar *)param);
 
191
  /* create a copy of tmp_array */
 
192
  if (tmp_array != NULL) {
 
193
    array = g_ptr_array_new();
 
194
    for(i=0; i<tmp_array->len; i++)
 
195
      g_ptr_array_add(array, g_strdup(g_ptr_array_index(tmp_array, i)));
 
196
  }
 
197
  g_datalist_clear(&gd);
 
198
  return array;
 
199
}
 
200
 
 
201
static void
 
202
gdata_query_to_dict_destroy_notify(gpointer data)
 
203
{
 
204
  guint i;
 
205
  GPtrArray *array = data;
 
206
 
 
207
  for (i=0; i<array->len; i++) {
 
208
    g_free(array->pdata[i]);
 
209
  }
 
210
  g_ptr_array_free(array, TRUE);
 
211
}
 
212
 
 
213
/**
 
214
 * lasso_query_to_dict:
 
215
 * @query: the query (an url-encoded node)
 
216
 * 
 
217
 * Explodes query to build a dictonary.
 
218
 * Dictionary values are stored in GPtrArray.
 
219
 * The caller is responsible for freeing returned object by calling
 
220
 * g_datalist_clear() function.
 
221
 *
 
222
 * Return value: a dictonary
 
223
 **/
 
224
GData *
 
225
lasso_query_to_dict(const gchar *query)
 
226
{
 
227
  GData *gd = NULL;
 
228
  gchar **sa1, **sa2, **sa3;
 
229
  xmlChar *str_unescaped;
 
230
  GPtrArray *gpa;
 
231
  guint i, j;
 
232
  
 
233
  g_datalist_init(&gd);
 
234
  
 
235
  i = 0;
 
236
  sa1 = g_strsplit(query, "&", 0);
 
237
 
 
238
  while (sa1[i++] != NULL) {
 
239
    /* split of key=value to get (key, value) sub-strings */
 
240
    sa2 = g_strsplit(sa1[i-1], "=", 0);
 
241
    /* if no key / value found, then continue */
 
242
    if (sa2 == NULL) {
 
243
      continue;
 
244
    }
 
245
    /* if only a key but no value, then continue */
 
246
    if (sa2[1] == NULL) {
 
247
      continue;
 
248
    }
 
249
 
 
250
    /* printf("%s => ", sa2[0]); */
 
251
    /* split of value to get mutli values sub-strings separated by SPACE char */
 
252
    str_unescaped = lasso_str_unescape(sa2[1]);
 
253
    sa3 = g_strsplit(str_unescaped, " ", 0);
 
254
    if (sa3 == NULL) {
 
255
      g_strfreev(sa2);
 
256
      continue;
 
257
    }
 
258
 
 
259
    xmlFree(str_unescaped);
 
260
    gpa = g_ptr_array_new();
 
261
    j = 0;
 
262
    while (sa3[j++] != NULL) {
 
263
      g_ptr_array_add(gpa, g_strdup(sa3[j-1]));
 
264
      /* printf("%s, ", sa3[j-1]); */
 
265
    }
 
266
    /* printf("\n"); */
 
267
    /* add key => values in dict */
 
268
    g_datalist_set_data_full(&gd, sa2[0], gpa,
 
269
                             gdata_query_to_dict_destroy_notify);
 
270
    g_strfreev(sa3);
 
271
    g_strfreev(sa2);
 
272
  }  
 
273
  g_strfreev(sa1);
 
274
 
 
275
  return gd;
 
276
}
 
277
 
 
278
/**
 
279
 * lasso_query_verify_signature:
 
280
 * @query: a query  (an url-encoded and signed node)
 
281
 * @sender_public_key_file: the sender public key
 
282
 * @recipient_private_key_file: the recipient private key
 
283
 * 
 
284
 * Verifys the query's signature.
 
285
 * 
 
286
 * Return value: 1 if signature is valid, 0 if invalid, 2 if no signature found
 
287
 * and -1 if an error occurs.
 
288
 **/
 
289
int
 
290
lasso_query_verify_signature(const gchar   *query,
 
291
                             const xmlChar *sender_public_key_file,
 
292
                             const xmlChar *recipient_private_key_file)
 
293
{
 
294
  xmlDocPtr doc;
 
295
  xmlNodePtr sigNode, sigValNode;
 
296
  xmlSecDSigCtxPtr dsigCtx;
 
297
  xmlChar *str_unescaped;
 
298
  gchar **str_split;
 
299
  /*
 
300
     0: signature invalid
 
301
     1: signature ok
 
302
     2: signature not found
 
303
    -1: error during verification
 
304
  */
 
305
  gint ret = -1;
 
306
 
 
307
  /* split query, signature (must be last param) */
 
308
  str_split = g_strsplit(query, "&Signature=", 0);
 
309
  if (str_split[1] == NULL)
 
310
    return 2;
 
311
  /* re-create doc to verify (signed + enrypted) */
 
312
  doc = lasso_str_sign(str_split[0],
 
313
                       lassoSignatureMethodRsaSha1,
 
314
                       recipient_private_key_file);
 
315
  sigValNode = xmlSecFindNode(xmlDocGetRootElement(doc),
 
316
                                          xmlSecNodeSignatureValue,
 
317
                                          xmlSecDSigNs);
 
318
  /* set SignatureValue content */
 
319
  str_unescaped = lasso_str_unescape(str_split[1]);
 
320
  xmlNodeSetContent(sigValNode, str_unescaped);
 
321
  g_free(str_unescaped);
 
322
 
 
323
  g_strfreev(str_split);
 
324
  /*xmlDocDump(stdout, doc);*/
 
325
 
 
326
  /* find start node */
 
327
  sigNode = xmlSecFindNode(xmlDocGetRootElement(doc),
 
328
                           xmlSecNodeSignature, xmlSecDSigNs);
 
329
  
 
330
  /* create signature context */
 
331
  dsigCtx = xmlSecDSigCtxCreate(NULL);
 
332
  if(dsigCtx == NULL) {
 
333
    message(G_LOG_LEVEL_CRITICAL, "Failed to create signature context\n");
 
334
    goto done;
 
335
  }
 
336
  
 
337
  /* load public key */
 
338
  dsigCtx->signKey = xmlSecCryptoAppKeyLoad(sender_public_key_file,
 
339
                                            xmlSecKeyDataFormatPem,
 
340
                                            NULL, NULL, NULL);
 
341
  if(dsigCtx->signKey == NULL) {
 
342
    message(G_LOG_LEVEL_CRITICAL, "Failed to load public pem key from \"%s\"\n",
 
343
            sender_public_key_file);
 
344
    goto done;
 
345
  }
 
346
  
 
347
  /* Verify signature */
 
348
  if(xmlSecDSigCtxVerify(dsigCtx, sigNode) < 0) {
 
349
    message(G_LOG_LEVEL_CRITICAL, "Signature verify failed\n");
 
350
    ret = 0;
 
351
    goto done;
 
352
  }
 
353
  
 
354
  /* print verification result to stdout and return */
 
355
  if(dsigCtx->status == xmlSecDSigStatusSucceeded) {
 
356
    ret = 1;
 
357
  }
 
358
  else {
 
359
    ret = 0;
 
360
  }
 
361
  
 
362
 done:
 
363
  /* cleanup */
 
364
  if(dsigCtx != NULL) {
 
365
    xmlSecDSigCtxDestroy(dsigCtx);
 
366
  }
 
367
  
 
368
  if(doc != NULL) {
 
369
    xmlFreeDoc(doc);
 
370
  }
 
371
  return ret;
 
372
}
 
373
 
 
374
/**
 
375
 * lasso_sha1:
 
376
 * @str: a string
 
377
 * 
 
378
 * Builds the SHA-1 message digest (cryptographic hash) of @str
 
379
 * 
 
380
 * Return value: a 20 bytes length string
 
381
 **/
 
382
xmlChar*
 
383
lasso_sha1(xmlChar *str)
 
384
{
 
385
  unsigned char *md;
 
386
 
 
387
  if (str != NULL) {
 
388
    md = xmlMalloc(20);
 
389
    return SHA1(str, strlen(str), md);
 
390
  }
 
391
  
 
392
  return NULL;
 
393
}
 
394
 
 
395
/**
 
396
 * lasso_str_escape:
 
397
 * @str: a string
 
398
 * 
 
399
 * Escapes the given string @str.
 
400
 * 
 
401
 * Return value: a new escaped string or NULL in case of error.
 
402
 **/
 
403
xmlChar *
 
404
lasso_str_escape(xmlChar *str)
 
405
{
 
406
  /* value returned must be xmlFree() */
 
407
  return xmlURIEscapeStr((const xmlChar *)str, NULL);
 
408
}
 
409
 
 
410
xmlChar *
 
411
lasso_str_hash(xmlChar    *str,
 
412
               const char *private_key_file)
 
413
{
 
414
  xmlDocPtr doc;
 
415
  xmlChar *b64_digest, *digest = g_new0(xmlChar, 21);
 
416
  gint i;
 
417
 
 
418
  doc = lasso_str_sign(str,
 
419
                       lassoSignatureMethodRsaSha1,
 
420
                       private_key_file);
 
421
  b64_digest = xmlNodeGetContent(xmlSecFindNode(
 
422
                                        xmlDocGetRootElement(doc),
 
423
                                        xmlSecNodeDigestValue,
 
424
                                        xmlSecDSigNs));
 
425
  i = xmlSecBase64Decode(b64_digest, digest, 21);
 
426
  /* printf("Decoded string %s length is %d\n", digest, i); */
 
427
  xmlFree(b64_digest);
 
428
  xmlFreeDoc(doc);
 
429
  /* value returned must be xmlFree() */
 
430
  return digest;
 
431
}
 
432
 
 
433
/**
 
434
 * lasso_str_sign:
 
435
 * @str: 
 
436
 * @sign_method: 
 
437
 * @private_key_file: 
 
438
 * 
 
439
 * 
 
440
 * 
 
441
 * Return value: 
 
442
 **/
 
443
xmlDocPtr
 
444
lasso_str_sign(xmlChar              *str,
 
445
               lassoSignatureMethod  sign_method,
 
446
               const char           *private_key_file)
 
447
{
 
448
  /* FIXME : renamed fct into lasso_query_add_signature
 
449
     SHOULD returned a query (xmlChar) instead of xmlDoc */
 
450
  xmlDocPtr  doc = xmlNewDoc("1.0");
 
451
  xmlNodePtr envelope = xmlNewNode(NULL, "Envelope");
 
452
  xmlNodePtr cdata, data = xmlNewNode(NULL, "Data");
 
453
  xmlNodePtr signNode = NULL;
 
454
  xmlNodePtr refNode = NULL;
 
455
  xmlNodePtr keyInfoNode = NULL;
 
456
  xmlSecDSigCtxPtr dsigCtx = NULL;
 
457
 
 
458
  /* create doc */
 
459
  xmlNewNs(envelope, "urn:envelope", NULL);
 
460
  cdata = xmlNewCDataBlock(doc, str, strlen(str));
 
461
  xmlAddChild(envelope, data);
 
462
  xmlAddChild(data, cdata);
 
463
  xmlAddChild((xmlNodePtr)doc, envelope);
 
464
 
 
465
  /* create signature template for enveloped signature */
 
466
  switch (sign_method) {
 
467
  case lassoSignatureMethodRsaSha1:
 
468
    signNode = xmlSecTmplSignatureCreate(doc, xmlSecTransformExclC14NId,
 
469
                                         xmlSecTransformRsaSha1Id, NULL);
 
470
    break;
 
471
  case lassoSignatureMethodDsaSha1:
 
472
    signNode = xmlSecTmplSignatureCreate(doc, xmlSecTransformExclC14NId,
 
473
                                         xmlSecTransformDsaSha1Id, NULL);
 
474
    break;
 
475
  }
 
476
 
 
477
  if (signNode == NULL) {
 
478
    message(G_LOG_LEVEL_CRITICAL, "Failed to create signature template\n");
 
479
    goto done;          
 
480
  }
 
481
  
 
482
  /* add <dsig:Signature/> node to the doc */
 
483
  xmlAddChild(xmlDocGetRootElement(doc), signNode);
 
484
  
 
485
  /* add reference */
 
486
  refNode = xmlSecTmplSignatureAddReference(signNode, xmlSecTransformSha1Id,
 
487
                                            NULL, NULL, NULL);
 
488
  if (refNode == NULL) {
 
489
    message(G_LOG_LEVEL_CRITICAL, "Failed to add reference to signature template\n");
 
490
    goto done;          
 
491
  }
 
492
  
 
493
  /* add enveloped transform */
 
494
  if (xmlSecTmplReferenceAddTransform(refNode,
 
495
                                      xmlSecTransformEnvelopedId) == NULL) {
 
496
    message(G_LOG_LEVEL_CRITICAL, "Failed to add enveloped transform to reference\n");
 
497
    goto done;          
 
498
  }
 
499
  
 
500
  /* add <dsig:KeyInfo/> and <dsig:KeyName/> nodes to put key name in the
 
501
     signed document */
 
502
  keyInfoNode = xmlSecTmplSignatureEnsureKeyInfo(signNode, NULL);
 
503
  if (keyInfoNode == NULL) {
 
504
    message(G_LOG_LEVEL_CRITICAL, "Failed to add key info\n");
 
505
    goto done;          
 
506
  }
 
507
  
 
508
  if (xmlSecTmplKeyInfoAddKeyName(keyInfoNode, NULL) == NULL) {
 
509
    message(G_LOG_LEVEL_CRITICAL, "Failed to add key name\n");
 
510
    goto done;          
 
511
  }
 
512
  
 
513
  /* create signature context */
 
514
  dsigCtx = xmlSecDSigCtxCreate(NULL);
 
515
  if (dsigCtx == NULL) {
 
516
    message(G_LOG_LEVEL_CRITICAL, "Failed to create signature context\n");
 
517
    goto done;
 
518
  }
 
519
 
 
520
  /* load private key */
 
521
  dsigCtx->signKey = xmlSecCryptoAppKeyLoad(private_key_file,
 
522
                                            xmlSecKeyDataFormatPem,
 
523
                                            NULL, NULL, NULL);
 
524
  if (dsigCtx->signKey == NULL) {
 
525
    message(G_LOG_LEVEL_CRITICAL, "Failed to load private pem key from \"%s\"\n",
 
526
            private_key_file);
 
527
    goto done;
 
528
  }
 
529
 
 
530
  /* sign the template */
 
531
  if (xmlSecDSigCtxSign(dsigCtx, signNode) < 0) {
 
532
    message(G_LOG_LEVEL_CRITICAL, "Signature failed\n");
 
533
    goto done;
 
534
  }
 
535
  
 
536
  /* xmlDocDump(stdout, doc); */
 
537
  xmlSecDSigCtxDestroy(dsigCtx);
 
538
  /* doc must be freed be caller */
 
539
  return doc;
 
540
 
 
541
 done:    
 
542
  /* cleanup */
 
543
  if (dsigCtx != NULL) {
 
544
    xmlSecDSigCtxDestroy(dsigCtx);
 
545
  }
 
546
  
 
547
  if (doc != NULL) {
 
548
    xmlFreeDoc(doc); 
 
549
  }
 
550
  return NULL;
 
551
}
 
552
 
 
553
/**
 
554
 * lasso_str_unescape:
 
555
 * @str: an escaped string
 
556
 * 
 
557
 * Unescapes the given string @str.
 
558
 * 
 
559
 * Return value: a new unescaped string or NULL in case of error.
 
560
 **/
 
561
xmlChar *
 
562
lasso_str_unescape(xmlChar *str)
 
563
{
 
564
  xmlChar *ret;
 
565
 
 
566
  ret = g_malloc(strlen(str) * 2); /* XXX why *2?  strlen(str) should be enough */
 
567
  xmlURIUnescapeString((const char *)str, 0, ret);
 
568
  return ret;
 
569
}