guardtime 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +5 -2
- data/README.rdoc +55 -0
- data/ext/guardtime.c +165 -171
- data/test/tc_guardtime.rb +91 -91
- metadata +10 -6
data/ChangeLog
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
-
Release 0.0.
|
1
|
+
Release 0.0.3 (2013-01-07 by risto):
|
2
|
+
- Documentation improvements, supports both RDoc and yard.
|
3
|
+
|
4
|
+
Release 0.0.2 (2013-01-05 by risto):
|
2
5
|
- added RDoc
|
3
6
|
- new function 'extend'
|
4
7
|
- minor bug: double free when 'extended' signature is verified
|
5
8
|
- GT library init done at 'require' time instead of instantiation time.
|
6
9
|
|
7
|
-
Release 0.0.1 (2013-01-
|
10
|
+
Release 0.0.1 (2013-01-02 by risto):
|
8
11
|
- initial verion
|
data/README.rdoc
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
== About
|
2
|
+
|
3
|
+
Ruby API for accessing GuardTime Keyless Signature services.
|
4
|
+
|
5
|
+
== Links
|
6
|
+
|
7
|
+
API docs: http://rubydoc.info/gems/guardtime/
|
8
|
+
|
9
|
+
Information about the service: http://www.guardtime.com/signatures/technology-overview/
|
10
|
+
|
11
|
+
Installation: see INSTALL
|
12
|
+
|
13
|
+
== Background
|
14
|
+
|
15
|
+
The two main operations are signing and verification. Signing involves cryptographically binding the data whose integrity is to be protected to the audited time value and server component's identity in a way that neither the data, the time value nor identity could later be changed undetectably. Verification means checking that the binding is still intact.
|
16
|
+
|
17
|
+
To get a sigature token from a GuardTime service, the client application computes a hash value of the data to be signed and submits the hash value to the signing service. The service returns a signature token binding the hash value to the current time. The interaction follows the standard cryptographic time-stamping protocol defined in the RFC 3161.
|
18
|
+
|
19
|
+
Every month, GuardTime creates an Integrity Code and publishes it in several newspapers across the world. The Integrity Code is a summary of all signatures issued by the GuardTime data integrity services. Any signature issued prior to the creation of the Integrity Code can be traced to it to unambiguously prove the signature’s issuing time, issuer ID and link to signed data, effectively proving the data integrity.
|
20
|
+
|
21
|
+
The proof connecting the signature to the Integrity Code can be inserted back into the token in a process we call extending. An extended signature token is completely independent of GuardTime. Only the original data, the signature and any copy of the newspaper where the Integrity Code is published will be necessary to prove the intgerity of the document.
|
22
|
+
|
23
|
+
For convenience, GuardTime publishes an electronic archive containing all Integrity Codes since the launch of the service and all valid signing keys. This electronic archive can be used to automate mass verification of thousands of timestamps (whether extended or not) per second in a large archive.
|
24
|
+
|
25
|
+
== Usage
|
26
|
+
|
27
|
+
Sign:
|
28
|
+
|
29
|
+
require 'guardtime'
|
30
|
+
require 'digest/sha2'
|
31
|
+
|
32
|
+
h = Digest::SHA2.new << 'This text shall be signed!'
|
33
|
+
gt = GuardTime.new
|
34
|
+
sig = gt.sign(h)
|
35
|
+
# ...and verify right away
|
36
|
+
puts 'OK!' if gt.verify(sig, h)
|
37
|
+
|
38
|
+
Verify:
|
39
|
+
|
40
|
+
def slurpfile (fn)
|
41
|
+
File.open(fn, 'rb') {|f| f.read}
|
42
|
+
end
|
43
|
+
|
44
|
+
token = slurpfile('importantdata.txt.gtts')
|
45
|
+
hasher = GuardTime.getnewdigester(token)
|
46
|
+
hasher << slurpfile('importantdata.txt')
|
47
|
+
gt = GuardTime.new
|
48
|
+
signedAt = nil
|
49
|
+
okay = gt.verify(token, hasher) do |r|
|
50
|
+
signedAt = r[:time]
|
51
|
+
/company$/ =~ r[:location_name] and
|
52
|
+
r[:verification_errors] == GuardTime::NO_FAILURES
|
53
|
+
end
|
54
|
+
puts "data signed at #{signedAt.utc.to_s}" if okay
|
55
|
+
|
data/ext/guardtime.c
CHANGED
@@ -36,7 +36,7 @@ typedef struct _GuardTimeData {
|
|
36
36
|
char * loadpubs;
|
37
37
|
time_t pubdataupdated;
|
38
38
|
GT_Time_t64 lastpublicationtime;
|
39
|
-
|
39
|
+
GTPublicationsFile *pub;
|
40
40
|
} GuardTimeData;
|
41
41
|
|
42
42
|
|
@@ -180,7 +180,6 @@ cleanup:
|
|
180
180
|
return res;
|
181
181
|
}
|
182
182
|
|
183
|
-
|
184
183
|
static void get_gtdatahash(VALUE digest, GTDataHash *dh)
|
185
184
|
{
|
186
185
|
int gtalgoid;
|
@@ -189,17 +188,17 @@ static void get_gtdatahash(VALUE digest, GTDataHash *dh)
|
|
189
188
|
int bitlen = 8 * NUM2INT(rb_funcall(digest, rb_intern("digest_length"), 0));
|
190
189
|
|
191
190
|
gtalgoid = (
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
191
|
+
strcasecmp(cn, "Digest::SHA1") == 0 ? GT_HASHALG_SHA1 :
|
192
|
+
strcasecmp(cn, "Digest::SHA2") == 0 ?
|
193
|
+
(bitlen == 224 ? GT_HASHALG_SHA224 :
|
194
|
+
bitlen == 256 ? GT_HASHALG_SHA256 :
|
195
|
+
bitlen == 384 ? GT_HASHALG_SHA384 :
|
196
|
+
bitlen == 512 ? GT_HASHALG_SHA512 : -1
|
197
|
+
):
|
198
|
+
strcasecmp(cn, "Digest::RMD160") == 0 ? GT_HASHALG_RIPEMD160 : -1
|
199
|
+
);
|
201
200
|
if (gtalgoid < 0)
|
202
|
-
rb_raise(
|
201
|
+
rb_raise(rb_eArgError, "Argument must be supported Digest::... instance.");
|
203
202
|
|
204
203
|
dh->context = NULL;
|
205
204
|
dh->algorithm = gtalgoid;
|
@@ -213,15 +212,15 @@ static void get_gtdatahash2(VALUE algo, VALUE digest, GTDataHash *dh)
|
|
213
212
|
StringValue(algo);
|
214
213
|
StringValue(digest);
|
215
214
|
gtalgoid = (
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
215
|
+
strcasecmp(RSTRING_PTR(algo), "sha1") == 0 ? GT_HASHALG_SHA1 :
|
216
|
+
strcasecmp(RSTRING_PTR(algo), "sha224") == 0 ? GT_HASHALG_SHA224 :
|
217
|
+
strcasecmp(RSTRING_PTR(algo), "sha256") == 0 ? GT_HASHALG_SHA256 :
|
218
|
+
strcasecmp(RSTRING_PTR(algo), "sha384") == 0 ? GT_HASHALG_SHA384 :
|
219
|
+
strcasecmp(RSTRING_PTR(algo), "sha512") == 0 ? GT_HASHALG_SHA512 :
|
220
|
+
strcasecmp(RSTRING_PTR(algo), "ripemd160") == 0 ? GT_HASHALG_RIPEMD160 :
|
221
|
+
-1);
|
223
222
|
if (gtalgoid < 0)
|
224
|
-
rb_raise(
|
223
|
+
rb_raise(rb_eArgError, "Argument must be supported Digest::... instance.");
|
225
224
|
|
226
225
|
dh->context = NULL;
|
227
226
|
dh->algorithm = gtalgoid;
|
@@ -252,8 +251,8 @@ guardtime_sign(int argc, VALUE *argv, VALUE obj)
|
|
252
251
|
GTDataHash dh;
|
253
252
|
GTTimestamp *ts;
|
254
253
|
unsigned char *data;
|
255
|
-
|
256
|
-
|
254
|
+
size_t data_length;
|
255
|
+
GuardTimeData *gt;
|
257
256
|
VALUE hash, hash2, result;
|
258
257
|
|
259
258
|
switch (rb_scan_args(argc, argv, "11", &hash, &hash2)) {
|
@@ -268,14 +267,13 @@ guardtime_sign(int argc, VALUE *argv, VALUE obj)
|
|
268
267
|
res = GTHTTP_createTimestampHash(&dh, gt->signeruri, &ts);
|
269
268
|
if (res != GT_OK)
|
270
269
|
rb_raise(rb_eRuntimeError, GT_getErrorString(res));
|
271
|
-
// todo - return here? or does it use longjmp etc?
|
272
270
|
|
273
|
-
|
271
|
+
res = GTTimestamp_getDEREncoded(ts, &data, &data_length);
|
274
272
|
if (res != GT_OK)
|
275
273
|
rb_raise(rb_eRuntimeError, GT_getErrorString(res));
|
276
|
-
|
277
|
-
|
278
|
-
|
274
|
+
GTTimestamp_free(ts);
|
275
|
+
result = rb_str_new((char*)data, data_length);
|
276
|
+
GT_free(data);
|
279
277
|
return result;
|
280
278
|
}
|
281
279
|
|
@@ -291,7 +289,7 @@ guardtime_sign(int argc, VALUE *argv, VALUE obj)
|
|
291
289
|
* - +ArgumentError+ -> if any value is nil or wrong type.
|
292
290
|
* - +RuntimeError+ -> other errors, including network, hash value, token too new or old etc. Proper description is in the error message.
|
293
291
|
* Extended signature token may be used for 'independent' verification without any keys or services. Just data, token and newspaper with published value.
|
294
|
-
* There is no point in extending
|
292
|
+
* There is no point in extending new signature before next newspaper publication is performed. Good rule of thumb is to wait for 35 days (after signing), or until 15th date plus 5 more days.
|
295
293
|
*/
|
296
294
|
static VALUE
|
297
295
|
guardtime_extend(VALUE obj, VALUE in)
|
@@ -300,28 +298,28 @@ guardtime_extend(VALUE obj, VALUE in)
|
|
300
298
|
GTDataHash dh;
|
301
299
|
GTTimestamp *ts, *ts2;
|
302
300
|
unsigned char *data;
|
303
|
-
|
304
|
-
|
301
|
+
size_t data_length;
|
302
|
+
GuardTimeData *gt;
|
305
303
|
VALUE result;
|
306
304
|
|
307
305
|
StringValue(in);
|
308
306
|
Data_Get_Struct(obj, GuardTimeData, gt);
|
309
307
|
res = GTTimestamp_DERDecode(RSTRING_PTR(in),
|
310
|
-
|
311
|
-
|
312
|
-
rb_raise(
|
308
|
+
RSTRING_LEN(in), &ts);
|
309
|
+
if (res != GT_OK)
|
310
|
+
rb_raise(rb_eRuntimeError, GT_getErrorString(res));
|
313
311
|
|
314
312
|
res = GTHTTP_extendTimestamp(ts, gt->verifieruri, &ts2);
|
315
313
|
GTTimestamp_free(ts);
|
316
314
|
if (res != GT_OK)
|
317
315
|
rb_raise(rb_eRuntimeError, GT_getErrorString(res));
|
318
316
|
|
319
|
-
|
317
|
+
res = GTTimestamp_getDEREncoded(ts2, &data, &data_length);
|
320
318
|
if (res != GT_OK)
|
321
319
|
rb_raise(rb_eRuntimeError, GT_getErrorString(res));
|
322
320
|
|
323
|
-
|
324
|
-
|
321
|
+
result = rb_str_new((char*)data, data_length);
|
322
|
+
GT_free(data);
|
325
323
|
return result;
|
326
324
|
}
|
327
325
|
|
@@ -331,7 +329,7 @@ int loadpubs_helper(GuardTimeData *gt) {
|
|
331
329
|
GTPubFileVerificationInfo *pub_ver;
|
332
330
|
|
333
331
|
if (gt->pub != NULL)
|
334
|
-
|
332
|
+
GTPublicationsFile_free(gt->pub);
|
335
333
|
res = GTHTTP_getPublicationsFile(gt->pubfileuri, &(gt->pub));
|
336
334
|
if (res == GT_OK)
|
337
335
|
res = GTPublicationsFile_verify(gt->pub, &pub_ver);
|
@@ -403,20 +401,20 @@ format_location_id(GT_UInt64 l)
|
|
403
401
|
static VALUE
|
404
402
|
format_hash_algorithm(int alg)
|
405
403
|
{
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
404
|
+
switch(alg) {
|
405
|
+
case GT_HASHALG_SHA256:
|
406
|
+
return rb_str_new2("SHA256");
|
407
|
+
case GT_HASHALG_SHA1:
|
408
|
+
return rb_str_new2("SHA1");
|
409
|
+
case GT_HASHALG_RIPEMD160:
|
410
|
+
return rb_str_new2("RIPEMD160");
|
411
|
+
case GT_HASHALG_SHA224:
|
412
|
+
return rb_str_new2("SHA224");
|
413
|
+
case GT_HASHALG_SHA384:
|
414
|
+
return rb_str_new2("SHA384");
|
415
|
+
case GT_HASHALG_SHA512:
|
416
|
+
return rb_str_new2("SHA512");
|
417
|
+
default:
|
420
418
|
return Qnil;
|
421
419
|
}
|
422
420
|
}
|
@@ -440,7 +438,7 @@ format_hash_algorithm(int alg)
|
|
440
438
|
* - +ArgumentError+ -> if any value is nil or wrong type, or signature token is corrupted.
|
441
439
|
* - +RuntimeError+ -> other errors, including network, hash value etc.
|
442
440
|
*
|
443
|
-
* Code block receives parameter +resulthash+ which is populated with signature properties. There are following keys:
|
441
|
+
* Code block receives parameter +resulthash+ which is populated with verified signature properties. There are following keys:
|
444
442
|
* *verification_errors*:: bitfield containing verification errors. See class constants. Verification is successful only if value equals to <tt>GuardTime::NO_FAILURES</tt>
|
445
443
|
* *verification_status*:: Numeric bitfield -- flags identifying successful verification checks. See class constants.
|
446
444
|
* *time*:: Time object containing signing (time-stamp) datum.
|
@@ -491,29 +489,29 @@ guardtime_verify(int argc, VALUE *argv, VALUE obj)
|
|
491
489
|
argcount = rb_scan_args(argc, argv, "12&", &tsdata, &hash, &hash2, &block);
|
492
490
|
StringValue(tsdata);
|
493
491
|
|
494
|
-
|
495
|
-
|
496
|
-
|
492
|
+
res = GTTimestamp_DERDecode(RSTRING_PTR(tsdata),
|
493
|
+
RSTRING_LEN(tsdata), &ts);
|
494
|
+
if (res != GT_OK)
|
497
495
|
rb_raise(rb_eArgError, GT_getErrorString(res));
|
498
496
|
|
499
497
|
loadpubs(obj);
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
498
|
+
GTVerificationInfo *verification_info = NULL;
|
499
|
+
switch (argcount) {
|
500
|
+
case 1:
|
501
|
+
res = verifyTimestamp(ts, NULL, gt, RTEST(block)? 1:0, &verification_info);
|
502
|
+
break;
|
503
|
+
case 2:
|
506
504
|
get_gtdatahash(hash, &dh);
|
507
|
-
|
508
|
-
|
509
|
-
|
505
|
+
res = verifyTimestamp(ts, &dh, gt, RTEST(block)? 1:0, &verification_info);
|
506
|
+
break;
|
507
|
+
case 3:
|
510
508
|
get_gtdatahash2(hash, hash2, &dh);
|
511
|
-
|
512
|
-
|
513
|
-
|
509
|
+
res = verifyTimestamp(ts, &dh, gt, RTEST(block)? 1:0, &verification_info);
|
510
|
+
break;
|
511
|
+
}
|
514
512
|
|
515
513
|
if (res != GT_OK) {
|
516
|
-
|
514
|
+
GTTimestamp_free(ts);
|
517
515
|
rb_raise(rb_eRuntimeError, GT_getErrorString(res));
|
518
516
|
}
|
519
517
|
|
@@ -522,7 +520,7 @@ guardtime_verify(int argc, VALUE *argv, VALUE obj)
|
|
522
520
|
#define RBSET(n, v) \
|
523
521
|
( rb_hash_aset(retval, ID2SYM(rb_intern(n)), (v)) )
|
524
522
|
|
525
|
-
|
523
|
+
if (RTEST(block)) {
|
526
524
|
retval = rb_hash_new();
|
527
525
|
RBSET("verification_status", INT2FIX( verification_info->verification_status ));
|
528
526
|
RBSET("verification_errors", INT2FIX( verification_info->verification_errors ));
|
@@ -549,11 +547,11 @@ guardtime_verify(int argc, VALUE *argv, VALUE obj)
|
|
549
547
|
|
550
548
|
RBSET("time", time_t_to_Time( verification_info->implicit_data->registered_time ));
|
551
549
|
RBSET("publication_time", time_t_to_Time( verification_info->explicit_data->publication_identifier ));
|
552
|
-
|
553
|
-
|
550
|
+
} else
|
551
|
+
retval = verification_info->verification_errors == GT_NO_FAILURES ? Qtrue : Qfalse;
|
554
552
|
|
555
|
-
|
556
|
-
|
553
|
+
GTTimestamp_free(ts);
|
554
|
+
GTVerificationInfo_free(verification_info);
|
557
555
|
|
558
556
|
if (RTEST(block))
|
559
557
|
return rb_funcall(block, rb_intern("call"), 1, retval);
|
@@ -577,48 +575,48 @@ static VALUE
|
|
577
575
|
guardtime_getnewdigester(VALUE self, VALUE tsdata)
|
578
576
|
{
|
579
577
|
int res;
|
580
|
-
|
578
|
+
int alg;
|
581
579
|
GTTimestamp *ts;
|
582
580
|
VALUE module_klass, args[1];
|
583
581
|
|
584
582
|
StringValue(tsdata);
|
585
583
|
|
586
584
|
res = GTTimestamp_DERDecode(RSTRING_PTR(tsdata), RSTRING_LEN(tsdata), &ts);
|
587
|
-
|
585
|
+
if (res != GT_OK)
|
588
586
|
rb_raise(rb_eRuntimeError, GT_getErrorString(res));
|
589
587
|
|
590
|
-
|
591
|
-
|
592
|
-
|
588
|
+
res = GTTimestamp_getAlgorithm(ts, &alg);
|
589
|
+
GTTimestamp_free(ts);
|
590
|
+
if (res != GT_OK)
|
593
591
|
rb_raise(rb_eRuntimeError, GT_getErrorString(res));
|
594
592
|
|
595
593
|
// checkifnecessary: rb_requre('digest');
|
596
594
|
module_klass = rb_const_get(rb_cObject, rb_intern("Digest"));
|
597
595
|
|
598
596
|
switch(alg) {
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
597
|
+
case GT_HASHALG_SHA256:
|
598
|
+
args[0] = INT2FIX(256);
|
599
|
+
return rb_class_new_instance(1, args,
|
600
|
+
rb_const_get(module_klass, rb_intern("SHA2")));
|
601
|
+
case GT_HASHALG_SHA1:
|
602
|
+
return rb_class_new_instance(0, NULL,
|
603
|
+
rb_const_get(module_klass, rb_intern("SHA1")));
|
604
|
+
case GT_HASHALG_RIPEMD160:
|
605
|
+
return rb_class_new_instance(0, NULL,
|
606
|
+
rb_const_get(module_klass, rb_intern("RMD160")));
|
609
607
|
case GT_HASHALG_SHA224:
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
608
|
+
args[0] = INT2FIX(224);
|
609
|
+
return rb_class_new_instance(1, args,
|
610
|
+
rb_const_get(module_klass, rb_intern("SHA2")));
|
611
|
+
case GT_HASHALG_SHA384:
|
612
|
+
args[0] = INT2FIX(384);
|
613
|
+
return rb_class_new_instance(1, args,
|
614
|
+
rb_const_get(module_klass, rb_intern("SHA2")));
|
615
|
+
case GT_HASHALG_SHA512:
|
616
|
+
args[0] = INT2FIX(512);
|
617
|
+
return rb_class_new_instance(1, args,
|
618
|
+
rb_const_get(module_klass, rb_intern("SHA2")));
|
619
|
+
default:
|
622
620
|
rb_raise(rb_eRuntimeError, "Unknown hash algorithm ID");
|
623
621
|
}
|
624
622
|
return Qnil;
|
@@ -640,34 +638,34 @@ static VALUE
|
|
640
638
|
guardtime_gethashalg(VALUE self, VALUE tsdata)
|
641
639
|
{
|
642
640
|
int res;
|
643
|
-
|
641
|
+
int alg;
|
644
642
|
GTTimestamp *ts;
|
645
643
|
|
646
644
|
StringValue(tsdata);
|
647
645
|
|
648
646
|
res = GTTimestamp_DERDecode(RSTRING_PTR(tsdata), RSTRING_LEN(tsdata), &ts);
|
649
|
-
|
647
|
+
if (res != GT_OK)
|
650
648
|
rb_raise(rb_eRuntimeError, GT_getErrorString(res));
|
651
649
|
|
652
|
-
|
653
|
-
|
654
|
-
|
650
|
+
res = GTTimestamp_getAlgorithm(ts, &alg);
|
651
|
+
GTTimestamp_free(ts);
|
652
|
+
if (res != GT_OK)
|
655
653
|
rb_raise(rb_eRuntimeError, GT_getErrorString(res));
|
656
654
|
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
655
|
+
switch(alg) {
|
656
|
+
case GT_HASHALG_SHA256:
|
657
|
+
return rb_str_new2("SHA256");
|
658
|
+
case GT_HASHALG_SHA1:
|
659
|
+
return rb_str_new2("SHA1");
|
660
|
+
case GT_HASHALG_RIPEMD160:
|
661
|
+
return rb_str_new2("RIPEMD160");
|
662
|
+
case GT_HASHALG_SHA224:
|
663
|
+
return rb_str_new2("SHA224");
|
664
|
+
case GT_HASHALG_SHA384:
|
665
|
+
return rb_str_new2("SHA384");
|
666
|
+
case GT_HASHALG_SHA512:
|
667
|
+
return rb_str_new2("SHA512");
|
668
|
+
default:
|
671
669
|
rb_raise(rb_eRuntimeError, "Unknown hash algorithm ID");
|
672
670
|
}
|
673
671
|
return Qnil;
|
@@ -683,49 +681,46 @@ each_conf_param(VALUE key, VALUE value, VALUE klass)
|
|
683
681
|
|
684
682
|
if (key == Qundef) return ST_CONTINUE;
|
685
683
|
switch(TYPE(key)) {
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
684
|
+
case T_STRING:
|
685
|
+
key_id = rb_intern(RSTRING_PTR(key));
|
686
|
+
break;
|
687
|
+
case T_SYMBOL:
|
688
|
+
key_id = SYM2ID(key);
|
689
|
+
break;
|
690
|
+
default:
|
691
|
+
rb_raise(rb_eArgError,
|
692
|
+
"config hash includes invalid key");
|
693
|
+
}
|
694
|
+
if (TYPE(value) != T_STRING)
|
695
|
+
rb_raise(rb_eArgError,
|
696
|
+
"config hash value for '%s' must be a String", rb_id2name(key_id));
|
697
|
+
|
698
|
+
if (strcasecmp(rb_id2name(key_id), "signeruri") == 0)
|
699
|
+
gt->signeruri = RSTRING_PTR(value); // strdup() perhaps?
|
700
|
+
else if (strcasecmp(rb_id2name(key_id), "verifieruri") == 0) {
|
701
|
+
if (strlen(gt->verifieruri) > 0)
|
702
|
+
gt->verifieruri = RSTRING_PTR(value);
|
703
|
+
else
|
704
|
+
gt->verifieruri = NULL; // no extending
|
707
705
|
}
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
706
|
+
else if (strcasecmp(rb_id2name(key_id), "publicationsuri") == 0)
|
707
|
+
gt->pubfileuri = RSTRING_PTR(value);
|
708
|
+
else if (strcasecmp(rb_id2name(key_id), "loadpubs") == 0)
|
709
|
+
gt->loadpubs = RSTRING_PTR(value);
|
710
|
+
else
|
711
|
+
rb_raise(rb_eArgError,
|
712
|
+
"config hash has unknown key '%s'", rb_id2name(key_id));
|
713
|
+
|
714
|
+
return ST_CONTINUE;
|
717
715
|
}
|
718
716
|
|
719
717
|
static void
|
720
718
|
guardtime_free(GuardTimeData *gt)
|
721
719
|
{
|
722
720
|
if (gt) {
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
// GTHTTP_finalize();
|
727
|
-
// GT_finalize();
|
728
|
-
free(gt);
|
721
|
+
if (gt->pub != NULL)
|
722
|
+
GTPublicationsFile_free(gt->pub);
|
723
|
+
free(gt);
|
729
724
|
}
|
730
725
|
|
731
726
|
}
|
@@ -754,11 +749,21 @@ guardtime_allocate(VALUE self)
|
|
754
749
|
* * *Args* :
|
755
750
|
* - +confighash+ -> Optional Hash containing configuration parameters. Defaults:
|
756
751
|
* { :signeruri => 'http://verifier.guardtime.net/gt-extendingservice',
|
757
|
-
* :verifieruri => 'http://verifier.guardtime.net/gt-extendingservice',
|
752
|
+
* :verifieruri => 'http://verifier.guardtime.net/gt-extendingservice',
|
758
753
|
* :publicationsuri => 'http://verify.guardtime.com/gt-controlpublications.bin',
|
759
|
-
* :loadpubs => 'auto'
|
754
|
+
* :loadpubs => 'auto'
|
760
755
|
* }
|
761
|
-
*
|
756
|
+
*
|
757
|
+
* * *Notes* :
|
758
|
+
* - If <tt>:verifieruri</tt> is blank String then online verification is not used.
|
759
|
+
*
|
760
|
+
* - <tt>:loadpubs</tt> may be either
|
761
|
+
* +once+:: Publications file is loaded once.
|
762
|
+
* +always+:: Publications file is reloaded at each verification call
|
763
|
+
* +no+:: Publications file is not used for verification. May be good as token consistency check, or with extra verification (e.g. manual publication string check)
|
764
|
+
* +auto+:: Publications file is automatically reloaded if older than 8 hours. Default.
|
765
|
+
*
|
766
|
+
* - Please use environment to specify proxy ({syntax}[http://curl.haxx.se/docs/manpage.html#ENVIRONMENT]), Internet Explorer settints will be used on Windows. Specify url as <em>http://name:pass@site/url</em> for basic auth.
|
762
767
|
*/
|
763
768
|
static VALUE
|
764
769
|
guardtime_initialize(int argc, VALUE *argv, VALUE obj)
|
@@ -771,18 +776,7 @@ guardtime_initialize(int argc, VALUE *argv, VALUE obj)
|
|
771
776
|
}
|
772
777
|
|
773
778
|
/*
|
774
|
-
* This API provides access to GuardTime keyless signature service.
|
775
|
-
* Please refer to http://www.guardtime.com/signatures/technology-overview/ for technology overview.
|
776
|
-
*
|
777
|
-
* The two main operations are signing and verification. Signing involves cryptographically binding the data whose integrity is to be protected to the audited time value and server component's identity in a way that neither the data, the time value nor identity could later be changed undetectably. Verification means checking that the binding is still intact.
|
778
|
-
*
|
779
|
-
* To get a sigature token from a GuardTime service, the client application computes a hash value of the data to be signed and submits the hash value to the signing service. The service returns a signature token binding the hash value to the current time. The interaction follows the standard cryptographic time-stamping protocol defined in the RFC 3161.
|
780
|
-
*
|
781
|
-
* Every month, GuardTime creates an Integrity Code and publishes it in several newspapers across the world. The Integrity Code is a summary of all signatures issued by the GuardTime data integrity services. Any signature issued prior to the creation of the Integrity Code can be traced to it to unambiguously prove the signature’s issuing time, issuer ID and link to signed data, effectively proving the data integrity.
|
782
|
-
*
|
783
|
-
* The proof connecting the signature to the Integrity Code can be inserted back into the token in a process we call extending. An extended signature token is completely independent of GuardTime. Only the original data, the signature and any copy of the newspaper where the Integrity Code is published will be necessary to prove the intgerity of the document.
|
784
|
-
*
|
785
|
-
* For convenience, GuardTime publishes an electronic archive containing all Integrity Codes since the launch of the service and all valid signing keys. This electronic archive can be used to automate mass verification of thousands of timestamps (whether extended or not) per second in a large archive.
|
779
|
+
* This API provides access to the GuardTime keyless signature service.
|
786
780
|
*/
|
787
781
|
void Init_guardtime()
|
788
782
|
{
|
@@ -794,7 +788,7 @@ void Init_guardtime()
|
|
794
788
|
if (res != GT_OK)
|
795
789
|
rb_raise(rb_eRuntimeError, GT_getErrorString(res));
|
796
790
|
|
797
|
-
|
791
|
+
rb_cGuardTime = rb_define_class("GuardTime", rb_cObject);
|
798
792
|
rb_define_alloc_func(rb_cGuardTime, guardtime_allocate);
|
799
793
|
rb_define_method(rb_cGuardTime, "initialize", guardtime_initialize, -1);
|
800
794
|
rb_define_method(rb_cGuardTime, "sign", guardtime_sign, -1);
|
data/test/tc_guardtime.rb
CHANGED
@@ -5,48 +5,48 @@ require 'test/unit'
|
|
5
5
|
class TestGuardTime < Test::Unit::TestCase
|
6
6
|
|
7
7
|
def test_old
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
8
|
+
ts = File.open( File.dirname(__FILE__) + File::SEPARATOR + 'cat.gif.gtts', 'rb' ) do |f|
|
9
|
+
f.read
|
10
|
+
end
|
11
|
+
gt3 = GuardTime.new
|
12
|
+
assert( gt3.verify(ts) )
|
13
|
+
assert_raise ArgumentError do
|
14
|
+
gt3.verify("corrupted signature token")
|
15
|
+
end
|
16
|
+
assert_block do
|
17
|
+
gt3.verify(ts) do |r|
|
18
|
+
assert_instance_of(Time, r[:time])
|
19
|
+
assert(Time.now - r[:time] > 60*60*24*45, 'must be old sig')
|
20
|
+
assert_equal( GuardTime::PUBLICATION_CHECKED | GuardTime::PUBLICATION_REFERENCE_PRESENT,
|
21
|
+
r[:verification_status])
|
22
|
+
assert_equal( GuardTime::NO_FAILURES, r[:verification_errors])
|
23
|
+
assert_instance_of( Array, r[:pub_reference_list])
|
24
|
+
assert_instance_of( String, r[:publication_string])
|
25
|
+
assert_instance_of(Time, r[:publication_time])
|
26
|
+
r[:verification_errors] == GuardTime::NO_FAILURES
|
27
|
+
end
|
28
|
+
end
|
29
|
+
h3 = Digest::SHA2.new << File.read(File.dirname(__FILE__) + File::SEPARATOR + 'cat.gif')
|
30
|
+
assert( gt3.verify(ts, h3) )
|
31
|
+
assert( gt3.verify(ts, h3) do |r|
|
32
|
+
assert_equal( GuardTime::PUBLICATION_CHECKED | GuardTime::PUBLICATION_REFERENCE_PRESENT |
|
33
|
+
GuardTime::DOCUMENT_HASH_CHECKED, r[:verification_status])
|
34
34
|
assert_equal(h3.hexdigest, r[:hash_value].delete(':'))
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
35
|
+
r[:verification_errors] == GuardTime::NO_FAILURES
|
36
|
+
end
|
37
|
+
)
|
38
|
+
# extending token first, then verifying
|
39
|
+
assert( tsext = gt3.extend(ts) )
|
40
|
+
gt4 = GuardTime.new({:loadpubs => 'once', :verifieruri => ''})
|
41
|
+
gt4.verify(tsext, h3) do |r|
|
42
|
+
assert_equal( GuardTime::PUBLICATION_CHECKED | GuardTime::PUBLICATION_REFERENCE_PRESENT |
|
43
|
+
GuardTime::DOCUMENT_HASH_CHECKED, r[:verification_status])
|
44
|
+
end
|
45
|
+
# verification without extending
|
46
|
+
gt4.verify(ts, h3) do |r|
|
47
|
+
assert_equal( GuardTime::PUBLICATION_CHECKED | GuardTime::PUBLIC_KEY_SIGNATURE_PRESENT |
|
48
|
+
GuardTime::DOCUMENT_HASH_CHECKED, r[:verification_status])
|
49
|
+
end
|
50
50
|
|
51
51
|
|
52
52
|
end
|
@@ -56,23 +56,23 @@ class TestGuardTime < Test::Unit::TestCase
|
|
56
56
|
gt = GuardTime.new
|
57
57
|
assert_instance_of(GuardTime, gt)
|
58
58
|
ts = gt.sign(h)
|
59
|
-
|
59
|
+
assert_equal('SHA256', GuardTime.gethashalg(ts).upcase, 'GuardTime.gethashalg() works')
|
60
60
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
61
|
+
assert_raise TypeError do
|
62
|
+
GuardTime.gethashalg(123)
|
63
|
+
end
|
64
|
+
assert_raise RuntimeError do
|
65
|
+
GuardTime.gethashalg("corrupted signature token")
|
66
|
+
end
|
67
|
+
assert_raise RuntimeError do
|
68
|
+
GuardTime.getnewdigester("corrupted signature token")
|
69
|
+
end
|
70
|
+
h2 = GuardTime.getnewdigester(ts) << 'bla bla blah'
|
71
|
+
assert_equal(h.inspect, h2.inspect, 'GuardTime.getnewdigester()')
|
72
|
+
assert( gt.verify(ts) do |r|
|
73
|
+
assert_equal( GuardTime::PUBLIC_KEY_SIGNATURE_PRESENT | GuardTime::PUBLICATION_CHECKED,
|
74
|
+
r[:verification_status])
|
75
|
+
assert_equal( GuardTime::NO_FAILURES, r[:verification_errors])
|
76
76
|
assert_equal( nil, r[:pub_reference_list])
|
77
77
|
assert_equal( nil, r[:publication_string])
|
78
78
|
assert_instance_of(Time, r[:time])
|
@@ -80,43 +80,43 @@ class TestGuardTime < Test::Unit::TestCase
|
|
80
80
|
assert_instance_of(Time, r[:publication_time])
|
81
81
|
assert_equal(h.hexdigest, r[:hash_value].delete(':'))
|
82
82
|
assert_equal('GT : GT : public', r[:location_name])
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
83
|
+
r[:verification_errors] == GuardTime::NO_FAILURES
|
84
|
+
end
|
85
|
+
)
|
86
|
+
assert( gt.verify(ts, h2))
|
87
|
+
assert( gt.verify(ts, h2) do |r|
|
88
|
+
assert_equal( GuardTime::PUBLIC_KEY_SIGNATURE_PRESENT | GuardTime::PUBLICATION_CHECKED |
|
89
|
+
GuardTime::DOCUMENT_HASH_CHECKED, r[:verification_status])
|
90
|
+
r[:verification_errors] == GuardTime::NO_FAILURES
|
91
|
+
end
|
92
|
+
)
|
93
93
|
|
94
|
-
|
94
|
+
assert(gt.verify(ts, 'SHA256', h2.digest))
|
95
95
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
96
|
+
gt2 = GuardTime.new({:loadpubs => 'no', :verifieruri => ''})
|
97
|
+
assert( gt2.verify(ts) do |r|
|
98
|
+
assert_equal( GuardTime::PUBLIC_KEY_SIGNATURE_PRESENT, r[:verification_status])
|
99
|
+
r[:verification_errors] == GuardTime::NO_FAILURES
|
100
|
+
end
|
101
|
+
)
|
102
|
+
assert( gt2.verify(ts, h2) do |r|
|
103
|
+
assert_equal( GuardTime::PUBLIC_KEY_SIGNATURE_PRESENT | GuardTime::DOCUMENT_HASH_CHECKED, r[:verification_status])
|
104
|
+
r[:verification_errors] == GuardTime::NO_FAILURES
|
105
|
+
end
|
106
|
+
)
|
107
107
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
108
|
+
wrongh = Digest::SHA2.new << 'whateverelse'
|
109
|
+
assert_equal( false, gt2.verify(ts, wrongh) )
|
110
|
+
assert_equal( false, gt2.verify(ts, wrongh) do |r|
|
111
|
+
assert_equal( GuardTime::PUBLIC_KEY_SIGNATURE_PRESENT | GuardTime::DOCUMENT_HASH_CHECKED, r[:verification_status])
|
112
|
+
assert_equal( GuardTime::WRONG_DOCUMENT_FAILURE, r[:verification_errors])
|
113
|
+
r[:verification_errors] == GuardTime::NO_FAILURES
|
114
|
+
end
|
115
|
+
)
|
116
|
+
exception = assert_raise RuntimeError do
|
117
|
+
ext = gt.extend(ts)
|
118
|
+
end
|
119
|
+
assert_match /not yet available/, exception.message
|
120
120
|
end
|
121
121
|
|
122
122
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- GuardTime AS
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2013-01-
|
17
|
+
date: 2013-01-07 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -32,6 +32,7 @@ extra_rdoc_files: []
|
|
32
32
|
files:
|
33
33
|
- COPYING
|
34
34
|
- INSTALL
|
35
|
+
- README.rdoc
|
35
36
|
- ChangeLog
|
36
37
|
- ext/guardtime.c
|
37
38
|
- ext/extconf.rb
|
@@ -39,12 +40,15 @@ files:
|
|
39
40
|
- test/cat.gif.gtts
|
40
41
|
- test/tc_guardtime.rb
|
41
42
|
has_rdoc: true
|
42
|
-
homepage:
|
43
|
+
homepage: https://github.com/ristik/ruby-guardtime
|
43
44
|
licenses:
|
44
45
|
- apache-2.0
|
45
46
|
post_install_message:
|
46
|
-
rdoc_options:
|
47
|
-
|
47
|
+
rdoc_options:
|
48
|
+
- --main
|
49
|
+
- README.rdoc
|
50
|
+
- README.rdoc
|
51
|
+
- ext/guardtime.c
|
48
52
|
require_paths:
|
49
53
|
- lib
|
50
54
|
required_ruby_version: !ruby/object:Gem::Requirement
|