ruar 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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