nm_datafile 0.0.0
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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +67 -0
- data/Rakefile +9 -0
- data/changelog.md +16 -0
- data/lib/nm_datafile/b_f.rb +33 -0
- data/lib/nm_datafile/crypto.rb +146 -0
- data/lib/nm_datafile/nm_datafile.rb +643 -0
- data/lib/nm_datafile/schema.rb +16 -0
- data/lib/nm_datafile/version.rb +3 -0
- data/lib/nm_datafile.rb +21 -0
- data/nm_datafile.gemspec +26 -0
- data/spec/nm_datafile_spec.rb +37 -0
- data/spec/spec_helper.rb +82 -0
- metadata +134 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d0e2702e30b76edee854437b9198f80aa8a0e17f
|
4
|
+
data.tar.gz: 2d6c9faeded184a7f7b460541c386be6f6a9a03b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 599da7c695dca546ddf93b02e2020454acd3aa4a90119dc8cf9ac0e16424797c757a867736002288f9862b5496e5259a44ea811d735a2966b66184b0ecbb235a
|
7
|
+
data.tar.gz: bead10acc0a21dc9332ef46d87c9d0269efee22175d8728610a78cbd93a0c82d4c2867de358bbfa91b234be21c5dd91ded066c48478f7768238e4a8d9db61790
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 antisec
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# NmDatafile
|
2
|
+
|
3
|
+
NmDatafile is a library that defines a file format that makes adding files and strings to an easy, selfencrypting file that can use either asymetric or symetric cryptography.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'nm_datafile'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install nm_datafile
|
20
|
+
|
21
|
+
|
22
|
+
## Code Example
|
23
|
+
|
24
|
+
nmd = NmDatafile.new(:shippable_file)
|
25
|
+
|
26
|
+
nmd.sales = [1,2,3,4]
|
27
|
+
nmd.ready_for_shipment_batch = 1
|
28
|
+
|
29
|
+
nmd.save(path_to_file)
|
30
|
+
|
31
|
+
nmd_loaded = NmDatafile::Load(path_to_file)
|
32
|
+
|
33
|
+
nmd_loaded.sales #=> [1,2,3,4]
|
34
|
+
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
First define a file Schema.
|
39
|
+
For instance, if you wanted a file type called 'shippable_file' and you wanted it to have many records of 'sales', 'line_items', etc,
|
40
|
+
and you wanted your file to have a string of data named 'ready_for_shipment_batch', then you would define it like so.
|
41
|
+
|
42
|
+
$nm_datafile_schemas = {
|
43
|
+
schemas: {
|
44
|
+
:shippable_file => {
|
45
|
+
data_collections: [:sales, :line_items, :discounts, :addresses, :ubws, :encryption_pairs], # name the data that is input into the NMDatafile as an array
|
46
|
+
data_objects: [:ready_for_shipment_batch]
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
TODO: Specify encryption type on file as well...
|
52
|
+
|
53
|
+
Then you'd want to create the NMD file:
|
54
|
+
|
55
|
+
And finally save it as shown below:
|
56
|
+
|
57
|
+
|
58
|
+
## Contributing
|
59
|
+
|
60
|
+
1. Fork it ( https://github.com/[my-github-username]/nm_datafile/fork )
|
61
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
62
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
63
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
64
|
+
5. Create a new Pull Request
|
65
|
+
|
66
|
+
|
67
|
+
|
data/Rakefile
ADDED
data/changelog.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
### 0.0.1
|
2
|
+
|
3
|
+
* Code migrated into this repo
|
4
|
+
* Created some tests
|
5
|
+
* Get pull out the class methods from the class
|
6
|
+
** 1. Pull all those methods into a module
|
7
|
+
** 2. Include them on the class where needed.
|
8
|
+
** 3. Extend them onto the parent namespace module
|
9
|
+
** 4. Plug the gem into rails and see if it still works.
|
10
|
+
|
11
|
+
|
12
|
+
### 0.0.0
|
13
|
+
|
14
|
+
Skeleton
|
15
|
+
|
16
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# https://gist.github.com/nono/2995118
|
2
|
+
|
3
|
+
require "openssl"
|
4
|
+
|
5
|
+
module NmDatafile
|
6
|
+
|
7
|
+
class BF < Struct.new(:key, :pad_with_spaces)
|
8
|
+
def encrypt(str)
|
9
|
+
cipher = OpenSSL::Cipher.new('bf-ecb').encrypt
|
10
|
+
if pad_with_spaces
|
11
|
+
str += " " until str.bytesize % 8 == 0
|
12
|
+
cipher.padding = 0
|
13
|
+
end
|
14
|
+
cipher.key = key
|
15
|
+
binary_data = cipher.update(str) << cipher.final
|
16
|
+
hex_encoded = binary_data.unpack('H*').first
|
17
|
+
end
|
18
|
+
|
19
|
+
def decrypt(hex_encoded)
|
20
|
+
cipher = OpenSSL::Cipher.new('bf-ecb').decrypt
|
21
|
+
cipher.padding = 0 if pad_with_spaces
|
22
|
+
cipher.key = key
|
23
|
+
binary_data = [hex_encoded].pack('H*')
|
24
|
+
str = cipher.update(binary_data) << cipher.final
|
25
|
+
str.force_encoding(Encoding::UTF_8)
|
26
|
+
str
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module NmDatafile
|
4
|
+
|
5
|
+
module Crypto
|
6
|
+
# The zip implementation... should use 7z though and throw this out when it's done... or truecript or gpg...
|
7
|
+
def decode_protected_zip_old_zip_based(password, decryptable_portion)
|
8
|
+
encryptable_portion = Tempfile.new('encryptable_portion', "#{Rails.root}/tmp")
|
9
|
+
FileUtils.rm(encryptable_portion.path)
|
10
|
+
File.binwrite(encryptable_portion.path, decryptable_portion)
|
11
|
+
|
12
|
+
supress_errors = "2>/dev/null"
|
13
|
+
decompress_zip_cmd = "funzip -'#{password}' '#{encryptable_portion.path}' #{supress_errors}"
|
14
|
+
|
15
|
+
clear_text_hash = `#{decompress_zip_cmd}`.chomp
|
16
|
+
|
17
|
+
clear_text_hash = convert_newline_chars_back_to_symbols(clear_text_hash)
|
18
|
+
|
19
|
+
FileUtils.rm(encryptable_portion.path)
|
20
|
+
clear_text_hash
|
21
|
+
end
|
22
|
+
|
23
|
+
# TODu: rename to decode_string_as_password_protected
|
24
|
+
def decode_string_as_password_protected(password, decryptable_portion)
|
25
|
+
#require 'b_f'
|
26
|
+
bf = BF.new(password, true)
|
27
|
+
|
28
|
+
bf.decrypt(decryptable_portion)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# Takes in a password and binary data of protected archive file and outputs
|
33
|
+
# a string of it's first entry in clear text which should be a hash
|
34
|
+
# of {:file_name, "file_data"}
|
35
|
+
def decode_password_protected_string(password, decryptable_portion)
|
36
|
+
decode = decode_string_as_password_protected(password, decryptable_portion)
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
# converts the string pairs into symbol/ string pairs
|
42
|
+
def symbolize_keys(decode)
|
43
|
+
p = {}
|
44
|
+
decode.each do |key_pair|
|
45
|
+
p.merge!( { key_pair[0].to_sym => key_pair[1] } )
|
46
|
+
end
|
47
|
+
p
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
def convert_newline_chars_back_to_symbols(clear_text_hash)
|
54
|
+
clear_text_hash.gsub("\n", "\\n")
|
55
|
+
end
|
56
|
+
|
57
|
+
# I think 7z is considered secure and zip is considered insecure so write this
|
58
|
+
def decode_protected_7z(password, decryptable_portion)
|
59
|
+
# TODu: implement
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def decrypt_encryptable_data!(password, hash)
|
64
|
+
return if hash[:crypt].nil? # leave this function if there's no 'crypt' entry to decrypt
|
65
|
+
|
66
|
+
decode = decode_string_into_NMDatafile_stores(password, hash[:crypt])
|
67
|
+
|
68
|
+
hash.delete :crypt
|
69
|
+
|
70
|
+
hash.merge!(decode)
|
71
|
+
end
|
72
|
+
|
73
|
+
def decode_string_into_NMDatafile_stores(password, crypt_string)
|
74
|
+
decode = YAML::load decode_password_protected_string(password, crypt_string)
|
75
|
+
decode = symbolize_keys(decode)
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
def clean_decrypt_string(string)
|
81
|
+
# string = Base64.decode64 string
|
82
|
+
# decode_password_protected_string(@@unsecure_pass, string)
|
83
|
+
NMDatafile.fast_decrypt_string_with_pass(@@unsecure_pass, string)
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
def fast_encrypt_string_with_pass(pass, string)
|
89
|
+
encoded_as_base64 = Base64.encode64(string)
|
90
|
+
rearranged = rearrangement(encoded_as_base64)
|
91
|
+
obfs = obfuscated_ending(rearranged)
|
92
|
+
Base64.encode64(obfs)
|
93
|
+
end
|
94
|
+
|
95
|
+
def fast_decrypt_string_with_pass(pass, string)
|
96
|
+
obfs = Base64.decode64(string)
|
97
|
+
rearranged = obfuscated_ending_undo(obfs)
|
98
|
+
encoded_as_base64 = rearrangement_undo(rearranged)
|
99
|
+
Base64.decode64(encoded_as_base64)
|
100
|
+
end
|
101
|
+
|
102
|
+
def rearrangement(s)
|
103
|
+
s = the_last_three_chars(s) + the_string_minus_the_last_three_chars(s)
|
104
|
+
end
|
105
|
+
|
106
|
+
def rearrangement_undo(s)
|
107
|
+
s = the_string_minus_the_first_three_chars(s) + the_first_three_chars(s)
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
def obfuscated_ending(s)
|
112
|
+
junk = "tlD3=\n"
|
113
|
+
s + junk
|
114
|
+
end
|
115
|
+
|
116
|
+
def obfuscated_ending_undo(s)
|
117
|
+
junk = "tlD3=\n"
|
118
|
+
s[0...(-1*junk.length)]
|
119
|
+
end
|
120
|
+
|
121
|
+
# hide these somewhere, so ugly
|
122
|
+
def the_last_three_chars(s)
|
123
|
+
s[-3..-1]
|
124
|
+
end
|
125
|
+
|
126
|
+
def the_first_three_chars(s)
|
127
|
+
s[0..3]
|
128
|
+
end
|
129
|
+
|
130
|
+
def the_string_minus_the_last_three_chars(s)
|
131
|
+
s[0...-3]
|
132
|
+
end
|
133
|
+
|
134
|
+
def the_string_minus_the_first_three_chars(s)
|
135
|
+
s[2..-1]
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
def encrypt_using_gpg(pass, string)
|
140
|
+
crypto = GPGME::Crypto.new :symmetric => true, :password => "gpgme"
|
141
|
+
encrypted_data = crypto.encrypt "string"
|
142
|
+
encrypted_data.read
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
@@ -0,0 +1,643 @@
|
|
1
|
+
module NmDatafile
|
2
|
+
|
3
|
+
# This class describes a way for AP to convert data into a portable format
|
4
|
+
# (zip_file.nmd). nms == aNonyMousDatafile
|
5
|
+
#
|
6
|
+
# To create a new file, you first must define a schema. As of right now,
|
7
|
+
# there are two public schemas. As defined in these schemas, NmDatafiles
|
8
|
+
# consist of collections of objects, and individual objects... eg
|
9
|
+
# An instance might have nmd.important_numbers which is an array of number,
|
10
|
+
# whereas it might also have a data_object, nmd#stupidest_number which is a
|
11
|
+
# single number.
|
12
|
+
#
|
13
|
+
# So to create an NmDatafile, do
|
14
|
+
# `NmDatafile.new(:shippable_file, sales, line_items, addresses, rfsb)`
|
15
|
+
# that will work if you have a schema that has 3 data_collections specified
|
16
|
+
# and 1 data_object specified in the schema named :shippable_file.
|
17
|
+
#
|
18
|
+
# Using NmDatafiles allows you access to utility functions such as
|
19
|
+
# - #save_to_string
|
20
|
+
# - #save_to_file
|
21
|
+
# - tons of testing methods
|
22
|
+
#
|
23
|
+
# The NmDatafile is just a zip, but it's a password protected zip, and
|
24
|
+
# it can also be obfuscated to confuse the feds.
|
25
|
+
#
|
26
|
+
# Creating a new APDatafile means creates, in memory, a way to:
|
27
|
+
# - Render the data to the hdd as a zip file
|
28
|
+
# - Render the data as a string which can be streamed as a zip file
|
29
|
+
# - Reading a file
|
30
|
+
# - Encrypt the data (assymetrically?) YES
|
31
|
+
# - Define the standard for formating
|
32
|
+
# - Create mock data outputs easily (helpful in testing)
|
33
|
+
# - Specify the version of the file
|
34
|
+
# - File can non-destructively corrupt itself so it doesn't look so much like a zip file to cryptographic analists
|
35
|
+
# - Object is responsive to what ever schema it's set to... so it will be able to do
|
36
|
+
# shippable_file = NmDatafile.new(:shippable_file); shippable_file.sales << Sale.new
|
37
|
+
# - loads data, nmd.load_data([sales, line_items, addresses, [ready_for_shipment_batch]])
|
38
|
+
# - loads variables nmd.load_data([sales, line_items, addresses, ready_for_shipment_batch])
|
39
|
+
|
40
|
+
# Encrypt the data (asymetrically?) YES
|
41
|
+
# To do this, all the files should be lumped together as one huge string (a hash of file names and data strings)... split by a special split marker and file name definer such as
|
42
|
+
# Data Encryption:
|
43
|
+
# Meth. 1) Once this massive string is created, a PGP key is used to encrypt the whole file.
|
44
|
+
# Meth. 2) The data can be encrypted using a randomly generated password, and then the password get's encrypted with PGP <- better
|
45
|
+
#
|
46
|
+
#
|
47
|
+
|
48
|
+
# (r) file_type: Can be
|
49
|
+
# - shippable_file, used to export shipping data from AP book shopping app
|
50
|
+
# - address_completion_file, used to transfer data from shipping machine to AP book shopping app
|
51
|
+
# - db_backup_file, used to download a complete backup of a webserver
|
52
|
+
# - etc, etc, used for other organization objectives
|
53
|
+
#
|
54
|
+
# (w) set_public_key: Specify a public PGP key to encode the data with
|
55
|
+
#
|
56
|
+
# (r) get_public_key: Shows the email and fingerprint of the key that's been set (for debugging)
|
57
|
+
#
|
58
|
+
# (r) show_current_schema: Shows the data schema for the file type
|
59
|
+
#
|
60
|
+
# (w) set_current_schema: deletes all data and sets the schema
|
61
|
+
#
|
62
|
+
# (r) save_to_string: creates the zip file, handleing
|
63
|
+
#
|
64
|
+
# (rw) protected: bool, specifies whether the file is secured with PGP or if it's just a zip file
|
65
|
+
#
|
66
|
+
# (m) Load: loads a file into memory as an NmDatafile
|
67
|
+
#
|
68
|
+
# (m) load_data: loads array of data into memory as an NmDatafile object
|
69
|
+
|
70
|
+
|
71
|
+
# PRIVATE
|
72
|
+
# (m) encrypt_file!: Does all encryption procedures before rendering to a string or file
|
73
|
+
#
|
74
|
+
# (m) decrypt_file!: reverse of encrypt_file!
|
75
|
+
#
|
76
|
+
# (m) encrypt_string: symetrically encrypts a string using a password
|
77
|
+
#
|
78
|
+
# (m) decrypt_string: reverse of encrypt_string
|
79
|
+
#
|
80
|
+
# (m) encrypt_symetric_key: uses PGP to encrypt the password that encrypted the data
|
81
|
+
#
|
82
|
+
# (m) decrypt_symetric_key: reverse of encrypt_symetric_key.
|
83
|
+
#
|
84
|
+
# (m) corrupt_zip: corrupts the zip file so it doesn't look like a zip file, make it look like a jpeg
|
85
|
+
#
|
86
|
+
# (m) uncorrupt_zip: reverses corrupt_zip file so the zip can be processed
|
87
|
+
#
|
88
|
+
# (m)
|
89
|
+
# aNonyMousDatafile
|
90
|
+
class NmDatafile
|
91
|
+
@@schemas = ::NmDatafile::SCHEMA[:schemas] # TODO: move to initialize
|
92
|
+
@@unsecure_pass = $FrontDoorKey # ppl with root access can decrypt nmd files
|
93
|
+
@@clear_text_path = "clear_text_protected_nmd" # used for using system calls to decrypt and encrypt using a zip password
|
94
|
+
attr_reader :file_type, :password
|
95
|
+
|
96
|
+
extend Crypto
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
###############################
|
102
|
+
# Loading and Dumping Methods #
|
103
|
+
###############################
|
104
|
+
|
105
|
+
# (m) Load: loads a file into memory as an NmDatafile
|
106
|
+
def self.Load(file_path)
|
107
|
+
zip_data = File.read(file_path)
|
108
|
+
LoadBinaryData(zip_data)
|
109
|
+
end
|
110
|
+
|
111
|
+
def self.LoadBinaryData(binary_data)
|
112
|
+
hash = extract_entities_from_binary_data(binary_data)
|
113
|
+
|
114
|
+
file_type = determine_file_type(hash[:attributes])
|
115
|
+
nmd = self.new( file_type )
|
116
|
+
|
117
|
+
nmd.load_attributes(hash[:attributes]) unless hash[:attributes].nil?
|
118
|
+
nmd.load_encryption(hash[:encryption])
|
119
|
+
|
120
|
+
nmd.load_data([*hash[:data_collections], *hash[:data_objects]])
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.determine_file_type(attributes_hash)
|
124
|
+
attributes_hash = YAML::load attributes_hash
|
125
|
+
attributes_hash["file_type"].to_sym
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.determine_password(hash)
|
129
|
+
d = YAML::load hash[:encryption]
|
130
|
+
clean_decrypt_string(d["password"])
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
def initialize(file_type, *args)
|
139
|
+
set_file_type(file_type)
|
140
|
+
|
141
|
+
load_data(args)
|
142
|
+
|
143
|
+
setup_object_for_schema
|
144
|
+
end
|
145
|
+
|
146
|
+
def load_attributes(attribute_data)
|
147
|
+
d = YAML::load attribute_data
|
148
|
+
@file_type = d["file_type"].to_sym
|
149
|
+
@build_date = Time.zone.parse d["build_date"] unless d["build_date"].nil?
|
150
|
+
end
|
151
|
+
|
152
|
+
def load_encryption(encryption_data)
|
153
|
+
d = YAML::load encryption_data
|
154
|
+
@integrity_hash = d["integrity_hash"] unless d["integrity_hash"].nil?
|
155
|
+
@password = clean_decrypt_string(d["password"]) unless d["password"].nil?
|
156
|
+
end
|
157
|
+
|
158
|
+
# (m) load_data: loads array of data into memory as an NmDatafile object
|
159
|
+
# clears the data arrays so it's like reinitializing the whole file
|
160
|
+
def load_data(*args)
|
161
|
+
init_data_arrays # wipes all preexisting data in @data_collections and @data_objects
|
162
|
+
|
163
|
+
args[0].each.with_index do |array_or_variable, i|
|
164
|
+
if i < data_collection_names.count
|
165
|
+
@data_collections[i] += array_or_variable
|
166
|
+
else
|
167
|
+
j = i - data_collection_names.count
|
168
|
+
@data_objects[j] = array_or_variable # if array_or_variable.class != Array
|
169
|
+
end
|
170
|
+
end
|
171
|
+
self
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
def save_to_string
|
176
|
+
set_password
|
177
|
+
|
178
|
+
clear_text = build_encryptable_portion
|
179
|
+
encrypted_data = encode_string_as_password_protected(clear_text)
|
180
|
+
|
181
|
+
hash_of_entries = { :crypt => encrypted_data,
|
182
|
+
:encryption => build_encryption,
|
183
|
+
:attributes => build_attributes }
|
184
|
+
|
185
|
+
encode_datafiles_as_zip(hash_of_entries)
|
186
|
+
end
|
187
|
+
|
188
|
+
def save_to_file(path)
|
189
|
+
File.write(path, save_to_string)
|
190
|
+
end
|
191
|
+
|
192
|
+
def set_password
|
193
|
+
len = 41
|
194
|
+
|
195
|
+
password = ""
|
196
|
+
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')']
|
197
|
+
|
198
|
+
1.upto(len) { |i| password << chars[rand(chars.size-1)]}
|
199
|
+
|
200
|
+
space = rand(len-1)
|
201
|
+
password[space] = ("0".."9").to_a[rand(9)] # ensure there's number and letter
|
202
|
+
space += 1
|
203
|
+
space = 0 if space == len
|
204
|
+
password[space] = ("a".."z").to_a[rand(22)]
|
205
|
+
|
206
|
+
@password = password
|
207
|
+
end
|
208
|
+
|
209
|
+
# TODO del me
|
210
|
+
def version
|
211
|
+
"0.0.0"
|
212
|
+
end
|
213
|
+
|
214
|
+
def integrity_hash
|
215
|
+
encryptable_portion = build_encryptable_portion
|
216
|
+
Digest::SHA2.hexdigest(encryptable_portion)
|
217
|
+
end
|
218
|
+
|
219
|
+
def build_data_collections
|
220
|
+
@data_collections.to_json
|
221
|
+
end
|
222
|
+
|
223
|
+
def build_data_objects
|
224
|
+
@data_objects.to_json
|
225
|
+
end
|
226
|
+
|
227
|
+
def build_attributes
|
228
|
+
hash = { file_type: @file_type
|
229
|
+
# build_date: Time.zone.now, # TODu: change me to the date the data was last modified...
|
230
|
+
}.to_json
|
231
|
+
end
|
232
|
+
|
233
|
+
def build_encryption
|
234
|
+
hash = { integrity_hash: integrity_hash,
|
235
|
+
password: clean_encrypt_string(@password)
|
236
|
+
}.to_json
|
237
|
+
end
|
238
|
+
|
239
|
+
def build_encryptable_portion
|
240
|
+
e = { :data_collections => @data_collections,
|
241
|
+
:data_objects => @data_objects
|
242
|
+
}.to_json
|
243
|
+
end
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
#######################
|
248
|
+
# schema related junk #
|
249
|
+
#######################
|
250
|
+
|
251
|
+
def schema
|
252
|
+
@@schemas[@file_type]
|
253
|
+
end
|
254
|
+
|
255
|
+
def data_collection_names
|
256
|
+
schema[:data_collections]
|
257
|
+
end
|
258
|
+
|
259
|
+
def data_object_names
|
260
|
+
schema[:data_objects]
|
261
|
+
end
|
262
|
+
|
263
|
+
def var_names
|
264
|
+
data_collection_names + data_object_names
|
265
|
+
end
|
266
|
+
|
267
|
+
# specify the schema type
|
268
|
+
def set_file_type(file_type)
|
269
|
+
@file_type = file_type
|
270
|
+
init_data_arrays
|
271
|
+
end
|
272
|
+
|
273
|
+
def init_data_arrays
|
274
|
+
@data_collections = []
|
275
|
+
data_collection_names.count.times { @data_collections << [] }
|
276
|
+
@data_objects = []
|
277
|
+
data_object_names.count.times { @data_objects << [] }
|
278
|
+
end
|
279
|
+
|
280
|
+
# This makes it so you can call file.attribute to get direct access to an attribute
|
281
|
+
def setup_object_for_schema
|
282
|
+
data_collection_names.each.with_index do |data_collection_name, i|
|
283
|
+
# define getter
|
284
|
+
self.define_singleton_method(var_names[i]) do
|
285
|
+
@data_collections[i]
|
286
|
+
end
|
287
|
+
# define setter
|
288
|
+
self.define_singleton_method((data_collection_names[i].to_s + "=").to_sym) do |val|
|
289
|
+
@data_collections[i] = val
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
data_object_names.each.with_index do |data_object_name, i|
|
294
|
+
# getters
|
295
|
+
self.define_singleton_method(data_object_name) do
|
296
|
+
@data_objects[i]
|
297
|
+
end
|
298
|
+
# setters
|
299
|
+
self.define_singleton_method((data_object_name.to_s + "=").to_sym) do |val|
|
300
|
+
@data_objects[i] = val
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|
305
|
+
|
306
|
+
|
307
|
+
|
308
|
+
|
309
|
+
# This method get's the temp directory. If it's a rails
|
310
|
+
# app, that would be Rails.root/tmp, else just /tmp
|
311
|
+
def get_temp_directory
|
312
|
+
defined?(Rails) ? "#{Rails.root}/tmp" : "/tmp"
|
313
|
+
end
|
314
|
+
|
315
|
+
###############
|
316
|
+
# zip methods #
|
317
|
+
###############
|
318
|
+
require 'zip'
|
319
|
+
|
320
|
+
# hash consists of {file1: string_data}
|
321
|
+
# output is a binary string representing a zip file of the entries specified
|
322
|
+
def encode_datafiles_as_zip(hash_of_entries)
|
323
|
+
temp_file = Tempfile.new(file_type.to_s, get_temp_directory)
|
324
|
+
FileUtils.rm temp_file.path
|
325
|
+
|
326
|
+
stream = ::Zip::OutputStream.write_buffer do |zos|
|
327
|
+
hash_of_entries.each do |entry_name, data|
|
328
|
+
zos.put_next_entry entry_name
|
329
|
+
zos.write data
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
stream.rewind
|
334
|
+
stream.read
|
335
|
+
end
|
336
|
+
|
337
|
+
# This method peers through a zip binary data blob and returns a hash consisting of
|
338
|
+
# { file_name1: file_data1, etc }
|
339
|
+
def self.extract_entities_from_binary_data(binary_data)
|
340
|
+
binary_data_io = StringIO.new(binary_data)
|
341
|
+
|
342
|
+
hash = {}
|
343
|
+
::Zip::InputStream.open(binary_data_io) do |io|
|
344
|
+
while (entry = io.get_next_entry)
|
345
|
+
hash[entry.name.to_sym] = io.read
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
password = self.determine_password(hash)
|
350
|
+
|
351
|
+
decrypt_encryptable_data!(password, hash)
|
352
|
+
hash
|
353
|
+
end
|
354
|
+
|
355
|
+
# Play:
|
356
|
+
# Below will write to stdout as long as stdout isn't the terminal (so works in irb and ruby)
|
357
|
+
# zip -P 'fp5!IZbVxgx2hWh8m*UQyc@d5nCGCrbiqPx73hh&' - file_to_add
|
358
|
+
#
|
359
|
+
# Below is attempt to read file from stdin:
|
360
|
+
# out = `echo 'hi' | zip -P 'fp5!IZbVxgx2hWh8m*UQyc@d5nCGCrbiqPx73hh&' - -`
|
361
|
+
#
|
362
|
+
#
|
363
|
+
# Zip commandline is
|
364
|
+
# zip -P 'fp5!IZbVxgx2hWh8m*UQyc@d5nCGCrbiqPx73hh&' zip_to_make.zip file_to_add
|
365
|
+
# maybe password has invalid chars
|
366
|
+
def encode_string_as_password_protected_old_zip_based(encryptable_portion, pass = nil)
|
367
|
+
pish = @password
|
368
|
+
pish = pass unless pass.nil?
|
369
|
+
|
370
|
+
supress_errors = "2>/dev/null"
|
371
|
+
# this will read that file... let's try passing it in from stdin pipes though
|
372
|
+
# alt = "zip -P '#{pish}' - #{@@clear_text_path}"
|
373
|
+
|
374
|
+
# TODu: escape single quotes or this command breaks...
|
375
|
+
raise "tried to encrypt an encryptable_portion which contained illegal character ' which would break the command being piped to zip" if encryptable_portion =~ /\'/
|
376
|
+
# passing in clear_text through pipes
|
377
|
+
alt = "echo '#{encryptable_portion}'| zip -P '#{pish}' - - #{supress_errors}"
|
378
|
+
#alt = "echo '#{encryptable_portion.gsub("\\n", "")}'| zip -P '#{pish}' - -"
|
379
|
+
|
380
|
+
binary_output = `#{alt}`
|
381
|
+
end
|
382
|
+
|
383
|
+
|
384
|
+
|
385
|
+
|
386
|
+
def encode_string_as_password_protected(encryptable_portion, pass = nil)
|
387
|
+
#require 'b_f'
|
388
|
+
|
389
|
+
pish = @password
|
390
|
+
pish = pass unless pass.nil?
|
391
|
+
raise "error, password given was too long, must be 56 or less chars" if pish.length > 56
|
392
|
+
|
393
|
+
bf = BF.new(pish, true)
|
394
|
+
|
395
|
+
encrypted = bf.encrypt(encryptable_portion)
|
396
|
+
end
|
397
|
+
|
398
|
+
|
399
|
+
def decode_password_protected_string(password, decryptable_portion)
|
400
|
+
NmDatafile.decode_password_protected_string(password, decryptable_portion)
|
401
|
+
end
|
402
|
+
|
403
|
+
def obfuscate_file_format
|
404
|
+
# TODu: implement me
|
405
|
+
end
|
406
|
+
|
407
|
+
def deobfuscate_file_format
|
408
|
+
|
409
|
+
end
|
410
|
+
|
411
|
+
|
412
|
+
def clean_encrypt_string(string)
|
413
|
+
# Base64.encode64(encode_string_as_password_protected(string, @@unsecure_pass))
|
414
|
+
NmDatafile.fast_encrypt_string_with_pass(@@unsecure_pass, string)
|
415
|
+
end
|
416
|
+
|
417
|
+
def clean_decrypt_string(string)
|
418
|
+
NmDatafile.clean_decrypt_string(string)
|
419
|
+
end
|
420
|
+
|
421
|
+
|
422
|
+
|
423
|
+
|
424
|
+
|
425
|
+
|
426
|
+
# `gpg -c --no-use-agent`
|
427
|
+
|
428
|
+
###################
|
429
|
+
# Testing methods #
|
430
|
+
###################
|
431
|
+
|
432
|
+
# do as address_completion_file to create an rfsb that should have already existed, but didn't because a fresh db was used
|
433
|
+
# for testing
|
434
|
+
def simulate_rfsb_existance_on_webserver
|
435
|
+
rfsb = self.ready_for_shipment_batch
|
436
|
+
ReadyForShipmentBatch.create(batch_stamp: rfsb["batch_stamp"], sf_integrity_hash: rfsb["integrity_hash"])
|
437
|
+
end
|
438
|
+
|
439
|
+
# creates an address_completion file based on the contents of the current shippable_file object
|
440
|
+
def simulate_address_completion_response(n_shipped)
|
441
|
+
raise "can't make an address_completion_response unless it's a shippable_file" if @file_type != :shippable_file
|
442
|
+
setup_object_for_schema # TODu: I put this in, there was a bug in the test where it needs to be run... does it not run on init somehow?
|
443
|
+
|
444
|
+
shipped_sales = []
|
445
|
+
erroneous_addresses = []
|
446
|
+
sales.each.with_index do |sale, i|
|
447
|
+
if i < n_shipped
|
448
|
+
shipped_sales << { "id" => sale["id"] }
|
449
|
+
else
|
450
|
+
erroneous_addresses << { "id" => sale["id"] }
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
simulated_response = [shipped_sales, erroneous_addresses, ready_for_shipment_batch]
|
455
|
+
|
456
|
+
nmd_address_completion = NmDatafile.new(:address_completion_file, *simulated_response)
|
457
|
+
end
|
458
|
+
|
459
|
+
def generate_upload_params(action = "upload_shippable_file")
|
460
|
+
temp_zip = Tempfile.new('temp_zip', "#{Rails.root}/tmp")
|
461
|
+
save_to_file(temp_zip.path)
|
462
|
+
|
463
|
+
upload_shippable_params = {
|
464
|
+
"file_upload" => { "my_file" => Rack::Test::UploadedFile.new(temp_zip.path, "application/zip") },
|
465
|
+
"controller"=>"admin_pages",
|
466
|
+
"action"=>action
|
467
|
+
}
|
468
|
+
end
|
469
|
+
|
470
|
+
# Don't use push on #sales, it will use the push on the array. TODu: to fix this, make the @sales arrays collections
|
471
|
+
# and give those collections special properties when push is invoked
|
472
|
+
def add_sale(sale)
|
473
|
+
# add to collection the sale
|
474
|
+
self.sales << sale
|
475
|
+
regenerate_rfsb if should_gen_a_new_rfsb?(sale)
|
476
|
+
end
|
477
|
+
|
478
|
+
# we should NOT make a new rfsb if we're adding an old erroneous sale to the object
|
479
|
+
# to check for age... see if it already has an rfsb_id
|
480
|
+
def should_gen_a_new_rfsb?(sale)
|
481
|
+
return true if sale.ready_for_shipment_batch_id.nil?
|
482
|
+
false
|
483
|
+
end
|
484
|
+
|
485
|
+
def regenerate_rfsb
|
486
|
+
rfsb = ReadyForShipmentBatch.gen
|
487
|
+
self.ready_for_shipment_batch = rfsb
|
488
|
+
rfsb.delete
|
489
|
+
end
|
490
|
+
|
491
|
+
# create's some sales, line_items and
|
492
|
+
def create_sales_for_shippable_file(n, e=nil)
|
493
|
+
|
494
|
+
if @file_type == :shippable_file
|
495
|
+
s, l, a, rfsb = create_sales_and_return_data(n)
|
496
|
+
self.sales += s
|
497
|
+
self.line_items += l
|
498
|
+
self.addresses += a
|
499
|
+
self.ready_for_shipment_batch = rfsb
|
500
|
+
rfsb.delete
|
501
|
+
Sale.deep_delete_sales(s)
|
502
|
+
elsif @file_type == :address_completion_file
|
503
|
+
s, e, rfsb = create_sales_and_return_data_address_completion_file(n, e)
|
504
|
+
self.sales += s
|
505
|
+
self.erroneous_sales += e
|
506
|
+
|
507
|
+
self.ready_for_shipment_batch = rfsb
|
508
|
+
rfsb.delete
|
509
|
+
Sale.deep_delete_sales(s)
|
510
|
+
end
|
511
|
+
|
512
|
+
end
|
513
|
+
alias create_sales create_sales_for_shippable_file
|
514
|
+
|
515
|
+
def create_sales_and_return_data_address_completion_file(n, e)
|
516
|
+
e = 0 if e.nil?
|
517
|
+
sales = []
|
518
|
+
errors = []
|
519
|
+
n.times { sales << FactoryGirl.create(:sale_with_1_book) }
|
520
|
+
e.times { errors << FactoryGirl.create(:sale_with_1_book) }
|
521
|
+
rfsb = ReadyForShipmentBatch.gen
|
522
|
+
|
523
|
+
sales.each {|s| s.ready_for_shipment_batch_id = rfsb.id}
|
524
|
+
errors.each {|s| s.ready_for_shipment_batch_id = rfsb.id}
|
525
|
+
|
526
|
+
[ sales, errors, rfsb ]
|
527
|
+
end
|
528
|
+
|
529
|
+
def create_sales_and_return_data(n)
|
530
|
+
sales = []
|
531
|
+
n.times { sales << FactoryGirl.create(:sale_with_1_book) }
|
532
|
+
rfsb = ReadyForShipmentBatch.gen
|
533
|
+
sales.each {|s| s.ready_for_shipment_batch_id = rfsb.id}
|
534
|
+
l = capture_line_items(sales)
|
535
|
+
a = capture_addresses(sales)
|
536
|
+
|
537
|
+
[sales, l, a, rfsb]
|
538
|
+
end
|
539
|
+
|
540
|
+
def capture_line_items(sales)
|
541
|
+
l = []
|
542
|
+
sales.each do |s|
|
543
|
+
l += s.line_items
|
544
|
+
end
|
545
|
+
l
|
546
|
+
end
|
547
|
+
|
548
|
+
def capture_addresses(sales)
|
549
|
+
a = []
|
550
|
+
sales.each do |s|
|
551
|
+
a << s.address
|
552
|
+
end
|
553
|
+
a
|
554
|
+
end
|
555
|
+
|
556
|
+
|
557
|
+
|
558
|
+
#################
|
559
|
+
# Debug methods #
|
560
|
+
#################
|
561
|
+
# render a count of sales
|
562
|
+
def to_s
|
563
|
+
string = "NmDatafile: \n"
|
564
|
+
data_collection_names.each.with_index do |collection_name, i|
|
565
|
+
string << " #{collection_name}: #{@data_collections[i].count} \n"
|
566
|
+
end
|
567
|
+
|
568
|
+
data_object_names.each.with_index do |variable_name, i|
|
569
|
+
string << " #{variable_name}: #{1} \n"
|
570
|
+
end
|
571
|
+
|
572
|
+
string
|
573
|
+
end
|
574
|
+
|
575
|
+
def inspect
|
576
|
+
puts self.to_s
|
577
|
+
end
|
578
|
+
|
579
|
+
def ==(other)
|
580
|
+
return false if other.class != self.class
|
581
|
+
return true if all_data_matches?(other)
|
582
|
+
false
|
583
|
+
end
|
584
|
+
|
585
|
+
def all_data_matches?(other)
|
586
|
+
if self.integrity_hash == other.integrity_hash
|
587
|
+
if self.build_attributes == other.build_attributes
|
588
|
+
return true
|
589
|
+
end
|
590
|
+
end
|
591
|
+
false
|
592
|
+
end
|
593
|
+
|
594
|
+
#####################################################
|
595
|
+
# batch checking, high conasence with Importable #
|
596
|
+
#####################################################
|
597
|
+
|
598
|
+
# Move to... NmDatafile
|
599
|
+
def duplicate_batch?(previous_batch)
|
600
|
+
return false if previous_batch.nil?
|
601
|
+
|
602
|
+
previous_batch.sf_integrity_hash == integrity_hash
|
603
|
+
end
|
604
|
+
|
605
|
+
|
606
|
+
end
|
607
|
+
|
608
|
+
|
609
|
+
# Zip Entry Structure
|
610
|
+
# data_collections: array of data collections... atm, contains
|
611
|
+
# data_objects: array of data_objects
|
612
|
+
# schema: Schema of the data... this should be encrypted if data1 is encrypted
|
613
|
+
# encryption: integrity_hash: SHA2 hash of data for fingerprinting, password: password used to encrypt the data entrys
|
614
|
+
# attributes: - @file_type, nmd_version, build_date, PGP key id used for encrypting the encryption entry
|
615
|
+
|
616
|
+
# Zip Entry Structure after full protection
|
617
|
+
# crypt (encrypted: data_collections, data_objects, schema)
|
618
|
+
# encryption (encrypted)
|
619
|
+
# attributes (clear_text)
|
620
|
+
|
621
|
+
|
622
|
+
# This hack is for... some tricky bullshit, I forgot about
|
623
|
+
def passfunc(hook, uid_hint, passphrase_info, prev_was_bad, fd)
|
624
|
+
$stderr.write("Passphrase for #{uid_hint}: ")
|
625
|
+
$stderr.flush
|
626
|
+
begin
|
627
|
+
system('stty -echo')
|
628
|
+
io = IO.for_fd(fd, 'w')
|
629
|
+
io.puts(gets)
|
630
|
+
io.flush
|
631
|
+
ensure
|
632
|
+
(0 ... $_.length).each do |i| $_[i] = ?0 end if $_
|
633
|
+
system('stty echo')
|
634
|
+
end
|
635
|
+
$stderr.puts
|
636
|
+
end
|
637
|
+
|
638
|
+
|
639
|
+
end
|
640
|
+
|
641
|
+
|
642
|
+
|
643
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# A default schema is defined here... this needs to be converted into
|
2
|
+
# something more generic
|
3
|
+
module NmDatafile
|
4
|
+
SCHEMA = { schemas:
|
5
|
+
{ :shippable_file => {
|
6
|
+
data_collections: [:sales, :line_items, :discounts, :addresses, :ubws, :encryption_pairs], # name the data that is input into the NMDatafile as an array
|
7
|
+
data_objects: [:ready_for_shipment_batch]
|
8
|
+
},
|
9
|
+
|
10
|
+
:address_completion_file => {
|
11
|
+
data_collections: [:sales, :erroneous_sales],
|
12
|
+
data_objects: [:ready_for_shipment_batch]
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
data/lib/nm_datafile.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require "nm_datafile/version"
|
4
|
+
require "nm_datafile/schema"
|
5
|
+
require 'nm_datafile/b_f'
|
6
|
+
require 'nm_datafile/crypto'
|
7
|
+
require "nm_datafile/nm_datafile"
|
8
|
+
|
9
|
+
|
10
|
+
module NmDatafile
|
11
|
+
# Your code goes here...
|
12
|
+
|
13
|
+
def self.new(file_type, *args)
|
14
|
+
NmDatafile.new(file_type, args)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
|
data/nm_datafile.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'nm_datafile/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "nm_datafile"
|
8
|
+
spec.version = NmDatafile::VERSION
|
9
|
+
spec.authors = ["antisec"]
|
10
|
+
spec.email = ["antisec@antisec.com"]
|
11
|
+
spec.summary = %q{A gem that saves files into a secure encrypted file.}
|
12
|
+
spec.description = %q{A gem that creates a data file based on arrays or strings that you feed it. When you save the file, you can choose from multiple encryption methods, asymetric, symetric, etc. etc.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "rubyzip"
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "pry"
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "nm_datafile" do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
#@sample_data = get_sample_data
|
7
|
+
@sale = {"address_id"=>1, "created_at"=>"2015-03-08T03:54:51Z", "currency_used"=>"BTC"}
|
8
|
+
@sample_data = [ @sale ]
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
it "should be instantiable" do
|
13
|
+
NmDatafile.new(:shippable_file)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
it "should be instantiable with data" do
|
18
|
+
#return_array = [ @sales,
|
19
|
+
# @line_items,
|
20
|
+
# @discounts,
|
21
|
+
# @addresses,
|
22
|
+
# @utilized_bitcoin_wallets,
|
23
|
+
# @encryption_pairs,
|
24
|
+
# rfsb ]
|
25
|
+
|
26
|
+
nmd_shippable = NmDatafile.new(:shippable_file, *@sample_data)
|
27
|
+
|
28
|
+
str = nmd_shippable.save_to_string
|
29
|
+
|
30
|
+
# TODO: actually test data and make more data in @sample_data
|
31
|
+
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'nm_datafile'
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
# rspec-expectations config goes here. You can use an alternate
|
6
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
7
|
+
# assertions if you prefer.
|
8
|
+
config.expect_with :rspec do |expectations|
|
9
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
10
|
+
# and `failure_message` of custom matchers include text for helper methods
|
11
|
+
# defined using `chain`, e.g.:
|
12
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
13
|
+
# # => "be bigger than 2 and smaller than 4"
|
14
|
+
# ...rather than:
|
15
|
+
# # => "be bigger than 2"
|
16
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
17
|
+
end
|
18
|
+
|
19
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
20
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
21
|
+
config.mock_with :rspec do |mocks|
|
22
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
23
|
+
# a real object. This is generally recommended, and will default to
|
24
|
+
# `true` in RSpec 4.
|
25
|
+
mocks.verify_partial_doubles = true
|
26
|
+
end
|
27
|
+
|
28
|
+
# The settings below are suggested to provide a good initial experience
|
29
|
+
# with RSpec, but feel free to customize to your heart's content.
|
30
|
+
=begin
|
31
|
+
# These two settings work together to allow you to limit a spec run
|
32
|
+
# to individual examples or groups you care about by tagging them with
|
33
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
34
|
+
# get run.
|
35
|
+
config.filter_run :focus
|
36
|
+
config.run_all_when_everything_filtered = true
|
37
|
+
|
38
|
+
# Limits the available syntax to the non-monkey patched syntax that is
|
39
|
+
# recommended. For more details, see:
|
40
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
41
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
42
|
+
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
43
|
+
config.disable_monkey_patching!
|
44
|
+
|
45
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
46
|
+
# be too noisy due to issues in dependencies.
|
47
|
+
config.warnings = true
|
48
|
+
|
49
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
50
|
+
# file, and it's useful to allow more verbose output when running an
|
51
|
+
# individual spec file.
|
52
|
+
if config.files_to_run.one?
|
53
|
+
# Use the documentation formatter for detailed output,
|
54
|
+
# unless a formatter has already been configured
|
55
|
+
# (e.g. via a command-line flag).
|
56
|
+
config.default_formatter = 'doc'
|
57
|
+
end
|
58
|
+
|
59
|
+
# Print the 10 slowest examples and example groups at the
|
60
|
+
# end of the spec run, to help surface which specs are running
|
61
|
+
# particularly slow.
|
62
|
+
config.profile_examples = 10
|
63
|
+
|
64
|
+
# Run specs in random order to surface order dependencies. If you find an
|
65
|
+
# order dependency and want to debug it, you can fix the order by providing
|
66
|
+
# the seed, which is printed after each run.
|
67
|
+
# --seed 1234
|
68
|
+
config.order = :random
|
69
|
+
|
70
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
71
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
72
|
+
# test failures related to randomization by passing the same `--seed` value
|
73
|
+
# as the one that triggered the failure.
|
74
|
+
Kernel.srand config.seed
|
75
|
+
=end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
def get_sample_data
|
81
|
+
|
82
|
+
end
|
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nm_datafile
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- antisec
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rubyzip
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: A gem that creates a data file based on arrays or strings that you feed
|
84
|
+
it. When you save the file, you can choose from multiple encryption methods, asymetric,
|
85
|
+
symetric, etc. etc.
|
86
|
+
email:
|
87
|
+
- antisec@antisec.com
|
88
|
+
executables: []
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- .gitignore
|
93
|
+
- .rspec
|
94
|
+
- Gemfile
|
95
|
+
- LICENSE.txt
|
96
|
+
- README.md
|
97
|
+
- Rakefile
|
98
|
+
- changelog.md
|
99
|
+
- lib/nm_datafile.rb
|
100
|
+
- lib/nm_datafile/b_f.rb
|
101
|
+
- lib/nm_datafile/crypto.rb
|
102
|
+
- lib/nm_datafile/nm_datafile.rb
|
103
|
+
- lib/nm_datafile/schema.rb
|
104
|
+
- lib/nm_datafile/version.rb
|
105
|
+
- nm_datafile.gemspec
|
106
|
+
- spec/nm_datafile_spec.rb
|
107
|
+
- spec/spec_helper.rb
|
108
|
+
homepage: ''
|
109
|
+
licenses:
|
110
|
+
- MIT
|
111
|
+
metadata: {}
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - '>='
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubyforge_project:
|
128
|
+
rubygems_version: 2.4.6
|
129
|
+
signing_key:
|
130
|
+
specification_version: 4
|
131
|
+
summary: A gem that saves files into a secure encrypted file.
|
132
|
+
test_files:
|
133
|
+
- spec/nm_datafile_spec.rb
|
134
|
+
- spec/spec_helper.rb
|