scrypty 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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