scrypty 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9ced050fcbd6b14dd5467920da66ed04decc423c
4
+ data.tar.gz: 4ff8de717cc7268c962292c5c5605c9467be38a2
5
+ SHA512:
6
+ metadata.gz: 830ad2a43e9064dbe82a18230e0c3a6cbbe0a4281eda1035f5199b15b45e17c0b2ffd7b607d86341f5a37442441a93285dcb5f2940517fd1cab66fc5d7bc2f17
7
+ data.tar.gz: ee25bc76c5df9a75d1996eec9f8649c9982a6f93717f6bf0332aceb9c8015e2c1dc9b7e23b4654991ce7c1c1c52f92fd9fe84f59017a0bdcb4e4bf85df88ea8b
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rake/testtask"
2
3
  require "rake/clean"
3
4
 
4
5
  file "ext/Makefile" => "ext/extconf.rb" do
@@ -32,5 +33,12 @@ task :prefix do
32
33
  end
33
34
  end
34
35
 
36
+ Rake::TestTask.new do |t|
37
+ t.test_files = FileList['test/test_*.rb']
38
+ t.verbose = true
39
+ end
40
+ task :test => :compile
41
+ task :default => :test
42
+
35
43
  CLEAN.clear
36
44
  CLEAN.include(["ext/*.o", "ext/*.so", "lib/*.so", "ext/extconf.h", "ext/Makefile"])
@@ -1,7 +1,21 @@
1
1
  require 'mkmf'
2
2
 
3
+ dir_config("openssl")
4
+
5
+ result = pkg_config("openssl") && have_header("openssl/ssl.h")
6
+
7
+ unless result
8
+ result = have_header("openssl/ssl.h")
9
+ result &&= %w[crypto libeay32].any? {|lib| have_library(lib, "AES_set_encrypt_key")}
10
+ result &&= %w[ssl ssleay32].any? {|lib| have_library(lib, "AES_set_encrypt_key")}
11
+ unless result
12
+ Logging::message "=== Checking for required stuff failed. ===\n"
13
+ Logging::message "Makefile wasn't created. Fix the errors above.\n"
14
+ exit 1
15
+ end
16
+ end
17
+
3
18
  have_library('rt', 'clock_gettime')
4
- have_library('crypto', 'AES_set_encrypt_key')
5
19
  %w{err.h fcntl.h inttypes.h memory.h stddef.h stdint.h stdlib.h string.h strings.h sys/endian.h sys/param.h sys/stat.h sys/time.h sys/types.h termios.h unistd.h}.each do |header|
6
20
  have_header(header)
7
21
  end
@@ -1,7 +1,13 @@
1
1
  #include <ruby.h>
2
2
  #include <ruby/io.h>
3
3
  #include <stdio.h>
4
+ #include <errno.h>
4
5
  #include "scryptenc.h"
6
+ #include "scryptenc_cpuperf.h"
7
+ #include "memlimit.h"
8
+ #include "crypto_scrypt.h"
9
+ #include "crypto_aesctr.h"
10
+ #include "sha256.h"
5
11
 
6
12
  VALUE mScrypty;
7
13
 
@@ -291,6 +297,347 @@ scrypty_decrypt_file(rb_obj, rb_infn, rb_outfn, rb_password, rb_maxmem, rb_maxme
291
297
  rb_maxmemfrac, rb_maxtime, 0);
292
298
  }
293
299
 
300
+ VALUE
301
+ scrypty_memlimit(rb_obj, rb_maxmem, rb_maxmemfrac)
302
+ VALUE rb_obj;
303
+ VALUE rb_maxmem;
304
+ VALUE rb_maxmemfrac;
305
+ {
306
+ long l_maxmem;
307
+ size_t maxmem, memlimit, max_size;
308
+ double maxmemfrac;
309
+
310
+ max_size = (size_t) -1;
311
+
312
+ if (TYPE(rb_maxmem) == T_FIXNUM) {
313
+ l_maxmem = FIX2LONG(rb_maxmem);
314
+
315
+ if (l_maxmem < 0) {
316
+ rb_raise(rb_eArgError, "maxmem (%ld) must not be less than 0", l_maxmem);
317
+ }
318
+ else if ((unsigned long) l_maxmem > (unsigned long) max_size) {
319
+ rb_raise(rb_eArgError, "maxmem (%ld) cannot exceed %zu", l_maxmem, max_size);
320
+ }
321
+ else {
322
+ maxmem = (size_t) l_maxmem;
323
+ }
324
+ }
325
+ else {
326
+ rb_raise(rb_eTypeError, "first argument (maxmem) must be a Fixnum");
327
+ }
328
+
329
+ if (FIXNUM_P(rb_maxmemfrac) || TYPE(rb_maxmemfrac) == T_FLOAT) {
330
+ maxmemfrac = NUM2DBL(rb_maxmemfrac);
331
+ }
332
+ else {
333
+ rb_raise(rb_eTypeError, "second argument (maxmemfrac) must be a Fixnum or Float");
334
+ }
335
+
336
+ if (scrypty_memtouse(maxmem, maxmemfrac, &memlimit) != 0) {
337
+ rb_raise(rb_eRuntimeError, "could not determine memory limit");
338
+ }
339
+
340
+ return SIZET2NUM(memlimit);
341
+ }
342
+
343
+ VALUE
344
+ scrypty_opslimit(rb_obj, rb_maxtime)
345
+ VALUE rb_obj;
346
+ VALUE rb_maxtime;
347
+ {
348
+ double opps, maxtime, opslimit;
349
+
350
+ if (FIXNUM_P(rb_maxtime) || TYPE(rb_maxtime) == T_FLOAT) {
351
+ maxtime = NUM2DBL(rb_maxtime);
352
+ }
353
+ else {
354
+ rb_raise(rb_eTypeError, "first argument (maxtime) must be a Fixnum or Float");
355
+ }
356
+
357
+ /* Figure out how fast the CPU is. */
358
+ if (scrypty_scryptenc_cpuperf(&opps) != 0) {
359
+ rb_raise(rb_eRuntimeError, "could not determine CPU performance");
360
+ }
361
+ opslimit = opps * maxtime;
362
+
363
+ /* Allow a minimum of 2^15 salsa20/8 cores. */
364
+ if (opslimit < 32768)
365
+ opslimit = 32768;
366
+
367
+ return DBL2NUM(opslimit);
368
+ }
369
+
370
+ /* Calculate parameters used for creating a derived key with the
371
+ * scrypt algorithm. */
372
+ VALUE
373
+ scrypty_params(rb_obj, rb_memlimit, rb_opslimit)
374
+ VALUE rb_obj;
375
+ VALUE rb_memlimit;
376
+ VALUE rb_opslimit;
377
+ {
378
+ VALUE rb_result;
379
+ long l_memlimit;
380
+ size_t memlimit, max_size;
381
+ double opslimit;
382
+ double maxN, maxrp;
383
+ int logN;
384
+ uint64_t N = 0;
385
+ uint32_t r = 0, p = 0;
386
+
387
+ max_size = (size_t) -1;
388
+
389
+ if (TYPE(rb_memlimit) == T_FIXNUM) {
390
+ l_memlimit = FIX2LONG(rb_memlimit);
391
+
392
+ if (l_memlimit < 0) {
393
+ rb_raise(rb_eArgError, "memlimit (%ld) must not be less than 0", l_memlimit);
394
+ }
395
+ else if ((unsigned long) l_memlimit > (unsigned long) max_size) {
396
+ rb_raise(rb_eArgError, "memlimit (%ld) cannot exceed %zu", l_memlimit, max_size);
397
+ }
398
+ else {
399
+ memlimit = (size_t) l_memlimit;
400
+ }
401
+ }
402
+ else {
403
+ rb_raise(rb_eTypeError, "first argument (memlimit) must be a Fixnum");
404
+ }
405
+
406
+ if (FIXNUM_P(rb_opslimit) || TYPE(rb_opslimit) == T_FLOAT) {
407
+ opslimit = NUM2DBL(rb_opslimit);
408
+ }
409
+ else {
410
+ rb_raise(rb_eTypeError, "second argument (opslimit) must be a Fixnum or Float");
411
+ }
412
+
413
+ /* Fix r = 8 for now. */
414
+ r = 8;
415
+
416
+ if (opslimit < memlimit/32) {
417
+ /* Set p = 1 and choose N based on the CPU limit. */
418
+ p = 1;
419
+ maxN = opslimit / (r * 4);
420
+ for (logN = 1; logN < 63; logN += 1) {
421
+ if ((uint64_t)(1) << logN > maxN / 2)
422
+ break;
423
+ }
424
+ } else {
425
+ /* Set N based on the memory limit. */
426
+ maxN = memlimit / (r * 128);
427
+ for (logN = 1; logN < 63; logN += 1) {
428
+ N = (uint64_t)(1) << logN;
429
+ if (N > maxN / 2)
430
+ break;
431
+ }
432
+
433
+ /* Choose p based on the CPU limit. */
434
+ maxrp = (opslimit / 4) / ((uint64_t)(1) << logN);
435
+ if (maxrp > 0x3fffffff)
436
+ maxrp = 0x3fffffff;
437
+ p = (uint32_t)(maxrp) / r;
438
+ }
439
+
440
+ rb_result = rb_ary_new3(3, UINT2NUM(N), UINT2NUM(r), UINT2NUM(p));
441
+ return rb_result;
442
+ };
443
+
444
+ VALUE
445
+ scrypty_dk(rb_obj, rb_password, rb_salt, rb_n, rb_r, rb_p, rb_keylen)
446
+ VALUE rb_obj;
447
+ VALUE rb_password;
448
+ VALUE rb_salt;
449
+ VALUE rb_n;
450
+ VALUE rb_r;
451
+ VALUE rb_p;
452
+ VALUE rb_keylen;
453
+ {
454
+ VALUE rb_dk;
455
+ const uint8_t *password, *salt;
456
+ uint8_t *dk;
457
+ uint64_t N;
458
+ uint32_t r, p;
459
+ size_t password_len, salt_len, keylen;
460
+
461
+ if (TYPE(rb_password) == T_STRING) {
462
+ password = (const uint8_t *) RSTRING_PTR(rb_password);
463
+ password_len = (size_t) RSTRING_LEN(rb_password);
464
+ }
465
+ else {
466
+ rb_raise(rb_eTypeError, "first argument (password) must be a String");
467
+ }
468
+
469
+ if (TYPE(rb_salt) == T_STRING) {
470
+ salt = (const uint8_t *) RSTRING_PTR(rb_salt);
471
+ salt_len = (size_t) RSTRING_LEN(rb_salt);
472
+ }
473
+ else {
474
+ rb_raise(rb_eTypeError, "second argument (salt) must be a String");
475
+ }
476
+
477
+ if (FIXNUM_P(rb_n)) {
478
+ N = (uint64_t) NUM2ULL(rb_n);
479
+ }
480
+ else {
481
+ rb_raise(rb_eTypeError, "third argument (n) must be a Fixnum");
482
+ }
483
+
484
+ if (FIXNUM_P(rb_r)) {
485
+ r = (uint32_t) NUM2ULONG(rb_r);
486
+ }
487
+ else {
488
+ rb_raise(rb_eTypeError, "fourth argument (r) must be a Fixnum");
489
+ }
490
+
491
+ if (FIXNUM_P(rb_p)) {
492
+ p = (uint32_t) NUM2ULONG(rb_p);
493
+ }
494
+ else {
495
+ rb_raise(rb_eTypeError, "fifth argument (p) must be a Fixnum");
496
+ }
497
+
498
+ if (FIXNUM_P(rb_keylen)) {
499
+ keylen = NUM2SIZET(rb_keylen);
500
+ }
501
+ else {
502
+ rb_raise(rb_eTypeError, "sixth argument (keylen) must be a Fixnum");
503
+ }
504
+
505
+ rb_dk = rb_str_buf_new(keylen);
506
+ dk = (uint8_t *) RSTRING_PTR(rb_dk);
507
+
508
+ if (scrypty_crypto_scrypt(password, password_len, salt,
509
+ salt_len, N, r, p, dk, keylen) != 0) {
510
+
511
+ switch (errno) {
512
+ case EFBIG:
513
+ rb_raise(rb_eRuntimeError, "parameters were too big");
514
+ break;
515
+
516
+ case EINVAL:
517
+ rb_raise(rb_eRuntimeError, "parameters were invalid");
518
+ break;
519
+
520
+ case ENOMEM:
521
+ rb_raise(rb_eNoMemError, "not enough memory to create derived key");
522
+ break;
523
+
524
+ default:
525
+ rb_raise(rb_eRuntimeError, "%s", strerror(errno));
526
+ }
527
+ }
528
+
529
+ rb_str_set_len(rb_dk, keylen);
530
+ return rb_dk;
531
+ }
532
+
533
+ VALUE
534
+ scrypty_encrypt_raw(rb_obj, rb_data, rb_dk)
535
+ VALUE rb_obj;
536
+ VALUE rb_data;
537
+ VALUE rb_dk;
538
+ {
539
+ VALUE rb_out;
540
+ uint8_t *data, *dk, *out, *key_enc, *key_hmac;
541
+ uint8_t hbuf[32];
542
+ size_t data_len;
543
+ scrypty_HMAC_SHA256_CTX hctx;
544
+ AES_KEY key_enc_exp;
545
+ struct crypto_aesctr *AES;
546
+
547
+ if (TYPE(rb_data) == T_STRING) {
548
+ data = (uint8_t *) RSTRING_PTR(rb_data);
549
+ data_len = (size_t) RSTRING_LEN(rb_data);
550
+ }
551
+ else {
552
+ rb_raise(rb_eTypeError, "first argument (data) must be a String");
553
+ }
554
+
555
+ if (TYPE(rb_dk) == T_STRING) {
556
+ dk = (uint8_t *) RSTRING_PTR(rb_dk);
557
+ }
558
+ else {
559
+ rb_raise(rb_eTypeError, "second argument (dk) must be a String");
560
+ }
561
+ key_enc = dk;
562
+ key_hmac = &dk[32];
563
+
564
+ rb_out = rb_str_buf_new(data_len + 32);
565
+ out = (uint8_t *) RSTRING_PTR(rb_out);
566
+
567
+ /* Encrypt data. */
568
+ if (AES_set_encrypt_key(key_enc, 256, &key_enc_exp))
569
+ rb_raise(eOpenSSLError, "OpenSSL error");
570
+
571
+ if ((AES = scrypty_crypto_aesctr_init(&key_enc_exp, 0)) == NULL)
572
+ rb_raise(rb_eNoMemError, "couldn't allocate memory");
573
+
574
+ scrypty_crypto_aesctr_stream(AES, data, out, data_len);
575
+ scrypty_crypto_aesctr_free(AES);
576
+
577
+ /* Add signature. */
578
+ scrypty_HMAC_SHA256_Init(&hctx, key_hmac, 32);
579
+ scrypty_HMAC_SHA256_Update(&hctx, out, data_len);
580
+ scrypty_HMAC_SHA256_Final(hbuf, &hctx);
581
+ memcpy(&out[data_len], hbuf, 32);
582
+
583
+ rb_str_set_len(rb_out, data_len + 32);
584
+ return rb_out;
585
+ }
586
+
587
+ VALUE
588
+ scrypty_decrypt_raw(rb_obj, rb_data, rb_dk)
589
+ VALUE rb_obj;
590
+ VALUE rb_data;
591
+ VALUE rb_dk;
592
+ {
593
+ VALUE rb_out;
594
+ uint8_t *data, *dk, *out, *key_enc, *key_hmac;
595
+ uint8_t hbuf[32];
596
+ size_t data_len;
597
+ scrypty_HMAC_SHA256_CTX hctx;
598
+ AES_KEY key_enc_exp;
599
+ struct crypto_aesctr *AES;
600
+
601
+ if (TYPE(rb_data) == T_STRING) {
602
+ data = (uint8_t *) RSTRING_PTR(rb_data);
603
+ data_len = (size_t) RSTRING_LEN(rb_data);
604
+ }
605
+ else {
606
+ rb_raise(rb_eTypeError, "first argument (data) must be a String");
607
+ }
608
+
609
+ if (TYPE(rb_dk) == T_STRING) {
610
+ dk = (uint8_t *) RSTRING_PTR(rb_dk);
611
+ }
612
+ else {
613
+ rb_raise(rb_eTypeError, "second argument (dk) must be a String");
614
+ }
615
+ key_enc = dk;
616
+ key_hmac = &dk[32];
617
+
618
+ rb_out = rb_str_buf_new(data_len - 32);
619
+ out = (uint8_t *) RSTRING_PTR(rb_out);
620
+
621
+ /* Decrypt data. */
622
+ if (AES_set_encrypt_key(key_enc, 256, &key_enc_exp))
623
+ rb_raise(eOpenSSLError, "OpenSSL error");
624
+ if ((AES = scrypty_crypto_aesctr_init(&key_enc_exp, 0)) == NULL)
625
+ rb_raise(rb_eNoMemError, "couldn't allocate memory");
626
+
627
+ scrypty_crypto_aesctr_stream(AES, data, out, data_len - 32);
628
+ scrypty_crypto_aesctr_free(AES);
629
+
630
+ /* Verify signature. */
631
+ scrypty_HMAC_SHA256_Init(&hctx, key_hmac, 32);
632
+ scrypty_HMAC_SHA256_Update(&hctx, data, data_len - 32);
633
+ scrypty_HMAC_SHA256_Final(hbuf, &hctx);
634
+ if (memcmp(hbuf, &data[data_len - 32], 32))
635
+ rb_raise(eInvalidBlockError, "data is not a valid scrypt-encrypted block");
636
+
637
+ rb_str_set_len(rb_out, data_len - 32);
638
+ return rb_out;
639
+ }
640
+
294
641
  void
295
642
  Init_scrypty_ext(void)
296
643
  {
@@ -299,6 +646,12 @@ Init_scrypty_ext(void)
299
646
  rb_define_singleton_method(mScrypty, "decrypt", scrypty_decrypt_buffer, 5);
300
647
  rb_define_singleton_method(mScrypty, "encrypt_file", scrypty_encrypt_file, 6);
301
648
  rb_define_singleton_method(mScrypty, "decrypt_file", scrypty_decrypt_file, 6);
649
+ rb_define_singleton_method(mScrypty, "memlimit", scrypty_memlimit, 2);
650
+ rb_define_singleton_method(mScrypty, "opslimit", scrypty_opslimit, 1);
651
+ rb_define_singleton_method(mScrypty, "params", scrypty_params, 2);
652
+ rb_define_singleton_method(mScrypty, "dk", scrypty_dk, 6);
653
+ rb_define_singleton_method(mScrypty, "encrypt_raw", scrypty_encrypt_raw, 2);
654
+ rb_define_singleton_method(mScrypty, "decrypt_raw", scrypty_decrypt_raw, 2);
302
655
 
303
656
  eScryptyError = rb_define_class_under(mScrypty, "Exception", rb_eException);
304
657
  eMemoryLimitError = rb_define_class_under(mScrypty, "MemoryLimitError", eScryptyError);
@@ -1,3 +1,3 @@
1
1
  module Scrypty
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -17,4 +17,6 @@ Gem::Specification.new do |gem|
17
17
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
19
  gem.require_paths = ["lib"]
20
+
21
+ gem.add_development_dependency('test-unit')
20
22
  end
@@ -0,0 +1,44 @@
1
+ require 'test/unit'
2
+ require 'securerandom'
3
+ require 'scrypty'
4
+
5
+ class TestScrypty < Test::Unit::TestCase
6
+ test 'memlimit' do
7
+ result = Scrypty.memlimit(2 ** 27, 0.5)
8
+ assert result >= (2 ** 27)
9
+ end
10
+
11
+ test 'opslimit' do
12
+ result = Scrypty.opslimit(5)
13
+ assert result >= 32768
14
+ end
15
+
16
+ test 'params' do
17
+ memlimit = Scrypty.memlimit(2 ** 27, 0.5)
18
+ opslimit = Scrypty.opslimit(5)
19
+ n, r, p = Scrypty.params(memlimit, opslimit)
20
+
21
+ assert_includes 1...63, Math.log2(n)
22
+ assert_equal 8, r
23
+ assert p > 0
24
+ end
25
+
26
+ test 'dk' do
27
+ memlimit = Scrypty.memlimit(2 ** 27, 0.5)
28
+ opslimit = Scrypty.opslimit(2)
29
+ n, r, p = Scrypty.params(memlimit, opslimit)
30
+ dk = Scrypty.dk("secret", SecureRandom.random_bytes(32), n, r, p, 64)
31
+ assert_equal 64, dk.length
32
+ end
33
+
34
+ test 'raw roundtrip' do
35
+ memlimit = Scrypty.memlimit(2 ** 27, 0.5)
36
+ opslimit = Scrypty.opslimit(2)
37
+ n, r, p = Scrypty.params(memlimit, opslimit)
38
+ dk = Scrypty.dk("secret", SecureRandom.random_bytes(32), n, r, p, 64)
39
+
40
+ ciphertext = Scrypty.encrypt_raw("foobar", dk);
41
+ plaintext = Scrypty.decrypt_raw(ciphertext, dk);
42
+ assert_equal "foobar", plaintext
43
+ end
44
+ end
metadata CHANGED
@@ -1,16 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scrypty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
5
- prerelease:
4
+ version: 0.0.4
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jeremy Stephens
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-12-10 00:00:00.000000000 Z
13
- dependencies: []
11
+ date: 2013-10-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: test-unit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
14
27
  description: Uses the scrypt algorithm by Colin Percival to encrypt/decrypt data
15
28
  email:
16
29
  - viking@pillageandplunder.net
@@ -45,28 +58,29 @@ files:
45
58
  - lib/scrypty.rb
46
59
  - lib/scrypty/version.rb
47
60
  - scrypty.gemspec
61
+ - test/test_scrypty.rb
48
62
  homepage: https://github.com/viking/scrypty
49
63
  licenses: []
64
+ metadata: {}
50
65
  post_install_message:
51
66
  rdoc_options: []
52
67
  require_paths:
53
68
  - lib
54
69
  required_ruby_version: !ruby/object:Gem::Requirement
55
- none: false
56
70
  requirements:
57
- - - ! '>='
71
+ - - '>='
58
72
  - !ruby/object:Gem::Version
59
73
  version: '0'
60
74
  required_rubygems_version: !ruby/object:Gem::Requirement
61
- none: false
62
75
  requirements:
63
- - - ! '>='
76
+ - - '>='
64
77
  - !ruby/object:Gem::Version
65
78
  version: '0'
66
79
  requirements: []
67
80
  rubyforge_project:
68
- rubygems_version: 1.8.23
81
+ rubygems_version: 2.0.3
69
82
  signing_key:
70
- specification_version: 3
83
+ specification_version: 4
71
84
  summary: Utility to encrypt/decrypt data with the scrypt algorithm
72
- test_files: []
85
+ test_files:
86
+ - test/test_scrypty.rb