ruar 0.0.2 → 0.0.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2ad6f68a61d0accd3eb5e970934fcbbf30e299c3caafb1ec69ebfc9f9a3b76bf
4
- data.tar.gz: 9a9f620ce167c3c5b0f1ab163b0d40f4261652005b83f8f2757098b600ee5b6a
3
+ metadata.gz: 71e86cbea690ee3e4c66ca5079ba3ae43ffa2d21c1b01e3574a3324b9509d712
4
+ data.tar.gz: 4dd935f956cefc3c13fae4ca9c95edefea91c556e92944e36d1d1b7c89b32791
5
5
  SHA512:
6
- metadata.gz: d5508e42916f61c9aeb2c47d90a956401139baac154d7a6776b600434da114aecdf156b860621c4117755a8dbf1f6ad45ee2cff5a601254b278b7d9804fe0065
7
- data.tar.gz: 790039c7c05c5da8c09a22bc349926c3c10476e7b37090ac2e2c213cd91bbd512aae7c596900ab89e1afcfad98d06cfe89671d0ebf39fe534d9444221f6f9cd0
6
+ metadata.gz: 7e31c46a4daa42ad6432b41e78c94004816a908810a4f1f51fd062129ac6244f44b7bcad9be7294a643a57559bdf179a91a60e2be85bc61c7961b908ca3517d6
7
+ data.tar.gz: f944938e97821f40ad011c30ebd62bf6586e4931184e3311feff029615503c8308fd117bae4958661a0414bfafacc72c91e86f765c95283ddf02db28d4d184d7
data/README.md CHANGED
@@ -5,6 +5,8 @@ Tar-like Archive for RIEN
5
5
  [![CI Tests](https://github.com/DarkKowalski/ruar/workflows/CI%20Tests/badge.svg)](https://github.com/DarkKowalski/ruar/actions?query=workflow%3A%22CI+Tests%22)
6
6
  [![Build](https://github.com/DarkKowalski/ruar/workflows/Build/badge.svg)](https://github.com/DarkKowalski/ruar/actions?query=workflow%3ABuild)
7
7
 
8
+ This GEM is still in development, please use the latest git dev branch
9
+
8
10
  ## Usage
9
11
 
10
12
  ```ruby
@@ -33,6 +35,57 @@ require 'dir/file'
33
35
  # Here you go
34
36
  ```
35
37
 
38
+ ### AEAD
39
+
40
+ Encrypt
41
+
42
+ ```ruby
43
+ require 'ruar'
44
+ require 'tmpdir'
45
+
46
+ auth_data = Base64.encode64('nekomimi')
47
+ Ruar.cipher.setup(auth_data: auth_data)
48
+
49
+ archive = File.join(Dir.tmpdir, 'aead.ruar')
50
+ Ruar::Serialize.aead('./test/sample', archive)
51
+ ```
52
+
53
+ Then you will get `/tmp/aead.ruar.setup`
54
+
55
+ ```ruby
56
+ Ruar.cipher.setup(
57
+ iv: 'niNGDaPcFiD8eKdb',
58
+ key: 'I0LpzLx3GuU19F5EOwTbfZJ6pYOlH4Pbumm9SolLJv8=',
59
+ auth_data: 'bmVrb21pbWk=',
60
+ tag: 'nN6rCnd5SmZaoTdpmUEjtw==')
61
+ Ruar.cipher.enable
62
+ ```
63
+
64
+ Decrypt
65
+
66
+ ```ruby
67
+ require 'lib/ruar'
68
+ require 'tmpdir'
69
+
70
+ # Decryption Setup
71
+ Ruar.cipher.setup(
72
+ iv: 'niNGDaPcFiD8eKdb',
73
+ key: 'I0LpzLx3GuU19F5EOwTbfZJ6pYOlH4Pbumm9SolLJv8=',
74
+ auth_data: 'bmVrb21pbWk=',
75
+ tag: 'nN6rCnd5SmZaoTdpmUEjtw==')
76
+ Ruar.cipher.enable
77
+
78
+ archive = File.join(Dir.tmpdir, 'aead.ruar')
79
+ Ruar.setup(archive: archive).activate
80
+
81
+ # Setup
82
+ Ruar.setup(
83
+ archive: archive
84
+ ).activate
85
+
86
+ # Require from /tmp/aeae.ruar
87
+ require 'dir/file'
88
+ ```
36
89
  ## Format
37
90
 
38
91
  ```
data/ext/ruar/ruar.c CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  #include <ctype.h>
4
4
  #include <ruby.h>
5
+ #include <stdbool.h>
5
6
  #include <stdint.h>
6
7
  #include <stdio.h>
7
8
  #include <stdlib.h>
@@ -43,7 +44,12 @@ STATIC_ASSERT(sizeof(struct ruar_header) == HEADER_SIZE);
43
44
  /* Constants */
44
45
  static const uint32_t current_major_version = 0;
45
46
  static const uint32_t current_minor_version = 0;
46
- static const uint32_t current_patch_version = 1;
47
+ static const uint32_t current_patch_version = 3;
48
+
49
+ /* Flags */
50
+ #define PLAIN 0
51
+ #define AEAD_AES_256_GCM 1
52
+ #define ZLIB_DEFLATE 1
47
53
 
48
54
  /* System info */
49
55
  #ifdef __linux__
@@ -67,6 +73,7 @@ static VALUE rb_mRuar_Serialize_Native;
67
73
 
68
74
  /* Functions exposed as module functions on Ruar::Serialize::Native */
69
75
  static VALUE ruar_serialize_rb_plain_header(VALUE self, VALUE dstfile, VALUE index);
76
+ static VALUE ruar_serialize_rb_aead_header(VALUE self, VALUE dstfile, VALUE index);
70
77
  static VALUE ruar_serialize_rb_append_file(VALUE self, VALUE dstfile, VALUE srcfile);
71
78
 
72
79
  /* Ruar::Access::Native */
@@ -96,6 +103,7 @@ void Init_ruar(void)
96
103
  rb_mRuar_Serialize = rb_define_module_under(rb_mRuar, "Serialize");
97
104
  rb_mRuar_Serialize_Native = rb_define_module_under(rb_mRuar_Serialize, "Native");
98
105
  rb_define_module_function(rb_mRuar_Serialize_Native, "plain_header", ruar_serialize_rb_plain_header, 2);
106
+ rb_define_module_function(rb_mRuar_Serialize_Native, "aead_header", ruar_serialize_rb_aead_header, 2);
99
107
  rb_define_module_function(rb_mRuar_Serialize_Native, "append_file", ruar_serialize_rb_append_file, 2);
100
108
 
101
109
  rb_kRuar_Access = rb_define_class_under(rb_mRuar, "Access", rb_cObject);
@@ -137,38 +145,74 @@ static VALUE ruar_rb_header_hash(const struct ruar_header *header)
137
145
  return rb_header;
138
146
  }
139
147
 
140
- static VALUE ruar_serialize_rb_plain_header(VALUE self, VALUE dstfile, VALUE index)
148
+ static struct ruar_header ruar_fillout_header(char *index_cstring, uint32_t index_size, uint32_t encryption_flags, uint32_t compression_flags)
141
149
  {
142
- /* Call into ruby to generate the index */
143
- char *index_cstring = rb_string_value_cstr(&index);
144
- uint32_t index_size = INDEX_SIZE(index_cstring);
145
-
146
150
  /* Fill out a header */
147
151
  struct ruar_header header = {
148
152
  .major_version = current_major_version,
149
153
  .minor_version = current_minor_version,
150
154
  .patch_version = current_patch_version,
151
155
  .platform = current_platform,
152
- .encryption_flags = 0,
153
- .compression_flags = 0,
156
+ .encryption_flags = encryption_flags,
157
+ .compression_flags = compression_flags,
154
158
  .index_start = HEADER_SIZE,
155
159
  .index_size = index_size,
156
160
  .index_checksum = ruar_crc32_generate((unsigned char *)index_cstring, index_size),
157
161
  .header_checksum = 0};
158
162
  header.header_checksum = ruar_crc32_generate((unsigned char *)&header, HEADER_SIZE);
159
163
 
160
- /* Write header */
161
- char *dstfile_cstring = rb_string_value_cstr(&dstfile);
164
+ return header;
165
+ }
166
+
167
+ static bool ruar_write_header_to_file(char *dstfile_cstring, char *index_cstring, uint32_t index_size, struct ruar_header *header)
168
+ {
162
169
  FILE *outfile = fopen(dstfile_cstring, "wb");
163
170
  if (outfile == NULL)
164
171
  {
165
172
  fprintf(stderr, "\nFailed to open file! %s\n", dstfile_cstring);
166
- return Qnil;
173
+ return false;
167
174
  }
168
175
 
169
- fwrite(&header, HEADER_SIZE, 1, outfile);
176
+ fwrite(header, HEADER_SIZE, 1, outfile);
170
177
  fwrite(index_cstring, index_size, 1, outfile);
171
178
  fclose(outfile);
179
+ return true;
180
+ }
181
+
182
+ static VALUE ruar_serialize_rb_plain_header(VALUE self, VALUE dstfile, VALUE index)
183
+ {
184
+ char *index_cstring = rb_string_value_cstr(&index);
185
+ uint32_t index_size = INDEX_SIZE(index_cstring);
186
+
187
+ /* Fill out a header */
188
+ struct ruar_header header = ruar_fillout_header(index_cstring, index_size, PLAIN, PLAIN);
189
+
190
+ /* Write header */
191
+ char *dstfile_cstring = rb_string_value_cstr(&dstfile);
192
+ if (!ruar_write_header_to_file(dstfile_cstring, index_cstring, index_size, &header))
193
+ {
194
+ return Qnil;
195
+ }
196
+
197
+ /* Get Ruby Hash */
198
+ return ruar_rb_header_hash(&header);
199
+ }
200
+
201
+ static VALUE ruar_serialize_rb_aead_header(VALUE self, VALUE dstfile, VALUE index)
202
+ {
203
+ /* Index should be encrypted in Ruby land */
204
+ char *index_cstring = rb_string_value_cstr(&index);
205
+ uint32_t index_size = INDEX_SIZE(index_cstring);
206
+
207
+ /* Fill out a header */
208
+ struct ruar_header header = ruar_fillout_header(index_cstring, index_size, AEAD_AES_256_GCM, ZLIB_DEFLATE);
209
+
210
+ /* Write header */
211
+ char *dstfile_cstring = rb_string_value_cstr(&dstfile);
212
+ if (!ruar_write_header_to_file(dstfile_cstring, index_cstring, index_size, &header))
213
+ {
214
+ return Qnil;
215
+ }
172
216
 
173
217
  /* Get Ruby Hash */
174
218
  return ruar_rb_header_hash(&header);
@@ -310,7 +354,6 @@ static VALUE ruar_access_rb_file(VALUE self, VALUE archive, VALUE offset, VALUE
310
354
 
311
355
  size_t size_cint = NUM2SIZET(size);
312
356
 
313
-
314
357
  /* Ruby C API will convert this C-style String to a Ruby String Object
315
358
  * Thus one extra byte for the trialing '\0'
316
359
  */
data/lib/ruar.rb CHANGED
@@ -2,8 +2,11 @@
2
2
 
3
3
  require 'json'
4
4
  require 'tmpdir'
5
+ require 'base64'
6
+ require 'openssl'
5
7
  require 'pathname'
6
- require 'binding_of_caller'
8
+ require 'zlib'
9
+ # require 'binding_of_caller'
7
10
 
8
11
  require_relative 'ruar/version'
9
12
  require_relative 'ruar/ruar'
@@ -12,6 +15,8 @@ require_relative 'ruar/error'
12
15
  require_relative 'ruar/index'
13
16
  require_relative 'ruar/serialize'
14
17
  require_relative 'ruar/access'
18
+ require_relative 'ruar/cipher'
19
+ require_relative 'ruar/compression'
15
20
  require_relative 'ruar/entrypoint'
16
21
 
17
22
  require_relative 'ruar/setup'
data/lib/ruar/access.rb CHANGED
@@ -27,6 +27,7 @@ module Ruar
27
27
  rebuild
28
28
  end
29
29
 
30
+ # TODO: Need to deliberately test this part
30
31
  def lookup(path)
31
32
  paths = Ruar::Access.clean_path(path)
32
33
  filename = paths.pop
@@ -50,7 +51,10 @@ module Ruar
50
51
 
51
52
  def read(path)
52
53
  offset, size, _executable = lookup(path)
53
- Ruar::Access::Native.file(@archive, offset.to_i, size.to_i)
54
+ file = Ruar::Access::Native.file(@archive, offset.to_i, size.to_i)
55
+ file = Ruar.cipher.decrypt(file)[:decrypted] if Ruar.cipher.enable?
56
+
57
+ file
54
58
  end
55
59
 
56
60
  def eval(path, eval_bind = TOPLEVEL_BINDING)
@@ -86,7 +90,9 @@ module Ruar
86
90
 
87
91
  def rebuild
88
92
  @header = Ruar::Access::Native.header(@archive)
89
- @index = JSON.parse(Ruar::Access::Native.index(@archive))
93
+ index_data = Ruar::Access::Native.index(@archive)
94
+ index_data = Ruar.cipher.decrypt(index_data)[:decrypted] if Ruar.cipher.enable?
95
+ @index = JSON.parse(index_data)
90
96
  @file_start = @header['index_start'] + @header['index_size']
91
97
  end
92
98
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ruar
4
+ class Cipher
5
+ def initialize
6
+ @enable = false
7
+ end
8
+
9
+ def aead
10
+ @aead ||= OpenSSL::Cipher.new('aes-256-gcm')
11
+ end
12
+
13
+ def enable?
14
+ @enable
15
+ end
16
+
17
+ def enable
18
+ @enable = true
19
+ end
20
+
21
+ def setup(key: nil, iv: nil, auth_data: nil, tag: nil)
22
+ @key = key.nil? ? aead.random_key : Base64.decode64(key)
23
+ @iv = iv.nil? ? aead.random_iv : Base64.decode64(iv)
24
+ @auth_data = auth_data.nil? ? 'ruar_default_auth_data' : Base64.decode64(auth_data)
25
+ @tag = tag.nil? ? 'ruar_invalid_auth_tag' : Base64.decode64(tag)
26
+
27
+ self
28
+ end
29
+
30
+ def encrypt(data, auth_data: @auth_data, key: @key, iv: @iv)
31
+ cipher = aead.encrypt
32
+ cipher.key = key
33
+ cipher.iv = iv
34
+ cipher.auth_data = auth_data
35
+
36
+ compressed = Ruar::Compression.compress(data)
37
+ encrypted = Base64.encode64(cipher.update(compressed) + cipher.final)
38
+ tag = cipher.auth_tag
39
+
40
+ {
41
+ encrypted: encrypted,
42
+ iv: iv,
43
+ key: key,
44
+ tag: tag,
45
+ auth_data: auth_data
46
+ }
47
+ end
48
+
49
+ def decrypt(data, auth_data: @auth_data, key: @key, iv: @iv, tag: @tag)
50
+ raise 'tag is truncated!' unless tag.bytesize == 16
51
+
52
+ cipher = aead.decrypt
53
+ cipher.key = key
54
+ cipher.iv = iv
55
+ cipher.auth_tag = tag
56
+ cipher.auth_data = auth_data
57
+
58
+ decrypted = cipher.update(Base64.decode64(data))
59
+ decompressed = Ruar::Compression.decompress(decrypted)
60
+
61
+ { decrypted: decompressed }
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,11 @@
1
+ module Ruar
2
+ module Compression
3
+ def self.compress(data)
4
+ Zlib::Deflate.deflate(data)
5
+ end
6
+
7
+ def self.decompress(data)
8
+ Zlib::Inflate.inflate(data)
9
+ end
10
+ end
11
+ end
@@ -14,9 +14,9 @@ module Ruar
14
14
  prefix = Ruar.path_prefix
15
15
  # TODO: support .so here
16
16
  if File.extname(path) == '.rb'
17
- File.join(prefix, path)
17
+ Pathname.new(File.join(prefix, path)).cleanpath.to_s
18
18
  else
19
- File.join(prefix, "#{path}.rb")
19
+ Pathname.new(File.join(prefix, "#{path}.rb")).cleanpath.to_s
20
20
  end
21
21
  end
22
22
  end
@@ -27,15 +27,14 @@ module Kernel
27
27
  module_function
28
28
 
29
29
  def ruar_eval_wrap(path, eval_bind = TOPLEVEL_BINDING)
30
- Ruar.eval(path, eval_bind)
31
- yield
30
+ Ruar.eval("#{path}.rb", eval_bind) # Ruar.eval(path.rb, eval_bind)
31
+ yield if block_given?
32
32
  true
33
33
  rescue Ruar::Error::FileNotFound
34
- # Try again with .rb extension
34
+ # Try again without .rb extension
35
35
  begin
36
- # For rubocop:
37
- Ruar.eval("#{path}.rb", eval_bind) # Ruar.eval(path.rb, eval_bind)
38
- yield
36
+ Ruar.eval(path, eval_bind)
37
+ yield if block_given?
39
38
  true
40
39
  rescue Ruar::Error::BaseError
41
40
  raise Ruar::Access::CoreExt.make_load_error(path)
@@ -91,9 +90,7 @@ module Kernel
91
90
  alias load_without_ruar load
92
91
 
93
92
  def load_with_ruar(path, eval_bind = TOPLEVEL_BINDING)
94
- ruar_eval_wrap(path, eval_bind) do
95
- # Do nothing, just read and eval
96
- end
93
+ ruar_eval_wrap(path, eval_bind)
97
94
  end
98
95
 
99
96
  def load(path, from: :both)
data/lib/ruar/index.rb CHANGED
@@ -6,7 +6,9 @@ module Ruar
6
6
 
7
7
  def initialize(dir = '.')
8
8
  @dir = dir
9
- generate(dir)
9
+ generate(dir) do |file|
10
+ yield(file) if block_given?
11
+ end
10
12
  end
11
13
 
12
14
  def json_index
@@ -17,7 +19,9 @@ module Ruar
17
19
 
18
20
  # Generate json format index
19
21
  def generate(dir)
20
- @index, @source_info = scan(dir, 0)
22
+ @index, @source_info = scan(dir, 0) do |file|
23
+ yield(file) if block_given?
24
+ end
21
25
  end
22
26
 
23
27
  # FIXME: don't recurse
@@ -32,6 +36,9 @@ module Ruar
32
36
 
33
37
  files = entities.select { |f| File.file?(f) }
34
38
  files.each do |f|
39
+ # Do something else likes encrypting the file
40
+ yield(f) if block_given?
41
+
35
42
  size = File.size(f)
36
43
 
37
44
  index['files'][f] = {
@@ -52,7 +59,9 @@ module Ruar
52
59
  dirs = entities.select { |d| File.directory?(d) }
53
60
  dirs.each do |d|
54
61
  # Notice: need to accumulate offset here
55
- sub_index, sub_source_info, offset = scan(d, offset)
62
+ sub_index, sub_source_info, offset = scan(d, offset) do |f|
63
+ yield(f) if block_given?
64
+ end
56
65
  index['files'][d] = sub_index
57
66
  source_info.concat(sub_source_info)
58
67
  end
data/lib/ruar/ruar.so CHANGED
Binary file
@@ -9,5 +9,38 @@ module Ruar
9
9
  Ruar::Serialize::Native.append_file(dstfile, src['realpath'])
10
10
  end
11
11
  end
12
+
13
+ def self.aead(srcdir, dstfile)
14
+ time = Time.now.strftime('%Y%m%dT%H%M')
15
+ tmpdir = File.expand_path("ruar_#{time}", Dir.tmpdir)
16
+ FileUtils.copy_entry(srcdir, tmpdir)
17
+
18
+ encryption_info = nil
19
+ index = Ruar::Index.new(tmpdir) do |file|
20
+ data = File.read(file)
21
+ encryption_info = Ruar.cipher.encrypt(data)
22
+ File.write(file, encryption_info[:encrypted])
23
+ end
24
+
25
+ encrypted_index = Ruar.cipher.encrypt(index.json_index)[:encrypted]
26
+ Ruar::Serialize::Native.aead_header(dstfile, encrypted_index)
27
+
28
+ index.source_info.each do |src|
29
+ Ruar::Serialize::Native.append_file(dstfile, src['realpath'])
30
+ end
31
+
32
+ setup_file = <<~SETUP
33
+ Ruar.cipher.setup(
34
+ iv: '#{Base64.encode64(encryption_info[:iv]).chomp}',
35
+ key: '#{Base64.encode64(encryption_info[:key]).chomp}',
36
+ auth_data: '#{Base64.encode64(encryption_info[:auth_data]).chomp}',
37
+ tag: '#{Base64.encode64(encryption_info[:tag]).chomp}')
38
+ Ruar.cipher.enable
39
+ SETUP
40
+
41
+ File.write("#{dstfile}.setup", setup_file)
42
+
43
+ FileUtils.rm_rf(tmpdir)
44
+ end
12
45
  end
13
46
  end
data/lib/ruar/setup.rb CHANGED
@@ -32,6 +32,10 @@ module Ruar
32
32
  def eval(path, bind = TOPLEVEL_BINDING)
33
33
  @entrypoint.eval(path, bind)
34
34
  end
35
+
36
+ def cipher
37
+ @cipher ||= Ruar::Cipher.new
38
+ end
35
39
  end
36
40
  end
37
41
  end
data/lib/ruar/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ruar
4
- VERSION = '0.0.2'
4
+ VERSION = '0.0.3'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kowalski Dark
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-20 00:00:00.000000000 Z
11
+ date: 2021-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: binding_of_caller
@@ -137,6 +137,8 @@ files:
137
137
  - ext/ruar/ruar.h
138
138
  - lib/ruar.rb
139
139
  - lib/ruar/access.rb
140
+ - lib/ruar/cipher.rb
141
+ - lib/ruar/compression.rb
140
142
  - lib/ruar/core_ext/kernel_require.rb
141
143
  - lib/ruar/core_ext/string_colorize.rb
142
144
  - lib/ruar/entrypoint.rb